【问题标题】:Sync Audio/Video in MP4 using AutoGen FFmpeg library使用 AutoGen FFmpeg 库在 MP4 中同步音频/视频
【发布时间】:2016-11-06 23:21:13
【问题描述】:

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

这些是我正在使用的 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 位置的示例。任何帮助的例子都会很棒!

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

我已经上传了一个测试视频来在这里展示我的结果:http://www.filedropper.com/test_124

【问题讨论】:

  • 标签错误,一定是c++
  • 我正在使用 AutoGen 库,它使用 C# 中的 Invoke 来访问这些库!
  • 我不使用 FFmpeg API,只使用编译后的 .exe 作为进程(std in/out)。不幸的是,我无法测试您的代码,但是...希望我的回答中的建议对您有所帮助。

标签: c# c++ ffmpeg synchronization mp4


【解决方案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 毫秒(或 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 表示“每个通道的样本数 一个音频帧”

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

audioCodecContext->frame_size = 88200;的尺寸,你可以试试= 1024;

简介:
我注意到您已将 MAIN 用于 AAC 配置文件。我习惯在视频中看到Low Complexity。我在我的硬盘上尝试了一些来自各种来源的随机 MP4 文件,但我找不到使用“主要”配置文件的文件。作为最后的手段,测试“低复杂性”不会有什么坏处。

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

PS:检查这里是否有 possible AAC issue(取决于您的 FFmpeg 版本)。

【讨论】:

  • 糟糕,忘记说明我的关于 a/v 时间戳的观点。希望对您有所帮助。
  • 这是一个非常有用的答案。需要尝试并从那里开始。
  • outputFrame->pts = (1000 / FPS) * frameIndex(用于视频帧)使 19 秒的视频在 1 秒内快速播放。
  • 你能提供一个示例视频文件的临时链接吗?我将尝试检查字节(以找到固定值)。尝试使用 44khz 和 128 比特率。当我使用你的设置时,FFmpeg 不会生成文件,但它会自动默认为使用 44100 采样率 + 16 位深度 + 128kbps 比特率的工作正常的视频。
  • 抱歉回复晚了。我在这里上传了视频:filedropper.com/test_124
【解决方案2】:

解决了这个问题。在设置帧 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;
}

【讨论】:

    猜你喜欢
    • 2018-05-09
    • 1970-01-01
    • 2012-03-16
    • 2016-05-26
    • 1970-01-01
    • 2012-10-02
    • 2023-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多