美文网首页
Android之MediaCodec视频解码

Android之MediaCodec视频解码

作者: 极客匠 | 来源:发表于2019-03-21 10:58 被阅读0次

    简介

    MediaCodec:负责媒体文件的编码和解码工作,内部方法均为native方法。

    正文

    视频播放主要步骤如下:

    • 将资源加载到MediaExtractor
    • 获取视频所在轨道
    • 设置MediaExtractor选中视频所在轨道
    • 创建视频解码的MediaCodec
    • 循环开始
    • 将extractor中资源以一个单位填充到decorder的输入缓存区中
    • decorder之后,将解码的视频填充到输出缓冲区中
    • 将缓冲区中的数据渲染到surface中
    • 循环结束,直至视频资源的末尾
    • 释放资源

    视频播放创建一个播放的Thread

    private class PlayerThread extends Thread {
        ...
    }
    
    1. 初始化

      private MediaExtractor extractor;
      private MediaCodec decoder;
      extractor = new MediaExtractor();
      ByteBuffer[] inputBuffers = decoder.getInputBuffers();
      ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
      MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();

    2.将资源加载到MediaExtractor

    try {
                    extractor.setDataSource(SAMPLE);
                } catch (IOException e) {
                    e.printStackTrace();
                }
    

    3.获取视频所在轨道,设置MediaExtractor选中视频所在轨道,创建视频解码的MediaCodec

    for (int i = 0; i < extractor.getTrackCount(); i++) {
                    MediaFormat format = extractor.getTrackFormat(i);
                    String mime = format.getString(MediaFormat.KEY_MIME);
                    if (mime.startsWith("video/")) {
                        extractor.selectTrack(i);
                        try {
                            decoder = MediaCodec.createDecoderByType(mime);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        decoder.configure(format, surface, null, 0);
                        break;
                    }
                }
    

    4.循环操作:将extractor中资源以一个单位填充到decorder的输入缓存区中,decorder之后,将解码的视频填充到输出缓冲区中,将缓冲区中的数据渲染到surface中,循环结束,直至视频资源的末尾

    while (!Thread.interrupted()) {
                    if (!isEOS) {
                        int inIndex = decoder.dequeueInputBuffer(10000);
                        if (inIndex >= 0) {
                            ByteBuffer buffer = inputBuffers[inIndex];
                            int sampleSize = extractor.readSampleData(buffer, 0);
                            if (sampleSize < 0) {
                                // We shouldn't stop the playback at this point, just pass the EOS
                                // flag to decoder, we will get it again from the
                                // dequeueOutputBuffer
                                Log.d("DecodeActivity", "InputBuffer BUFFER_FLAG_END_OF_STREAM");
                                decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                                isEOS = true;
                            } else {
                                decoder.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);
                                extractor.advance();
                            }
                        }
                    }
    
                    int outIndex = decoder.dequeueOutputBuffer(info, 10000);
                    switch (outIndex) {
                        case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                            Log.d("DecodeActivity", "INFO_OUTPUT_BUFFERS_CHANGED");
                            outputBuffers = decoder.getOutputBuffers();
                            break;
                        case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                            Log.d("DecodeActivity", "New format " + decoder.getOutputFormat());
                            break;
                        case MediaCodec.INFO_TRY_AGAIN_LATER:
                            Log.d("DecodeActivity", "dequeueOutputBuffer timed out!");
                            break;
                        default:
                            ByteBuffer buffer = outputBuffers[outIndex];
                            Log.v("DecodeActivity", "We can't use this buffer but render it due to the API limit, " + buffer);
    
                            // We use a very simple clock to keep the video FPS, or the video
                            // playback will be too fast
                            while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
                                try {
                                    sleep(10);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                    break;
                                }
                            }
                            //将缓冲区中数据渲染到surface,render=true就会渲染到surface
                            decoder.releaseOutputBuffer(outIndex, true);
                            break;
                    }
    
                    // All decoded frames have been rendered, we can stop playing now
                    if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                        Log.d("DecodeActivity", "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
                        break;
                    }
                }
    

    5.释放资源

     decoder.stop();
                decoder.release();
                extractor.release();
    

    总结

    了解mediacodec和MediaExtractor的api和原理,按视频播放的步骤进行解码播放。

    学习一步一个脚印,谢谢今天的自己。

    相关文章

      网友评论

          本文标题:Android之MediaCodec视频解码

          本文链接:https://www.haomeiwen.com/subject/ucfqvqtx.html