【Android】MediaCodec学习

在开源Android屏幕投屏代码scrcpy中,使用了MediaCodec去获取和display关联的surface的内容,再通过写fd的方式(socket等)传给PC端,

MediaCodec的处理看起来比较清楚,数据in和数据out

这里我们做另外一个尝试,读取手机中的mp4文件,显示到app的surface上,来学习MediaCodec的使用。

code

import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import androidx.appcompat.app.AppCompatActivity;
import java.io.IOException;public class PlayActivity2 extends AppCompatActivity implements SurfaceHolder.Callback {private static final int REQUEST_PERMISSION = 1;private static final String SAMPLE_MP4_FILE = "/sdcard/Download/test.mp4";private SurfaceView surfaceView;private MediaExtractor mediaExtractor;private MediaCodec mediaCodec;private boolean isPlaying = false;private String TAG = "testPlay";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_new);Log.i(TAG, "onCreate");surfaceView = findViewById(R.id.surfaceView);surfaceView.getHolder().addCallback(this);}@Overrideprotected void onResume() {super.onResume();Log.i(TAG, "onResume");if (!isPlaying) {Log.i(TAG, "set isPlaying true");isPlaying = true;//        playVideo();}}@Overrideprotected void onPause() {super.onPause();if (isPlaying) {Log.i(TAG, "onPause");isPlaying = false;releaseMediaCodec();}}@Overridepublic void surfaceCreated(SurfaceHolder holder) {isPlaying = true;Log.i(TAG, "surfaceCreated");//需要另外启动一个线程去处理new Thread() {@Overridepublic void run() {playVideo();}}.start();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {Log.i(TAG, "surfaceChanged");}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {Log.i(TAG, "surfaceDestroyed");releaseMediaCodec();}private void playVideo() {try {Log.i(TAG, "playVideo");mediaExtractor = new MediaExtractor();mediaExtractor.setDataSource(SAMPLE_MP4_FILE);int videoTrackIndex = getVideoTrackIndex();if (videoTrackIndex >= 0) {MediaFormat format = mediaExtractor.getTrackFormat(videoTrackIndex);String mimeType = format.getString(MediaFormat.KEY_MIME);mediaCodec = MediaCodec.createDecoderByType(mimeType);Surface surface = surfaceView.getHolder().getSurface();mediaCodec.configure(format, surface, null, 0);mediaCodec.start();Log.i(TAG, "mediaCodec.start");decodeFrames(videoTrackIndex);}} catch (IOException e) {e.printStackTrace();}}private int getVideoTrackIndex() {for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {MediaFormat format = mediaExtractor.getTrackFormat(i);String mime = format.getString(MediaFormat.KEY_MIME);if (mime.startsWith("video/")) {mediaExtractor.selectTrack(i);return i;}}return -1;}private void decodeFrames(int videoTrackIndex) {boolean isEOS = false;final int TIMEOUT_US = 10000;while (!Thread.interrupted()) {if (!isPlaying)break;Log.i(TAG, "decodeFrames=, isPlaying=" + isPlaying);int inputBufferIndex = mediaCodec.dequeueInputBuffer(TIMEOUT_US);Log.i(TAG, "inputBufferIndex=" + inputBufferIndex);if (inputBufferIndex >= 0) {int sampleSize = mediaExtractor.readSampleData(mediaCodec.getInputBuffer(inputBufferIndex), 0);if (sampleSize < 0) {isEOS = true;sampleSize = 0;}long presentationTimeUs = mediaExtractor.getSampleTime();mediaCodec.queueInputBuffer(inputBufferIndex, 0, sampleSize, presentationTimeUs, isEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);if (!isEOS) {Log.i(TAG, "mediaExtractor.advance()=" + sampleSize);mediaExtractor.advance();}}MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US);Log.i(TAG, "outputBufferIndex======" + outputBufferIndex);if (outputBufferIndex >= 0) {mediaCodec.releaseOutputBuffer(outputBufferIndex, true);if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {Log.i(TAG, "inputBufferIndex=, break");break;}}try {Thread.sleep(10);} catch (Exception e) {}}}private void releaseMediaCodec() {if (mediaCodec != null) {mediaCodec.stop();mediaCodec.release();mediaCodec = null;}if (mediaExtractor != null) {mediaExtractor.release();mediaExtractor = null;}}
}

注意,这里的mp4文件放在了sdcard中,需要获取读取权限

public void requestPermission() {if (Build.VERSION.SDK_INT >= 30) {if (!Environment.isExternalStorageManager()) {Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);startActivity(intent);return;}} else {if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {if (PermissionChecker.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PermissionChecker.PERMISSION_GRANTED) {requestPermissions(requestPermission, requestPermissionCode);}}}
}

