]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - apps/dual-camera-demo.git/blobdiff - loopback.cpp
Fix for DSS scaling error on very small screens
[apps/dual-camera-demo.git] / loopback.cpp
index 09dee7312e5820e9ea0f771f83cdfb24c6e97162..18a1cf352feaa7046715a34531d8d01c7cae0969 100644 (file)
@@ -32,7 +32,9 @@ struct fbdev_device_info
     int height;
     char dev_name[9];
     char name[4];
+    int buf_current;
     char *buf_start;
+    char *buf_start2;
     unsigned int buf_length;
 
     struct fb_fix_screeninfo fixinfo;
@@ -40,6 +42,9 @@ struct fbdev_device_info
     struct omapfb_display_info di;
     struct omapfb_mem_info mi;
     struct omapfb_plane_info pi;
+    struct omapfb_caps caps;
+
+    enum omapfb_update_mode update_mode;
 
 } fb1_device, fb2_device;
 
@@ -79,12 +84,6 @@ static int v4l2_init_device(struct v4l2_device_info *device)
 
     printf("\n%s: Opened Channel\n", device->name);
 
-    /* Set the format of the capture device */
-    device->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
-
-    device->width = 800;
-    device->height = 600;
-
     /* Check if the device is capable of streaming */
     if (ioctl(device->fd, VIDIOC_QUERYCAP, &capability) < 0) {
         perror("VIDIOC_QUERYCAP");
@@ -154,13 +153,6 @@ static int v4l2_init_device(struct v4l2_device_info *device)
         }
         printf("temp_buffers[%d].start - %x\n", i,
                 (unsigned int)temp_buffers[i].start);
-
-        ret = ioctl(device->fd, VIDIOC_QBUF, &buf);
-        if (ret < 0) {
-            perror("VIDIOC_QBUF");
-            device->num_buffers = i;
-            goto ERROR1;
-        }
     }
 
     device->buffers = temp_buffers;
@@ -201,7 +193,26 @@ static void v4l2_exit_device(struct v4l2_device_info *device)
  */
 static int v4l2_stream_on(struct v4l2_device_info *device)
 {
-    int a, ret;
+    int a, i, ret;
+
+    for (i = 0; i < device->num_buffers; ++i) {
+            struct v4l2_buffer buf;
+
+            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+            buf.memory = V4L2_MEMORY_MMAP;
+            buf.index = i;
+
+            ret = ioctl(device->fd, VIDIOC_QBUF, &buf);
+            if (ret < 0) {
+                perror("VIDIOC_QBUF");
+                device->num_buffers = i;
+                return -1;
+            }
+    }
+
+    device->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    device->buf.index = 0;
+    device->buf.memory = device->memory_mode;
 
     a = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     ret = ioctl(device->fd, VIDIOC_STREAMON, &a);
@@ -211,10 +222,6 @@ static int v4l2_stream_on(struct v4l2_device_info *device)
     }
     printf("%s: Stream on\n", device->name);
 
-    device->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-    device->buf.index = 0;
-    device->buf.memory = device->memory_mode;
-
     return 0;
 }
 
@@ -236,6 +243,88 @@ static int v4l2_stream_off(struct v4l2_device_info *device)
     return 0;
 }
 
