2015-08-12 9 views
0

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

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

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

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

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

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

Resampler Настройка

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; 

Письмо (только если кодирование составило пакет et, конечно)

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

Я совершенно не в курсе, что может привести к такому поведению.

ответ

0

Итак, мне, наконец, удалось разобраться в себе.

Проблема была в самом деле в отличии 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.