音视频5.2——MediaCodec 异步方式完成AAC硬解成PCM

 音视频开发路线:

Android 音视频开发入门指南_Jhuster的专栏的技术博客_51CTO博客_android 音视频开发入门

demo地址:

GitHub - wygsqsj/videoPath: 音视频学习路线demo

MediaCodec异步方式

上一节使用同步方式使用MediaCodec总感觉比较麻烦,我们java中使用大量的回调来实现监听者模式,MediaCodec在sdk 19版本后也通过回调来告知使用者,input或者output已经准备好的情况,具体的api就是为MediaCodec设置CallBack,并实现其中的四个方法:

decodeCodec.setCallback(new MediaCodec.Callback() {
    @Override
    public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
        Log.i(LOG_TAG, "input数据已准备好,当前index:" + index);
    }

    @Override
    public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, @NonNull MediaCodec.BufferInfo info) {
         Log.i(LOG_TAG, "outPut数据已准备好,当前index:" + index);
    }

    @Override
    public void onError(@NonNull MediaCodec codec, @NonNull MediaCodec.CodecException e) {
        Log.i(LOG_TAG, "编解码出错 onError" + e.toString());
    }

    @Override
    public void onOutputFormatChanged(@NonNull MediaCodec codec, @NonNull MediaFormat format) {
        Log.i(LOG_TAG, "format发生更改,onOutputFormatChanged" + format.toString());

    }
});

注意点:

  • 先设置callBack,再设置config
  • callBack回调发生再主线程,需要自己手动切换

使用

具体的使用流程跟上一节其实是一样的,就是while循环中手动获取现在改为回调通知了,切换线程我是通过在子线程中构建Handler,收到回调之后再通过Handler把消息发出去,回调的信息通过一个队列去记录一下,具体的代码如下:

public class DecodeAACAsyn extends Thread {

    private Context context;
    private MediaFormat audioFormat;
    private File pcmFile;
    private FileOutputStream fos = null;

    private MediaCodec decodeCodec = null;
    private Queue<byte[]> mOutDataQueue = new LinkedBlockingQueue<>();
    private Queue<Integer> mInputDataQueue = new LinkedBlockingQueue<>();

    private MediaExtractor audioExtractor = new MediaExtractor();

    private Handler mHandler;

