Android MediaCodec学习笔记

目录

1. 模块介绍

1.1 基本概念

1.2 软件层次

2. 工作流程

2.1 API使用

2.3 控制流

2.3.1.createByType

2.3.2.configure

2.3.3.start

2.3.4.queue/dequeue buffer

2.3.5.stop

2.3.6.release

2.4.数据流

2.4.1.申请

2.4.2.传输

2.4.3.释放

3.参考


1. 模块介绍

1.1 基本概念

        OpenMAX是Khronos Group提出的标准,目标在于创造一个统一的接口,加速大量多媒体资料的处理。OpenMAX分成三层:应用层(Application Layer, AL), 整合层(Integration Layer, IL) 以及开发层(Development Layer, DL)。

OpenMAX (Open Media Acceleration)

OpenMAX AL

Application Layer 应用层

OpenMAX IL

Integration Layer 整合层

OpenMAX DL

Development Layer 开发层

  • OpenMAX AL是多媒体应用程式(如Media Player)和平台媒体框架之间的接口;
  • OpenMAX IL是多媒体框架(如MediaCodec)和多媒体元件之间接口;
  • OpenMAX DL是实体硬件(如CPU)和驱动之间的接口;

        MediaCodec是在android 4.1(API level 16)时引入的API,可用于在Java层访问硬件多媒体encoder/decoder。MediaCodec使用OpenMAX IL接口来实现。

1.2 软件层次

        Android MediaCodec和OpenMAX IL的对应关系如下:

Android

OpenMAX IL

APK

MediaCodec ( JAVA/JNI/Native )

ACodec

IL client

libstagefrighthw

IL plugin

libOmxCore

IL core

libOmxVdec/Venc

IL component

        其中:

        MediaCodec分为3部分:Java、JNI和Native。Java是上层apk使用的接口;JNI是Java访问Native的JNI方法;Nat        ive是ACodec的wrapper;

        ACodec是OpenMAX IL中的OpenMAX IL client;

        Libstagefrighthw是厂商的OpenMAX IL plugin;

        libOMXCore是OpenMAX IL中的core;

        libOMXVdec和libOMXVenc是OpenMAX IL中的component。

1.3 源码结构

android/frameworks/base/media/java/android/media/

        Api层(MediaCodec.java)

android/frameworks/base/core/jni/

        JNI层(android_media_MediaCodec.cpp)

android/frameworks/av/media/libstagefright/

        Native层(MediaCodec.cpp)

android/hardware/qcom/media/libstagefrighthw/

        libstagefrighthw实现

android/hardware/qcom/media/mm-core/

        libOMXCore实现

android/hardware/qcom/media/mm-video-v4l2/vidc/

        libOMXVdec/libOMXVenc实现

        注意:本文基于Android5.0 API总结。

2. 工作流程

2.1 API使用

ediaCodec API的使用demo如下:

MediaCodec codec = MediaCodec.createByType(type);
 codec.configure(format, ...);
 codec.start();
 ByteBuffer[] inputBuffers = codec.getInputBuffers();
 ByteBuffer[] outputBuffers = codec.getOutputBuffers();
 for (;;) {
   int inputBufferIndex = codec.dequeueInputBuffer(timeoutUs);
   if (inputBufferIndex >= 0) {
     // fill inputBuffers[inputBufferIndex] with valid data
     ...
     codec.queueInputBuffer(inputBufferIndex, ...);
   }

   int outputBufferIndex = codec.dequeueOutputBuffer(timeoutUs);
   if (outputBufferIndex >= 0) {
     // outputBuffer is ready to be processed or rendered.
     ...
     codec.releaseOutputBuffer(outputBufferIndex, ...);
   } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
     outputBuffers = codec.getOutputBuffers();
   } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
     // Subsequent data will conform to new format.
     MediaFormat format = codec.getOutputFormat();
     ...
   }
 }
 codec.stop();
 codec.release();
 codec = null;
  • MediaCodec.configure()和MediaCodec.start()初始化;
  • MediaCodec.getInputBuffers()和MediaCodec.getOutputBuffers()取出MediaCodec的input/output buffer;
  • MediaCodec.dequeueInputBuffer()和MediaCodec.queueInputBuffer()用于将码流传进MediaCodec;
  • MediaCodec.dequeueOutputBuffer()和MediaCodec.releaseOutputBuffer()用于取得MediaCodec传出来的frame buffer;
  • MediaCodec.stop()和MediaCodec.release()结束;

        Apk与MediaCodec的交互如下:

Android MediaCodec学习笔记_第1张图片

2.2 状态

        ACodec中定义的状态机如下:

Android MediaCodec学习笔记_第2张图片

        OpenMAX IL中定义的状态机如下:

Android MediaCodec学习笔记_第3张图片

        Acodec和OpenMAX IL状态机不尽相同:少了WaitForResources State和Paused State,多了Flushing State,简述如下:

        Uninitialized:最开始的状态,未初始化;

        Loaded:打开了相应的component;

        Idle:申请了in port和out port的input buffer和output buffer;

        Executing:开始读入input buffer,写到output buffer,即component开始处理数据;

        Flushing:清空input buffer和output buffer。

        

