premetto che questa è la prima volta che scrivo un HOW-TO e sicuramente avrò bisogno del vostro aiuto per perfezionarlo!! :P
QUESTO HOW-TO VI GUIDERÀ NELLA MODIFICA DEL DRIVER UVCVIDEO E FA RIFERIMENTO ALLA VERSIONE 0.1.0. INOLTRE LE PATCH PRESENTI IN QUESTO HOW-TO SONO STUDIATE APPOSTA PER IL FORMATO YUV DELLE IMMAGINI, QUINDI SE LA VOSTRA WEBCAM SUPPORTA ALTRI FORMATI O L'APPLICAZIONE CHE UTILIZZA LA WEBCAM FA RICHIESTA DI UN ALTRO FORMATO, PURTROPPO QUESTA GUIDA NON VI SERVIRÀ!!! COMUNQUE PRIMA DI GETTARE LA SPUGNA, PROVATE TUTTE LE PATCH CHE HO INSERITO!!
LE PATCH SONO ANCORA IN FASE DI TEST, QUINDI È MOLTO PROBABILE CHE DA UN GIORNO ALL'ALTRO VENGANO FATTE DELLE MODIFICHE A QUESTO HOW-TO!! RIMANETE SINTONIZZATI SE VOLETE AVERE UNA VERSIONE MENO "BUGGOSA"!!!
Veniamo al dunque...
meno di un mese fa ho comprato un portatile dell'ASUS (F3SR) con webcam integrata della SONIX da 1.3 Mpx [174f:5a35].
Purtroppo la webcam è stata montata all'incontrario (per problemi di spazio credo).
Quindi sia sotto winzoz che sotto linux o qualsiasi altro S.O. per ottenere immagini non ribaltate è necessaria una conversione dell'immagine.
Per fare ciò ci sono principalmente tre metodi:
- o la webcam stessa supporta la conversione in tempo reale e quindi il driver può richiedere che l'immagine venga convertita via hardware (soluzione più veloce);
- o con una pipe: con un software (es. motion) si ribalta l'immagine e poi si redirige l'output su un altro iNode (es. /dev/videoY) e si usa quest'ultimo come se fosse il device associato alla webcam;
- oppure il driver si deve prendere carico della conversione.
Le ultime due ovviamente sono le soluzioni più lente, la seconda è la più lenta in assoluto!!
Purtroppo a quanto ho capito, la mia webcam non supporta nessun ribaltamento in tempo reale, quindi in uscità invierà sempre immagini a "testa in giù"!!
Inoltre ho pravato a fare una "pipe", ma proprio non ci sono riuscito!! :'(
Proprio allora ho deciso di provare a modificare il driver... (b2b)
e dopo qualche dannazione....ce l'ho fatta...grazie anche al contributo datomi da Laurent Pinchart, il creatore del driver "uvcvideo", e dagli iscritti alla sua mailing-list.
La seguente frase non è più vera perchè, dopo essermi studiato i vari formati YUV delle immagini, ora ho anche messo a disposizione le patch per avere immagini non speculari.
Una piccola precisazione: per cause che non ho ancora compreso, le immagini ribaltate che ottenevo perdevano di qualità del colore (per esempio blu e rosso erano invertiti). L'unica soluzione che ho trovato è stata quella di ribaltare le immagini in modo tale da ottenerle speculari, come se ci guardassimo allo specchio (e sinceramente io preferisco così). Solamente in questo modo riesco a ottenere colori "perfetti"!!!
Prima di arrivare al dunque, è necessaria una breve introduzione al funzionamento di uvcvideo (ma solo le parti che a noi interessano..cercherò di essere breve), premetto anche che non sono un'esperto di questo driver (quindi potrei dire anche delle cavolate, scusatemi).
UVC è l'acronimo di "USB VIDEO CLASS" e il driver UVCVIDEO vuole essere la soluzione a tutti i problemi di coloro che utilizzano sorgenti/uscite video tramite interfaccia USB in ambiente Linux. Nel nostro caso ci interessano le webcam!!
Purtroppo non tutte le webcam possono essere "pilotate" con questo driver, per sapere se avete sotto mano una webcam compatibile con UVCVIDEO ci sono due vie:
- o controllate se la vostra webcam è presente nell'elenco in questa pagina http://linux-uvc.berlios.de/#devices
(l'elenco non è completo, quindi in caso negativo provate i prossimi passaggi)
- oppure (dopo aver pluggato la vostra webcam) lanciate questi comandi da shell:
per sapere qual è l'ID della vostra webcam:
Codice: Seleziona tutto
lsusb
A questo punto lanciate il seguente comando (al posto di XXXX:YYYY dovete mettere l'ID della webcam che avete ottenuto con il comando precedente):Bus 002 Device 001: ID 0000:0000
Bus 001 Device 002: ID XXXX:YYYY "ModelloDellaVostraWebcam"
Bus 001 Device 001: ID 0000:0000
Se come output ottenete qualcosa del genere (ci possono essere anche più/meno linee)sudo lsusb -d XXXX:YYYY -v | grep "14 Video"
allora la vostra webcam è compatibile col driver UVCVIDEO.bFunctionClass 14 Video
bInterfaceClass 14 Video
bInterfaceClass 14 Video
bInterfaceClass 14 Video
Nel caso in cui questo tentativo fallisse, purtroppo non c'è nulla da fare. Infatti questo vuol dire che il produttore della webcam ha creato un proprio protocollo di comunicazione tra webcam e driver e perciò sarete obbligati a usare un driver specifico, e non potrete utilizzare driver generici come appunto UVCVIDEO.
Comunque anche se questo test fosse fallito....tentar non dovrebbe nuocere!!!
Per tutti coloro che hanno "superato" questo primo test si può procedere al vero e proprio HOW-TO!!! (good)
In fase di programmazione, grazie ai consigli ottenuti sulla mailing-list sopra citata, ho ottenuto due versioni funzionanti del driver UVCVIDEO:
in realtà il mio contributo è stato molto esiguo ed è consistito nell'aggiunta di poche righe di codice in una sola funzione.
Per far sì che tutti capiate di cosa parlerò vi conviene scaricarvi l'ultima versione del driver UVCVIDEO:
[preso da http://ugaciaka.wordpress.com/2008/05/2 ... -uvcvideo/]
[thanks ugaciaka]
Una cartella nominata "Trunk" verrà creata nel direttorio da cui avete l'anciato l'ultimo comando, dentro questa cartella sono presenti i sorgenti del driver e il MAKEFILE necessario per l'installazione.I sorgenti si trovano in un repository SVN, quindi installiamo il client
e successivamente scarichiamo nella nostra home i sorgentiCodice: Seleziona tutto
sudo apt-get install subversion
Codice: Seleziona tutto
svn checkout svn://svn.berlios.de/linux-uvc/linux-uvc/trunk
Per quanto mi riguarda, io ho dovuto solamente modificare il file "uvc_video.c" e in particolare solamente la funzione:
Codice: Seleziona tutto
uvc_video_decode_data
Codice: Seleziona tutto
static void uvc_video_decode_data(struct uvc_video_device *video,
struct uvc_buffer *buf, const __u8 *data, int len)
{
struct uvc_video_queue *queue = &video->queue;
unsigned int maxlen, nbytes;
void *mem;
if (len <= 0)
return;
/* Copy the video data to the buffer. */
maxlen = buf->buf.length - buf->buf.bytesused;
mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused;
nbytes = min((unsigned int)len, maxlen);
memcpy(mem, data, nbytes);
buf->buf.bytesused += nbytes;
/* Complete the current frame if the buffer size was exceeded. */
if (len > maxlen) {
uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n");
buf->state = UVC_BUF_STATE_DONE;
}
}
La funzione che ho riportato sopra svolge appunto questo compito ed è proprio lei che viene richiamata più e più volte prima che un frame sia completato. In particolare il passo saliente è constituito da una sola linea di codice:
Codice: Seleziona tutto
memcpy(mem, data, nbytes);
- "data" contiene la parte di frame proveniente dalla webcam;
- "nbytes" indica quanti byte occupa la porzione di frame fornita;
- "mem" indica l'area di memoria in cui salvare e completare il frame.
Alla fine sarà proprio mem che verrà poi passata all'applicativo.
Inoltre:
Codice: Seleziona tutto
if (len > maxlen)
La frase sopra non è del tutto corretta: l' "if" serve per sapere se dalla webcam sono giunti più byte di quanti ne fossero necessari. Comunque in un'eventualità del genere significa che il frame è stato completato, ma che alcuni byte (quelli eccedenti) sono stati scartati.
A questo punto dovreste essere in grado di comprendere anche la parte successiva...
Come vi ho già detto alla fine ho ottenuto due versioni funzionanti del driver.
La prima è stata la più semplice da creare, però teoricamente dovrebbe essere più lenta in quanto viene richiesto al sistema di allocare della memoria, cioè di dedicare un tot di RAM per memorizzare temporaneamente il frame, e di fare inoltre un'altra "memcpy", operazioni abbastanza costose dal punto di vista delle performance (soprattutto all'aumentare della risoluzione del video richiesta). Operazioni che comunque verrebbero svolte una volta che il frame viene completato, cioè all'interno dell' "if" sopracitato e quindi una volta sola per ogni frame.
La seconda invece non necessita nessuna allocazione e trasferisce i dati direttamente da "data" a "mem" secondo un algoritmo che ho studiato ad-hoc. Questo algoritmo viene applicato per ogni singola chiamata alla funzione "uvc_video_decode_data" (quindi diverse centinaia di volte), cioè per ogni singola porzione del frame passata dalla webcam.
Ecco a voi finalmente la parte pratica per entrambe le soluzioni:
vi pregherei di provarle entrambe e di farmi sapere quale sia la migliore, cioè quella con meno ritardi!!
Per entrambe le soluzioni ora sono disponibili due varianti: una con le immagini rispecchiate e l'altra con le immagini normali!!!
PER RENDERE LE PATCH PIÙ COMPATIBILI CON LE FUTURE VERSIONI DEL DRIVER, HO DECISO DI FORNIRE NON PIÙ TUTTO IL CODICE DELLA FUNZIONE DA ME MODIFICATA, MA SOLAMENTE LE VERE E PROPRIE PATCH.
Tutte le patch sono state create con il seguente comando:
Codice: Seleziona tutto
diff -uN original_folder patched_folder > file_di_patch
ATTENZIONE: tutte le patch fanno riferimento al file ORIGINALE "uvc_video.c", quindi se dopo aver applicato una patch ne volete applicare un'altra, dovrete prima sostituire il file "uvc_video.c" patchato con quello originale. Vi conviene quindi fare una copia di backup dell'originale.
Quindi veniamo al dunque: vi basterà copiare in un nuovo file (per esempio "uvc_video_soluzione1.patch") la patch che preferite (per evitare eventuali errori, è necessario copiare anche l'ultima riga (vuota) della patch); questo file poi dovrà essere copiato nella cartella "Trunk" precedentemente scaricata.
NOVITÀ (27 giugno 2008):
ORA, INVECE DI COPIARE IL TESTO DELLE PATCH, È POSSIBILE SCARICARE DIRETTAMENTE I 4 FILE CON LE PATCH. LI TROVATE ALLA FINE DI QUESTO HOW-TO!!
Una volta fatto questo, per applicare la patch da shell vi basterà andare nella cartella "Trunk" e lanciare il seguente comando:
Codice: Seleziona tutto
patch < uvc_video_soluzione1.patch
#added 26/06/'08
Per quanto riguarda entrambe le versioni della SOLUZIONE 1, ho cercato di accelerarle un po' dimezzando l'area allocata!!
PATCH SOLUZIONE 1 (IMMAGINI A SPECCHIO)
Codice: Seleziona tutto
diff -uN UVCVIDEO_v0.1.0/uvc_video.c UVCVIDEO_patched/uvc_video.c
--- UVCVIDEO_v0.1.0/uvc_video.c 2008-06-26 10:41:01.000000000 +0200
+++ UVCVIDEO_patched/uvc_video.c 2008-06-26 15:33:33.000000000 +0200
@@ -371,23 +371,92 @@
return data[0];
}
+/* This patched function allows to overturn video images from an upside-down
+ * orientation to a normal one with mirrored effect. The conversion simply
+ * consists in reversing the order of the rows of imagines.
+ * This patch performs its job just once for each frame and only when current
+ * frame is completed, but each time it is required to allocate memory in order
+ * to store a copy of that frame.
+ * This patch should work with all YUV image formats.
+ */
static void uvc_video_decode_data(struct uvc_video_device *video,
struct uvc_buffer *buf, const __u8 *data, int len)
{
struct uvc_video_queue *queue = &video->queue;
unsigned int maxlen, nbytes;
void *mem;
+ /* Patch variables */
+ __u8 *mem_tmp, *ptr_tmp;
+ int i, k, row_size;
if (len <= 0)
return;
/* Copy the video data to the buffer. */
+ /* How many bytes are needed to complete the buffer? */
maxlen = buf->buf.length - buf->buf.bytesused;
+ /* Where do pixels stored in "data" have to be copied? */
mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused;
+ /* How many bytes really can be copied into "mem"? */
nbytes = min((unsigned int)len, maxlen);
+ /* "nbytes" are copied from "data" to "mem" buffer.
+ * "data" stores a sequence of pixels coming from the video source.
+ * This sequence is not a full frame or a full row of pixel, but just an
+ * ordered vector of pixels (from top-left to bottom-right), whose
+ * represents just an area of the current frame.
+ * This function has to be called hundreds of times before a frame is
+ * completed and "nbytes" is not constant! Each time "data" contains the
+ * next part of the frame. At the end data stored in "mem" buffer will
+ * be used by the application who requested the video stream.
+ */
memcpy(mem, data, nbytes);
buf->buf.bytesused += nbytes;
+ /* Have the last copied bytes completed the current frame? */
+ if (nbytes == maxlen) {
+ /* Area where to save the upper half part of the original frame
+ * (just half in order to speed up the patch) before reversing.
+ */
+ mem_tmp = (__u8 *) kmalloc(buf->buf.bytesused / 2, GFP_ATOMIC);
+ if (mem_tmp != NULL ) {
+ /* Copy top-half part of frame in a temporary buffer */
+ memcpy(mem_tmp, queue->mem + buf->buf.m.offset,
+ buf->buf.bytesused / 2);
+ /* "row_size" does not depend only on the width of the
+ * frame, but also on the pixel color depth (bpp).
+ */
+ row_size = video->streaming->cur_frame->wWidth *
+ video->streaming->format->bpp / 8;
+ /* The following cycle just copy full frame rows from
+ * the last one stored in "mem" (and going up) to the
+ * first one (and going down) stored in "mem" itself.
+ */
+ ptr_tmp = queue->mem + buf->buf.m.offset
+ + buf->buf.bytesused / 2;
+ /* When the top-half of the frame has been reversed,
+ * rows are copied from the last one stored in "mem_tmp"
+ * (and going up) into the bottom half part of "mem"
+ * buffer.
+ */
+ for (i = 0, k = buf->buf.bytesused / 2 - row_size;
+ i < buf->buf.bytesused;
+ i += row_size, k -= row_size) {
+ /* If the top-half of the frame has been
+ * revesed, then it is needed to split the
+ * source buffer from "mem" to "mem_tmp".
+ */
+ if (i == buf->buf.bytesused / 2) {
+ ptr_tmp = mem_tmp;
+ k = buf->buf.bytesused / 2 - row_size;
+ }
+ memcpy(queue->mem + buf->buf.m.offset + i,
+ ptr_tmp + k,
+ row_size);
+ }
+ /* For this frame "mem_tmp" is not needed any more. */
+ kfree(mem_tmp);
+ }
+ }
/* Complete the current frame if the buffer size was exceeded. */
if (len > maxlen) {
uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n");
Se volete avere immagini non speculari, fate riferimento alla Soluzione 1 (immagini normali)!!!
Se volete provare ad avere immagini non speculari dovete sostituire la linea:
Codice: Seleziona tutto
pixel_size = video->streaming->cur_frame->wWidth* video->streaming->format->bpp / 8;
Codice: Seleziona tutto
pixel_size = video->streaming->format->bpp / 8;
Codice: Seleziona tutto
diff -uN UVCVIDEO_v0.1.0/uvc_video.c UVCVIDEO_patched/uvc_video.c
--- UVCVIDEO_v0.1.0/uvc_video.c 2008-06-26 10:41:01.000000000 +0200
+++ UVCVIDEO_patched/uvc_video.c 2008-06-26 15:33:33.000000000 +0200
@@ -371,23 +371,105 @@
return data[0];
}
+/* This patch should work ONLY with YUY2 image formats, also known as YUYV or
+ * YUV422 formats.
+ * This patched function allows to overturn video images from an upside-down
+ * orientation to a normal one. The conversion consists in copying 4 bytes at a
+ * time (Y0,U0,Y1,V0) corresponding to 2 pixels, in a bottom-up direction, from
+ * the frame (coming from the video source) to the buffer that will be used by
+ * the application requesting the video stream. But in order to satisfy the YUY2
+ * image format byte has to be copied in this way: Y1 U0 Y0 VO.
+ * This patch performs its job just once for each frame and only when current
+ * frame is completed, but each time it is required to allocate memory in order
+ * to store a copy of that frame.
+ */
static void uvc_video_decode_data(struct uvc_video_device *video,
struct uvc_buffer *buf, const __u8 *data, int len)
{
struct uvc_video_queue *queue = &video->queue;
unsigned int maxlen, nbytes;
void *mem;
+ /* Patch variables */
+ __u8 *mem_tmp, *ptr_tmp;
+ int i, k, pixel_size;
if (len <= 0)
return;
/* Copy the video data to the buffer. */
+ /* How many bytes are needed to complete the buffer? */
maxlen = buf->buf.length - buf->buf.bytesused;
+ /* Where do pixels stored in "data" have to be copied? */
mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused;
+ /* How many bytes really can be copied into "mem"? */
nbytes = min((unsigned int)len, maxlen);
+ /* "nbytes" are copied from "data" to "mem" buffer.
+ * "data" stores a sequence of pixels coming from the video source.
+ * This sequence is not a full frame or a full row of pixel, but just
+ * an ordered vector of pixels (from top-left to bottom-right), whose
+ * represents just an area of the current frame.
+ * This function has to be called hundreds of times before a frame is
+ * completed and "nbytes" is not constant! Each time "data" contains the
+ * next part of the frame. At the end data stored in "mem" buffer will
+ * be used by the application who requested the video stream.
+ */
memcpy(mem, data, nbytes);
buf->buf.bytesused += nbytes;
+ /* Have the last copied bytes completed the current frame? */
+ if (nbytes == maxlen) {
+ /* Area where to save the original frame before manipulation. */
+ mem_tmp = (__u8 *) kmalloc(buf->buf.bytesused / 2, GFP_ATOMIC);
+ if (mem_tmp != NULL ) {
+ /* Copy the original frame in a temporary buffer. */
+ memcpy(mem_tmp, queue->mem + buf->buf.m.offset,
+ buf->buf.bytesused / 2);
+ /* "pixel_size" depens on the pixel color depth (bpp),
+ * but in YUY2 image format is constant and equal to 2.
+ */
+ pixel_size = video->streaming->format->bpp / 8;
+ /* The following loop copy 2 pixels at a time (4 bytes
+ * in YUY2 format) from the last two stored in "mem"
+ * (and going back) to the first two (and going on)
+ * stored in "mem" itself following a sort of YUY2
+ * algorithm.
+ */
+ ptr_tmp = queue->mem + buf->buf.m.offset
+ + buf->buf.bytesused / 2;
+ /* When the top-half of the frame has been reversed,
+ * rows are copied from the last one stored in "mem_tmp"
+ * (and going up) into the bottom half part of "mem"
+ * buffer.
+ */
+ for (i = 0, k = buf->buf.bytesused / 2 - 2 * pixel_size;
+ i < buf->buf.bytesused;
+ i += 2 * pixel_size, k -= 2 * pixel_size){
+ /* If the top-half of the frame has been
+ * revesed, then it is needed to split the
+ * source buffer from "mem" to "mem_tmp".
+ */
+ if (i == buf->buf.bytesused / 2) {
+ ptr_tmp = mem_tmp;
+ k = buf->buf.bytesused / 2
+ - 2 * pixel_size;
+ }
+ /* The order of copied bytes is changed from
+ * (Y0 U0 Y1 V1) to (Y1 U0 Y0 V1), i.e. from
+ * (#0 #1 #2 #3) to (#2 #1 #0 #3).
+ */
+ ((__u8 *)(queue->mem+buf->buf.m.offset + i))[0] =
+ ((__u8 *)(ptr_tmp + k))[2];
+ ((__u8 *)(queue->mem+buf->buf.m.offset + i))[1] =
+ ((__u8 *)(ptr_tmp + k))[1];
+ ((__u8 *)(queue->mem+buf->buf.m.offset + i))[2] =
+ ((__u8 *)(ptr_tmp + k))[0];
+ ((__u8 *)(queue->mem+buf->buf.m.offset + i))[3] =
+ ((__u8 *)(ptr_tmp + k))[3];
+ }
+ /* For this frame "mem_tmp" is not needed any more. */
+ kfree(mem_tmp);
+ }
+ }
/* Complete the current frame if the buffer size was exceeded. */
if (len > maxlen) {
uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n");
Codice: Seleziona tutto
diff -uN UVCVIDEO_v0.1.0/uvc_video.c UVCVIDEO_patched/uvc_video.c
--- UVCVIDEO_v0.1.0/uvc_video.c 2008-06-26 10:41:01.000000000 +0200
+++ UVCVIDEO_patched/uvc_video.c 2008-06-26 14:03:58.000000000 +0200
@@ -371,23 +371,91 @@
return data[0];
}
+/* This patched function allows to overturn video images from an upside-down
+ * orientation to a normal one with mirrored effect. The conversion consists in
+ * reversing the order of the rows of imagines.
+ * "data" stores a sequence of pixels coming from the video source.
+ * This sequence is not a full frame or a full row of pixel, but just an
+ * ordered vector of pixels (from top-left to bottom-right), whose
+ * represents just an area of the current frame and which size ("nbytes") is
+ * not constant. In fact this function has to be called hundreds of times
+ * before a frame is completed. Each time "data" contains the next part of the
+ * current frame (upside-down). At the end data stored in "mem" buffer will be
+ * used by the application who requested the video stream.
+ * No memory allocation is needed because pixel order is modified directly
+ * while copying from "data" into "mem" buffer (i.e. in each call of this
+ * function), and not just once when the frame is already completed.
+ * This patch should work with all YUV image formats.
+ */
static void uvc_video_decode_data(struct uvc_video_device *video,
struct uvc_buffer *buf, const __u8 *data, int len)
{
struct uvc_video_queue *queue = &video->queue;
unsigned int maxlen, nbytes;
void *mem;
+ /* Patch variables */
+ unsigned int row_size, to_be_copied, shift_right;
if (len <= 0)
return;
/* Copy the video data to the buffer. */
+ /* How many bytes are needed to complete the buffer? */
maxlen = buf->buf.length - buf->buf.bytesused;
+ /* Where do pixels stored in "data" have to be copied? */
mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused;
+ /* How many bytes really can be copied into "mem"? */
nbytes = min((unsigned int)len, maxlen);
- memcpy(mem, data, nbytes);
- buf->buf.bytesused += nbytes;
+ /* "row_size" is the number of bytes required to store a full row of
+ * the frame.
+ */
+ row_size = video->streaming->cur_frame->wWidth *
+ video->streaming->format->bpp / 8;
+ /* Each loop "nbytes" is decremented of the number of bytes just copied.
+ * So are there any other bytes to be copied?
+ */
+ while (nbytes > 0) {
+ /* As the rows of modified frames have to be fulfilled from
+ * bottom-left to top-right, each cycle tries to complete a
+ * single row.
+ * In this cycle where is it needed to start to store bytes
+ * within the selected row? From the beginning or shifted
+ * right? Because other bytes could have been already stored in
+ * that row without completing it, so it could be needed a right
+ * shift.
+ */
+ shift_right = buf->buf.bytesused % row_size;
+ /* In this cycle how many byte can we copy in the selected row?
+ */
+ if (nbytes > row_size - shift_right)
+ to_be_copied = row_size - shift_right ;
+ else
+ to_be_copied = nbytes;
+ /* "queue->mem + buf->buf.m.offset" is the base-address where to
+ * start to store the current frame. This address refers to a
+ * preallocated area (just for a sigle frame) taking part in a
+ * circular buffer, where to store a fixed number of sequent
+ * frames.
+ */
+ memcpy(queue->mem + buf->buf.m.offset
+ /* Go to the end of this frame. */
+ + row_size * video->streaming->cur_frame->wHeight
+ /* Go back for the number of bytes corrisponding to the
+ * already fully completed rows.
+ */
+ - (buf->buf.bytesused - shift_right)
+ /* Go back at the starting point of the upper row. */
+ - row_size
+ /* Shift right on this row if it is needed. */
+ + shift_right,
+ data,
+ to_be_copied );
+ /* Update "data", "byteused" and "nbytes" values. */
+ data += to_be_copied;
+ buf->buf.bytesused += to_be_copied ;
+ nbytes -= to_be_copied;
+ }
/* Complete the current frame if the buffer size was exceeded. */
if (len > maxlen) {
uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n");
In questo caso essendo una funzione studiata ad-hoc per ottenere immagini ribaltate in modo speculare potrebbe essere che sostituendo la linea:
Codice: Seleziona tutto
pixel_size = video->streaming->cur_frame->wWidth* video->streaming->format->bpp / 8;
Codice: Seleziona tutto
pixel_size = video->streaming->format->bpp / 8;
Quanto prima proverò a fare anche questo test e vi farò sapere.
PATCH SOLUZIONE 2 (IMMAGINI NORMALI: NON A SPECCHIO)
Codice: Seleziona tutto
diff -uN UVCVIDEO_v0.1.0/uvc_video.c UVCVIDEO_patched/uvc_video.c
--- UVCVIDEO_v0.1.0/uvc_video.c 2008-06-26 10:41:01.000000000 +0200
+++ UVCVIDEO_patched/uvc_video.c 2008-06-26 14:03:58.000000000 +0200
@@ -371,23 +371,81 @@
return data[0];
}
+/* This patch should work ONLY with YUY2 image formats, also known as YUYV or
+ * YUV422 formats.
+ * This patched function allows to overturn video images from an upside-down
+ * orientation to a normal one. The conversion consists in copying 4 bytes at a
+ * time (Y0,U0,Y1,V0) corresponding to 2 pixels from the frame (coming from the
+ * video source) to the buffer that will be used by the application requesting
+ * the video stream. But in order to satisfy the YUY2 image format byte has to
+ * be copied in this way: Y1 U0 Y0 VO. Bytes are copied in a bottom-up
+ * direction into the reversed frame.
+ * "data" stores a sequence of pixels coming from the video source.
+ * This sequence is not a full frame or a full row of pixel, but just an
+ * ordered vector of pixels (from top-left to bottom-right), whose
+ * represents just an area of the current frame and which size ("nbytes") is
+ * not constant. In fact this function has to be called hundreds of times
+ * before a frame is completed. Each time "data" contains the next part of the
+ * current frame (upside-down). At the end data stored in "mem" buffer will be
+ * used by the application who requested the video stream.
+ * No memory allocation is needed because pixel order is modified directly
+ * while copying from "data" into "mem" buffer (i.e. in each call of this
+ * function), and not just once when the frame is already completed.
+ */
static void uvc_video_decode_data(struct uvc_video_device *video,
struct uvc_buffer *buf, const __u8 *data, int len)
{
struct uvc_video_queue *queue = &video->queue;
unsigned int maxlen, nbytes;
void *mem;
+ /* Patch variables */
+ unsigned int i, pixel_size;
+ __u8 *ptr_tmp;
if (len <= 0)
return;
/* Copy the video data to the buffer. */
+ /* How many bytes are needed to complete the buffer? */
maxlen = buf->buf.length - buf->buf.bytesused;
+ /* Where do pixels stored in "data" have to be copied? */
mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused;
+ /* How many bytes really can be copied into "mem"? */
nbytes = min((unsigned int)len, maxlen);
- memcpy(mem, data, nbytes);
- buf->buf.bytesused += nbytes;
+ /* "pixel_size" depens on the pixel color depth (bpp),
+ * but in YUY2 image format is constant and equal to 2.
+ */
+ pixel_size = video->streaming->format->bpp / 8;
+ /* In each loop 4 bytes are modified and copied into "mem" buffer. */
+ for (i = 0; i < nbytes; i += 2 * pixel_size) {
+ /* "queue->mem + buf->buf.m.offset" is the base-address
+ * where to start to store the current frame. This
+ * address refers to a preallocated area (just for a
+ * sigle frame) taking part in a circular buffer, where
+ * to store a fixed number of sequent frames.
+ */
+ ptr_tmp = (__u8 *)(queue->mem + buf->buf.m.offset
+ /* Go to the end of this frame. */
+ + video->streaming->cur_frame->wWidth * pixel_size
+ * video->streaming->cur_frame->wHeight
+ /* Go back for the number of already copied bytes. */
+ - buf->buf.bytesused
+ /* Go back for the number of bytes (4 bytes) to be
+ * copied in this cycle.
+ */
+ - 2 * pixel_size);
+ /* The order of copied bytes is changed from
+ * (Y0 U0 Y1 V1) to (Y1 U0 Y0 V1), i.e. from
+ * (#0 #1 #2 #3) to (#2 #1 #0 #3).
+ */
+ ptr_tmp[0] = ((__u8 *)(data + i))[2];
+ ptr_tmp[1] = ((__u8 *)(data + i))[1];
+ ptr_tmp[2] = ((__u8 *)(data + i))[0];
+ ptr_tmp[3] = ((__u8 *)(data + i))[3];
+ /* Update "byteused" value. */
+ buf->buf.bytesused += 2 * pixel_size;
+ }
/* Complete the current frame if the buffer size was exceeded. */
if (len > maxlen) {
uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Wow a questo punto il più grosso è stato fatto....
ora è solamente necessario compilare i sorgenti appena modificati e installare il nuovo modulo!!!
Per questo prendo dinuovo spunto dal sito http://ugaciaka.wordpress.com/2008/05/2 ... c-uvcvideo
[ grazie ugaciaka ]
(Aggiunta del 14/12/08)Preparazione e compilazione
Ora ci sposteremo nella directory trunk che abbiamo scaricato e poi compiliamo (ovviamente abbiamo gli header del kernel e build-essential…lo do per scontato eh)Modifichiamo il Makefile, questo perché ubuntu mette i driver compilati, per le periferiche video, in questa cartellaCodice: Seleziona tutto
cd trunk make
/lib/modules/$(uname -r)/ubuntu/media/usbvideo
Apriamo il Makefile con un editor di testo e cambiamo la riga
conCodice: Seleziona tutto
INSTALL_MOD_DIR := usb/media
Installazione e caricamento moduloCodice: Seleziona tutto
INSTALL_MOD_DIR := ubuntu/media/usbvideo
A questo punto è consigliabile tentare di rimuove l'enventuale modulo "uvcvideo" già caricato:
Codice: Seleziona tutto
sudo modprobe -r uvcvideo
Codice: Seleziona tutto
make
(Aggiunta del 14/12/08)Compiliamo tutto con il solito
Codice: Seleziona tutto
sudo make install
Codice: Seleziona tutto
sudo cp uvcvideo.ko /lib/modules/`uname -r`/ubuntu/media/usbvideo/
sudo cp uvcvideo.ko /lib/modules/`uname -r`/usb/media/
(Aggiunta del 14/12/08)e poi ovviamente carichiamo finalmente il modulo:
Codice: Seleziona tutto
sudo depmod -ae
sudo modprobe -f uvcvideo
Beh che dire,
a questo punto non bisogna fare altro che provare e incrociare le dita!!!
Ma soprattutto dovete farmi sapere se avete avuto dei problemi e quale delle due soluzioni vi è sembrata la migliore!!!
In bocca al lupo!!
Marco
ps.
Eccovi alcune fonti da cui pescare informazioni molto interessanti:
http://linux-uvc.berlios.de/ -- Home page di UVCVIDEO dove trovare tutte le informazioni relative uvc driver
http://www.ibiblio.org/pub/Linux/docs/H ... bcam-HOWTO
http://www.linuxtv.org/v4lwiki/index.php/Main_Page -- Per tutte le info relative al driver V4L2 su cui si basa UVCVIDEO