Android直播從入門到精通(3):PCM轉(zhuǎn)AAC

1.什么是AAC待榔?

AAC(Advanced Audio Coding)是一種音頻編碼標(biāo)準(zhǔn)蜻展,最早定義在MPEG-2標(biāo)準(zhǔn)(ISO/IEC 13818-7)中涮帘,后來在MPEG-4(ISO/IEC 14496-3)標(biāo)準(zhǔn)中又加入了SBR技術(shù)和PS技術(shù)(<font color=gray size=2>MPEG的介紹可以看這里:MPEG標(biāo)準(zhǔn)介紹</font>)。AAC標(biāo)準(zhǔn)是作為MP3的繼承者而設(shè)計(jì)出來的赶促,相同的比特率之下愕秫,AAC比MP3有更好的音質(zhì)慨菱。
</br></br></br>
為了適應(yīng)不同的應(yīng)用場(chǎng)景,AAC定義9種Profile

  • MPEG-2 AAC LC 低復(fù)雜度規(guī)格(Low Complexity)--比較簡(jiǎn)單戴甩,沒有增益控制符喝,但提高了編碼效率,在中等碼率的編碼效率以及音質(zhì)方面甜孤,都能找到平衡點(diǎn)
  • MPEG-2 AAC Main 主規(guī)格
  • MPEG-2 AAC SSR 可變采樣率規(guī)格(Scaleable Sample Rate)
  • MPEG-4 AAC LC低復(fù)雜度規(guī)格(Low Complexity)------現(xiàn)在的手機(jī)比較常見的MP4文件中的音頻部份就包括了該規(guī)格音頻文件
  • MPEG-4 AAC Main 主規(guī)格 ------包含了除增益控制之外的全部功能协饲,其音質(zhì)最好
  • MPEG-4 AAC SSR 可變采樣率規(guī)格(Scaleable Sample Rate)
  • MPEG-4 AAC LTP 長(zhǎng)時(shí)期預(yù)測(cè)規(guī)格(Long Term Predicition)
  • MPEG-4 AAC LD 低延遲規(guī)格(Low Delay)
  • MPEG-4 AAC HE 高效率規(guī)格(High Efficiency)-----這種規(guī)格適合用于低碼率編碼畏腕,有Nero ACC 編碼器支持

目前使用最多的是LCHE。其中LC-AAC用于中高碼率(>=80Kbps)茉稠,HE-AAC(LC + SBR技術(shù))主要用于中低碼(<=80Kbps)描馅,而新近推出的HE-AACv2(LC+SBR+PS)主要用于低碼率(<=48Kbps),事實(shí)上大部分編碼器設(shè)成<=48Kbps自動(dòng)啟用PS技術(shù),而>48Kbps就不加PS而线。流行的Nero AAC編碼程序只支持LC铭污,HE,HEv2這三種規(guī)格膀篮,編碼后的AAC音頻嘹狞,規(guī)格顯示都是LC。

image

<center><font color=gray size=2>圖中AAC即為AAC-LC誓竿,aacPlus v1磅网,v2分別代表Hev1和HEv2</font></center>

HE:“High Efficiency”(高效性)。HE-AAC v1(又稱AACPlusV1烤黍,SBR)知市,用容器的方法實(shí)現(xiàn)了AAC(LC)+SBR技術(shù)。SBR其實(shí)代表的是Spectral Band Replication(頻段復(fù)制)速蕊。簡(jiǎn)要敘述一下嫂丙,音樂的主要頻譜集中在低頻段,高頻段幅度很小规哲,但很重要跟啤,決定了音質(zhì)。如果對(duì)整個(gè)頻段編碼唉锌,若是為了保護(hù)高頻就會(huì)造成低頻段編碼過細(xì)以致文件巨大隅肥;若是保存了低頻的主要成分而失去高頻成分就會(huì)喪失音質(zhì)。SBR把頻譜切割開來袄简,低頻單獨(dú)編碼保存主要成分腥放,高頻單獨(dú)放大編碼保存音質(zhì),“統(tǒng)籌兼顧”了绿语,在減少文件大小的情況下還保存了音質(zhì)秃症,完美的化解這一矛盾。

