File qtwebengine-ffmpeg-playback-fix.patch of Package qt6-webengine
From 54a756b84e0b7a1104f9b336a1253fe36c573f56 Mon Sep 17 00:00:00 2001
From: Martin Negyokru <negyokru@inf.u-szeged.hu>
Date: Fri, 17 Jan 2025 15:40:37 +0100
Subject: [fixup][ffmpeg] Fix building with system ffmpeg
Refactor workaround for codec selection issues.
Introduce FindDecoder which meant to replace avcodec_find_decoder.
Imporove codec detection in format context.
Fixes: QTBUG-132762
Task-number: QTBUG-130273
Pick-to: 126-based 122-based
Change-Id: I6d520633ebebbb9bdb7aa71b8247066c2a662be3
Reviewed-on: https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/617873
Reviewed-by: Moss Heim <moss.heim@qt.io>
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
---
.../clear_key_cdm/ffmpeg_cdm_audio_decoder.cc | 5 ++++
chromium/media/ffmpeg/ffmpeg_common.cc | 23 +++++++++++++++++
chromium/media/ffmpeg/ffmpeg_common.h | 6 +++++
chromium/media/filters/audio_file_reader.cc | 5 ++++
chromium/media/filters/ffmpeg_audio_decoder.cc | 15 ++---------
chromium/media/filters/ffmpeg_glue.cc | 29 ++++++++++++++--------
chromium/media/filters/ffmpeg_video_decoder.cc | 5 ++++
chromium/media/filters/media_file_checker.cc | 5 ++++
8 files changed, 69 insertions(+), 24 deletions(-)
diff --git a/src/3rdparty/chromium/media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_audio_decoder.cc b/src/3rdparty/chromium/media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_audio_decoder.cc
index 9646e70..5b9a02e 100644
--- a/src/3rdparty/chromium/media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_audio_decoder.cc
+++ b/src/3rdparty/chromium/media/cdm/library_cdm/clear_key_cdm/ffmpeg_cdm_audio_decoder.cc
@@ -177,7 +177,12 @@ bool FFmpegCdmAudioDecoder::Initialize(
if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P)
codec_context_->request_sample_fmt = AV_SAMPLE_FMT_S16;
+#if BUILDFLAG(IS_QTWEBENGINE) && BUILDFLAG(USE_SYSTEM_FFMPEG)
+ const AVCodec* codec =
+ FindDecoder(codec_context_->codec_id, codec_context_->codec_whitelist);
+#else
const AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
+#endif
if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) {
DLOG(ERROR) << "Could not initialize audio decoder: "
<< codec_context_->codec_id;
diff --git a/src/3rdparty/chromium/media/ffmpeg/ffmpeg_common.cc b/src/3rdparty/chromium/media/ffmpeg/ffmpeg_common.cc
index 63159ef..f872a8e 100644
--- a/src/3rdparty/chromium/media/ffmpeg/ffmpeg_common.cc
+++ b/src/3rdparty/chromium/media/ffmpeg/ffmpeg_common.cc
@@ -29,6 +29,11 @@
#endif
#endif
+#if BUILDFLAG(IS_QTWEBENGINE) && BUILDFLAG(USE_SYSTEM_FFMPEG)
+extern "C" {
+#include <libavutil/avstring.h>
+}
+#endif
namespace media {
namespace {
@@ -67,6 +72,24 @@ static_assert(
VideoFrame::kFrameAddressAlignment % kFFmpegBufferAddressAlignment == 0,
"VideoFrame frame address alignment does not fit ffmpeg requirement");
+#if BUILDFLAG(IS_QTWEBENGINE) && BUILDFLAG(USE_SYSTEM_FFMPEG)
+const AVCodec* FindDecoder(AVCodecID id, const char* whitelist) {
+ if (!whitelist) {
+ return avcodec_find_decoder(id);
+ }
+
+ void* i = 0;
+ const AVCodec* codec;
+ while (codec = av_codec_iterate(&i)) {
+ if (av_codec_is_decoder(codec) && codec->id == id &&
+ av_match_list(codec->name, whitelist, ',')) {
+ return codec;
+ }
+ }
+ return nullptr;
+}
+#endif
+
static const AVRational kMicrosBase = { 1, base::Time::kMicrosecondsPerSecond };
base::TimeDelta ConvertFromTimeBase(const AVRational& time_base,
diff --git a/src/3rdparty/chromium/media/ffmpeg/ffmpeg_common.h b/src/3rdparty/chromium/media/ffmpeg/ffmpeg_common.h
index 9993d34..2332722 100644
--- a/src/3rdparty/chromium/media/ffmpeg/ffmpeg_common.h
+++ b/src/3rdparty/chromium/media/ffmpeg/ffmpeg_common.h
@@ -79,6 +79,12 @@ inline void ScopedPtrAVFreeFrame::operator()(void* x) const {
av_frame_free(&frame);
}
+#if BUILDFLAG(IS_QTWEBENGINE) && BUILDFLAG(USE_SYSTEM_FFMPEG)
+// Systemlib friendly version of avcodec_find_decoder that respects the
+// whitelisted codecs.
+MEDIA_EXPORT const AVCodec* FindDecoder(AVCodecID id, const char* whitelist);
+#endif
+
// Converts an int64_t timestamp in |time_base| units to a base::TimeDelta.
// For example if |timestamp| equals 11025 and |time_base| equals {1, 44100}
// then the return value will be a base::TimeDelta for 0.25 seconds since that
diff --git a/src/3rdparty/chromium/media/filters/audio_file_reader.cc b/src/3rdparty/chromium/media/filters/audio_file_reader.cc
index dd4f3b5..172bd9a 100644
--- a/src/3rdparty/chromium/media/filters/audio_file_reader.cc
+++ b/src/3rdparty/chromium/media/filters/audio_file_reader.cc
@@ -95,7 +95,12 @@ bool AudioFileReader::OpenDemuxer() {
}
bool AudioFileReader::OpenDecoder() {
+#if BUILDFLAG(IS_QTWEBENGINE) && BUILDFLAG(USE_SYSTEM_FFMPEG)
+ const AVCodec* codec =
+ FindDecoder(codec_context_->codec_id, codec_context_->codec_whitelist);
+#else
const AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
+#endif
if (codec) {
// MP3 decodes to S16P which we don't support, tell it to use S16 instead.
if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P)
diff --git a/src/3rdparty/chromium/media/filters/ffmpeg_audio_decoder.cc b/src/3rdparty/chromium/media/filters/ffmpeg_audio_decoder.cc
index 22fa4d4..5d94b76 100644
--- a/src/3rdparty/chromium/media/filters/ffmpeg_audio_decoder.cc
+++ b/src/3rdparty/chromium/media/filters/ffmpeg_audio_decoder.cc
@@ -360,19 +360,8 @@ bool FFmpegAudioDecoder::ConfigureDecoder(const AudioDecoderConfig& config) {
}
#if BUILDFLAG(IS_QTWEBENGINE) && BUILDFLAG(USE_SYSTEM_FFMPEG)
- // Workaround http://crbug.com/41492567
- // Chromium only supports the 'libopus' decoder.
- // 'avcodec_find_decoder' finds the experimental 'opus' decoder first
- // because the lookup is based on codec_id and both have the same id.
- // Bundled ffmpeg only have libopus but the system usually have both.
- const AVCodec* codec = [&config, this]() {
- if (config.codec() == AudioCodec::kOpus)
- return avcodec_find_decoder_by_name("libopus");
- if (config.codec() == AudioCodec::kMP3) {
- return avcodec_find_decoder_by_name("mp3");
- }
- return avcodec_find_decoder(codec_context_->codec_id);
- }();
+ AVCodecID id = AudioCodecToCodecID(config.codec(), config.sample_format());
+ const AVCodec* codec = FindDecoder(id, codec_context_->codec_whitelist);
#else
const AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
#endif
diff --git a/src/3rdparty/chromium/media/filters/ffmpeg_glue.cc b/src/3rdparty/chromium/media/filters/ffmpeg_glue.cc
index 6407b5b..e9bd44b 100644
--- a/src/3rdparty/chromium/media/filters/ffmpeg_glue.cc
+++ b/src/3rdparty/chromium/media/filters/ffmpeg_glue.cc
@@ -242,20 +242,27 @@ bool FFmpegGlue::OpenContext(bool is_local_file) {
LogContainer(is_local_file, container_);
#if BUILDFLAG(IS_QTWEBENGINE) && BUILDFLAG(USE_SYSTEM_FFMPEG)
- // Sometimes FFmpeg is not aware of the whitelisted codecs and
- // configures streams and demuxers with unsupported codecs/params.
- // Force the correct codecs to avoid problems later.
+ // 'avformat_find_stream_info' is not aware of the whitelisted codecs.
+ // However it respects the codecs set in the format_context.
+ // Try to force the correct codecs here at context creation.
// https://ffmpeg.org/doxygen/7.0/structAVFormatContext.html#a52f39351b15890ef57cc6ff0ec9ab42d
// https://ffmpeg.org/doxygen/7.0/structAVFormatContext.html#ae5e087f4623b907517c0f7dd8327387d
- // Note: don't forget to update FFmpeg[Audio|Video]Decoder::ConfigureDecoder
-
- if (strcmp(format_context_->iformat->name, "mp3") == 0) {
- const AVCodec* mp3_codec = avcodec_find_decoder_by_name("mp3");
- if (mp3_codec) {
- format_context_->audio_codec = mp3_codec;
- } else {
- LOG(ERROR) << "No supported codec for mp3";
+ for (int i = 0; i < format_context_->nb_streams; i++) {
+ AVCodecParameters *params = format_context_->streams[i]->codecpar;
+ if (!params)
+ continue;
+ const AVCodec* audio_codec =
+ FindDecoder(params->codec_id, GetAllowedAudioDecoders());
+ if (audio_codec) {
+ if (format_context_->audio_codec &&
+ format_context_->audio_codec != audio_codec) {
+ LOG(INFO) << "Conflicting codecs " << format_context_->audio_codec->name
+ << ", " << audio_codec->name;
+ format_context_->audio_codec = nullptr;
+ break;
+ }
+ format_context_->audio_codec = audio_codec;
}
}
#endif
diff --git a/src/3rdparty/chromium/media/filters/ffmpeg_video_decoder.cc b/src/3rdparty/chromium/media/filters/ffmpeg_video_decoder.cc
index d08ee1d..832a3fa 100644
--- a/src/3rdparty/chromium/media/filters/ffmpeg_video_decoder.cc
+++ b/src/3rdparty/chromium/media/filters/ffmpeg_video_decoder.cc
@@ -524,7 +524,12 @@ bool FFmpegVideoDecoder::ConfigureDecoder(const VideoDecoderConfig& config,
codec_context_->flags2 |= AV_CODEC_FLAG2_CHUNKS;
}
+#if BUILDFLAG(IS_QTWEBENGINE) && BUILDFLAG(USE_SYSTEM_FFMPEG)
+ const AVCodec* codec =
+ FindDecoder(codec_context_->codec_id, codec_context_->codec_whitelist);
+#else
const AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
+#endif
if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) {
ReleaseFFmpegResources();
return false;
diff --git a/src/3rdparty/chromium/media/filters/media_file_checker.cc b/src/3rdparty/chromium/media/filters/media_file_checker.cc
index a2d4757..afe40e4 100644
--- a/src/3rdparty/chromium/media/filters/media_file_checker.cc
+++ b/src/3rdparty/chromium/media/filters/media_file_checker.cc
@@ -68,7 +68,12 @@ bool MediaFileChecker::Start(base::TimeDelta check_time) {
auto context = AVStreamToAVCodecContext(format_context->streams[i]);
if (!context)
continue;
+#if BUILDFLAG(IS_QTWEBENGINE) && BUILDFLAG(USE_SYSTEM_FFMPEG)
+ const AVCodec* codec =
+ FindDecoder(cp->codec_id, context->codec_whitelist);
+#else
const AVCodec* codec = avcodec_find_decoder(cp->codec_id);
+#endif
if (codec && avcodec_open2(context.get(), codec, nullptr) >= 0) {
auto loop = std::make_unique<FFmpegDecodingLoop>(context.get());
stream_contexts[i] = {std::move(context), std::move(loop)};