使用AutoGen FFmpeg库在MP4中同步音频/视频

时间:2016-07-05 07:54:04

标签: c# c++ ffmpeg synchronization mp4

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

这些是我使用的AVCodecContexts:

视频:

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文件中设置视频/音频的pts / dts位置的示例。任何帮助的例子都会很棒!

另外,我先写视频帧,之后(一旦写完)我就写了音频。我已使用评论中建议的调整值更新了此问题。

我上传了一个测试视频,以便在此显示我的搜索结果:http://www.filedropper.com/test_124

2 个答案:

答案 0 :(得分:1)

PS:查看 A/V Sync with FFmpeg 上的这篇文章/教程。如果以下情况没有,它可能对您有帮助。

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

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

另一种方法是通过使用视频的每秒帧数(FPS)来确保首先正确创建PTS值(在outputFrame->pts中)。要做到这一点......

对于每个视频帧outputFrame->pts = (1000 / FPS) * frameIndex;
(对于30 FPS视频,第1帧有0时间,第30帧时间"时钟"已达到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)关于音频设置......

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

尝试将96000128000作为最低起始值。

相框尺寸:

  

int AVCodecContext::frame_size表示" 每个频道的样本数量   音频帧"

考虑到Docs的上述引用,并且MPEG AAC不会每个频道"" (因为两个L / R通道的数据都包含在每个帧内)。每个AAC帧可容纳1024个PCM样本。

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

个人资料:
我注意到您已将MAIN用于AAC配置文件。我过去常常在视频中看到Low Complexity。我在我的硬盘上尝试了一些随机的MP4文件来自各种来源,我找不到使用" Main"轮廓。作为最后的手段,测试"低复杂性"不会受伤。

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

PS:选中 possible AAC issue (具体取决于您的FFmpeg版本)。

答案 1 :(得分:1)

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

视频只是通常的增量(每帧+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;
}