]> git.sesse.net Git - nageru/blobdiff - nageru/ffmpeg_capture.cpp
Remove unused variable in ALSAInput.
[nageru] / nageru / ffmpeg_capture.cpp
index 7bd927801359b5906db8cec4641557c1898a82ed..1c7a30f2e82e1245574174420a145c7fa8b8ee0f 100644 (file)
@@ -28,12 +28,12 @@ extern "C" {
 #include <vector>
 
 #include "bmusb/bmusb.h"
-#include "ffmpeg_raii.h"
+#include "shared/ffmpeg_raii.h"
 #include "ffmpeg_util.h"
 #include "flags.h"
 #include "image_input.h"
 #include "ref_counted_frame.h"
-#include "timebase.h"
+#include "shared/timebase.h"
 
 #define FRAME_SIZE (8 << 20)  // 8 MB.
 
@@ -134,10 +134,10 @@ 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);
+       AVColorSpace colorspace = frame->colorspace;
        switch (colorspace) {
        case AVCOL_SPC_BT709:
                format.luma_coefficients = YCBCR_REC_709;
@@ -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;
@@ -220,7 +231,7 @@ FFmpegCapture::~FFmpegCapture()
        if (has_dequeue_callbacks) {
                dequeue_cleanup_callback();
        }
-       avresample_free(&resampler);
+       swr_free(&resampler);
 }
 
 void FFmpegCapture::configure_card()
@@ -365,10 +376,7 @@ bool FFmpegCapture::play_video(const string &pathname)
                last_modified = buf.st_mtim;
        }
 
-       AVDictionary *opts = nullptr;
-       av_dict_set(&opts, "fflags", "nobuffer", 0);
-
-       auto format_ctx = avformat_open_input_unique(pathname.c_str(), nullptr, &opts, AVIOInterruptCB{ &FFmpegCapture::interrupt_cb_thunk, this });
+       auto format_ctx = avformat_open_input_unique(pathname.c_str(), nullptr, nullptr, AVIOInterruptCB{ &FFmpegCapture::interrupt_cb_thunk, this });
        if (format_ctx == nullptr) {
                fprintf(stderr, "%s: Error opening file\n", pathname.c_str());
                return false;
@@ -386,6 +394,8 @@ bool FFmpegCapture::play_video(const string &pathname)
        }
 
        int audio_stream_index = find_stream_index(format_ctx.get(), AVMEDIA_TYPE_AUDIO);
+       int subtitle_stream_index = find_stream_index(format_ctx.get(), AVMEDIA_TYPE_SUBTITLE);
+       has_last_subtitle = false;
 
        // Open video decoder.
        const AVCodecParameters *video_codecpar = format_ctx->streams[video_stream_index]->codecpar;
@@ -407,6 +417,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) {
@@ -444,7 +457,7 @@ bool FFmpegCapture::play_video(const string &pathname)
                int64_t audio_pts;
                bool error;
                AVFrameWithDeleter frame = decode_frame(format_ctx.get(), video_codec_ctx.get(), audio_codec_ctx.get(),
-                       pathname, video_stream_index, audio_stream_index, audio_frame.get(), &audio_format, &audio_pts, &error);
+                       pathname, video_stream_index, audio_stream_index, subtitle_stream_index, audio_frame.get(), &audio_format, &audio_pts, &error);
                if (error) {
                        return false;
                }
