Jak przekonwertować częstotliwość próbkowania z próbki AV FMT FLTP na próbkę AV FMT S16?

Dekoduję AAC do pcm z ffmpeg z avcodec_decode_audio3. Jednak dekoduje do formatu przykładowego AV_SAMPLE_FMT_FLTP (PCM 32bit Float Planar) i potrzebuję AV_SAMPLE_FMT_S16 (PCM 16 bit signed - s16le).

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

Audio_resample nie działa dla: to fails with error message: .... konwersja nie powiodła się.

Author: frankish, 2013-02-21

3 answers

EDIT 9th April 2013: opracowano, jak używać libswresample, aby to zrobić... o wiele szybciej!

W pewnym momencie w ciągu ostatnich 2-3 lat format wyjściowy dekodera 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 z AV_SAMPLE_FMT_S16 dane są w jednym buforze, z samplami przeplatanymi, a każda próbka jest podpisaną liczbą całkowitą od -32767 do +32767.

I jeśli naprawdę potrzebujesz audio jako AV_SAMPLE_FMT_S16, musisz wykonać konwersję samodzielnie. Wymyśliłem dwa sposoby:

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, co musisz zrobić!

2. Do it by hand in C (original answer, not recommended)

Więc w pętli dekodowania, gdy masz pakiet audio, dekodujesz 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);

I wtedy, jeśli masz pełną klatkę audio, możesz ją dość łatwo przekonwertować:

    // 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łam kilka rzeczy dla jasności... zacisk w ścieżce mono powinien być idealnie powielony w ścieżce stereo. A kod można łatwo zoptymalizować.
 37
Author: Reuben Scratton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-09 21:11:28

Znalazłem 2 funkcje resample z FFMPEG. Wydajność może 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
 5
Author: Albert Liao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-08 07:43:44

Dzięki Reuben za rozwiązanie tego problemu. Odkryłem, że niektóre wartości próbki były nieco wyłączone w porównaniu z prostym plikiem ffmpeg-i.wav. Wygląda na to, że w konwersji używają metody round () na wartości.

Aby zrobić konwersję, zrobiłem to, co ty zrobiłeś z ofertą modyfikacji do pracy 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.

}

Przeszedłem z ffmpeg 0.11.1 - > 1.1.3 i stwierdziłem, że zmiana formatu próbki jest irytująca. Spojrzałem na ustawienie request_sample_fmt na AV_SAMPLE_FMT_S16 ale wygląda na to, że dekoder aac i tak nie obsługuje niczego innego niż AV_SAMPLE_FMT_FLTP.

 2
Author: Brad Mitchell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-21 22:29:49