2013-02-20 3 views
7

Я декодирую aac до pcm с ffmpeg с avcodec_decode_audio3. Однако он декодирует в формат образца AV_SAMPLE_FMT_FLTP (PCM 32bit Float Planar), и мне нужен AV_SAMPLE_FMT_S16 (бит PCM 16 бит - S16LE).Как конвертировать частоту дискретизации с AV_SAMPLE_FMT_FLTP в AV_SAMPLE_FMT_S16?

Я знаю, что ffmpeg легко справится с -sample_fmt. Я хочу сделать то же самое с кодом, но я все еще не мог понять.

audio_resample не работает: он с ошибкой: сообщение не выполнено.

+0

ли вы когда-нибудь работать в ответ на это? Я сталкиваюсь с той же проблемой –

ответ

35

EDIT 9 апреля 2013 г.: Разработано, как использовать libswresample для этого ... намного быстрее!

В какой-то момент за последние 2-3 года выходной формат декодера формата AAC FFmpeg изменился с AV_SAMPLE_FMT_S16 на AV_SAMPLE_FMT_FLTP. Это означает, что каждый аудиоканал имеет собственный буфер, и каждое значение выборки представляет собой 32-разрядное значение с плавающей запятой, масштабированное от -1,0 до +1,0.

Принимая во внимание, что данные AV_SAMPLE_FMT_S16 находятся в одном буфере, причем образцы чередуются, и каждый образец является целым числом со знаком от -32767 до +32767.

И если вам действительно нужен ваш аудиофайл как AV_SAMPLE_FMT_S16, вам нужно сделать преобразование самостоятельно. Я понял, два способа сделать это:

1. Используйте libswresample (рекомендуется)

#include "libswresample/swresample.h" 

... 

SwrContext *swr; 

... 

// Set up SWR context once you've got codec information 
swr = swr_alloc(); 
av_opt_set_int(swr, "in_channel_layout", audioCodec->channel_layout, 0); 
av_opt_set_int(swr, "out_channel_layout", audioCodec->channel_layout, 0); 
av_opt_set_int(swr, "in_sample_rate",  audioCodec->sample_rate, 0); 
av_opt_set_int(swr, "out_sample_rate", audioCodec->sample_rate, 0); 
av_opt_set_sample_fmt(swr, "in_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); 
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); 
swr_init(swr); 

... 

// In your decoder loop, after decoding an audio frame: 
AVFrame *audioFrame = ...; 
int16_t* outputBuffer = ...; 
swr_convert(&outputBuffer, audioFrame->nb_samples, audioFrame->extended_data, audioFrame->nb_samples); 

И это все, что вам нужно сделать!

2. Делайте это вручную в C (оригинальный ответ, не рекомендуется)

Так в цикле декодирования, когда у вас есть аудио пакет вы декодирует его, как это:

AVCodecContext *audioCodec; // init'd elsewhere 
AVFrame *audioFrame;   // init'd elsewhere 
AVPacket packet;    // init'd elsewhere 
int16_t* outputBuffer;  // init'd elsewhere 
int out_size = 0; 
... 
int len = avcodec_decode_audio4(audioCodec, audioFrame, &out_size, &packet); 

И потом, если у вас есть полный кадр аудио, вы можете преобразовать его довольно легко:

// Convert from AV_SAMPLE_FMT_FLTP to AV_SAMPLE_FMT_S16 
    int in_samples = audioFrame->nb_samples; 
    int in_linesize = audioFrame->linesize[0]; 
    int i=0; 
    float* inputChannel0 = (float*)audioFrame->extended_data[0]; 
    // Mono 
    if (audioFrame->channels==1) { 
     for (i=0 ; i<in_samples ; i++) { 
      float sample = *inputChannel0++; 
      if (sample<-1.0f) sample=-1.0f; else if (sample>1.0f) sample=1.0f; 
      outputBuffer[i] = (int16_t) (sample * 32767.0f); 
     } 
    } 
    // Stereo 
    else { 
     float* inputChannel1 = (float*)audioFrame->extended_data[1]; 
     for (i=0 ; i<in_samples ; i++) { 
      outputBuffer[i*2] = (int16_t) ((*inputChannel0++) * 32767.0f); 
      outputBuffer[i*2+1] = (int16_t) ((*inputChannel1++) * 32767.0f); 
     } 
    } 
    // outputBuffer now contains 16-bit PCM! 

