4.音頻解碼流程

第一步:組冊(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);
}


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容