+/*
+ * Queue V4L2 buffer
+ */
+static int v4l2_queue_buffer(struct v4l2_device_info *device)
+{
+    int ret;
+    fd_set fds;
+    struct timeval tv;
+
+    FD_ZERO(&fds);
+    FD_SET(device->fd, &fds);
+
+    /* Timeout. */
+    tv.tv_sec = 10;
+    tv.tv_usec = 0;
+
+    ret = select(device->fd + 1, &fds, NULL, NULL, &tv);
+
+    if (-1 == ret) {
+        if (EINTR == errno) {
+            perror( "select\n");
+            return -1;
+        }
+    }
+
+    if (0 == ret) {
+        perror( "select timeout\n");
+        return -1;
+    }
+
+    /* Queue buffer for the v4l2 capture device */
+    ret = ioctl(device->fd, VIDIOC_QBUF,
+            &device->buf);
+    if (ret < 0) {
+        perror("VIDIOC_QBUF");
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * DeQueue V4L2 buffer
+ */
+static int v4l2_dequeue_buffer(struct v4l2_device_info *device)
+{
+    int ret;
+    fd_set fds;
+    struct timeval tv;
+
+    FD_ZERO(&fds);
+    FD_SET(device->fd, &fds);
+
+    /* Timeout. */
+    tv.tv_sec = 10;
+    tv.tv_usec = 0;
+
+    ret = select(device->fd + 1, &fds, NULL, NULL, &tv);
+
+    if (-1 == ret) {
+        if (EINTR == errno) {
+            perror( "select\n");
+            return -1;
+        }
+    }
+
+    if (0 == ret) {
+        perror( "select timeout\n");
+        return -1;
+    }
+
+    /* Dequeue buffer for the v4l2 capture device */
+    ret = ioctl(device->fd, VIDIOC_DQBUF,
+            &device->buf);
+    if (ret < 0) {
+        perror("VIDIOC_DQBUF");
+        return -1;
+    }
+
+    return 0;
+}
+
 /*
  * Fbdev device init
  */
@@ -264,8 +353,6 @@ static int fbdev_init_device(struct fbdev_device_info *device)
         exit(0);
     }
 
-    printf("%s resolution: %dx%d\n",device->name, device->di.xres, device->di.yres);
-
     /* Now the memory for the fb device needs to be set up */
 
     if (ioctl(device->fd, OMAPFB_QUERY_PLANE, &device->pi)) {
@@ -281,7 +368,7 @@ static int fbdev_init_device(struct fbdev_device_info *device)
         }
     }
 
-    printf("Display Screen Properties:\n");
+    printf("\nDisplay Screen Properties:\n");
     printf("%s bpp: %d\n",device->name, device->varinfo.bits_per_pixel);
     printf("%s di resolution: %dx%d\n",device->name,
            device->di.xres, device->di.yres);
@@ -290,13 +377,18 @@ static int fbdev_init_device(struct fbdev_device_info *device)
     status.display_xres = device->di.xres;
     status.display_yres = device->di.yres;
 
+    if (device->di.xres < 640) {
+        device->width = 640;
+        device->height = 480;
+    }
+
     /* Set the input resolution of the video framebuffer to be the size
      * of the camera image.  Later, the output size will be matched to
      * the display to allow for up/down scaling */
-    device->varinfo.xres = 800;
-    device->varinfo.yres = 600;
-    device->varinfo.xres_virtual = 800;
-    device->varinfo.yres_virtual = 600;
+    device->varinfo.xres = device->width;
+    device->varinfo.yres = device->height;
+    device->varinfo.xres_virtual = device->width;
+    device->varinfo.yres_virtual = 2 * device->height;
 
     device->varinfo.bits_per_pixel = 16;
     device->varinfo.nonstd = OMAPFB_COLOR_YUY422;
@@ -328,6 +420,16 @@ static int fbdev_init_device(struct fbdev_device_info *device)
     device->pi.out_height = device->di.yres;
     device->pi.enabled = 1;
 
+    /* Get device capabilities */
+    ioctl(device->fd,OMAPFB_GET_CAPS, &device->caps);
+
+    printf("Checking for manual update capability...\n");
+    if (device->caps.ctrl & OMAPFB_CAPS_MANUAL_UPDATE)
+            printf("Manual update supported\n");
+
+    ioctl(device->fd,OMAPFB_GET_UPDATE_MODE, &device->update_mode);
+    printf("OMAPFB_GET_UPDATE_MODE: %d\n", device->update_mode);
+
     if (ioctl(device->fd, OMAPFB_SETUP_PLANE, &device->pi)) {
         printf("OMAPFB_SETUP_PLANE error for: %s\n", device->name);
         exit(0);
@@ -352,10 +454,16 @@ static int fbdev_init_device(struct fbdev_device_info *device)
                 device->fixinfo.line_length);
 
     /* Mmap the driver buffers in application space so that application
-     * can write on to them */
+     * can write on to them.  Buffers need to be 2x for double buffering
+     * to eliminate tearing
+     */
     device->buf_length = device->fixinfo.line_length * device->varinfo.yres_virtual;
     device->buf_start = (char *)mmap (0, device->buf_length,
             (PROT_READ|PROT_WRITE), MAP_SHARED, device->fd, 0);
