Continuando a giocare con la webcam e motion

Bene, diciamo che abbiamo il nostro device che produce una sana serie di immagini nella amata directory preposta alla scopo, quindi abbiamo una serie di foto sequenziali, e ci interessa mostrare l’ultima su di una pagina html. Che fare?

Mumble

Un metodo semplice sarebbe creare una pagina html statica con un meta che fa l’auto-refresh

<meta http-equiv="refresh" content="2"/>

ma ci sono metodi migliori lato server.

La soluzione che ho approntato è un po’ macchinosa, ma almeno mi ha permesso di vedere un paio di cose interessanti.

Il tutto ruota attorno al content-type: multipart/x-mixed-replace; (Ho trovato info qui e quo).
Essenzialmente si tratta di lasciare una connessione aperta fra client e server nella quale il secondo passa contenuti al primo dopo la chiamata (server push). Citando wikipedia:

Mixed-Replace (Experimental)

The content type multipart/x-mixed-replace was developed as part of a technology to emulate server push and streaming over HTTP.

All parts of a mixed-replace message have the same semantic meaning. However, each part invalidates – “replaces” – the previous parts as soon as it is received completely. Clients should process the individual parts as soon as they arrive and should not wait for the whole message to finish.

Originally developed by Netscape, it is still supported by Mozilla, Firefox, Safari (but not in Safari on the iPhone) and Opera, but traditionally ignored by Microsoft. It is commonly used in IP cameras as the MIME type for MJPEG streams.

Quindi a parte Explorer, siamo a posto con tutti gli altri browser (strano).

Seguendo il libro della O’Reilly ho creato uno script cgi in perl utile per creare lo stream video (sostanzialmente il ciclo di immagini jpeg utili):

#!/usr/bin/perl        

use strict;
use warnings;

sub display_img {
        my ($file) = @_;
        if ( open(FILE, ‘<’, $file) ) {
                my $content_length = (stat (FILE))[7];
                print "Content-type: image/jpeg\n";
                print "Content-length: ", $content_length, "\n\n";
                print <FILE>;
                close(FILE);
        } else {
                print "Content-type: text/plain\n\n";
                print "<p>500, File Access Error, Cannot open graphic file: $file</p>";
        }
}

$| = 1;

my $boundary_string = "\n–xstring\n";
my $end_string = "\n–xstring–\n";
my $delay = 3;
my $img_path = ‘/var/www/webcam/’;
my $image = ‘webcam-snap.jpg’;

print "Content-type: multipart/x-mixed-replace; boundary=xstring","\n\n";

print $boundary_string;

while (1) {
        display_img ($img_path.$image);
        print $boundary_string;
        sleep($delay);
}
print $end_string;

exit(0);

Il $| serve per avere lo stdout flushato automaticamente (man perlvar).

Creata questa sequenza di content-type: image/jpeg l’ho cacciata dentro un html generato dal php, in modo da controllare dinamicamente 1) se motion è in uso (ovvero se la webcam è accesa) e 2) se il browser è Explorer (nel qual caso lo stream non è visualizzabile).

<?php                                

