MediaCodec(四) 从MediaCodec到Codec2之create Competent(1)

MediaCodec.java 中有几个创建组件的API 可用:

1. JAVA 层

其实 createDecoderByType 和 createByCodecName 的实现是一样的,都是创建了 MediaCodec 对象,只是nameIsType 这个参数 值不一样:

 /frameworks/base/media/java/android/media/MediaCodec.java
1977      /**
1978       * Instantiate the preferred decoder supporting input data of the given mime type.
2001       * @param type The mime type of the input data.
2005       */
2006      @NonNull
2007      public static MediaCodec createDecoderByType(@NonNull String type)
2008              throws IOException {
2009          return new MediaCodec(type, true /* nameIsType */, false /* encoder */);
2010      }
==================
2030      /**
2031       * If you know the exact name of the component you want to instantiate
2032       * use this method to instantiate it. Use with caution.
2033       * Likely to be used with information obtained from {@link android.media.MediaCodecList}
2038       */
2039      @NonNull
2040      public static MediaCodec createByCodecName(@NonNull String name)
2041              throws IOException {
2042          return new MediaCodec(name, false /* nameIsType */, false /* encoder */);
2043      }

MediaCodec 对象初始化主要就是 创建了 Looper/Handler 然后就是调用native_setup 继续往下去初始化;

 /frameworks/base/media/java/android/media/MediaCodec.java
2071      private MediaCodec(@NonNull String name, boolean nameIsType, boolean encoder) {
2072          this(name, nameIsType, encoder, -1 /* pid */, -1 /* uid */);
2073      }
====================
2075      private MediaCodec(@NonNull String name, boolean nameIsType, boolean encoder, int pid,
2076              int uid) {
2077          Looper looper;  //创建 Looper
2078          if ((looper = Looper.myLooper()) != null) {
2079              mEventHandler = new EventHandler(this, looper);  //创建Handler
2080          } else if ((looper = Looper.getMainLooper()) != null) {
2081              mEventHandler = new EventHandler(this, looper);
2082          } else {
2083              mEventHandler = null;
2084          }
2085          mCallbackHandler = mEventHandler;
2086          mOnFirstTunnelFrameReadyHandler = mEventHandler;
2087          mOnFrameRenderedHandler = mEventHandler;
2088  
2089          mBufferLock = new Object();
2090  
2091          // save name used at creation
2092          mNameAtCreation = nameIsType ? null : name;
2093  
2094          native_setup(name, nameIsType, encoder, pid, uid);
2095      }

2. JNI

native_setup  调用到了JNI ,再来看JNI 的实现

 /frameworks/base/media/jni/android_media_MediaCodec.cpp
3787      { "native_setup", "(Ljava/lang/String;ZZII)V",
3788        (void *)android_media_MediaCodec_native_setup },
=====================
3449  static void android_media_MediaCodec_native_setup(
3450          JNIEnv *env, jobject thiz,
3451          jstring name, jboolean nameIsType, jboolean encoder, int pid, int uid) {
3463      sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder, pid, uid);
3464  
3465      const status_t err = codec->initCheck();
... //一些异常判断
3495  
3496      codec->registerSelf();
3497  
3498      setMediaCodec(env, thiz, codec);
===================
214  JMediaCodec::JMediaCodec(
215          JNIEnv *env, jobject thiz,
216          const char *name, bool nameIsType, bool encoder, int pid, int uid)
217      : mClass(NULL),
218        mObject(NULL) {
224  
225      mLooper = new ALooper;
226      mLooper->setName("MediaCodec_looper");
227  
228      mLooper->start(
229              false,      // runOnCallingThread
230              true,       // canCallJava
231              ANDROID_PRIORITY_VIDEO);
232  
233      if (nameIsType) {
234          mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus, pid, uid);
238      } else {
239          mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus, pid, uid);
240          mNameAtCreation = name;
241      }
242      CHECK((mCodec != NULL) != (mInitStatus != OK));
243  }

这边 JNI 根据 nameIsType 分了两个API CreateByType & CreateByComponentName

3. C++

JNI 往下就是调用到 MediaCodec.cpp 中的 CreateByType :看了下CreateByType 的实现:

  1. 首先 findMatchingCodecs 去寻找匹配的matchingCodecs,可能会找到多个;
  2. 然后根据找到的 matchingCodecs 循环去创建MediaCodec 知道创建成功就返回 Codec;
 /frameworks/av/media/libstagefright/MediaCodec.cpp
