]> git.sesse.net Git - nageru/commitdiff
Add a hack to FFmpegCapture for decoding Futatabi's Y'CbCr streams correctly.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 5 Dec 2018 19:20:08 +0000 (20:20 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 5 Dec 2018 19:20:08 +0000 (20:20 +0100)
nageru/ffmpeg_capture.cpp
nageru/ffmpeg_capture.h

index e5cb096c5720f3eee227fc56a6bc2b03f2fdfba9..c8b87fee9628fa67776350f619768468d8e91e97 100644 (file)
@@ -134,7 +134,7 @@ AVPixelFormat decide_dst_format(AVPixelFormat src_format, bmusb::PixelFormat dst
        return av_get_pix_fmt(best_format);
 }
 
-YCbCrFormat decode_ycbcr_format(const AVPixFmtDescriptor *desc, const AVFrame *frame)
+YCbCrFormat decode_ycbcr_format(const AVPixFmtDescriptor *desc, const AVFrame *frame, bool is_mjpeg)
 {
        YCbCrFormat format;
        AVColorSpace colorspace = av_frame_get_colorspace(frame);
@@ -198,6 +198,17 @@ YCbCrFormat decode_ycbcr_format(const AVPixFmtDescriptor *desc, const AVFrame *f
                break;
        }
 
+       if (is_mjpeg && !format.full_range) {
+               // Limited-range MJPEG is only detected by FFmpeg whenever a special
+               // JPEG comment is set, which means that in practice, the stream is
+               // almost certainly generated by Futatabi. Override FFmpeg's forced
+               // MJPEG defaults (it disregards the values set in the mux) with what
+               // Futatabi sets.
+               format.luma_coefficients = YCBCR_REC_709;
+               format.cb_x_position = 0.0;
+               format.cb_y_position = 0.5;
+       }
+
        format.cr_x_position = format.cb_x_position;
        format.cr_y_position = format.cb_y_position;
        return format;
@@ -404,6 +415,9 @@ bool FFmpegCapture::play_video(const string &pathname)
        unique_ptr<AVCodecContext, decltype(avcodec_close)*> video_codec_ctx_cleanup(
                video_codec_ctx.get(), avcodec_close);
 
+       // Used in decode_ycbcr_format().
+       is_mjpeg = video_codecpar->codec_id == AV_CODEC_ID_MJPEG;
+
        // Open audio decoder, if we have audio.
        AVCodecContextWithDeleter audio_codec_ctx;
        if (audio_stream_index != -1) {
@@ -848,7 +862,7 @@ UniqueFrame FFmpegCapture::make_video_frame(const AVFrame *frame, const string &
                video_frame->len = (width * 2) * height;
 
                const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(sws_dst_format);
-               current_frame_ycbcr_format = decode_ycbcr_format(desc, frame);
+               current_frame_ycbcr_format = decode_ycbcr_format(desc, frame, is_mjpeg);
        } else {
                assert(pixel_format == bmusb::PixelFormat_8BitYCbCrPlanar);
                const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(sws_dst_format);
@@ -867,7 +881,7 @@ UniqueFrame FFmpegCapture::make_video_frame(const AVFrame *frame, const string &
 
                video_frame->len = width * height + 2 * chroma_width * chroma_height;
 
-               current_frame_ycbcr_format = decode_ycbcr_format(desc, frame);
+               current_frame_ycbcr_format = decode_ycbcr_format(desc, frame, is_mjpeg);
        }
        sws_scale(sws_ctx.get(), frame->data, frame->linesize, 0, frame->height, pic_data, linesizes);
 
index 31e94ab41fefc103b150b5319877554450dfa720..b0a1be2d542ce3b231bb4efe1263f43793c8b424 100644 (file)
@@ -255,6 +255,7 @@ private:
        int sws_last_width = -1, sws_last_height = -1, sws_last_src_format = -1;
        AVPixelFormat sws_dst_format = AVPixelFormat(-1);  // In practice, always initialized.
        AVRational video_timebase, audio_timebase;
+       bool is_mjpeg = false;
 
        QuittableSleeper producer_thread_should_quit;
        std::thread producer_thread;