1. 程式人生 > 其它 >ffmpeg 解碼音訊(aac、mp3)輸出pcm檔案

ffmpeg 解碼音訊(aac、mp3)輸出pcm檔案

技術標籤:音視訊流媒體ffmpeg音訊編碼解碼

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 4096 static void decode(AVCodecContext* dec_ctx, AVPacket* pkt, AVFrame* frame, FILE *outfile) { int i, ch; int ret, sample_size; int send_ret = 1; do { //傳入要解碼的packet ret = avcodec_send_packet(dec_ctx, pkt); //AVERROR(EAGAIN) 傳入失敗,表示先要receive frame再重新send packet
if(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 AAC const char* filename = "believe.aac"; #else const char* filename = "believe.mp3"; #endif const 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(); //要使用的解碼器ID enum 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; }