HEv2:用容器的方法包含了HE-AAC v1和PS技術(shù)吕粹。PS指“parametric stereo”(參數(shù)立體聲)种柑。原來的立體聲文件文件大小是一個(gè)聲道的兩倍。但是兩個(gè)聲道的聲音存在某種相似性匹耕,根據(jù)香農(nóng)信息熵編碼定理聚请,相關(guān)性應(yīng)該被去掉才能減小文件大小。所以PS技術(shù)存儲(chǔ)了一個(gè)聲道的全部信息稳其,然后驶赏,花很少的字節(jié)用參數(shù)描述另一個(gè)聲道和它不同的地方炸卑。

2.AAC文件格式

AAC的音頻文件格式有ADIF和ADTS:

  • ADIF:Audio Data Interchange Format 音頻數(shù)據(jù)交換格式。這種格式的特征是可以確定的找到這個(gè)音頻數(shù)據(jù)的開始煤傍,不需進(jìn)行在音頻數(shù)據(jù)流中間開始的解碼矾兜,即它的解碼必須在明確定義的開始處進(jìn)行。故這種格式常用在磁盤文件中患久。

  • ADTS:Audio Data Transport Stream 音頻數(shù)據(jù)傳輸流椅寺。這種格式的特征是它是一個(gè)有同步字的比特流,解碼可以在這個(gè)流中任何位置開始蒋失。它的特征類似于mp3數(shù)據(jù)流格式返帕。

簡(jiǎn)單來說,ADIF只有一個(gè)統(tǒng)一的頭篙挽,所以必須得到所有的數(shù)據(jù)后解碼荆萤,ADTS每一幀都有頭信息,可以從任意幀開始解碼铣卡,因此網(wǎng)絡(luò)上的aac基本都是ADTS格式链韭。

關(guān)于ADTS格式的定義見ISO/IEC 13818文檔的Part 7,6.2節(jié):

2.1 adts_sequence

image

可以看出adts的aac流是一個(gè)個(gè)adts_frame組成序列



2.2 adts_frame
image

每個(gè)adts幀包含以下數(shù)據(jù)結(jié)構(gòu)adts_fixed_header煮落,adts_variable_header,raw_data_block序列



2.3 adts_fixed_header

image

字段定義在ISO/IEC 13818的Part 7敞峭,8.1.1.1節(jié)中,其中部分字段同MP3定義一樣蝉仇,見ISO/IEC 11172的Part 3旋讹,2.4.2.3節(jié):


syncword:12bit,所有位都位1轿衔,即'1111 1111 1111'
ID:1bit沉迹,始終為1。1 - MPEG audio害驹,0 - 保留
Layer:2bit鞭呕,始終為00。決定用那種layer協(xié)議.

  • "11" Layer I
  • "10" Layer II
  • "01" Layer III
  • "00" reserved

protection_bit:1bit,表示是否有crc校驗(yàn)宛官。1 - 無 0 - 有
profile:2bit,決定用哪種profile.

image

sampling_frequency_index:4bit葫松,采樣頻率index。
image

private_bit:1bit,bit for private use. This bit will not be used in the future by ISO
channel_configuration:3bit,聲道配置. 如果等于0摘刑,則聲道配置在第一個(gè)raw_data_block中通過調(diào)用program_config_element設(shè)置进宝;如果大于0刻坊,則參照下圖
image

original_copy:1bit, 0 - 無版權(quán)保護(hù) 1 - 有版權(quán)保護(hù)
home:1bit,表明當(dāng)前數(shù)據(jù)是拷貝流還是原始流枷恕。0 - 拷貝流 1 - 原始流



2.4 adts_variable_header

image

copyright_identification_bit:1bit,版權(quán)信息,暫不深究
copyright_identification_start:1bit谭胚,版權(quán)相關(guān)徐块,暫不深究
frame_length:13bit未玻,一個(gè)ADTS幀的字節(jié)數(shù),包含headers和error_check的長(zhǎng)度
adts_buffer_fullness:11bit, 如果值為7FF則表明當(dāng)前碼流的碼率是可變的
number_of_raw_data_blocks_in_frame:2bit,表示ADTS幀中有number_of_raw_data_blocks_in_frame + 1個(gè)AAC原始幀



