Android codec2 编码 -- 基于录屏

news2025/1/20 21:55:45

文章目录

      • 前言
      • android 原生的应用srcreenrecord
      • MediaCodec获取编码数据流程

前言

本篇文章主要是理解Android 12编码的流程, 首先从上层的应用出发理解mediacodec提供给外部API的用法。然后针对每个api 分析编码各个流程中框架中的流程。

熟悉一个框架的流程 可以从简单到复杂、从整体到局部去展开。 同时在理解过中会产生各种各样的问题,各种问题的解决就是一个知识经验的形成过程。

android 原生的应用srcreenrecord

  • 应用和代码路径

代码路径:frameworks\av\cmds\screenrecord\screenrecord.cpp

编译生成的是screenrecord在system/bin目录,默认在android系统都会携带。

使用命令:这个命令会将屏幕的操作录制到/sdcard/test.mp4下。

 screenrecord /sdcard/test.mp4
  • 应用流程

    • 首先在编码器mediacodec调用createInputSurface创建一个inputSurface。这个inputSurface传递出来到显示 作为虚拟显示的bufferProducer。
    • 在surfaceFlinger 端,inputSurface作为prepareVirtualDisplay的参数, 使得surfaceFlinger从这个surface中获取bufffer, 然后将屏幕合成后的数据写到这个buffer里面。
    • 在编码端将这个buffer 作为编码的输入进行处理。mediacodec编码完成之后调用dequeueOutputBuffer 将编码之后的数据取出来写到文件,然后调用releaseOutputBuffer将这个buffer释放回去。
    • 在编码器这边,surfaceflinger 是生产端,mediacodec是消费端。其他有关屏幕录制或者surface 直接到编码的流程大概都是这样的。
    创建编码器,创建输入的surface,配置format,启动编码器
    sp<AMessage> 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<sp<MediaCodecBuffer> > buffers;
    err = encoder->getOutputBuffers(&buffers);
    err = encoder->dequeueOutputBuffer(&bufIndex, &offset, &size, &ptsUsec,
    &flags, kTimeout);
    

上述流程的疑问?

  1. mediacodec是如何将surface的数据取出来 然后进行编码的?

    相对应于解码的流程,会有一个queueInbufferbuffer 将未解码的数据喂给mediacodec,而在编码这边编码器只有一个从codec创建出来的Surface,这个surface会配置到surfaceFlinger那边的虚拟显示中。

MediaCodec获取编码数据流程

代码路径:
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

简单的理解可以分为这两个路径:

  1. 生产者: surfaceFlinger从MediaCodec创建的InputSurface中申请buffer,然后将各个图层的数据合成到这块buffer中,合成后 通知到消费者 也就是componet这一端。
  2. 消费者:componet收到生产者surfaceFlinger的通知后,将这个合成的buffer给到硬解或者软解编码器进行编码。编码后的数据,外部应用通过dequeueOutputBuffer可以获取到。

这里我们关注消费者这一端的实现。

  • 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<GraphicBufferSourceWrapper>(
                    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<Codec2Client::Component> &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<hardware::graphics::common::V1_0::Dataspace>(mDataSpace));
            return OK;
    }
    }
    
    status_t GraphicBufferSource::configure(
            const sp<ComponentWrapper>& component,
            int32_t dataSpace,
            int32_t bufferCount,
            uint32_t frameWidth,
            uint32_t frameHeight,
            uint32_t consumerUsage)
    {
        mComponent = component;
    }
    
  • onFrameAvailable
    • 通过之前在GraphicBufferSource注册onFrameAvailable到producer中lister,当合成后又buffer 可用的时候,会回调到GraphicBufferSource的onFrameAvailable。
    • onFrameAvailable经过一系列的处理 会调用到mComponent->submitBuffer,这个调用C2OMXNode->emptyBuffer。
      c2OMXNode这边将这块omxBuf 封装成c2Buffer,然后queue到c2OMXNode 的队列中去。C2OMXNode有专门的mQueueThread来把队列中c2works queue到Codec2Client中。
    • 在client中的Codec2Client::Component::queue()在调用 mComponent->queue_nb(&c2works)。
      mComponet 是simpleC2Componet, 在其中的queue_nb会把上面传递的items 放到componet的mWorkQueue中,然后发送kWhatProcess消息, 收到消息后调用processQueue。然后调用各个组件的process。