911  sp<MediaCodec> MediaCodec::CreateByType(
912          const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,
913          uid_t uid) {
914      sp<AMessage> format;
915      return CreateByType(looper, mime, encoder, err, pid, uid, format);
916  }
=================
918  sp<MediaCodec> MediaCodec::CreateByType(
919          const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,
920          uid_t uid, sp<AMessage> format) {
921      Vector<AString> matchingCodecs;
922  
923      MediaCodecList::findMatchingCodecs(
924              mime.c_str(),
925              encoder,
926              0,
927              format,
928              &matchingCodecs); //寻找匹配的 Codecs
929  
930      if (err != NULL) {
931          *err = NAME_NOT_FOUND;
932      }
933      for (size_t i = 0; i < matchingCodecs.size(); ++i) {
934          sp<MediaCodec> codec = new MediaCodec(looper, pid, uid); //创建 MediaCodec
935          AString componentName = matchingCodecs[i];
936          status_t ret = codec->init(componentName); //初始化 component
937          if (err != NULL) {
938              *err = ret;
939          }
940          if (ret == OK) {
941              return codec;
942          }
943          ALOGD("Allocating component '%s' failed (%d), try next one.",
944                  componentName.c_str(), ret);
945      }
946      return NULL;
947  }

Step1: findMatchingCodecs 的实现

 /frameworks/av/media/libstagefright/MediaCodecList.cpp
349  void MediaCodecList::findMatchingCodecs(
350          const char *mime, bool encoder, uint32_t flags,
351          Vector<AString> *matches) {
352      sp<AMessage> format;        // initializes as clear/null
353      findMatchingCodecs(mime, encoder, flags, format, matches);
354  }

 findMatchingCodecs 主要是为了根据 mime 找到 匹配的Codec 用于解码

==> 准备单独开一篇来分析寻找匹配的Codecs 以及 Codecs 的中的支持的格式是如何加载的 《MediaCodec(五) findMatchingCodecs 的流程及组件加载

Step2: 找到匹配的Codecs 后就会去 new MediaCodec

579      MediaCodec(
580              const sp<ALooper> &looper, pid_t pid, uid_t uid,
581              std::function<sp<CodecBase>(const AString &, const char *)> getCodecBase = nullptr,
582              std::function<status_t(const AString &, sp<MediaCodecInfo> *)> getCodecInfo = nullptr);
=========================
 /frameworks/av/media/libstagefright/MediaCodec.cpp