activity_new.xml里定义一个SurfaceView

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".newActivity"><SurfaceViewandroid:id="@+id/surfaceView"android:layout_width="match_parent"android:layout_height="match_parent" /></androidx.constraintlayout.widget.ConstraintLayout>

playVideo的处理需要在另外一个线程中执行,不能在主线程执行,不然只能显示停止的一个画面。
01-28 18:14:19.388 21442 21442 I testPlay: set isPlaying true
01-28 18:14:19.431 21442 21442 I testPlay: surfaceCreated
01-28 18:14:19.431 21442 21442 I testPlay: playVideo
01-28 18:14:19.479 21442 21442 I testPlay: mediaCodec.start
01-28 18:14:19.479 21442 21442 I testPlay: decodeFrames=, isPlaying=true
01-28 18:14:19.480 21442 21442 I testPlay: inputBufferIndex=2
01-28 18:14:19.483 21442 21442 I testPlay: mediaExtractor.advance()=85878
01-28 18:14:19.493 21442 21442 I testPlay: outputBufferIndex======-1
01-28 18:14:19.504 21442 21442 I testPlay: decodeFrames=, isPlaying=true
01-28 18:14:19.504 21442 21442 I testPlay: inputBufferIndex=3
01-28 18:14:19.507 21442 21442 I testPlay: mediaExtractor.advance()=3049

在上述代码中,视频帧是通过 MediaCodec 解码后,使用 Surface 对象在 SurfaceView 上进行渲染的。

以下代码片段展示了视频帧的渲染过程:

MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US);
if (outputBufferIndex >= 0) {mediaCodec.releaseOutputBuffer(outputBufferIndex, true);if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {break;}
}

在每次循环中,首先调用 dequeueOutputBuffer() 方法来获取可用的输出缓冲区的索引。如果返回的索引大于等于0,则说明有可用的输出缓冲区。

然后,通过调用 releaseOutputBuffer() 方法,将输出缓冲区的索引传递给 MediaCodec,通知它可以释放该缓冲区并将其渲染到指定的 Surface 上。

最后,检查 BufferInfo 的 flags 标志,如果标志中包含 BUFFER_FLAG_END_OF_STREAM,则说明已经解码并渲染完整个视频帧序列,可以退出循环。

在循环中不断解码和渲染视频帧,就可以在 SurfaceView 上实时显示视频内容。

参考资料

Android MediaCodec解析-CSDN博客

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

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

相关文章

AI嵌入式K210项目(19)-安装CanMV IDE开发软件

文章目录 前言一、软件下载安装二、软件简介三、设备连接四、在线模拟五、开机运行程序附录&#xff1a;MicroPython固件烧录总结 前言 前几章我们介绍K210使用C语言裸机开发方法&#xff0c;大家对K210内部的硬件和各种加速器有了初步的了解&#xff0c;但是开发人工智能相关…

TypeScript(四) 运算符

