【Qt 5】QaudioOutputを使用してffmpeg復号化されたオーディオを再生する

5188 ワード

QaudioOutputを使用してffmpeg復号化されたオーディオを再生
前に書いて、QaudioOutputでメディアオーディオを再生するのはお勧めしません.強くないので、制御しにくいです.SDLがおすすめです.
オーディオデータフォーマット
オーディオの裸ストリームを再生するには、データ自体のほかに、正しく再生するには、このデータのフォーマットを規定する必要があります.ここで、チャネル数、サンプリングレート、サンプリングデータ型は最も基本的なフォーマット内容である.例えば、1セグメントのチャネル数が2であり、サンプリングレートが48000 Hzであり、データ型が8ビットの符号なし整形オーディオ裸ストリームであり、格納方式は以下の通りである.
チャネル0のサンプリングポイント0
チャネル1のサンプリングポイント0
チャネル0のサンプリングポイント1
チャネル1のサンプリングポイント1
チャネル0のサンプリングポイント2
チャネル1のサンプリングポイント2
このように...
上で、各格子は1チャネルのサンプリングポイントであり、48000 Hzは1秒間に1チャネル48000サンプリングポイントがあり、2チャネルは2*48000=96000サンプリングポイントがあり、各サンプリングポイントは8ビットの符号なし整形データである.このように配列された1セグメントのデータのチャネル数、サンプリングレート、サンプリングデータ型が明確に分かれば、このオーディオデータを再生することができる.
ffmpegでのオーディオデータフォーマット
復号コンテキストでは、オーディオのデータフォーマットを取得できます.
構造体AVCodecContextでは、
    /* audio only */
    int sample_rate; ///< samples per second
    int channels;    ///< number of audio channels

    /**
     * audio sample format
     * - encoding: Set by user.
     * - decoding: Set by libavcodec.
     */
    enum AVSampleFormat sample_fmt;  ///< sample format

それぞれサンプリングレート、チャネル数、サンプリングデータフォーマットです.
ここで、データフォーマットAVSampleFormatは以下の通りである.
enum AVSampleFormat {
    AV_SAMPLE_FMT_NONE = -1,
    AV_SAMPLE_FMT_U8,          ///< unsigned 8 bits
    AV_SAMPLE_FMT_S16,         ///< signed 16 bits
    AV_SAMPLE_FMT_S32,         ///< signed 32 bits
    AV_SAMPLE_FMT_FLT,         ///< float
    AV_SAMPLE_FMT_DBL,         ///< double

    AV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planar
    AV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planar
    AV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planar
    AV_SAMPLE_FMT_FLTP,        ///< float, planar
    AV_SAMPLE_FMT_DBLP,        ///< double, planar
    AV_SAMPLE_FMT_S64,         ///< signed 64 bits
    AV_SAMPLE_FMT_S64P,        ///< signed 64 bits, planar

    AV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically
};

同じタイプのデータフォーマットの多くは、AV_SAMPLE_FMT_U 8とAV_SAMPLE_FMT_U 8 Pは、いずれも8ビットの符号なし整形ですが、後者は接尾辞Pが1つ増えていて、注釈でplanarを説明していますが、これはどういう意味ですか?
これは実は別のデータの構造で、一般的にffmpegが復号したばかりのデータは私たちが前に見たように並べられているのではなく、各チャネルパケットで並べられています.
チャネル0のサンプリングポイント0
チャネル0のサンプリングポイント1
チャネル0のサンプリングポイント2
...
チャネル1のサンプリングポイント0
チャネル1のサンプリングポイント1
チャネル1のサンプリングポイント2
...
このようなデータは直接再生することはできず、以前のようにサンプリングポイント順に並べて再生しなければならないので、再生する前に非planar形式に変換しなければならない.
QaudioOutputのオーディオフォーマットQaudioFormat
QaudioOutputを初期化するには、QaudioFormatを設定する必要があります.次のプロパティが必要です.
setSampleRate(int);       //サンプリングレートの設定
setChannelCount(int);   //チャネル数の設定
setSampleSize(int);         //サンプリングデータサイズの設定(bitビット数)
setSampleType(QAudioFormat::SampleType);   //サンプリングデータのフォーマット
setCodec(QString);         //デコーダタイプを設定すると、裸ストリームは「audio/pcm」に設定されます.
QaudioFormat::SampleTypeサンプリングデータフォーマットは次のとおりです.
enum SampleType { Unknown, SignedInt, UnSignedInt, Float };

有用なのはSignedInt有符号整形、UnSignedInt無符号整形、Float浮動小数点型であり、実際にsampleSizeとsampleTypeの2つを合わせてサンプリングデータ型を決定していることがわかる.例えばsetSampleSize(8)であり、setSampleType(UnSignedInt)はffmpegのAV_に対応するSAMPLE_FMT_U8.
ffmpegの関数av_get_bytes_per_sampleはAVSampleFormatに対応するサンプリングデータ型のバイト数を得ることができ,この結果に8を乗じてQaudioFormatのsampleSizeパラメータを得ることができる.
FFmpegでのオーディオフォーマット変換
前述したように、ffmpegで復号されたばかりのデータは、配列方式のため、直接再生できないため、変換する必要があります.まず、オーディオ復号コンテキストに基づいて変換コンテキストを設定し、初期化します.
swrCtx = swr_alloc_set_opts(nullptr,
                            audioCodecCtx->channel_layout, AV_SAMPLE_FMT_S16, audioCodecCtx->sample_rate,
                            audioCodecCtx->channel_layout, audioCodecCtx->sample_fmt, audioCodecCtx->sample_rate,
                            0, nullptr);
swr_init(swrCtx);

1フレームのオーディオを復号した後、変換後に必要なメモリサイズを計算し、メモリを割り当ててフォーマット変換します.
int bufsize = av_samples_get_buffer_size(nullptr, frame->channels, frame->nb_samples,
                                         AV_SAMPLE_FMT_S16, 0);
uint8_t *buf = new uint8_t[bufsize];
swr_convert(swrCtx, &buf, frame->nb_samples, (const uint8_t**)(frame->data), frame->nb_samples);

このようにして得られたbufのオーディオデータを再生することができます.使用後はdelete[]bufを忘れないでください.
最後に復号が完了したら、変換コンテキストを解放することを忘れないでください.
swr_free(&swrCtx);

QaudioOutputオーディオ再生
QaudioFormatを設定してからQaudioOutputを初期化し、オーディオデバイスを開きます.
QAudioFormat audioFormat;
audioFormat.setSampleRate(audioCodecCtx->sample_rate);
audioFormat.setChannelCount(audioCodecCtx->channels);
audioFormat.setSampleSize(8*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16));
audioFormat.setSampleType(QAudioFormat::SignedInt);
audioFormat.setCodec("audio/pcm");

QAudioOutput audioOutput = new QAudioOutput(audioFormat);
QIODevice *audioDevice = audioOutput->start();

これにより、QIODevice::write()メソッドで、オーディオデータを書き込み、再生することができます.前節で変換したデータを次のコードで再生します.
audioDevice->write((const char*)buf, bufsize);
delete[] buf;

再生が停止した後、QaudioOutputを停止して解放することを忘れないでください.
audioOutput->stop();
delete audioOutput;