ffmpeg(四)——音頻解碼

音頻解碼和視頻解碼有很多是相似的,我們直接貼代碼

Java_com_example_ffmpegapplication_MyAudioPlayer_playaudio(JNIEnv *env, jobject instance,
                                                           jstring src_, jstring des_) {
    const char *src = env->GetStringUTFChars(src_, 0);
    const char *des = env->GetStringUTFChars(des_, 0);

    avformat_network_init();
    //總上下文
    AVFormatContext *pContext = avformat_alloc_context();

    //字典
    AVDictionary *pDictionary = NULL;

    av_dict_set(&pDictionary, "timeout", "3000000", 0);

    // 打開(kāi)源文件輸入流
    if(avformat_open_input(&pContext, src, NULL, &pDictionary) != 0) {
        avformat_free_context(pContext);
        return;
    }
    // 獲取視頻流信息齿穗,通知ffmpeg把流解析出來(lái)
    if(avformat_find_stream_info(pContext,NULL) < 0){
        LOGI("%s","無(wú)法獲取輸入文件信息");
        return;
    }

    //音頻流索引
    int audio_stream_index = -1;
    //nb_streams:視頻里面流的數(shù)量讳侨,比如流0是視頻,流1是音頻姜胖,流2是字幕
    for(int i = 0; i < pContext->nb_streams; i++) {
        //codecpar:解碼器參數(shù)钦勘,舊版本的是codec,  codec_type:解碼器類型棋傍。
        if(pContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){
            //如果是音頻流救拉,保存索引
            audio_stream_index = i;
            break;
        }
    }
    //音頻流的解碼參數(shù)
    AVCodecParameters *pParameters = pContext->streams[audio_stream_index]->codecpar;

    //通過(guò)解碼器id找到解碼器
    AVCodec *pCodec = avcodec_find_decoder(pParameters->codec_id);

    //創(chuàng)建解碼器上下文
    AVCodecContext *pCodecContext = avcodec_alloc_context3(pCodec);

    //將解碼器參數(shù)copy到解碼器上下文
    avcodec_parameters_to_context(pCodecContext, pParameters);

    //打開(kāi)解碼器
    avcodec_open2(pCodecContext, pCodec, &pDictionary);

    //音頻轉(zhuǎn)換上下文
    SwrContext *pSwrContext = swr_alloc();

    //輸入?yún)?shù)
    //輸入采樣格式
    AVSampleFormat in_sample =  pCodecContext->sample_fmt;
    //輸入采樣率
    int in_sample_rate = pCodecContext->sample_rate;
    //輸入聲道布局
    uint64_t in_ch_layout = pCodecContext->channel_layout;

    //輸出參數(shù) 固定
    //輸出采樣格式
    AVSampleFormat out_sample = AV_SAMPLE_FMT_S16;
    //輸出采樣率
    int out_sample_rate = 44100;
    //輸出聲道布局 AV_CH_LAYOUT_STEREO 雙通道
    uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;
    //設(shè)置轉(zhuǎn)換參數(shù)
    swr_alloc_set_opts(pSwrContext,out_ch_layout,out_sample,out_sample_rate
            ,in_ch_layout,in_sample,in_sample_rate,0,NULL);
    //初始化轉(zhuǎn)換器
    swr_init(pSwrContext);

    //為輸出文件申請(qǐng)內(nèi)存
    uint8_t *out_buffer = (uint8_t *)(av_malloc(2 * 44100));
    FILE *fp_pcm = fopen(des, "wb");

    //數(shù)據(jù)存儲(chǔ)在一個(gè)AVPacket中 舊版本需要用戶自己去malloc。新版本的可以理解為:待解碼隊(duì)列
    AVPacket *pPacket = av_packet_alloc();

    //解碼后的yuv數(shù)據(jù)容器
    AVFrame *pFrame = av_frame_alloc();

    while (av_read_frame(pContext, pPacket) >= 0) {
        //AVCodecContext *avctx, const AVPacket *avpkt  從待解碼隊(duì)列中取出視頻流數(shù)據(jù)發(fā)送給解碼器
        avcodec_send_packet(pCodecContext, pPacket);
        //AVCodecContext *avctx, AVFrame *frame 從解碼器取出解碼后到數(shù)據(jù)(yuv)到frame
        int ret = avcodec_receive_frame(pCodecContext, pFrame);
        if (ret == AVERROR(EAGAIN)) {
            continue;
        } else if (ret < 0) {
            LOGI("%s","解碼完成");
            break;
        }

        //frame  ---->統(tǒng)一的格式
        swr_convert(pSwrContext, &out_buffer, 2 * 44100,
                    (const uint8_t **)pFrame->data, pFrame->nb_samples);
        int out_channerl_nb= av_get_channel_layout_nb_channels(out_ch_layout);
        //緩沖區(qū)的 大小
        int out_buffer_size=  av_samples_get_buffer_size(NULL, out_channerl_nb, pFrame->nb_samples, out_sample, 1);
        fwrite(out_buffer,1, out_buffer_size, fp_pcm);
    }

    fclose(fp_pcm);
    av_free(out_buffer);
    swr_free(&pSwrContext);
    //釋放容器
    av_frame_free(&pFrame);
    //釋放AVPacket
    av_packet_free(&pPacket);

    //釋放解碼器上下文
    avcodec_free_context(&pCodecContext);
    //關(guān)閉文件
    avformat_close_input(&pContext);
    //釋放總上下文
    avformat_free_context(pContext);

    env->ReleaseStringUTFChars(src_, src);
    env->ReleaseStringUTFChars(des_, des);
}

