MediaCodec Java API 与 StageFright (2014-09-17 22:28:54)


转载
标签: 

stagefright

 

android

 

xbmc

 
Android App 通过 MediaCodec Java API 获得的编解码器,实际上是由 StageFright 媒体框架提供。android.media.MediaCodec 调用 libmedia_jni.so 中 JNI native 函数,这些 JNI 函数再去调用 libstagefright.so 库获得 StageFright 框架中的编解码器。

## MediaCodec Java 使用 libmedia_jni.so JNI native 函数

XBMC 通过 MediaCodec API 创建解码器时,使用了 android.media.MediaCodec 包中 createByCodecName(), configure() 和 start() 方法。

其中 createByCodecName() 为 MediaCodec 类静态函数,用于创建 MediaCodec 对象:

      frameworks/base/media/java/android/media/MediaCodec.java

      final public class MediaCodec {
            public static MediaCodec createByCodecName(String name) {
                  return new MediaCodec(
                              name, false , false );
            }

            private MediaCodec(
                        String name, boolean nameIsType, boolean encoder) {
                  native_setup(name, nameIsType, encoder);
            }

      }

在 MediaCodec 构造函数中,调用 JNI 函数 native_setup():

      frameworks/base/media/jni/android_media_MediaCodec.cpp
      
      static void android_media_MediaCodec_native_setup(
                  JNIEnv *env, jobject thiz,
                  jstring name, jboolean nameIsType, jboolean encoder) {

            const char *tmp = env->GetStringUTFChars(name, NULL);
            sp codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
            env->ReleaseStringUTFChars(name, tmp);
            
            setMediaCodec(env,thiz, codec);
      }

JNI 中 JMediaCodec 类封装了 StageFright 框架编解码器 API。

      struct JMediaCodec : public RefBase {
            JMediaCodec(
                        JNIEnv *env, jobject thiz,
                        const char *name, bool nameIsType, bool encoder);

            status_t initCheck() const;

            status_t configure(
                        const sp &format,
                        const sp &bufferProducer,
                        const sp &crypto,
                        int flags);

            status_t start();

            status_t queueInputBuffer(
                        size_t index,
                        size_t offset, size_t size, int64_t timeUs, uint32_t flags,
                        AString *errorDetailMsg);
            ...
            }

JMediaCodec 对象创建时通过调用 StageFright 框架中的 MediaCodec 函数获得解码器对象。

      JMediaCodec::JMediaCodec(
                  JNIEnv *env, jobject thiz,
                  const char *name, bool nameIsType, bool encoder)
            : mClass(NULL),
               mObject(NULL) {

            mLooper = new ALooper;

            if (nameIsType) {
                  mCodec = MediaCodec::CreateByType(mLooper, name, encoder);
            } else {
                  mCodec = MediaCodec::CreateByComponentName(mLooper, name);
            }
      }

MediaCodec Java API 中 configure() 和 start() 等方法也使用类似的方式,通过 JNI 访问 StageFright 框架函数:

      frameworks/base/media/java/android/media/MediaCodec.java
      
            public void configure(
                        MediaFormat format,
                        Surface surface, MediaCrypto crypto, int flags) {
                  Map formatMap = format.getMap();

                  String[] keys = null;
                  Object[] values = null;

                  if (format != null) {
                        keys = new String[formatMap.size()];
                        values = new Object[formatMap.size()];

                        int i = 0;
                        for (Map.Entry entry: formatMap.entrySet()) {
                              keys[i] = entry.getKey();
                              values[i] = entry.getValue();
                              ++i;
                        }
                  }

                  native_configure(keys, values, surface, crypto, flags);
            }

            public native final void start();
      }