1. 运算符 1.1. 描述 运算符用于执行程序代码运算。 1.2. 运算符主要包括&#xff1a; &#xff08;1&#xff09;算术运算符 &#xff08;2&#xff09;逻辑运算符 &#xff08;3&#xff09;关系运算符 &#xff08;4&#xff09;按位运算符 &#xff08;5&#xff09;赋值…

内部类 --java学习笔记

内部类 是类中的五大成分之一&#xff08;成员变量、方法、构造器、内部类、代码块&#xff09;&#xff0c;如果一个类定义在另一个类的内部&#xff0c;那么这个类就是内部类当一个类的内部包含了一个整体的事务&#xff0c;且这个事务没必要单独设计时&#xff0c;就可以把…

(十三)centos7案例实战——用户、用户组及用户权限管理

前言 对于完备的centos系统来说&#xff0c;创建一套规范的账户权限系统是十分必要的。针对不同的用户提供不同的操作权限&#xff0c;避免直接使用root账号&#xff0c;造成系统的误操作&#xff0c;导致一些不必要的损失&#xff0c;将删库跑路扼杀在摇篮中。本节内容会从用…

基于springboot校园台球厅人员与设备管理系统源码和论文

在Internet高速发展的今天&#xff0c;我们生活的各个领域都涉及到计算机的应用&#xff0c;其中包括校园台球厅人员与设备管理系统的网络应用&#xff0c;在外国管理系统已经是很普遍的方式&#xff0c;不过国内的管理网站可能还处于起步阶段。校园台球厅人员与设备管理系统具…

Fastbee物联网项目新手快速入门

一&#xff0c;前提条件 后端环境准备如下&#xff1a; 正式环境推荐硬件资源最低要求4c8G&#xff0c;硬盘40G。JDK 1.8.0_2xx (需要小版本号大于200) 。Maven3.6.3。&#xff08;IDEA启动时使用IDEA默认自带的版本即可&#xff09;。 启动fastbee之前&#xff0c;请先确定…

课时6:编程语言逻辑

1.2.2 编程语言逻辑 学习目标 这一节&#xff0c;我们从 语言分类、编程逻辑、小结 三个方面来学习。 语言分类 语言分类 低级编程语言&#xff1a;机器&#xff1a;- 二进制的0和1的序列&#xff0c;称为机器指令。- 一般人看不懂汇编&#xff1a;- 用一些助记符号替代机…

LPC804开发(4.ctimer使用)

1.前言 昨天晚上画完板子&#xff0c;还剩点时间就再翻了翻手册&#xff0c;大致清楚了时钟树的运行&#xff0c;顺带搞清楚了定时的使用&#xff0c;那就出一份教程吧。 如果各位在此之前没有接触过LPC单片机&#xff0c;还是建议先把程序直接贴进自己的项目&#xff0c;稍微…

“拔叔”的新片又杀疯了!

近&#xff0c;“拔叔”麦斯米科尔斯的新片登上口碑榜。 该片曾入围去年威尼斯金狮奖&#xff0c;上映后颇受好评&#xff0c;目前豆瓣评分8.2分。 将代表丹麦角逐2024年奥斯卡最佳外语片&#xff0c;目前已入15强。 作为“丹麦最性感的男人”&#xff0c;拔叔的实力不容小觑。…

Logistics 逻辑回归概念

1. sigmoid函数 逻辑回归算法的拟合函数&#xff0c;叫做sigmoid函数&#xff1a; 函数图像如下&#xff08;百度图片搜到的图&#xff09;&#xff1a; sigmoid函数是一个s形曲线&#xff0c;就像是阶跃函数的温和版&#xff0c;阶跃函数在0和1之间是突然的起跳&#xff0c;…

std::for_each

std::for_each 是 C STL 中的标准函数之一&#xff0c;用于对指定的容器或序列中的元素执行指定的操作。该函数的头文件为 <algorithm>&#xff0c;定义在 namespace std 中&#xff0c;因此需要包含该头文件才能使用该函数。 std::for_each 函数是 C STL 中自 C98 就开…

