ffmpeg 解碼音頻(aac、mp3)輸出pcm文件
播放pcm可以參考: ffplay -ar 48000 -ac 2 -f f32le out.pcm
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <libavutil/frame.h>
#include <libavutil/mem.h>
#include <libavcodec/avcodec.h>#define AUDIO_INBUF_SIZE 10240
#define AUDIO_REFILL_THRESH 4096static void decode(AVCodecContext* dec_ctx, AVPacket* pkt, AVFrame* frame, FILE *outfile)
{int i, ch;int ret, sample_size;int send_ret = 1;do{//傳入要解碼的packetret = avcodec_send_packet(dec_ctx, pkt);//AVERROR(EAGAIN) 傳入失敗,表示先要receive frame再重新send packetif(ret == AVERROR(EAGAIN)){send_ret = 0;fprintf(stderr, "avcodec_send_packet = AVERROR(EAGAIN)\n");}else if(ret < 0){char err[128] = {0};av_strerror(ret, err, 128);fprintf(stderr, "avcodec_send_packet = ret < 0 : %s\n", err);return;}while (ret >= 0){//調用avcodec_receive_frame會在內部首先調用av_frame_unref來釋放frame本來的數據//就是這次調用會將上次調用返回的frame數據釋放ret = avcodec_receive_frame(dec_ctx, frame);if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)return;else if(ret < 0){fprintf(stderr, "avcodec_receive_frame = ret < 0\n");exit(1);}//獲取采樣點占用的字節sample_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);if(sample_size < 0){fprintf(stderr, "av_get_bytes_per_sample = sample_size < 0\n");exit(1);}//在第一幀的時候輸出一下音頻信息static int print_info = 1;if(print_info){print_info = 0;printf("ar-samplerate: %uHz\n", frame->sample_rate);printf("ac-channel: %u\n", frame->channels);printf("f-format: %u\n", frame->format);}//平面方式://LLLLRRRRLLLLRRRRLLLLRRRR (LLLLRRRR這樣為一個音頻幀)//交錯方式://LRLRLRLRLRLRLRLRLR (LR這樣為一個音頻樣本)//按交錯方式寫入(nb_samples這一幀多少樣本)for(i = 0; i < frame->nb_samples; i++){ //dec_ctx->channels 多少個通道的數據for(int channle = 0; channle < dec_ctx->channels; channle++){//frame->data[0] = L 通道//frame->data[1] = R 通道fwrite(frame->data[channle] + sample_size * i, 1, sample_size, outfile);}}}}while(!send_ret);}#define AAC 0
int main()
{const char* outfilename = "out.pcm";#if AACconst char* filename = "test.aac";
#elseconst char* filename = "test.mp3";
#endifconst AVCodec *codec;AVCodecContext *codec_ctx= NULL;AVCodecParserContext *parser = NULL;int len = 0;int ret = 0;FILE *infile = NULL;FILE *outfile = NULL;uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];uint8_t *data = NULL;size_t data_size = 0;AVPacket *pkt = NULL;AVFrame *de_frame = NULL;//申請AVPacket本身的內存pkt = av_packet_alloc();//要使用的解碼器IDenum AVCodecID audio_codec_id = AV_CODEC_ID_AAC;if(strstr(filename, "aac") != NULL){audio_codec_id = AV_CODEC_ID_AAC;}else if(strstr(filename, ".mp3") != NULL){audio_codec_id = AV_CODEC_ID_MP3;}else{printf("audio_codec_id = AV_CODEC_ID_NONE\n");return 0;}//查找相應的解碼器codec = avcodec_find_decoder(audio_codec_id);if(!codec){printf("Codec not find!\n");return 0;}//根據解碼器ID獲取裸流的解析器parser = av_parser_init(codec->id);if(!parser){printf("Parser not find!\n");return 0;}//分配codec使用的上下文codec_ctx = avcodec_alloc_context3(codec);if(!codec_ctx){printf("avcodec_alloc_context3 failed!\n");return 0;}//將解碼器和解碼使用的上下文關聯if(avcodec_open2(codec_ctx, codec, NULL) < 0){printf("avcodec_open2 failed!\n");return 0;}//打開輸入文件infile = fopen(filename, "rb");if(!infile){printf("infile fopen failed!\n");return 0;}//輸出文件outfile = fopen(outfilename, "wb");if(!outfile){printf("outfilie fopen failed!\n");return 0;}int file_end = 0;//讀取文件開始解碼data = inbuf;data_size = fread(inbuf, 1, AUDIO_INBUF_SIZE, infile);de_frame = av_frame_alloc();while (data_size > 0){//獲取傳入到avcodec_send_packet一個packet的數據量//(一幀的量可能也會多幀的量,這里測試是一幀的量)ret = av_parser_parse2(parser, codec_ctx, &pkt->data, &pkt->size,data, data_size,AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);if(ret < 0){printf("av_parser_parser2 Error!\n");return 0;}//使用了多少數據做一個偏移data += ret;data_size -= ret;if(pkt->size)decode(codec_ctx, pkt, de_frame, outfile);//如果當前緩沖區中數據少于AUDIO_REFILL_THRESH就再讀//避免多次讀文件if((data_size < AUDIO_REFILL_THRESH) && !file_end ){//剩余數據移動緩沖區前memmove(inbuf, data, data_size);data = inbuf;//跨過已有數據存儲len = fread(data + data_size, 1, AUDIO_INBUF_SIZE - data_size, infile);if(len > 0)data_size += len;else if(len == 0){file_end = 1;printf("file end!\n");}}}//沖刷解碼器pkt->data = NULL;pkt->size = 0;decode(codec_ctx, pkt, de_frame, outfile);fclose(infile);fclose(outfile);avcodec_free_context(&codec_ctx);av_parser_close(parser);av_frame_free(&de_frame);av_packet_free(&pkt);printf("audio decoder end!\n");return 0;
}