Sisyphus repository
Last update: 1 october 2023 | SRPMs: 18631 | Visits: 37369941
en ru br
ALT Linux repos
S:1.0.2-alt2
5.0: 0.9.9-alt2
4.1: 0.9.9-alt0.M41.1

Group :: Games/Strategy
RPM: hedgewars

 Main   Changelog   Spec   Patches   Sources   Download   Gear   Bugs and FR  Repocop 

Patch: ffmpeg6.0-support.patch
Download


From 71691fad8654031328f4af077fc32aaf29cdb7d0 Mon Sep 17 00:00:00 2001
From: Pekka Ristola <pekkarr@protonmail.com>
Date: Tue, 9 May 2023 20:11:47 +0300
Subject: [PATCH] Add support for ffmpeg 6.0
- Use the new send_frame/receive_packet API for encoding
- Use the new channel layout API for audio
- Fix audio recording
  - Copy codec parameters to the stream parameters
  - Set correct pts for audio frames
- Read audio samples from file directly to the refcounted AVFrame buffer instead of the `g_pSamples` buffer
- Use global AVPackets allocated with `av_packet_alloc`
- Stop trying to write more audio frames when `WriteAudioFrame` fails with a negative error code
- Fix segfault with `g_pContainer->url`. The field has to be allocated with `av_malloc` before writing to it. It's set to `NULL` by default.
- Properly free allocations with `avcodec_free_context` and `avformat_free_context`
---
 hedgewars/avwrapper/avwrapper.c | 234 +++++++++++++++++++++++++++-----
 1 file changed, 203 insertions(+), 31 deletions(-)
diff --git a/hedgewars/avwrapper/avwrapper.c b/hedgewars/avwrapper/avwrapper.c
index 6c0fe739b4..3daeb07b75 100644
--- a/hedgewars/avwrapper/avwrapper.c
+++ b/hedgewars/avwrapper/avwrapper.c
@@ -42,15 +42,19 @@
 #define UNUSED(x) (void)(x)
 
 static AVFormatContext* g_pContainer;
-static AVOutputFormat* g_pFormat;
+static const AVOutputFormat* g_pFormat;
 static AVStream* g_pAStream;
 static AVStream* g_pVStream;
 static AVFrame* g_pAFrame;
 static AVFrame* g_pVFrame;
-static AVCodec* g_pACodec;
-static AVCodec* g_pVCodec;
+static const AVCodec* g_pACodec;
+static const AVCodec* g_pVCodec;
 static AVCodecContext* g_pAudio;
 static AVCodecContext* g_pVideo;
+#if LIBAVCODEC_VERSION_MAJOR >= 58
+static AVPacket* g_pAPacket;
+static AVPacket* g_pVPacket;
+#endif
 
 static int g_Width, g_Height;
 static uint32_t g_Frequency, g_Channels;
@@ -58,8 +62,13 @@ static int g_VQuality;
 static AVRational g_Framerate;
 
 static FILE* g_pSoundFile;
+#if LIBAVUTIL_VERSION_MAJOR < 53
 static int16_t* g_pSamples;
+#endif
 static int g_NumSamples;
+#if LIBAVCODEC_VERSION_MAJOR >= 53
+static int64_t g_NextAudioPts;
+#endif
 
 
 // compatibility section
@@ -93,6 +102,8 @@ static void rescale_ts(AVPacket *pkt, AVRational ctb, AVRational stb)
     if (pkt->duration > 0)
         pkt->duration = av_rescale_q(pkt->duration, ctb, stb);
 }
+
+#define avcodec_free_context(ctx)           do { avcodec_close(*ctx); av_freep(ctx); } while (0)
 #endif
 
 #ifndef AV_CODEC_CAP_DELAY
@@ -165,8 +176,42 @@ static void Log(const char* pFmt, ...)
     AddFileLogRaw(Buffer);
 }
 
