FFmpegのAPIを使う～AACデータを再生する

# はじめに
AACデータを再生する方法を記載します。
OSはWindows10です。開発環境はVisual Studio2015を使いました。

データフローは次の通りです。
TS file -> demux -> (AAC) -> decode -> (fltp PCM) -> (s16 PCM) -> Speaker out

フローのStepを説明します。

- FFmpegでTS fileからAACデータを取り出します(demux)。
- FFmpegでAACデータをdecodeしてfltp PCMデータにします。
- FFmpegでfltp PCMデータをs16 PCMデータに変換します。
- Xaudio2でs16 PCMデータをSpeaker outします。

# PCMフォーマット
PCMのフォーマットを説明します。

FFmpegでAACをデコードするとAV_SAMPLE_FMT_FLTP(fltp)になります。
1sampleが32bit-floatのplanar形式です。
> LRが別々のエリア;L1,L2,...,Ln:R1,R2,...,Rn

Xaudio2ではAV_SAMPLE_FMT_S16(s16)にする必要があります。
1sample 16bit-integerのinterleave形式です。
> LRが交互に現れる;L1,R1,L2,R2,...,Ln,Rn

# fltpをs16に変換する

swresampleを使ってfltpをs16に変換します。

```cpp
(snip)
if (swr == NULL) {
    swr = swr_alloc();
    if (swr == NULL) {
        printf("swr_alloc error.\n");
        break;
    }
    av_opt_set_int(swr, "in_channel_layout", frame->channel_layout, 0);
    av_opt_set_int(swr, "out_channel_layout", frame->channel_layout, 0);
    av_opt_set_int(swr, "in_sample_rate", frame->sample_rate, 0);
    av_opt_set_int(swr, "out_sample_rate", frame->sample_rate, 0);
    av_opt_set_sample_fmt(swr, "in_sample_fmt", (AVSampleFormat)frame->format, 0);
    av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
    ret = swr_init(swr);
    if (ret < 0) {
        printf("swr_init error ret=%08x.\n", AVERROR(ret));
        break;
    }
    int buf_size = frame->nb_samples*frame->channels * 2; /* the 2 means S16 */
    swr_buf = new BYTE[buf_size];
    swr_buf_len = buf_size;
}

ret = swr_convert(swr, &swr_buf, frame->nb_samples, (const uint8_t**)frame->extended_data, frame->nb_samples);
if (ret < 0) {
    printf("swr_convert error ret=%08x.\n", AVERROR(ret));
}
(snip)
```

