android下播放器视频输出方法总结

2023-10-30

转自: http://m.myexception.cn/android/1790584.html

题记:

   bitmap,native_windows,opengles api, android_app,native player

      在Android下输出视频画面,有很多种方法,每个都有自己的特点,比如将视频数据送回到java层然后用lockCanvas画出来这种方法的特点就是慢.

    上面这个完全不值得提倡,视频数据从native层传到jni层很耗时间.

    开发基于ffmpeg的播放器时,可以使用ffmpeg的各种软解码器,也可以使用android带的OMXCodec解码器,OMXCodec解码器是对OMX的一个封装.其中ffmpeg解码器主要输出YUV420P的帧,而OMXCodec解码器输出的格式就多样化,因具体的平台不同而不同.

    使用OMXCodec解码器解出来的视频,可以让它自己输出,只要在打开解码器的时候给它传个ANativeWindow:

<span style="font-size:14px;"> OMXCodec::Create(
        const sp<IOMX> &omx,
        const sp<MetaData> &meta, bool createEncoder,
        const sp<MediaSource> &source,
        const char *matchComponentName,
        uint32_t flags,
        const sp<ANativeWindow> &nativeWindow</span>
<span style="font-size:14px;">   )</span>
<span style="font-size:14px;">
</span>
    解码函数read出来的是MediaBuffer,其实是GraphicBuffer,不需要关心它的格式,只需要queueBuffer它,然后设置好render完成,并release它:


<span style="font-size:14px;">g_ANativeWindow->queueBuffer (g_ANativeWindow.get(),mbuf->graphicBuffer()->getNativeBuffer());
...
mbuf->meta_data()->setInt32(kKeyRendered,1);
...
mbuf->release();//这个是告诉OMXCodec</span></span>

    值得说明的是,在创建OMXCodec的时候,也可以传入kKeyColorFormat来指定成想要的格式,但是不幸的是,有时候并不会支持你想要的,比如有的手机就不支持HAL_PIXEL_FORMAT_YV12,而是使用了一种内部格式,纠结吧,所以还是让它自己输出比较好.

    在采用ffmpeg解码器时,目前我所知道的,输出YUV420P有两种方法,一种是通过Lock ANativeWindow写入YUV数据,一种是通过opengles在片断着色器中将YUV数据通过公式转换成RGB输出.

    参考AwesomePlayer.cpp,会发现有个SoftwareRenderer的东东,这个类可以输出HAL_PIXEL_FORMAT_YV12, YUV420P->YV12的转换其实很简单,就是UV分量的存储位置不一样,拷贝时变换下就可以了.这个是Lock ANativeWindow的方法.里面还有一个AwesomeNativeWindowRenderer,其实就是上面提到的让OMXCodec自己去输出视频的方法.

    我在实践中发现,有些手机的SoftwareRenderer没有处理好,特别是刷第三方ROM的时候,会花屏,所以需要自己手动从SoftwareRenderer.cpp中提取代码,其实就是操作ANativeWindow的一些函数:


<span style="font-size:14px;">native_window_api_connect(gDirectRendererContext.anativeWindow.get(),NATIVE_WINDOW_API_CPU);
native_window_set_usage(gDirectRendererContext.anativeWindow.get(),GraphicBuffer::USAGE_SW_WRITE_OFTEN|GraphicBuffer::USAGE_HW_TEXTURE);
native_window_set_buffers_geometry(gDirectRendererContext.anativeWindow.get(),w,h,android_fmt);
native_window_set_crop(gDirectRendererContext.anativeWindow.get(),&android_crop);
native_window_set_scaling_mode(gDirectRendererContext.anativeWindow.get(),NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
ANativeWindow_lock(gDirectRendererContext.anativeWindow.get(), &buffer, NULL);
//写数据
ANativeWindow_unlockAndPost(gDirectRendererContext.anativeWindow.get());</span>

    

   上面的代码省略了很多,只是一个基本的流程.android的源码中还有一个gl2_yuvtex.cpp,里面有输出YUV的方法,是采用了初始化egl和opengles,然后创建GraphicBuffer写入数据,跟据GraphicBuffer创建EGLImageKHR,最后调用opengles的扩展    GL_OES_EGL_image_external 输出YUV,这种方法我也把它归属于LockAnativeWindow方法,因为这个方法就是上面的底层实现.

    Lock AnativeWindow方法有一个很大的缺点,所以我在最终的播放器代码中没有采用.在lockBuffer之后,我们可以拿到y分量的行字节数,即stride,但是没有办法获取到uv分量的stride,跟据一般情况下应该是y_stride/2然后按16字节对齐,但是有些厂商不按常理出牌,这个值不一定是这样的,导致结果就是花屏或者内存Abort,所以得适配手机,麻烦!

    再来说说使用opengles将YUV转换成RGB输出的方法,这种方法很早有就人使用了,最早的时候就使用软件将YUV转换成RGB输出,但是效率太低了,于是大神们将眼光投向了opengles的可编程片段着色器,将YUV的转换放到着色器去做,硬件处理是非常快的,这样就大大提高了效率,具体的做法是:初始化EGL(参考gl2_yuvtex.cpp)->创建着色器程序并编译连接->创建并上传纹理->画三角形.下面是顶点和纹理坐标,着色器代码:


<span style="font-size:14px;">static float squareV[]={
	-1.0f,-1.0f,
	1.0f,-1.0f,
	1.0f,1.0f,
	-1.0f,1.0f
};
static float coordV[]={
	0.0f,1.0f,
	1.0f,1.0f,
	1.0f,0.0f,
	0.0f,0.0f
};
static char vertexShaderCode[]=
			"attribute vec4 squareV;"
			"attribute vec2 coordV;"
			"varying vec2 tc;"
			"void main(){"
			"	gl_Position=squareV;"
			"	tc=coordV;"
			"}";

static char fragmentShaderCode_yuv420[]=
			"precision mediump float;"
			"uniform sampler2D tex_y;"
			"uniform sampler2D tex_u;"
			"uniform sampler2D tex_v;"
			"varying vec2 tc;"
			"void main(){"
			" mediump vec3 yuv;"
			" mediump vec3 rgb;"
			" yuv.x=texture2D(tex_y,tc).r;"
			" yuv.y=texture2D(tex_u,tc).r-0.5;"
			" yuv.z=texture2D(tex_v,tc).r-0.5;"
			" rgb=mat3("
			"          1,1,1,"
			"          0,-0.39456,2.03211,"
			"          1.13983,-0.58060,0)*yuv;"
			" gl_FragColor=vec4(rgb,1);"
			"}";</span></span>

    

    要注意的是,在NativeWindow改变大小里,需要重新创建EGLSurface和EGLContex,另外EGLContex和线程相关,是opengles的状态机.

    都是根据前人经验总结,如有不对,欢迎指正!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

android下播放器视频输出方法总结 的相关文章

  • linux gcc 头文件搜索路径

    linux gcc 头文件搜索路径 2011 02 15 13 40 423人阅读 评论 0 收藏 举报 一 头文件 gcc 在编译时寻找所需要的头文件 搜寻会从 I开始 然后找gcc的环境变量 C INCLUDE PATH CPLUS I
  • locust

    python做性能测试首选库 内部使用requests库 可以基本将接口自动化的代码搬过来 稍微修改即可 内部使用协程的模式 单机并发远超jmeter 第一步 安装 pip install locust 第二步 使用 创建一个locustf
  • semtic kityy数据集信息和工具可视化

    0 sem11 21的提交测试地址 https competitions codalab org competitions 24025 learn the details evaluation 1 数据集结构 instance是panopt
  • 模板全特化和偏特化

    模板特化就是告诉编译器我这有更好的实现 走我这边 能更快的执行 类模板特化分为全特化和偏特化 全特化就是对所有的模板类型给一个限定 偏特化只限定一部分 可分为个数偏 范围偏 具体如下代码 模板类 tempalte
  • CHECK TABLE语法--MySql数据库

    CHECK TABLE tbl name tbl name option option QUICK FAST MEDIUM EXTENDED CHANGED 检查一个或多个表是否有错误 CHECK TABLE对MyISAM和InnoDB表有
  • 正一派道家奇门遁甲排盘算法python程序

    正一派道家奇门遁甲排盘算法python程序程序 首先声明 这篇文章可能很快就会消失了 毕竟已经2020年了 还有很多人觉得奇门遁甲是 剪纸成人 撒豆成兵 的法术 其实奇门遁甲是一种符号预测学 这里我写了一个python程序来排盘 因为奇门遁
  • Moveit编程——moveit 编程技巧笔记——圆弧轨迹规划+修改轨迹

    moveit 编程技巧笔记 圆弧轨迹规划 修改轨迹 1 笛卡尔空间圆弧轨迹规划 2 轨迹重定义 修改moveit生成的轨迹数据 1 笛卡尔空间圆弧轨迹规划 之前学习过笛卡尔空间下轨迹规划API plan fraction arm compu
  • jsp 有哪些域对象和内置对象及他们的作用?

    四大域对象 1 pageContext page 域 指当前页面 在当前 jsp 页面有效 跳到其它页面失效 2 request request 域 指一次请求范围内有效 从 http 请求到服务器处理结束 返回响应的整个过程 在这个过程中
  • 快速排序的代码

    一 快速排序的思想 通过一趟排序将要排序的数据分割成独立的两部分 分割点左边都是比它小的数 右边都是比它大的数 二 实例演示 第一次排序过程中left始终小于right 当left right表示第一次排序完成 此时以3为分割点左边的数都比
  • 物联网传输协议MQTT

    本文转载至 http www infoq com cn news 2014 12 mqtt ibm iot utm campaign infoq content utm source infoq utm medium feed utm te
  • C++设计模式之原型模式

    举一个最简单的例子来说明原型模式 记得上小学的时候 老师把需要做的课外习题写到黑板上 而下面的我们都要把这些题抄写到自己的本子上 回家做好 第二天交上来 也就是每道题 全班50个人 每个人都要抄写一遍 按照现在的时间理论来说 就是浪费了50
  • 马士兵python_马士兵老师的python入门教程

    01 Python编程语言历史及特性 mp4 02 Python编程语言初接触 mp4 03 Python程序文件结构 mp4 04 准备Python编程环境 mp4 05 Python编程语言基础技术框架 1 mp4 06 Python编
  • 如何用python进行数据分析

    1 Python数据分析流程及学习路径 数据分析的流程概括起来主要是 读写 处理计算 分析建模和可视化四个部分 在不同的步骤中会用到不同的Python工具 每一步的主题也包含众多内容 根据每个部分需要用到的工具 Python数据分析的学习路
  • 02-18 周六 图解机器学习之SMV 第五章5-2

    02 18 周六 图解机器学习之SMV 第五章5 2 时间 版本 修改人 描述 2023年2月18日11 47 18 V0 1 宋全恒 新建文档 环境 程序的基本环境 是使用了jupyter 在容器中运行的 简介 本程序主要演示支持向量的获
  • sockboom 更新日志

    2023 09 04 主域名 sockboom link 订阅域名 sockboom me 邀请域名 sockboom love
  • Js课堂笔记(二)

    一 基础 1 js的语言特点 lt 1 gt 是一种脚本语言 lt 2 gt 是基于客户端 lt 3 gt 是基于对象 lt 4 gt 是在浏览器直接执行 2 1 NaN 表示不是数字 例 console log parseInt 小李 2
  • 《安卓逆向》Magisk的编译踩坑记录-安装方法-分享魔改后的Magisk过root检测方法

    为什么 1 体验下如何编译面具源码 2 魔改面具 绕过更深的root检测 1 ubuntu 虚拟机下载地址 http mirrors aliyun com ubuntu releases 20 04 github https github
  • QT从零开始作单片机上位机-串口调试助手+波形显示-实现串口模块的配置

    目录 实现串口模块的配置 1 一 先列举需要思考的问题 二 所有的问题要由简单到复杂逐一解决 实现串口模块的配置 1 完成了基本的界面设计后 我们就要着手实现功能 下来的几章我们看串口数据收发的功能怎么实现 首先我需要阐明的是 由于做嵌入式
  • Linux 忘记密码解决方法

    很多同学经常会忘记Linux系统的用户密码 忘记密码的情况该怎么办呢 一般情况下 忘记密码有两种 忘记忘记超级用户密码和普通用户密码 本文将分别介绍解决方法 解决方法如下 超级用户忘记密码 在开机时进入到这个界面按上下键 打断其自动选择系统

随机推荐

  • Web 前端—HTML+CSS系列

    HTML CSS 一 HTML CSS 1 1什么是HTML CSS 1 2宇宙第一编辑器VS Code 1 3Chrome浏览器 1 4 深入了解网站开发 一 HTML基本操作 1 web前端三大核心技术 2 HTML初始代码 3 HTM
  • #Vue# Vue使用print实现打印及另存为PDF功能

    步骤 1 在src utils里面加入print js 这个文件里面 也解决了 canvass echarts图表转为图片 的问题 打印类属性 方法定义 eslint disable const Print function dom opt
  • Vue之非父子组件通信

    Vue之非父子组件通信 非父子组件传值方法 1 在model中新建一个js文件 引入Vue 实例化Vue 最后暴露相关实例 引入vue import Vue from vue 创建一个空的vue实例 var bus new Vue 将实例暴
  • FOC之PI控制的理解

    PI控制器里 输入是电流差 被控制量是d q轴电压 通过适当的PI控制系数 使得可以在一定时间内将被控制量收敛到给定目标值上 存在一定允许的误差范围 输入和输出之间不存在静态数学关系 但是可以通过动态的传递函数使得输出可控 PI内部是没有物
  • 输入密码显示星号

    include
  • OPTEE的进程间通信(Inter-Process Communication, IPC)

    好久没有翻看这本书了 今天来看看 手机安全和可信应用开发指南 1 什么是IPC 进程间通信 Inter Process Communication IPC 机制是指系统中进程或线程之间的通信机制 用于实现线程与线程之间进行通信 数据交互等功
  • [填坑]QT信号与槽机制注意事项

    1 信号与槽机制与回调函数性能对比 信号与槽机制比回调函数的方式要慢 当槽函数是非虚函数时 信号与槽机制大约比回到函数机制慢10倍 但依旧能够满足大多数应用的需求 因为1秒钟可以出发200万次这样的信号 i586 500机器 1个信号绑定一
  • matlab_无约束的非线性优化

    模型 min f x s t x1 x x2 fminbnd 求定区间上单变量函数的最小值 x fval exitflag output fminbnd fun x1 x2 options fminunc 求解单变量及多变量的最小值 fmi
  • Servlet编程之会话管理

    目录 一 什么是会话管理 二 Cookie技术 2 1 Cookie技术的特点 2 2 Cookie技术的原理 2 3 Cookie技术核心API 2 4 示例 三 Session技术 3 1 Session技术的特点 3 2 Sessio
  • Visio文件编辑查看工具Visio Viewer for Mac

    Visio Viewer for Mac可以打开和查看Visio文件 vsd vdx和 vsdm文件 它具有简单易用的用户界面 可以快速加载和显示Visio文件 此外 它还支持导出文件为PDF PNG JPEG等格式 方便用户进行文件转换和
  • Java垃圾收集的困境与JVM的跨代引用解决方案

    垃圾收集 Garbage Collection GC 是Java语言中的一项重要特性 它可以自动管理内存并回收不再使用的对象 然而 垃圾收集面临着一些困境 其中之一是处理跨代引用的问题 在本文中 我们将探讨这个困境 并介绍JVM Java虚
  • web服务器原理

    目录 一 第一步 DNS域名解析 二 第二步 建立TCP连接 三 第三步 HTTP通信 四 HTTP请求报文 五 HTTP响应报文 六 第四步 关闭TCP连接 首先 举个例子 例如 在浏览器地址栏键入URL 按下回车之后会经历以下流程 1
  • CSS_高度自动过渡 auto height

    方法一 grid 布局中的 fr 单位 推荐使用 div class wrap div
  • _kbhit()以及_getch()函数的用法

    kbhit kbhit用来检测键bai盘是否有按键 有则返回 1 没有则返回0 函数名 kbhit 包含头文件 include
  • node.js模拟cookie信息请求数据

    有时候获取第三方服务器数据 抓取网站数据时 需要验证cookie信息才能拿到 用nodejs本身的 https模块设置请求头 再发送请求即可 方法 https request url options callback const https
  • 访问计算机需要用户名密码,访问局域网电脑提示需要用户名和密码该怎么办

    在日常工作中经常要建设局域网 如果访问局域网中工作组的电脑时提示需要用户名和密码怎么办 访问局域网电脑提示需要用户名和密码的解决方法其实很简单 下面由学习啦小编告诉你 访问局域网电脑提示需要用户名和密码的解决方法 如果我们访问对方电脑出现如
  • linux硬盘io高排错,磁盘I/O很高的解决思路

    介绍 磁盘IO突然很高是运维人员经常碰到的问题 这是由于有大量的磁盘读和写造成的 通常发生在数据库身上 然而发生的场景各种各样 本文举几个例子阐述解决思路 正文 找到是什么程序在大量的进行读写操作 可以通过监控软件 如zabbix 或工具
  • js的Math对象方法

  • Leetcode 刷题笔记(二) —— 数组类型解题方法二:双指针法

    文章目录 系列文章目录 快慢双指针法 27 移除元素 26 删除有序数组中的重复项 283 移动零 844 比较含退格的字符串 总结 首尾双指针法 977 有序数组的平方 系列文章目录 一 数组类型解题方法一 二分法 二 数组类型解题方法二
  • android下播放器视频输出方法总结

    转自 http m myexception cn android 1790584 html 题记 bitmap native windows opengles api android app native player 在Android下输