2.5 raw_data_block

image

id_syn_ele:3bit胡控,元素類型id扳剿,定義如下圖:

image

SCE: Single Channel Element,單通道元素昼激。單通道元素基本上只由一個(gè)ICS組成庇绽。一個(gè)原始數(shù)據(jù)塊最可能由16個(gè)SCE組成。
CPE: Channel Pair Element橙困,雙通道元素瞧掺,由兩個(gè)可能共享邊信息的ICS和一些聯(lián)合立體聲編碼信息組成。
CCE: Coupling Channel Element凡傅,藕合通道元素辟狈。代表一個(gè)塊的多通道聯(lián)合立體聲信息或者多語種程序的對(duì)話信息。
LFE: Low Frequency Element夏跷,低頻元素哼转。包含了一個(gè)加強(qiáng)低采樣頻率的通道。
DSE: Data Stream Element槽华,數(shù)據(jù)流元素壹蔓,包含了一些并不屬于音頻的附加信息。
PCE: Program Config Element猫态,程序配置元素庶溶。包含了聲道的配置信息。它可能出現(xiàn)在ADIF 頭部信息中懂鸵。
FIL: Fill Element偏螺,填充元素。包含了一些擴(kuò)展信息匆光。如SBR套像,動(dòng)態(tài)范圍控制信息等。

后面就是元素內(nèi)容的具體分析了终息,由于涉及到音頻編碼的知識(shí)夺巩,比較復(fù)雜,這里不做討論周崭。

2.6 aac格式解析示例
用二進(jìn)制查看工具打開一個(gè)aac文件柳譬,如下圖:

image

按照上一節(jié)的aac格式來解析

字段名 占用bit數(shù) 含義
adts_fixed_header - 1111 1111 1111 1001 0101 0000 1000(0xFFF9508)
syncword 12 0xFFF 頭標(biāo)識(shí)
ID 1 1 MPEG audio
Layer 2 00 保留字段
protection_bit 1 1 無crc校驗(yàn)
profile 2 01 采用LC profile
sampling_frequency_index 4 0100 采樣率44100Hz
private_bit 1 0 ISO保留字段
channel_configuration 3 010 雙聲道
original_copy 1 0 無版權(quán)
home 1 0 原始數(shù)據(jù)
adts_variable_header - 0000 0010 1110 0111 1111 1111 1100(0x02E7FFC)
copyright_identification_bit 1 0 版權(quán)信息
copyright_identification_start 1 0 版權(quán)信息
frame_length 13 00 0010 1110 011 幀長(zhǎng)度為371字節(jié)
adts_buffer_fullness 11 1 1111 1111 11 碼率可變
number_of_raw_data_blocks_in_frame 2 00 有一個(gè)原始幀數(shù)據(jù)
raw_data_block - 001
id_syn_ele 3 001 雙通道元素
channel_pair_element ... ... ...
... ... ... ...

第一幀長(zhǎng)度為371字節(jié),然后又開始下一幀(頭三個(gè)字節(jié)0xFFF)续镇,剛好對(duì)應(yīng)圖中劃紅線的地方美澳。

3.AAC編碼

AAC編碼流程在ISO/IEC 13818-7中制定:


image

