MP4是一个封装格式,mp4包含视频流和音频流。大部分的音频流都是aac格式。那么我们现在来提取音频流。
使用到的api
/**
* Subsequent calls to {@link #readSampleData}, {@link #getSampleTrackIndex} and
* {@link #getSampleTime} only retrieve information for the subset of tracks
* selected.
* Selecting the same track multiple times has no effect, the track is
* only selected once.
*/
public native void selectTrack(int index);
这个函数用来设置Extractor提取那一路流
/**
* Retrieve the current encoded sample and store it in the byte buffer
* starting at the given offset.
* <p>
* <b>Note:</b>As of API 21, on success the position and limit of
* {@code byteBuf} is updated to point to the data just read.
* @param byteBuf the destination byte buffer
* @return the sample size (or -1 if no more samples are available).
*/
public native int readSampleData(@NonNull ByteBuffer byteBuf, int offset);
这个函数用来从流中获取数据放入byteBuf中
ByteBuffer byteBuffer = ByteBuffer.allocate(100 * 1024);
byteBuffer的大小要能够足够放读取出来的数据。
我们从流中读取出来的是aac数据,aac数据要能够播放,必须加入adts头。
https://blog.csdn.net/tantion/article/details/82743942?这篇文章讲了adts的内容
简单来说就是设置声道? 采样率 aac_profile 数据大小等
下面是完整的代码
package com.yuanxuzhen.androidmedia.demux;
import android.media.MediaExtractor;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
public class ExecutorAudio {
public void extractAudio(String srcPath, String dstPath){
AudioMediaInfo mediaInfo = MediaUtil.getAudioMeidaInfo(srcPath);
FileOutputStream dstFileOutPutStream = null;
File file = new File(dstPath);
if(file.exists()){
file.delete();
}
try{
dstFileOutPutStream=new FileOutputStream(dstPath,true);
MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(srcPath);
extractor.selectTrack(mediaInfo.trackIndex);
ByteBuffer byteBuffer = ByteBuffer.allocate(100 * 1024);
while (true){
int readSampleCount = extractor.readSampleData(byteBuffer, 0);
if(readSampleCount <= 0){
break;
}
byte[] buffer = new byte[readSampleCount];
byteBuffer.get(buffer);
byte[] adtsHeader = getAdtsHeader(readSampleCount,
mediaInfo.aacProfile,
mediaInfo.sampleRate,
mediaInfo.channelCount);
dstFileOutPutStream.write(adtsHeader);
dstFileOutPutStream.write(buffer);
dstFileOutPutStream.flush();
byteBuffer.clear();
extractor.advance();
}
extractor.release();
extractor = null;
dstFileOutPutStream.close();
}catch (Exception e){
}finally {
}
}
int getAudioObjType(int aactype)
{
//AAC HE V2 = AAC LC + SBR + PS
//AAV HE = AAC LC + SBR
//所以无论是 AAC_HEv2 还是 AAC_HE 都是 AAC_LC
switch (aactype)
{
case 0:
case 2:
case 3:
return aactype + 1;
case 1:
case 4:
case 28:
return 2;
default:
return 2;
}
}
int getSampleRateIndex(int freq, int aactype)
{
int i = 0;
int[] freq_arr = {
96000, 88200, 64000, 48000, 44100, 32000,
24000, 22050, 16000, 12000, 11025, 8000, 7350};
//如果是 AAC HEv2 或 AAC HE, 则频率减半
if (aactype == 28 || aactype == 4)
{
freq /= 2;
}
for (i = 0; i < 13; i++)
{
if (freq == freq_arr[i])
{
return i;
}
}
return 4; //默认是44100
}
int getChannelConfig(int channels, int aactype)
{
//如果是 AAC HEv2 通道数减半
if (aactype == 28)
{
return (channels / 2);
}
return channels;
}
/*AdtsHeader
*AdtsHeader可能是7个字节也可能是9个字节,取决于
*syncword 12bit 0xfff 总是0xfff代表一个帧的开始
* ID: 1bit 代表MPEG的版本 MPEG-4: 0 MPGE2:1
* Layer: 2bit always: '00'
*protection_absent: 1bit 不存在CRC是1(adtsheader就是7个字节) 存在CRC的是0(就是9个字节)
*profile: 2bit 表示使用哪个级别的AAC,如01 Low Complexity(LC) -- AAC LC profile的值等于 Audio Object Type的值减1. profile = MPEG-4 Audio Object Type - 1
*sampling_frequency_index:4bit 采样率的下标
*channel_configuration:3bit 声道数
*
* */
private byte[] getAdtsHeader(int dataLen, int aactype, int frequency, int channels)
{
byte[] adtsHeader = new byte[7];
int audio_object_type = getAudioObjType(aactype);
int sampling_frequency_index = getSampleRateIndex(frequency, aactype);
int channel_config = getChannelConfig(channels, aactype);
Log.e("EXecutorAudio", "aot=" + audio_object_type + "freq_index=" + sampling_frequency_index + "channel=" + channel_config);
int adtsLen = dataLen + 7;
adtsHeader[0] = (byte) 0xff; //syncword:0xfff 高8bits
adtsHeader[1] = (byte) 0xf0; //syncword:0xfff 低4bits
adtsHeader[1] |= (0 << 3); //MPEG Version:0 for MPEG-4,1 for MPEG-2 1bit
adtsHeader[1] |= (0 << 1); //Layer:0 2bits
adtsHeader[1] |= 1; //protection absent:1 1bit
adtsHeader[2] = (byte) ((audio_object_type - 1) << 6); //profile:audio_object_type - 1 2bits
adtsHeader[2] |= (sampling_frequency_index & 0x0f) << 2; //sampling frequency index:sampling_frequency_index 4bits
adtsHeader[2] |= (0 << 1); //private bit:0 1bit
adtsHeader[2] |= (channel_config & 0x04) >> 2; //channel configuration:channel_config 高1bit
adtsHeader[3] = (byte) ((channel_config & 0x03) << 6); //channel configuration:channel_config 低2bits
adtsHeader[3] |= (0 << 5); //original:0 1bit
adtsHeader[3] |= (0 << 4); //home:0 1bit
adtsHeader[3] |= (0 << 3); //copyright id bit:0 1bit
adtsHeader[3] |= (0 << 2); //copyright id start:0 1bit
adtsHeader[3] |= ((adtsLen & 0x1800) >> 11); //frame length:value 高2bits
adtsHeader[4] = (byte)((adtsLen & 0x7f8) >> 3); //frame length:value 中间8bits
adtsHeader[5] = (byte)((adtsLen & 0x7) << 5); //frame length:value 低3bits
adtsHeader[5] |= 0x1f; //buffer fullness:0x7ff 高5bits
adtsHeader[6] = (byte) 0xfc;
return adtsHeader;
}
}
package com.yuanxuzhen.androidmedia.demux;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import java.io.IOException;
public class MediaUtil {
public static int getTrackIndex(String targetTrack, String path) {
MediaExtractor extractor = new MediaExtractor();//实例一个MediaExtractor
try {
extractor.setDataSource(path);//设置添加MP4文件路径
} catch (IOException e) {
e.printStackTrace();
}
int trackIndex = -1;
int count = extractor.getTrackCount();//获取轨道数量
for (int i = 0; i < count; i++) {
MediaFormat mediaFormat = extractor.getTrackFormat(i);
String currentTrack = mediaFormat.getString(MediaFormat.KEY_MIME);
if (currentTrack.startsWith(targetTrack)) {
trackIndex = i;
break;
}
}
return trackIndex;
}
public static AudioMediaInfo getAudioMeidaInfo(String path){
int index = getTrackIndex("audio", path);
if(index < 0){
return null;
}
try{
MediaExtractor extractor = new MediaExtractor();//实例一个MediaExtractor
extractor.setDataSource(path);
MediaFormat mediaFormat = extractor.getTrackFormat(index);
AudioMediaInfo mediaINfo = new AudioMediaInfo();
mediaINfo.trackIndex = index;
try{
mediaINfo.bitRate = mediaFormat.getInteger(MediaFormat.KEY_BIT_RATE);//获取比特
}catch (Exception e){
e.printStackTrace();
}
try{
// mediaINfo.pcmEncoding = mediaFormat.getInteger(MediaFormat.KEY_PCM_ENCODING);//PCM-编码 模拟信号编码
mediaINfo.sampleRate = mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
mediaINfo.channelCount = mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
}catch (Exception e){
e.printStackTrace();
}
try{
mediaINfo.isAdts = mediaFormat.getInteger(MediaFormat.KEY_IS_ADTS);
}catch (Exception e){
e.printStackTrace();
}
try{
mediaINfo.keyChannelMask = mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_MASK);//图块分辨率
}catch (Exception e){
e.printStackTrace();
}
try{
mediaINfo.aacProfile = mediaFormat.getInteger(MediaFormat.KEY_AAC_PROFILE);//图块分辨率
}catch (Exception e){
e.printStackTrace();
}
return mediaINfo;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
public VideoMediaInfo getVideoMediaInfo(String path){
int index = getTrackIndex("video", path);
if(index < 0){
return null;
}
try{
MediaExtractor extractor = new MediaExtractor();//实例一个MediaExtractor
extractor.setDataSource(path);
MediaFormat mediaFormat = extractor.getTrackFormat(index);
VideoMediaInfo videoMediaInfo = new VideoMediaInfo();
videoMediaInfo.language = mediaFormat.getString(MediaFormat.KEY_LANGUAGE);//获取语言格式内容
videoMediaInfo.width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
videoMediaInfo.height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
videoMediaInfo.durationTime = mediaFormat.getLong(MediaFormat.KEY_DURATION);
videoMediaInfo.maxByteCount = mediaFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);//获取视频缓存输出的最大大小
try{
Integer bitRate = mediaFormat.getInteger(MediaFormat.KEY_BIT_RATE);//获取比特
}catch (Exception e){
e.printStackTrace();
}
try{
videoMediaInfo.colorFormat = mediaFormat.getInteger(MediaFormat.KEY_COLOR_FORMAT);//颜色格式
}catch (Exception e){
e.printStackTrace();
}
try{
videoMediaInfo.frameRate = mediaFormat.getInteger(MediaFormat.KEY_FRAME_RATE);//帧率
}catch (Exception e){
e.printStackTrace();
}
try{
videoMediaInfo. tileWidth = mediaFormat.getInteger(MediaFormat.KEY_TILE_WIDTH);//图块分辨率
}catch (Exception e){
e.printStackTrace();
}
try{
videoMediaInfo. tileHeight = mediaFormat.getInteger(MediaFormat.KEY_TILE_HEIGHT);//图块分辨率
}catch (Exception e){
e.printStackTrace();
}
try{
videoMediaInfo.gridRows = mediaFormat.getInteger(MediaFormat.KEY_GRID_ROWS);//网格行
}catch (Exception e){
e.printStackTrace();
}
try{
videoMediaInfo.gridColumns = mediaFormat.getInteger(MediaFormat.KEY_GRID_COLUMNS);//网格列
}catch (Exception e){
e.printStackTrace();
}
return videoMediaInfo;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
public static int getMediaCount(String path){
try{
MediaExtractor extractor = new MediaExtractor();//实例一个MediaExtractor
extractor.setDataSource(path);
int count = extractor.getTrackCount();//获取轨道数量
return count;
}catch (Exception e){
e.printStackTrace();
}
return 0;
}
}
gitee地址
https://gitee.com/creat151/android-media.git
|