// 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<GraphicBuffer> &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。

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1007380.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式这个领域会变得过于内卷吗?

今日话题&#xff0c;嵌入式这个领域会变得过于内卷吗&#xff1f;嵌入式开发主要服务于第二产业&#xff0c;尤其是制造业&#xff0c;包括电器、电气、机械、汽车、装备、航空航天等行业的“智能制造”部门&#xff0c;稳定性较强&#xff0c;不像互联网行业那样波动大。因此…

【LangChain系列 8】Prompt模版——少样本prompt模版(二)

原文地址&#xff1a;【LangChain系列 8】Prompt模版——少样本prompt模版(二) 本文速读&#xff1a; 固定少样本prompt模版 动态少样本prompt模版 在上篇文章中介绍了少样本模版的基本用法&#xff0c;本文将介绍 对话模型(chat model) 中 少样本prompt模版的用法。 LangCh…

C语言双向链表

文章目录 前言双向链表链表头结点的创建节点尾插与尾删节点头插与头删特定位置插入或删除节点链表节点查找双向链表的销毁 链表的打印 前言 假期时间因为为学校开学考试做准备所以一直没更新博客&#xff0c;今天开始博客会陆续更新。 双向链表 之前我们说过了顺序表和单链表…

加密狗软件有什么作用?

加密狗软件是一种用于加密和保护计算机软件和数据的安全设备。它通常是一个硬件设备&#xff0c;可以通过USB接口连接到计算机上。加密狗软件的作用主要体现在以下几个方面&#xff1a; 软件保护&#xff1a;加密狗软件可以对软件进行加密和授权&#xff0c;防止未经授权的用户…

K8S:kubectl陈述式、声明式资源管理及金丝雀部署

文章目录 一.陈述式资源管理方法1.陈述式资源管理概念2.基本信息查看&#xff08;1&#xff09;查看版本信息&#xff08;2&#xff09;查看资源对象简写&#xff08;3&#xff09;查看集群信息&#xff08;4&#xff09;配置kubectl自动补全&#xff08;5&#xff09;node节点…

【Java实战项目】【超详细过程】—大饼的图片服务器4

目录 1.引入servlet依赖2.处理客户端请求2.1 上传图片2.1.1.获取图片属性写入数据库&#xff08;1&#xff09;创建factory对象和fileUpload对象为获取图片信息做准备&#xff08;2&#xff09;将获取到的文件信息存到列表items中&#xff08;3&#xff09;获取列表items中第一…

无涯教程-JavaScript - XNPV函数

描述 XNPV函数返回的现金Stream量表的净现值不一定是周期性的。要计算一系列定期现金Stream量的净现值,请使用NPV函数。 语法 XNPV (rate, values, dates)争论 Argument描述Required/OptionalRateThe discount rate to apply to the cash flows.RequiredValues 与日期付款时…

(已解决)AttributeError: module ‘cv2.gapi.wip.draw‘ has no attribute ‘Text‘

问题描述 今天再跑Caption-Anything项目的时候&#xff0c;最开始的时候就报了这样一个错误&#xff1a;AttributeError: module cv2.gapi.wip.draw has no attribute Text。 Caption-Anything是一种多功能的图像处理工具&#xff0c;结合了Segment Anything&#xff0c;Visual…

前端项目开发流程

一 参加需求对称(评审)会议 时间&#xff1a;在产品设计完成以后&#xff0c;进入正式的开发流程之前 组织者&#xff1a;产品&项目经理 目的&#xff1a;统一大家对产品的认识&#xff0c;及时发现产品设计缺陷&#xff0c;尽可能降低后续修改需求的频率 参与者&#xff…

短信验证码的登录注册功能

1 基于session实现登录流程 1.1发送验证码&#xff1a; 用户在提交手机号后&#xff0c;会校验手机号是否合法&#xff0c;如果不合法&#xff0c;则要求用户重新输入手机 如果手机号合法&#xff0c;后台此时生成对应的验证码&#xff0c;同时将验证码进行保存&#xff0c;然…

