Android音频处理学习之MediaExtractor获取aac文件后添加ADTS头

参考:
http://stackoverflow.com/questions/18862715/how-to-generate-the-aac-adts-elementary-stream-with-android-mediacodec

1 ADTS概念

上文利用MediaExtractor从视频中得到了aac文件,但是得到的文件无法播放,需要增加adts头,播放器才能识别aac文件进行播放。
AAC(Advanced Audio Coding)高级音频编码,有基于MPEG-2和MPEG-4标准两种音频编码技术。aac音频格式有两种编码方式ADIF和ADTS,ADTS(Audio Data Transport Stream)音频数据传输流是aac的一种传输格式。ADIF(Audio Data Interchange Format)音频数据交换格式。

ADTS和ADIF两者之间的区别是ADIF在所有aac数据的开始添加一个ADIF头,可以确定音频数据的开始,aac文件有且只有这一个头信息。ADTS则把aac数据分成多个es帧,在每一帧的前面添加一个ADTS头信息,所以adts可以任意帧解码,因为每一帧都有头部信息。
ADTS具有流特性,所以是最常用的传输格式。
ADTS格式说明:
在这里插入图片描述

2 ADTS 头格式

ADTS 头部由7个字节组成(或者9个字节,是否有CRC),包括采样率,声道数,帧长度等信息。
在这里插入图片描述
参数解释说明

  • syncword:长度12bits 同步头,总是0xFFF,代表adts的开始

  • ID:长度 1bits, MPEG的版本,0 表示MPEG-4,1表示MPEG-2

  • layer:长度2bits, 总是00

  • protection_absent:长度1bits,是否有CRC,1是没有,2是有CRC

  • profile;2 哪个级别的aac,有的芯片只支持AAC LC,0表示Main profile,1 表示low complexityprofile lc,2Scalable sampling rate profile SSLEngineResult,3 reserved
    profile的值等于 Audio Object Type的值减1.
    profile = MPEG-4 Audio Object Type - 1

  • MPEG-4 sampling_frequency_index;长度3bits 采样率

  • private_stream;长度1bits,编码时为0,解码是忽略

  • MPEG-4 channel_configuration;长度3bits, 声道数

  • original_copy;长度1bits,编码时为0,解码是忽略

  • home;长度1bits,编码时为0,解码是忽略

  • copyright_identification_bit;长度1bits,编码时为0,解码是忽略

  • copyright_identification_start;长度1bits,编码时为0,解码是忽略

  • aac_frame_length;长度13bits, ADTS头加上帧数据总长度,aac_frame_length = (protection_absent == 1 ? 7 : 9) + size(AACFrame)
    protection_absent=0时, header length=9bytes
    protection_absent=1时, header length=7bytes

  • adts_buffer_fullness;长度11bits,0x7FF 说明是码率可变的码流。

  • number_of_raw_data_blocks_in_frame;长度2bits,表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。所以说number_of_raw_data_blocks_in_frame == 0 表示说ADTS帧中有一个AAC数据块。

** 内置采样率对应:**
0: 96000 Hz
1: 88200 Hz
2: 64000 Hz
3: 48000 Hz
4: 44100 Hz
5: 32000 Hz
6: 24000 Hz
7: 22050 Hz
8: 16000 Hz
9: 12000 Hz
10: 11025 Hz
11: 8000 Hz
12: 7350 Hz
13: 预留
14: 预留

内置声道数对应:
0: 默认
1: 1 个声道
2: 2 个声道
3: 3
4: 4
5: 5
6: 6
7: 8
8-15: 预留

3 实例代码

/** 不包含CRC,所以packetLen需要一帧的长度+7
 * @param packet    一帧数据(包含adts头长度)
 * @param packetLen 一帧数据(包含adts头)的长度,
 *
 */
private static void addADTStoPacket(byte[] packet, int packetLen) {
    int profile = 2; // AAC LC
    int freqIdx = 8; //8 标识16000,取特定
    int channelCfg = 2; // 音频声道数为两个

    // fill in ADTS data
    packet[0] = (byte) 0xFF;//1111 1111
    packet[1] = (byte) 0xF9;//1111 1001  1111 还是syncword
    // 1001 第一个1 代表MPEG-2,接着00为常量,最后一个1,标识没有CRC

    packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (channelCfg >> 2));
    packet[3] = (byte) (((channelCfg & 3) << 6) + (packetLen >> 11));
    packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
    packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
    packet[6] = (byte) 0xFC;
}

