From c7999b717a903ff4bdb041c987a03f7023ba87c0 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Mon, 7 Oct 2024 16:47:24 +0100 Subject: [PATCH] fix: support building against ffmpeg 7.0 --- .../bluefish/producer/bluefish_producer.cpp | 18 +++++++---- .../decklink/producer/decklink_producer.cpp | 23 ++++++++++--- src/modules/ffmpeg/CMakeLists.txt | 1 + .../ffmpeg/consumer/ffmpeg_consumer.cpp | 19 +++++++++-- src/modules/ffmpeg/defines.h | 5 +++ src/modules/ffmpeg/producer/av_producer.cpp | 32 ++++++++++++++++--- src/modules/ffmpeg/util/av_util.cpp | 32 ++++++++++++++++--- src/modules/ffmpeg/util/av_util.h | 4 +++ .../newtek/producer/newtek_ndi_producer.cpp | 6 +++- src/modules/oal/consumer/oal_consumer.cpp | 12 ++++++- 10 files changed, 127 insertions(+), 25 deletions(-) create mode 100644 src/modules/ffmpeg/defines.h diff --git a/src/modules/bluefish/producer/bluefish_producer.cpp b/src/modules/bluefish/producer/bluefish_producer.cpp index 53bb885eb4..d0f56c912e 100644 --- a/src/modules/bluefish/producer/bluefish_producer.cpp +++ b/src/modules/bluefish/producer/bluefish_producer.cpp @@ -451,7 +451,7 @@ struct bluefish_producer src_video->interlaced_frame = !is_progressive; src_video->top_field_first = height != 486; src_video->key_frame = 1; - src_video->display_picture_number = frames_captured; + //src_video->display_picture_number = frames_captured; src_video->pts = capture_ts; void* video_bytes = reserved_frames_.front()->image_data(); @@ -461,8 +461,12 @@ struct bluefish_producer } // Audio - src_audio->format = AV_SAMPLE_FMT_S32; - src_audio->channels = format_desc_.audio_channels; + src_audio->format = AV_SAMPLE_FMT_S32; +#if FFMPEG_NEW_CHANNEL_LAYOUT + av_channel_layout_default(&src_audio->ch_layout, format_desc_.audio_channels); +#else + src_audio->channels = format_desc_.audio_channels; +#endif src_audio->sample_rate = format_desc_.audio_sample_rate; src_audio->nb_samples = 0; int samples_decoded = 0; @@ -480,15 +484,15 @@ struct bluefish_producer card_type, reinterpret_cast(hanc_buffer), reinterpret_cast(&decoded_audio_bytes_[0]), - src_audio->channels); + format_desc_.audio_channels); audio_bytes = reinterpret_cast(&decoded_audio_bytes_[0]); - samples_decoded = no_extracted_pcm_samples / src_audio->channels; + samples_decoded = no_extracted_pcm_samples / format_desc_.audio_channels; src_audio->nb_samples = samples_decoded; src_audio->data[0] = reinterpret_cast(audio_bytes); src_audio->linesize[0] = - src_audio->nb_samples * src_audio->channels * + src_audio->nb_samples * format_desc_.audio_channels * av_get_bytes_per_sample(static_cast(src_audio->format)); src_audio->pts = capture_ts; } @@ -504,7 +508,7 @@ struct bluefish_producer auto audio_bytes = reinterpret_cast(&decoded_audio_bytes_[0]); if (audio_bytes) { src_audio->nb_samples = remainaing_audio_samples_; - int bytes_left = remainaing_audio_samples_ * 4 * src_audio->channels; + int bytes_left = remainaing_audio_samples_ * 4 * format_desc_.audio_channels; src_audio->data[0] = audio_bytes + bytes_left; src_audio->linesize[0] = bytes_left; remainaing_audio_samples_ = 0; diff --git a/src/modules/decklink/producer/decklink_producer.cpp b/src/modules/decklink/producer/decklink_producer.cpp index cc8f723306..f42b5ea9cf 100644 --- a/src/modules/decklink/producer/decklink_producer.cpp +++ b/src/modules/decklink/producer/decklink_producer.cpp @@ -204,7 +204,7 @@ struct Filter auto args = (boost::format("time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%#x") % 1 % format_desc.audio_sample_rate % format_desc.audio_sample_rate % AV_SAMPLE_FMT_S32 % - av_get_default_channel_layout(format_desc.audio_channels)) + ffmpeg::get_channel_layout_mask_for_channels(format_desc.audio_channels)) .str(); auto name = (boost::format("in_%d") % 0).str(); @@ -237,12 +237,23 @@ struct Filter #pragma warning(push) #pragma warning(disable : 4245) #endif + AVSampleFormat sample_fmts[] = {AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_NONE}; - int64_t channel_layouts[] = {av_get_default_channel_layout(format_desc.audio_channels), 0}; int sample_rates[] = {format_desc.audio_sample_rate, 0}; FF(av_opt_set_int_list(sink, "sample_fmts", sample_fmts, -1, AV_OPT_SEARCH_CHILDREN)); - FF(av_opt_set_int_list(sink, "channel_layouts", channel_layouts, 0, AV_OPT_SEARCH_CHILDREN)); FF(av_opt_set_int_list(sink, "sample_rates", sample_rates, 0, AV_OPT_SEARCH_CHILDREN)); + +#if FFMPEG_NEW_CHANNEL_LAYOUT + AVChannelLayout channel_layout = AV_CHANNEL_LAYOUT_STEREO; + av_channel_layout_default(&channel_layout, format_desc.audio_channels); + + FF(av_opt_set_chlayout(sink, "ch_layouts", &channel_layout, AV_OPT_SEARCH_CHILDREN)); + av_channel_layout_uninit(&channel_layout); +#else + int64_t channel_layouts[] = {av_get_default_channel_layout(format_desc.audio_channels), 0}; + FF(av_opt_set_int_list(sink, "channel_layouts", channel_layouts, 0, AV_OPT_SEARCH_CHILDREN)); +#endif + #ifdef _MSC_VER #pragma warning(pop) #endif @@ -585,7 +596,11 @@ class decklink_producer : public IDeckLinkInputCallback if (audio) { auto src = std::shared_ptr(av_frame_alloc(), [](AVFrame* ptr) { av_frame_free(&ptr); }); src->format = AV_SAMPLE_FMT_S32; +#if FFMPEG_NEW_CHANNEL_LAYOUT + av_channel_layout_default(&src->ch_layout, format_desc_.audio_channels); +#else src->channels = format_desc_.audio_channels; +#endif src->sample_rate = format_desc_.audio_sample_rate; void* audio_bytes = nullptr; @@ -594,7 +609,7 @@ class decklink_producer : public IDeckLinkInputCallback src = std::shared_ptr(src.get(), [src, audio](AVFrame* ptr) { audio->Release(); }); src->nb_samples = audio->GetSampleFrameCount(); src->data[0] = reinterpret_cast(audio_bytes); - src->linesize[0] = src->nb_samples * src->channels * + src->linesize[0] = src->nb_samples * format_desc_.audio_channels * av_get_bytes_per_sample(static_cast(src->format)); if (SUCCEEDED(audio->GetPacketTime(&in_audio_pts, format_desc_.audio_sample_rate))) { diff --git a/src/modules/ffmpeg/CMakeLists.txt b/src/modules/ffmpeg/CMakeLists.txt index 0283d1205e..631792e27c 100644 --- a/src/modules/ffmpeg/CMakeLists.txt +++ b/src/modules/ffmpeg/CMakeLists.txt @@ -18,6 +18,7 @@ set(HEADERS producer/ffmpeg_producer.h consumer/ffmpeg_consumer.h + defines.h ffmpeg.h StdAfx.h ) diff --git a/src/modules/ffmpeg/consumer/ffmpeg_consumer.cpp b/src/modules/ffmpeg/consumer/ffmpeg_consumer.cpp index 7512617017..823ee44a87 100644 --- a/src/modules/ffmpeg/consumer/ffmpeg_consumer.cpp +++ b/src/modules/ffmpeg/consumer/ffmpeg_consumer.cpp @@ -192,7 +192,7 @@ struct Stream } else if (codec->type == AVMEDIA_TYPE_AUDIO) { auto args = (boost::format("time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%#x") % 1 % format_desc.audio_sample_rate % format_desc.audio_sample_rate % AV_SAMPLE_FMT_S32 % - av_get_default_channel_layout(format_desc.audio_channels)) + get_channel_layout_mask_for_channels(format_desc.audio_channels)) .str(); auto name = (boost::format("in_%d") % 0).str(); @@ -229,8 +229,15 @@ struct Stream #endif // TODO codec->profiles FF(av_opt_set_int_list(sink, "sample_fmts", codec->sample_fmts, -1, AV_OPT_SEARCH_CHILDREN)); - FF(av_opt_set_int_list(sink, "channel_layouts", codec->channel_layouts, 0, AV_OPT_SEARCH_CHILDREN)); FF(av_opt_set_int_list(sink, "sample_rates", codec->supported_samplerates, 0, AV_OPT_SEARCH_CHILDREN)); + +#if FFMPEG_NEW_CHANNEL_LAYOUT + // TODO: need to translate codec->ch_layouts into something that can be passed via av_opt_set_* + // FF(av_opt_set_chlayout(sink, "ch_layouts", codec->ch_layouts, AV_OPT_SEARCH_CHILDREN)); +#else + FF(av_opt_set_int_list(sink, "channel_layouts", codec->channel_layouts, 0, AV_OPT_SEARCH_CHILDREN)); +#endif + #ifdef _MSC_VER #pragma warning(pop) #endif @@ -283,15 +290,21 @@ struct Stream enc->sample_fmt = static_cast(av_buffersink_get_format(sink)); enc->sample_rate = av_buffersink_get_sample_rate(sink); + enc->time_base = st->time_base; + +#if FFMPEG_NEW_CHANNEL_LAYOUT + FF(av_buffersink_get_ch_layout(sink, &enc->ch_layout)); +#else enc->channels = av_buffersink_get_channels(sink); enc->channel_layout = av_buffersink_get_channel_layout(sink); - enc->time_base = st->time_base; if (!enc->channels) { enc->channels = av_get_channel_layout_nb_channels(enc->channel_layout); } else if (!enc->channel_layout) { enc->channel_layout = av_get_default_channel_layout(enc->channels); } +#endif + } else { // TODO } diff --git a/src/modules/ffmpeg/defines.h b/src/modules/ffmpeg/defines.h new file mode 100644 index 0000000000..7b81161983 --- /dev/null +++ b/src/modules/ffmpeg/defines.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +#define FFMPEG_NEW_CHANNEL_LAYOUT LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(60, 0, 0) \ No newline at end of file diff --git a/src/modules/ffmpeg/producer/av_producer.cpp b/src/modules/ffmpeg/producer/av_producer.cpp index efd02bddfd..79ad84b15e 100644 --- a/src/modules/ffmpeg/producer/av_producer.cpp +++ b/src/modules/ffmpeg/producer/av_producer.cpp @@ -127,12 +127,14 @@ class Decoder ctx->framerate = av_guess_frame_rate(nullptr, stream, nullptr); ctx->sample_aspect_ratio = av_guess_sample_aspect_ratio(nullptr, stream, nullptr); } else if (ctx->codec_type == AVMEDIA_TYPE_AUDIO) { +#if !(FFMPEG_NEW_CHANNEL_LAYOUT) if (!ctx->channel_layout && ctx->channels) { ctx->channel_layout = av_get_default_channel_layout(ctx->channels); } if (!ctx->channels && ctx->channel_layout) { ctx->channels = av_get_channel_layout_nb_channels(ctx->channel_layout); } +#endif } if (codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) { @@ -178,7 +180,11 @@ class Decoder // TODO (fix) is this always best? av_frame->pts = av_frame->best_effort_timestamp; +#if LIBAVUTIL_VERSION_MAJOR < 58 auto duration_pts = av_frame->pkt_duration; +#else + auto duration_pts = av_frame->duration; +#endif if (duration_pts <= 0) { if (ctx->codec_type == AVMEDIA_TYPE_VIDEO) { const auto ticks = av_stream_get_parser(st) ? av_stream_get_parser(st)->repeat_pict + 1 @@ -313,7 +319,12 @@ struct Filter AVRational tb = {1, format_desc.audio_sample_rate}; for (auto n = 0U; n < input->nb_streams; ++n) { const auto st = input->streams[n]; - if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->channels > 0) { +#if FFMPEG_NEW_CHANNEL_LAYOUT + const auto codec_channels = st->codecpar->ch_layout.nb_channels; +#else + const auto codec_channels = st->codecpar->channels; +#endif + if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && codec_channels > 0) { tb = {1, st->codecpar->sample_rate}; break; } @@ -364,7 +375,12 @@ struct Filter for (auto n = 0U; n < input->nb_streams; ++n) { const auto st = input->streams[n]; - if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->channels == 0) { +#if FFMPEG_NEW_CHANNEL_LAYOUT + const auto codec_channels = st->codecpar->ch_layout.nb_channels; +#else + const auto codec_channels = st->codecpar->channels; +#endif + if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && codec_channels == 0) { continue; } @@ -468,9 +484,16 @@ struct Filter FF(avfilter_link(source, 0, cur->filter_ctx, cur->pad_idx)); sources.emplace(index, source); } else if (st->codec_type == AVMEDIA_TYPE_AUDIO) { +#if FFMPEG_NEW_CHANNEL_LAYOUT + char channel_layout[128]; + FF(av_channel_layout_describe(&st->ch_layout, channel_layout, sizeof(channel_layout))); +#else + const auto channel_layout = st->channel_layout; +#endif + auto args = (boost::format("time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%#x") % st->pkt_timebase.num % st->pkt_timebase.den % st->sample_rate % - av_get_sample_fmt_name(st->sample_fmt) % st->channel_layout) + av_get_sample_fmt_name(st->sample_fmt) % channel_layout) .str(); auto name = (boost::format("in_%d") % index).str(); @@ -523,8 +546,7 @@ struct Filter const AVSampleFormat sample_fmts[] = {AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_NONE}; FF(av_opt_set_int_list(sink, "sample_fmts", sample_fmts, -1, AV_OPT_SEARCH_CHILDREN)); - const int channel_counts[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, -1}; - FF(av_opt_set_int_list(sink, "channel_counts", channel_counts, -1, AV_OPT_SEARCH_CHILDREN)); + FF(av_opt_set_int(sink, "all_channel_counts", 1, AV_OPT_SEARCH_CHILDREN)); const int sample_rates[] = {format_desc.audio_sample_rate, -1}; FF(av_opt_set_int_list(sink, "sample_rates", sample_rates, -1, AV_OPT_SEARCH_CHILDREN)); diff --git a/src/modules/ffmpeg/util/av_util.cpp b/src/modules/ffmpeg/util/av_util.cpp index fa6a2e4736..63fb45cc02 100644 --- a/src/modules/ffmpeg/util/av_util.cpp +++ b/src/modules/ffmpeg/util/av_util.cpp @@ -72,7 +72,13 @@ core::mutable_frame make_frame(void* tag, const int channel_count = 16; frame.audio_data() = std::vector(audio->nb_samples * channel_count, 0); - if (audio->channels == channel_count) { +#if FFMPEG_NEW_CHANNEL_LAYOUT + auto source_channel_count = audio->ch_layout.nb_channels; +#else + auto source_channel_count = audio->channels; +#endif + + if (source_channel_count == channel_count) { std::memcpy(frame.audio_data().data(), reinterpret_cast(audio->data[0]), sizeof(int32_t) * channel_count * audio->nb_samples); @@ -82,8 +88,8 @@ core::mutable_frame make_frame(void* tag, auto dst = frame.audio_data().data(); auto src = reinterpret_cast(audio->data[0]); for (auto i = 0; i < audio->nb_samples; i++) { - for (auto j = 0; j < std::min(channel_count, audio->channels); ++j) { - dst[i * channel_count + j] = src[i * audio->channels + j]; + for (auto j = 0; j < std::min(channel_count, source_channel_count); ++j) { + dst[i * channel_count + j] = src[i * source_channel_count + j]; } } } @@ -291,11 +297,15 @@ std::shared_ptr make_av_audio_frame(const core::const_frame& frame, con const auto& buffer = frame.audio_data(); // TODO (fix) Use sample_format_desc. +#if FFMPEG_NEW_CHANNEL_LAYOUT + av_channel_layout_default(&av_frame->ch_layout, format_desc.audio_channels); +#else av_frame->channels = format_desc.audio_channels; av_frame->channel_layout = av_get_default_channel_layout(av_frame->channels); +#endif av_frame->sample_rate = format_desc.audio_sample_rate; av_frame->format = AV_SAMPLE_FMT_S32; - av_frame->nb_samples = static_cast(buffer.size() / av_frame->channels); + av_frame->nb_samples = static_cast(buffer.size() / format_desc.audio_channels); FF(av_frame_get_buffer(av_frame.get(), 32)); std::memcpy(av_frame->data[0], buffer.data(), buffer.size() * sizeof(buffer.data()[0])); @@ -330,4 +340,18 @@ std::map to_map(AVDictionary** dict) return map; } + +uint64_t get_channel_layout_mask_for_channels(int channel_count) { +#if FFMPEG_NEW_CHANNEL_LAYOUT + AVChannelLayout layout = AV_CHANNEL_LAYOUT_STEREO; + av_channel_layout_default(&layout, channel_count); + uint64_t channel_layout = layout.u.mask; + av_channel_layout_uninit(&layout); + + return channel_layout; +#else + return av_get_default_channel_layout(channel_count); +#endif +} + }} // namespace caspar::ffmpeg diff --git a/src/modules/ffmpeg/util/av_util.h b/src/modules/ffmpeg/util/av_util.h index 8a6ceed869..04f58fa1f4 100644 --- a/src/modules/ffmpeg/util/av_util.h +++ b/src/modules/ffmpeg/util/av_util.h @@ -9,6 +9,8 @@ #include #include +#include "../defines.h" + struct AVFrame; struct AVPacket; struct AVFilterContext; @@ -33,4 +35,6 @@ std::shared_ptr make_av_audio_frame(const core::const_frame& frame, con AVDictionary* to_dict(std::map&& map); std::map to_map(AVDictionary** dict); +uint64_t get_channel_layout_mask_for_channels(int channel_count); + }} // namespace caspar::ffmpeg diff --git a/src/modules/newtek/producer/newtek_ndi_producer.cpp b/src/modules/newtek/producer/newtek_ndi_producer.cpp index 0596f57999..797fbe78b9 100644 --- a/src/modules/newtek/producer/newtek_ndi_producer.cpp +++ b/src/modules/newtek/producer/newtek_ndi_producer.cpp @@ -206,7 +206,11 @@ struct newtek_ndi_producer : public core::frame_producer if (audio_frame.p_data != nullptr) { audio_frame_32s.reference_level = 0; ndi_lib_->util_audio_to_interleaved_32s_v2(&audio_frame, &audio_frame_32s); - a_frame->channels = audio_frame_32s.no_channels; +#if FFMPEG_NEW_CHANNEL_LAYOUT + av_channel_layout_default(&a_frame->ch_layout, audio_frame_32s.no_channels); +#else + a_frame->channels = audio_frame_32s.no_channels; +#endif a_frame->sample_rate = audio_frame_32s.sample_rate; a_frame->nb_samples = audio_frame_32s.no_samples; a_frame->data[0] = reinterpret_cast(audio_frame_32s.p_data); diff --git a/src/modules/oal/consumer/oal_consumer.cpp b/src/modules/oal/consumer/oal_consumer.cpp index fa790fe9fb..652a53786e 100644 --- a/src/modules/oal/consumer/oal_consumer.cpp +++ b/src/modules/oal/consumer/oal_consumer.cpp @@ -35,6 +35,8 @@ #include #include +#include + #include #include #include @@ -249,8 +251,12 @@ struct oal_consumer : public core::frame_consumer auto dst = std::shared_ptr(av_frame_alloc(), [](AVFrame* ptr) { av_frame_free(&ptr); }); dst->format = AV_SAMPLE_FMT_S16; dst->sample_rate = format_desc_.audio_sample_rate; +#if FFMPEG_NEW_CHANNEL_LAYOUT + av_channel_layout_default(&dst->ch_layout, 2); +#else dst->channels = 2; dst->channel_layout = av_get_default_channel_layout(dst->channels); +#endif dst->nb_samples = duration_; if (av_frame_get_buffer(dst.get(), 32) < 0) { // TODO FF error @@ -277,9 +283,13 @@ struct oal_consumer : public core::frame_consumer auto src = std::shared_ptr(av_frame_alloc(), [](AVFrame* ptr) { av_frame_free(&ptr); }); src->format = AV_SAMPLE_FMT_S32; src->sample_rate = format_desc_.audio_sample_rate; +#if FFMPEG_NEW_CHANNEL_LAYOUT + av_channel_layout_default(&src->ch_layout, format_desc_.audio_channels); +#else src->channels = format_desc_.audio_channels; src->channel_layout = av_get_default_channel_layout(src->channels); - src->nb_samples = static_cast(frame.audio_data().size() / src->channels); +#endif + src->nb_samples = static_cast(frame.audio_data().size() / format_desc_.audio_channels); src->extended_data[0] = const_cast(reinterpret_cast(frame.audio_data().data())); src->linesize[0] = static_cast(frame.audio_data().size() * sizeof(int32_t));