2013-02-20 50 views
7

Sto decodificando da aac a pcm con ffmpeg con avcodec_decode_audio3. Tuttavia decodifica nel formato di esempio AV_SAMPLE_FMT_FLTP (PCM 32 bit Float Planar) e ho bisogno di AV_SAMPLE_FMT_S16 (PCM a 16 bit firmato - S16LE).Come convertire la frequenza di campionamento da AV_SAMPLE_FMT_FLTP a AV_SAMPLE_FMT_S16?

So che ffmpeg può farlo facilmente con -sample_fmt. Voglio fare lo stesso con il codice ma non riesco ancora a capirlo.

audio_resample non ha funzionato: fallisce con messaggio di errore: .... conversione fallita.

+0

Ha mai funzionato la risposta a questo? Sto affrontando esattamente lo stesso problema –

risposta

35

EDIT 9 aprile 2013: ha funzionato come utilizzare libswresample per fare questo ... molto più velocemente!

A un certo punto negli ultimi 2-3 anni il formato di uscita del decoder AAC di FFmpeg è stato modificato da AV_SAMPLE_FMT_S16 a AV_SAMPLE_FMT_FLTP. Ciò significa che ogni canale audio ha il proprio buffer e ogni valore di esempio è un valore in virgola mobile a 32 bit ridimensionato da -1,0 a +1,0.

Mentre con AV_SAMPLE_FMT_S16 i dati sono in un buffer singolo, con i campioni interlacciati e ogni campione è un numero intero con segno compreso tra -32767 e +32767.

E se hai davvero bisogno del tuo audio come AV_SAMPLE_FMT_S16, allora devi fare la conversione da solo. Ho capito due modi per farlo:

1. Utilizzare libswresample (consigliato)

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

E questo è tutto quello che devi fare!

2. farlo a mano in C (risposta originale, non raccomandato)

Quindi nel tuo ciclo di decodifica, quando hai un pacchetto audio a decodificare in questo modo:

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

E poi, se hai un full frame di audio, è possibile convertire abbastanza facilmente:

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

ho lasciato un paio di cose fuori per chiarezza ... th Il clampaggio nel percorso mono dovrebbe idealmente essere duplicato nel percorso stereo. E il codice può essere facilmente ottimizzato.

+0

Ho un problema correlato, questa volta, ho bisogno di convertire da S16 a S16P. Perché l'ultimo ffmpeg ha bisogno di S16P per la codifica libmp3lame. Sarò felice se dai un'occhiata a: http://stackoverflow.com/questions/18131389/how-to-convert-av-sample-fmt-s16-to-av-sample-fmt-s16p – frankish

+0

Reuben, ti capita di avere ancora questo codice? Sto cercando di far funzionare questa conversione, ma sto riscontrando alcuni problemi. Mi piacerebbe vedere la soluzione completa di lavoro se si potesse pubblicare un link. Grazie in anticipo. –

+0

Non ho più il codice per l'opzione 2 ... usare libswresample è l'unico modo corretto per risolvere questo problema. Quali sono i problemi che hai? –

2

Grazie a Reuben per una soluzione a questo. Ho trovato che alcuni dei valori di esempio erano leggermente fuori rispetto a un file ffmpeg -i file.wav. Sembra che nella conversione utilizzino un round() sul valore.

per eseguire la conversione, ho fatto quello che hai fatto con un'offerta di modifica di lavorare per qualsiasi quantità di canali:

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. 

} 

Sono andato da ffmpeg 0.11.1 -> 1.1.3 e ho trovato il cambiamento di formato campione fastidioso. Ho cercato di impostare request_sample_fmt su AV_SAMPLE_FMT_S16 ma sembra che il decodificatore aac non supporti comunque altro che AV_SAMPLE_FMT_FLTP.

+0

ottima aggiunta, grazie – frankish

+0

Ho aggiornato la mia risposta in modo migliore usando libswresample. È sorprendentemente facile da fare. –

+0

@BradMitchell Come possiamo fare l'opposto di questo? Ti dispiacerebbe dare un'occhiata a http://stackoverflow.com/questions/18131389/how-to-convert-av-sample-fmt-s16-to-av-sample-fmt-s16p? – frankish

5

Ho trovato 2 funzioni di resample da FFMPEG. La performance forse è meglio.

  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

Sei decisamente sulla buona strada qui Albert ... Oggi ho avuto un reclamo sul rendimento, quindi ho dovuto cercare un metodo ottimizzato per fare questa conversione e libswresample è il mio nuovo migliore amico. La mia risposta sopra è stata aggiornata con il codice necessario. –