private void initMediaDecode() {
    String srcPath = Environment.getExternalStorageDirectory()
            .getPath() + "/video/video.mp4";
    File file = new File(dirPath);
    if (!file.exists()){
        file.mkdir();
    }

    File file1 = new File(pcmPath);
    File file2 = new File(mp4Path);
    try {
        if (file1.exists()){
            file1.delete();

        }
        file1.createNewFile();
    } catch (IOException e) {
        e.printStackTrace();
    }

    try {
        if (file2.exists()){
            file2.delete();
        }
        file2.createNewFile();
    } catch (IOException e) {
        e.printStackTrace();
    }

    try {
        mediaExtractor = new MediaExtractor();//此类可分离视频文件的音轨和视频轨道
        mediaExtractor.setDataSource(srcPath);//媒体文件的位置
        System.out.println("==========getTrackCount()===================" + mediaExtractor.getTrackCount());
        for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {//遍历媒体轨道 此处我们传入的是音频文件,所以也就只有一条轨道
            MediaFormat format = mediaExtractor.getTrackFormat(i);
            String mime = format.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith("audio")) {//获取音频轨道
                mediaExtractor.selectTrack(i);//选择此音频轨道
                System.out.println("====audio=====KEY_MIME==========="+format.getString(MediaFormat.KEY_MIME));
                System.out.println("====audio=====KEY_CHANNEL_COUNT==========="+format.getInteger(MediaFormat.KEY_CHANNEL_COUNT)+"");
                System.out.println("====audio=====KEY_SAMPLE_RATE==========="+format.getInteger(MediaFormat.KEY_SAMPLE_RATE)+"");
                System.out.println("====audio=====KEY_DURATION==========="+format.getLong(MediaFormat.KEY_DURATION)+"");

                System.out.println("====audio=====getSampleFlags==========="+mediaExtractor.getSampleFlags()+"");
                System.out.println("====audio=====getSampleTime==========="+mediaExtractor.getSampleTime()+"");
              //  System.out.println("====audio=====getSampleSize==========="+mediaExtractor.getSampleSize()+"");api28
                System.out.println("====audio=====getSampleTrackIndex==========="+ mediaExtractor.getSampleTrackIndex()+"");

                try {
                    ByteBuffer inputBuffer = ByteBuffer.allocate(100 * 1024);
                    FileOutputStream fe=new FileOutputStream(file1,true);

                    while ( true) {
                        int readSampleCount = mediaExtractor.readSampleData(inputBuffer, 0);
                        if (readSampleCount < 0) {
                            break;
                        }
                        byte[] buffer = new byte[readSampleCount];
                        inputBuffer.get(buffer);
                        fe.write(buffer);
                        inputBuffer.clear();
                        mediaExtractor.advance();

                    }

                    fe.flush();
                    fe.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {

                }

            }

            if (mime.startsWith("video")){
                mediaExtractor.selectTrack(i);//选择此视频轨道
                System.out.println("====video=====KEY_MIME==========="+format.getString(MediaFormat.KEY_MIME));
                System.out.println("====video=====KEY_DURATION==========="+format.getLong(MediaFormat.KEY_DURATION)+"");
                System.out.println("====video=====KEY_WIDTH==========="+format.getInteger(MediaFormat.KEY_WIDTH)+"");
                System.out.println("====video=====KEY_HEIGHT==========="+format.getInteger(MediaFormat.KEY_HEIGHT)+"");

                System.out.println("====video=====getSampleFlags==========="+mediaExtractor.getSampleFlags()+"");
                System.out.println("====video=====getSampleTime==========="+mediaExtractor.getSampleTime()+"");
               // System.out.println("====video=====getSampleSize==========="+mediaExtractor.getSampleSize()+"");api28
                System.out.println("====video=====getSampleTrackIndex==========="+ mediaExtractor.getSampleTrackIndex()+"");

                try {
                    ByteBuffer inputBuffer = ByteBuffer.allocate(100 * 1024);
                    FileOutputStream fe=new FileOutputStream(file2,true);

                    while ( true) {
                        int readSampleCount = mediaExtractor.readSampleData(inputBuffer, 0);
                        if (readSampleCount < 0) {
                            break;
                        }
                        byte[] buffer = new byte[readSampleCount];
                        inputBuffer.get(buffer);
                        byte[] newbuff = new byte[readSampleCount + 7];
                        addADTStoPacket(newbuff, readSampleCount + 7);
                        System.arraycopy(buffer, 0, newbuff, 7, readSampleCount);
                        fe.write(newbuff);

                       // fe.write(buffer);
                        inputBuffer.clear();
                        mediaExtractor.advance();

                    }

                    fe.flush();
                    fe.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {

                }
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        mediaExtractor.release();
        mediaExtractor = null;
    }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值