其編碼流程概述如下: 當(dāng)音頻信號(hào)送至編碼端時(shí),會(huì)分別送至聽覺心里模型(Psychoacoustic Model)以求得編碼所需之相關(guān)參數(shù)及增益控制(gain control)模塊中,將信號(hào)做某個(gè)程度的衰減制跟,以降低其峰值大小舅桩,如此可減少Pre-echo 的發(fā)生。之后雨膨,再以MDCT 將時(shí)域信號(hào)轉(zhuǎn)換至頻率域擂涛,而送入至TNS(Temporal Noise Shaping Module)模塊中,來判斷是否需要啟動(dòng)TNS聊记,此模塊系利用開回路預(yù)測(cè)(open-loop prediction) 來修飾其量化噪聲撒妈,如此可將其量化噪聲的分布,修飾到原始信號(hào)能量所能含蓋的范圍之下排监,進(jìn)一步的減少Pre-echo 的發(fā)生踩身,若TNS 被啟動(dòng),則傳出其預(yù)測(cè)差值社露;反之挟阻,則傳出原始頻譜值。AAC 為了提升其壓縮效率峭弟,則使用了Joint Stereo Coding與預(yù)測(cè)(Prediction)模塊來進(jìn)一步消除信號(hào)間的冗余成份附鸽。在Joint Stereo Coding中又可分為Intensity Stereo Coding 與M/S Stereo Coding。在Intensity Stereo Coding模塊中瞒瘸,是利用信號(hào)在高頻時(shí)坷备,人耳只對(duì)能量較敏感,對(duì)于其相位不敏感之特性情臭,將其左右聲道之頻譜系數(shù)合并省撑,以節(jié)省使用之位;在M/S Stereo Coding 模塊中俯在,利用左右聲道之和與差竟秫,做進(jìn)一步地壓縮,若其差值能量很小跷乐,如此便可以用較少之位編碼此一聲道肥败,將剩余之位應(yīng)用于另一聲道上的編碼,如此來提升其壓縮率愕提。而預(yù)測(cè)模塊的主要架構(gòu)是使用Backward Adaptive Predictors馒稍,利用前兩個(gè)音頻幀來預(yù)測(cè)現(xiàn)在的音頻幀,若決定啟動(dòng)此模塊浅侨,則傳出其預(yù)測(cè)差值纽谒,如此一來可以減少其數(shù)據(jù)量,達(dá)數(shù)據(jù)壓縮之目的如输。經(jīng)過上述處理頻譜信號(hào)上的壓縮tools程序后鼓黔,則將其數(shù)據(jù)予以量化與編碼央勒,為了達(dá)到量化編碼的最佳化,AAC 使用了雙巢狀式循環(huán)(two nested loop)的量化編碼結(jié)構(gòu)请祖,以得最佳的壓縮質(zhì)量,最后則將其位串送至解碼端脖祈,而完成整個(gè)編碼程序肆捕。

AAC編碼的原理比較復(fù)雜,涉及信息編碼以及人耳的生理知識(shí)盖高,按照功能大致可以劃分為熵編碼慎陵,量化編碼,變換編碼喻奥,預(yù)測(cè)編碼席纽,音頻建模5大類,這里就不展開了撞蚕。

4.利用ffmpeg和fdk-aac將pcm編碼成aac格式

ffmpeg作為音視頻開發(fā)必不可少的工具润梯,這里就不做介紹了。至于如何在編譯環(huán)境搭建可以參考:
fdk-aac是一款開源的aac編解碼實(shí)現(xiàn)庫(kù)甥厦,源碼地址:https://github.com/mstorsjo/fdk-aac

下面我們實(shí)現(xiàn)一個(gè)在Android上將pcm文件轉(zhuǎn)成aac文件的功能纺铭。新建一個(gè)Android工程,導(dǎo)入ffmpeg和fdk-aac的so庫(kù)刀疙,工程配置這里就不講了舶赔,文章末尾有源碼地址。
JNI java接口

package me.huaisu.audio.encode;

public class AacEncoder {

    static {
        System.loadLibrary("fdk-aac");
        System.loadLibrary("avcodec");
        System.loadLibrary("avdevice");
        System.loadLibrary("avfilter");
        System.loadLibrary("avformat");
        System.loadLibrary("avutil");
        System.loadLibrary("swresample");
        System.loadLibrary("swscale");
        System.loadLibrary("aac_encoder");
    }

    public native int encodePcmFile(String pcmFile, String aacFile);
}

JNI c++實(shí)現(xiàn)

#include <jni.h>
#include <string>
#include "AACEncoder.h"

extern "C" JNIEXPORT jint JNICALL
Java_me_huaisu_audio_encode_AacEncoder_encodePcmFile(
        JNIEnv* env,
        jobject thiz,
        jstring pcmFile,
        jstring aacFile) {
    AACEncoder* encoder = new AACEncoder();
    const char* pcm_file = env->GetStringUTFChars(pcmFile, NULL);
    if (pcm_file == NULL) {
        return NULL;
    }
    const char* aac_file = env->GetStringUTFChars(aacFile, NULL);
    if (aac_file == NULL) {
        return NULL;
    }
    env->ReleaseStringUTFChars(pcmFile, pcm_file);
    env->ReleaseStringUTFChars(aacFile, aac_file);
    return encoder->encode(pcm_file, aac_file);
}

