Android音视频 MediaCodec框架-启动编码(4)

embedded/2024/10/24 2:43:19/

Android音视频 MediaCodec框架-启动编码

简述

上一节我们介绍了MediaCodec框架创建编码器流程,编解码的流程其实基本是一样的,只是底层的最终的实现组件不同,所以我们只看启动编码流程。

MediaCodec启动编码

从MediaCodec的start方法开始。
在这里插入图片描述

1.1 MediaCodec.start
调用jni方法native_start

public final void start() {native_start();
}

1.2 native_start
调用JMediaCodec的start方法。

static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {ALOGV("android_media_MediaCodec_start");sp<JMediaCodec> codec = getMediaCodec(env, thiz);// ... JMediaCodec状态检测// 详见1.3status_t err = codec->start();// ...
}

1.3 JMediaCodec::start
调用MediaCodec的start方法。

status_t JMediaCodec::start() {// 详见1.4return mCodec->start();
}

1.4 MediaCodec::start
发送kWhatStart的AMessage通知CCodec2 start,这个流程和init很类似,处理消息start的流程详见1.5

status_t MediaCodec::start() {sp<AMessage> msg = new AMessage(kWhatStart, this);sp<AMessage> callback;status_t err;std::vector<MediaResourceParcel> resources;resources.push_back(MediaResource::CodecResource(mFlags & kFlagIsSecure,toMediaResourceSubType(mDomain)));resources.push_back(MediaResource::GraphicMemoryResource(1));for (int i = 0; i <= kMaxRetry; ++i) {if (i > 0) {// ...sp<AMessage> response;err = PostAndAwaitResponse(mConfigureMsg, &response);// ...}// ...}return err;
}

1.5 MediaCodec::onMessageReceived
消息处理方法,调用CCodec的initiateStart

void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {// ...case kWhatStart:{// ...// 更新状态到STARTINGsetState(STARTING);// 调用CCodec的initiateStart,详见1.6mCodec->initiateStart();break;}// ...
}

1.6 CCodec::initiateStart
这里只是修改了状态到STARTING,然后做了消息转发。

void CCodec::initiateStart() {auto setStarting = [this] {Mutexed<State>::Locked state(mState);if (state->get() != ALLOCATED) {return UNKNOWN_ERROR;}state->set(STARTING);return OK;};if (tryAndReportOnError(setStarting) != OK) {return;}(new AMessage(kWhatStart, this))->post();
}

1.7 CCodec::onMessageReceived
调用CCodec::start方法。

void CCodec::onMessageReceived(const sp<AMessage> &msg) {// ...case kWhatStart: {// 调用start方法setDeadline(now, 1500ms, "start");start();break;}// ...
}

1.8 CCodec::start
该方法主要做了几件事:
调用Component的start方法,Codec2的Component都是基于SimpleC2Component实现的,SimpleC2Component中处理了一些状态管理的逻辑,不同的编解码组件继承了SimpleC2Component,实现生命周期回调方法,例如onInit等。
从CCodecConfig获取mOutputFormat或者inputFormat信息,前者是解码时会有,后者是编码才会有的。
调用CCodecBufferChannel::start。

void CCodec::start() {// ...// 调用Component start,Codec2的Component都是基于SimpleC2Component实现的,SimpleC2Component中处理了一些状态管理的逻辑// 不同的编解码最终实现都继承于SimpleC2Component,然后实现对应的生命周期回调方法做自己的事,由于我们H264的实现里没有做什么事,就不看了。c2_status_t err = comp->start();if (err != C2_OK) {mCallback->onError(toStatusT(err, C2_OPERATION_Component_start),ACTION_CODE_FATAL);return;}sp<AMessage> inputFormat;sp<AMessage> outputFormat;status_t err2 = OK;bool buffersBoundToCodec = false;{// CCodecConfig是在之前初始化的,里面有配置信息,这里mOutputFormat是解码时候才有的,而inputFormat则是编码的时候才有// mInputSurface是编码时候配置了InputSurface时传入的参数,表示编码数据来源于一个Surface// 我们之前在SurfaceFlinger章节说过,Surface是表示一个窗口,可以作为图像BufferQueue的一个生产者。Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);const std::unique_ptr<Config> &config = *configLocked;inputFormat = config->mInputFormat;outputFormat = config->mOutputFormat = config->mOutputFormat->dup();if (config->mInputSurface) {err2 = config->mInputSurface->start();config->mInputSurfaceDataspace = config->mInputSurface->getDataspace();}buffersBoundToCodec = config->mBuffersBoundToCodec;}if (err2 != OK) {mCallback->onError(err2, ACTION_CODE_FATAL);return;}// 调用CCodecBufferChannel::start,详见1.9err2 = mChannel->start(inputFormat, outputFormat, buffersBoundToCodec);if (err2 != OK) {mCallback->onError(err2, ACTION_CODE_FATAL);return;}// ... 更新状态std::map<size_t, sp<MediaCodecBuffer>> clientInputBuffers;// 根据input里numSlots数量,调用input->buffers->requestNewBuffer创建buffer填充clientInputBuffers。err2 = mChannel->prepareInitialInputBuffers(&clientInputBuffers);if (err2 != OK) {ALOGE("Initial preparation for Input Buffers failed");mCallback->onError(err2, ACTION_CODE_FATAL);return;}// 回调MediaCodec onStartCompletedmCallback->onStartCompleted();mChannel->requestInitialInputBuffers(std::move(clientInputBuffers));
}

1.9 CCodecBufferChannel::start
这个方法很长,主要是一些参数的初始化,分为编码和解码的情况,其中最重要的参数就是C2BlockPool和input/output。
以编码为例,input里面有一个buffers,这个buffers有多种类型,buffers会持有一个C2BlockPool来分配内存,而C2BlockPool又会通过Allocator分配C2Buffer,这里的Allocator也是在hal层,Allocator是通过AllocatorStore到hal获取到。
所以相当于在上层使用一个buffers来控制buffer到分配释放等,这个buffers类型比如LinearInputBuffers,GraphicInputBuffers,GraphicMetadataInputBuffers等,而buffers会通过C2BlockPool分配buffer,而C2BlockPool会通过Allocator到hal层获取buffer。
解码的情况类似,这里就不细说了。

status_t CCodecBufferChannel::start(const sp<AMessage> &inputFormat,const sp<AMessage> &outputFormat,bool buffersBoundToCodec) {C2StreamBufferTypeSetting::input iStreamFormat(0u);C2StreamBufferTypeSetting::output oStreamFormat(0u);C2ComponentKindSetting kind;C2PortReorderBufferDepthTuning::output reorderDepth;C2PortReorderKeySetting::output reorderKey;C2PortActualDelayTuning::input inputDelay(0);C2PortActualDelayTuning::output outputDelay(0);C2ActualPipelineDelayTuning pipelineDelay(0);C2SecureModeTuning secureMode(C2Config::SM_UNPROTECTED);// ... 参数初始化以及检测// 由C2AllocateStore来获取Allocation,而Allocation是用于分配buffer的,有不同类型的buffer,对应底层分配的内存可能也不同,例如dmastd::shared_ptr<C2AllocatorStore> allocatorStore = GetCodec2PlatformAllocatorStore();int poolMask = GetCodec2PoolMask();C2PlatformAllocatorStore::id_t preferredLinearId = GetPreferredLinearAllocatorId(poolMask);// 编码if (inputFormat != nullptr) {// ... 参数配置// 构造C2BlockPoolstd::shared_ptr<C2BlockPool> pool;{Mutexed<BlockPools>::Locked pools(mBlockPools);// ... 参数检查配置if ((poolMask >> pools->inputAllocatorId) & 1) {// 根据inputAllocatorId构造C2BlockPool,C2BlockPool里面持有Allocator,通过擦欧总Allocator管理Buffer的构建err = CreateCodec2BlockPool(pools->inputAllocatorId, nullptr, &pool);// ...} else {err = C2_NOT_FOUND;}// ...异常处理pools->inputPool = pool;}bool forceArrayMode = false;Mutexed<Input>::Locked input(mInput);// ...构造填充input对象// 其中input->buffers会根据buffer的类型不同而不同// 这里的关系是input->buffers会最终提供给CCodec分配Buffer的能力,而input->buffers是通过前面构造的C2BlockPool来获取或者释放buffer// 而C2BlockPool通过持有的Allocator来分配不同Buffer,不同的Buffer的区别在于底层可能使用不同的系统调用来分配内存,可能是dma之类的。input->buffers->setFormat(inputFormat);if (err == C2_OK) {input->buffers->setPool(pool);} else {// TODO: error}if (forceArrayMode) {input->buffers = input->buffers->toArrayMode(numInputSlots);}}// 解码,这里和上面编码类似,主要是初始化一些必要对象。if (outputFormat != nullptr) {sp<IGraphicBufferProducer> outputSurface;uint32_t outputGeneration;int maxDequeueCount = 0;{Mutexed<OutputSurface>::Locked output(mOutputSurface);maxDequeueCount = output->maxDequeueBuffers = numOutputSlots +reorderDepth.value + mRenderingDepth;outputSurface = output->surface ?output->surface->getIGraphicBufferProducer() : nullptr;if (outputSurface) {output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);}outputGeneration = output->generation;}bool graphic = (oStreamFormat.value == C2BufferData::GRAPHIC);C2BlockPool::local_id_t outputPoolId_;C2BlockPool::local_id_t prevOutputPoolId;{Mutexed<BlockPools>::Locked pools(mBlockPools);// ... 初始化BlockPools,和解码流程构造C2BlockPools类似。}Mutexed<Output>::Locked output(mOutput);// ... 构造output,和上面input类似// 如果接受输出是一个Surface,通知给Componentif (outputSurface) {mComponent->setOutputSurface(outputPoolId_,outputSurface,outputGeneration,maxDequeueCount);} else {// ...}// ...}// 编解码监测器初始化if (inputFormat || outputFormat) {Mutexed<PipelineWatcher>::Locked watcher(mPipelineWatcher);watcher->inputDelay(inputDelayValue).pipelineDelay(pipelineDelayValue).outputDelay(outputDelayValue).smoothnessFactor(kSmoothnessFactor);watcher->flush();}mInputMetEos = false;// 初始化buffer的锁mSync.start();return OK;
}

MediaCodec 获取buffer index

在创建并启动解码器后,我们会通过dequeueInputBuffer获取一个Buffer index,然后再通过getInputBuffers获取所有Buffer数组,然后根据索引在Buffer数组中获取buffer,往里面写入需要编码的数据,接下来我们来看看这个流程。
在这里插入图片描述

2.1 MediaCodec.dequeueInputBuffer
jni调用native_dequeueInputBuffer。

public final int dequeueInputBuffer(long timeoutUs) {// ...// 详见2.2int res = native_dequeueInputBuffer(timeoutUs);// ...return res;
}

2.2 android_media_MediaCodec_dequeueInputBuffer
调用JMediaCodec::dequeueInputBuffer

static jint android_media_MediaCodec_dequeueInputBuffer(JNIEnv *env, jobject thiz, jlong timeoutUs) {// ...size_t index;// JMediaCodec::dequeueInputBuffer,详见2.3status_t err = codec->dequeueInputBuffer(&index, timeoutUs);if (err == OK) {return (jint) index;}return throwExceptionAsNecessary(env, err, codec);
}

2.3 JMediaCodec::dequeueInputBuffer
C++层的MediaCodec,MediaCodec::dequeueInputBuffer

status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {// 详见2.4return mCodec->dequeueInputBuffer(index, timeoutUs);
}

2.4 MediaCodec::dequeueInputBuffer
发送kWhatDequeueInputBuffer消息。

status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, this);msg->setInt64("timeoutUs", timeoutUs);sp<AMessage> response;status_t err;// 发送kWhatDequeueInputBuffer消息,处理逻辑详见2.5if ((err = PostAndAwaitResponse(msg, &response)) != OK) {return err;}CHECK(response->findSize("index", index));return OK;
}