+#if LIBAVCODEC_VERSION_MAJOR >= 58
+static int EncodeAndWriteFrame(
+        const AVStream* pStream,
+        AVCodecContext* pCodecContext,
+        const AVFrame* pFrame,
+        AVPacket* pPacket)
+{
+    int ret;
+
+    ret = avcodec_send_frame(pCodecContext, pFrame);
+    if (ret < 0)
+        return FatalError("avcodec_send_frame failed: %d", ret);
+    while (1)
+    {
+        ret = avcodec_receive_packet(pCodecContext, pPacket);
+        if (ret == AVERROR(EAGAIN))
+            return 1;
+        else if (ret == AVERROR_EOF)
+            return 0;
+        else if (ret < 0)
+            return FatalError("avcodec_receive_packet failed: %d", ret);
+
+        av_packet_rescale_ts(pPacket, pCodecContext->time_base, pStream->time_base);
+
+        // Write the compressed frame to the media file.
+        pPacket->stream_index = pStream->index;
+        ret = av_interleaved_write_frame(g_pContainer, pPacket);
+        if (ret != 0)
+            return FatalError("Error while writing frame: %d", ret);
+    }
+}
+#endif
+
 static void AddAudioStream()
 {
+    int ret;
     g_pAStream = avformat_new_stream(g_pContainer, g_pACodec);
     if(!g_pAStream)
     {
@@ -176,20 +221,44 @@ static void AddAudioStream()
     g_pAStream->id = 1;
 
 #if LIBAVCODEC_VERSION_MAJOR >= 59
-    const AVCodec *audio_st_codec = avcodec_find_decoder(g_pAStream->codecpar->codec_id);
-    g_pAudio = avcodec_alloc_context3(audio_st_codec);
-    avcodec_parameters_to_context(g_pAudio, g_pAStream->codecpar);
+    g_pAudio = avcodec_alloc_context3(g_pACodec);
 #else
     g_pAudio = g_pAStream->codec;
-#endif
 
     avcodec_get_context_defaults3(g_pAudio, g_pACodec);
     g_pAudio->codec_id = g_pACodec->id;
+#endif
 
     // put parameters
     g_pAudio->sample_fmt = AV_SAMPLE_FMT_S16;
     g_pAudio->sample_rate = g_Frequency;
+#if LIBAVCODEC_VERSION_MAJOR >= 60
+    const AVChannelLayout* pChLayout = g_pACodec->ch_layouts;
+    if (pChLayout)
+    {
+        for (; pChLayout->nb_channels; pChLayout++)
+        {
+            if (pChLayout->nb_channels == g_Channels)
+            {
+                ret = av_channel_layout_copy(&g_pAudio->ch_layout, pChLayout);
+                if (ret != 0)
+                {
+                    Log("Channel layout copy failed: %d\n", ret);
+                    return;
+                }
+                break;
+            }
+        }
+    }
+    if (!g_pAudio->ch_layout.nb_channels)
+    {
+        // no suitable layout found
+        g_pAudio->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC;
+        g_pAudio->ch_layout.nb_channels = g_Channels;
+    }
+#else
     g_pAudio->channels = g_Channels;
+#endif
 
     // set time base as invers of sample rate
     g_pAudio->time_base.den = g_pAStream->time_base.den = g_Frequency;
@@ -213,6 +282,15 @@ static void AddAudioStream()
         return;
     }
 
+#if LIBAVCODEC_VERSION_MAJOR >= 58
+    ret = avcodec_parameters_from_context(g_pAStream->codecpar, g_pAudio);
+    if (ret < 0)
+    {
+        Log("Could not copy parameters from codec context: %d\n", ret);
+        return;
+    }
+#endif
+
 #if LIBAVCODEC_VERSION_MAJOR >= 54
     if (g_pACodec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)
 #else
@@ -221,13 +299,46 @@ static void AddAudioStream()
         g_NumSamples = 4096;
     else
         g_NumSamples = g_pAudio->frame_size;
-    g_pSamples = (int16_t*)av_malloc(g_NumSamples*g_Channels*sizeof(int16_t));
     g_pAFrame = av_frame_alloc();
     if (!g_pAFrame)
     {
         Log("Could not allocate frame\n");
         return;
     }
+#if LIBAVUTIL_VERSION_MAJOR >= 53
+#if LIBAVCODEC_VERSION_MAJOR >= 60
+    ret = av_channel_layout_copy(&g_pAFrame->ch_layout, &g_pAudio->ch_layout);
+    if (ret != 0)
+    {
+        Log("Channel layout copy for frame failed: %d\n", ret);
+        return;
+    }
+#else
+    g_pAFrame->channels = g_pAudio->channels;
+#endif
+    g_pAFrame->format = g_pAudio->sample_fmt;
+    g_pAFrame->sample_rate = g_pAudio->sample_rate;
+    g_pAFrame->nb_samples = g_NumSamples;
+    ret = av_frame_get_buffer(g_pAFrame, 1);
+    if (ret < 0)
+    {
+        Log("Failed to allocate frame buffer: %d\n", ret);
+        return;
+    }
+#else
+    g_pSamples = (int16_t*)av_malloc(g_NumSamples*g_Channels*sizeof(int16_t));
+#endif
+#if LIBAVCODEC_VERSION_MAJOR >= 58
+    g_pAPacket = av_packet_alloc();
+    if (!g_pAPacket)
+    {
+        Log("Could not allocate audio packet\n");
+        return;
+    }
+#endif
+#if LIBAVCODEC_VERSION_MAJOR >= 53
+    g_NextAudioPts = 0;
+#endif
 }
 
 // returns non-zero if there is more sound, -1 in case of error
