Транскодированный звук FFmpeg (AAC) останавливается через половину времени видео

У меня странная проблема с моим транскодером C / C ++ FFmpeg, который принимает входной MP4 (различные входные кодеки) и производит и выводит MP4 (x264, базовая частота и частота дискретизации AAC LC @ 44100 с libfdk_aac):

Получающееся в результате видео в формате mp4 имеет прекрасные изображения (x264), а звук (AAC LC) также работает нормально, но воспроизводится только до половины видео.

Звук не тормозит, не растягивается и не заикается. Он просто останавливается прямо в середине видео.

Один намек может заключаться в том, что входной файл имеет частоту дискретизации 22050, а 22050/44100 - 0,5, но я действительно не понимаю, почему это заставляет звук просто останавливаться через половину времени. Я ожидал, что такая ошибка приведет к неправильной скорости звука. Все работает нормально, если я не пытаюсь принудительно использовать 44100, а вместо этого просто использую входящий sample_rate.

Еще можно предположить, что подсчет очков не работает. Но звук звучит просто отлично (до полной остановки), и я делаю в точности то же самое для видео части, где он работает безупречно. «Точно», как в том же коде, но переменные «аудио» заменены на «видео» -переменные.

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);

Я совершенно не понимаю, что могло бы вызвать такое поведение.


person TheSHEEEP    schedule 12.08.2015    source источник


Ответы (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 не кончится фрейм.

Итак, звук был вдвое короче, потому что я закодировал столько кадров, сколько декодировал. Там, где мне действительно нужно было кодировать в 2 раза больше кадров из-за в 2 раза больше sample_rate.

person TheSHEEEP    schedule 17.08.2015