2013-02-20 1 views
7

Je décode aac à pcm avec ffmpeg avec avcodec_decode_audio3. Cependant, il décode en format d'échantillon AV_SAMPLE_FMT_FLTP (PCM 32bit Float Planar) et j'ai besoin de AV_SAMPLE_FMT_S16 (PCM 16 bits signé - S16LE).Comment convertir le taux d'échantillonnage de AV_SAMPLE_FMT_FLTP à AV_SAMPLE_FMT_S16?

Je sais que ffmpeg peut le faire facilement avec -sample_fmt. Je veux faire la même chose avec le code mais je ne pouvais toujours pas le comprendre. Audio_resample n'a pas fonctionné pour: il échoue avec un message d'erreur: .... la conversion a échoué.

+0

Avez-vous déjà trouver la réponse à cette question? Suis confronté exactement le même problème –

Répondre

35

EDIT 9 avril 2013: J'ai trouvé comment utiliser libswresample pour cela ... beaucoup plus rapidement!

À un certain moment au cours des 2-3 dernières années, le format de sortie du décodeur AAC de FFmpeg changé de AV_SAMPLE_FMT_S16 à AV_SAMPLE_FMT_FLTP. Cela signifie que chaque canal audio possède son propre tampon, et chaque valeur d'échantillon est une valeur à virgule flottante de 32 bits mise à l'échelle de -1,0 à +1,0.

Considérant qu'avec AV_SAMPLE_FMT_S16 les données sont dans un seul tampon, avec les échantillons entrelacés, et chaque échantillon est un entier signé de -32767 à +32767.

Et si vous avez vraiment besoin de votre fichier audio comme AV_SAMPLE_FMT_S16, vous devez effectuer la conversion vous-même. Je me suis dit à deux façons de le faire:

1. Utilisez libswresample (recommandé)

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

Et c'est tout ce que vous avez à faire!

2. le faire manuellement en C (réponse originale, non recommandé)

Donc, dans votre boucle de décodage, lorsque vous avez un paquet audio vous décodez comme ceci:

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

Et puis, si vous avez une image complète de l'audio, vous pouvez le convertir assez facilement:

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

J'ai laissé deux choses pour plus de clarté ... e Le serrage dans le chemin mono doit idéalement être dupliqué dans le chemin stéréo. Et le code peut être facilement optimisé.

+0

J'ai un problème connexe, cette fois, j'ai besoin de convertir S16 en S16P. Parce que le dernier ffmpeg a besoin de S16P pour le codage libmp3lame. Je serai heureux si vous jetez un oeil à: http://stackoverflow.com/questions/18131389/how-to-convert-av-sample-fmt-s16-to-av-sample-fmt-s16p – frankish

+0

Reuben, auriez-vous ce code encore? J'essaie de faire fonctionner cette conversion mais j'ai quelques problèmes. Je voudrais voir la solution de travail complète si vous pouviez poster un lien. Merci d'avance. –

+0

Je n'ai plus le code pour l'option 2 ... l'utilisation de libswresample est la seule façon de résoudre ce problème. Quels sont les problèmes que vous avez? –

2

Merci Reuben pour une solution à ce problème. J'ai trouvé que certaines des valeurs de l'échantillon étaient légèrement décalées par rapport à un ffmpeg -i file.wav. Il semble que dans la conversion, ils utilisent un round() sur la valeur.

Pour faire la conversion, je l'ai fait ce que vous avez fait avec une offre de modification de travailler pour une quantité de canaux:

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. 

} 

Je suis passé de ffmpeg 0.11.1 -> 1.1.3 et trouvé le changement du format de l'échantillon ennuyeux. J'ai regardé en réglant le request_sample_fmt à AV_SAMPLE_FMT_S16 mais il semble que le décodeur aac ne supporte pas autre chose que AV_SAMPLE_FMT_FLTP de toute façon.

+0

super ajout, merci – frankish

+0

J'ai mis à jour ma réponse avec un meilleur moyen en utilisant libswresample. C'est étonnamment facile à faire. –

+0

@BradMitchell Comment pouvons-nous faire le contraire de celui-ci? Pourriez-vous jeter un oeil à http://stackoverflow.com/questions/18131389/how-to-convert-av-sample-fmt-s16-to-av-sample-fmt-s16p? – frankish

5

J'ai trouvé 2 fonctions de rééchantillonnage à partir de FFMPEG. La performance peut-être mieux.

  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

Vous étiez certainement sur la bonne voie ici Albert ... J'ai eu une plainte de performance plus tôt aujourd'hui alors je devais chercher une méthode optimisée pour faire cette conversion et libswresample est mon nouveau meilleur ami. Ma réponse ci-dessus a été mise à jour avec le code nécessaire. –

Questions connexes