@@ -236,22 +347,46 @@ static int WriteAudioFrame()
     if (!g_pAStream)
         return 0;
 
-    AVPacket Packet;
-    av_init_packet(&Packet);
-    Packet.data = NULL;
-    Packet.size = 0;
+    int ret;
+    int16_t* pData;
+#if LIBAVUTIL_VERSION_MAJOR >= 53
+    ret = av_frame_make_writable(g_pAFrame);
+    if (ret < 0)
+        return FatalError("Could not make audio frame writable: %d", ret);
+    pData = (int16_t*) g_pAFrame->data[0];
+#else
+    pData = g_pSamples;
+#endif
 
-    int NumSamples = fread(g_pSamples, 2*g_Channels, g_NumSamples, g_pSoundFile);
+    int NumSamples = fread(pData, 2*g_Channels, g_NumSamples, g_pSoundFile);
 
 #if LIBAVCODEC_VERSION_MAJOR >= 53
     AVFrame* pFrame = NULL;
     if (NumSamples > 0)
     {
         g_pAFrame->nb_samples = NumSamples;
+        g_pAFrame->pts = g_NextAudioPts;
+        g_NextAudioPts += NumSamples;
+#if LIBAVUTIL_VERSION_MAJOR < 53
         avcodec_fill_audio_frame(g_pAFrame, g_Channels, AV_SAMPLE_FMT_S16,
-                                 (uint8_t*)g_pSamples, NumSamples*2*g_Channels, 1);
+                                 (uint8_t*)pData, NumSamples*2*g_Channels, 1);
+#endif
         pFrame = g_pAFrame;
     }
+#endif
+
+#if LIBAVCODEC_VERSION_MAJOR >= 58
+    ret = EncodeAndWriteFrame(g_pAStream, g_pAudio, pFrame, g_pAPacket);
+    if (ret < 0)
+        return FatalError("Audio frame processing failed");
+    return ret;
+#else
+    AVPacket Packet;
+    av_init_packet(&Packet);
+    Packet.data = NULL;
+    Packet.size = 0;
+
+#if LIBAVCODEC_VERSION_MAJOR >= 53
     // when NumSamples == 0 we still need to call encode_audio2 to flush
     int got_packet;
     if (avcodec_encode_audio2(g_pAudio, &Packet, pFrame, &got_packet) != 0)
