Android音视频开发入门(九)

任务目标

通过MediaCodec API实现视频的硬解

流程介绍

本例采用的视频格式为mp4,先把mp4格式的视频解码,然后通过SurfaceView进行播放。具体流程如下:

  1. 初始化SurfaceView,并实现SurfaceHolder.Callback接口
  2. 创建 MediaExtractor,设置视频源,通过MediaExtractor获取视频的MediaFormat
  3. 通过2获取到的MediaFormat创建解码器,并进行解码器的配置,开始解码
  4. 通过解码器不断获取缓冲区,并通过queueInputBuffer()发送解码数据
  5. 解码器调用dequeueOutputBuffer()获取解码数据并播放。
主要代码
1.同步方式

要放到线程中调用

		initManager();
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        long startTime = System.currentTimeMillis();
        while (!isDecodeFinish) {
            int inputIndex = mDecodeMediaCodec.dequeueInputBuffer(10000);
            if (inputIndex >= 0) {
                ByteBuffer buffer = mDecodeMediaCodec.getInputBuffer(inputIndex);
                int sampleSize = mExtractor.readSampleData(buffer, 0);
                if (sampleSize < 0) {
                    mDecodeMediaCodec.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                    isDecodeFinish = true;
                } else {
                    mDecodeMediaCodec.queueInputBuffer(inputIndex, 0, sampleSize, mExtractor.getSampleTime(), 0);
                    mExtractor.advance();
                }
            }
            int outputIndex = mDecodeMediaCodec.dequeueOutputBuffer(bufferInfo, 0);
            while (bufferInfo.presentationTimeUs / 1000 > System.currentTimeMillis() - startTime) {
                try {
                    sleep(10);//注意这儿,如果不加视频可能会播放很快
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if (outputIndex >= 0) {
                mDecodeMediaCodec.releaseOutputBuffer(outputIndex, true);
            }
            if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                Log.d("decode", "BUFFER_FLAG_END_OF_STREAM");
                break;
            }
        }
        mDecodeMediaCodec.stop();
        mDecodeMediaCodec.release();
        mExtractor.release();

	private void initManager() {
        try {
            mExtractor = new MediaExtractor();
            mExtractor.setDataSource(mVideoPath); //设置视频源
            for (int i = 0; i < mExtractor.getTrackCount(); i++) {
                MediaFormat format = mExtractor.getTrackFormat(i);
                String mime = format.getString(MediaFormat.KEY_MIME);
                if (mime.startsWith("video/")) {
                    mExtractor.selectTrack(i);

                    mDecodeMediaCodec = MediaCodec.createDecoderByType(mime);
                    mDecodeMediaCodec.configure(format, mSurface, null, 0);
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (mDecodeMediaCodec == null) {
            Log.e("decode", "MediaCodec is null");
        }

        mDecodeMediaCodec.start();
    }
2.异步方式

初始化与同步方式相似,只不过在调用解码器的start()方法前设置解码回调setCallback(MediaCodec.CallBack),回调方法的主要代码如下:

	/**
     * 解码回调
     */
    private MediaCodec.Callback codecCallback = new MediaCodec.Callback() {

        @Override
        public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
            ByteBuffer buffer = codec.getInputBuffer(index);

            int sampleSize = mExtractor.readSampleData(buffer, index);
            if (sampleSize < 0) {
                mDecoderCodec.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
            } else {
                mDecoderCodec.queueInputBuffer(index, 0, sampleSize, mExtractor.getSampleTime(), 0);
                mExtractor.advance();
            }
        }

        @Override
        public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, @NonNull MediaCodec.BufferInfo info) {
            try {
                Thread.sleep(30); //不休眠播放很快
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (index >= 0) {
                codec.releaseOutputBuffer(index, true);
            }
            if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                release();
            }
        }

        @Override
        public void onError(@NonNull MediaCodec codec, @NonNull MediaCodec.CodecException e) {
            Log.e("decoder", "error");
        }

        @Override
        public void onOutputFormatChanged(@NonNull MediaCodec codec, @NonNull MediaFormat format) {
            Log.e("decoder", "Format Changed");
        }
    };

代码已上传GitHub

参考

MediaEditDemo

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值