精簡但精確的PCM編碼爲AAC解析

核心知識:

一、輸出媒體文件所需的結構 體:AVOutputFormat,AVFormatContext,AVStream,AVCodec,AVCodecContext,AVFrame,AVPacket(按出現先後 順序)。

二、AVOutputFormat是輸出容器(container)句柄,直接關聯文件,但只有句柄遠遠不夠,我們需要設置相關參數 AVFormatContext就是容器上下文(用avformat_alloc_context初始化),我們於其設置目標容器文件名,目標 容器句柄,I/O上下文(this->pb);

三、只有上面兩個,我們還是不能對相應文件進行寫入操作,需要對其進行流化操作,我們用avformat_new_stream( aacFormatCtx, NULL);初始化一個空流,但這時這個Stream還是不能用!因爲這是一個真正的空流!裏面的數據 指針和流頭都還未被初始化,我們需要avformat_write_header(aacFormatCtx, NULL);這次纔是真正的生成了可 以用的流。

四、AVCodec這個結構體包含了一個解碼器的必要參數,好在ffmpeg爲我們提供了簡單的接口avcodec_find_encoder ,這樣就可以很容易的獲取一個編碼器(只是相關參數!)。

五、僅僅有AvCOdec是遠遠不夠的,如果我們想要得到一個真正的編碼器,還需要在AVCodecContext設置採樣率、 比特率等控制音頻必要的參數。至此,我們有了編碼所需的一切準備,不過別急,我們還需要打開它,恰好avco dec_open2就提供了這個功能!

六、我們這時可以初始化我們的編碼幀AVFrame了,編碼格式和幀大小是我們要特別注意的,也是我們必須要設置的 ,因爲這是我們確定一個楨大小的必要條件。當然我們還需要填充它,爲了我們更好的控制,ffmpeg把緩衝區的 控制留給了我們,但是這也正是我們應該要特別小心的地方,不要忘記在不用這個Frame的時候釋放它!別相信 Av_malloc,它只是malloc的一個wrapper:)

七、我們的Rtp傳輸需要用包,所以這時就需要把frame寫爲AVPacket,但是我們要先給AVPacket設置內存空間av_new _packet(&pkt, aacBufferSize);後面一個是PayLoad,不知道PayLoad是什麼的回家去讀幼兒園:)

八、看起來這時我們可以把包寫進文件了av_write_frame(aacFormatCtx, &pkt);(有坑:)相對於av_interleaved_writ e_frame()),但是我們最重要的流序號和時間戳可不要忘了先設置好:),而且發送一個包後不要忘了清空這 個包,(用的流媒體模型:))。

九、盡情地寫入或發送把!

十、最後結束時可不要忘了清理戰場!

av_frame_free用來釋放AvFrame,我看到過好多例子,在這個之後還要av_free(Avframe->data[0]),其實完全沒有 必要!這個函數可以清空Avframe裏面所有的動態內存!然後我們關閉aacFormatCtx的I/O口;av_write_trailer(a acFormatCtx);只是用來清空流的,一點都沒有碰aacFormatCtx!所以我們需要手動釋放aacFormatCtx!最後我 們關閉編碼器:)

至此,Good Job and enjoy it by yourself!

// EncodeToAAC.cpp : 定義控制檯應用程序的入口點。
//
#include "stdafx.h"
extern "C"
{
#include"libavcodec/avcodec.h"
#include"libavformat/avformat.h"
#include"libavdevice/avdevice.h"
#include<stdlib.h>
}
int _tmain(int argc, _TCHAR* argv[])
{
	av_register_all();
	/*pcmfile:要打開的pcm波形文件
	* aacStream:將要編碼的流
	* aacFormatCtx:目標句柄上下文
	* aacCodecCtx:要創建的aac編碼器上下文
	* aacCodec:aac編碼器*/
	FILE *pcmfile = NULL;
	fopen_s(&pcmfile,"audio.pcm", "rb");
	if (!pcmfile)
	{
		printf("Find PCMfile error!\n");
		return -1;
	}
	int aacBufferSize = 0;
	uint8_t *aacBuffer = NULL;
	AVFrame *aacFrame = NULL;
	AVStream *aacStream = NULL;
	AVOutputFormat *aacOutFmt = NULL;
	AVFormatContext *aacFormatCtx = NULL;
	AVCodecContext *aacCodecCtx = NULL;
	AVCodec *aacCodec = NULL;
	//容器流的準備
	aacFormatCtx = avformat_alloc_context();
	aacOutFmt = av_guess_format(NULL, "audio.aac", NULL);
	aacFormatCtx->oformat = aacOutFmt;
	sprintf_s(aacFormatCtx->filename, "%s", "audio.aac");
	if (avio_open(&aacFormatCtx->pb, "audio.aac", AVIO_FLAG_READ_WRITE)<0)
	{
		printf("open file failed(get handle failed!)");
		return -1;
	}
	aacStream = avformat_new_stream(aacFormatCtx, NULL);
	avformat_write_header(aacFormatCtx, NULL);
	//解碼器相關準備
	aacCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
	aacCodecCtx = avcodec_alloc_context3(aacCodec);
	aacCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
	aacCodecCtx->codec = aacCodec;
	aacCodecCtx->time_base.den = 1;
	aacCodecCtx->time_base.num = 3600;
	aacCodecCtx->channels = 2;
	aacCodecCtx->channel_layout = AV_CH_LAYOUT_STEREO;
	aacCodecCtx->bit_rate = 192000;
	aacCodecCtx->sample_rate = 44100;
	aacCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;
	if (avcodec_open2(aacCodecCtx, aacCodec, NULL)<0)
	{
		printf("creat this encoder erro !");
		return -2;
	}
	//編碼幀的準備
	aacFrame = av_frame_alloc();
	aacFrame->nb_samples = aacCodecCtx->frame_size;
	aacFrame->format = aacCodecCtx->sample_fmt;
	aacBufferSize = av_samples_get_buffer_size(NULL, aacCodecCtx->channels, aacCodecCtx->frame_size
		, aacCodecCtx->sample_fmt, NULL);
	aacBuffer = (uint8_t *)av_malloc(aacBufferSize);
	avcodec_fill_audio_frame(aacFrame, aacCodecCtx->channels, aacCodecCtx->sample_fmt, aacBuffer
		, aacBufferSize, NULL);
	//發送包:)
	AVPacket pkt;
	av_new_packet(&pkt, aacBufferSize);
	for (int i = 1;; i++)
	{
		fread(aacBuffer, sizeof(uint8_t), aacBufferSize, pcmfile);
		if (feof(pcmfile))
		{
			printf("srcfile meet end! exiting....\n");
			break;
		}
		aacFrame->data[0] = aacBuffer;
		int gotFrame;
		if (avcodec_encode_audio2(aacCodecCtx, &pkt, aacFrame, &gotFrame)<0)
		{
			printf("encode error!!\n");
			break;
		}
		if (gotFrame)
		{
			printf("encode success %d frame(s)\n", i);
			pkt.stream_index = aacStream->index;
			if (pkt.pts == AV_NOPTS_VALUE)
			{
				pkt.pts = i * 3600;
				printf("encoded pkt pts: %d\n", pkt.pts);
			}
			av_write_frame(aacFormatCtx, &pkt);
			av_free_packet(&pkt);
		}

	}
	av_free_packet(&pkt);
	av_frame_free(&aacFrame);
	printf("encode all file success!\n");
	av_write_trailer(aacFormatCtx);
	avio_close(aacFormatCtx->pb);
	avformat_free_context(aacFormatCtx);
	avcodec_close(aacCodecCtx);
	fclose(pcmfile);
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章