媒体编解码器MediaCodec

目录

1.介绍MediaCodec类

2.创建MediaCodec的方式

3.MediaCodec流程

(1)配置编码参数

(2)创建编码器

(3)创建混合器

(4)开始编码 

4.MediaCodec编码的工作方式

 5.MediaCodec状态周期图

 6.使用缓冲区的异步和同步处理

7.录制纯视频demo


1.介绍MediaCodec类


     

final public class MediaCodec

MediaCodec类可以访问底层媒体编解码框架(Stage或OpenMAX),即编解码组件。通常与MediaExtractor、MediaSync、MediaMuxer、Image、Surface和AudioTrack一起使用。它本身并不是Codec,它通过调用底层编解码组件获得了Codec的能力。

2.创建MediaCodec的方式


创建MediaCodec(按格式创建):

  • MediaCodec createDecoderByType(String type):创建编码器。
  • MediaCodec createEncoderByType(String type):创建解码器。
  • type:数据解析阶段的mimeType,如”video/avc“。

创建MediaCodec(按Codec名字创建):

  • MediaCodec createByCodecName(String name)。
  • OMX.google.h264.decoder:软解码
  • OMX.MTK.VIDEO.DECODER.AVC:硬解码

3.MediaCodec流程


(1)配置编码参数

视频类型的Mediaformat

可以通过如下代码创建视频类型Mediaformat:

MediaFormat videoFormat = MediaFormat.createVideoFormat(videoType, width, height);

videoType常用的有两种:

  • MediaFormat.MIMETYPE_VIDEO_AVC(H.264)
  • MediaFormat.MIMETYPE_VIDEO_HEVC(H.265)

以下配置是必须要指定的,否则会报错。

            MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);//色彩空间format.setInteger(MediaFormat.KEY_COLOR_FORMAT,MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);//码率format.setInteger(MediaFormat.KEY_BIT_RATE,500_000);//帧率fpsformat.setInteger(MediaFormat.KEY_FRAME_RATE,20);//关键帧间隔format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,2);

音频类型的Mediaformat

可以通过如下代码创建音频类型Mediaformat:

MediaFormat.createAudioFormat(audioType, sampleRate, channelCount);
  • audioType:常用的是MediaFormat.MIMETYPE_AUDIO_AAC;
  • sampleRate:采样率
  • channelCount:声道数

以下配置是必须要指定的,否则会报错。

//音频比特率(码率)
audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, 96000);
(2)创建编码器

配置解码器或者编码器configure函数

public void configure( @Nullable MediaFormat format,

@Nullable Surface surface,
@Nullable MediaCrypto crypto,

@ConfigureFlag int flags) 

函数的参数介绍:

  • MediaFormat format:输入数据的格式(解码器)或输出数据的所需格式(编码器)。传null等同于传递MediaFormat.MediaFormat作为空的MediaFormat。
  • Surface surface:指定Surface,用于解码器输出的渲染。如果编解码器不生成原始视频输出(例如,不是视频解码器)和/或想配置解码器输出ByteBuffer,则传null。
  • MediaCrypto crypto:指定一个crypto对象,用于对媒体数据进行安全解密。对于非安全的编解码器,传null。
  • int flags:当组件是编码器时,flags指定为常量CONFIGURE_FLAG_ENCODE。
     

使用示例:

  •             mediaCodec=MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);mediaCodec.configure(format,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);mediaCodec.start();
(3)创建混合器

创建混合器MediaMuxer函数

 public MediaMuxer(@NonNull String path, @Format int format)

函数的参数介绍:

  • path:输出文件的路径
  • format:输出的格式

示例代码:

            mMuxer=new MediaMuxer(path,MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);mMuxer.setOrientationHint(degress);
(4)开始编码 

介绍MediaCodec编码用到的四个方法:
1. dequeueInputBuffer

返回用于填充有效数据的输入buffer的索引,如果当前没有可用的buffer,则返回-1。

public final int dequeueInputBuffer(long timeoutUs)

函数的参数介绍:

  • long timeoutUs:等待可用的输入buffer的时间。
  • 如果timeoutUs == 0,则立即返回。
  • 如果timeoutUs < 0,则无限期等待可用的输入buffer。
  • 如果timeoutUs > 0,则等待“timeoutUs”微秒。

2. queueInputBuffer

在指定索引处填充输入buffer后,使用queueInputBuffer将buffer提交给组件。