2.5 MediaCodec::onMessageReceived
调用handleDequeueInputBuffer处理,并且发送kWhatDequeueInputTimedOut消息来配置超时监测。

void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {switch (msg->what()) {//...case kWhatDequeueInputBuffer:{sp<AReplyToken> replyID;CHECK(msg->senderAwaitsResponse(&replyID));// ...// 详见2.6if (handleDequeueInputBuffer(replyID, true /* new request */)) {break;}// ... 通过发送kWhatDequeueInputTimedOut消息配置超时监测break;}// ...}
}

2.6 MediaCodec::handleDequeueInputBuffer
通过dequeuePortBuffer获取buffer index,然后返回index。

bool MediaCodec::handleDequeueInputBuffer(const sp<AReplyToken> &replyID, bool newRequest) {// ...异常处理// 详见2.7ssize_t index = dequeuePortBuffer(kPortIndexInput);if (index < 0) {CHECK_EQ(index, -EAGAIN);return false;}// 返回indexsp<AMessage> response = new AMessage;response->setSize("index", index);response->postReply(replyID);return true;
}

2.7 MediaCodec::dequeuePortBuffer
mAvailPortBuffers里是两个int数组,分别给编码和解码使用,这里的int数组用于存储可以使用的buffer index。这个方法做的事就是从这个int数组里获取一个index,并且将它从数组里移除。

ssize_t MediaCodec::dequeuePortBuffer(int32_t portIndex) {CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);BufferInfo *info = peekNextPortBuffer(portIndex);if (!info) {return -EAGAIN;}// 从availBuffers获取可用buffer index,并且把他从数组中移除。  std::list<size_t> *availBuffers = &mAvailPortBuffers[portIndex];size_t index = *availBuffers->begin();CHECK_EQ(info, &mPortBuffers[portIndex][index]);availBuffers->erase(availBuffers->begin());// ...return index;
}