下面是具體的aac編碼實(shí)現(xiàn)

//
// Created by Administrator on 2020/2/23.
//

#ifndef ANDROID_LIVE_AACENCODER_H
#define ANDROID_LIVE_AACENCODER_H

#ifdef __cplusplus
extern "C" {
#endif

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>

#ifdef __cplusplus
}
#endif

#include "AndroidLog.h"

class AACEncoder {
private:
    uint8_t** src_data = NULL;//一幀的數(shù)據(jù)谦秧,是個(gè)二位數(shù)組
    int src_linesize;
    int src_bufsize;//一幀數(shù)據(jù)的長(zhǎng)度

    AVFormatContext* pFormatContext;
    AVStream* audioStream;
    AVCodecParameters* param;
    AVCodecContext* pCodecContext;
    AVCodec* pCodec;
    AVFrame* pFrame;
    int frame_cnt = 0;
    AVPacket *pkt;
    int ret;

    int initCodec();
    int initAudioStream(const char* aac_file);
    int initAudioFrame();
public:
    AACEncoder();
    ~AACEncoder();
    int encode(const char *pcm_file, const char *aac_file);
};


#endif //ANDROID_LIVE_AACENCODER_H

#include "AACEncoder.h"


AACEncoder::AACEncoder() {

}

AACEncoder::~AACEncoder() {

}

static void android_log_callback(void *ptr, int level, const char *fmt, va_list vl)
{
    switch (level) {
        case AV_LOG_VERBOSE:
            LOGV(fmt, vl);
            break;
        case AV_LOG_DEBUG:
            LOGD(fmt, vl);
            break;
        case AV_LOG_INFO:
            LOGI(fmt, vl);
            break;
        case AV_LOG_WARNING:
            LOGW(fmt, vl);
            break;
        case AV_LOG_ERROR:
            LOGE(fmt, vl);
            break;
    }
}

int AACEncoder::initCodec() {
    pCodec = avcodec_find_encoder_by_name("libfdk_aac");
    if (!pCodec) {
        LOGE("Codec not found\n");
        return -1;
    }
    pCodecContext = avcodec_alloc_context3(pCodec);
    if (!pCodecContext) {
        LOGE("Codec context alloc fail\n");
        return -1;
    }

    pCodecContext->codec_id = AV_CODEC_ID_AAC;
    pCodecContext->codec_type = AVMEDIA_TYPE_AUDIO;
    pCodecContext->sample_fmt = AV_SAMPLE_FMT_S16;
    pCodecContext->sample_rate = 44100;
    pCodecContext->channel_layout = AV_CH_LAYOUT_STEREO;
    pCodecContext->channels = av_get_channel_layout_nb_channels(pCodecContext->channel_layout);
    pCodecContext->bit_rate = 96000;

    if (avcodec_open2(pCodecContext, pCodec, NULL) < 0) {
        LOGE("Can't open codec\n");
        return -1;
    }
    return 0;
}

