FFmpeg转码后的声音(AAC)在半视频时间后停止

时间:2015-08-12 14:54:47

标签: ffmpeg mp4 aac libav sample-rate

我的C / C ++ FFmpeg转码器中有一个奇怪的问题,它采用输入MP4(变化的输入编解码器)并使用libfdk_aac生成并输出MP4(x264,基线和AAC LC @ 44100采样率):

由此产生的mp4视频具有精细图像(x264),音频(AAC LC)也能正常工作,但只能播放到视频的一半。

音频不会减慢,不会拉伸,也不会断断续续。它只是停在视频中间。

一个提示可能是输入文件的采样率为22050且22050/44100为0.5,但我真的不明白为什么这会使声音在一半时间后停止。我期待这样的错误导致声音速度错误。如果我不尝试强制执行44100而只是使用传入的sample_rate,那么一切正常。

另一个猜测是pts计算不起作用。但是音频听起来很不错(直到它停止)并且我对视频部分完全相同,它完美无缺地工作。 "完全",如同相同的代码,但" audio" -variables替换为" video" -variables。

FFmpeg报告整个过程中没有错误。在完成从输入读取的所有包之后,我还刷新解码器/编码器/ interleaved_writing。它适用于视频,所以我怀疑我的一般方法有多大错误。

以下是我的代码的功能(剥离了错误处理和其他类的东西):

AudioCodecContext设置

outContext->_audioCodec = avcodec_find_encoder(outContext->_audioTargetCodecID);
outContext->_audioStream = 
        avformat_new_stream(outContext->_formatContext, outContext->_audioCodec);
outContext->_audioCodecContext = outContext->_audioStream->codec;
outContext->_audioCodecContext->channels = 2;
outContext->_audioCodecContext->channel_layout = av_get_default_channel_layout(2);
outContext->_audioCodecContext->sample_rate = 44100;
outContext->_audioCodecContext->sample_fmt = outContext->_audioCodec->sample_fmts[0];
outContext->_audioCodecContext->bit_rate = 128000;
outContext->_audioCodecContext->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
outContext->_audioCodecContext->time_base = 
        (AVRational){1, outContext->_audioCodecContext->sample_rate};
outContext->_audioStream->time_base = (AVRational){1, outContext->_audioCodecContext->sample_rate};
int retVal = avcodec_open2(outContext->_audioCodecContext, outContext->_audioCodec, NULL);

重新采样器设置

outContext->_audioResamplerContext = 
        swr_alloc_set_opts( NULL, outContext->_audioCodecContext->channel_layout,
                            outContext->_audioCodecContext->sample_fmt,
                            outContext->_audioCodecContext->sample_rate,
                            _inputContext._audioCodecContext->channel_layout,
                            _inputContext._audioCodecContext->sample_fmt,
                            _inputContext._audioCodecContext->sample_rate,
                            0, NULL);
int retVal = swr_init(outContext->_audioResamplerContext);

解码

decodedBytes = avcodec_decode_audio4(   _inputContext._audioCodecContext, 
                                        _inputContext._audioTempFrame, 
                                        &p_gotAudioFrame, &_inputContext._currentPacket);

转换(当然,只有当解码产生了一个帧时)

int retVal = swr_convert(   outContext->_audioResamplerContext, 
                            outContext->_audioConvertedFrame->data, 
                            outContext->_audioConvertedFrame->nb_samples, 
                            (const uint8_t**)_inputContext._audioTempFrame->data, 
                            _inputContext._audioTempFrame->nb_samples);

编码(当然,只有当解码产生了一个帧时)

outContext->_audioConvertedFrame->pts = 
        av_frame_get_best_effort_timestamp(_inputContext._audioTempFrame);

// Init the new packet
av_init_packet(&outContext->_audioPacket);
outContext->_audioPacket.data = NULL;
outContext->_audioPacket.size = 0;

// Encode
int retVal = avcodec_encode_audio2( outContext->_audioCodecContext, 
                                    &outContext->_audioPacket, 
                                    outContext->_audioConvertedFrame,
                                    &p_gotPacket);


// Set pts/dts time stamps for writing interleaved
av_packet_rescale_ts(   &outContext->_audioPacket, 
                        outContext->_audioCodecContext->time_base,
                        outContext->_audioStream->time_base);
outContext->_audioPacket.stream_index = outContext->_audioStream->index;

写作(当然,只有当编码产生数据包时)

int retVal = av_interleaved_write_frame(outContext->_formatContext, &outContext->_audioPacket);

我完全没有关于会导致这种行为的想法。

1 个答案:

答案 0 :(得分:1)

所以,我终于成功地解决了问题。

问题确实存在于sample_rate的差异中。 您可以假设调用swr_convert()会为您提供转换音频帧所需的所有样本。 当然,这太容易了。

相反,您需要多次调用swr_convert(可能)每帧并缓冲其输出(如果需要)。然后你需要从缓冲区中获取一个帧,这就是你需要编码的内容。

这是我新的convertAudioFrame函数:

// Calculate number of output samples
int numOutputSamples = av_rescale_rnd(  
    swr_get_delay(outContext->_audioResamplerContext, _inputContext._audioCodecContext->sample_rate) 
    + _inputContext._audioTempFrame->nb_samples, 
    outContext->_audioCodecContext->sample_rate, 
    _inputContext._audioCodecContext->sample_rate, 
    AV_ROUND_UP);
if (numOutputSamples == 0) 
{
    return;
}

uint8_t* tempSamples;
av_samples_alloc(   &tempSamples, NULL, 
                    outContext->_audioCodecContext->channels, numOutputSamples,
                    outContext->_audioCodecContext->sample_fmt, 0);

int retVal = swr_convert(   outContext->_audioResamplerContext, 
                            &tempSamples, 
                            numOutputSamples, 
                            (const uint8_t**)_inputContext._audioTempFrame->data, 
                            _inputContext._audioTempFrame->nb_samples);

// Write to audio fifo
if (retVal > 0)
{
    retVal = av_audio_fifo_write(outContext->_audioFifo, (void**)&tempSamples, retVal);
}
av_freep(&tempSamples);

// Get a frame from audio fifo
int samplesAvailable = av_audio_fifo_size(outContext->_audioFifo);
if (samplesAvailable > 0)
{
    retVal = av_audio_fifo_read(outContext->_audioFifo, 
                                (void**)outContext->_audioConvertedFrame->data,
                                outContext->_audioCodecContext->frame_size);

    // We got a frame, so also set its pts
    if (retVal > 0)
    {
        p_gotConvertedFrame = 1;

        if (_inputContext._audioTempFrame->pts != AV_NOPTS_VALUE)
        {
            outContext->_audioConvertedFrame->pts = _inputContext._audioTempFrame->pts;
        }
        else if (_inputContext._audioTempFrame->pkt_pts != AV_NOPTS_VALUE)
        {
            outContext->_audioConvertedFrame->pts = _inputContext._audioTempFrame->pkt_pts;
        }
    }
}

这个函数我基本上调用,直到音频fifo缓冲区中没有帧。

所以,音频只有一半长,因为我只编码了与解码时一样多的帧。由于sample_rate的2倍,我实际上需要编码2倍的帧。