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)};
openSUSE Build Service is sponsored by