FFMpeg に基づくオーディオ mp3/aac/wav デコード

コンパイル環境:Ubuntu16.04 64ビット
クロスコンパイルツール:arm-himix200-linux-gcc

1.ffmpegソースコードのダウンロード

ここで使用するのは ffmpeg-5.1.2.tar.gz ダウンロードアドレスは、ダウンロードアドレス をクリックしてください

2. クロスコンパイル

cd /root/
tar zxvf ffmpeg-5.1.2.tar.gz
cd ffmpeg-5.1.2
mkdir output
./configure --cross-prefix=arm-himix200-linux- --enable-cross-compile --target-os=linux --cc=arm-himix200-linux-gcc --arch=arm --prefix=/root/ffmpeg-5.1.2/output --disable-x86asm --disable-debug --disable-doc --disable-zlib  --disable-v4l2-m2m --disable-iconv --disable-network --disable-ffplay --disable-ffprobe --disable-symver --disable-indevs --disable-outdevs --disable-parsers --disable-bsfs --disable-filters --disable-protocols --disable-hwaccels --disable-muxers --disable-demuxers --disable-encoders --disable-decoders --enable-demuxer=aac --enable-demuxer=mp3 --enable-demuxer=wav --enable-decoder=mp3 --enable-decoder=aac --enable-decoder=pcm_alaw --enable-decoder=pcm_bluray --enable-decoder=pcm_dvd --enable-decoder=pcm_f16le --enable-decoder=pcm_f24le --enable-decoder=pcm_f32be --enable-decoder=pcm_f32le --enable-decoder=pcm_f64be --enable-decoder=pcm_f64le --enable-decoder=pcm_lxf --enable-decoder=pcm_mulaw --enable-decoder=pcm_s16be --enable-decoder=pcm_s16be_planar --enable-decoder=pcm_s16le --enable-decoder=pcm_s16le_planar --enable-decoder=pcm_s24be --enable-decoder=pcm_s24daud --enable-decoder=pcm_s24le --enable-decoder=pcm_s24le_planar --enable-decoder=pcm_s32be --enable-decoder=pcm_s32le --enable-decoder=pcm_s32le_planar --enable-decoder=pcm_s64be --enable-decoder=pcm_s64le --enable-decoder=pcm_s8 --enable-decoder=pcm_s8_planar --enable-decoder=pcm_u16be --enable-decoder=pcm_u16le --enable-decoder=pcm_u24be --enable-decoder=pcm_u24le --enable-decoder=pcm_u32be --enable-decoder=pcm_u32le --enable-decoder=pcm_u8 --enable-decoder=pcm_vidc --enable-decoder=pcm_zork --enable-protocol=file --enable-small --enable-muxer=pcm_s16le --extra-cflags="-ffunction-sections -fdata-sections -fsigned-char -Wformat"
make
make install

このようにして、必要なプログラムは /root/ffmpeg-5.1.2/output にあります。bin ディレクトリ内の ffmpeg は開発ボード上で実行できます。include の下には必要なヘッダー ファイルがあり、lib の下には必要な静的ライブラリがあります。 share/ffmpeg/ の例は、参照できるサンプル コードです。
./configure 設定コマンドは実際のニーズに応じて調整できることに注意してください。私のプロジェクトでは mp3、aac、および wav をデコードするだけでよいため、対応するデマルチプレクサとデコーダのみが設定されます。

CFLAGS += -ffunction-sections -fdata-sections
LDFLAGS += -Wl,--gc-sections

GCCリンク動作はセクションを最小の処理単位としており、セクション内のシンボルが参照されている限り、そのセクションが実行可能プログラムに追加されます。したがって、GCC はコンパイル時に -ffunction-sections および -fdata-sections を使用して、各関数またはシンボルをセクションとして作成できます。各セクションの名前は関数またはデータ名と一致します。リンク段階で、-Wl,--gc-sections は、未使用のセクション (-wl、後続のパラメーター -gc-sections がリンカーに渡されることを示します) を削除するようにリンカーに指示し、最終的な実行可能プログラムのサイズを削減します。 。

3. 静的ライブラリのリンク

LIBS += libavdevice.a \
		libavfilter.a \
		libavformat.a \
		libavcodec.a \
		libavutil.a \
		libswresample.a \
		libswscale.a

