安卓MediaRecorder(4)视频采集编码写入详细源码分析

文章目录

    • 前言
    • 视频采集
    • 视频编码
    • 视频编码写入
    • 结语

本文首发地址 https://blog.csdn.net/CSqingchen/article/details/134896821
最新更新地址 https://gitee.com/chenjim/chenjimblog

前言

通过 文2 我们知道了 MediaRecorder 各个接口 Framework 中的实现。
通过 文3 我们 知道了 MediaRecorder 底层音频的采集、编码、写入文件等详细流程。
本文主要介绍 MediaRecorder 视频的采集、编码等相关流程。

视频采集

在 文1 我们知道了如何使用 MediaRecorder 录制音频,那么如何同时录制声音和视频呢,可以参见 Demo Camera2Video,这里不再贴代码。
通过此示例,我们知道录制视频需要如下设置

val surface = MediaCodec.createPersistentInputSurface()
...
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
setVideoSource(MediaRecorder.VideoSource.SURFACE)
setVideoEncoder(videoEncoder)
setInputSurface(surface)

也就是视频采集源,就是这个 surface = MediaCodec.createPersistentInputSurface()
也就是 mInputSurface,也是 PreviewFragment 中 encoderSurface,最终传递到 eglEncoderSurface
eglEncoderSurface = EGL14.eglCreateWindowSurface(eglDisplay, eglConfig, encoderSurface, surfaceAttribs, 0)
在 HardwarePipeline中,可以看到添加了一路预览流:

    // 创建一个 GL_TEXTURE_EXTERNAL_OES 纹理cameraTexId = createTexture()cameraTexture = SurfaceTexture(cameraTexId)...session.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {// 添加预览 surface targetaddTarget(cameraSurface)}.build()

基于 cameraTexture.setOnFrameAvailableListener(this), 当预览流可用时,会进行如下消费、复制操作:

private fun onFrameAvailableImpl(surfaceTexture: SurfaceTexture) {if (eglContext == EGL14.EGL_NO_CONTEXT) {return}/* 消费掉camera出来的流 ,updateTexImage 相关介绍可以参考 https://blog.csdn.net/CSqingchen/article/details/135637088 */cameraTexture.updateTexImage()/** 复制 cameraTexture 到 eglRenderSurface  */if (eglRenderSurface != EGL_NO_SURFACE) {copyCameraToRender()}/** 复制 eglRenderSurface 到 TextureView 显示*/copyRenderToPreview()/**  复制 eglRenderSurface 到 eglEncoderSurface ,通过消息 encoder.frameAvailable() 告知编码*/if (eglEncoderSurface != EGL_NO_SURFACE && currentlyRecording) {copyRenderToEncode()}
}

至此,我们知道了 MediaRecorder 采集视频的数据流。
这里是基于 Demo Camera2Video 分析,其它情况也差不多。

视频编码

在示例 Camera2Video 中,如果 useMediaRecorder 为 false,编码相关代码如下:

public fun drainEncoder(): Boolean {while (true) {// 编码 var encoderStatus: Int = mEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC)...// 取编码的数据var encodedData: ByteBuffer? = mEncoder.getOutputBuffer(encoderStatus)...//写入编码后数据到文件mMuxer.writeSampleData(mVideoTrack, encodedData, mBufferInfo)}
}

完整代码参见 EncoderWrapper.kt
如果 useMediaRecorder 为 true ,编码及写入均在Framework,我们可以从 setInputSurface 往下底层查看。
通过 文2 ,setInputSurface的最终实现如下

status_t StagefrightRecorder::setInputSurface(const sp<PersistentSurface>& surface) {mPersistentSurface = surface;return OK;
}

编码器初始化创建如下:

status_t StagefrightRecorder::setupVideoEncoder(const sp<MediaSource> &cameraSource,sp<MediaCodecSource> *source){...sp<MediaCodecSource> encoder = MediaCodecSource::Create(mLooper, format, cameraSource, mPersistentSurface, flags);...
}
status_t MediaCodecSource::initEncoder() {...if (mPersistentSurface != NULL) {err = mEncoder->setInputSurface(mPersistentSurface);}...
}
status_t MediaCodec::setInputSurface(const sp<PersistentSurface> &surface) {sp<AMessage> msg = new AMessage(kWhatSetInputSurface, this);msg->setObject("input-surface", surface.get());sp<AMessage> response;return PostAndAwaitResponse(msg, &response);
}

通过如下代码将 Camera 数据源设置为编码

status_t StagefrightRecorder::setupCameraSource(sp<CameraSource> *cameraSource) {if (mCaptureFpsEnable) {*cameraSource = CameraSource::CreateFromCamera(mCamera, mCameraProxy, mCameraId, clientName, uid, pid,videoSize, mFrameRate,mPreviewSurface);}
}
...
setupVideoEncoder(mediaSource, &encoder);

视频编码写入

视频的编码写入流程同 文3 音频的编码、写入。

结语

到这里,通过相关文章的介绍,我们已经很清晰 MediaRecorder 底层的音、视频采集、编码、写入编码后内容等相关流程的源码实现。
希望对你有所帮助。如果你在使用 MediaRecorder 的过程中遇到了其他问题,欢迎留言讨论。
如果你觉得本文还不错,可以点赞+收藏。


相关文章
安卓MediaRecorder(1)录制音频的详细使用
安卓MediaRecorder(2)录制源码分析
安卓MediaRecorder(3)音频采集编码写入源码分析
安卓MediaRecorder(4)视频采集编码写入源码分析

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

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

相关文章

<网络安全>《5 网络安全产品之网关》

1 基础概念 网关(Gateway)又称网间连接器、协议转换器。 网关在网络层以上实现网络互连&#xff0c;是复杂的网络互连设备&#xff0c;仅用于两个高层协议不同的网络互连。 网关既可以用于广域网互连&#xff0c;也可以用于局域网互连。 网关是一种充当转换重任的计算机系统或…

《游戏-03_3D-开发》之—新输入系统人物移动攻击连击

本次修改unity的新输入输出系统。本次修改unity需要重启&#xff0c;请先保存项目&#xff0c; 点击加号起名为MyCtrl&#xff0c; 点击加号设置为一轴的&#xff0c; 继续设置W键&#xff0c; 保存 生成自动脚本&#xff0c; 修改MyPlayer代码&#xff1a; using UnityEngine;…

华为产业链之车载激光雷达

一、智能汽车 NOA 加快普及&#xff0c;L3 上路利好智能感知硬件 1、感知层是 ADAS 最重要的一环 先进驾驶辅助系统 &#xff08;ADAS&#xff0c; Advanced driver-assistance system&#xff09;分“感知层、决策层、执行层”三个层级&#xff0c;其中感知层是最重要的一环…

Python数据分析:数据处理

数据处理是指对原始数据进行清洗、转换、整合和分析&#xff0c;以便从中提取有价值的信息。 常见的数据处理步骤包括&#xff1a; 数据清洗&#xff1a;去除缺失值、异常值和重复值。数据转换&#xff1a;对数据进行格式转换、归一化、标准化等操作。数据整合&#xff1a;将…

vue创建前端项目

背景 项目中需要用到前端技术&#xff0c;通过技术调研和团队分析&#xff0c;则采用vue作为前端主要技术栈。 问题 安装好后vue&#xff0c;按理说就可以创建vue项目 vue init webpack 项目名称 npm install&#xff0c;使用vue-cli脚手架搭建项目卡在sill idealTree buil…

C++高级编程——STL:list容器、set容器和map容器

本专栏记录C学习过程包括C基础以及数据结构和算法&#xff0c;其中第一部分计划时间一个月&#xff0c;主要跟着黑马视频教程&#xff0c;学习路线如下&#xff0c;不定时更新&#xff0c;欢迎关注。 当前章节处于&#xff1a; ---------第1阶段-C基础入门 ---------第2阶段实战…

C++入门【35-类访问修饰符】

数据封装是面向对象编程的一个重要特点&#xff0c;它防止函数直接访问类类型的内部成员。类成员的访问限制是通过在类主体内部对各个区域标记 public、private、protected 来指定的。关键字 public、private、protected 称为访问修饰符。 一个类可以有多个 public、protected…

C#hybridCLR热更新方案初探

前言 暂时处于初步研究状态&#xff0c;目前的框架使用还是尚少&#xff0c;本篇文章旨在同步给大家大概的使用流程和使用心得&#xff0c;在初步建立新项目时可以适当考虑。 介绍 热更新 与强制更新相对应&#xff0c;移动平台上App的可执行程序没有发生变化&#xff0c;仅…

[Go]认识Beego框架

对比Gin的简洁&#xff0c;自己之前基于Gin撸了一个架子&#xff0c;确实比beego目录看着舒服多了&#xff0c;不过最近接触到beego的项目&#xff0c;beego的bee工具使用还是很方便&#xff0c;来简单梳理下细节&#xff1b; Beego是一个开源的Go语言Web应用框架&#xff0c;…

边缘计算:在挑战与机遇的浪潮中破浪前行

在这个万物互联的时代&#xff0c;边缘计算如同一股清新奔腾的泉水&#xff0c;涌入了浩如烟海的技术海洋。它带着信息时代的涟漪&#xff0c;泛起了片片波澜&#xff0c;既有数据安全与隐私保护的风险&#xff0c;也有网络稳定性的挑战。但是&#xff0c;正如每一朵风雨都孕育…

设计一个在线聊天系统

约束和限制 假设我们聚焦在以下的工作流 只是输入Text进行对话 Users Add a UserRemove a UserUpdate a userAdd to a user’s friends list Add friend request Approve friend requestReject friend request Remove from a user’s friends list Create a group chat 邀请…

摄像机视角的切换_unity基础开发教程

摄像机视角的切换 前言一、场景搭建二、脚本编辑三、脚本挂载四、运行效果结语 前言 我们在游戏中经常可以看到游戏视角的切换&#xff0c;今天我们就做一个视角切换的小demo&#xff0c;学会之后可以将其融入到自己的游戏制作当中。 话不多说&#xff0c;我们现在开始&#xf…

eNSP学习——理解交换机Hybird接口的应用

目录 原理概述 实验内容 实验目的 实验步骤 实验拓扑 实验编址 实验步骤 基本配置&#xff08;此处仅以PC1为例&#xff09; 实现组内通信、组间间隔 实现网络管理员对所有网络的访问 原理概述 Hybrid接口既可以连接普通终端的接入链路又可以连接交换机间的干道…

Word中插入公式并引用

1、如何插入公式 在word中,键入快捷键 “alt” + “=”,即可快速插入一个公式,并立即编辑。 2、利用表格框住公式 新建一个 1 行 3 列的表格,总宽度为页面宽度,第一个单元格和最后一个单元格都保持在 2.25cm,中间尽可能长。我设置的这个数值比较合理。 记住,要把表格…

初识人工智能,一文读懂机器学习之逻辑回归知识文集(4)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

使用Linux SDK客户端向AWS Iot发送数据

参考链接&#xff1a; https://ap-southeast-1.console.aws.amazon.com/iot/home?regionap-southeast-1#/test 此篇文章用于测试&#xff0c;使用Linux SDK客户端向AWS Iot发送数据&#xff0c;准备环境如下&#xff1a; 1、客户端环境准备 1.1 客户端操作系统 虚拟机一台…

ifconfig 主机ip url记录

ifconfig 容器Pods相关主机与url信息 一文搞懂网络知识&#xff0c;IP、子网掩码、网关、DNS、端口号_关于ip,网关。端口-CSDN博客 计算机网络知识之URL、IP、子网掩码、端口号_ip地址和url-CSDN博客 阅读看下以上文章 由此可知 1.主机ip 10.129.22.124 10.129.22 是网段…

【AIGC】Diffusers:扩散模型的开发手册说明1

主要组件 最先进的扩散管道 diffusion pipelines&#xff0c;只需几行代码即可进行推理。可交替使用的各种噪声调度器 noise schedulers&#xff0c;用于平衡生成速度和质量。预训练模型 models&#xff0c;可作为构建模块&#xff0c;并与调度程序结合使用&#xff0c;来创建…

Java数据结构与算法:有向图和无向图

Java数据结构与算法&#xff1a;有向图和无向图 大家好&#xff0c;我是免费搭建查券返利机器人赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 什么是图&#xff1f; 在计算机科学中&#xff0c;图是一种非常重…

通过Android Logcat分析firebase崩溃

参考&#xff1a;UnityIL2CPP包Crash闪退利用Android Logcat还原符号表堆栈日志 - 简书 一、安装Android Logcat插件 1、新建空白unity工程&#xff0c;打开PackageManager窗口&#xff0c;菜单栏Window/PackageManager 2、PackageManager中安装Android Logcat日志工具 3、安…