2013-02-20 18 views
7

Ich entschlüsseln aac zu PCM mit ffmpeg mit avcodec_decode_audio3. Allerdings dekodiert es in AV_SAMPLE_FMT_FLTP Sample-Format (PCM 32bit Float Planar) und ich brauche AV_SAMPLE_FMT_S16 (PCM 16 Bit signiert - S16LE).Wie konvertiert man die Abtastrate von AV_SAMPLE_FMT_FLTP zu AV_SAMPLE_FMT_S16?

Ich weiß, dass ffmpeg dies leicht mit -sample_fmt tun kann. Ich möchte das gleiche mit dem Code tun, aber ich konnte es immer noch nicht herausfinden.

audio_resample hat nicht funktioniert für: es schlägt mit der Fehlermeldung: .... Konvertierung fehlgeschlagen.

+0

Haben Sie jemals die Antwort auf diese funktioniert? Bin genau gegenüber demselben Problem –

Antwort

35

BEARBEITEN 9. April 2013: Ausgeführt wie libswresample zu verwenden, um dies zu tun ... viel schneller!

Irgendwann in den letzten 2-3 Jahren FFmpegs AAC-Decoder Ausgabeformat von AV_SAMPLE_FMT_S16 zu AV_SAMPLE_FMT_FLTP geändert. Dies bedeutet, dass jeder Audiokanal über einen eigenen Puffer verfügt und jeder Abtastwert ein 32-Bit-Gleitkommawert ist, der von -1,0 bis +1,0 skaliert wird. Wenn sich AV_SAMPLE_FMT_S16 mit AV_SAMPLE_FMT_S16 befindet, befinden sich die Daten in einem einzelnen Puffer mit den Interleaved Samples und jedes Sample ist eine Ganzzahl mit Vorzeichen von -32767 bis +32767.

Und wenn Sie Ihr Audio wirklich als AV_SAMPLE_FMT_S16 benötigen, müssen Sie die Konvertierung selbst durchführen. Ich fand heraus, zwei Möglichkeiten, es zu tun:

1. Verwenden Sie libswresample (empfohlen)

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

Und das ist alles, was Sie tun müssen!

2. es in C von Hand tun (original Antwort, nicht empfohlen) Also in Ihrer Decodierschleife

, wenn Sie ein Audiopaket Sie es so dekodieren haben:

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

Und dann, wenn Sie einen vollständigen Rahmen von Audio haben, können Sie es ziemlich leicht umwandeln:

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

ich habe für Klarheit, ein paar Dinge aus links ... th Die Einspannung im Mono-Pfad sollte idealerweise im Stereo-Pfad dupliziert werden. Und der Code kann einfach optimiert werden.

+0

Ich habe ein verwandtes Problem, diesmal muss ich S16 zu S16P konvertieren. Weil das neueste ffmpeg S16P für libmp3lame encoding benötigt. Ich freue mich, wenn Sie sich ansehen: http://stackoverflow.com/questions/18131389/how-to-convert-av-sample-fmt-s16-to-av-sample-fmt-s16p – frankish

+0

Reuben, würdest du diesen Code noch haben? Ich versuche, diese Konvertierung zu erhalten, aber ich habe einige Probleme. Ich würde gerne die komplette Arbeitslösung sehen, wenn Sie einen Link posten könnten. Danke im Voraus. –

+0

Ich habe nicht mehr den Code für die Option 2 ... mit libswresample ist die einzige vernünftige Möglichkeit, um dieses Problem zu lösen. Was sind die Probleme, die Sie haben? –

2

Vielen Dank Reuben für eine Lösung. Ich habe festgestellt, dass einige der Sample-Werte im Vergleich zu einem geraden ffmpeg -i file.wav etwas aus waren. Es scheint, dass sie bei der Konvertierung eine runde() auf den Wert verwenden.

die Konvertierung zu tun, tat ich, was Sie mit einem Gebot von Änderung hat für jede Menge Kanäle zu arbeiten:

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. 

} 

ich aus ging 0.11.1 ffmpeg -> 1.1.3 und fand die Änderung Stichprobenformat nervig. Ich schaute auf die Einstellung der request_sample_fmt auf AV_SAMPLE_FMT_S16 aber es scheint, dass der AAC-Decoder nichts anderes als AV_SAMPLE_FMT_FLTP sowieso unterstützt.

+0

tolle Ergänzung, danke – frankish

+0

Ich aktualisierte meine Antwort mit einem besseren Weg mit libswresample. Es ist überraschend einfach zu machen. –

+0

@BradMitchell Wie können wir das Gegenteil von diesem tun? Würde es Ihnen etwas ausmachen, einen Blick auf http://stackoverflow.com/questions/18131389/how-to-convert-av-sample-fmt-s16-to-av-sample-fmt-s16p zu werfen? – frankish

5

Ich fand 2 Resample-Funktion von FFMPEG. Die Leistung ist vielleicht besser.

  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

Sie waren definitiv auf dem richtigen Weg hier Albert ... Ich hatte früher eine Leistungsbeschwerde und musste nach einer optimierten Methode suchen, um diese Konvertierung durchzuführen. Libswresample ist meine neue beste Freundin. Meine obige Antwort wurde mit dem erforderlichen Code aktualisiert. –