オーディオとビデオを支える技術(8)- FFMPEGを使用して、ビデオの音声だけを保存する。

画像を再生した次は、音楽を再生していきます。
その一段目として、オーディオをファイルに保存するところまで行います。

画像の保存と、オーディオの保存ほとんど同じです。
画像の時は、sws_xxxといった関数を多く使いました。
オーディオの時には、swr_xxxといった関数を多く使います。

オーディオが持つ情報とは、

1  チャンネル
オーディオは、2つ、2:1つ、4つ、5:1つ、7:1つと、多くのオーディオを持つ場合があります。
いわゆる、モノラル、サラウンド、5:1chといったように何個の音が聞こえてくるか?といった問題です。

2  サンプリングレート
1秒間に、実行する標本化の回数を表す値です。
標準的なサンプリングレートとして44.1kHの場合一秒間に44100回分割したグラフを作ります。

f:id:nakadasanda1:20200717161422p:plain

3 ビットレート
音を何段階で表すかといった問題です。
標準的なビットレートが16bitの場合は、65536段階で分割します。

f:id:nakadasanda1:20200717161501p:plain

といった情報を持ちます。

 プログラム

この3つの情報を考えながら画像ファイルと同様に、コンバートコンテキストの作成、
デコードコンバートの操作を行うことで、音声の作成を行うことができます。

    //audioの変換コンテキスト作成
    if(pAudioCodecCtx->sample_fmt != AV_SAMPLE_FMT_S16){
        audio_convert_ctx = swr_alloc_set_opts(NULL,audioChanel_out,
                                               AV_SAMPLE_FMT_S16,sample_rate,
                                               pAudioCodecCtx->channel_layout,pAudioCodecCtx->sample_fmt,
                                               pAudioCodecCtx->sample_rate,0,NULL);
        if(swr_init(audio_convert_ctx)<0){
            cout << "swr init Failed " << endl;
        }
        cout << "swr convert init  " << endl;

        convert_spe =true;
        out_audioChanels = av_get_channel_layout_nb_channels(pAudioCodecCtx->channel_layout);
        Sample_size = av_rescale_rnd(DEFAULT_NB_SAMPLE,sample_rate,
                                     pAudioCodecCtx->sample_rate,
                                     AV_ROUND_UP);
        MAX_Sample_size = Sample_size;
        ret = av_samples_alloc_array_and_samples(&out_Audiobuffer,&LineSize,out_audioChanels,
                                                 DEFAULT_NB_SAMPLE,AV_SAMPLE_FMT_S16,1);
        if(ret < 0){
            cout << "audio buffer not create" << endl;
        }
        cout << "audio buffer create" << endl;
    }
    else
    {
        convert_spe = false;
    }

    cout << "start" << endl;
    cout << "______________________________" << endl;
    while (1) {

        if(av_read_frame(pFormatCtx,audiopackt) < 0){
            break;
        }

        if(audiopackt->stream_index ==audioStream)
        {

                readSize = avcodec_decode_audio4(pAudioCodecCtx,pAudioFrame,&got_Frame,audiopackt);
                if(readSize < 0){
                    cout << "length <0 .skip this frame" << endl;
                    break;
                }
                if(got_Frame){
                    if(convert_spe){
                        Sample_size = av_rescale_rnd(pAudioFrame->nb_samples,
                                    sample_rate,pAudioFrame->sample_rate,AV_ROUND_UP);
                        if(Sample_size > MAX_Sample_size){
                            //out_Audiobuffer初期化
                            av_freep(&out_Audiobuffer[0]);
                            ret = av_samples_alloc(out_Audiobuffer,&LineSize,out_audioChanels
                                                   ,Sample_size,AV_SAMPLE_FMT_S16,1);
                            if(ret < 0){
                                cout << "faild allocate" << endl;
                                break;
                            }
                            MAX_Sample_size = Sample_size;
                        }
                        //変換する
                        ret = swr_convert(audio_convert_ctx,out_Audiobuffer,Sample_size
                                          ,(const uint8_t **)pAudioFrame->data,pAudioFrame->nb_samples);

                        if(ret < 0){
                            cout << "faild allocate" << endl;
                            break;
                        }
                        int buf_size = av_samples_get_buffer_size(&LineSize,out_audioChanels,
                                                                  Sample_size,AV_SAMPLE_FMT_S16,1);

                        if(buf_size < 0){
                            cout << "Error while converting audio" << endl;
                            break;
                        }
                    }
                av_free_packet(audiopackt);
            }
        }
    }

最後にこのファイルを保存して終わり

    FILE *CacheFile;
    char *CacheFilename = "CacheFile.raw";
    CacheFile = fopen(CacheFilename,"wb");

    if(CacheFile==NULL){
        cout << "Can't open Cache File" << endl;
        return -1;
    }
    cout << "open Cache File" << endl;

----省略------

fwrite(out_Audiobuffer[0],1,buf_size,CacheFile);

out_audiobufferのサイズは、char型と同じなので第二引数に1を入れ、第3引数のバッファーサイズは、av_sample_get_buffer_sizeから習得できます。

実行ファイルの中にCacheFile.rawがあるので、mp3に変換して、音楽ソフトから再生したら、ビデオの音声が流れます。

最終的なソースコード

github.com

シリーズ一覧 nakadasanda.hatenablog.jp