順序を間違えないように注意してください。順序を間違えると、依存関係があるため関数が見つからないと報告されます。

libavdevice.a (読み取りデバイス、カメラ、画面録画など)、libavfilter.a (透かしなどの特殊効果の追加)、libswscale.a (画像のストレッチ、ピクセル形式の変換など) を使用する必要はありません。 。)。

LIBS += libavformat.a \
		libavcodec.a \
		libavutil.a \
		libswresample.a

4. ヘッダファイル

#ifndef __AUDIO_DECODER__
#define __AUDIO_DECODER__

#ifdef __cplusplus
extern "C" {
#endif
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavcodec/avcodec.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/frame.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "libswresample/swresample.h"
#ifdef __cplusplus
}
#endif

class CAudioDecoder
{
public:
	CAudioDecoder();
	virtual ~CAudioDecoder(void);

	int DecodeOpen(uint codec);
	int DecodeClose(void);

	int DecodeFrame(uchar *pFrame, int nFrameSize, uchar *pOutBuf, int nBufferSize);
private:
	void Decode(uchar *pOutBuf, int nBufferSize, int &nSize);
	
public:
	static int transcode_pcm(const char* inputFile, const char* outputFile);

private:
	AVCodec *m_Codec;
	AVCodecContext *m_Context;
	AVCodecParserContext *m_Parser;
	AVPacket *m_Packet;
	AVFrame *m_Frame;

	struct SwrContext* m_converCtx;
	AVChannelLayout m_ChannelLayout;
	int m_outBytesPerSample;
	
	uint8_t* m_pData;
	int m_nSize;
};

ffmpeg のヘッダー ファイルに extern "C" がない理由がわかりませんが、リンク時の C++ と C の違いは何ですか。

5. 音声ファイルの変換


#define TRANSCODE_SAMPLERATE 8000
#define TRANSCODE_FORMAT AV_SAMPLE_FMT_S16
#define TRANSCODE_CHANNELS 1
#define TRANSCODE_NBSAMPLES 4096

/*
	* inputFile        input file name
	* outputFile        output file name
	* return	decode the length of the data
*/
int CAudioDecoder::transcode_pcm(const char* inputFile, const char* outputFile)
{
	CFile file;
	bool bRet = file.Open(outputFile, CFile::modeCreate | CFile::modeWrite);
	if (!bRet)
		return -1;

	int ret = -1;
	AVCodec* codec = NULL;
	AVCodecContext* codecContext = NULL;
	AVCodecParameters* codecpar = NULL;
	AVFrame* frame = NULL;	
	AVPacket *pPacket = NULL;
	struct SwrContext* converCtx = NULL;
	uint8_t** data = NULL;
	
	int streamindex = 0;
	int outBytesPerSample = 0;
	int allocsize = 0;
	AVChannelLayout out_ch_layout = AV_CHANNEL_LAYOUT_MONO;
	
	AVFormatContext* frameContext = avformat_alloc_context();
	if (frameContext == NULL) {
		goto cleanup;
	}

	if (avformat_open_input(&frameContext, inputFile, NULL, NULL) != 0) {
		goto cleanup;
	}

	if (avformat_find_stream_info(frameContext, NULL) < 0) {
		goto cleanup;
	}

	streamindex = av_find_best_stream(frameContext, AVMEDIA_TYPE_AUDIO, -1, -1, (const AVCodec**)&codec, -1);
	if (!codec) {
		goto cleanup;
	}
	
	codecContext = avcodec_alloc_context3(codec);
	codecpar = frameContext->streams[streamindex]->codecpar;
	avcodec_parameters_to_context(codecContext, codecpar);
	if (avcodec_open2(codecContext, codec, NULL) < 0) {
		goto cleanup;
	}

	outBytesPerSample = TRANSCODE_CHANNELS * av_get_bytes_per_sample(TRANSCODE_FORMAT);
	frame = av_frame_alloc();
	swr_alloc_set_opts2(&converCtx, &out_ch_layout, TRANSCODE_FORMAT, TRANSCODE_SAMPLERATE, &codecContext->ch_layout, codecContext->sample_fmt, codecContext->sample_rate, 0, NULL);
	swr_init(converCtx);
	data = (uint8_t**)av_calloc(1, sizeof(*data));
	allocsize = av_samples_alloc(data, NULL, TRANSCODE_CHANNELS, TRANSCODE_NBSAMPLES, TRANSCODE_FORMAT, 0);

	pPacket = av_packet_alloc();
	while (av_read_frame(frameContext, pPacket) >= 0) {
		if (pPacket->stream_index != streamindex)
			continue;

		if (avcodec_send_packet(codecContext, pPacket) < 0) {
			goto cleanup;
		}

		while (avcodec_receive_frame(codecContext, frame) >= 0) {
			ret = swr_convert(converCtx, data, allocsize, (const uint8_t**)frame->data, frame->nb_samples);
			if (ret > 0)
				file.Write(data[0], ret * outBytesPerSample);
		}
		av_packet_unref(pPacket);
	}

	while ((ret = swr_convert(converCtx, data, allocsize, NULL, 0)) > 0) {
		file.Write(data[0], ret * outBytesPerSample);
	}

cleanup:
	if (frameContext) {
		avformat_close_input(&frameContext);
		avformat_free_context(frameContext);
	}

	if (codecContext)
		avcodec_free_context(&codecContext);

	if (pPacket)
		av_packet_free(&pPacket);

	if (data)
		av_freep(&data[0]);
	
	av_freep(&data);

	if (frame)
		av_frame_free(&frame);
	
	if (converCtx)
		swr_free(&converCtx);

	if (file.IsOpened()) {
		ret = file.GetLength();
		file.Close();
	}
	
	return ret;
}

