1. 程式人生 > >使用ffmpeg轉碼pcm至aac格式

使用ffmpeg轉碼pcm至aac格式

準備工作

去官網下載原始碼自己編譯所需要的庫,或者直接從官網下載已經編寫好的庫,由於本例是Window平臺下的開發,而官網已經有了編譯好的庫,所以直接下載編譯所需要的庫檔案即可。下載地址:需要下載兩個壓縮包,Dev版本和Shared版本,其中Dev版本是標頭檔案和lib庫檔案,Shared是包含執行所需要的dll檔案。

下載完成後,需要把這些標頭檔案、靜態庫、動態連結庫引入到我們的工程裡面,不過在Windows下面,標頭檔案除了要用到FFMPEG提供的,還要使用另外三個檔案:inttypes.h,stdint.h,_mingw.h,這三個檔案放到FFMPEG標頭檔案的根目錄下即可。

專案工程

程式碼示例

// C++程式碼需要加這個!!否則找不到函式的
extern "C"
{
#include "libavcodec\avcodec.h"
#include "libavformat\avformat.h"
#include "libswscale\swscale.h"

// 連結的ffmpeg庫,我把所有可能需要用到的都寫這裡了
#pragma comment (lib, "Ws2_32.lib")  
#pragma comment (lib, "avcodec.lib")
#pragma comment (lib, "avdevice.lib")
#pragma comment (lib, "avfilter.lib")
#pragma comment (lib, "avformat.lib")
#pragma comment (lib, "avutil.lib")
#pragma comment (lib, "swresample.lib")
#pragma comment (lib, "swscale.lib")
};

int main(int argc, char* argv[])
{
	AVFormatContext* pFormatCtx;
	AVOutputFormat* fmt;
	AVStream* audio_st;
	AVCodecContext* pCodecCtx;
	AVCodec* pCodec;

	uint8_t* frame_buf;
	AVFrame* frame;
	int size;

	FILE *fileIn = fopen("Beyond.pcm", "rb");
	char* fileOut = "Beyond.aac";		

	// 初始化註冊FFMPEG以供使用
	av_register_all();

	// 解碼檔案格式
	avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, fileOut);
	fmt = pFormatCtx->oformat;

	//注意輸出路徑
	if (avio_open(&pFormatCtx->pb, fileOut, AVIO_FLAG_READ_WRITE) < 0)  
	{
		printf("輸出檔案開啟失敗!\n");
		return -1;
	}

	audio_st = avformat_new_stream(pFormatCtx, 0);
	if (audio_st == NULL)
	{
		printf("新建流失敗!!!\n");
		return -1;
	}

	// 設定轉碼資訊
	pCodecCtx = audio_st->codec;
	pCodecCtx->codec_id = fmt->audio_codec;
	pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
	pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;
	pCodecCtx->sample_rate= 44100;				// 音訊的取樣率
	pCodecCtx->channel_layout=AV_CH_LAYOUT_STEREO;
	pCodecCtx->channels = av_get_channel_layout_nb_channels(pCodecCtx->channel_layout);
	pCodecCtx->bit_rate = 64000;				// 音訊的位元率

	//除錯輸出格式資訊
	av_dump_format(pFormatCtx, 0, fileOut, 1);

	pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
	if (!pCodec)
	{
		printf("找不到輸入檔案所需的編碼器!\n");
		return -1;
	}
	if (avcodec_open2(pCodecCtx, pCodec,NULL) < 0)
	{
		printf("開啟編碼器失敗!\n");
		return -1;
	}

	frame = avcodec_alloc_frame();
	frame->nb_samples= pCodecCtx->frame_size;
	frame->format= pCodecCtx->sample_fmt;
	
	size = av_samples_get_buffer_size(NULL, pCodecCtx->channels,pCodecCtx->frame_size,pCodecCtx->sample_fmt, 1);
	frame_buf = (uint8_t *)av_malloc(size);
	avcodec_fill_audio_frame(frame, pCodecCtx->channels, pCodecCtx->sample_fmt,(const uint8_t*)frame_buf, size, 1);
	
	// 填充輸出的標頭檔案資訊
	avformat_write_header(pFormatCtx,NULL);

	AVPacket pkt;
	av_new_packet(&pkt,size);

	for (int i=0; ; i++)
	{
		//讀入PCM
		if (fread(frame_buf, 1, size, fileIn) < 0)
		{
			printf("檔案讀取錯誤!\n");
			return -1;
		}
		else if(feof(fileIn))
		{
			break;
		}
		frame->data[0] = frame_buf;  //取樣訊號

		frame->pts=i*100;
		int got_frame=0;
		//編碼
		int ret = avcodec_encode_audio2(pCodecCtx, &pkt,frame, &got_frame);
		if(ret < 0)
		{
			printf("編碼錯誤!\n");
			return -1;
		}
		if (got_frame==1)
		{
			pkt.stream_index = audio_st->index;
			ret = av_write_frame(pFormatCtx, &pkt);
			av_free_packet(&pkt);
		}
	}
	
	//寫檔案尾
	av_write_trailer(pFormatCtx);

	//清理
	if (audio_st)
	{
		avcodec_close(audio_st->codec);
		av_free(frame);
		av_free(frame_buf);
	}
	avio_close(pFormatCtx->pb);
	avformat_free_context(pFormatCtx);
	fclose(fileIn);

	printf("轉碼完畢!!!\n");
	return 0;
}

由於FFMPEG會沒事就修改介面,所以不同版本的FFMPEG可能會編譯不過,反正2015年1月23日17:29:31是可以編譯通過的。

參考