From 0d2153abd0da7f9bba8104ba8c732a985d821461 Mon Sep 17 00:00:00 2001
From: pfeatherstone <45853521+pfeatherstone@users.noreply.github.com>
Date: Wed, 13 Aug 2025 13:16:36 +0100
Subject: [PATCH] FFMpeg 7.1 API (#3105)

* Support new FFMpeg 7.1 API when applicable

* extra job

---------

Co-authored-by: me <me@me>
---
 .github/workflows/build_cpp.yml | 35 ++++++++++++-
 dlib/media/ffmpeg_muxer.h       | 92 +++++++++++++++++++++++----------
 2 files changed, 99 insertions(+), 28 deletions(-)

diff --git a/.github/workflows/build_cpp.yml b/.github/workflows/build_cpp.yml
index 77144df199..28ee56f8c3 100644
--- a/.github/workflows/build_cpp.yml
+++ b/.github/workflows/build_cpp.yml
@@ -141,7 +141,7 @@ jobs:
     - name: Build examples, etc
       run: cmake --build build --config Release --parallel 2
 
-  ubuntu-22-04-ffmpeg7:
+  ubuntu-22-04-ffmpeg701:
     runs-on: 'ubuntu-22.04'
     steps:
     - uses: actions/checkout@v2
@@ -174,6 +174,39 @@ jobs:
     - name: Build ffmpeg example
       run: cmake --build build --config Release --target ffmpeg_video_muxing_ex --parallel 4
 
+  ubuntu-22-04-ffmpeg711:
+    runs-on: 'ubuntu-22.04'
+    steps:
+    - uses: actions/checkout@v2
+
+    - name: Install dependencies
+      run: |
+        sudo apt update
+        sudo apt install make yasm 
+
+    - name: Cache FFmpeg 7
+      uses: actions/cache@v3
+      id: cache-ffmpeg7
+      with:
+        path: /home/runner/ffmpeg-n7.1.1_installation
+        key: ffmpeg-n7.1.1_try1
+        
+    - name: Build FFmpeg 7
+      if: steps.cache-ffmpeg7.outputs.cache-hit != 'true'
+      run: |
+        wget https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n7.1.1.tar.gz
+        tar -xf n7.1.1.tar.gz
+        cd FFmpeg-n7.1.1
+        ./configure --prefix=/home/runner/ffmpeg-n7.1.1_installation --disable-doc --disable-programs
+        make -j4
+        make install
+        cd ..
+        
+    - name: Configure
+      run: cmake . -B build -DCMAKE_PREFIX_PATH=/home/runner/ffmpeg-n7.1.1_installation
+    - name: Build ffmpeg example
+      run: cmake --build build --config Release --target ffmpeg_video_muxing_ex --parallel 4
+      
   windows-latest:
     runs-on: 'windows-latest'
     steps:
diff --git a/dlib/media/ffmpeg_muxer.h b/dlib/media/ffmpeg_muxer.h
index 2dc4561120..d98f96be5d 100644
--- a/dlib/media/ffmpeg_muxer.h
+++ b/dlib/media/ffmpeg_muxer.h
@@ -688,13 +688,21 @@ namespace dlib
             )
             {
                 // Video properties
-                if (pCodec->supported_framerates && pCodecCtx->framerate != 0)
+                const AVRational*   supported_framerates{nullptr};
+                int                 nsupported_framerates{0};
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 13, 100)
+                avcodec_get_supported_config(pCodecCtx, NULL, AV_CODEC_CONFIG_FRAME_RATE, 0, (const void**)&supported_framerates, &nsupported_framerates);
+#else
+                supported_framerates = pCodec->supported_framerates;
+                for (; supported_framerates != nullptr && supported_framerates[nsupported_framerates] != AVRational{0,0}  ; ++nsupported_framerates) {}
+#endif
+                if (supported_framerates && nsupported_framerates > 0 && pCodecCtx->framerate != 0)
                 {
                     bool framerate_supported = false;
 
-                    for (int i = 0 ; pCodec->supported_framerates[i] != AVRational{0,0} ; i++)
+                    for (int i = 0 ; i < nsupported_framerates ; ++i)
                     {
-                        if (pCodecCtx->framerate == pCodec->supported_framerates[i])
+                        if (pCodecCtx->framerate == supported_framerates[i])
                         {
                             framerate_supported = true;
                             break;
@@ -707,19 +715,26 @@ namespace dlib
                             << "Requested framerate "
                             << pCodecCtx->framerate.num / pCodecCtx->framerate.den
                             << " not supported. Changing to default "
-                            << pCodec->supported_framerates[0].num / pCodec->supported_framerates[0].den;
+                            << supported_framerates[0].num / supported_framerates[0].den;
 
-                        pCodecCtx->framerate = pCodec->supported_framerates[0];
+                        pCodecCtx->framerate = supported_framerates[0];
                     }
                 }
-
-                if (pCodec->pix_fmts)
+                const enum AVPixelFormat* pix_fmts{nullptr};
+                int                       npix_fmts{0};
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 13, 100)
+                avcodec_get_supported_config(pCodecCtx, NULL, AV_CODEC_CONFIG_PIX_FORMAT, 0, (const void**)&pix_fmts, &npix_fmts);
+#else
+                pix_fmts = pCodec->pix_fmts;
+                for (; pix_fmts != nullptr && pix_fmts[npix_fmts] != AV_PIX_FMT_NONE ; ++npix_fmts) {}
+#endif
+                if (pix_fmts && npix_fmts > 0)
                 {
                     bool pix_fmt_supported = false;
 
-                    for (int i = 0 ; pCodec->pix_fmts[i] != AV_PIX_FMT_NONE ; i++)
+                    for (int i = 0 ; i < npix_fmts; ++i)
                     {
-                        if (pCodecCtx->pix_fmt == pCodec->pix_fmts[i])
+                        if (pCodecCtx->pix_fmt == pix_fmts[i])
                         {
                             pix_fmt_supported = true;
                             break;
@@ -732,20 +747,28 @@ namespace dlib
                             << "Requested pixel format "
                             << av_get_pix_fmt_name(pCodecCtx->pix_fmt)
                             << " not supported. Changing to default "
-                            << av_get_pix_fmt_name(pCodec->pix_fmts[0]);
+                            << av_get_pix_fmt_name(pix_fmts[0]);
 
-                        pCodecCtx->pix_fmt = pCodec->pix_fmts[0];
+                        pCodecCtx->pix_fmt = pix_fmts[0];
                     }
                 }
 
                 // Audio properties
-                if (pCodec->supported_samplerates)
+                const int*  supported_samplerates{nullptr};
+                int         nsupported_samplerates{0};
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 13, 100)
+                avcodec_get_supported_config(pCodecCtx, NULL, AV_CODEC_CONFIG_SAMPLE_RATE, 0, (const void**)&supported_samplerates, &nsupported_samplerates);
+#else
+                supported_samplerates = pCodec->supported_samplerates;
+                for (; supported_samplerates != nullptr && supported_samplerates[nsupported_samplerates] != 0 ; ++nsupported_samplerates) {}
+#endif
+                if (supported_samplerates && nsupported_samplerates > 0)
                 {
                     bool sample_rate_supported = false;
 
-                    for (int i = 0 ; pCodec->supported_samplerates[i] != 0 ; i++)
+                    for (int i = 0 ; i < nsupported_samplerates ; ++i)
                     {
-                        if (pCodecCtx->sample_rate == pCodec->supported_samplerates[i])
+                        if (pCodecCtx->sample_rate == supported_samplerates[i])
                         {
                             sample_rate_supported = true;
                             break;
@@ -758,19 +781,26 @@ namespace dlib
                             << "Requested sample rate "
                             << pCodecCtx->sample_rate
                             << " not supported. Changing to default "
-                            << pCodec->supported_samplerates[0];
+                            << supported_samplerates[0];
 
-                        pCodecCtx->sample_rate = pCodec->supported_samplerates[0];
+                        pCodecCtx->sample_rate = supported_samplerates[0];
                     }
                 }
-
-                if (pCodec->sample_fmts)
+                const AVSampleFormat* sample_fmts{nullptr};
+                int                   nsample_fmts{0};
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 13, 100)
+                avcodec_get_supported_config(pCodecCtx, NULL, AV_CODEC_CONFIG_SAMPLE_FORMAT, 0, (const void**)&sample_fmts, &nsample_fmts);
+#else
+                sample_fmts = pCodec->sample_fmts;
+                for (; sample_fmts != nullptr && sample_fmts[nsample_fmts] != AV_SAMPLE_FMT_NONE ; ++nsample_fmts) {}
+#endif
+                if (sample_fmts && nsample_fmts > 0)
                 {
                     bool sample_fmt_supported = false;
 
-                    for (int i = 0 ; pCodec->sample_fmts[i] != AV_SAMPLE_FMT_NONE ; i++)
+                    for (int i = 0 ; i < nsample_fmts ; ++i)
                     {
-                        if (pCodecCtx->sample_fmt == pCodec->sample_fmts[i])
+                        if (pCodecCtx->sample_fmt == sample_fmts[i])
                         {
                             sample_fmt_supported = true;
                             break;
@@ -783,20 +813,28 @@ namespace dlib
                             << "Requested sample format "
                             << av_get_sample_fmt_name(pCodecCtx->sample_fmt)
                             << " not supported. Changing to default "
-                            << av_get_sample_fmt_name(pCodec->sample_fmts[0]);
+                            << av_get_sample_fmt_name(sample_fmts[0]);
 
-                        pCodecCtx->sample_fmt = pCodec->sample_fmts[0];
+                        pCodecCtx->sample_fmt = sample_fmts[0];
                     }
                 }
 
 #if FFMPEG_HAS_CH_LAYOUT
-                if (pCodec->ch_layouts)
+                const AVChannelLayout* ch_layouts{nullptr};
+                int                    nch_layouts{0};
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 13, 100)
+                avcodec_get_supported_config(pCodecCtx, NULL, AV_CODEC_CONFIG_CHANNEL_LAYOUT, 0, (const void**)&ch_layouts, &nch_layouts);
+#else
+                ch_layouts = pCodec->ch_layouts;
+                for (; ch_layouts != nullptr && av_channel_layout_check(&ch_layouts[nch_layouts]) ; ++nch_layouts) {}
+#endif
+                if (ch_layouts && nch_layouts > 0)
                 {
                     bool channel_layout_supported = false;
 
-                    for (int i = 0 ; av_channel_layout_check(&pCodec->ch_layouts[i]) ; ++i)
+                    for (int i = 0 ; i < nch_layouts ; ++i)
                     {
-                        if (av_channel_layout_compare(&pCodecCtx->ch_layout, &pCodec->ch_layouts[i]) == 0)
+                        if (av_channel_layout_compare(&pCodecCtx->ch_layout, &ch_layouts[i]) == 0)
                         {
                             channel_layout_supported = true;
                             break;
@@ -809,9 +847,9 @@ namespace dlib
                             << "Channel layout "
                             << details::get_channel_layout_str(pCodecCtx)
                             << " not supported. Changing to default "
-                            << details::get_channel_layout_str(pCodec->ch_layouts[0]);
+                            << details::get_channel_layout_str(ch_layouts[0]);
 
-                        av_channel_layout_copy(&pCodecCtx->ch_layout, &pCodec->ch_layouts[0]);
+                        av_channel_layout_copy(&pCodecCtx->ch_layout, &ch_layouts[0]);
                     }
                 }
 #else
