14

私はavcodec_decode_audio3でffmpegを使ってaacをpcmにデコードしています。ただし、AV_SAMPLE_FMT_FLTPサンプル形式(PCM 32ビット浮動小数点平面)にデコードされるため、AV_SAMPLE_FMT_S16(PCM 16ビット符号付き-S16LE)が必要です。

私はffmpegが-sample_fmtでこれを簡単に行うことができることを知っています。コードでも同じことをしたいのですが、それでも理解できませんでした。

audio_resampleが機能しませんでした:エラーメッセージで失敗します:....変換に失敗しました。

4

3 に答える 3

43

編集 2013 年 4 月 9 日: libswresample を使用してこれを行う方法を考え出しました... はるかに高速です!

過去 2 ~ 3 年のある時点で、FFmpeg の AAC デコーダーの出力形式が AV_SAMPLE_FMT_S16 から AV_SAMPLE_FMT_FLTP に変更されました。つまり、各オーディオ チャネルには独自のバッファがあり、各サンプル値は -1.0 から +1.0 にスケーリングされた 32 ビット浮動小数点値です。

AV_SAMPLE_FMT_S16 の場合、データは単一のバッファーにあり、サンプルはインターリーブされ、各サンプルは -32767 から +32767 までの符号付き整数です。

AV_SAMPLE_FMT_S16 としてのオーディオが本当に必要な場合は、自分で変換する必要があります。私はそれを行う2つの方法を考え出しました:

1. libswresample を使用する(推奨)

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

そして、それはあなたがしなければならないすべてです!

2.Cで手作業で行う(元の回答、お勧めしません)

したがって、デコード ループでは、オーディオ パケットを取得したら、次のようにデコードします。

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

そして、オーディオのフル フレームがある場合は、かなり簡単に変換できます。

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

わかりやすくするために、いくつかのことを省略しています...モノパスのクランプは、理想的にはステレオパスで複製する必要があります。また、コードは簡単に最適化できます。

于 2013-03-12T21:23:12.663 に答える
7

FFMPEG から 2 つのリサンプル関数を見つけました。パフォーマンスは向上するかもしれません。

  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
于 2013-04-08T07:43:44.793 に答える
2

これを解決してくれた Reuben に感謝します。単純な ffmpeg -i file.wav と比較すると、サンプル値の一部がわずかにずれていることがわかりました。変換では、値に round() を使用しているようです。

変換を行うために、任意の数のチャネルで機能するように変更の入札で行ったことを行いました。

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.

}

私は ffmpeg 0.11.1 -> 1.1.3 から行き、サンプル形式の変更が煩わしいことに気づきました。request_sample_fmt を AV_SAMPLE_FMT_S16 に設定することを検討しましたが、aac デコーダーは AV_SAMPLE_FMT_FLTP 以外をサポートしていないようです。

于 2013-03-21T22:29:49.787 に答える