    private Runnable outRunnable = () -> {
        try {
            Log.e(LOG_TAG, "outRunnable,当前线程: " + Thread.currentThread().getName());
            byte[] pcmData = mOutDataQueue.poll();
            if (pcmData == null) {
                return;
            }
            Log.e(LOG_TAG, "Handler回调收到,当前数据大小:" + pcmData.length);
            //装车
            fos.write(pcmData);//数据写入文件中
            fos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    };

    private Runnable inputRunnable = () -> {
        try {
            Log.e(LOG_TAG, "inputRunnable,当前线程: " + Thread.currentThread().getName());

            Integer index = mInputDataQueue.poll();
            if (index == null) {
                return;
            }

            ByteBuffer buffer;
            if (Build.VERSION.SDK_INT >= 21) {
                buffer = decodeCodec.getInputBuffer(index);
            } else {
                buffer = decodeCodec.getInputBuffers()[index];
            }
            int sampleSize = audioExtractor.readSampleData(buffer, 0);
            if (sampleSize < 0) {
                Log.i(LOG_TAG, "当前音频已经读取完了");
                decodeCodec.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
            } else {
                Log.i(LOG_TAG, "读取到了音频数据,当前音频数据的数据长度为:" + sampleSize);
                long sampleTime = audioExtractor.getSampleTime();
                decodeCodec.queueInputBuffer(index, 0, sampleSize, sampleTime, 0);
                audioExtractor.advance();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    };

    public DecodeAACAsyn(Demo5Activity demo5Activity) {
        context = demo5Activity;
        pcmFile = new File(context.getExternalFilesDir(Environment.DIRECTORY_MUSIC), "demo5a.pcm");
        try {
            pcmFile.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    public void run() {
        super.run();
        Looper.prepare();
        mHandler = new Handler(Looper.myLooper()) {
            @Override
            public void handleMessage(@NonNull @NotNull Message msg) {
                super.handleMessage(msg);
                if (msg.what == 0) {
                    destory();
                }
            }
        };
        Log.e(LOG_TAG, "Decode,当前线程: " + Thread.currentThread().getName());
        try {
            fos = new FileOutputStream(pcmFile.getAbsoluteFile());
            audioExtractor.setDataSource(context.getResources().openRawResourceFd(R.raw.demo5mp3));
            int count = audioExtractor.getTrackCount();
            for (int i = 0; i < count; i++) {
                audioFormat = audioExtractor.getTrackFormat(i);
                if (audioFormat.getString(MediaFormat.KEY_MIME).startsWith("audio/")) {
                    audioExtractor.selectTrack(i);
                    Log.i(LOG_TAG, "aac 找到了通道" + i);
                    break;
                }
            }

            //初始化MiediaCodec
            decodeCodec = MediaCodec.createDecoderByType(audioFormat.getString(MediaFormat.KEY_MIME));
            /*
             * 通过回调方式来进行数据的编码,比刚才手动调用方式更合理,回调运行在主线程,记得切换线程
             */
            decodeCodec.setCallback(new MediaCodec.Callback() {
                @Override
                public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
                    Log.i(LOG_TAG, "异步回调,onInputBufferAvailable,当前index:" + index);
                    mInputDataQueue.offer(index);
                    mHandler.post(inputRunnable);

                }

                @Override
                public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, @NonNull MediaCodec.BufferInfo info) {
                    Log.i(LOG_TAG, "异步回调,onOutputBufferAvailable,当前index:" + index);
                    Log.i(LOG_TAG, "获取到解码后的数据了,当前解析后的数据长度为:" + info.size);
                    //拿到当前装满火腿肠的筐
                    ByteBuffer outputBuffer;
                    if (Build.VERSION.SDK_INT >= 21) {
                        outputBuffer = codec.getOutputBuffer(index);
                    } else {
                        outputBuffer = codec.getOutputBuffers()[index];
                    }
                    //将火腿肠放到新的容器里,便于后期装车运走
                    byte[] pcmData = new byte[info.size];
                    outputBuffer.get(pcmData);//写入到字节数组中
                    outputBuffer.clear();//清空当前筐
                    //将装猪的数据放到队列里面,通过handler发送消息在子线程装入数据
                    mOutDataQueue.offer(pcmData);
                    mHandler.post(outRunnable);
                    //把筐放回工厂里面
                    codec.releaseOutputBuffer(index, false);

                    if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                        mHandler.sendEmptyMessage(0);
                    }
                }

                @Override
                public void onError(@NonNull MediaCodec codec, @NonNull MediaCodec.CodecException e) {
                    Log.i(LOG_TAG, "异步回调,onError" + e.toString());
                }

                @Override
                public void onOutputFormatChanged(@NonNull MediaCodec codec, @NonNull MediaFormat format) {
                    Log.i(LOG_TAG, "异步回调,onOutputFormatChanged" + format.toString());

                }
            });
            //先配置callBack,再配置config;数据格式,surface用来渲染解析出来的数据;加密用的对象;标志 encode :1 decode:0
            decodeCodec.configure(audioFormat, null, null, 0);

            //启动解码
            decodeCodec.start();

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

        Looper.loop();
    }

    public void destory() {
        Log.i(LOG_TAG, "销毁资源");
        if (audioExtractor != null) {
            audioExtractor.release();
        }
        if (decodeCodec != null) {
            decodeCodec.stop();
            decodeCodec.release();
        }
        if (fos != null) {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
能转换下面这些音频和视频,压缩包有包和源码,还有文档api. 整理资料不易,不喜欢可以自己百度 Audio decoders adpcm_4xm adpcm_adx adpcm_ct adpcm_ea adpcm_ea_r1 adpcm_ea_r2 adpcm_ea_r3 adpcm_ea_xas adpcm_ima_amv adpcm_ima_dk3 adpcm_ima_dk4 adpcm_ima_ea_eacs adpcm_ima_ea_sead adpcm_ima_qt adpcm_ima_smjpeg adpcm_ima_wav adpcm_ima_ws adpcm_ms adpcm_sbpro_2 adpcm_sbpro_3 adpcm_sbpro_4 adpcm_swf adpcm_thp adpcm_xa adpcm_yamaha alac ape atrac 3 cook dca dsicinaudio flac g726 imc interplay_dpcm liba52 libamr_nb libamr_wb libfaad libgsm libgsm_ms mace3 mace6 mp2 mp3 mp3adu mp3on4 mpc sv7 mpc sv8 mpeg4aac nellymoser pcm_alaw pcm_mulaw pcm_s16be pcm_s16le pcm_s16le_planar pcm_s24be pcm_s24daud pcm_s24le pcm_s32be pcm_s32le pcm_s8 pcm_u16be pcm_u16le pcm_u24be pcm_u24le pcm_u32be pcm_u32le pcm_u8 pcm_zork qdm2 real_144 real_288 roq_dpcm shorten smackaud sol_dpcm sonic truespeech tta vmdaudio vorbis wavpack wmav1 wmav2 ws_snd1 xan_dpcm Audio encoders ac3 adpcm_adx adpcm_ima_wav adpcm_ms adpcm_swf adpcm_yamaha flac g726 libamr_nb libamr_wb libfaac libgsm libgsm_ms libmp3lame libvorbis mp2 pcm_alaw pcm_mulaw pcm_s16be pcm_s16le pcm_s24be pcm_s24daud pcm_s24le pcm_s32be pcm_s32le pcm_s8 pcm_u16be pcm_u16le pcm_u24be pcm_u24le pcm_u32be pcm_u32le pcm_u8 pcm_zork roq_dpcm sonic sonicls vorbis wmav1 wmav2 Video decoders 4xm 8bps VMware video aasc amv asv1 asv2 avs bethsoftvid bmp c93 camstudio camtasia cavs cinepak cljr cyuv dnxhd dsicinvideo dvvideo dxa ffv1 ffvhuff flashsv flic flv fraps gif h261 h263 h263i h264 huffyuv idcinvideo indeo2 indeo3 interplayvideo jpegls kmvc loco mdec mjpeg mjpegb mmvideo mpeg1video mpeg2video mpeg4 mpegvideo msmpeg4 msmpeg4v1 msmpeg4v2 msrle msvideo1 mszh nuv pam pbm pgm pgmyuv png ppm ptx qdraw qpeg qtrle rawvideo roqvideo rpza rv10 rv20 sgi smackvid smc snow sp5x svq1 svq3 targa theora thp tiertexseqvideo tiff truemotion1 truemotion2 txd ultimotion vb vc1 vcr1 vmdvideo vp3 vp5 vp6 vp6a vp6f vqavideo wmv1 wmv2 wmv3 wnv1 xan_wc3 xl zlib zmbv Video encoders asv1 asv2 bmp dnxhd dvvideo ffv1 ffvhuff flashsv flv gif h261 h263 h263p huffyuv jpegls libtheora libx264 libxvid ljpeg mjpeg mpeg1video mpeg2video mpeg4 msmpeg4 msmpeg4v1 msmpeg4v2 pam pbm pgm pgmyuv png ppm qtrle rawvideo roqvideo rv10 rv20 sgi snow svq1 targa tiff wmv1 wmv2 zlib zmbv
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值