Hi everybody,
it's a very quick how-to that will help you to solve the problem of those webcams who give back upside-down images/videos!!!
This help is intended only for those who have a UVC capable webcam.
But it will be usefull only if your webcam supports YUV image format and the applications that use your webcam request YUV format to your webcam!!! I've tested it and it works with:
skype, amsn, kopete, luvcview, mplayer!!
So...let's start!!!!
In order to know if your webcam is UVC capable you just need to open your shell and run:
you will get something like this:
Bus 002 Device 001: ID 0000:0000
Bus 001 Device 002: ID xxxx:yyyy "Your_Webcam_Model"
Bus 001 Device 001: ID 0000:0000
Then type:
Code:
sudo lsusb -d xxxx:yyyy -v | grep "14 Video"
If you get something like the following, your webcam is UVC capable:
bFunctionClass 14 Video
bInterfaceClass 14 Video
bInterfaceClass 14 Video
bInterfaceClass 14 Video
If your webcam passes this test, you can go on reading. Otherwise, your camera needs to use propertary driver, because it doesn't use standard protocol/command.
Well,
now you need to donwload the UVCVIDEO driver sources, but they are on a SVN repository, so you need to install the SVN client:
Code:
sudo apt-get install subversion
and now you can download the sources:
Code:
svn checkout svn://svn.berlios.de/linux-uvc/linux-uvc/trunk
The sources will be saved into a folder called "Trunk" in the path from where you run the previous command.
Now there are 2 solutions, please try both and tell me which one you think is the best!!
Now for both solutions there are two patches: the former gives back mirrored images, the latter NOT mirrored images.
You have to copy one of them into "Trunk" folder in a file (called for example "uvc_video_solution1.patch"), and then from terminal go into the "Trunk" folder and do:
Code:
patch < uvc_video_solution1.patch
WARNING: each of the following patch refers to the original "uvc_video.c" file, so before applying a new patch be sure that the "uvc_video.c" file in "Trunk" folder is the original one. So please remember to backup the original file.
WARNING:
YOU HAVE TO ADD A BLANK LINE AT THE END OF EACH PATCH IN ORDER TO SUCCEED THE PATCHING!!
NEWS:
NOW YOU CAN DOWNLOAD THE PATCHES AND DON'T NEED ANY MORE TO ADD A NEW LINE!!!
TAKE A LOOK AT THE BOTTOM OF THIS HOW-TO, YOU WILL FIND 4 PATCHES, JUST CHOOSE THE ONE YOU PREFER AND DONWLOAD IT!!
WARNING: Patches available in this HowTo refer to the UVC driver version 0.1.0, so if you install a different version you will have to apply the patch by hand!!!
FIRST SOLUTION (MIRRORED IMAGES)
# modified June 26, 2008
Code:
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");
FIRST SOLUTION (NOT MIRRORED IMAGES)
# modified June 26, 2008
Code:
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");
SECOND SOLUTION (MIRRORED IMAGES)
# modified June 26, 2008
Code:
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");
SECOND SOLUTION (NOT MIRRORED IMAGES)
# modified June 26, 2008
Code:
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");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~
Well, now the worst part has been done!!!
We just need to compile our modded file and to install the new driver, so from shell you have to go to the "Trunk" directory and type:
there shouldn't be errors!!
Then, ONLY if you are using one of the Ubuntu distributions (ubuntu, kubuntu, etc.), open with you editor the "Makefile" and change the following line:
Code:
INSTALL_MOD_DIR := usb/media
with
Code:
INSTALL_MOD_DIR := ubuntu/media/usbvideo
Now we just need to remove uvcvideo module (if you have previously installed it):
Code:
sudo modprobe -r uvcvideo
Then:
Code:
sudo make install
sudo cp uvcvideo.ko /lib/modules/`uname -r`/ubuntu/media/usbvideo/
sudo cp uvcvideo.ko /lib/modules/`uname -r`/usb/media/
sudo depmod -ae
sudo modprobe -f uvcvideo
Now everything should work!!!
Let me know!!
And please try both solutions..
in case you don't see any difference just use the SECOND solution (mirrored or not, has you prefer), it should be the safest!!
Good luck,
enjoy your webcam!!
Arjos85
ps. excuse me for this too quick HOW-TO, without good explainations, but really just right now I haven't time to do it more verbose!!!
pps. excuse me also in case I made some mistakes!!!
Bookmarks