native_configure() 和 start() 使用的 JMediaCodec 对象 codec 在 native_setup() 中创建。

      frameworks/base/media/jni/android_media_MediaCodec.cpp
      
      static void android_media_MediaCodec_native_configure(
                  JNIEnv *env,
                  jobject thiz,
                  jobjectArray keys, jobjectArray values,
                  jobject jsurface,
                  jobject jcrypto,
                  jint flags) {

            sp codec = getMediaCodec(env, thiz);

            sp format;
            status_t err = ConvertKeyValueArraysToM essage(env, keys, values, &format);

            sp bufferProducer;
            if (jsurface != NULL) {
                  sp surface(android_view_Surface_getSurface(env, jsurface));
                  if (surface != NULL) {
                        bufferProducer = surface->getIGraphicBufferProduce r();
                  }
            }

            err = codec->configure(format, bufferProducer, crypto, flags);
      }

      status_t JMediaCodec::configure(
                  const sp &format,
                  const sp &bufferProducer,
                  const sp &crypto,
                  int flags) {
            sp client;
            if (bufferProducer != NULL) {
                  mSurfaceTextureClient = new Surface(bufferProducer, true );
            } else {
                  mSurfaceTextureClient.clear();
            }

            return mCodec->configure(format, mSurfaceTextureClient, crypto, flags);
      }

      static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
            sp codec = getMediaCodec(env, thiz);

            status_t err = codec->start();
      }

      status_t JMediaCodec::start() {
            return mCodec->start();
      }

## StageFright MediaCodec

StageFright 框架支持 OpenMAX IL 接口的编解码器,硬件厂商只需提供 OMX 接口的组件就可以为 Android 平台上提供通用的硬件编解码支持。StageFright 支持软件和硬件实现的编解码器, 如果一种编码有多种解码器组件,用户可以配置 /etc/media_codecs.xml 进行选择。

使用 OMX 组件有两种模式:一是阻塞式的,StageFright 用 OMXCodec 类封装相应函数;另一种是非阻塞式的,用 ACodec 类封装。Android 提供的 MediaPlayer 采用的阻塞式方法,MediaCodec 则使用非阻塞式的方法。

非阻塞式方法通过异步函数调用实现,调用程序使用解码组件时,不等待处理结束直接返回,然后借助消息处理机制,解码组件处理完数据后会发送消息给调用程序,这时调用程序再处理解码后的数据。

MediaCodec 结构继承自消息处理类 AHandler:

      frameworks/av/include/media/stagefright/MediaCodec.h

      struct MediaCodec : public AHandler {
            static sp CreateByComponentName(
                        const sp &looper, const char *name);

            status_t configure(
                        const sp &format,
                        const sp &nativeWindow,
                        const sp &crypto,
                        uint32_t flags);

            status_t start();
      }

通过 MediaCodec 静态函数 CreateByComponentName() 创建 MediaCodec 对象:

      frameworks/av/media/libstagefright/MediaCodec.cpp
      
      // static
      sp MediaCodec::CreateByComponentName(
                  const sp &looper, const char *name) {
            sp codec = new MediaCodec(looper);
            if (codec->init(name, false , false ) != OK) {
                  return NULL;
            }

            return codec;
      }

MediaCodec 构造函数中会创建 ACodec 对象:

      MediaCodec::MediaCodec(const sp &looper)
            : mState(UNINITIALIZED),
               mLooper(looper),
               mCodec(new ACodec),
               mReplyID(0),
               mFlags(0),
               mSoftRenderer(NULL),
               mDequeueInputTimeoutGene ration(0),
               mDequeueInputReplyID(0),
               mDequeueOutputTimeoutGen eration(0),
               mDequeueOutputReplyID(0),
               mHaveInputSurface(false) {
      }

MediaCodec 对象初始化时先使用 MediaCodecList 获取编解码器组件列表,从中查询对应的解码器组件,然后告诉 ACodec 回调的通知消息 kWhatCodecNotify,发送 kWhatInit 消息到消息队列后返回:

      status_t MediaCodec::init(const char *name, bool nameIsType, bool encoder) {
            bool needDedicatedLooper = false;
            if (nameIsType && !strncasecmp(name, "video/", 6)) {
                  needDedicatedLooper = true;
            } else {
                  AString tmp = name;
                  const MediaCodecList *mcl = MediaCodecList::getInstance();
                  ssize_t codecIdx = mcl->findCodecByName(tmp.c_str());
            }

            mCodec->setNotificationMessage(new AMessage(kWhatCodecNotify, id()));

            sp msg = new AMessage(kWhatInit, id());
            msg->setString("name", name);
            msg->setInt32("nameIsType", nameIsType);

            sp response;
            return PostAndAwaitResponse(msg, &response);
      }