MediaCodec 获取input buffer数组

在这里插入图片描述

3.1 MediaCodec.getInputBuffers

public ByteBuffer[] getInputBuffers() {synchronized (mBufferLock) {// ...// java层有一个buffer数组缓存,第一次需要通过jni去获取,详见3.2if (mCachedInputBuffers == null) {cacheBuffersLocked(true /* input */);}// ...return mCachedInputBuffers;}
} 

3.2 MediaCodec.cacheBuffersLocked
通过jni获取buffers,并缓存记录。

private void cacheBuffersLocked(boolean input) {ByteBuffer[] buffers = null;try {// 通过jni获取buffer,详见3.3buffers = getBuffers(input);invalidateByteBuffersLocked(buffers);} catch (IllegalStateException e) {// we don't get buffers in async mode}// ...// 缓存记录结果if (input) {mCachedInputBuffers = buffers;} else {mCachedOutputBuffers = buffers;}
}

3.3 JMediaCodec::getBuffers
jni方法通过调用JMediaCodec的getBuffers,而JMediaCodec又通过调用c++层的MediaCodec的getInputBuffers来获取buffers。

status_t JMediaCodec::getBuffers(JNIEnv *env, bool input, jobjectArray *bufArray) const {Vector<sp<MediaCodecBuffer> > buffers;// 详见3.4status_t err =input? mCodec->getInputBuffers(&buffers): mCodec->getOutputBuffers(&buffers);// ...将C++层的buffers通过jni关联给java层return OK;
}

3.4 MediaCodec::getInputBuffers
发送kWhatGetBuffers。

status_t MediaCodec::getInputBuffers(Vector<sp<MediaCodecBuffer> > *buffers) const {sp<AMessage> msg = new AMessage(kWhatGetBuffers, this);msg->setInt32("portIndex", kPortIndexInput);msg->setPointer("buffers", buffers);sp<AMessage> response;return PostAndAwaitResponse(msg, &response);
}

3.5 MediaCodec::onMessageReceived
我们目前流程是在获取inputBuffer,通过inputBuffer给编码器传入编码前的数据,而如果我们配置了InputSurface,以InputSurface为输入,则不需要获取Buffer。
通过CCodecBufferChannel::getInputBufferArray来获取Buffer数组。

void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {// ...case kWhatGetBuffers:{// ...异常处理dstBuffers->clear();// 通过getInputBufferArray获取buffer,如果配置来InputSurface则不会走这个模式。详见3.6if (portIndex != kPortIndexInput || !mHaveInputSurface) {if (portIndex == kPortIndexInput) {mBufferChannel->getInputBufferArray(dstBuffers);} else {mBufferChannel->getOutputBufferArray(dstBuffers);}}mApiUsageMetrics.isArrayMode = true;(new AMessage)->postReply(replyID);break;}// ...
}

3.6 CCodecBufferChannel::getInputBufferArray
这里会调用input->buffers->getArray来获取Buffer,这里input->buffers是在start的过程中就已经初始化的,具体是在1.8节,调用mChannel->prepareInitialInputBuffers时候分配的,其中会循环调用buffers的requestNewBuffer方法,我们之前说过buffers里面有pool,而pool会通过allocator来分配buffer。

void CCodecBufferChannel::getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {array->clear();Mutexed<Input>::Locked input(mInput);if (!input->buffers) {ALOGE("getInputBufferArray: No Input Buffers allocated");return;}if (!input->buffers->isArrayMode()) {input->buffers = input->buffers->toArrayMode(input->numSlots);}input->buffers->getArray(array);
}

MediacCodec queueInputBuffer

在这里插入图片描述

4.1 MediaCodec.queueInputBuffer
调用jni层native_queueInputBuffer

public final void queueInputBuffer(int index,int offset, int size, long presentationTimeUs, int flags)throws CryptoException {// ...try {// 详见4.2native_queueInputBuffer(index, offset, size, presentationTimeUs, flags);} catch (CryptoException | IllegalStateException e) {revalidateByteBuffer(mCachedInputBuffers, index, true /* input */);throw e;}
}

4.2 android_media_MediaCodec_queueInputBuffer
和之前一样,jni会调用JMediaCodec的queueInputBuffer方法。

static void android_media_MediaCodec_queueInputBuffer(JNIEnv *env,jobject thiz,jint index,jint offset,jint size,jlong timestampUs,jint flags) {// ...// 详见4.3status_t err = codec->queueInputBuffer(index, offset, size, timestampUs, flags, &errorDetailMsg);throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL,codec->getExceptionMessage(errorDetailMsg.c_str()).c_str());
}

4.3 JMediaCodec::queueInputBuffer
调用MediaCodec的queueInputBuffer

status_t JMediaCodec::queueInputBuffer(size_t index,size_t offset, size_t size, int64_t timeUs, uint32_t flags,AString *errorDetailMsg) {//详见4.4return mCodec->queueInputBuffer(index, offset, size, timeUs, flags, errorDetailMsg);
}

4.4 MediaCodec::queueInputBuffer
发送kWhatQueueInputBuffer消息。

status_t MediaCodec::queueInputBuffer(size_t index,size_t offset,size_t size,int64_t presentationTimeUs,uint32_t flags,AString *errorDetailMsg) {if (errorDetailMsg != NULL) {errorDetailMsg->clear();}sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);msg->setSize("index", index);msg->setSize("offset", offset);msg->setSize("size", size);msg->setInt64("timeUs", presentationTimeUs);msg->setInt32("flags", flags);msg->setPointer("errorDetailMsg", errorDetailMsg);sp<AMessage> response;return PostAndAwaitResponse(msg, &response);
}

4.5 MediaCodec::onMessageReceived
调用onQueueInputBuffer处理任务。

void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {//...case kWhatQueueInputBuffer:{// ...status_t err = UNKNOWN_ERROR;if (!mLeftover.empty()) {mLeftover.push_back(msg);size_t index;msg->findSize("index", &index);err = handleLeftover(index);} else {// 详见4.6err = onQueueInputBuffer(msg);}PostReplyWithError(replyID, err);break;}// ...
}

4.6 MediaCodec::onQueueInputBuffer
处理一些参数的检查和传递,这里分需要加密和不需要加密的两种场景,我们不看加密的逻辑,这里主要构建来一个新的buffer,把数据填充,并且调用attachBuffer把queue进来的buffer里面的数据拷贝到新建到buffer里。
最后调用queueInputBuffer。

status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {// ... 参数预处理和检查,将一些参数配置到buffer// ... 加密逻辑if (c2Buffer || memory) {sp<AMessage> tunings = NULL;if (msg->findMessage("tunings", &tunings) && tunings != NULL) {onSetParameters(tunings);}status_t err = OK;if (c2Buffer) {// 拷贝数据到新的buffererr = mBufferChannel->attachBuffer(c2Buffer, buffer);} else if (memory) {AString errorDetailMsg;err = mBufferChannel->attachEncryptedBuffer(memory, (mFlags & kFlagIsSecure), key, iv, mode, pattern,offset, subSamples, numSubSamples, buffer, &errorDetailMsg);if (err != OK && hasCryptoOrDescrambler()&& (mFlags & kFlagUseCryptoAsync)) {// ... 加密逻辑}} else {// error log}// ...}// ...if (hasCryptoOrDescrambler() && !c2Buffer && !memory) {// 需要加密场景} else {// 调用queueInputBuffer,详见4.7err = mBufferChannel->queueInputBuffer(buffer);// ...}// ...return err;
}

4.7 CCodecBufferChannel::queueInputBuffer
调用queueInputBufferInternal。

status_t CCodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {QueueGuard guard(mSync);if (!guard.isRunning()) {ALOGD("[%s] No more buffers should be queued at current state.", mName);return -ENOSYS;}return queueInputBufferInternal(buffer);
}

4.8 CCodecBufferChannel::queueInputBufferInternal
这里主要会解析buffer,然后将buffer里的信息封装到一个C2Work中,然后把C2Work存到一个C2Work数组,调用Component的queue来处理这个这个任务。
这里Component的queue会在hal层调用到对方的queue_nb方法,这里就是SimpleC2Component的::SimpleC2Component。

status_t CCodecBufferChannel::queueInputBufferInternal(sp<MediaCodecBuffer> buffer,std::shared_ptr<C2LinearBlock> encryptedBlock,size_t blockSize) {// ...获取buffer里的参数// ...构造一个C2Work数组和一个C2Work,后续最终会提交这个数组,将信息封装在C2Work中std::list<std::unique_ptr<C2Work>> items;std::unique_ptr<C2Work> work(new C2Work);work->input.ordinal.timestamp = timeUs;work->input.ordinal.frameIndex = mFrameIndex++;work->input.ordinal.customOrdinal = timeUs;work->input.buffers.clear();sp<Codec2Buffer> copy;bool usesFrameReassembler = false;if (buffer->size() > 0u) {Mutexed<Input>::Locked input(mInput);std::shared_ptr<C2Buffer> c2buffer;// 将buffer数据存到c2bufferif (!input->buffers->releaseBuffer(buffer, &c2buffer, false)) {return -ENOENT;}// ...if (input->frameReassembler) {usesFrameReassembler = true;input->frameReassembler.process(buffer, &items);} else {// ...// 将c2buffer存到C2Work里。work->input.buffers.push_back(c2buffer);if (encryptedBlock) {work->input.infoBuffers.emplace_back(C2InfoBuffer::CreateLinearBuffer(kParamIndexEncryptedBuffer,encryptedBlock->share(0, blockSize, C2Fence())));}}} else if (eos) {// ...}if (usesFrameReassembler) {// ...} else {work->input.flags = (C2FrameData::flags_t)flags;// TODO: fill info'swork->input.configUpdate = std::move(mParamsToBeSet);if (tunnelFirstFrame) {C2StreamTunnelHoldRender::input tunnelHoldRender{0u /* stream */,C2_TRUE /* value */};work->input.configUpdate.push_back(C2Param::Copy(tunnelHoldRender));}work->worklets.clear();work->worklets.emplace_back(new C2Worklet);// 将C2Work存到items里items.push_back(std::move(work));eos = eos && buffer->size() > 0u;}if (eos) {// ...}c2_status_t err = C2_OK;if (!items.empty()) {// ...// 将C2Work数组提交给Component(通过hal层到真正实现编解码逻辑实现),这里是SimpleC2Component::queue_nb,详见4.9err = mComponent->queue(&items);}if (err != C2_OK) {// ...} else {// ...释放buffer}feedInputBufferIfAvailableInternal();return err;
}

4.9 SimpleC2Component::queue_nb
这里把前面传过来的C2Work都放到mWorkQueue中,然后发送了kWhatProcess消息触发处理逻辑。

c2_status_t
SimpleC2Component::queue_nb(std::list<std::unique_ptr<C2Work>> *const items) {{Mutexed<ExecState>::Locked state(mExecState);if (state->mState != RUNNING) {return C2_BAD_STATE;}}bool queueWasEmpty = false;{Mutexed<WorkQueue>::Locked queue(mWorkQueue);queueWasEmpty = queue->empty();// 将items里面的数据放到mWorkQueue中while (!items->empty()) {queue->push_back(std::move(items->front()));items->pop_front();}}if (queueWasEmpty) {// 发送kWhatProcess,详见4.10  (new AMessage(WorkHandler::kWhatProcess, mHandler))->post();}return C2_OK;
}

4.10 SimpleC2Component::processQueue
这里主要就是会调用process方法,这个方法是由子类实现的,SimpleC2Component是用于管理流程框架的模版,它的子类来实现具体编解码逻辑。
例如H264的编解码就是由子类C2SoftAacDec和C2SoftAacEnc实现的,我们本节不会介绍编解码的细节,下一节会介绍H264编解码的一些核心原理。

bool SimpleC2Component::processQueue() {// ...{Mutexed<WorkQueue>::Locked queue(mWorkQueue);if (queue->empty()) {return false;}generation = queue->generation();drainMode = queue->drainMode();isFlushPending = queue->popPendingFlush();// 从队列中取出最前面的任务work = queue->pop_front();hasQueuedWork = !queue->empty();}if (isFlushPending) {// flush的回调,这里SimpleC2Component是一个处理流程的架构,最终需要由它的子类来实现对应生命周期所做的事。  c2_status_t err = onFlush_sm();// ...}if (!mOutputBlockPool) {// ...创建用于管理输出Buffer的Pool}// ...// ... input内buffer的检测// 调用 process,也是由子类实现的。  // 传入的参数是work和用于管理输出buffer的Poolprocess(work, mOutputBlockPool);// ...return hasQueuedWork;
}

小结

本节介绍了MediaCodec编解码的流程,从java层MediaCodec调用到JMediaCodec,然后调用C++层到MediaCodec,通过发送对应流程的消息,触发CCodec,而里面通过一个CCodecBufferChannel来管理这些逻辑,CCodecBufferChannel里面分input和output分别用于管理编解码,他们中都有一个buffers用于管理所有buffer,通过一个C2BlockPool来管理buffer的分配,C2BlockPool内有一个Allocator,会通过hal层进行最终的buffer分配释放。
CCodecBufferChannel工作会通过Component调用hal层,传参数到SimpleC2Component,SimpleC2Component的子类进行最终的编解码操作。
下一节我们会介绍H264编解码的核心原理。


http://www.ppmy.cn/embedded/129963.html

相关文章

如何在 HarmonyOS NEXT 中使用 @Builder 装饰器优化 UI 组件的复用?

摘要 在鸿蒙 NEXT 开发中&#xff0c;Builder 装饰器是一种轻量级的 UI 元素复用机制&#xff0c;它允许开发者将重复使用的 UI 元素抽象成一个方法&#xff0c;并在 build() 方法中多次调用&#xff0c;以实现 UI 结构的复用。以下是如何使用 Builder 装饰器来优化 UI 组件复…

三种单例实现

1、不继承Mono的单例 实现 使用 注&#xff1a; 使用需要继承BaseManager 泛型填写自己本身 需要实现无参构造函数 2、挂载式的Mono单例 实现 使用 注&#xff1a; 使用需要继承SingletonMono 泛型填写自己本身 需要挂载在unity引擎面板 3、不用挂载式的单例 实现 使…

基于Django的推荐系统、人脸识别登录、微信支付Demo、打卡门禁系统

基于Django的推荐系统、人脸识别登录、微信支付Demo、打卡门禁系统 1、推荐系统 图书管理、电影推荐、音乐推荐、在线课程选修、旅游推荐系统 图书管理点我跳转 电影管理点我跳转 课程管理点我跳转 2、算法 基于用户协同过滤推荐、物品协同过滤推荐、神经网络推荐、随机森…

2024入门测参考答案(c语言版)

先自己看代码思考&#xff0c;不理解的地方可以截图私聊…… 7-1 祝福祖国&#xff01; 这是一个编程题模板。 2024年即将到来&#xff0c;作为一名大学生&#xff0c;为了表达在新的一年里&#xff0c;对祖国的祝福&#xff0c;请编写程序输出祝福语&#xff1a;在新的一年…

CTFHUB技能树之SQL——布尔盲注

开启靶场&#xff0c;打开链接&#xff1a; 输入1&#xff1a; 显示查询成功但没有回显出相关信息&#xff0c;初步判断是布尔盲注入、时间盲注或报错注入 输入1&#xff1a; 还是没有回显 输入1"&#xff1a; 还是没有回显&#xff0c;到这里已经可以确认是布尔盲注了&a…

论文阅读-Causality Guided Disentanglement for Cross-PlatformHate Speech Detection

https://arxiv.org/pdf/2308.02080 GitHub - paras2612/CATCH 目录 摘要 1 INTRODUCTION 3 PROPOSED METHOD 3.1 Preliminaries 3.2 Disentangling Causal and Target Representations 3.3 Model Training 4 EXPERIMENTS 4.1 Datasets and Evaluation Metrics 4.3 Perf…

【Flutter】Dart:运算符

在 Dart 中&#xff0c;运算符是非常重要的组成部分&#xff0c;它们可以对变量和常量进行多种运算操作。理解和掌握 Dart 中的各种运算符不仅可以帮助你编写更加高效、简洁的代码&#xff0c;还能更好地理解其背后的逻辑和设计。本文将深入探讨 Dart 中的运算符&#xff0c;包…

OpenCV高级图形用户界面(9)更改指定窗口的位置函数moveWindow()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 将窗口移动到指定的位置。 cv::moveWindow() 函数用于更改指定窗口的位置。你可以使用这个函数来移动窗口到屏幕上的任何位置。 函数原型 void …