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;
}
}