CFile はプロジェクトのファイル クラスであり、ファイル IO API で置き換えることができることに注意してください。

6. リアルタイムオーディオデコード

aac を例に挙げると、aac のパーサーを使用するため、ffmpeg をクロスコンパイルするときにオプションを追加する必要があります。

--enable-parser=aac

同様に、mp3 デコードをサポートする必要がある場合は、ffmpeg をクロスコンパイルするときにオプションを追加する必要があります。

--enable-parser=mp3

他の形式も同様です。

#include "AudioDecoder.h"

CAudioDecoder::CAudioDecoder()
{
	m_Codec = NULL;
	m_Context = NULL;
	m_Parser = NULL;
	m_Packet = NULL;
	m_Frame = NULL;

	m_converCtx = NULL;
	m_ChannelLayout = AV_CHANNEL_LAYOUT_MONO;
	m_outBytesPerSample = 0;
	
	m_pData = NULL;
	m_nSize = 0;
}

CAudioDecoder::~CAudioDecoder(void)
{
	DecodeClose();
}

int CAudioDecoder::DecodeOpen(uint codec)
{
	int ret = -1;

	switch (codec) {
		case CODEC_AAC:
			m_Codec = (AVCodec *)avcodec_find_decoder(AV_CODEC_ID_AAC);
			break;
		default:
			m_Codec = NULL;
			break;
	}

	if (!m_Codec) {
		fprintf(stderr, "Codec not found\n");
		goto error;
	}
	
	m_Context = avcodec_alloc_context3(m_Codec);
	if (!m_Context) {
		fprintf(stderr, "Could not allocate audio codec context\n");
		goto error;
	}
	
	ret = avcodec_open2(m_Context, m_Codec, NULL);
	if (ret < 0) {
		fprintf(stderr, "Could not open codec\n");
		goto error;
	}

	m_Parser = av_parser_init(m_Codec->id);
	if (!m_Parser) {
		fprintf(stderr, "Parser not found\n");
		goto error;
	}

	m_Packet = av_packet_alloc();
	if (!m_Packet) {
		fprintf(stderr, "Could not allocate audio packet\n");
		goto error;
	}
		
	m_Frame = av_frame_alloc();
	if (!m_Frame) {
		fprintf(stderr, "Could not allocate audio frame\n");
		goto error;
	}

	m_outBytesPerSample = TRANSCODE_CHANNELS * av_get_bytes_per_sample(TRANSCODE_FORMAT);
	m_nSize = TRANSCODE_NBSAMPLES * m_outBytesPerSample;
	m_pData = (uint8_t *)av_malloc(m_nSize);

	return 0;
	
error:
	DecodeClose();
	return ret;
}

