第一步:組冊(cè)組件
av_register_all();
第二步:打開(kāi)封裝格式->打開(kāi)文件
avformat_open_input();
第三步:查找音頻流->拿到音頻信息
avformat_find_stream_info();
第四步:查找音頻解碼器
avcodec_find_decoder();
第五步:打開(kāi)音頻解碼器
avcodec_open2();
第六步:讀取音頻壓縮數(shù)據(jù)->循環(huán)讀取
第七步:音頻解碼
第八步:釋放內(nèi)存資源,關(guān)閉音頻解碼器
代碼實(shí)現(xiàn):
音頻解碼:
//導(dǎo)入音視頻頭文件庫(kù)
//核心庫(kù)
#include "libavcodec/avcodec.h"
//封裝格式處理庫(kù)
#include "libavformat/avformat.h"
//工具庫(kù)
#include "libavutil/imgutils.h"
//視頻像素?cái)?shù)據(jù)格式庫(kù)
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
+(void)ffmpegAudioDecode:(NSString*)inFilePath outFilePath:(NSString*)outFilePath{
//第一步:組冊(cè)組件->解碼器、編碼器等等…
//視頻解碼器、視頻編碼器、音頻解碼器、音頻編碼器等等…
av_register_all();
//第二步:打開(kāi)封裝格式文件(解封裝)
//參數(shù)一:封裝格式上下文
AVFormatContext *avformat_context = avformat_alloc_context();
//參數(shù)二:視頻路徑
const char *cinFilePath = [inFilePath UTF8String];
//參數(shù)三:指定輸入的格式
//參數(shù)四:設(shè)置默認(rèn)參數(shù)
if (avformat_open_input(&avformat_context, cinFilePath, NULL, NULL) != 0) {
NSLog(@"打開(kāi)文件失敗");
return;
}
//第三步:查找音頻流(視頻流、字母流等…)信息
if (avformat_find_stream_info(avformat_context, NULL) < 0) {
NSLog(@"查找失敗");
return;
}
//第四步:查找音頻解碼器
//1、查找音頻流索引位置
int av_audio_stream_index = -1;
for (int i = 0; i < avformat_context->nb_streams; ++i) {
//判斷流類型:視頻流、音頻流、字母流等等...
if (avformat_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){
av_audio_stream_index = i;
break;
}
}
//2、根據(jù)視頻流索引,獲取解碼器上下文
AVCodecContext *avcodec_context = avformat_context->streams[av_audio_stream_index]->codec;
//3、根據(jù)音頻解碼器上下文,獲得解碼器ID,然后查找音頻解碼器
AVCodec *avcodec = avcodec_find_decoder(avcodec_context->codec_id);
//第五步:打開(kāi)音頻解碼器
if (avcodec_open2(avcodec_context, avcodec, NULL) != 0) {
NSLog(@"打開(kāi)解碼器失敗");
return;
}
//打印信息
NSLog(@"解碼器名稱:%s", avcodec->name);
//第六步:循環(huán)讀取每一幀音頻壓縮數(shù)據(jù)
//參數(shù)一:封裝格式上下文呢
//參數(shù)二:音頻壓縮數(shù)據(jù)(一幀)
//返回值:>=0表示讀取成功,<0表示失敗或者解碼完成(讀取完畢)
//準(zhǔn)備一幀音頻壓縮數(shù)據(jù)
AVPacket *avPacket = (AVPacket *) av_malloc(sizeof(AVPacket));
//準(zhǔn)備一幀音頻采樣數(shù)據(jù)
AVFrame *avFrame = av_frame_alloc();
//3、類型轉(zhuǎn)換->統(tǒng)一轉(zhuǎn)換為pcm格式->swr_convert()
//初始化音頻采樣數(shù)據(jù)上下文
//3.1:開(kāi)辟一塊內(nèi)存空間
SwrContext *swrContext = swr_alloc();
//3.2:設(shè)置默認(rèn)配置
//參數(shù)一:音頻采樣數(shù)據(jù)上下文
//參數(shù)二:輸出聲道布局(立體聲、環(huán)繞聲...)
//參數(shù)三:輸出采樣精度(編碼)
//參數(shù)四:輸出采樣率
//參數(shù)五:輸入聲道布局
int64_t in_ch_layout = av_get_default_channel_layout(avcodec_context->channels);
//參數(shù)六:輸入采樣精度
//參數(shù)七:輸入采樣率
//參數(shù)八:日志統(tǒng)計(jì)開(kāi)始位置
//參數(shù)九:日志上下文
swr_alloc_set_opts(swrContext,
AV_CH_LAYOUT_STEREO,
AV_SAMPLE_FMT_S16,
avcodec_context->sample_rate,
in_ch_layout,
avcodec_context->sample_fmt,
avcodec_context->sample_rate,
0,
NULL);
//3.3:初始化上下文
swr_init(swrContext);
//3.4:統(tǒng)一輸出音頻采樣數(shù)據(jù)格式->pcm
int MAX_AUDIO_SIZE = 44100 * 2;
uint8_t *out_buffer = (uint8_t *) av_malloc(MAX_AUDIO_SIZE);
//4、獲取緩沖區(qū)實(shí)際大小
int out_nb_channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
//5.1 打開(kāi)文件
const char *outfile = [outFilePath UTF8String];
FILE* file_pcm = fopen(outfile, "wb+");
if (file_pcm == NULL){
NSLog(@"輸出文件打開(kāi)失敗");
return;
}
int current_index = 0;
while (av_read_frame(avformat_context, avPacket) >= 0) {
//判定這一幀數(shù)據(jù)是否音頻流(視頻流、音頻流、字母流等等...)
//1、音頻解碼->判定流類型
if (avPacket->stream_index == av_audio_stream_index) {
//音頻流->處理
//2、音頻解碼->開(kāi)始解碼
//2.1 發(fā)送數(shù)據(jù)包->一幀音頻壓縮數(shù)據(jù)->acc格式、mp3格式
avcodec_send_packet(avcodec_context, avPacket);
//2.2 解碼數(shù)據(jù)包->解碼->一幀音頻采樣數(shù)據(jù)->pcm格式
int ret = avcodec_receive_frame(avcodec_context, avFrame);
if (ret == 0) {
//表示解碼成功,否則失敗
//3、類型轉(zhuǎn)換->統(tǒng)一轉(zhuǎn)換為pcm格式->swr_convert()
//為什么呢?因?yàn)榻獯a之后的音頻采樣數(shù)據(jù)格式->有很多種類型->保證格式一致
//參數(shù)一:音頻采樣數(shù)據(jù)上下文
//參數(shù)二:輸出音頻采樣數(shù)據(jù)
//參數(shù)三:輸出音頻采樣數(shù)據(jù)大小
//參數(shù)四:輸入音頻采樣數(shù)據(jù)
//參數(shù)五:輸入音頻采樣數(shù)據(jù)大小
swr_convert(swrContext,
&out_buffer,
MAX_AUDIO_SIZE,
(const uint8_t **) avFrame->data,
avFrame->nb_samples);
//4、獲取緩沖區(qū)實(shí)際大小
//參數(shù)一:行大小
//參數(shù)二:輸出聲道數(shù)量(單聲道、雙聲道)
//參數(shù)三:輸入大小
//參數(shù)四:輸出音頻采樣數(shù)據(jù)格式
//參數(shù)五:字節(jié)對(duì)齊方式->默認(rèn)是1
int buffer_size = av_samples_get_buffer_size(NULL,
out_nb_channels,
avFrame->nb_samples,
avcodec_context->sample_fmt,
1);
//5、寫(xiě)入文件
//5.1 打開(kāi)文件
//5.2 寫(xiě)入文件
fwrite(out_buffer, 1, buffer_size, file_pcm);
current_index++;
NSLog(@"當(dāng)前解碼到了第%d幀", current_index);
}
}
}
//第八步:釋放資源(內(nèi)存)->關(guān)閉解碼器
av_packet_free(&avPacket);
fclose(file_pcm);
av_frame_free(&avFrame);
free(out_buffer);
avcodec_close(avcodec_context);
avformat_free_context(avformat_context);
}