Я оставил пару вещи для ясности ... й e в идеале должен быть дублирован в стереофоническом канале. И код можно легко оптимизировать.

+0

У меня есть связанная с этим проблема, на этот раз мне нужно преобразовать S16 в S16P. Поскольку для последней версии ffmpeg требуется S16P для кодировки libmp3lame. Я буду рад, если вы посмотрите на: http://stackoverflow.com/questions/18131389/how-to-convert-av-sample-fmt-s16-to-av-sample-fmt-s16p – frankish

+0

Reuben, у вас будет этот код еще? Я пытаюсь заставить это преобразование работать, но у меня есть некоторые проблемы. Я хотел бы увидеть полное рабочее решение, если вы можете опубликовать ссылку. Заранее спасибо. –

+0

У меня больше нет кода для опции 2 ... использование libswresample - единственный разумный способ решить эту проблему. Какие у вас проблемы? –

2

Поблагодарите Reuben за это решение. Я обнаружил, что некоторые из значений образца были слегка отключены по сравнению с прямым файлом ffmpeg -i file.wav. Кажется, что при конвертации они используют значение round() для значения.

Чтобы сделать преобразование, я сделал то, что вы сделали с заявкой модификации для работы на любое количество каналов:

if (audioCodecContext->sample_fmt == AV_SAMPLE_FMT_FLTP) 
{ 
    int nb_samples = decoded_frame->nb_samples; 
    int channels = decoded_frame->channels; 
    int outputBufferLen = nb_samples & channels * 2; 
    short* outputBuffer = new short[outputBufferLen/2]; 

    for (int i = 0; i < nb_samples; i++) 
    { 
     for (int c = 0; c < channels; c++) 
     { 
      float* extended_data = (float*)decoded_frame->extended_data[c]; 
      float sample = extended_data[i]; 
      if (sample < -1.0f) sample = -1.0f; 
      else if (sample > 1.0f) sample = 1.0f; 
      outputBuffer[i * channels + c] = (short)round(sample * 32767.0f); 
     } 
    } 

    // Do what you want with the data etc. 

} 

Я пошел от FFmpeg 0.11.1 -> 1.1.3 и обнаружили изменения образцового формата раздражает. Я посмотрел на параметр request_sample_fmt на AV_SAMPLE_FMT_S16, но кажется, что aac-декодер в любом случае не поддерживает ничего, кроме AV_SAMPLE_FMT_FLTP.

+0

отличное дополнение, спасибо – frankish

+0

Я обновил свой ответ с помощью лучшего способа с помощью libswresample. Это удивительно легко сделать. –

+0

@BradMitchell Как мы можем сделать противоположное этому? Не могли бы вы взглянуть на http://stackoverflow.com/questions/18131389/how-to-convert-av-sample-fmt-s16-to-av-sample-fmt-s16p? – frankish

5

Я нашел 2 функции resample от FFMPEG. Производительность может быть лучше.

  1. avresample_convert() http://libav.org/doxygen/master/group__lavr.html
  2. swr_convert() http://spirton.com/svn/MPlayer-SB/ffmpeg/libswresample/swresample_test.c
+0

Вы были на правильных строчках здесь, Альберт ... У меня вчера была жалоба на производительность, поэтому мне пришлось искать оптимальный метод для этого преобразования, а libswresample - мой новый лучший друг. Мой ответ выше был обновлен с необходимым кодом. –