2013-02-20 9 views
7

Dekoduje aac do pcm z ffmpeg przy pomocy avcodec_decode_audio3. Jednak dekoduje do formatu próbki AV_SAMPLE_FMT_FLTP (PCM 32bit Float Planar) i potrzebuję AV_SAMPLE_FMT_S16 (podpisana 16-bitowym PCM - S16LE).Jak przekonwertować częstotliwość próbkowania z AV_SAMPLE_FMT_FLTP na AV_SAMPLE_FMT_S16?

Wiem, że ffmpeg może to łatwo zrobić przy pomocy -sample_fmt. Chcę zrobić to samo z kodem, ale nadal nie mogłem tego rozgryźć.

audio_resample nie działa dla: nie powiedzie się z komunikatem o błędzie: .... konwersja nie powiodła się.

+0

Czy kiedykolwiek wypracować odpowiedź na to? Mam do czynienia z tym samym problemem –

Odpowiedz

35

EDIT 09 kwietnia 2013: Działało jak używać libswresample to zrobić ... o wiele szybciej!

W pewnym momencie w ciągu ostatnich 2-3 lat format wyjściowy dekodera AAC FFmpeg zmienił się z AV_SAMPLE_FMT_S16 na AV_SAMPLE_FMT_FLTP. Oznacza to, że każdy kanał audio ma swój własny bufor, a każda wartość próbki jest 32-bitową wartością zmiennoprzecinkową skalowaną od -1,0 do +1,0.

Podczas gdy AV_SAMPLE_FMT_S16 dane są w pojedynczym buforze, z próbkami przeplecionymi, a każda próbka jest liczbą całkowitą ze znakiem od -32767 do +32767.

A jeśli naprawdę potrzebujesz dźwięku jako AV_SAMPLE_FMT_S16, musisz wykonać konwersję samodzielnie. I zorientowali się dwa sposoby, aby to zrobić:

1. Użyj libswresample (zalecane)

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

i to wszystko trzeba zrobić!

2. Wykonaj go ręcznie w C (oryginalna odpowiedzi, nie zalecane)

Więc w pętli dekodowania, gdy masz pakiet audio, którą dekodować go tak:

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

a potem, jeśli masz pełną klatkę audio, można przekonwertować go dość łatwo:

// 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! 

zostawiłem parę rzeczy dla jasności ... th Zaciśnięcie w ścieżce mono powinno idealnie być powielone w ścieżce stereo. Kod można łatwo zoptymalizować.

+0

Mam podobny problem, tym razem potrzebuję przekonwertować S16 na S16P. Ponieważ najnowszy ffmpeg potrzebuje S16P do kodowania libmp3lame. Będę zadowolony, jeśli spojrzysz na: http://stackoverflow.com/questions/18131389/how-to-convert-av-sample-fmt-s16-to-av-sample-fmt-s16p – frankish

+0

Reuben, czy nadal masz ten kod? Próbuję uruchomić tę konwersję, ale mam pewne problemy. Chciałbym zobaczyć kompletne działające rozwiązanie, jeśli mógłbyś opublikować link. Z góry dziękuję. –

+0

Nie mam już kodu dla opcji 2 ... używanie libswresample jest jedynym rozsądnym sposobem rozwiązania tego problemu. Jakie masz problemy? –

2

Dziękuję firmie Reuben za rozwiązanie tego problemu. Zauważyłem, że niektóre z przykładowych wartości były nieznacznie wyłączone w porównaniu z prostym plikiem ffmpeg -i file.wav. Wygląda na to, że podczas konwersji używają round() na wartości.

Aby wykonać konwersję, zrobiłem to, co zrobiłeś z ofertą modyfikacji pracować dla dowolnej ilości kanałów:

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. 

} 

poszedłem z ffmpeg 0.11.1 -> 1.1.3 i stwierdzono zmiany przykładowy format irytujący. Spojrzałem na ustawienie request_sample_fmt na AV_SAMPLE_FMT_S16, ale wygląda na to, że dekoder aac nie obsługuje niczego poza AV_SAMPLE_FMT_FLTP.

+0

świetny dodatek, dziękuję – frankish

+0

Zaktualizowałem swoją odpowiedź w lepszy sposób za pomocą libswresample. To zaskakująco łatwe do zrobienia. –

+0

@BradMitchell Jak możemy zrobić przeciwieństwo tego? Czy mógłbyś rzucić okiem na http://stackoverflow.com/questions/18131389/how-to-convert-av-sample-fmt-s16-to-av-sample-fmt-s16p? – frankish

5

Znalazłem 2 funkcję resample od FFMPEG. Wydajność może być lepsza.

  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

Byliście na właściwej linii w tym miejscu Albert ... Miałem wcześniej skargę dotyczącą wydajności, więc musiałem zajrzeć do znalezienia zoptymalizowanej metody wykonania tej konwersji, a libswresample to mój nowy najlepszy przyjaciel. Moja powyższa odpowiedź została zaktualizowana o niezbędny kod. –

Powiązane problemy