int AACEncoder::initAudioStream(const char* aac_file) {
    avformat_alloc_output_context2(&pFormatContext, NULL, NULL, aac_file);
    if (avio_open(&pFormatContext->pb, aac_file, AVIO_FLAG_READ_WRITE) < 0) {
        LOGE("Could't open output file\n");
        return -1;
    }
    audioStream = avformat_new_stream(pFormatContext, pCodec);
    if (audioStream == NULL) {
        LOGE("Could't create stream\n");
        return -1;
    }
    param = avcodec_parameters_alloc();
    ret = avcodec_parameters_from_context(param, pCodecContext);
    if (ret < 0) {
        LOGE("create parameters fail\n");
        return -1;
    }
    audioStream->codecpar = param;
    return 0;
}

 /**
  * ffmpeg一幀有1024個(gè)采樣點(diǎn)竟纳,即pCodecContext->frame_size=1024
  *
  * 雙聲道,AV_SAMPLE_FMT_S16采樣格式的數(shù)據(jù)方式存儲(chǔ)如下:
  * LRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRR……
  * 所有數(shù)據(jù)存在data[0]疚鲤,大小為1024 * 2(每個(gè)采樣點(diǎn)占2字節(jié)) * 2(雙聲道) =4096
  *
  * 雙聲道锥累,AV_SAMPLE_FMT_FLTP采樣格式的數(shù)據(jù)方式存儲(chǔ)如下:
  * LLLLLLLLLLLLLLLLLLLLLLLLLLRRRRRRRRRRRRRRRRRRRRRRRRRRRR……
  * 左聲道數(shù)據(jù)存在data[0],大小為1024 * 4(每個(gè)采樣點(diǎn)占4字節(jié))=4096
  * 右聲道數(shù)據(jù)存在data[1]集歇,大小為1024 * 4(每個(gè)采樣點(diǎn)占4字節(jié))=4096
  *
  * pFrame->linesize[0]揩悄,表示data[0]數(shù)組的長(zhǎng)度
  * av_samples_get_buffer_size返回一幀的數(shù)據(jù)長(zhǎng)度:
  * 雙聲道、AV_SAMPLE_FMT_S16長(zhǎng)度為4096
  * 雙聲道鬼悠,AV_SAMPLE_FMT_FLTP長(zhǎng)度為8192
  */
int AACEncoder::initAudioFrame() {
    ret = av_samples_alloc_array_and_samples(&src_data, &src_linesize, pCodecContext->channels,
            pCodecContext->frame_size, pCodecContext->sample_fmt, 0);
    if (ret < 0) {
        LOGE("Could not allocate source samples\n");
        return -1;
    }
    src_bufsize = av_samples_get_buffer_size(&src_linesize, pCodecContext->channels,
            pCodecContext->frame_size, pCodecContext->sample_fmt, 1);
    pFrame = av_frame_alloc();
    pFrame->nb_samples = pCodecContext->frame_size;
    pFrame->format = pCodecContext->sample_fmt;
    pFrame->channels = pCodecContext->channels;
    pFrame->channel_layout = pCodecContext->channel_layout;
    pFrame->linesize[0] = src_linesize;
    pFrame->sample_rate = pCodecContext->sample_rate;
    return 0;
}

int AACEncoder::encode(const char* pcm_file, const char* aac_file)
{
    //打印ffmpeg系統(tǒng)日志删性,方便排查問題
    av_log_set_level(AV_LOG_VERBOSE);
    av_log_set_callback(android_log_callback);

    // 初始化編碼器
    if (initCodec() < 0) {
        return -1;
    }
    // 創(chuàng)建AVStream
    if (initAudioStream(aac_file) < 0) {
        return -1;
    }
    // 寫入aac文件頭
    avformat_write_header(pFormatContext, NULL);
    // 初始化AVFrame,存放原始音頻數(shù)據(jù)
    if (initAudioFrame() < 0) {
        return -1;
    }
    // 初始化AVPacket焕窝,存放編碼后的aac數(shù)據(jù)
    pkt = av_packet_alloc();
    if (!pkt) {
        LOGE("could not allocate the packet\n");
        return -1;
    }

    FILE* fp_in = fopen(pcm_file, "rb");
    if (!fp_in) {
        LOGE("Can't open pcm input file\n");
        return -1;
    }
    int pts = 0;
    for (;;)
    {
        // 每次從pcm文件讀取一幀數(shù)據(jù)
        if ((ret = fread(src_data[0], 1, src_bufsize, fp_in)) <= 0) {
            LOGE("Fail to read buf from input file\n");
            return -1;
        }
        else if (feof(fp_in)) {
            LOGE("End of input file\n");
            break;
        }
        // 設(shè)置當(dāng)前幀的顯示位置
        pFrame->pts = pts;
        pts++;
        // 將讀到的幀數(shù)據(jù)賦值給AVFrame
        pFrame->data[0] = src_data[0];
        // 將AVFrame發(fā)送到編碼器進(jìn)行編碼
        ret = avcodec_send_frame(pCodecContext, pFrame);
        if (ret < 0) {
            LOGE("Error sending the frame to the encoder\n");
            return -1;
        }
        while (ret >= 0) {
            // 得到編碼后的AVPacket
            ret = avcodec_receive_packet(pCodecContext, pkt);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
//                LOGD("Error encoding audio frame %d\n", ret);
                continue;
            } else if (ret < 0) {
                LOGE("Error encoding audio frame %d\n", ret);
                return -1;
            }
//            LOGD("Success encode frame[%d] size:%d\n", frame_cnt, pkt->size);
            frame_cnt++;
            pkt->stream_index = audioStream->index;
            // 將編碼后的AVPacket數(shù)據(jù)寫入aac文件
            ret = av_interleaved_write_frame(pFormatContext, pkt);
            av_packet_unref(pkt);
            if (ret < 0) {
                LOGE("Error write frame to output file,err code=%d", ret);
                return -1;
            }
        }
    }

    //寫入文件尾
    av_write_trailer(pFormatContext);

    fclose(fp_in);

    avcodec_close(pCodecContext);
    av_free(pCodecContext);
    av_free(&pFrame->data[0]);
    av_frame_free(&pFrame);
    return 0;
}

