本文重點針對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就要去做,呵呵!