1006  MediaCodec::MediaCodec(
1007          const sp<ALooper> &looper, pid_t pid, uid_t uid,
1008          std::function<sp<CodecBase>(const AString &, const char *)> getCodecBase,
1009          std::function<status_t(const AString &, sp<MediaCodecInfo> *)> getCodecInfo)
1010      : mState(UNINITIALIZED),  // UNINITIALIZED
1012        mLooper(looper),
1013        mCodec(NULL),
1049        mGetCodecInfo(getCodecInfo) {
1050      mCodecId = GenerateCodecId();
1051      mResourceManagerProxy = new ResourceManagerServiceProxy(pid, uid,
1052              ::ndk::SharedRefBase::make<ResourceManagerClient>(this, pid, uid));
1053      if (!mGetCodecBase) {
1054          mGetCodecBase = [](const AString &name, const char *owner) {
1055              return GetCodecBase(name, owner);
1056          };
1057      }
1058      if (!mGetCodecInfo) {
1059          mGetCodecInfo = [&log = mErrorLog](const AString &name,
1060                                             sp<MediaCodecInfo> *info) -> status_t {
1061              *info = nullptr;
1062              const sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
1067              AString tmp = name;
1068              if (tmp.endsWith(".secure")) {
1069                  tmp.erase(tmp.size() - 7, 7);
1070              }
1071              for (const AString &codecName : { name, tmp }) {
1072                  ssize_t codecIdx = mcl->findCodecByName(codecName.c_str());
1076                  *info = mcl->getCodecInfo(codecIdx);
1077                  return OK;
1078              }
1079              log.log(LOG_TAG, base::StringPrintf("Codec with name '%s' is not found on the device.",
1080                                    name.c_str()));
1081              return NAME_NOT_FOUND;
1082          };
1083      }
1084  
1085      // we want an empty metrics record for any early getMetrics() call
1086      // this should be the *only* initMediametrics() call that's not on the Looper thread
1087      initMediametrics();

(1)  GenerateCodecId  看起来为 Codec 生成一个 随机的 ID (用于判别Codec)

989  // GenerateCodecId generates a 64bit Random ID for each codec that is created.
990  // The Codec ID is generated as:
991  //   - A process-unique random high 32bits
992  //   - An atomic sequence low 32bits
993  //
994  static uint64_t GenerateCodecId() {
995      static std::atomic_uint64_t sId = [] {
996          std::random_device rd;
997          std::mt19937 gen(rd());
998          std::uniform_int_distribution<uint32_t> distrib(0, UINT32_MAX);
999          uint32_t randomID = distrib(gen);
1000          uint64_t id = randomID;
1001          return id << 32;
1002      }();
1003      return sId++;
1004  }

(2)mGetCodecInfo = 这步主要是给 mGetCodecInfo 赋值函数实现 目前还不会运行;

简单看下主要是为了在 MediaCodecList 中 findCodecByName(codecName.c_str());  找到设备中匹配的 Codec 获取 CodecInfo

 /frameworks/av/media/libstagefright/MediaCodec.cpp
294  ssize_t MediaCodecList::findCodecByName(const char *name) const {
295      Vector<AString> aliases;
296      for (size_t i = 0; i < mCodecInfos.size(); ++i) {
297          if (strcmp(mCodecInfos[i]->getCodecName(), name) == 0) { //比较Codec名字
298              return i;
299          }
300          mCodecInfos[i]->getAliases(&aliases); //比较别名
301          for (const AString &alias : aliases) {
302              if (alias == name) {
303                  return i;
304              }
305          }
306      }
307  
308      return -ENOENT;
309  }
===================
3216  status_t MediaCodec::getCodecInfo(sp<MediaCodecInfo> *codecInfo) const {
3217      sp<AMessage> msg = new AMessage(kWhatGetCodecInfo, this);
3218  
3219      sp<AMessage> response;
3220      status_t err;
3221      if ((err = PostAndAwaitResponse(msg, &response)) != OK) { //Aloop 发送消息
3222          return err;
3223      }
3224  
3225      sp<RefBase> obj;
3226      CHECK(response->findObject("codecInfo", &obj));
3227      *codecInfo = static_cast<MediaCodecInfo *>(obj.get());
3228  
3229      return OK;
3230  }
--------------
5294          case kWhatGetCodecInfo:
5295          {
5296              sp<AReplyToken> replyID;
5297              CHECK(msg->senderAwaitsResponse(&replyID));
5298  
5299              sp<AMessage> response = new AMessage;
5300              response->setObject("codecInfo", mCodecInfo);  
//获取到的结果就是 mCodecInfo,而 mCodecInfo 是在 MediaCodec::init 中初始化的
5301              response->postReply(replyID);
5302              break;
5303          }

(3)initMediametrics  “初始化媒体指标”:看起来是在初始化一些变量

1105  // except for in constructor, called from the looper thread (and therefore mutexed)
1106  void MediaCodec::initMediametrics() {
1107      if (mMetricsHandle == 0) {
1108          mMetricsHandle = mediametrics_create(kCodecKeyName);
1109      }
1110  
1111      mLatencyHist.setup(kLatencyHistBuckets, kLatencyHistWidth, kLatencyHistFloor);
1112  
1113      {
1114          Mutex::Autolock al(mRecentLock);
1115          for (int i = 0; i<kRecentLatencyFrames; i++) {
1116              mRecentSamples[i] = kRecentSampleInvalid;
1117          }
1118          mRecentHead = 0;
1119      }
1120  
1121      {
1122          Mutex::Autolock al(mLatencyLock);
1123          mBuffersInFlight.clear();
1124          mNumLowLatencyEnables = 0;
1125          mNumLowLatencyDisables = 0;
1126          mIsLowLatencyModeOn = false;
1127          mIndexOfFirstFrameWhenLowLatencyOn = -1;
1128          mInputBufferCounter = 0;
1129      }
1130  
1131      mLifetimeStartNs = systemTime(SYSTEM_TIME_MONOTONIC);
1132      resetMetricsFields();
1133  }

到这边 new MediaCodec 就看完了,主要作用就是初始化一些变量和函数,同时根据name 获取到设备支持的Codec

Step 3:codec->init(componentName); 这一步内容非常多也很重要,在第二篇里去看《MediaCodec(六) 从MediaCodec 到 Codec2 之 createCompetent(2)

Android中从MediaCodec读取数据并将其存储到AVFrame中,需要进行以下步骤: 1. 初始化AVFrame 在使用AVFrame之前,需要先初始化它。可以使用下面的代码初始化AVFrame: ``` AVFrame *frame = av_frame_alloc(); frame->format = AV_PIX_FMT_YUV420P; // 设置像素格式 frame->width = width; // 设置帧宽度 frame->height = height; // 设置帧高度 ``` 2. 从MediaCodec读取数据 可以使用MediaCodec的dequeueOutputBuffer()方法从MediaCodec读取输出缓冲区。以下是一个示例代码: ``` MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US); while (outputBufferIndex >= 0) { ByteBuffer outputBuffer = mediaCodec.getOutputBuffer(outputBufferIndex); if (outputBuffer != null) { // 将数据存储到AVFrame中 // ... // 释放输出缓冲区 mediaCodec.releaseOutputBuffer(outputBufferIndex, false); } outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US); } ``` 在从MediaCodec读取数据后,可以将数据存储到AVFrame中。以下是一个示例代码: ``` byte[] yuvData = new byte[width * height * 3 / 2]; outputBuffer.get(yuvData); // 将数据存储到AVFrame中 frame->data[0] = yuvData; // Y数据 frame->data[1] = yuvData + width * height; // U数据 frame->data[2] = yuvData + width * height * 5 / 4; // V数据 // 设置AVFrame的时间戳 frame->pts = bufferInfo.presentationTimeUs * AV_TIME_BASE / 1000000; ``` 在存储完数据后,需要释放MediaCodec的输出缓冲区。最后,我们可以在循环中继续读取数据,直到读取完所有数据。 需要注意的是,在从MediaCodec读取数据时,需要根据实际情况进行一些设置,例如超时时间和输出缓冲区的标志位等。另外,需要根据实际情况设置AVFrame的时间戳。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值