| 1 | From 9445c40eb179047e85c47acedb724eee42e3f0df Mon Sep 17 00:00:00 2001
|
| 2 | From: Pekka Ristola <[email protected]>
|
| 3 | Date: Mon, 27 Jan 2025 19:08:05 +0100
|
| 4 | Subject: [PATCH] Add support for ffmpeg 6.0 - Use the new
|
| 5 | send_frame/receive_packet API for encoding - Use the new channel layout API
|
| 6 | for audio - Fix audio recording - Copy codec parameters to the stream
|
| 7 | parameters - Set correct pts for audio frames - Read audio samples from
|
| 8 | file directly to the refcounted AVFrame buffer instead of the `g_pSamples`
|
| 9 | buffer - Use global AVPackets allocated with `av_packet_alloc` - Stop trying
|
| 10 | to write more audio frames when `WriteAudioFrame` fails with a negative error
|
| 11 | code - Fix segfault with `g_pContainer->url`. The field has to be allocated
|
| 12 | with `av_malloc` before writing to it. It's set to `NULL` by default. -
|
| 13 | Properly free allocations with `avcodec_free_context` and
|
| 14 | `avformat_free_context`
|
| 15 |
|
| 16 | ---
|
| 17 | hedgewars/avwrapper/avwrapper.c | 234 +++++++++++++++++++++++++++-----
|
| 18 | 1 file changed, 203 insertions(+), 31 deletions(-)
|
| 19 |
|
| 20 | diff --git a/hedgewars/avwrapper/avwrapper.c b/hedgewars/avwrapper/avwrapper.c
|
| 21 | index 6c0fe739b4..3daeb07b75 100644
|
| 22 | |
| 23 | |
| 24 | @@ -42,15 +42,19 @@
|
| 25 | #define UNUSED(x) (void)(x)
|
| 26 |
|
| 27 | static AVFormatContext* g_pContainer;
|
| 28 | -static AVOutputFormat* g_pFormat;
|
| 29 | +static const AVOutputFormat* g_pFormat;
|
| 30 | static AVStream* g_pAStream;
|
| 31 | static AVStream* g_pVStream;
|
| 32 | static AVFrame* g_pAFrame;
|
| 33 | static AVFrame* g_pVFrame;
|
| 34 | -static AVCodec* g_pACodec;
|
| 35 | -static AVCodec* g_pVCodec;
|
| 36 | +static const AVCodec* g_pACodec;
|
| 37 | +static const AVCodec* g_pVCodec;
|
| 38 | static AVCodecContext* g_pAudio;
|
| 39 | static AVCodecContext* g_pVideo;
|
| 40 | +#if LIBAVCODEC_VERSION_MAJOR >= 58
|
| 41 | +static AVPacket* g_pAPacket;
|
| 42 | +static AVPacket* g_pVPacket;
|
| 43 | +#endif
|
| 44 |
|
| 45 | static int g_Width, g_Height;
|
| 46 | static uint32_t g_Frequency, g_Channels;
|
| 47 | @@ -58,8 +62,13 @@ static int g_VQuality;
|
| 48 | static AVRational g_Framerate;
|
| 49 |
|
| 50 | static FILE* g_pSoundFile;
|
| 51 | +#if LIBAVUTIL_VERSION_MAJOR < 53
|
| 52 | static int16_t* g_pSamples;
|
| 53 | +#endif
|
| 54 | static int g_NumSamples;
|
| 55 | +#if LIBAVCODEC_VERSION_MAJOR >= 53
|
| 56 | +static int64_t g_NextAudioPts;
|
| 57 | +#endif
|
| 58 |
|
| 59 |
|
| 60 | // compatibility section
|
| 61 | @@ -93,6 +102,8 @@ static void rescale_ts(AVPacket *pkt, AVRational ctb, AVRational stb)
|
| 62 | if (pkt->duration > 0)
|
| 63 | pkt->duration = av_rescale_q(pkt->duration, ctb, stb);
|
| 64 | }
|
| 65 | +
|
| 66 | +#define avcodec_free_context(ctx) do { avcodec_close(*ctx); av_freep(ctx); } while (0)
|
| 67 | #endif
|
| 68 |
|
| 69 | #ifndef AV_CODEC_CAP_DELAY
|
| 70 | @@ -165,8 +176,42 @@ static void Log(const char* pFmt, ...)
|
| 71 | AddFileLogRaw(Buffer);
|
| 72 | }
|
| 73 |
|
| 74 | +#if LIBAVCODEC_VERSION_MAJOR >= 58
|
| 75 | +static int EncodeAndWriteFrame(
|
| 76 | + const AVStream* pStream,
|
| 77 | + AVCodecContext* pCodecContext,
|
| 78 | + const AVFrame* pFrame,
|
| 79 | + AVPacket* pPacket)
|
| 80 | +{
|
| 81 | + int ret;
|
| 82 | +
|
| 83 | + ret = avcodec_send_frame(pCodecContext, pFrame);
|
| 84 | + if (ret < 0)
|
| 85 | + return FatalError("avcodec_send_frame failed: %d", ret);
|
| 86 | + while (1)
|
| 87 | + {
|
| 88 | + ret = avcodec_receive_packet(pCodecContext, pPacket);
|
| 89 | + if (ret == AVERROR(EAGAIN))
|
| 90 | + return 1;
|
| 91 | + else if (ret == AVERROR_EOF)
|
| 92 | + return 0;
|
| 93 | + else if (ret < 0)
|
| 94 | + return FatalError("avcodec_receive_packet failed: %d", ret);
|
| 95 | +
|
| 96 | + av_packet_rescale_ts(pPacket, pCodecContext->time_base, pStream->time_base);
|
| 97 | +
|
| 98 | + // Write the compressed frame to the media file.
|
| 99 | + pPacket->stream_index = pStream->index;
|
| 100 | + ret = av_interleaved_write_frame(g_pContainer, pPacket);
|
| 101 | + if (ret != 0)
|
| 102 | + return FatalError("Error while writing frame: %d", ret);
|
| 103 | + }
|
| 104 | +}
|
| 105 | +#endif
|
| 106 | +
|
| 107 | static void AddAudioStream()
|
| 108 | {
|
| 109 | + int ret;
|
| 110 | g_pAStream = avformat_new_stream(g_pContainer, g_pACodec);
|
| 111 | if(!g_pAStream)
|
| 112 | {
|
| 113 | @@ -176,20 +221,44 @@ static void AddAudioStream()
|
| 114 | g_pAStream->id = 1;
|
| 115 |
|
| 116 | #if LIBAVCODEC_VERSION_MAJOR >= 59
|
| 117 | - const AVCodec *audio_st_codec = avcodec_find_decoder(g_pAStream->codecpar->codec_id);
|
| 118 | - g_pAudio = avcodec_alloc_context3(audio_st_codec);
|
| 119 | - avcodec_parameters_to_context(g_pAudio, g_pAStream->codecpar);
|
| 120 | + g_pAudio = avcodec_alloc_context3(g_pACodec);
|
| 121 | #else
|
| 122 | g_pAudio = g_pAStream->codec;
|
| 123 | -#endif
|
| 124 |
|
| 125 | avcodec_get_context_defaults3(g_pAudio, g_pACodec);
|
| 126 | g_pAudio->codec_id = g_pACodec->id;
|
| 127 | +#endif
|
| 128 |
|
| 129 | // put parameters
|
| 130 | g_pAudio->sample_fmt = AV_SAMPLE_FMT_S16;
|
| 131 | g_pAudio->sample_rate = g_Frequency;
|
| 132 | +#if LIBAVCODEC_VERSION_MAJOR >= 60
|
| 133 | + const AVChannelLayout* pChLayout = g_pACodec->ch_layouts;
|
| 134 | + if (pChLayout)
|
| 135 | + {
|
| 136 | + for (; pChLayout->nb_channels; pChLayout++)
|
| 137 | + {
|
| 138 | + if (pChLayout->nb_channels == g_Channels)
|
| 139 | + {
|
| 140 | + ret = av_channel_layout_copy(&g_pAudio->ch_layout, pChLayout);
|
| 141 | + if (ret != 0)
|
| 142 | + {
|
| 143 | + Log("Channel layout copy failed: %d\n", ret);
|
| 144 | + return;
|
| 145 | + }
|
| 146 | + break;
|
| 147 | + }
|
| 148 | + }
|
| 149 | + }
|
| 150 | + if (!g_pAudio->ch_layout.nb_channels)
|
| 151 | + {
|
| 152 | + // no suitable layout found
|
| 153 | + g_pAudio->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC;
|
| 154 | + g_pAudio->ch_layout.nb_channels = g_Channels;
|
| 155 | + }
|
| 156 | +#else
|
| 157 | g_pAudio->channels = g_Channels;
|
| 158 | +#endif
|
| 159 |
|
| 160 | // set time base as invers of sample rate
|
| 161 | g_pAudio->time_base.den = g_pAStream->time_base.den = g_Frequency;
|
| 162 | @@ -213,6 +282,15 @@ static void AddAudioStream()
|
| 163 | return;
|
| 164 | }
|
| 165 |
|
| 166 | +#if LIBAVCODEC_VERSION_MAJOR >= 58
|
| 167 | + ret = avcodec_parameters_from_context(g_pAStream->codecpar, g_pAudio);
|
| 168 | + if (ret < 0)
|
| 169 | + {
|
| 170 | + Log("Could not copy parameters from codec context: %d\n", ret);
|
| 171 | + return;
|
| 172 | + }
|
| 173 | +#endif
|
| 174 | +
|
| 175 | #if LIBAVCODEC_VERSION_MAJOR >= 54
|
| 176 | if (g_pACodec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)
|
| 177 | #else
|
| 178 | @@ -221,13 +299,46 @@ static void AddAudioStream()
|
| 179 | g_NumSamples = 4096;
|
| 180 | else
|
| 181 | g_NumSamples = g_pAudio->frame_size;
|
| 182 | - g_pSamples = (int16_t*)av_malloc(g_NumSamples*g_Channels*sizeof(int16_t));
|
| 183 | g_pAFrame = av_frame_alloc();
|
| 184 | if (!g_pAFrame)
|
| 185 | {
|
| 186 | Log("Could not allocate frame\n");
|
| 187 | return;
|
| 188 | }
|
| 189 | +#if LIBAVUTIL_VERSION_MAJOR >= 53
|
| 190 | +#if LIBAVCODEC_VERSION_MAJOR >= 60
|
| 191 | + ret = av_channel_layout_copy(&g_pAFrame->ch_layout, &g_pAudio->ch_layout);
|
| 192 | + if (ret != 0)
|
| 193 | + {
|
| 194 | + Log("Channel layout copy for frame failed: %d\n", ret);
|
| 195 | + return;
|
| 196 | + }
|
| 197 | +#else
|
| 198 | + g_pAFrame->channels = g_pAudio->channels;
|
| 199 | +#endif
|
| 200 | + g_pAFrame->format = g_pAudio->sample_fmt;
|
| 201 | + g_pAFrame->sample_rate = g_pAudio->sample_rate;
|
| 202 | + g_pAFrame->nb_samples = g_NumSamples;
|
| 203 | + ret = av_frame_get_buffer(g_pAFrame, 1);
|
| 204 | + if (ret < 0)
|
| 205 | + {
|
| 206 | + Log("Failed to allocate frame buffer: %d\n", ret);
|
| 207 | + return;
|
| 208 | + }
|
| 209 | +#else
|
| 210 | + g_pSamples = (int16_t*)av_malloc(g_NumSamples*g_Channels*sizeof(int16_t));
|
| 211 | +#endif
|
| 212 | +#if LIBAVCODEC_VERSION_MAJOR >= 58
|
| 213 | + g_pAPacket = av_packet_alloc();
|
| 214 | + if (!g_pAPacket)
|
| 215 | + {
|
| 216 | + Log("Could not allocate audio packet\n");
|
| 217 | + return;
|
| 218 | + }
|
| 219 | +#endif
|
| 220 | +#if LIBAVCODEC_VERSION_MAJOR >= 53
|
| 221 | + g_NextAudioPts = 0;
|
| 222 | +#endif
|
| 223 | }
|
| 224 |
|
| 225 | // returns non-zero if there is more sound, -1 in case of error
|
| 226 | @@ -236,22 +347,46 @@ static int WriteAudioFrame()
|
| 227 | if (!g_pAStream)
|
| 228 | return 0;
|
| 229 |
|
| 230 | - AVPacket Packet;
|
| 231 | - av_init_packet(&Packet);
|
| 232 | - Packet.data = NULL;
|
| 233 | - Packet.size = 0;
|
| 234 | + int ret;
|
| 235 | + int16_t* pData;
|
| 236 | +#if LIBAVUTIL_VERSION_MAJOR >= 53
|
| 237 | + ret = av_frame_make_writable(g_pAFrame);
|
| 238 | + if (ret < 0)
|
| 239 | + return FatalError("Could not make audio frame writable: %d", ret);
|
| 240 | + pData = (int16_t*) g_pAFrame->data[0];
|
| 241 | +#else
|
| 242 | + pData = g_pSamples;
|
| 243 | +#endif
|
| 244 |
|
| 245 | - int NumSamples = fread(g_pSamples, 2*g_Channels, g_NumSamples, g_pSoundFile);
|
| 246 | + int NumSamples = fread(pData, 2*g_Channels, g_NumSamples, g_pSoundFile);
|
| 247 |
|
| 248 | #if LIBAVCODEC_VERSION_MAJOR >= 53
|
| 249 | AVFrame* pFrame = NULL;
|
| 250 | if (NumSamples > 0)
|
| 251 | {
|
| 252 | g_pAFrame->nb_samples = NumSamples;
|
| 253 | + g_pAFrame->pts = g_NextAudioPts;
|
| 254 | + g_NextAudioPts += NumSamples;
|
| 255 | +#if LIBAVUTIL_VERSION_MAJOR < 53
|
| 256 | avcodec_fill_audio_frame(g_pAFrame, g_Channels, AV_SAMPLE_FMT_S16,
|
| 257 | - (uint8_t*)g_pSamples, NumSamples*2*g_Channels, 1);
|
| 258 | + (uint8_t*)pData, NumSamples*2*g_Channels, 1);
|
| 259 | +#endif
|
| 260 | pFrame = g_pAFrame;
|
| 261 | }
|
| 262 | +#endif
|
| 263 | +
|
| 264 | +#if LIBAVCODEC_VERSION_MAJOR >= 58
|
| 265 | + ret = EncodeAndWriteFrame(g_pAStream, g_pAudio, pFrame, g_pAPacket);
|
| 266 | + if (ret < 0)
|
| 267 | + return FatalError("Audio frame processing failed");
|
| 268 | + return ret;
|
| 269 | +#else
|
| 270 | + AVPacket Packet;
|
| 271 | + av_init_packet(&Packet);
|
| 272 | + Packet.data = NULL;
|
| 273 | + Packet.size = 0;
|
| 274 | +
|
| 275 | +#if LIBAVCODEC_VERSION_MAJOR >= 53
|
| 276 | // when NumSamples == 0 we still need to call encode_audio2 to flush
|
| 277 | int got_packet;
|
| 278 | if (avcodec_encode_audio2(g_pAudio, &Packet, pFrame, &got_packet) != 0)
|
| 279 | @@ -266,7 +401,7 @@ static int WriteAudioFrame()
|
| 280 | int BufferSize = OUTBUFFER_SIZE;
|
| 281 | if (g_pAudio->frame_size == 0)
|
| 282 | BufferSize = NumSamples*g_Channels*2;
|
| 283 | - Packet.size = avcodec_encode_audio(g_pAudio, g_OutBuffer, BufferSize, g_pSamples);
|
| 284 | + Packet.size = avcodec_encode_audio(g_pAudio, g_OutBuffer, BufferSize, pData);
|
| 285 | if (Packet.size == 0)
|
| 286 | return 1;
|
| 287 | if (g_pAudio->coded_frame && g_pAudio->coded_frame->pts != AV_NOPTS_VALUE)
|
| 288 | @@ -280,25 +415,25 @@ static int WriteAudioFrame()
|
| 289 | if (av_interleaved_write_frame(g_pContainer, &Packet) != 0)
|
| 290 | return FatalError("Error while writing audio frame");
|
| 291 | return 1;
|
| 292 | +#endif
|
| 293 | }
|
| 294 |
|
| 295 | // add a video output stream
|
| 296 | static int AddVideoStream()
|
| 297 | {
|
| 298 | + int ret;
|
| 299 | g_pVStream = avformat_new_stream(g_pContainer, g_pVCodec);
|
| 300 | if (!g_pVStream)
|
| 301 | return FatalError("Could not allocate video stream");
|
| 302 |
|
| 303 | #if LIBAVCODEC_VERSION_MAJOR >= 59
|
| 304 | - const AVCodec *video_st_codec = avcodec_find_decoder(g_pVStream->codecpar->codec_id);
|
| 305 | - g_pVideo = avcodec_alloc_context3(video_st_codec);
|
| 306 | - avcodec_parameters_to_context(g_pVideo, g_pVStream->codecpar);
|
| 307 | + g_pVideo = avcodec_alloc_context3(g_pVCodec);
|
| 308 | #else
|
| 309 | g_pVideo = g_pVStream->codec;
|
| 310 | -#endif
|
| 311 |
|
| 312 | avcodec_get_context_defaults3(g_pVideo, g_pVCodec);
|
| 313 | g_pVideo->codec_id = g_pVCodec->id;
|
| 314 | +#endif
|
| 315 |
|
| 316 | // put parameters
|
| 317 | // resolution must be a multiple of two
|
| 318 | @@ -361,6 +496,12 @@ static int AddVideoStream()
|
| 319 | if (avcodec_open2(g_pVideo, g_pVCodec, NULL) < 0)
|
| 320 | return FatalError("Could not open video codec %s", g_pVCodec->long_name);
|
| 321 |
|
| 322 | +#if LIBAVCODEC_VERSION_MAJOR >= 58
|
| 323 | + ret = avcodec_parameters_from_context(g_pVStream->codecpar, g_pVideo);
|
| 324 | + if (ret < 0)
|
| 325 | + return FatalError("Could not copy parameters from codec context: %d", ret);
|
| 326 | +#endif
|
| 327 | +
|
| 328 | g_pVFrame = av_frame_alloc();
|
| 329 | if (!g_pVFrame)
|
| 330 | return FatalError("Could not allocate frame");
|
| 331 | @@ -370,6 +511,12 @@ static int AddVideoStream()
|
| 332 | g_pVFrame->height = g_Height;
|
| 333 | g_pVFrame->format = AV_PIX_FMT_YUV420P;
|
| 334 |
|
| 335 | +#if LIBAVCODEC_VERSION_MAJOR >= 58
|
| 336 | + g_pVPacket = av_packet_alloc();
|
| 337 | + if (!g_pVPacket)
|
| 338 | + return FatalError("Could not allocate packet");
|
| 339 | +#endif
|
| 340 | +
|
| 341 | return avcodec_default_get_buffer2(g_pVideo, g_pVFrame, 0);
|
| 342 | }
|
| 343 |
|
| 344 | @@ -380,6 +527,10 @@ static int WriteFrame(AVFrame* pFrame)
|
| 345 | // write interleaved audio frame
|
| 346 | if (g_pAStream)
|
| 347 | {
|
| 348 | +#if LIBAVCODEC_VERSION_MAJOR >= 58
|
| 349 | + if (!g_pAPacket)
|
| 350 | + return FatalError("Error while writing video frame: g_pAPacket does not exist");
|
| 351 | +#endif
|
| 352 | VideoTime = (double)g_pVFrame->pts * g_pVStream->time_base.num/g_pVStream->time_base.den;
|
| 353 | do
|
| 354 | {
|
| 355 | @@ -388,7 +539,7 @@ static int WriteFrame(AVFrame* pFrame)
|
| 356 | AudioTime = (double)g_pAFrame->pts * g_pAStream->time_base.num/g_pAStream->time_base.den;
|
| 357 | ret = WriteAudioFrame();
|
| 358 | }
|
| 359 | - while (AudioTime < VideoTime && ret);
|
| 360 | + while (AudioTime < VideoTime && ret > 0);
|
| 361 | if (ret < 0)
|
| 362 | return ret;
|
| 363 | }
|
| 364 | @@ -396,13 +547,18 @@ static int WriteFrame(AVFrame* pFrame)
|
| 365 | if (!g_pVStream)
|
| 366 | return 0;
|
| 367 |
|
| 368 | + g_pVFrame->pts++;
|
| 369 | +#if LIBAVCODEC_VERSION_MAJOR >= 58
|
| 370 | + ret = EncodeAndWriteFrame(g_pVStream, g_pVideo, pFrame, g_pVPacket);
|
| 371 | + if (ret < 0)
|
| 372 | + return FatalError("Video frame processing failed");
|
| 373 | + return ret;
|
| 374 | +#else
|
| 375 | AVPacket Packet;
|
| 376 | av_init_packet(&Packet);
|
| 377 | Packet.data = NULL;
|
| 378 | Packet.size = 0;
|
| 379 |
|
| 380 | - g_pVFrame->pts++;
|
| 381 | -#if LIBAVCODEC_VERSION_MAJOR < 58
|
| 382 | if (g_pFormat->flags & AVFMT_RAWPICTURE)
|
| 383 | {
|
| 384 | /* raw video case. The API will change slightly in the near
|
| 385 | @@ -417,7 +573,6 @@ static int WriteFrame(AVFrame* pFrame)
|
| 386 | return 0;
|
| 387 | }
|
| 388 | else
|
| 389 | -#endif
|
| 390 | {
|
| 391 | #if LIBAVCODEC_VERSION_MAJOR >= 54
|
| 392 | int got_packet;
|
| 393 | @@ -447,6 +602,7 @@ static int WriteFrame(AVFrame* pFrame)
|
| 394 |
|
| 395 | return 1;
|
| 396 | }
|
| 397 | +#endif
|
| 398 | }
|
| 399 |
|
| 400 | AVWRAP_DECL int AVWrapper_WriteFrame(uint8_t *buf)
|
| 401 | @@ -539,9 +695,13 @@ AVWRAP_DECL int AVWrapper_Init(
|
| 402 | char ext[16];
|
| 403 | strncpy(ext, g_pFormat->extensions, 16);
|
| 404 | ext[15] = 0;
|
| 405 | - ext[strcspn(ext,",")] = 0;
|
| 406 | + size_t extLen = strcspn(ext, ",");
|
| 407 | + ext[extLen] = 0;
|
| 408 | #if LIBAVCODEC_VERSION_MAJOR >= 59
|
| 409 | - snprintf(g_pContainer->url, sizeof(g_pContainer->url), "%s.%s", pFilename, ext);
|
| 410 | + // pFilename + dot + ext + null byte
|
| 411 | + size_t urlLen = strlen(pFilename) + 1 + extLen + 1;
|
| 412 | + g_pContainer->url = av_malloc(urlLen);
|
| 413 | + snprintf(g_pContainer->url, urlLen, "%s.%s", pFilename, ext);
|
| 414 | #else
|
| 415 | snprintf(g_pContainer->filename, sizeof(g_pContainer->filename), "%s.%s", pFilename, ext);
|
| 416 | #endif
|
| 417 | @@ -636,21 +796,33 @@ AVWRAP_DECL int AVWrapper_Close()
|
| 418 | // free everything
|
| 419 | if (g_pVStream)
|
| 420 | {
|
| 421 | - avcodec_close(g_pVideo);
|
| 422 | - av_free(g_pVideo);
|
| 423 | - av_free(g_pVStream);
|
| 424 | + avcodec_free_context(&g_pVideo);
|
| 425 | av_frame_free(&g_pVFrame);
|
| 426 | +#if LIBAVCODEC_VERSION_MAJOR >= 58
|
| 427 | + av_packet_free(&g_pVPacket);
|
| 428 | +#endif
|
| 429 | }
|
| 430 | if (g_pAStream)
|
| 431 | {
|
| 432 | - avcodec_close(g_pAudio);
|
| 433 | - av_free(g_pAudio);
|
| 434 | - av_free(g_pAStream);
|
| 435 | + avcodec_free_context(&g_pAudio);
|
| 436 | av_frame_free(&g_pAFrame);
|
| 437 | +#if LIBAVCODEC_VERSION_MAJOR >= 58
|
| 438 | + av_packet_free(&g_pAPacket);
|
| 439 | +#endif
|
| 440 | +#if LIBAVUTIL_VERSION_MAJOR < 53
|
| 441 | av_free(g_pSamples);
|
| 442 | +#endif
|
| 443 | fclose(g_pSoundFile);
|
| 444 | }
|
| 445 |
|
| 446 | +#if LIBAVCODEC_VERSION_MAJOR >= 59
|
| 447 | + avformat_free_context(g_pContainer);
|
| 448 | +#else
|
| 449 | + if (g_pVStream)
|
| 450 | + av_free(g_pVStream);
|
| 451 | + if (g_pAStream)
|
| 452 | + av_free(g_pAStream);
|
| 453 | av_free(g_pContainer);
|
| 454 | +#endif
|
| 455 | return 0;
|
| 456 | }
|