IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android MediaExtractor的使用(2)--提取aac音频 -> 正文阅读

[移动开发]Android MediaExtractor的使用(2)--提取aac音频

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

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-28 07:56:17  更:2021-07-28 07:58:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 11:02:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码