# sample code
Win32 Console アプリケーションを作成します。
[FFmpegのAPIを使う～ビルド環境構築](http://qiita.com/tobira-code/items/28df143014b606a53889)を参照してください。
[構成プロパティ]-[リンカー]-[入力]-[追加の依存ファイル]に次を追加します。

```
avcodec.lib
avdevice.lib
avfilter.lib
avformat.lib
avutil.lib
postproc.lib
swresample.lib
swscale.lib
xaudio2.lib
```

TS file "test.mpg"をFFmpegでdemuxしてAACデータを取り出します。
取り出したAACデータをデコードしてfltpにします。
fltpをs16に変換します。s16をXaudio2でSpeaker outします。

```cpp
#include "stdafx.h"
#include <xaudio2.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libswresample/swresample.h>
}

class VoiceCallback : public IXAudio2VoiceCallback
{
public:
	HANDLE event;
	VoiceCallback() : event(CreateEvent(NULL, FALSE, FALSE, NULL)) {}
	~VoiceCallback() { CloseHandle(event); }
	void STDMETHODCALLTYPE OnStreamEnd() {}
	void STDMETHODCALLTYPE OnVoiceProcessingPassEnd() {}
	void STDMETHODCALLTYPE OnVoiceProcessingPassStart(UINT32 samples) {}
	void STDMETHODCALLTYPE OnBufferEnd(void * context) { SetEvent(event); }
	void STDMETHODCALLTYPE OnBufferStart(void * context) {}
	void STDMETHODCALLTYPE OnLoopEnd(void * context) {}
	void STDMETHODCALLTYPE OnVoiceError(void * context, HRESULT Error) {}
};

int main()
{
	int ret = -1;

	ret = CoInitializeEx(NULL, COINIT_MULTITHREADED);
	if (FAILED(ret)) {
		printf("error CoInitializeEx ret=%d\n", ret);
		return -1;
	}

	IXAudio2 *audio = NULL;
	ret = XAudio2Create(&audio);
	if (FAILED(ret)) {
		printf("error XAudio2Create ret=%d\n", ret);
		return -1;
	}

	IXAudio2MasteringVoice *master = NULL;
	ret = audio->CreateMasteringVoice(&master);
	if (FAILED(ret)) {
		printf("error CreateMasteringVoice ret=%d\n", ret);
		return -1;
	}

	av_register_all();

	const char *filename = "test.mpg";
	AVFormatContext *format_context = NULL;
	ret = avformat_open_input(&format_context, filename, NULL, NULL);
	if (ret < 0) {
		printf("cannot open file. filename=%s, ret=%08x\n", filename, AVERROR(ret));
		return -1;
	}

	ret = avformat_find_stream_info(format_context, NULL);
	if (ret < 0) {
		printf("avformat_find_stream_info error. ret=%08x\n", AVERROR(ret));
		return -1;
	}

	AVStream *audio_stream = NULL;
	for (unsigned int i = 0; i < format_context->nb_streams; i++) {
		if (format_context->streams[i]->codecpar->codec_type == AVMediaType::AVMEDIA_TYPE_AUDIO) {
			audio_stream = format_context->streams[i];
			break;
		}
	}
	if (audio_stream == NULL) {
		printf("stream not found\n");
		return -1;
	}

	AVCodec *codec = avcodec_find_decoder(audio_stream->codecpar->codec_id);
	if (codec == NULL) {
		printf("avcodec_find_decoder codec not found. codec_id=%d\n", audio_stream->codecpar->codec_id);
		return -1;
	}

	AVCodecContext *codec_context = avcodec_alloc_context3(codec);
	if (codec_context == NULL) {
		printf("avcodec_alloc_context3 error.\n");
		return -1;
	}

	ret = avcodec_open2(codec_context, codec, NULL);
	if (ret < 0) {
		printf("avcodec_open2 error. ret=%08x\n", AVERROR(ret));
		return -1;
	}

	AVFrame *frame = av_frame_alloc();
	AVPacket packet;
	int frame_number = 0;
	SwrContext *swr = NULL;
	BYTE* swr_buf = 0;
	int swr_buf_len = 0;
	IXAudio2SourceVoice *voice = NULL;
	bool first_submit = true;
	BYTE** buf = NULL;
	int buf_cnt = 0;
	VoiceCallback callback;
	WAVEFORMATEX format = { 0 };

	while (1) {
		// read ES
		if ((ret = av_read_frame(format_context, &packet)) < 0) {
			printf("av_read_frame eof or error. ret=%08x\n", AVERROR(ret));
			break; // eof or error
		}
		if (packet.stream_index == audio_stream->index) {
			// decode ES
			if ((ret = avcodec_send_packet(codec_context, &packet)) < 0) {
				printf("avcodec_send_packet error. ret=%08x\n", AVERROR(ret));
			}
			if ((ret = avcodec_receive_frame(codec_context, frame)) < 0) {
				if (ret != AVERROR(EAGAIN)) {
					printf("avcodec_receive_frame error. ret=%08x\n", AVERROR(ret));
					break;
				}
			}
			else {
				printf("frame_number=%d\n", ++frame_number);
				if (swr == NULL) {
					swr = swr_alloc();
					if (swr == NULL) {
						printf("swr_alloc error.\n");
						break;
					}
					av_opt_set_int(swr, "in_channel_layout", frame->channel_layout, 0);
					av_opt_set_int(swr, "out_channel_layout", frame->channel_layout, 0);
					av_opt_set_int(swr, "in_sample_rate", frame->sample_rate, 0);
					av_opt_set_int(swr, "out_sample_rate", frame->sample_rate, 0);
					av_opt_set_sample_fmt(swr, "in_sample_fmt", (AVSampleFormat)frame->format, 0);
					av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
					ret = swr_init(swr);
					if (ret < 0) {
						printf("swr_init error ret=%08x.\n", AVERROR(ret));
						break;
					}
					int buf_size = frame->nb_samples*frame->channels * 2; /* the 2 means S16 */
					swr_buf = new BYTE[buf_size];
					swr_buf_len = buf_size;
				}

				ret = swr_convert(swr, &swr_buf, frame->nb_samples, (const uint8_t**)frame->extended_data, frame->nb_samples);
				if (ret < 0) {
					printf("swr_convert error ret=%08x.\n", AVERROR(ret));
				}
				if (voice == NULL) {
					format.wFormatTag = WAVE_FORMAT_PCM;
					format.nChannels = frame->channels;
					format.wBitsPerSample = 16;
					format.nSamplesPerSec = frame->sample_rate;
					format.nBlockAlign = format.wBitsPerSample / 8 * format.nChannels;
					format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
					ret = audio->CreateSourceVoice(
						&voice,
						&format,
						0,                          // UINT32 Flags = 0,
						XAUDIO2_DEFAULT_FREQ_RATIO, // float MaxFrequencyRatio = XAUDIO2_DEFAULT_FREQ_RATIO,
						&callback                   // IXAudio2VoiceCallback *pCallback = NULL,
					);
					if (FAILED(ret)) {
						printf("error CreateSourceVoice ret=%d\n", ret);
					}
					voice->Start();
				}
				if (buf == NULL) {
					buf = new BYTE*[2];
					buf[0] = new BYTE[swr_buf_len];
					buf[1] = new BYTE[swr_buf_len];
				}
				memcpy(buf[buf_cnt], swr_buf, swr_buf_len);
				XAUDIO2_BUFFER buffer = { 0 };
				buffer.AudioBytes = swr_buf_len;
				buffer.pAudioData = buf[buf_cnt];
				ret = voice->SubmitSourceBuffer(&buffer);
				if (FAILED(ret)) {
					printf("error SubmitSourceBuffer ret=%d\n", ret);
				}

				if (first_submit) {
					first_submit = false;
				}
				else {
					if (WaitForSingleObject(callback.event, INFINITE) != WAIT_OBJECT_0) {
						printf("error WaitForSingleObject\n");
					}
				}
				if (2 <= ++buf_cnt)
					buf_cnt = 0;
			}
		}
		else {
			// does not audio ES.
		}
		av_packet_unref(&packet);
	}

	if (WaitForSingleObject(callback.event, INFINITE) != WAIT_OBJECT_0) {
		printf("error WaitForSingleObject\n");
	}

	if (voice)
		voice->Stop();

	getchar(); // wait user input

	// omit cleanup for simplify. (free/release etc...)

	return 0;
}
```

# references
[Xaudio2を使う](http://qiita.com/tobira-code/items/39936c4e2b1168fb79ce)
[FFmpegのAPIを使う～ビルド環境構築](http://qiita.com/tobira-code/items/28df143014b606a53889)
[FFmpegのAPIを使う～AACデコーダ出力フォーマットfltpを調べてみる](http://qiita.com/tobira-code/items/00f138d41602787e2441)