@@ -266,7 +401,7 @@ static int WriteAudioFrame()
     int BufferSize = OUTBUFFER_SIZE;
     if (g_pAudio->frame_size == 0)
         BufferSize = NumSamples*g_Channels*2;
-    Packet.size = avcodec_encode_audio(g_pAudio, g_OutBuffer, BufferSize, g_pSamples);
+    Packet.size = avcodec_encode_audio(g_pAudio, g_OutBuffer, BufferSize, pData);
     if (Packet.size == 0)
         return 1;
     if (g_pAudio->coded_frame && g_pAudio->coded_frame->pts != AV_NOPTS_VALUE)
@@ -280,25 +415,25 @@ static int WriteAudioFrame()
     if (av_interleaved_write_frame(g_pContainer, &Packet) != 0)
         return FatalError("Error while writing audio frame");
     return 1;
+#endif
 }
 
 // add a video output stream
 static int AddVideoStream()
 {
+    int ret;
     g_pVStream = avformat_new_stream(g_pContainer, g_pVCodec);
     if (!g_pVStream)
         return FatalError("Could not allocate video stream");
 
 #if LIBAVCODEC_VERSION_MAJOR >= 59
-    const AVCodec *video_st_codec = avcodec_find_decoder(g_pVStream->codecpar->codec_id);
-    g_pVideo = avcodec_alloc_context3(video_st_codec);
-    avcodec_parameters_to_context(g_pVideo, g_pVStream->codecpar);
+    g_pVideo = avcodec_alloc_context3(g_pVCodec);
 #else
     g_pVideo = g_pVStream->codec;
-#endif
 
     avcodec_get_context_defaults3(g_pVideo, g_pVCodec);
     g_pVideo->codec_id = g_pVCodec->id;
+#endif
 
     // put parameters
     // resolution must be a multiple of two
@@ -361,6 +496,12 @@ static int AddVideoStream()
     if (avcodec_open2(g_pVideo, g_pVCodec, NULL) < 0)
         return FatalError("Could not open video codec %s", g_pVCodec->long_name);
 
+#if LIBAVCODEC_VERSION_MAJOR >= 58
+    ret = avcodec_parameters_from_context(g_pVStream->codecpar, g_pVideo);
+    if (ret < 0)
+        return FatalError("Could not copy parameters from codec context: %d", ret);
+#endif
+
     g_pVFrame = av_frame_alloc();
     if (!g_pVFrame)
         return FatalError("Could not allocate frame");
@@ -370,6 +511,12 @@ static int AddVideoStream()
     g_pVFrame->height = g_Height;
     g_pVFrame->format = AV_PIX_FMT_YUV420P;
 
+#if LIBAVCODEC_VERSION_MAJOR >= 58
+    g_pVPacket = av_packet_alloc();
+    if (!g_pVPacket)
+        return FatalError("Could not allocate packet");
+#endif
+
     return avcodec_default_get_buffer2(g_pVideo, g_pVFrame, 0);
 }
 
@@ -380,6 +527,10 @@ static int WriteFrame(AVFrame* pFrame)
     // write interleaved audio frame
     if (g_pAStream)
     {
+#if LIBAVCODEC_VERSION_MAJOR >= 58
+        if (!g_pAPacket)
+            return FatalError("Error while writing video frame: g_pAPacket does not exist");
+#endif
         VideoTime = (double)g_pVFrame->pts * g_pVStream->time_base.num/g_pVStream->time_base.den;
         do
         {
@@ -388,7 +539,7 @@ static int WriteFrame(AVFrame* pFrame)
             AudioTime = (double)g_pAFrame->pts * g_pAStream->time_base.num/g_pAStream->time_base.den;
             ret = WriteAudioFrame();
         }
-        while (AudioTime < VideoTime && ret);
+        while (AudioTime < VideoTime && ret > 0);
         if (ret < 0)
             return ret;
     }