在 MediaCodec 的消息处理 onMessageRececived() 函数中对 KWhatInit 消息进行处理,包括设置状态,调用 ACodec 对象 mCodec->initiateAllocateComponen t() 函数。

      void MediaCodec::onMessageReceived(const sp &msg) {
            switch (msg->what()) {
                  case kWhatInit:
                  {
                        mReplyID = replyID;
                        setState(INITIALIZING);

                        AString name;
                        CHECK(msg->findString("name", &name));

                        int32_t nameIsType;
                        int32_t encoder = false;
                        CHECK(msg->findInt32("nameIsType", &nameIsType));
                        if (nameIsType) {
                              CHECK(msg->findInt32("encoder", &encoder));
                        }

                        sp format = new AMessage;

                        if (nameIsType) {
                              format->setString("mime", name.c_str());
                              format->setInt32("encoder", encoder);
                        } else {
                              format->setString("componentName", name.c_str());
                        }

                        mCodec->initiateAllocateComponen t(format);
                        break;
                  }
                  ...
            }
      }

ACodec 初始化并为组件分配内存动作完成后,发送 ACodec::kWhatComponentAllocated 消息通知MediaCodec,MediaCodec 的 onMessageReceived() 中再对此消息进行响应:

                  case kWhatCodecNotify:
                  {
                        int32_t what;
                        CHECK(msg->findInt32("what", &what));

                        switch (what) {
                              
                              case ACodec::kWhatComponentAllocated:
                              {
                                    CHECK_EQ(mState, INITIALIZING);
                                    setState(INITIALIZED);

                                    CHECK(msg->findString("componentName", &mComponentName));

                                    if (mComponentName.startsWith("OMX.google.") ||
                                                mComponentName.startsWith("OMX.ffmpeg.")) {
                                          mFlags |= kFlagIsSoftwareCodec;
                                    } else {
                                          mFlags &= ~kFlagIsSoftwareCodec;
                                    }

                                    if (mComponentName.endsWith(".secure")) {
                                          mFlags |= kFlagIsSecure;
                                    } else {
                                          mFlags &= ~kFlagIsSecure;
                                    }

                                    (new AMessage)->postReply(mReplyID);
                                    break;
                              }
                              ...
                        }
                  }

configure() 和 start() 函数也使用同样方式分别发送 kWhatConfigure 和 kWhatStart 消息到消息队列

      status_t MediaCodec::configure(
                  const sp &format,
                  const sp &nativeWindow,
                  const sp &crypto,
                  uint32_t flags) {
            sp msg = new AMessage(kWhatConfigure, id());

            msg->setMessage("format", format);
            msg->setInt32("flags", flags);

            if (nativeWindow != NULL) {
                  msg->setObject(
                              "native-window",
                              new NativeWindowWrapper(nativeWindow));
            }

            sp response;
            return PostAndAwaitResponse(msg, &response);
      }

      status_t MediaCodec::start() {
            sp msg = new AMessage(kWhatStart, id());

            sp response;
            return PostAndAwaitResponse(msg, &response);
      }

消息处理函数中调用 mCodec->initiateConfigureCompone nt(format) 和 mCodec->initiateStart():

                  case kWhatConfigure:
                  {
                        uint32_t replyID;
                        CHECK(msg->senderAwaitsResponse(&replyID));

                        mCodec->initiateConfigureCompone nt(format);
                        break;
                  }

                  case kWhatStart:
                  {
                        uint32_t replyID;
                        CHECK(msg->senderAwaitsResponse(&replyID));

                        mReplyID = replyID;
                        setState(STARTING);

                        mCodec->initiateStart();
                        break;
                  }

然后再响应 ACodec 返回的消息,其中 initiateStart() 完成后没有消息通知 MediaCodec。

                              case ACodec::kWhatComponentConfigured :
                              {
                                    CHECK_EQ(mState, CONFIGURING);
                                    setState(CONFIGURED);

                                    // reset input surface flag
                                    mHaveInputSurface = false;

                                    (new AMessage)->postReply(mReplyID);
                                    break;
                              }

你可能感兴趣的:(MediaCodec Java API 与 StageFright (2014-09-17 22:28:54))