Android上HDMI介绍(基于高通平台)
本文重点针对HDMI在android上的应用,而比较相关的就是overlay机制。overlay在这里只是简单的介绍,后续会有文章再专门详述。
我没记错的话,高通从7X30开始,平台就可以支持HDMI(1.3)输出了。只不过在7x30上通过RGB接口外接一颗HDMI的transmitter来实现;而到了8系列(8x60),高通把这颗IC也集成了,直接就提供HDMI的输出了。(这样下去,以后渐渐的把外围器件都集成了,做底层的估计要失业了,做硬件的似乎工作量也没多少了)。
先来看看HW的能力,下图是MDP4.0的结构图:

可以看到,MDP4内部有4个overlay pipe,2个是for UI(RGB)的,2个VG是for video和graphics的;另外有2个mixer,
mixer1是for primary display的(可以是MDDI接口的,也可以是RGB接口的lcd、oled等);mixer2是for external display的,
如通过RGB interface2外接HDMI transmitter到TV,也可以是NTSC/PAL等模拟电视信号。
Note:VG1和RGB1被mixer1混合到primary lcd,VG2和RGB2被mixer2混合到external LCD(如HDMI TV)
如果是MDP4.1的话,MDDI接口被移除了,另外RGB接口只有一个,另一个内部集成为HDMI接口了。
上面提到的是硬件平台相关的,就是说硬件有支持HDMI输出的能力,但是软件的状况呢?我们来看看Android和高通的状况。
关于HDMI本身,我就不介绍了,网上随便找找都可以看明白。
研究过Android的都知道,surfacefinger负责管理应用程序的显示窗口,每个窗口可以由多个surface组成,surfaceflinger通过OpenGL(可以通过HW,也可以是SW)把所有的surface合成后,通过调用gralloc模块或是overlay模块(MDP4.X才支持)把整屏数据送到显示设备上。可是Android(截止到2.2,3.0的状况还未知)上目前只支持一个显示设备,也就是说在surfaceflinger只能固定一个显示设备,那么HDMI这个应用在android手机上如何应用呢?
这里介绍2个做法,一个是高通给做好的,叫做UI mirroring和video mirroring;另一个就是我们自己添加接口,AP自己来实现想要的功能。
先来看高通在android中的做法,根据字面不难理解,UI mirroring和video mirroring其实就是把原来显示在primary LCD上的数据mirror到HDMI接口。下图为软件框架图:


先来看看HDMI的控制方面,上图的右侧,user空间中有一个HDMI service,包含一个listener(都是java的),当HDMI cable插入后,底层HDMI的驱动检测到(HPD)后,通过kobject_uevent传送给HDMI daemon,daemon再把event发送给HDMI service,HDMI service除了判断这个event(cable状态),另外还要判断qualcomm setting中HDMI的on/off选项,然后把判断结果broadcast给各个AP,各个AP也就知道当前是否要开启HDMI输出了
接着先看UI mirroring(不含video的状况)的实现,它针对的是界面的操作,数据为RGB格式。我们知道在kernel中每个显示设备都对应一个fb,初始化时都会分配framebuffer,在这里,primary lcd对应fb0设备,HDMI对应fb1设备。正常情况下,surfaceflinger合成好一个main surface后,通过post buffer(gralloc模块)把数据放入fb0,然后,通过overlay(kernel下做的,上层看到的还是通过IOCRL-FBIOPUT_VSCREENINFO命令实现)输出到primary lcd;当平台支持HDMI并且UI mirroring开启时,gralloc中(framebuffer.cpp)初始化时会多创建一个task(hdmi_ui_loop),并新建一个overlay(主要是控制和数据,参考overlaylib.h),这个overlay对应的channel固定为fb1,src fd就是fb0,也就是说这个overlay的源数据就是fb0,也就是primary lcd上的数据,通过rotator进行旋转(电视是横屏),然后在overlay中再scale up后再通过HDMI送到TV。这样看来,送到HDMI上的数据其实就是把fb0中的数据copybit了一份并放大,多少会有些失真的,但对于UI界面来说是可以接受的。上述整个过程,surfaceflinger是不参与的。
再来看video mirroring是怎么做的?
先来看看什么是video mirroring,其实就是手机播放视频,同时通过HDMI输出到TV上,手机上的内容分为2个部分,一个是视频本身部分,另一个是UI,这已经占用2个overlay pipe了(一个VG pipe,一个RGB pipe),TV上视频部分肯定是需要一个VG pipe的,另外,由于视频大小问题,视频不可能正好为全屏模式,这样必须还需要一个RGB pipe来实现一个背景(全黑)。4个pipe都被占用了,没有多余的pipe来把UI部分传到TV上,所以再使用高通平台时候,进行video mirroring时,TV上只能播放视频画面,UI部分(如菜单)在TV上是无法显示的。
接着来看video部分是怎么处理的?首先手机端UI部分的处理模式不变,只不过上面提到的hdmi_ui_loop这个task会被停掉(UI不需要送到HDMI,原因上面已经解释过);video部分的frame通过opencore解码出来后,首先会通过surfaceflinger来创建overlay(参考layerbuffer.cpp),当系统支持HDMI时通过create overlay都会创建2个通道(这里是2个VG通道),其中包含2个control channel和2个data channel,它们的HAL层接口都再overlaylib.cpp中,channel0 for fb0;channel1 for fb1,如果需要旋转,则从系统pmem中再分配对应的内存。AP中overlay基本上的流程是这样的(可以参考overlays.cpp,里面不全,我补充了一些):
sp<SurfaceComposerClient> client = new SurfaceComposerClient();//新建surface客户端
// create pushbuffer surface sp<Surface> surface = client->createSurface(getpid(), 0, 320, 240, PIXEL_FORMAT_UNKNOWN, ISurfaceComposer::ePushBuffers);//创建一个surface
// get to the isurface sp<ISurface> isurface = Test::getISurface(surface);//得到surface相关接口 printf("isurface = %p/n", isurface.get()); // now request an overlay sp<OverlayRef> ref = isurface->createOverlay(320, 240, PIXEL_FORMAT_RGB_565);//创建overlay,并得到控制通道 sp<Overlay> overlay = new Overlay(ref);//初始化overlay并得到数据通道
overlay->setFd(mFd);//设置src data的fd
overlay->setCrop(x,y,w,h);//设置剪裁信息(根据需要)
overlay->queueBuffer(offset);//设置显示数据的偏移
这样video player没解码出一个frame,都会调用quene函数把数据送入2个数据通道,overlay engine会把数据送到2个显示设备。
关于那个背景,暂时在code中还没发现,也许是因为目前的版本不是最终版本,后续还会更新。
上面的做法是高通的;但它是有限制的,比如说无法在2个屏幕上显示不同的内容。如果我们要做,也是可以的,主要就是看AP怎么定义规则了。另外framework中需要添加接口,主要是提供一个针对fb1设备的控制接口,同样也是绕过surfaceflinger。比如说手机在播放一个影片,通过HDMI把影片传送到TV上,同时手机端可以去浏览网页。这个功能可以这样做,AP背景播放影片,得到的frame不送到primary display上,而是通过新加的接口输出到fb1设备上,而browser的UI正常显示即可。如果是在高通平台去实现的话,需要把qualcomm setting里面的HDMI选项关掉,否则高通的做法和你自己AP的做法就乱套了。不过目前看,高通提供的方式似乎也可以满足应用了,但应用是永无止境的,只要user有这样的需求,developer就要去做,呵呵!
Android上HDMI介绍(基于高通平台)的更多相关文章
- Android高通平台调试Camera驱动全纪录
项目比较紧,3周内把一个带有外置ISP,MIPI数据通信,800万像素的camera从无驱动到实现客户全部需求. 1日 搭平台,建环境,编译内核,烧写代码. 我是一直在Window下搭个虚拟机登服务器 ...
- 高通平台启动log概述(PBL log、sbl1 log、kernel log)【转】
本文转自:https://blog.csdn.net/RadianceBlau/article/details/78416776?utm_source=blogxgwz9 高通平台启动log概述(PB ...
- Android:高通平台性能调试
1.GPU / CPU 信息打印脚本 gpu_cpu_info.bat @echo off echo "==================GPU====================== ...
- 高通平台如何避免误入FFBM模式
前面两篇博客分别介绍了通过fastboot和QFIL工具退出FFBM模式的方法.虽然售后的同学可以这么指导用户做恢复,但步骤多操作也麻烦,且属于事后处理,如果大面积高概率地出现,会严重影响用户体验.这 ...
- 高通平台点亮LCD个人总结
点击打开链接 高通平台LCD模块大致分为两部分:KERNEL和LK.在进行点屏之前,应该认真查看LCD原理图,弄清楚LCD亮屏需要满足的条件和上电时序,同时可以跟LCD IC原厂拿到初始化代码. 首先 ...
- 高通平台读写nv总结
一,引言 1. 什么是NV 高通平台的NV,保存了系统运行过程中各个模块可能用到的一些参数值,它是以单个文件的形式保存在EFS中,但用户是不能随意访问的,只能通过QXDM来进行读 ...
- 高通平台读写nv总结【转】
本文转载自:https://blog.csdn.net/suofeng12345/article/details/52713993 一,引言 1. 什么是NV 高通平台的NV,保 ...
- 高通平台手机开发之LCD
4.1. LCD 参考文档: 1) 80-NA157-174_E_DSI_Programing_Guide_B-Family_Android_Devices.pdf 2) 80-NN766-1_A_L ...
- 高通平台获取secure boot,串号等状态
adb shell下 运行./system/bin/r address 其中address对应各个flag参数的地址,具体如下: 无法打开/dev/mem节点(没有该节点),这时只需在内核配置中选上C ...
随机推荐
- 【bzoj5146】有趣的概率 微积分
题目描述 "可爱的妹子就像有理数一样多,但是我们知道的,你在数轴上随便取一个点取到有理数的概率总是0,"芽衣在床上自顾自的说着这句充满哲理的话,"诶,柚子,我写完概率论的 ...
- canvas - 简单的神经网络
1.国际惯例,先上效果图 一下效果图使用三次贝塞尔曲线进行连线,代码中有直接使用直线连线的代码,可直使用. 2.查看演示请看 这里. 3 代码 html: <canvas id=&quo ...
- 网络流24题之星际转移问题(洛谷P2754)
洛谷 P2754 题目背景 none! 题目描述 由于人类对自然资源的消耗,人们意识到大约在 2300 年之后,地球就不能再居住了.于是在月球上建立了新的绿地,以便在需要时移民.令人意想不到的是,21 ...
- c# partial使用
1.有2个类 class1.cs ,class2.cs 2.这2个类里面都可以定义成这样 public partial class ClassAll { } 3.结果,里面的方法都是共享的,就像 ...
- 3.7 TCP拥塞控制
3.7 TCP拥塞控制 在3.5.5流量控制中有,接收方通过维护一个rwnd来控制流量,本节中考虑三个问题: 第一, 一个TCP发送方如何限制它向其他连接发送流量的速率. 第二, 一个TCP发送方 ...
- P3932 浮游大陆的68号岛 【线段树】
P3932 浮游大陆的68号岛 有一天小妖精们又在做游戏.这个游戏是这样的. 妖精仓库的储物点可以看做在一个数轴上.每一个储物点会有一些东西,同时他们之间存在距离. 每次他们会选出一个小妖精,然后剩下 ...
- Linux内核分析第六周学习笔记——分析Linux内核创建一个新进程的过程
Linux内核分析第六周学习笔记--分析Linux内核创建一个新进程的过程 zl + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/U ...
- HUD.2795 Billboard ( 线段树 区间最值 单点更新 单点查询 建树技巧)
HUD.2795 Billboard ( 线段树 区间最值 单点更新 单点查询 建树技巧) 题意分析 题目大意:一个h*w的公告牌,要在其上贴公告. 输入的是1*wi的w值,这些是公告的尺寸. 贴公告 ...
- js new date()说明
javaScript UTC() 方法: UTC() 方法可根据世界时返回 1970 年 1 月 1 日 到指定日期的毫秒数. 要创建一个一个日期对象,可以使用以下的方式: var now=new D ...
- static_cast 和 dynamic_cast
1.static_cast static_cast < type-id > ( expression ) 该运算符把expression转换为type-id类型,但没有运行时类型检查来保证 ...