源碼地址:
Gitee:https://gitee.com/huaisu2020/Android-Live
Github:https://github.com/xh2009cn/Android-Live

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蹬挺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子它掂,更是在濱河造成了極大的恐慌巴帮,老刑警劉巖溯泣,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異榕茧,居然都是意外死亡垃沦,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門用押,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肢簿,“玉大人,你說我怎么就攤上這事蜻拨〕爻洌” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵缎讼,是天一觀的道長(zhǎng)收夸。 經(jīng)常有香客問我,道長(zhǎng)血崭,這世上最難降的妖魔是什么卧惜? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮夹纫,結(jié)果婚禮上序苏,老公的妹妹穿的比我還像新娘。我一直安慰自己捷凄,他們只是感情好忱详,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著跺涤,像睡著了一般匈睁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上桶错,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天航唆,我揣著相機(jī)與錄音,去河邊找鬼院刁。 笑死糯钙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的退腥。 我是一名探鬼主播任岸,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼狡刘!你這毒婦竟也來了享潜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤嗅蔬,失蹤者是張志新(化名)和其女友劉穎剑按,沒想到半個(gè)月后疾就,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡艺蝴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年猬腰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猜敢。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡姑荷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出锣枝,到底是詐尸還是另有隱情厢拭,我是刑警寧澤兰英,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布撇叁,位于F島的核電站,受9級(jí)特大地震影響畦贸,放射性物質(zhì)發(fā)生泄漏陨闹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一薄坏、第九天 我趴在偏房一處隱蔽的房頂上張望趋厉。 院中可真熱鬧,春花似錦胶坠、人聲如沸君账。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乡数。三九已至,卻和暖如春闻牡,著一層夾襖步出監(jiān)牢的瞬間净赴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工罩润, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留玖翅,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓割以,卻偏偏與公主長(zhǎng)得像金度,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子严沥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 學(xué)習(xí)整理的相關(guān)章節(jié)鏈接:基礎(chǔ)篇_1.音視頻學(xué)習(xí)框架基礎(chǔ)篇_2. 顏色空間模型 RBG审姓、YUV、HSV基礎(chǔ)篇_3.圖...
    Allan_Wang閱讀 2,325評(píng)論 0 3
  • AAC(Advanced Audio Coding)祝峻,中文名:高級(jí)音頻編碼魔吐,出現(xiàn)于1997年扎筒,基于MPEG-2的音...
    充滿活力的早晨閱讀 4,564評(píng)論 0 4
  • 介紹 AAC(Advanced Audio Coding),中文稱為“高級(jí)音頻編碼”酬姆,出現(xiàn)于1997年嗜桌,基于 MP...
    sxyxsp123閱讀 7,168評(píng)論 0 7
  • AAC基本格式 AAC音頻格式有ADIF和ADTS: ADIF:Audio Data Interchange Fo...
    愛玩保齡球閱讀 5,148評(píng)論 0 2
  • AAC介紹 介紹AAC,全稱Advanced Audio Coding辞色,是一種專為聲音數(shù)據(jù)設(shè)計(jì)的文件壓縮格式骨宠。他的...
    請(qǐng)叫我果爸閱讀 10,450評(píng)論 3 9