目录
  • 前言
  • MediaCodec
    • 编解码流程
    • 生命周期
  • 接口简介

    前言

    获取到音视频轨道(编解码格式),知道设备支持哪些编解码器,下一步就是创建编解码器去实现数据流的编解码过程了。在Android开发中提供了实现音视频编解码工具MediaCodec,针对对应音视频解码类型通过该类创建对应解码器就能实现对数据进行解码操作。

    MediaCodec

    MediaCodec所支持的数据类型:压缩的音视频数据、原始音频数据和原始视频数据。 首先show代码,紧接着之前MediaExtactor提取资源,MediaCodecList遍历支持格式,确认设置支持该资源格式后通过MediaCodec创建解码器(这里是做视频解码播放)。

    // 加载资源
    extractor.setDataSource(path);
    // 获取视频轨道
    int trackIndex = getTrackIndex(extractor,"video/");
    // 获取视频轨道参数
    MediaFormatInfo mediaFormatInfo = MediaFormatInfo.buildUpVideoMediaFormatInfo(extractor.getTrackFormat(trackIndex));
    // 选取上述的视频轨道
    extractor.selectTrack(trackIndex);
    MediaCodecInfo mediaCodecInfo = CodecInfoInstance.getInstance().selectDeCodec(mediaFormatInfo.getMime());
    // 判断设备是否支持该视频解码,创建视频编码器
    if(mediaCodecInfo != null){
        mediaCodec = MediaCodec.createDecoderByType(mediaFormatInfo.getMime());
        mediaCodec.configure(mediaFormatInfo.getMediaFormat(),surface,null,0);
    }

    编解码流程

    Android音视频开发之MediaCodec的使用教程

    • MediaCodec的功能其实很简单通过一个数据缓冲去,将数据填充到输入缓冲区给到Codec
    • Codec通过异步方式处理输入缓冲区数据将处理好数据填充到输出缓冲区
    • 客户端从输出缓冲区获取到处理好的数据去消费,最后把缓冲区返还给Codec

    部分代码实现

    • dequeueInputBuffer:从输入流队列取数据进行编码操作
    • getInputBuffers: 获取需要编码数据的输入队列 返回ByteBuffer数组
    • queueInoutBuffer: 输入流入队列
    • dequeueOutputBuffer: 从输入队列中取出编码操作结果数据
    • getOutPutBuffer: 获取编解码之后数据输出队列 返回一个ByterBuffer数组
    • releaseOutPutBuffer: 处理完成释放ByterBuffer数组
    while (!isEnd && !isInterrupted()){
        if (!mIsEOS) {
            mIsEOS = dequeueInputBuffers();
        }
        isEnd = dequeueOutputBuffers();
    }
    
    //输入缓冲区
    private boolean dequeueInputBuffers() {
        boolean isMediaEOS = false;
        //  等待编码器输入缓冲区数据出队
        int inputBufferId = mediaCodec.dequeueInputBuffer(TIMEOUT_US);
        if (inputBufferId >= 0) {
            // 获取缓存区数据
            ByteBuffer inputBuffer = mediaCodec.getInputBuffer(inputBufferId);
            // 读取数据 返回值sampleSize大于0表示还有数据,否则表示结束
            int sampleSize = extractor.readSampleData(inputBuffer, 0);
            if (sampleSize < 0) {
                // 数据末尾 必须再次调用queueInputBuffer使用BUFFER_FLAG_END_OF_STREAM标识符输入到编码器
                mediaCodec.queueInputBuffer(inputBufferId, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                isMediaEOS = true;
                MediaLogUtils.printI(TAG + "end of stream");
            } else {
                // 输入缓冲区数据入队
                mediaCodec.queueInputBuffer(inputBufferId, 0, sampleSize, extractor.getSampleTime(), 0);
                extractor.advance();
            }
        }
        return isMediaEOS;
    }
    
    //输出缓冲区
    private synchronized boolean dequeueOutputBuffers() {
        MediaCodec.BufferInfo outBufferInfo = new MediaCodec.BufferInfo();
        //输出缓冲区
        int outputBufferId = mediaCodec.dequeueOutputBuffer(outBufferInfo, TIMEOUT_US);
        switch (outputBufferId) {
            case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                MediaLogUtils.printI(TAG+"INFO_OUTPUT_FORMAT_CHANGED");
                break;
            case MediaCodec.INFO_TRY_AGAIN_LATER:
                MediaLogUtils.printI(TAG+ "INFO_TRY_AGAIN_LATER");
                break;
            case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                MediaLogUtils.printI(TAG+ "INFO_OUTPUT_BUFFERS_CHANGED");
                break;
            default:
                // 延迟解码
                decodeDelay(outBufferInfo, mStartMs);
                // 释放输出缓冲区数据 render为true渲染到surface上
                mediaCodec.releaseOutputBuffer(outputBufferId, true);
                break;
        }
        // 结尾
        if ((outBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
            MediaLogUtils.printI(TAG+"buffer stream end");
            return true;
        }
        return false;
    }

    生命周期

    MediaCodec生命周期分为三种状态:StoppedExecutingReleased

    • Stopped具有三种子状态:Uninitialized、Configured、Error
    • Executing具有三种子状态:Flushed、Running、End-of-Stream

    Android音视频开发之MediaCodec的使用教程

    Stopped

    Uninitialized: UninitializedMediaCodec创建后初始状态。可通过reset()复位到Uninitialized

    Configured: MediaCodec通过configure方法设置配置(编解码器类型等)进入到Configured状态。

    Error: MediaCodec发生异常情况下会进入Error状态。

    Executing

    Flush:MediaCodec调用start方法后进入FlushedMediaCodec就具备所有缓存能力。若可以在Executing,调用flush()回到Flushed状态。

    Running: 当第一次InputBuffer输入缓存被移除队列,MediaCodec就会进入到Running状态。

    End-of-Stream:将end-of-stream标记输入InputBuffer队列,MediaCodec就会进入到 End-of-Stream状态,MediaCodec就不再接收InputBuffer,但不影响输出队列OutBuffer产出直到end-of-stream标记输出为止(输入和输出中间是有一定处理时间)。

    接口简介

    createDecoderByType/createEncoderByType,创建解码器/编码器对象

    createByCodecName,根据编解码器名称创建

    configure,配置编解码器配置

    start,配置完成后需要执行start完成配置

    dequeueInputBuffer, 输入队列取数据编码操作

    queueInputBuffer,输入入队列

    dequeueOutputBuffer,从输出队列取出编码操作后的数据

    releaseOutputBuffer,释放输出队列释放ByteBuffer数据

    getInputBuffers,获取需要编码数据输入流队列,返回ByteBuffer数组

    getOutputBuffers,获取编解码后数据输出流队列,返回ByteBuffer数组

    flush,清空输入和输出端口

    stop,终止decode/encode会话

    release,释放编解码器资源

    参考文章

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。