本篇文章主要是理解Android 12编码的流程, 首先从上层的应用出发理解mediacodec提供给外部API的用法。然后针对每个api 分析编码各个流程中框架中的流程。
熟悉一个框架的流程 可以从简单到复杂、从整体到局部去展开。 同时在理解过中会产生各种各样的问题,各种问题的解决就是一个知识经验的形成过程。
代码路径:frameworks\av\cmds\screenrecord\screenrecord.cpp
编译生成的是screenrecord在system/bin目录,默认在android系统都会携带。
使用命令:这个命令会将屏幕的操作录制到/sdcard/test.mp4下。
screenrecord /sdcard/test.mp4
应用流程
创建编码器,创建输入的surface,配置format,启动编码器
sp format = new AMessage;
format->setInt32(KEY_WIDTH, gVideoWidth);
format->setInt32(KEY_HEIGHT, gVideoHeight);
.....
codec = MediaCodec::CreateByType(looper, kMimeTypeAvc, true);
err = codec->configure(format, NULL, NULL,
MediaCodec::CONFIGURE_FLAG_ENCODE);
err = codec->createInputSurface(&bufferProducer);
err = codec->start();
err = prepareVirtualDisplay(displayState, bufferProducer, &dpy);
从编码器中取出buffer,后续就是将这个buffer写入到mp4文件中。
Vector > buffers;
err = encoder->getOutputBuffers(&buffers);
err = encoder->dequeueOutputBuffer(&bufIndex, &offset, &size, &ptsUsec,
&flags, kTimeout);
上述流程的疑问?
mediacodec是如何将surface的数据取出来 然后进行编码的?
相对应于解码的流程,会有一个queueInbufferbuffer 将未解码的数据喂给mediacodec,而在编码这边编码器只有一个从codec创建出来的Surface,这个surface会配置到surfaceFlinger那边的虚拟显示中。
代码路径:
frameworks\av\media\libstagefright\MediaCodec.cpp
frameworks\av\media\codec2\sfplugin\CCodec.cpp
frameworks\av\media\libstagefright\bqhelper\GraphicBufferSource.cpp
frameworks\av\media\codec2\sfplugin\C2OMXNode.cpp
简单的理解可以分为这两个路径:
这里我们关注消费者这一端的实现。
mediacodec creatInputSurface
调用流程
遵循 mediacodec—>ccodec这样的流程,ccodec调用的是codec2client。client 通过HIDL调用到componentstore端(在IComponetSotore.hal中有定义了这样的接口C2PlatformComponentStore–>componentStore–>IComponetSotore 具体用vendor定义的还是default google实现的 是在之前service端创建服务的时候决定的)。
createInputSurface
创建了GraphicBufferSource, 在这个类的初始化中调用BufferQueue::createBufferQueue
创建Producer和Consumer,通过将GraphicBufferSource监听注册到mConsumer中,
这里就是onFrameAvailable注册的地方。Producer和GraphicBufferSource会封装到InputSurface 返回到codec2client。
GraphicBufferSourceWrapper的connect
创建好之后的InputSurface会强制转换为GraphicBufferSourceWrapper,然后会调用这个类的connect。connect中是创建了C2OMXNode,传递的参数是之前MediaCodec::CreateByType
创建的componet。同时通过调用GraphicBufferSource::configure,将这个C2OMXNode配置到GraphicBufferSource的mComponent。
CCodec::createInputSurface()
int32_t width = 0;
(void)outputFormat->findInt32("width", &width);
int32_t height = 0;
(void)outputFormat->findInt32("height", &height);
err = setupInputSurface(std::make_shared(
gbs, width, height, usage));
bufferProducer = persistentSurface->getBufferProducer();
CCodec::setupInputSurface:
status_t err = mChannel->setInputSurface(surface);
CCodecBufferChannel::setInputSurface:
mInputSurface->connect(mComponent);
class GraphicBufferSourceWrapper : public InputSurfaceWrapper {
connect(const std::shared_ptr &comp) {
mNode = new C2OMXNode(comp);
mOmxNode = new hardware::media::omx::V1_0::utils::TWOmxNode(mNode);
mNode->setFrameSize(mWidth, mHeight);
// Usage is queried during configure(), so setting it beforehand.
OMX_U32 usage = mConfig.mUsage & 0xFFFFFFFF;
(void)mNode->setParameter(
(OMX_INDEXTYPE)OMX_IndexParamConsumerUsageBits,
&usage, sizeof(usage));
mSource->configure(
mOmxNode, static_cast(mDataSpace));
return OK;
}
}
status_t GraphicBufferSource::configure(
const sp& component,
int32_t dataSpace,
int32_t bufferCount,
uint32_t frameWidth,
uint32_t frameHeight,
uint32_t consumerUsage)
{
mComponent = component;
}
// BufferQueue::ConsumerListener callback
void GraphicBufferSource::onFrameAvailable(const BufferItem& item __unused)
onBufferAcquired_l(buffer);
void GraphicBufferSource::onBufferAcquired_l(const VideoBuffer &buffer)
fillCodecBuffer_l();
bool GraphicBufferSource::fillCodecBuffer_l() {
err = submitBuffer_l(item);
status_t GraphicBufferSource::submitBuffer_l(const VideoBuffer &item)
status_t err = mComponent->submitBuffer(
codecBufferId, graphicBuffer, codecTimeUs, buffer->getAcquireFenceFd());
class OmxComponentWrapper : public ComponentWrapper {
status_t submitBuffer(
int32_t bufferId, const sp &buffer,
int64_t timestamp, int fenceFd) override {
ALOGD("submitBuffer bufferId:%d", (int)bufferId);
return mOmxNode->emptyBuffer(
bufferId, OMX_BUFFERFLAG_ENDOFFRAME, buffer, timestamp, fenceFd);
}
status_t C2OMXNode::emptyBuffer(
buffer_id buffer, const OMXBuffer &omxBuf,
OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
mQueueThread->queue(comp, fenceFd, std::move(work), std::move(fd0), std::move(fd1));
}
class C2OMXNode::QueueThread : public Thread {
protected:
bool threadLoop() override {
comp->queue(&items);
}
总结: Android 录屏编码这一部分 调用的路径非常长,主要连接surface和componet的是GraphicBufferSource类。在这里监听surface buffer的生成,并将其传递给编码的componet。