2.3 控制流

        本节以H265 Video Decoder为例子,分别讨论MediaCodec API的实现。createByType、configure、start、queue/dequeue buffer、stop、release。


2.3.1.createByType

Android MediaCodec学习笔记_第4张图片

        createByType根据type="video/hevc",(1)选择相应的decoder component name="OMX.xxx. video.hevc.decoder",(2)将动态库libstagefright.so/libOmxCore.so/libOmxVdecHevc.so加载,OMX API映射建立。ACodec state从UninitializedState改成LoadedState。Component state从OMX_StateInvalid改成OMX_StateLoaded。


2.3.2.configure

Android MediaCodec学习笔记_第5张图片

        configure()向component调用setParameter配置参数。
        注意:紫色API为显示相关操作。


2.3.3.start

        start()时,client将component状态设置为OMX_StateIdle,然后在本地申请出input buffer,NativeWindow dequeue出output buffer。Buffer申请完成后,client将component状态设置为OMX_StateExecuting。使用OMX->useBuffer()将这些buffer传给component,再使用useGraphicBuffer ()将output buffer的使用权交给component。最后client状态设置为ExecutingState。


2.3.4.queue/dequeue buffer

        queueBuffer和dequeueBuffer是一个OMX buffer管理的机制。

  • emptyBuffer:Client通过调用此函数传递input Buffer给Component,让其读取其中的数据进行编解码等处理。此函数会调用OMX标准接口OMX_ EmptyThisBuffer ()。
  • fillBuffer:Client通过调用此函数传递空的output Buffer给Component,让其将处理好的数据填入其中。此函数会调用OMX标准接口OMX_FillThisBuffer()。
  • OnFillBufferDone:Component完成相应处理将输出数据填入output Buffer后,调用此回调函数,向Client发送FillBufferDone消息。
  • OnEmptyBufferDone:Component完成对input buffer的读取后,调用此回调函数,向Client发送EmptyBufferDone消息。

        ACodec通过emptyBuffer()/OnEmptyBufferDone()/fillBuffer()/OnFillBufferDone()来实现input buffer与output buffer的管理,也使component能从input buffer中读取数据,往output buffer中填充数据。


2.3.5.stop

        stop()后,client状态变为LoadedState;component的state先设置为OMX_StateIdle,stop结束后再设置为OMX_StateLoaded。stop()是start()的逆操作。


2.3.6.release

Android MediaCodec学习笔记_第6张图片

        release()卸载相应动态库,并将client状态设置为UninitializedState,是createByType()的逆操作。


2.4.数据流


        以hevc/video decoder为例子,OMX component Name="OMX.xxx. video.hevc.decoder" ,对应libOmxVdecHevc。将input buffer的H265裸码流bit stream,解码为output buffer的YUV 数据frame buffer,component本身包含input port和output port作为数据传输通道,示意图如下:

Android MediaCodec学习笔记_第7张图片

2.4.1.申请


        内存的申请是在ACodec中完成,即在OMX IL的client中完成,具体是在start()函数中完成的。
        Input:
        1.使用getParameter(OMX_IndexParamPortDefinition, input)取得nBufferCountActual和nBufferSize。
        2.在ACodec中使用MemoryDealer申请出nBufferCountActual个InputBuffer共享内存;
        Output:
        如果支持StoreMetaDataInOutputBuffers,只申请出MetaDataBuffer,否则向NativeWindow申请出frame buffer内存。
        1.使用getParameter(OMX_IndexParamPortDefinition, output)取得nBufferCountActual和nBufferSize;
        2.使用mNativeWindow->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS)取得window需要的最少buffer数。
        3.使用native_window_dequeue_buffer_and_wait()申请出最少buffer数目 + 1个frame buffer,native_window_set_buffers_geometry()配置frame buffer的大小。


2.4.2.传输


        1.申请出buffer后,ACodec会将所有output buffer使用FillThisBuffer()传到component的output port;
        2.在apk填充完input buffer调用MediaCodec.queueInputBuffer()时,ACodec会将input buffer通过EmptyThisBuffer()传到input port;
        3.component读取完input buffer中的数据后,调用EmptyBufferDone()将input buffer使用权交回ACodec;
        4.component完成一帧数据的处理后填充output buffer,填充完成后调用FillBufferDone()将output buffer使用权交回ACodec;
        5.这样通过这4个函数完成数据传输;
        示意图如下:

Android MediaCodec学习笔记_第8张图片

2.4.3.释放


        内存的释放是在ExecutingToIdleState::changeStateIfWeOwnAllBuffers()中完成的,即在变成Idle状态前完成。
        1.MemoryDealer调用clear(),释放input buffer;
        2.cancelBufferToNativeWindow()将从Native Window中申请的Buffer还回到Native Window,释放output buffer。


3.参考


        1.《OpenMAX_IL_1_2_0_Specification.pdf》
        2. Android5.0 Codes

你可能感兴趣的:(android)