AAC文件解码成PCM格式文件

上一篇写到PCM格式文件编码成AAC格式文件,这一步的原因是有利于传输。可以将PCM文件做了很大的压缩力度,使得包变得更小,便于传输。我使用播放器播放了AAC文件听到的是音速明显变快了,声音也变得尖锐了许多,AAC文件变小了很多。


    本文主要是讲 AAC文件解码成PCM文件。接收到AAC文件以后,需要还原成PCM文件再播放。


    需要用到的类:


    MediaCodec  :编解码。上一文简单介绍了它的工作过程,有兴趣请出门左转。


    MediaExtractor :主要是用于分离各种轨道。


 (这里有必要说明一下,比如说MP4文件,叫它MP4格式并不是很正确,它就像是一个标准,一个容器。因为一个MP4文件包含了许多信息,比如音频,视频,字幕等咱们常见的,而音频,视频等在MP4文件中是以轨道【track】形式存在的)。
     ByteBuffer : 这个类在多媒体开发中经常用到。


     直接上代码:

(1)准备各种需要的类:

//用于分离出音频轨道
    private MediaExtractor mMediaExtractor;
    private MediaCodec mMediaDecode;
    private File targetFile;
    //类型
    private String mime = "audio/mp4a-latm";
    //输入缓存组
    private ByteBuffer[] inputBuffers;
    //输出缓存组
    private ByteBuffer[] outputBuffers;
    private MediaCodec.BufferInfo bufferInfo;
    private File pcmFile;
    private FileOutputStream fileOutputStream;
    private int totalSize = 0;
(2)初始化

File root = Environment.getExternalStorageDirectory();
        targetFile = new File(root, "生成的aac.aac");
        pcmFile = new File(root, "解码的pcm.pcm");
        if (!pcmFile.exists()) {
            try {
                pcmFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            fileOutputStream = new FileOutputStream(pcmFile.getAbsoluteFile());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        mMediaExtractor = new MediaExtractor();
        try {
            //设置资源
            mMediaExtractor.setDataSource(targetFile.getAbsolutePath());
            //获取含有音频的MediaFormat
            MediaFormat mediaFormat = createMediaFormat();
            mMediaDecode = MediaCodec.createDecoderByType(mime);
            mMediaDecode.configure(mediaFormat, null, null, 0);//当解压的时候最后一个参数为0
            mMediaDecode.start();//开始,进入runnable状态
            //只有MediaCodec进入到Runnable状态后,才能过去缓存组
            inputBuffers = mMediaDecode.getInputBuffers();
            outputBuffers = mMediaDecode.getOutputBuffers();
            bufferInfo = new MediaCodec.BufferInfo();
        } catch (IOException e) {
            Log.e("tag_ioException",e.getMessage()+"");
            e.printStackTrace();
        }
设置文件资源:
mMediaExtractor.setDataSource();
          支持本地文件(文件描述),网络文件。需要主要的是,设置的资源必须是可用的,不然程序会报错。
          从MediaExtractor中获取音频轨道的MediaFormat:
private MediaFormat createMediaFormat() {
        //获取文件的轨道数,做循环得到含有音频的mediaFormat
        for (int i = 0; i < mMediaExtractor.getTrackCount(); i++) {
            MediaFormat mediaFormat = mMediaExtractor.getTrackFormat(i);
            //MediaFormat键值对应
            String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
            if (mime.contains("audio/")) {
                mMediaExtractor.selectTrack(i);
                return mediaFormat;
            }
        }
        return null;
    }


mMediaDecode.configure(mediaFormat, null, null, 0);当解压的时候最后一个参数为0
(3)解码操作

public void decode() {
        boolean inputSawEos = false;
        boolean outputSawEos = false;
        long kTimes = 5000;//循环时间
        while (!outputSawEos) {
            if (!inputSawEos) {
                //每5000毫秒查询一次
                int inputBufferIndex = mMediaDecode.dequeueInputBuffer(kTimes);
                //输入缓存index可用
                if (inputBufferIndex >= 0) {
                    //获取可用的输入缓存
                    ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                    //从MediaExtractor读取数据到输入缓存中,返回读取长度
                    int bufferSize = mMediaExtractor.readSampleData(inputBuffer, 0);
                    if (bufferSize <= 0) {//已经读取完
                        //标志输入完毕
                        inputSawEos = true;
                        //做标识
                        mMediaDecode.queueInputBuffer(inputBufferIndex, 0, 0, kTimes, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                    } else {
                        long time = mMediaExtractor.getSampleTime();
                        //将输入缓存放入MediaCodec中
                        mMediaDecode.queueInputBuffer(inputBufferIndex, 0, bufferSize, time, 0);
                        //指向下一帧
                        mMediaExtractor.advance();
                    }
                }
            }
            //获取输出缓存,需要传入MediaCodec.BufferInfo 用于存储ByteBuffer信息
            int outputBufferIndex = mMediaDecode.dequeueOutputBuffer(bufferInfo, kTimes);
            if (outputBufferIndex >= 0) {
                int id = outputBufferIndex;
                if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                    mMediaDecode.releaseOutputBuffer(id, false);
                    continue;
                }
                //有输出数据
                if (bufferInfo.size > 0) {
                    //获取输出缓存
                    ByteBuffer outputBuffer = outputBuffers[id];
                    //设置ByteBuffer的position位置
                    outputBuffer.position(bufferInfo.offset);
                    //设置ByteBuffer访问的结点
                    outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
                    byte[] targetData = new byte[bufferInfo.size];
                    //将数据填充到数组中
                    outputBuffer.get(targetData);
                    try {
                        fileOutputStream.write(targetData);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                //释放输出缓存
                mMediaDecode.releaseOutputBuffer(id, false);
                //判断缓存是否完结
                if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    outputSawEos = true;
                }
            } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                outputBuffers = mMediaDecode.getOutputBuffers();

            }else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
                MediaFormat  mediaFormat = mMediaDecode.getOutputFormat();
            }
        }
        //释放资源
        try {
            fileOutputStream.flush();
            fileOutputStream.close();
            mMediaDecode.stop();
            mMediaDecode.release();
            mMediaExtractor.release();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

这部分的操作完全是遵循MediaCodec的操作原理实现的:获取输入缓存,将数据存储到输入缓存中,将输入缓存放回MediaCodec中,获取输出缓存,处理输出缓存的数据。
站在巨人的肩膀上: http://yedaxia.me/Android-MediaExtractor-And-MediaCodec/




你可能感兴趣的:(Android,Android音视频学习)