public native final void queueInputBuffer(
    int index,
    int offset, int size, long presentationTimeUs, int flags)

  • int index:以前调用dequeueInputBuffer(long)返回的输入buffer的索引。
  • int offset:数据开始时输入buffer中的字节偏移量。
  • int size:有效输入数据的字节数。
  • long presentationTimeUs:此buffer的PTS(以微秒为单位)。
  • int flags:一个由BUFFER_FLAG_CODEC_CONFIG和BUFFER_FLAG_END_OF_STREAM标志组成的位掩码。虽然没有被禁止,但是大多数codec并不对输入buffer使用BUFFER_FLAG_KEY_FRAME标志。

3. dequeueOutputBuffer

从MediaCodec获取输出buffer。

    public final int dequeueOutputBuffer(
            @NonNull BufferInfo info, long timeoutUs) 

  • BufferInfo info:输出buffer的metadata。
  • long timeoutUs:含义同dequeueInputBuffer中的timeoutUs参数。
  • 返回值:已成功解码的输出buffer的索引或INFO_*常量之一(INFO_TRY_AGAIN_LATER, INFO_OUTPUT_FORMAT_CHANGED 或 INFO_OUTPUT_BUFFERS_CHANGED)。返回INFO_TRY_AGAIN_LATER而timeoutUs指定为了非负值,表示超时了。返回INFO_OUTPUT_FORMAT_CHANGED表示输出格式已更改,后续数据将遵循新格式。

4. releaseOutputBuffer

使用此方法将输出buffer返回给codec或将其渲染在输出surface。

public void releaseOutputBuffer (int index, boolean render)

  • index:以前调用dequeueOutputBuffer(long)返回的输入buffer的索引。
  • boolean render:如果在配置codec时指定了一个有效的surface,则传递true会将此输出buffer在surface上渲染。一旦不再使用buffer,该surface将把buffer释放回codec。