@@ -625,7 +638,7 @@ namespace {
 }  // namespace
 
 AVFrameWithDeleter FFmpegCapture::decode_frame(AVFormatContext *format_ctx, AVCodecContext *video_codec_ctx, AVCodecContext *audio_codec_ctx,
-       const std::string &pathname, int video_stream_index, int audio_stream_index,
+       const std::string &pathname, int video_stream_index, int audio_stream_index, int subtitle_stream_index,
        FrameAllocator::Frame *audio_frame, AudioFormat *audio_format, int64_t *audio_pts, bool *error)
 {
        *error = false;
@@ -661,6 +674,9 @@ AVFrameWithDeleter FFmpegCapture::decode_frame(AVFormatContext *format_ctx, AVCo
                                        *error = true;
                                        return AVFrameWithDeleter(nullptr);
                                }
+                       } else if (pkt.stream_index == subtitle_stream_index) {
+                               last_subtitle = string(reinterpret_cast<const char *>(pkt.data), pkt.size);
+                               has_last_subtitle = true;
                        }
                } else {
                        eof = true;  // Or error, but ignore that for the time being.
@@ -742,22 +758,24 @@ void FFmpegCapture::convert_audio(const AVFrame *audio_avframe, FrameAllocator::
            audio_avframe->format != last_src_format ||
            dst_format != last_dst_format ||
            channel_layout != last_channel_layout ||
-           av_frame_get_sample_rate(audio_avframe) != last_sample_rate) {
-               avresample_free(&resampler);
-               resampler = avresample_alloc_context();
+           audio_avframe->sample_rate != last_sample_rate) {
+               swr_free(&resampler);
+               resampler = swr_alloc_set_opts(nullptr,
+                                              /*out_ch_layout=*/AV_CH_LAYOUT_STEREO_DOWNMIX,
+                                              /*out_sample_fmt=*/dst_format,
+                                              /*out_sample_rate=*/OUTPUT_FREQUENCY,
+                                              /*in_ch_layout=*/channel_layout,
+                                              /*in_sample_fmt=*/AVSampleFormat(audio_avframe->format),
+                                              /*in_sample_rate=*/audio_avframe->sample_rate,
+                                              /*log_offset=*/0,
+                                              /*log_ctx=*/nullptr);
+
                if (resampler == nullptr) {
                        fprintf(stderr, "Allocating resampler failed.\n");
                        exit(1);
                }
 
-               av_opt_set_int(resampler, "in_channel_layout",  channel_layout,                             0);
-               av_opt_set_int(resampler, "out_channel_layout", AV_CH_LAYOUT_STEREO_DOWNMIX,                0);
-               av_opt_set_int(resampler, "in_sample_rate",     av_frame_get_sample_rate(audio_avframe),    0);
-               av_opt_set_int(resampler, "out_sample_rate",    OUTPUT_FREQUENCY,                           0);
-               av_opt_set_int(resampler, "in_sample_fmt",      audio_avframe->format,                      0);
-               av_opt_set_int(resampler, "out_sample_fmt",     dst_format,                                 0);
-
-               if (avresample_open(resampler) < 0) {
+               if (swr_init(resampler) < 0) {
                        fprintf(stderr, "Could not open resample context.\n");
                        exit(1);
                }
@@ -765,15 +783,15 @@ void FFmpegCapture::convert_audio(const AVFrame *audio_avframe, FrameAllocator::
                last_src_format = AVSampleFormat(audio_avframe->format);
                last_dst_format = dst_format;
                last_channel_layout = channel_layout;
-               last_sample_rate = av_frame_get_sample_rate(audio_avframe);
+               last_sample_rate = audio_avframe->sample_rate;
        }
 
        size_t bytes_per_sample = (audio_format->bits_per_sample / 8) * 2;
        size_t num_samples_room = (audio_frame->size - audio_frame->len) / bytes_per_sample;
 
        uint8_t *data = audio_frame->data + audio_frame->len;
-       int out_samples = avresample_convert(resampler, &data, 0, num_samples_room,
-               const_cast<uint8_t **>(audio_avframe->data), audio_avframe->linesize[0], audio_avframe->nb_samples);
+       int out_samples = swr_convert(resampler, &data, num_samples_room,
+               const_cast<const uint8_t **>(audio_avframe->data), audio_avframe->nb_samples);
        if (out_samples < 0) {
                 fprintf(stderr, "Audio conversion failed.\n");
                 exit(1);
@@ -796,7 +814,7 @@ VideoFormat FFmpegCapture::construct_video_format(const AVFrame *frame, AVRation
                video_format.stride = width;
        }
        video_format.frame_rate_nom = video_timebase.den;
-       video_format.frame_rate_den = av_frame_get_pkt_duration(frame) * video_timebase.num;
+       video_format.frame_rate_den = frame->pkt_duration * video_timebase.num;
        if (video_format.frame_rate_nom == 0 || video_format.frame_rate_den == 0) {
                // Invalid frame rate.
                video_format.frame_rate_nom = 60;
@@ -851,7 +869,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);
@@ -870,7 +888,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);