+    device->buf_start2 = device->buf_start + device->buf_length/2;
+
+    printf("1st buffer addr: %x\n", (unsigned int)device->buf_start);
+    printf("2nd buffer addr: %x\n", (unsigned int)device->buf_start2);
 
     if (device->buf_start == MAP_FAILED) {
         printf("MMap failed for %s\n", device->name);
@@ -382,9 +490,9 @@ static void fbdev_exit_device(struct fbdev_device_info *device)
 }
 
 /*
- * Set up the DSS for PiP plane
+ * Set up the DSS for blending of video and graphics planes
  */
-static int fbdev_init_pip(void)
+static int fbdev_init_dss(void)
 {
     FILE *f;
 
@@ -397,10 +505,12 @@ static int fbdev_init_pip(void)
     fprintf(f,"%d", 1);
     fclose(f);
 
-    /* Set scalar for PiP video plane to 1/2 */
+    /* Set scalar for PiP video plane to 1/3 of main video plane */
     fb2_device.pi.enabled = 0;
-    fb2_device.pi.out_width = fb1_device.pi.out_width/2;
-    fb2_device.pi.out_height = fb1_device.pi.out_height/2;
+    fb2_device.pi.out_width = fb1_device.pi.out_width/3;
+    fb2_device.pi.out_height = fb1_device.pi.out_height/3;
+    fb2_device.pi.pos_x = 25;
+    fb2_device.pi.pos_y = 25;
     fb2_device.pi.enabled = 1;
 
     if (ioctl(fb2_device.fd, OMAPFB_SETUP_PLANE, &fb2_device.pi)) {
@@ -414,7 +524,7 @@ static int fbdev_init_pip(void)
 /*
  * Fbdev enable pip layer
  */
-static int fbdev_enable_pip(void)
+int fbdev_enable_pip(void)
 {
     fb2_device.pi.enabled = 1;
     if (ioctl(fb2_device.fd, OMAPFB_SETUP_PLANE, &fb2_device.pi)) {
@@ -428,7 +538,7 @@ static int fbdev_enable_pip(void)
 /*
  * Fbdev disable pip layer
  */
-static int fbdev_disable_pip(void)
+int fbdev_disable_pip(void)
 {
     fb2_device.pi.enabled = 0;
     if (ioctl(fb2_device.fd, OMAPFB_SETUP_PLANE, &fb2_device.pi)) {
@@ -444,77 +554,103 @@ static int fbdev_disable_pip(void)
  */
 static int display_frame(struct v4l2_device_info *v4l2_device, struct fbdev_device_info *fbdev_device)
 {
-    int ret, fb_size = fbdev_device->fixinfo.line_length*fbdev_device->varinfo.yres;
-    fd_set fds;
-    struct timeval tv;
-
-    FD_ZERO(&fds);
-    FD_SET(v4l2_device->fd, &fds);
-
-    /* Timeout. */
-    tv.tv_sec = 10;
-    tv.tv_usec = 0;
-
-    ret = select(v4l2_device->fd + 1, &fds, NULL, NULL, &tv);
-
-    if (-1 == ret) {
-        if (EINTR == errno) {
-            perror( "select\n");
-            return -1;
+    int fb_size = fbdev_device->fixinfo.line_length*fbdev_device->varinfo.yres;
+
+    /* Request a capture buffer from the driver that can be copied to framebuffer */
+    v4l2_dequeue_buffer(v4l2_device);
+
+    /* Copy the new camera frame into the buffer that is not currently displayed */
+    if (fbdev_device->buf_current == 0)
+    {
+        /* Copy the contents of the v4l2 capture buffer to framebuffer */
+        memcpy(fbdev_device->buf_start2,
+        v4l2_device->buffers[v4l2_device->buf.index].start,
+        fb_size);
+
+        /* After the buffer copy is complete, tell the DSS to display the new data */
+        fbdev_device->varinfo.yoffset = 600;
+        ioctl(fbdev_device->fd,FBIOPAN_DISPLAY, fbdev_device->varinfo);
+        if (ioctl(fbdev_device->fd, FBIOPUT_VSCREENINFO, &fbdev_device->varinfo) < 0) {
+            printf("Error setting variable information for: %s\n", fbdev_device->name);
+            exit(0);
         }
+        fbdev_device->buf_current=1;
     }
-
-    if (0 == ret) {
-        perror( "select timeout\n");
-        return -1;
+    else {
+        /* Copy the contents of the v4l2 capture buffer to framebuffer */
+        memcpy(fbdev_device->buf_start,
+        v4l2_device->buffers[v4l2_device->buf.index].start,
+        fb_size);
+
+        /* After the buffer copy is complete, tell the DSS to display the new data */
+        fbdev_device->varinfo.yoffset = 0;
+        ioctl(fbdev_device->fd,FBIOPAN_DISPLAY, fbdev_device->varinfo);
+        if (ioctl(fbdev_device->fd, FBIOPUT_VSCREENINFO, &fbdev_device->varinfo) < 0) {
+            printf("Error setting variable information for: %s\n", fbdev_device->name);
+            exit(0);
+        }
+        fbdev_device->buf_current=0;
     }
 
-    /* Dequeue buffer for the v4l2 capture device */
-    ret = ioctl(v4l2_device->fd, VIDIOC_DQBUF,
-            &v4l2_device->buf);
-    if (ret < 0) {
-        perror("VIDIOC_DQBUF");
-        return -1;
-    }
 
-    /* Wait for VSYNC */
-    ioctl(fbdev_device->fd, OMAPFB_WAITFORGO);
+    /* Give the buffer back to the driver so it can be filled again */
+    v4l2_queue_buffer(v4l2_device);
 
-    /* Copy the contents of the v4l2 capture buffer to framebuffer */
-    memcpy(fbdev_device->buf_start,
-    v4l2_device->buffers[v4l2_device->buf.index].start,
-    fb_size);
+    return 0;
+}
 
-    /* Queue buffer for the v4l2 capture device */
-    ret = ioctl(v4l2_device->fd, VIDIOC_QBUF,
-            &v4l2_device->buf);
-    if (ret < 0) {
-        perror("VIDIOC_QBUF");
-        return -1;
-    }
+/*
+ * Capture v4l2 frame and save to jpeg
+ */
+static int capture_frame(struct v4l2_device_info *v4l2_device)
+{
+    /* Request a capture buffer from the driver that can be copied to framebuffer */
+    v4l2_dequeue_buffer(v4l2_device);
+
+    jpegWrite((unsigned char *)v4l2_device->buffers[cap0_device.buf.index].start,
+            status.num_jpeg, v4l2_device->width, v4l2_device->height);
+
+    /* Give the buffer back to the driver so it can be filled again */
+    v4l2_queue_buffer(v4l2_device);
 
     return 0;
 }
 
 void default_parameters(void)
 {
+    /* Main camera display */
     strcpy(fb1_device.dev_name,"/dev/fb1");
     strcpy(fb1_device.name,"fb1");
+    fb1_device.width=800;
+    fb1_device.height=600;
+    fb1_device.buf_current=0;
 
+    /* PiP camera display */
     strcpy(fb2_device.dev_name,"/dev/fb2");
     strcpy(fb2_device.name,"fb2");
+    fb2_device.width=800;
+    fb2_device.height=600;
+    fb2_device.buf_current=0;
 
+    /* Main camera */
     cap0_device.memory_mode = V4L2_MEMORY_MMAP;
     cap0_device.num_buffers = 3;
     strcpy(cap0_device.dev_name,"/dev/video0");
     strcpy(cap0_device.name,"Capture 0");
     cap0_device.buffers = NULL;
+    cap0_device.fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+    cap0_device.width = 800;
+    cap0_device.height = 600;
 
+    /* PiP camera */
     cap1_device.memory_mode = V4L2_MEMORY_MMAP;
     cap1_device.num_buffers = 3;
     strcpy(cap1_device.dev_name,"/dev/video1");
     strcpy(cap1_device.name,"Capture 1");
     cap1_device.buffers = NULL;
+    cap1_device.fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+    cap1_device.width = 800;
+    cap1_device.height = 600;
 
     /* Set the default parameters for device options */
     status.main_cam=0;
@@ -532,13 +668,13 @@ void default_parameters(void)
 
 void exit_devices(void)
 {
+    fbdev_exit_device(&fb1_device);
+    fbdev_exit_device(&fb2_device);
+
     v4l2_exit_device(&cap0_device);
     if (status.num_cams==2) {
         v4l2_exit_device(&cap1_device);
     }
-
-    fbdev_exit_device(&fb1_device);
-    fbdev_exit_device(&fb2_device);
 }
 
 void end_streaming(void)
@@ -562,8 +698,18 @@ int init_loopback(void)
     if (fbdev_init_device(&fb1_device)) goto Error;
     if (fbdev_init_device(&fb2_device)) goto Error;
 
-    /* Set the scalar for PiP */
-    if (fbdev_init_pip() < 0 ) goto Error;
+    /* Check to see if the display resolution is very small.  If so, the
+     * camera capture resolution needs to be lowered so that the scaling
+     * limits of the DSS are not reached */
+    if (fb1_device.di.xres < 640) {
+        /* Set capture 0 device resolution */
+        cap0_device.width = 640;
+        cap0_device.height = 480;
+
+        /* Set capture 1 device resolution */
+        cap1_device.width = 640;
+        cap1_device.height = 480;
+    }
 
     /* Initialize the v4l2 capture devices */
     if (v4l2_init_device(&cap0_device) < 0) goto Error;
@@ -571,7 +717,6 @@ int init_loopback(void)
         /* If there is not a second camera, program can still continue */
         status.num_cams=1;
         status.pip=false;
-        fbdev_disable_pip();
         printf("Only one camera detected\n");
     }
 
@@ -581,6 +726,14 @@ int init_loopback(void)
         if (v4l2_stream_on(&cap1_device) < 0) goto Error;
     }
 
+    /* Configure the DSS to blend video and graphics layers */
+    if (fbdev_init_dss() < 0 ) goto Error;
+
+    if (status.pip==false)
+        fbdev_disable_pip();
+    else
+        fbdev_enable_pip();
+
     return 0;
 
 Error:
@@ -595,36 +748,27 @@ Error:
  */
 void process_frame(void) {
 
-    /* Display main camera */
+    /* Display the main camera */
     if (status.main_cam==0)
         display_frame(&cap0_device, &fb1_device);
     else
         display_frame(&cap1_device, &fb1_device);
 
-    // Display PiP if enabled
+    /* Display PiP if enabled */
     if (status.pip==true) {
-        fbdev_enable_pip();
-
         if (status.main_cam==0)
             display_frame(&cap1_device, &fb2_device);
         else
             display_frame(&cap0_device, &fb2_device);
     }
-    else
-        fbdev_disable_pip();
-
 
-    // Save jpeg image if triggered
+    /* Save jpeg image if triggered */
     if (status.jpeg==true) {
         if (status.main_cam==0) {
-            jpegWrite((unsigned char *)cap0_device.buffers[cap0_device.buf.index].start,
-                    status.num_jpeg);
-
+            capture_frame(&cap0_device);
         }
         else {
-            jpegWrite((unsigned char *)cap1_device.buffers[cap1_device.buf.index].start,
-                    status.num_jpeg);
-
+            capture_frame(&cap1_device);
         }
 
         status.jpeg=false;