使用 AutoGen FFmpeg 库在 MP4 中同步 Audio/Video

Sync Audio/Video in MP4 using AutoGen FFmpeg library

我目前在保持音频和视频流同步方面遇到问题。

这些是我正在使用的 AVCodecContext:

对于视频:

AVCodec* videoCodec = ffmpeg.avcodec_find_encoder(AVCodecID.AV_CODEC_ID_H264)
AVCodecContext* videoCodecContext = ffmpeg.avcodec_alloc_context3(videoCodec);
videoCodecContext->bit_rate = 400000;
videoCodecContext->width = 1280;
videoCodecContext->height = 720;
videoCodecContext->gop_size = 12;
videoCodecContext->max_b_frames = 1;
videoCodecContext->pix_fmt = videoCodec->pix_fmts[0];
videoCodecContext->codec_id = videoCodec->id;
videoCodecContext->codec_type = videoCodec->type;
videoCodecContext->time_base = new AVRational
{
    num = 1,
    den = 30
};

对于音频:

AVCodec* audioCodec = ffmpeg.avcodec_find_encoder(AVCodecID.AV_CODEC_ID_AAC)
AVCodecContext* audioCodecContext = ffmpeg.avcodec_alloc_context3(audioCodec);
audioCodecContext->bit_rate = 1280000;
audioCodecContext->sample_rate = 48000;
audioCodecContext->channels = 2;
audioCodecContext->channel_layout = ffmpeg.AV_CH_LAYOUT_STEREO;
audioCodecContext->frame_size = 1024;
audioCodecContext->sample_fmt = audioCodec->sample_fmts[0];
audioCodecContext->profile = ffmpeg.FF_PROFILE_AAC_LOW;
audioCodecContext->codec_id = audioCodec->id;
audioCodecContext->codec_type = audioCodec->type;

在写视频帧时,我将PTS位置设置如下:

outputFrame->pts = frameIndex;  // The current index of the image frame being written

然后我使用 avcodec_encode_video2() 对帧进行编码。在此之后,我调用以下命令来设置时间戳:

ffmpeg.av_packet_rescale_ts(&packet, videoCodecContext->time_base, videoStream->time_base);

这个播放完美。

但是,当我对音频执行相同操作时,视频以慢动作播放,先播放音频,然后继续播放没有声音的视频。

我在任何地方都找不到有关如何在 MP4 文件中为 video/audio 设置 pts/dts 位置的示例。任何帮助的例子都会很棒!

另外,我先写视频帧,然后(一旦它们都写完)我写音频。我已经用评论中建议的调整值更新了这个问题。

我上传了一个测试视频来显示我的结果:http://www.filedropper.com/test_124

PS:在 A/V Sync with FFmpeg 上查看此 article/tutorial。如果以下没有帮助,它可能会对您有所帮助。

1)关于视频和音频时间戳...

而不是使用当前 frameIndex 作为时间戳,然后 重新缩放 它们。如果可能,请跳过重新缩放。

替代方法是确保首先使用视频的每秒帧数 (FPS) 正确创建 PTS 值(在 outputFrame->pts 中)。为此...

对于每个视频帧outputFrame->pts = (1000 / FPS) * frameIndex;
(对于一个 30 FPS 的视频,第 1 帧的时间为 0,到第 30 帧 "clock" 已经达到 1 秒。
所以 1000 / 30 现在给每个视频帧一个呈现间隔33.333 毫秒。当 frameIndex 为 30 时,我们可以说 33.333 x 30 = 1000 m.secs(或 1 秒,每秒确认 30 帧)。

对于每个音频帧outputFrame->pts = ((1024 / 48000) * 1000) * frameIndex;
(由于 48khz AAC 帧的持续时间为 21.333 m.secs,时间戳会增加该时间量。公式为:(1024 PCM / SampleRate)x 1000 ms/perSec)然后乘以帧索引)。

2)关于音频设置...

比特率:
如果您的 sample_rate 是 48000Hz,audioCodecContext->bit_rate = 64000; 似乎很奇怪(我假设您的位深度是每个样本 16 位?)。

尝试 96000128000 作为最低起始值。

帧大小:

int AVCodecContext::frame_size means "Number of samples per channel in an audio frame".

考虑到文档的上述引用,并且 MPEG AAC 不执行 "per channel"(因为两个 L/R 通道的数据都包含在每个帧中)。每个 AAC 帧包含 1024 个 PCM 样本。

audioCodecContext->frame_size = 88200;大小,可以试试= 1024;

简介:
我注意到您已将 MAIN 用于 AAC 配置文件。我习惯在视频中看到 Low Complexity。我在我的硬盘上尝试了一些来自不同来源的随机 MP4 文件,但我找不到使用 "Main" 配置文件的文件。作为最后的手段,测试 "Low Complexity" 不会有任何伤害。

尝试使用 audioCodecContext->profile = ffmpeg.FF_PROFILE_AAC_LOW;

PS:检查此是否有 (取决于您的 FFmpeg 版本)。

问题解决了。在设置帧 PTS 位置后,我添加了一个新功能来设置 video/audio 位置。

视频只是通常的增量(每帧+1),而音频是这样完成的:

outputFrame->pts = ffmpeg.av_rescale_q(m_audioFrameSampleIncrement, new AVRational { num = 1, den = 48000 }, m_audioCodecContext->time_base);

m_audioFrameSampleIncrement += outputFrame->nb_samples;

帧编码后,我调用我的新函数:

private static void SetPacketProperties(ref AVPacket packet, AVCodecContext* codecContext, AVStream* stream)
{
    packet.pts = ffmpeg.av_rescale_q_rnd(packet.pts, codecContext->time_base, stream->time_base, AVRounding.AV_ROUND_NEAR_INF | AVRounding.AV_ROUND_PASS_MINMAX);
    packet.dts = ffmpeg.av_rescale_q_rnd(packet.dts, codecContext->time_base, stream->time_base, AVRounding.AV_ROUND_NEAR_INF | AVRounding.AV_ROUND_PASS_MINMAX);
    packet.duration = (int)ffmpeg.av_rescale_q(packet.duration, codecContext->time_base, stream->time_base);
    packet.stream_index = stream->index;
}