【计算机视觉 | 目标检测】干货:目标检测常见算法介绍合集(二)

文章目录 十六、EfficientDet十七、Deformable DETR十八、YOLOX十九、Sparse R-CNN二十、Contour Proposal Network二十一、VarifocalNet二十二、Libra R-CNN二十三、Stand-Alone Self Attention二十四、ThunderNet二十五、Hierarchical Transferability Calibration Network二…

垃圾收集算法

1.如何判断对象是否存活&#xff1f; 1.1引用计数算法 基本思路&#xff1a; 在对象中添加一个引用计数器每当有一个地方引用它的时候&#xff0c;计数器就加1每当有一个引用失效的时候&#xff0c;计数器就减-1当计数器的值为0的时候&#xff0c;那么该对象就是可被GC回收的…

leetcode 2366. Minimum Replacements to Sort the Array(数组排序的最少替换数)

数组nums中的元素nums[ i ] 可以替换为任意两个数a, b, 前提是ab nums[ i ]. 把数组nums变为升序&#xff08;可以有相等&#xff09;数组需要多少次替换。 思路&#xff1a; 排序数组是左边的元素<右边元素&#xff0c;以右边元素为边界。 所以从右到左遍历数组&#xf…

第二章 网络应用

第一节 计算机网络应用体系结构 三种类型&#xff1a; 1. 客户/服务器&#xff08;c/s&#xff09;结构 最主要的特征是通信只在客户与服务器之间进行&#xff0c;客户与客户之间不进行直接通信。 2. P2P(Peer to Peer) 结构 每个对等端都同时具备C/S应用的客户与服务器的…

Python算法练习 9.12

leetcode 643 子数组最大平均数 给你一个由 n 个元素组成的整数数组 nums 和一个整数 k 。 请你找出平均数最大且 长度为 k 的连续子数组&#xff0c;并输出该最大平均数。 任何误差小于 10-5 的答案都将被视为正确答案 输入&#xff1a;nums [1,12,-5,-6,50,3], k 4 输出…

buuctf web [极客大挑战 2019]Secret File

纯网页&#xff0c;看一下源码。 这一块源码中有个隐藏的超链接&#xff0c;点击后跳转到了新页面。 新页面的源码里&#xff0c;也有一处可以跳转的超链接。 点进新页面啥也没有了。 单看网页&#xff0c;什么也没有&#xff0c;尝试用burp抓包试试。 在/Archive_room.php跳…

循环语句详解

文章目录 循环语句详解1. 循环使用 v-for 指令2. v-for 还支持一个可选的第二个参数&#xff0c;参数值为当前项的索引3. 模板template 中使用 v-for4. v-for 迭代对象-第一个参数为value5. v-for的第二个参数为键名6. v-for的第三个参数为索引7. v-for迭代整数8. computed计算…

运营商大数据精准营销获客?

多年来&#xff0c;大数据运营商一直致力于为企业提供互联网大数据精准营销的新项目&#xff0c;并以确保自身信息安全为前提。例如&#xff0c;如果移动用户查看了任何网站&#xff0c;在网页上搜索了任何关键词&#xff0c;登录了应用程序&#xff0c;给任何人打了电话&#…

【Linux】多线程互斥与同步

文章目录 一、线程互斥1. 线程互斥的引出2. 互斥量3. 互斥锁的实现原理 二、可重入和线程安全三、线程和互斥锁的封装1. 线程封装1. 互斥锁封装 四、死锁1. 死锁的概念2. 死锁的四个必要条件3. 避免死锁 五、线程同步1. 线程同步的理解2. 条件变量 一、线程互斥 1. 线程互斥的…

kaggle近三年NLP比赛top方案汇总及新赛推荐

NLP的赛题任务主要有文本分类、情感分析、关系抽取、文本匹配、阅读理解、问答系统等&#xff0c;自Google开发的NLP处理模型BERT被广泛应用后&#xff0c;目前解决NLP任务的首选方案就是深度学习方法&#xff08;textCNN、LSTM、GRU、BiLSTM、Attention等&#xff09;&#xf…