【发布时间】:2018-08-26 19:09:01
【问题描述】:
我正在复用视频和音频流。视频流来自生成的图像数据。音频流来自 aac 文件。一些音频文件比我设置的总视频时间长,所以我的策略是在其时间大于总视频时间(我通过编码视频帧数控制的最后一个)时停止音频流复用器。
我不会把整个设置代码放在这里,但它类似于来自最新 FFMPEG 存储库的muxing.c 示例。唯一的区别是,正如我所说,我使用来自文件的音频流,而不是来自综合生成的编码帧。我很确定问题出在复用器循环期间我的错误同步。这就是我所做的:
void AudioSetup(const char* audioInFileName)
{
AVOutputFormat* outputF = mOutputFormatContext->oformat;
auto audioCodecId = outputF->audio_codec;
if (audioCodecId == AV_CODEC_ID_NONE) {
return false;
}
audio_codec = avcodec_find_encoder(audioCodecId);
avformat_open_input(&mInputAudioFormatContext,
audioInFileName, 0, 0);
avformat_find_stream_info(mInputAudioFormatContext, 0);
av_dump_format(mInputAudioFormatContext, 0, audioInFileName, 0);
for (size_t i = 0; i < mInputAudioFormatContext->nb_streams; i++) {
if (mInputAudioFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
inAudioStream = mInputAudioFormatContext->streams[i];
AVCodecParameters *in_codecpar = inAudioStream->codecpar;
mAudioOutStream.st = avformat_new_stream(mOutputFormatContext, NULL);
mAudioOutStream.st->id = mOutputFormatContext->nb_streams - 1;
AVCodecContext* c = avcodec_alloc_context3(audio_codec);
mAudioOutStream.enc = c;
c->sample_fmt = audio_codec->sample_fmts[0];
avcodec_parameters_to_context(c, inAudioStream->codecpar);
//copyparams from input to autput audio stream:
avcodec_parameters_copy(mAudioOutStream.st->codecpar, inAudioStream->codecpar);
mAudioOutStream.st->time_base.num = 1;
mAudioOutStream.st->time_base.den = c->sample_rate;
c->time_base = mAudioOutStream.st->time_base;
if (mOutputFormatContext->oformat->flags & AVFMT_GLOBALHEADER) {
c->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
break;
}
}
}
void Encode()
{
int cc = av_compare_ts(mVideoOutStream.next_pts, mVideoOutStream.enc->time_base,
mAudioOutStream.next_pts, mAudioOutStream.enc->time_base);
if (mAudioOutStream.st == NULL || cc <= 0) {
uint8_t* data = GetYUVFrame();//returns ready video YUV frame to work with
int ret = 0;
AVPacket pkt = { 0 };
av_init_packet(&pkt);
pkt.size = packet->dataSize;
pkt.data = data;
const int64_t duration = av_rescale_q(1, mVideoOutStream.enc->time_base, mVideoOutStream.st->time_base);
pkt.duration = duration;
pkt.pts = mVideoOutStream.next_pts;
pkt.dts = mVideoOutStream.next_pts;
mVideoOutStream.next_pts += duration;
pkt.stream_index = mVideoOutStream.st->index;
ret = av_interleaved_write_frame(mOutputFormatContext, &pkt);
} else
if(audio_time < video_time) {
//5 - duration of video in seconds
AVRational r = { 60, 1 };
auto cmp= av_compare_ts(mAudioOutStream.next_pts, mAudioOutStream.enc->time_base, 5, r);
if (cmp >= 0) {
mAudioOutStream.next_pts = (int64_t)std::numeric_limits<int64_t>::max();
return true; //don't mux audio anymore
}
AVPacket a_pkt = { 0 };
av_init_packet(&a_pkt);
int ret = 0;
ret = av_read_frame(mInputAudioFormatContext, &a_pkt);
//if audio file is shorter than stop muxing when at the end of the file
if (ret == AVERROR_EOF) {
mAudioOutStream.next_pts = (int64_t)std::numeric_limits<int64_t>::max();
return true;
}
a_pkt.stream_index = mAudioOutStream.st->index;
av_packet_rescale_ts(&a_pkt, inAudioStream->time_base, mAudioOutStream.st->time_base);
mAudioOutStream.next_pts += a_pkt.pts;
ret = av_interleaved_write_frame(mOutputFormatContext, &a_pkt);
}
}
现在,视频部分完美无缺。但是,如果音轨比视频持续时间长,我的总视频长度会增加大约 5% - 20%,很明显,音频在其中起到了作用,因为视频帧正好在应该在的地方完成。
我最接近的'hack'是这部分:
AVRational r = { 60 ,1 };
auto cmp= av_compare_ts(mAudioOutStream.next_pts, mAudioOutStream.enc->time_base, 5, r);
if (cmp >= 0) {
mAudioOutStream.next_pts = (int64_t)std::numeric_limits<int64_t>::max();
return true;
}
在这里,我试图将音频流的next_pts 与为视频文件设置的总时间(5 秒)进行比较。通过设置r = {60,1},我将这些秒数转换为音频流的time_base。至少那是我相信我正在做的事情。有了这个 hack,当使用标准 AAC 文件时,我与正确的电影长度的偏差非常小,即 44100 的采样率,立体声。但是,如果我使用更多有问题的样本进行测试,例如 AAC 采样率 16000,单声道 - 那么视频文件的大小几乎会增加一整秒。
如果有人能指出我在这里做错了什么,我将不胜感激。
重要提示:我没有为任何上下文设置持续时间。我控制多路复用会话的终止,它基于视频帧数。音频输入流当然有持续时间,但它对我没有帮助,因为视频持续时间是定义电影长度的因素。
更新:
这是第二次赏金尝试。
更新 2:
实际上,我的 {den,num} 音频时间戳是错误的,而 {1,1} 确实是要走的路,正如答案所解释的那样。阻止它工作的是这一行中的一个错误(我的错):
mAudioOutStream.next_pts += a_pkt.pts;
必须是:
mAudioOutStream.next_pts = a_pkt.pts;
该错误导致 pts 呈指数增加,这导致非常早地到达流的末尾(以 pts 而言),因此导致音频流比预期的更早终止。
【问题讨论】:
-
奖给谁?真的没有人回答不了这个问题吗?
-
截断 - 不是一个选项。第二件事——这就是我想要做的。看看我的代码。
-
“我不熟悉 FFMPEG API”——拜托,我对 API 非常熟悉,这不是一个小问题。我不会因为简单的事情放弃 250 分。顺便说一句,我故意翻转那个时基,因为它是唯一给我一些“相对”合理的东西的变体......在那个例子中使用 {1,1} 在我的情况下不会返回任何有意义的东西。我想那是因为我的音频流不像示例中使用的那样合成。文件中音频流的时基由编解码器上下文设置,可能看起来很奇怪。
-
“请”是粗鲁的吗?来吧伙计,如果你帮不上忙,别怪我。为了不了解 FFMPEG 文档,我陷入这个错误太久了。
-
简而言之,用 C++ 代码实现类似于stackoverflow.com/questions/13041061/… 的东西?
标签: c++ audio video ffmpeg libavformat