function top() {
        $top = <<<EOF
<html>
<head>
        <title>Webcam on H</title>
    <meta name="creator" content="Webcamd" />
        <style type="text/css">
                body {
                        margin: 10px 50px;
                        color: #999;
                        background-color: black;
                        text-align: center;
                }
                img, div.riquadro {
                        border: 1px solid #999;
                        padding: 3px;
                        width: 320px;
                        height: 240px;
                }
                .Online { color: #393; }
                .Offline        { color: #933; }
                a:link  { color: #D93; }    
                a:visited { color: #A43; }
                a:hover { color: #AA3; }
        </style>
</head>
<body>
EOF;                        
        return $top;                      

}

function foot() {
        $bot = <<<EOF
</body>              
</html>

EOF;
        return $bot;
}                    

function body_cam($status, $ua) {
        $body = ;
        $body .= "<h1 class=\"$status\">Cam $status</h1>";
        $body .= "<div style=\"margin: 1em 0 3em;\">";
        if ($status == ‘Online’) {
                $body .= ( preg_match(‘/MSIE/’,$ua) ) ? ‘<div class="riquadro" style="margin: 0 auto;"><p class="Online">Stream video non disponibile con Explorer.</p></div>’
                                                                   : ‘<img src="http://192.168.0.1/cgi-bin/webcam"/>’;              
        } else {
                $body .= ‘<div class="riquadro" style="margin: 0 auto;"><p class="Offline">Webcam Offline</p></div>’;
        }
        $body .= "</div>";

        return $body;
}

function body_extra($ua) {
        $body = <<<EOF
<div style="margin: 1em 0 1em;">
<p><strong>User-agent:</strong> $ua</p>
</div>
<hr/>
<p>
        Made by <a href="http://www.simotrone.it">simotrone</a>,<br/>
        with <a href="http://www.lavrsen.dk/foswiki/bin/view/Motion/WebHome">Motion</a>.
</p>
EOF;
        return $body;
}

$user_agent = $_SERVER[‘HTTP_USER_AGENT’];
$motion_pid = ‘/tmp/motion.pid’;

if (file_exists($motion_pid)) {
        $status = ‘Online’;
} else {
        $status = ‘Offline’;
}

echo top();
echo body_cam($status, $user_agent);
echo body_extra($user_agent);
echo foot();

?>

Buono così.

Giochicchiando con la webcam mi sono ritrovato a volerla usare da riga di comando (cosa buona per diverse ragioni), e così ho scoperto motion.
Qui si trova di tutto: Motion.

Dopo aver fatto riconoscere la webcam al kernel (come indicato qua), si può ottenere qualche informazione con $ v4l-info (installato tramite il pacchetto v4l-conf).
Nel mio caso:

sim@idrogeno:~$ v4l-info

### v4l2 device info [/dev/video0] ###
general info                          
    VIDIOC_QUERYCAP                  
        driver                  : "zc3xx"
        card                    : "USB Camera (046d:08d9)"
        bus_info                : "usb-0000:00:02.0-1"    
        version                 : 2.8.0                  
        capabilities            : 0×5000001 [VIDEO_CAPTURE,READWRITE,STREAMING]

standards
[]
video capture
    VIDIOC_ENUM_FMT(0,VIDEO_CAPTURE)
        index                   : 0
        type                    : VIDEO_CAPTURE
        flags                   : 1            
        description             : "JPEG"      
        pixelformat             : 0x4745504a [JPEG]
    VIDIOC_G_FMT(VIDEO_CAPTURE)                    
        type                    : VIDEO_CAPTURE    
        fmt.pix.width           : 320              
        fmt.pix.height          : 240              
        fmt.pix.pixelformat     : 0x4745504a [JPEG]
        fmt.pix.field           : NONE            
        fmt.pix.bytesperline    : 320              
        fmt.pix.sizeimage       : 29390            
        fmt.pix.colorspace      : JPEG            
        fmt.pix.priv            : 1                
[]

Avendo le dimensioni “accattabili” dalla webcam, ho dovuto solo configurare motion per convogliare una serie di screenshot in un filepath e poi “montarci” una pagina html sopra.

Una semplice configurazione di motion potrebbe essere questa:

videodevice /dev/video0
input 8

daemon off
process_id_file /tmp/motion.pid

jpeg_filename webcam
target_dir /var/www/webcam

height 320
width 240
auto_brightness on

minimum_frame_time 2

Con questa configurazione, al lancio di motion (con daemon on va direttamente in background) verrà salvata in /var/www/webcam/webcam.jpg la foto fatta dalla webcam ogni 2 secondi. :-)

Motion ha un miliardo di altre finezze, ma per ora è tutto.

Guardicchiando per un progetto mi sono imbattuto in HTML::CalendarMonth, un modulo perl interessante per la generazione di calendarietti in html.

Il tutto si basa sui moduli HTML::Element che creano degli oggetti per la gestione dei singoli elementi della pagina html. Segue un esempio particolarmente brutto, ma utile ai fini di capire la logica.
Il punto della questione è definire gli elementi HTML con l’apposito costruttore new(‘tag’, eventuale_attributo => ‘come hash’); gli elementi possono poi essere gestiti con una struttura parent-child attraverso il metodo push_content che ordina in un array gli elementi che lo compongono.

use HTML::Element;

# HTML::Element crea oggetti con nomi/tag e attributi.
my $html  = HTML::Element->new(‘html’);
my $head  = HTML::Element->new(‘head’);
my $title = HTML::Element->new(‘title’);
   $title->push_content(‘Titolo della pagina’);
my $body  = HTML::Element->new(‘body’);
my $h1    = HTML::Element->new(‘h1′);
   $h1->push_content(‘Header nel body’);
my $hlink = HTML::Element->new(‘a’, href => ‘http://www.simotrone.it’);
   $hlink->push_content(‘il mio sito’);
my $p     = HTML::Element->new(‘p’);
   $p->push_content(‘Questo paragrafo ha un link: ‘, $hlink, ‘.’);

$head->push_content($title);
$body->push_content($h1, $p);

$html->push_content($head, $body);

print $html->as_HTML;

Per ottenere:

<html>
        <head>
                <title>Titolo della pagina</title>
        </head>
        <body>
                <h1>Header nel body</h1>
                <p>Questo paragrafo ha un link: <a href="http://www.simotrone.it">il mio sito</a>.</p>
        </body>
</html>

Ok, fin qui sembra una pompa, 150 righe per ottenerne 5. Non molto economico.

Comunque il punto della questione era far vedere HTML::CalendarMonth, che sfruttando i moduli suddetti ritorna cose interessanti con poco codice. :)
Con queste poche righe…

use HTML::AsSubs;
use HTML::CalendarMonth;

my $cal = HTML::CalendarMonth->new( month => 10, year => 2009 );
$cal->item($cal->year, $cal->month)->attr(style => ‘background-color: wheat; font-size: 150%;’);
$cal->col(0)->attr(style => ‘font-weight: bold’);
print $cal->as_HTML;

possiamo ottenere questo:

October 2009
Sun Mon Tue Wed Thu Fri Sat
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

E con la semplice aggiunta di un ciclo for che genera i mesi e di una sub possiamo ottenere un calendario molto flessibile.

sub mensile {
        my ($mm,$yyyy) = @_;
        my $cal = HTML::CalendarMonth->new( month => $mm, year => $yyyy );
        $cal->item($cal->year, $cal->month)->attr(style => ‘background-color: wheat; font-size: 150%;’);
        $cal->col(0)->attr(style => ‘font-weight: bold’);
        print $cal->as_HTML;
        print "\n";
}

for (1,2,3) {
         mensile($_,1979);
}

Su Debian i pacchetti sono questi qua:

idrogeno:~# dpkg -l libhtml-calendarmonth-perl libhtml-element-extended-perl libhtml-tableextract-perl
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Cfg-files/Unpacked/Failed-cfg/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name              Version           Description
+++-=================-=================-=======================
ii  libhtml-calendarm 1.19-1            generate and manipulate
                                        calandar months in HTML
ii  libhtml-element-e 1.17-3            extended HTML::Element
                                        classes
ii  libhtml-tableextr 2.10-3            module for extracting
                                        the content contained
                                        in tab

Tempo addietro m’ero fatto uno script in bash per scrivermi rapidamente un layout html standard (coi tag html, head, body, il doctype, ecc.).

Aveva qualche opzione, ma era una cosa semplice e comunque utile.

Ora posso farlo semplicemente così:

$ perl -MCGI=:standard -e ‘print start_html("[% title %]"),end_html;’

<!DOCTYPE html
        PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
<head>
<title>[% title %]</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body>

</body>
</html>

Il [% title %] è per preparare il campo a TT, niente di che.

Come “prendere” una selezione col mouse su una pagina html grazie a javascript?
Semplice: si scrive una funzione js che accatta la selezione. :) Il problema, come al solito, è capire QUALI oggetti danno la possibilità di prendere l’evento che ci interessa e COME questi oggetti operano.
(Odio i browser e javascript che sta in mezzo a ste cose.)

Nei vari browser mozzillari getSelection restituisce il testo della selezione, mentre in Exploder l’oggetto raccattato con selection è un TextRange che va manipolato ancora un pochetto.

function print_selection() {
    var selected = ;
    var area = document.getElementById(‘name_area_dove_stampare’);
    if (document.getSelection) {
        selected = document.getSelection();
    } else if (document.selection) {
        // Parte per IE
        selected = document.selection.createRange().text;
    }
    area.value = selected;
}

Per altro sui browser come FFox si puo’ fare anche un’altro numero:

{
    var area = document.getElementById(elemento);
    selected = area.value.substring(area.selectionStart, area.selectionEnd);
}

In questo caso stiamo ottenendo la substring che inizia nella posizione dove inizia la selezione, e finisce nella posizione dove finisce la selezione (in pratica estrapola la stessa porzione di testo, capendo la posizione degli estremi con selectionStart e selectionEnd).

Boh, che orticaria.

Ah, nota interessante: Opera (che è sempre un sacco avanti) supporta entrambi i metodi (document.getSelection e document.selection)

Molte info accattate su quirksmode.org.

Nota, se non si fosse capito: Il mio rapporto con javascript e coi CSS è decisamente conflittuale (più odi che amo, per intenderci).

Un indice di un resoconto di articoli sui css, da smashingmagazine.com. :)

Mastering CSS, Part 1: Styling Design Elements

  1. Layout and User Interface Techniques
  2. Navigation and Menu Techniques
  3. Image Styles and Galleries
  4. Typography Techniques
  5. Icons, Buttons and Links


Mastering CSS, Part 2: Advanced Techniques and Tools

  1. Calendars, Lists, Tables, and Timelines
  2. iPhone CSS Techniques
  3. Form and Search Techniques
  4. Visualization Techniques
  5. Other Handy Techniques and Tips
  6. CSS3 Techniques
  7. CSS Tools
  8. More Articles and Resources

Per ora ho usato i form, e letti gli articoli riguardanti le date (O_O), il testo sulle immagini, e gli angoli da sfogliare (bellissimo, provate il demo!). Molto carini anche gli articoli sulle liste visualizzabili in maniera diversa e riguardanti la validazione dei form.

Ho trovato un giochino bellissimo.
Inserite il testo. Date un’immagine a caso, e selezionate. :)
Hide an image in html
Genera anche il codice.
Bellissimo. :)

« Previous Articles    
SIMOTRONE WEB PAGE is based on WordPress platform, RSS tech , RSS comments design by Gx3.