@@ -396,13 +547,18 @@ static int WriteFrame(AVFrame* pFrame)
     if (!g_pVStream)
         return 0;
 
+    g_pVFrame->pts++;
+#if LIBAVCODEC_VERSION_MAJOR >= 58
+    ret = EncodeAndWriteFrame(g_pVStream, g_pVideo, pFrame, g_pVPacket);
+    if (ret < 0)
+        return FatalError("Video frame processing failed");
+    return ret;
+#else
     AVPacket Packet;
     av_init_packet(&Packet);
     Packet.data = NULL;
     Packet.size = 0;
 
-    g_pVFrame->pts++;
-#if LIBAVCODEC_VERSION_MAJOR < 58
     if (g_pFormat->flags & AVFMT_RAWPICTURE)
     {
         /* raw video case. The API will change slightly in the near
@@ -417,7 +573,6 @@ static int WriteFrame(AVFrame* pFrame)
         return 0;
     }
     else
-#endif
     {
 #if LIBAVCODEC_VERSION_MAJOR >= 54
         int got_packet;
@@ -447,6 +602,7 @@ static int WriteFrame(AVFrame* pFrame)
 
         return 1;
     }
+#endif
 }
 
 AVWRAP_DECL int AVWrapper_WriteFrame(uint8_t *buf)
@@ -539,9 +695,13 @@ AVWRAP_DECL int AVWrapper_Init(
     char ext[16];
     strncpy(ext, g_pFormat->extensions, 16);
     ext[15] = 0;
-    ext[strcspn(ext,",")] = 0;
+    size_t extLen = strcspn(ext, ",");
+    ext[extLen] = 0;
 #if LIBAVCODEC_VERSION_MAJOR >= 59
-    snprintf(g_pContainer->url, sizeof(g_pContainer->url), "%s.%s", pFilename, ext);
+    // pFilename + dot + ext + null byte
+    size_t urlLen = strlen(pFilename) + 1 + extLen + 1;
+    g_pContainer->url = av_malloc(urlLen);
+    snprintf(g_pContainer->url, urlLen, "%s.%s", pFilename, ext);
 #else
     snprintf(g_pContainer->filename, sizeof(g_pContainer->filename), "%s.%s", pFilename, ext);
 #endif
@@ -636,21 +796,33 @@ AVWRAP_DECL int AVWrapper_Close()
     // free everything
     if (g_pVStream)
     {
-        avcodec_close(g_pVideo);
-        av_free(g_pVideo);
-        av_free(g_pVStream);
+        avcodec_free_context(&g_pVideo);
         av_frame_free(&g_pVFrame);
+#if LIBAVCODEC_VERSION_MAJOR >= 58
+        av_packet_free(&g_pVPacket);
+#endif
     }
     if (g_pAStream)
     {
-        avcodec_close(g_pAudio);
-        av_free(g_pAudio);
-        av_free(g_pAStream);
+        avcodec_free_context(&g_pAudio);
         av_frame_free(&g_pAFrame);
+#if LIBAVCODEC_VERSION_MAJOR >= 58
+        av_packet_free(&g_pAPacket);
+#endif
+#if LIBAVUTIL_VERSION_MAJOR < 53
         av_free(g_pSamples);
+#endif
         fclose(g_pSoundFile);
     }
 
+#if LIBAVCODEC_VERSION_MAJOR >= 59
+    avformat_free_context(g_pContainer);
+#else
+    if (g_pVStream)
+        av_free(g_pVStream);
+    if (g_pAStream)
+        av_free(g_pAStream);
     av_free(g_pContainer);
+#endif
     return 0;
 }
 
design & coding: Vladimir Lettiev aka crux © 2004-2005, Andrew Avramenko aka liks © 2007-2008
current maintainer: Michael Shigorin