【JavaSE篇】——类和对象

目录 &#x1f393;类定义和使用 &#x1f393;简单认识类 &#x1f6a9;类的定义格式 ❗注意事项 &#x1f393;类的实例化 &#x1f6a9;什么是实例化 &#x1f6a9;类和对象的说明 &#x1f393;this引用 &#x1f6a9;为什么要有this引用 &#x1f6a9;什么是thi…

【JavaScript 基础入门】02 JavaScrip 详细介绍

JavaScrip 详细介绍 目录 JavaScrip 详细介绍1. JavaScript 是什么2. JavaScript的作用3. HTML/CSS/JS 的关系4. 浏览器执行 JS 简介5. JavaScript 的组成6. JavaScript 的特点 1. JavaScript 是什么 JavaScript&#xff0c;通常缩写为 JS&#xff0c;是一种高级的&#xff0c;…

分布式技术浅见之复制

分布式技术浅见之复制 前言一、何为复制&#xff1f;1.1 主从复制和无主从复制1.2 同步复制与异步复制1.3 来点栗子1.3.1 Redis1.3.2 Mysql1.3.3 Mongo 二、复制是如何实现的&#xff1f;2.1 复制的过程2.2 来点栗子2.2.1 Redis2.2.2 Mysql2.2.3 Mongo 2.2 复制的实现方式2.4 举…

OpenHarmony—不支持解构赋值

规则&#xff1a;arkts-no-destruct-assignment 级别&#xff1a;错误 ArkTS不支持解构赋值。可使用其他替代方法&#xff0c;例如&#xff0c;使用临时变量。 TypeScript let [one, two] [1, 2]; // 此处需要分号 [one, two] [two, one];let head, tail [head, ...tail]…

MySQL十部曲之六:数据操作语句(DML)

文章目录 前言语法约定DELETEINSERTSELECT查询列表SELECT 选项子句FROMWHEREORDER BYGROUP BYHAVINGWINDOWLIMITFOR SELECT ... INTO连接查询CROSS JOIN和INNER JOINON和USINGOUTER JOINNATURE JOIN 子查询标量子查询使用子查询进行比较带有ANY、IN或SOME的子查询带有ALL的子查…

DETR解读,将Transformer带入CV

论文出处 [2005.12872] End-to-End Object Detection with Transformers (arxiv.org) 一个前置知识 匈牙利算法&#xff1a;来源于二部图匹配&#xff0c;计算最小或最大匹配 算法操作&#xff1a;在n*n的矩阵中 减去行列最小值&#xff0c;更新矩阵&#xff08;此时行或者…

搜维尔科技:【简报】元宇宙数字人赛道,优秀作品赏析《大福太郎》

这次采用亮眼的浅粉做为发色&#xff0c;为了贴合她小警察的身分 给了她一顶特制的警帽&#xff0c;上面有大福的荧光蓝叶片作为标 志&#xff0c;而在配件及裙子上也加入了许多科技元素的小巧思。 学校&#xff1a; 朝阳科技大学&#xff08;台湾&#xff09; 选手&#xff…

消息中间件之RocketMQ(二)

RocketMQ支持的消息类型 了解之前&#xff0c;首先要熟悉RocketMQ中的组件架构设计 1.顺序消息 将同一个订单(即具有相同的orderId)的消息按状态先后顺序消费的&#xff0c;所以消息生产者调用send方法发送时需要传入MessageQueueSelector接口的,实现类&#xff0c;将order…

【GitHub项目推荐--开源PDF 工具】【转载】

12 年历史的 PDF 工具开源了 最近在整理 PDF 的时候&#xff0c;有一些需求普通的 PDF 编辑器没办法满足&#xff0c;比如 PDF 批量合并、编辑等。 于是&#xff0c;我就去 GitHub 上看一看有没有现成的轮子&#xff0c;发现了这个 PDF 神器「PDF 补丁丁」&#xff0c;让人惊…