示例代码:

 public void queueEncode(byte[] buffer){if(!isRecording){Log.e("xxx","没开始录制");return;}Log.e("xxx","开始录制");mHandler.post(new Runnable() {@Overridepublic void run() {//立即得到有效输入缓冲区int index=mediaCodec.dequeueInputBuffer(0);if(index>=0){ByteBuffer inputBuffer=mediaCodec.getInputBuffer(index);inputBuffer.clear();inputBuffer.put(buffer,0,buffer.length);//填充数据后再加入队列mediaCodec.queueInputBuffer(index,0,buffer.length,System.nanoTime()/1000,0);}while (true){//获取输出缓冲区(编码后的数据从缓冲区获取)MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();int encoderStatus = mediaCodec.dequeueOutputBuffer(bufferInfo, 10_000);//稍后重试if(encoderStatus== MediaCodec.INFO_TRY_AGAIN_LATER){break;}else if(encoderStatus==MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){//输出格式发生变化 第一次总会调用,所以在这里开启混合器MediaFormat newFormat=mediaCodec.getOutputFormat();videoTrack=mMuxer.addTrack(newFormat);mMuxer.start();}else if(encoderStatus==MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED){//可以忽略}else{//正常则encoderStatus 获取缓冲区下标ByteBuffer encodedData=mediaCodec.getOutputBuffer(encoderStatus);//如果当前的buffer是配置信息,不管它,不用写出去if((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG)!=0){bufferInfo.size=0;}if(bufferInfo.size!=0){//设置从哪里开始读数据(读出来就是编码后的数据)encodedData.position(bufferInfo.offset);//设置能读数据的总长度encodedData.limit(bufferInfo.offset+bufferInfo.size);//写出为MP4mMuxer.writeSampleData(videoTrack,encodedData,bufferInfo);}//释放这个缓冲区,后续可以存放新的编码后的数据mediaCodec.releaseOutputBuffer(encoderStatus,false);}}}});}

4.MediaCodec编码的工作方式


简单来说,可以看做是一个生产者-消费者模式,在MediaCodec里面有两个队列,一个是输入队列,一个是输出队列,数据放入输入队列,MediaCodec拿出数据进行编码,把编码完的数据放入输出队列。

 5.MediaCodec状态周期图


在MediaCodec的生命周期内存在3种状态,即Stopped、Executing和Released。

Stopped状态还可处于三个状态:Uninitialized、Configures和Error。

Executing状态概念上的进展通过3个子状态进行:Flushed、Running和end-of-Stream。

MediaCodec状态周期图如下图:

 6.使用缓冲区的异步和同步处理


使用缓冲区的异步处理:

在异步模式下,媒体编解码器通常像这样使用:

MediaCodec codec = MediaCodec.createByCodecName(name);MediaFormat mOutputFormat; // member variablecodec.setCallback(new MediaCodec.Callback() {@Overridevoid onInputBufferAvailable(MediaCodec mc, int inputBufferId) {ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId);// fill inputBuffer with valid data…codec.queueInputBuffer(inputBufferId, …);}@Overridevoid onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) {ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A// bufferFormat is equivalent to mOutputFormat// outputBuffer is ready to be processed or rendered.…codec.releaseOutputBuffer(outputBufferId, …);}@Overridevoid onOutputFormatChanged(MediaCodec mc, MediaFormat format) {// Subsequent data will conform to new format.// Can ignore if using getOutputFormat(outputBufferId)mOutputFormat = format; // option B}@Overridevoid onError(…) {…}@Overridevoid onCryptoError(…) {…}});codec.configure(format, …);mOutputFormat = codec.getOutputFormat(); // option Bcodec.start();// wait for processing to completecodec.stop();codec.release();

 使用缓冲区的同步处理:

媒体编解码器在同步模式下通常这样使用:

MediaCodec codec = MediaCodec.createByCodecName(name);codec.configure(format, …);MediaFormat outputFormat = codec.getOutputFormat(); // option Bcodec.start();for (;;) {int inputBufferId = codec.dequeueInputBuffer(timeoutUs);if (inputBufferId >= 0) {ByteBuffer inputBuffer = codec.getInputBuffer(…);// fill inputBuffer with valid data…codec.queueInputBuffer(inputBufferId, …);}int outputBufferId = codec.dequeueOutputBuffer(…);if (outputBufferId >= 0) {ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A// bufferFormat is identical to outputFormat// outputBuffer is ready to be processed or rendered.…codec.releaseOutputBuffer(outputBufferId, …);} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {// Subsequent data will conform to new format.// Can ignore if using getOutputFormat(outputBufferId)outputFormat = codec.getOutputFormat(); // option B}}codec.stop();codec.release();

7.录制纯视频demo

功能介绍:

实现Camera1采集相机数据,设置预览画布TextureView显示采集的数据,然后MediaCode对图像数据编码,最后MediaMuxer把图像数据放入MP4盒子。

运行图:

这是我的项目代码:

(小白一个,代码只是能简单地实现功能)

https://github.com/Bookonpillow/mediacode

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

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

相关文章

2023年十大开源项目:革新技术创新

来源整理 : 小托 | 开源社翻译组PM 翻译 : 张锋 | 开源社翻译 Open-source projects have revolutionized the world of software development by fostering innovation, collaboration, and community-driven contributions. These projects are often the backbone of countl…

iOS应用程序的签名、重签名和安装测试

目录 前言 打开要处理的IPA文件 设置签名使用的证书和描述文件 开始ios ipa重签名 前言 ipa编译出来后&#xff0c;或者ipa进行修改后&#xff0c;需要进行重新签名才能安装到测试手机&#xff0c;或者提交app store供apple 商店审核上架。ipaguard有签名和重签名功能&…

vue前端项目中添加独立的静态资源

如果想要在vue项目中放一些独立的静态资源&#xff0c;比如html文件或者用于下载的业务模板或其他文件等&#xff0c;需要在vue打包的时候指定一下静态资源的位置和打包后的目标位置。 使用的是 copy-webpack-plugin 插件&#xff0c;如果没有安装则需要先安装一下&#xff0c;…

记一次实战案例

1、目标&#xff1a;inurl:news.php?id URL&#xff1a;https://www.lghk.com/news.php?id5 网站标题&#xff1a;趋时珠宝首饰有限公司 手工基础判断&#xff1a; And用法 and 11: 这个条件始终是为真的, 也就是说, 存在SQL注入的话, 这个and 11的返回结果必定是和正常页…

【深度学习实验】卷积神经网络(三):自定义二维卷积层:步长、填充、输入输出通道

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. 步长、填充 a. 二维互相关运算&#xff08;corr2d&#xff09; b. 二维卷积层类&#xff08;Conv2D&#xff09; c. 模型测试 d. 代码整合 2. 输入输出通道 a…

【冰糖R语言】创建R包(打包R程序)

目标&#xff1a;将现有R程序打包 可能涉及知识点&#xff1a;devtools包、usethis包、Rstudio软件 一、R包的类型 通常一个R包中包含以下元素&#xff1a; 1&#xff09;R文件夹&#xff1a;函数代码 2&#xff09;man文件夹&#xff1a;存放每个函数的注释文件 3&#x…

KNN(上):数据分析 | 数据挖掘 | 十大算法之一

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…

36 二叉树中序遍历

二叉树中序遍历 题解1 递归题解2 迭代 给定一个二叉树的根节点 root &#xff0c;返回它的 中序 遍历 。 提示&#xff1a; 树中节点数目在范围 [0, 100] 内-100 < Node.val < 100 进阶: 递归算法很简单&#xff0c;你可以通过迭代算法完成吗&#xff1f; 题解1 递归…

解决大模型行业落地三大挑战,华为云GaussDB向量数据库正式发布

随着AI大模型产品及应用呈现爆发式增长,新的AI时代已经到来。向量数据库可与大语言模型配合使用,解决大模型落地过程中的痛点,已成为企业数据处理和应用大模型的必选项。在近日举行的华为全联接大会2023期间,华为云正式发布GaussDB向量数据库。GaussDB向量数据库基于GaussD…

Nodejs+vue高校机房设备管理系统jt07u

开发语言 node.js 框架&#xff1a;Express 前端:Vue.js 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;VScode 集成IDE对高校机房设备管理系统统进行开发,整合系统的各个模块。 拟开发的高校机房设备管理系统通过测试,确保在最大负载的情况下…

亚马逊无线鼠标FCC认证办理 FCC ID

无线鼠标是指无线缆直接连接到主机的鼠标&#xff0c;采用无线技术与计算机通信&#xff0c;从而省却电线的束缚。通常采用无线通信方式&#xff0c;包括蓝牙、Wi-Fi (IEEE 802.11)、Infrared (IrDA)、ZigBee (IEEE 802.15.4)等多个无线技术标准。随着人们对办公环境和操作便捷…

Polygon Miden:扩展以太坊功能集的ZK-optimized rollup

1. 引言 Polygon Miden定位为zkVM&#xff0c;定于2023年Q4上公开测试网。 zk、zkVM、zkEVM及其未来中指出&#xff0c;当前主要有3种类型的zkVM&#xff0c;括号内为其相应的指令集&#xff1a; mainstream&#xff08;WASM, RISC-V&#xff09;EVM&#xff08;EVM bytecod…

C理解(一):内存与位操作

本文主要探讨C语言的内存和为操作操作相关知识。 冯诺依曼结构和哈佛结构 冯诺依曼结构&#xff1a;数据和代码放在一起,便于读取和修改,安全性低 哈佛结构是&#xff1a;数据和代码分开存放,安全性高,读取和修麻烦 内存 内存是用来存储全局变量、局…

Cloudflare进阶技巧:缓存利用最大化

1. 引言 cloudflare我想你应该知道是什么&#xff0c;一家真正意义上免费无限量的CDN&#xff0c;至今未曾有哥们喷它的。当然&#xff0c;在国内的速度确实比较一般&#xff0c;不过这也不能怪它。 CDN最大的特色&#xff0c;我想就是它的缓存功能&#xff0c;达到防攻击&am…

云原生之使用Docker部署PDF多功能工具Stirling-PDF

云原生之使用Docker部署PDF多功能工具Stirling-PDF 一、Stirling-PDF介绍1.1 Stirling-PDF简介1.2 Stirling-PDF功能 二、本次实践规划2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Stirli…

redis查看耗时久的命令

redis查看耗时久的命令主要有两招&#xff1a;latency和slow log 【latency】 在Redis中&#xff0c;latency命令用于监视和测量Redis实例的延迟。 先进入redis: redis-cli -h 127.0.0.1 -p 24000[查看延迟监视器阈值] CONFIG GET latency-monitor-threshold这个值返回0&…

【计算机视觉】3.传统计算机视觉方法

传统计算机视觉方法 一、大纲图像分割人脸检测行人检测 二、图像分割基于阈值检测的方法基于边缘检测的方法基于区域的分割方法基于图论的分割方法 三、人脸检测四、行人检测五、SVM六、DPM 一、大纲 图像分割 基于阈值、基于边缘 基于区域、基于图论 人脸检测 Haar-like 特征…

Java实现使用多线程,实现复制文件到另一个目录,起不一样的名字,创建100万个数据

目录 1 需求2 实现 1 需求 我现在有一个300MB 的文件&#xff0c;想要根据这个文件&#xff0c;创建100万个大小一样的&#xff0c;名称不一样&#xff0c;如何实现&#xff0c;如何比较快点实现 2 实现 1 先准备好这个文件 2 准备好目录 3 写代码 private static void crea…

python:bottle + eel 模仿 mdict 查英汉词典

Eel 是一个轻量的 Python 库&#xff0c;用于制作简单的类似于离线 HTML/JS GUI 应用程序&#xff0c;并具有对 Python 功能和库的完全访问权限。 Eel 托管一个本地 Web 服务器&#xff0c;允许您使用 Python 注释函数&#xff08;annotate functions&#xff09;&#xff0c;…

(附源码)springboot体检预约APP 计算机毕设16370

目 录 摘要 1 绪论 1.1开发背景 1.2研究现状 1.3springboot框架介绍 1.4论文结构与章节安排 2 Springboot体检预约APP系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 操作可行性分析 2.2 系统流程分析 2.2.1 数据添加流程 2.2.2 数据…