我們來(lái)分析一下代碼瘫拣,相同的就不在看了

初始化網(wǎng)路
打開(kāi)文件流
初始化解碼器
轉(zhuǎn)換器的初始化和使用
  • struct SwrContext *swr_alloc(void);
    音頻轉(zhuǎn)換上下文
    使用swr_free釋放
  • struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,
    int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
    int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate,
    int log_offset, void *log_ctx);
    設(shè)置轉(zhuǎn)換參數(shù)
    音頻要播放出來(lái)亿絮,就需要把原始數(shù)據(jù)轉(zhuǎn)換成固定格式的數(shù)據(jù)。比如代碼中,不管原始數(shù)據(jù)的參數(shù)如何壹无。都轉(zhuǎn)成固定的輸出采樣格式葱绒、輸出采樣率、輸出聲道布局
  • int swr_init(struct SwrContext *s);
    初始化轉(zhuǎn)換器斗锭,必須調(diào)用
  • int swr_convert(struct SwrContext *s, uint8_t **out, int out_count,
    const uint8_t **in , int in_count);
    轉(zhuǎn)換文件到固定格式
輸出文件
  • uint8_t *out_buffer = (uint8_t *)(av_malloc(2 * 44100));
    我們看下這個(gè)代碼里面地淀,申請(qǐng)的內(nèi)存大小是244100,這個(gè)大小一般就是:通道數(shù)采樣率
  • int av_get_channel_layout_nb_channels(uint64_t channel_layout);
    獲取通道數(shù)
  • int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples,
    enum AVSampleFormat sample_fmt, int align);
    獲取這一幀聲音的實(shí)際大小
  • fwrite(out_buffer,1, out_buffer_size, fp_pcm);
    這里面第二個(gè)參數(shù)是1岖是,是因?yàn)橐纛l的最小單位就是1個(gè)字節(jié)帮毁。如果是像素,就是4個(gè)字節(jié)

最終在輸出目錄下生成一個(gè)pcm文件

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末豺撑,一起剝皮案震驚了整個(gè)濱河市烈疚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌聪轿,老刑警劉巖爷肝,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異陆错,居然都是意外死亡灯抛,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門音瓷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)对嚼,“玉大人,你說(shuō)我怎么就攤上這事绳慎∽菔” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵杏愤,是天一觀的道長(zhǎng)靡砌。 經(jīng)常有香客問(wèn)我,道長(zhǎng)声邦,這世上最難降的妖魔是什么乏奥? 我笑而不...
    開(kāi)封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任摆舟,我火速辦了婚禮亥曹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘恨诱。我一直安慰自己媳瞪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布照宝。 她就那樣靜靜地躺著蛇受,像睡著了一般。 火紅的嫁衣襯著肌膚如雪厕鹃。 梳的紋絲不亂的頭發(fā)上兢仰,一...
    開(kāi)封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天乍丈,我揣著相機(jī)與錄音,去河邊找鬼把将。 笑死轻专,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的察蹲。 我是一名探鬼主播请垛,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼洽议!你這毒婦竟也來(lái)了宗收?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤亚兄,失蹤者是張志新(化名)和其女友劉穎混稽,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體审胚,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡荚坞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了菲盾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颓影。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖懒鉴,靈堂內(nèi)的尸體忽然破棺而出诡挂,到底是詐尸還是另有隱情,我是刑警寧澤临谱,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布璃俗,位于F島的核電站,受9級(jí)特大地震影響悉默,放射性物質(zhì)發(fā)生泄漏城豁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一抄课、第九天 我趴在偏房一處隱蔽的房頂上張望唱星。 院中可真熱鬧,春花似錦跟磨、人聲如沸间聊。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)哎榴。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間尚蝌,已是汗流浹背迎变。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留飘言,地道東北人氏豌。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像热凹,于是被迫代替她去往敵國(guó)和親泵喘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355