i-rinat / freshplayerplugin

ppapi2npapi compatibility layer
MIT License
727 stars 52 forks source link

Some webcams return green screen #370

Open i-rinat opened 6 years ago

i-rinat commented 6 years ago

Currently, libv4lconvert from libv4l is used for image capture. It simplifies conversion from numerous formats various webcams support to the desired V4L2_PIX_FMT_YUV420. It turned out that some webcams have MJPG as a primary format, with resolution which is not multiple of 8, dct block size (960x540 for example). It turned out that libv4lconvert does not expect such resolution and refuses to work.

I can see two possible solutions. First is to read RGB24 from libv4lconvert, and then convert it back to YUV420. Second is to fix non-8-multiple heights in libv4lconvert.

i-rinat commented 6 years ago

Here is an idea for a patch.

diff --git a/lib/libv4lconvert/jpeg.c b/lib/libv4lconvert/jpeg.c
index 15f8dec7..4f4af582 100644
--- a/lib/libv4lconvert/jpeg.c
+++ b/lib/libv4lconvert/jpeg.c
@@ -238,14 +238,30 @@ static int decode_libjpeg_h_samp2(struct v4lconvert_data *data,
    struct jpeg_decompress_struct *cinfo = &data->cinfo;
    int y;
    unsigned int width = cinfo->image_width;
+   unsigned char *drain_buf;
    JSAMPROW y_rows[16], u_rows[8], v_rows[8];
    JSAMPARRAY rows[3] = { y_rows, u_rows, v_rows };

+   drain_buf = v4lconvert_alloc_buffer(width,
+                       &data->convert_pixfmt_buf,
+                       &data->convert_pixfmt_buf_size);
+   if (!drain_buf)
+       return v4lconvert_oom_error(data);
+
    while (cinfo->output_scanline < cinfo->image_height) {
-       for (y = 0; y < 8 * v_samp; y++) {
+       int last_y = cinfo->image_height - cinfo->output_scanline;
+       int last_uv;
+
+       last_y = last_y < 8 * v_samp ? last_y : 8 * v_samp;
+       last_uv = last_y / v_samp;
+
+       for (y = 0; y < last_y; y++) {
            y_rows[y] = ydest;
            ydest += width;
        }
+       for (; y < 8 * v_samp; y++)
+           y_rows[y] = drain_buf;
+
        /*
         * For v_samp == 1 were going to get 1 set of uv values per
         * line, but we need only 1 set per 2 lines since our output
@@ -253,7 +269,7 @@ static int decode_libjpeg_h_samp2(struct v4lconvert_data *data,
         * effectively using the second set for each output line.
         */
        if (v_samp == 1) {
-           for (y = 0; y < 8; y++) {
+           for (y = 0; y < last_uv; y++) {
                u_rows[y] = udest;
                v_rows[y] = vdest;
                y++;
@@ -262,13 +278,18 @@ static int decode_libjpeg_h_samp2(struct v4lconvert_data *data,
                udest += width / 2;
                vdest += width / 2;
            }
+           for (; y < 8; y++)
+               u_rows[y] = v_rows[y] = drain_buf;
+
        } else { /* v_samp == 2 */
-           for (y = 0; y < 8; y++) {
+           for (y = 0; y < last_uv; y++) {
                u_rows[y] = udest;
                v_rows[y] = vdest;
                udest += width / 2;
                vdest += width / 2;
            }
+           for (; y < 8; y++)
+               u_rows[y] = v_rows[y] = drain_buf;
        }

        y = jpeg_read_raw_data(cinfo, rows, 8 * v_samp);
@@ -390,12 +411,14 @@ int v4lconvert_decode_jpeg_libjpeg(struct v4lconvert_data *data,
        }

        /* We don't want any padding as that may overflow our dest */
+#if 0
        if (width % (8 * h_samp) || height % (8 * v_samp)) {
            V4LCONVERT_ERR(
                "resolution is not a multiple of dctsize");
            errno = EIO;
            return -1;
        }
+#endif

        if (dest_pix_fmt == V4L2_PIX_FMT_YVU420) {
            vdest = dest + width * height;