X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=nageru%2Fffmpeg_capture.cpp;h=6dbcbd602b79b544c8319ddd9446b9992c66ef10;hb=HEAD;hp=09c1d26e15b3642be4ea64eff4fa3e8da606c01c;hpb=2f92c975a3cf9f4803a58267fd2a12765e34a69e;p=nageru diff --git a/nageru/ffmpeg_capture.cpp b/nageru/ffmpeg_capture.cpp index 09c1d26..14ad91f 100644 --- a/nageru/ffmpeg_capture.cpp +++ b/nageru/ffmpeg_capture.cpp @@ -1,24 +1,49 @@ #include "ffmpeg_capture.h" +#include "defs.h" +#include "shared/shared_defs.h" #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include #include #include #include +#include #include -#include +#include +#include extern "C" { #include +#include +#include +#include #include #include +#include +#include +#include +#include #include #include -#include -#include +#include +#include +#include #include -#include +#include +#include +#include +#include #include } @@ -33,10 +58,10 @@ extern "C" { #include #include "bmusb/bmusb.h" +#include "shared/context.h" #include "shared/ffmpeg_raii.h" #include "ffmpeg_util.h" #include "flags.h" -#include "image_input.h" #include "ref_counted_frame.h" #include "shared/timebase.h" @@ -50,6 +75,11 @@ using namespace bmusb; using namespace movit; using namespace Eigen; +// Avoid deprecation warnings, but we don't want to drop FFmpeg 5.1 support just yet. +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 30, 100) +#define pkt_duration duration +#endif + namespace { steady_clock::time_point compute_frame_start(int64_t frame_pts, int64_t pts_origin, const AVRational &video_timebase, const steady_clock::time_point &origin, double rate) @@ -256,8 +286,8 @@ RGBTriplet get_neutral_color(AVDictionary *metadata) } // namespace -FFmpegCapture::FFmpegCapture(const string &filename, unsigned width, unsigned height) - : filename(filename), width(width), height(height), video_timebase{1, 1} +FFmpegCapture::FFmpegCapture(const string &filename, unsigned width, unsigned height, QSurface *surface) + : filename(filename), width(width), height(height), video_timebase{1, 1}, surface(surface) { description = "Video: " + filename; @@ -267,12 +297,13 @@ FFmpegCapture::FFmpegCapture(const string &filename, unsigned width, unsigned he } #ifdef HAVE_SRT -FFmpegCapture::FFmpegCapture(int srt_sock, const string &stream_id) +FFmpegCapture::FFmpegCapture(int srt_sock, const string &stream_id, QSurface *surface) : srt_sock(srt_sock), width(0), // Don't resize; SRT streams typically have stable resolution, and should behave much like regular cards in general. height(0), pixel_format(bmusb::PixelFormat_8BitYCbCrPlanar), - video_timebase{1, 1} + video_timebase{1, 1}, + surface(surface) { if (stream_id.empty()) { description = "SRT stream"; @@ -296,6 +327,7 @@ FFmpegCapture::~FFmpegCapture() srt_close(srt_sock); } #endif + delete surface; } void FFmpegCapture::configure_card() @@ -356,6 +388,21 @@ void FFmpegCapture::producer_thread_func() snprintf(thread_name, sizeof(thread_name), "FFmpeg_C_%d", card_index); pthread_setname_np(pthread_self(), thread_name); + // We need a context in case create_frame() needs to reallocate something. + // (If none is given, we are probably in Kaeru, which uses MallocFrameAllocator + // anyway, which doesn't reallocate currently and definitely doesn't need + // an active OpenGL context to do so.) + QOpenGLContext *context = nullptr; + if (surface != nullptr) { + context = create_context(this->surface); + eglBindAPI(EGL_OPENGL_API); + if (!make_current(context, this->surface)) { + printf("display=%p surface=%p context=%p curr=%p err=%d\n", eglGetCurrentDisplay(), this->surface, context, eglGetCurrentContext(), + eglGetError()); + abort(); + } + } + while (!producer_thread_should_quit.should_quit()) { string filename_copy; { @@ -399,9 +446,11 @@ void FFmpegCapture::producer_thread_func() } if (has_dequeue_callbacks) { - dequeue_cleanup_callback(); + dequeue_cleanup_callback(); has_dequeue_callbacks = false; - } + } + + delete_context(context); } void FFmpegCapture::send_disconnected_frame() @@ -1015,28 +1064,36 @@ void FFmpegCapture::convert_audio(const AVFrame *audio_avframe, FrameAllocator:: } audio_format->num_channels = 2; - int64_t channel_layout = audio_avframe->channel_layout; - if (channel_layout == 0) { - channel_layout = av_get_default_channel_layout(audio_avframe->channels); + AVChannelLayout channel_layout = audio_avframe->ch_layout; + if (!av_channel_layout_check(&channel_layout) || + channel_layout.order == AV_CHANNEL_ORDER_UNSPEC) { + av_channel_layout_default(&channel_layout, audio_avframe->ch_layout.nb_channels); } if (resampler == nullptr || audio_avframe->format != last_src_format || dst_format != last_dst_format || - channel_layout != last_channel_layout || + av_channel_layout_compare(&channel_layout, &last_channel_layout) != 0|| audio_avframe->sample_rate != last_sample_rate) { + // TODO: When we get C++20, use AV_CHANNEL_LAYOUT_STEREO_DOWNMIX. + AVChannelLayout stereo_downmix; + stereo_downmix.order = AV_CHANNEL_ORDER_NATIVE; + stereo_downmix.nb_channels = 2; + stereo_downmix.u.mask = AV_CH_LAYOUT_STEREO_DOWNMIX; + 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) { + resampler = nullptr; + int err = swr_alloc_set_opts2(&resampler, + /*out_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 (err != 0 || resampler == nullptr) { fprintf(stderr, "Allocating resampler failed.\n"); abort(); } @@ -1059,9 +1116,9 @@ void FFmpegCapture::convert_audio(const AVFrame *audio_avframe, FrameAllocator:: int out_samples = swr_convert(resampler, &data, num_samples_room, const_cast(audio_avframe->data), audio_avframe->nb_samples); if (out_samples < 0) { - fprintf(stderr, "Audio conversion failed.\n"); - abort(); - } + fprintf(stderr, "Audio conversion failed.\n"); + abort(); + } audio_frame->len += out_samples * bytes_per_sample; } @@ -1090,7 +1147,7 @@ UniqueFrame FFmpegCapture::make_video_frame(const AVFrame *frame, const string & { *error = false; - UniqueFrame video_frame(video_frame_allocator->alloc_frame()); + UniqueFrame video_frame(video_frame_allocator->create_frame(frame->width, frame->height, frame->width)); if (video_frame->data == nullptr) { return video_frame; } @@ -1155,8 +1212,8 @@ UniqueFrame FFmpegCapture::make_video_frame(const AVFrame *frame, const string & // FIXME: Currently, if the video is too high-res for one of the allocated // frames, we simply refuse to scale it here to avoid crashes. It would be better // if we could somehow signal getting larger frames, especially as 4K is a thing now. - if (video_frame->len > FRAME_SIZE) { - fprintf(stderr, "%s: Decoded frame would be larger than supported FRAME_SIZE (%zu > %u), not decoding.\n", pathname.c_str(), video_frame->len, FRAME_SIZE); + if (video_frame->len > video_frame->size) { + fprintf(stderr, "%s: Decoded frame would be larger than supported frame size (%zu > %zu), not decoding.\n", pathname.c_str(), video_frame->len, video_frame->size); *error = true; return video_frame; }