从MP4中提取AAC到本地

Posted on 2025-03-26 00:26  C27  阅读(32)  评论(0)    收藏  举报

先是介绍一下提取流程

image

 

点击查看代码
int main(){

    FILE* file = fopen("./lis.aac","wb");
    //open input file
    AVFormatContext *fctx = NULL; //  媒体文件格式上下文
    int res = avformat_open_input(&fctx,VIDEO_FILE,NULL,NULL);
    if(res != 0){
        return -1;
    }
    res = avformat_find_stream_info(fctx,NULL);  //get media info
    if(res < 0){
        avformat_close_input(&fctx);
    }

    printf("streamNum:%d \n",fctx->nb_streams);

    int audio_index[2] = {0};
    int audio_num = 0;
    //这里获取一下通道标识
    for(int i = 0; i<fctx->nb_streams;i++){         
        if(fctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ){
            audio_index[i] = i;
            audio_num++;
        }
    }

    printf("audio_index[0]= %d\n",audio_index[0]);
    AVPacket ptk;
    int len = 0;
    while(av_read_frame(fctx,&ptk) == 0){   
        if(ptk.stream_index == audio_index[0]){
            len = fwrite(ptk.data,1,ptk.size,file);
            //printf("len = %u\n");
        }

        av_packet_unref(&ptk);      //这里调用是因为av_read_frame中会给avbuffer分配内存调用av_packet_ref
    }

    avformat_close_input(&fctx);
    fclose(file);
    return 0; 
}
运行上面的代码会发现使用ffplay无法播放aac文件,这是因为从mp4中提取出来的aac是没有解码头部信息的,所以下面说一下AAC的传输格式

ADTS
image

从mp4中提取出来的AAC是ES,所以我们需要手动加上ADTS_HEADER.(1 ADTS_HEADER+ 1 ADTS_ES 就是一帧ADTS)

ADTS_HEADER总共7字节分为
1.固定头部 adts_fixed_header(); 28bit
2.可变头部 adts_variable_header(); 28bit

头部信息:

点击查看代码
	adts_fixed_header(){
		syncword	\\同步头 一个ADTS的开始,始终为0xfff
		ID			\\mpeg version	0-mpeg4 1-mpeg2
		Layer		\\ '00'
		protection_absent
		profile		\\AAC级别			0-mainprofile  1-LC 2-SSR 3-reserved
		sampling_frequency_index	\\采样率下标
		private_bit
		channel_configuration	\\声道数
		original_copy
		home
	}
	
	adts_variable_header(){
		copyright_identification_bit
		copyright_identification_start
		acc_frame_length
		adts_buffer_fullness
		number_of_raw_data_blocks_in_frame
	}

完整代码

点击查看代码
const int sampling_frequencies[] = {
    96000,  // 0x0
    88200,  // 0x1
    64000,  // 0x2
    48000,  // 0x3
    44100,  // 0x4
    32000,  // 0x5
    24000,  // 0x6
    22050,  // 0x7
    16000,  // 0x8
    12000,  // 0x9
    11025,  // 0xa
    8000   // 0xb
    // 0xc d e f是保留的
};
int adts_header(char * const p_adts_header, const int data_length,
    const int profile, const int samplerate,
    const int channels)
{

    int sampling_frequency_index = 3; // 默认使用48000hz
    int adtsLen = data_length + 7;

    int frequencies_size = sizeof(sampling_frequencies) / sizeof(sampling_frequencies[0]);
    int i = 0;
    for(i = 0; i < frequencies_size; i++)
    {
    if(sampling_frequencies[i] == samplerate)
    {
    sampling_frequency_index = i;
    break;
    }
    }
    if(i >= frequencies_size)
    {
    printf("unsupport samplerate:%d\n", samplerate);
    return -1;
    }

    p_adts_header[0] = 0xff;         //syncword:0xfff                          高8bits
    p_adts_header[1] = 0xf0;         //syncword:0xfff                          低4bits
    p_adts_header[1] |= (0 << 3);    //MPEG Version:0 for MPEG-4,1 for MPEG-2  1bit
    p_adts_header[1] |= (0 << 1);    //Layer:0                                 2bits
    p_adts_header[1] |= 1;           //protection absent:1                     1bit

    p_adts_header[2] = (profile)<<6;            //profile:profile               2bits
    p_adts_header[2] |= (sampling_frequency_index & 0x0f)<<2; //sampling frequency index:sampling_frequency_index  4bits
    p_adts_header[2] |= (0 << 1);             //private bit:0                   1bit
    p_adts_header[2] |= (channels & 0x04)>>2; //channel configuration:channels  高1bit

    p_adts_header[3] = (channels & 0x03)<<6; //channel configuration:channels 低2bits
    p_adts_header[3] |= (0 << 5);               //original:0                1bit
    p_adts_header[3] |= (0 << 4);               //home:0                    1bit
    p_adts_header[3] |= (0 << 3);               //copyright id bit:0        1bit
    p_adts_header[3] |= (0 << 2);               //copyright id start:0      1bit
    p_adts_header[3] |= ((adtsLen & 0x1800) >> 11);           //frame length:value   高2bits

    p_adts_header[4] = (uint8_t)((adtsLen & 0x7f8) >> 3);     //frame length:value    中间8bits
    p_adts_header[5] = (uint8_t)((adtsLen & 0x7) << 5);       //frame length:value    低3bits
    p_adts_header[5] |= 0x1f;                                 //buffer fullness:0x7ff 高5bits
    p_adts_header[6] = 0xfc;
    // number_of_raw_data_blocks_in_frame:
    //    表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。

    return 0;
}


int main(){

    FILE* file = fopen("./lis.aac","wb");
    //open input file
    AVFormatContext *fctx = NULL; //  媒体文件格式上下文
    int res = avformat_open_input(&fctx,VIDEO_FILE,NULL,NULL);
    if(res != 0){
        return -1;
    }
    res = avformat_find_stream_info(fctx,NULL);  //get media info
    if(res < 0){
        avformat_close_input(&fctx);
    }

    printf("streamNum:%d \n",fctx->nb_streams);

    int audio_index[2] = {0};
    int audio_num = 0;
    //这里获取一下通道标识
    for(int i = 0; i<fctx->nb_streams;i++){
        if(fctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ){
            audio_index[i] = i;
            audio_num++;
        }
    }

    printf("audio_index[0]= %d\n",audio_index[0]);
    AVPacket ptk;
    int len = 0;
    while(av_read_frame(fctx,&ptk) == 0){   
        if(ptk.stream_index == audio_index[0]){
            char adts_header_buf[7] = {0};
            adts_header(adts_header_buf, ptk.size,                  //
                fctx->streams[audio_index[0]]->codecpar->profile,
                fctx->streams[audio_index[0]]->codecpar->sample_rate,
                fctx->streams[audio_index[0]]->codecpar->ch_layout.nb_channels);
            fwrite(adts_header_buf, 1, 7, file); 
            len = fwrite(ptk.data,1,ptk.size,file);
            //printf("len = %u\n");
        }

        av_packet_unref(&ptk);      //这里调用是因为av_read_frame中会给avbuffer分配内存调用av_packet_ref
    }

    avformat_close_input(&fctx);
    fclose(file);
    return 0;
}