int CAudioDecoder::DecodeClose(void)
{
	av_freep(&m_pData);

	if (m_Frame)
	    av_frame_free(&m_Frame);

	if (m_converCtx)
		swr_free(&m_converCtx);

	if (m_Packet)
	    av_packet_free(&m_Packet);

	if (m_Parser)
    	av_parser_close(m_Parser);

	if (m_Context)
    	avcodec_free_context(&m_Context);


	return 0;
}

int CAudioDecoder::DecodeFrame(uchar *pFrame, int nFrameSize, uchar *pOutBuf, int nBufferSize)
{
	if (pFrame == NULL || nFrameSize == 0 || pOutBuf == NULL || nBufferSize == 0) {
		return -1;
	}

	int nSize = 0;
    uint8_t *data = pFrame;
    int data_size = nFrameSize;
	
	while (data_size > 0) {
        int ret = av_parser_parse2(m_Parser, m_Context, &m_Packet->data, &m_Packet->size, data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
		if (ret < 0) {
			fprintf(stderr, "Error while parsing\n");
			break;
		}

		if (m_Packet->size > 0) {
			data += ret;
			data_size -= ret;

			Decode(pOutBuf, nBufferSize, nSize);
		} else {
			break;
		}
	}

	return nSize;
}

void CAudioDecoder::Decode(uchar *pOutBuf, int nBufferSize, int &nSize)
{
	int ret = avcodec_send_packet(m_Context, m_Packet);
	if (ret < 0) {
		return ;
	}
	
	swr_alloc_set_opts2(&m_converCtx, &m_ChannelLayout, TRANSCODE_FORMAT, TRANSCODE_SAMPLERATE, &m_Context->ch_layout, m_Context->sample_fmt, m_Context->sample_rate, 0, NULL);
	swr_init(m_converCtx);

	while (avcodec_receive_frame(m_Context, m_Frame) >= 0) {		
		ret = swr_convert(m_converCtx, &m_pData, m_nSize, (const uint8_t**)m_Frame->data, m_Frame->nb_samples);
		if (ret > 0) {
			if (nBufferSize < nSize + ret * m_outBytesPerSample) {
				fprintf(stderr, "nBufferSize=[%d] too small!!!\n", nBufferSize);
				break;
			}
			
			memcpy(pOutBuf + nSize, m_pData, ret * m_outBytesPerSample);
			nSize += ret * m_outBytesPerSample;
		}
	}

	while ((ret = swr_convert(m_converCtx, &m_pData, m_nSize, NULL, 0)) > 0) {
		if (nBufferSize < nSize + ret * m_outBytesPerSample) {
			fprintf(stderr, "nBufferSize=[%d] too small!!!\n", nBufferSize);
			break;
		}
		memcpy(pOutBuf + nSize, m_pData, ret * m_outBytesPerSample);
		nSize += ret * m_outBytesPerSample;
	}

	return ;
}

注: CODEC_AAC は、私のプロジェクトの aac 形式マクロです。これを独自の aac/mp3 に置き換え、AV_CODEC_ID_AAC を対応する列挙に置き換えて、他の形式のデコードを完了します。

7. 使用上の注意

7.1 ファイル変換

int nPcmSize = CAudioDecoder::transcode_pcm(inputFile, outputFile);

inputFile: デコードする必要があるファイル名
OutputFile: デコードされたファイル名
nPcmSize: デコードされたファイルの長さ、<=0 は失敗、>0 は成功

7.2 リアルタイムデコード

AACデコーダを作成する

CAudioDecoder *decoder = new CAudioDecoder();
decoder->DecodeOpen(CODEC_AAC);

デコード

int OutSize = decoder->DecodeFrame(pFrame, nFrameSize, pOutBuf, nBufferSize);	

デコーダーを破壊する

delete decoder;
decoder = NULL;

デコード データ、mn フレーム送信可能、m>=0 n>=0
pFrame 入力、デコードされるデータ
nFrameSize 入力、デコードされるデータ長
pOutBuf 入力 出力、デコードされたデータ、呼び出し元によって割り当てられたバッファ
nBufferSize 入力、渡されるバッファ pOutBuf length
return は、デコード後のバッファの使用データ長を返します。受信データが 1 フレーム未満の場合、次の入力データが 1 フレームに達するまで、ローカル呼び出しは 0 を返します。

注: オーディオ データの連続セットに対して、デコーダの作成と破棄を繰り返し呼び出さないでください。

おすすめ

転載: blog.csdn.net/weixin_43549602/article/details/129927030