天天看點

一種基于安卓系統的雙屏異顯技術的實作

一.技術的背景

随着電梯傳媒行業的飛速發展,雙屏視訊播放機應運而生,客戶可以根據自己的需求,制作兩個螢幕的内容,分别播放适合螢幕顯示方式播放的内容,如上面播放視訊,下面播放圖檔,使得宣傳的效果多樣化。基于市場的需求,研發出了基于rk3288平台的雙屏異顯視訊播放機。

二.技術方案的具體實作

1.硬體層面的實作原理

RK3188 PX3 RK3288 RK3399 的 SOC 内部,都有內建兩個 LCDC 控制器,是以這就給雙

屏異顯功能的實作提供了基礎。那麼軟體上的實作就是通過開辟兩塊不同的 buffer,對應外部的

實體螢幕。然後每塊 buffer,通過不同 LCDC 送給對應的螢幕,即實作了雙屏異顯的功能。是以

這也是為什麼必須有兩塊 LCDC 的 SOC 才能去做雙屏異顯功能的原因。

我司采用的rk3288平台就有2個LCD控制器,可以外接兩個螢幕。我司采用的方案是,LCDC0接LVDS接口的螢幕(18.5寸的主屏),LCDC1接EDP接口的螢幕(10.1寸的副屏)。硬體邏輯圖如下所示:

一種基于安卓系統的雙屏異顯技術的實作

2.底層軟體層面的實作原理

預設情況下,我司rk3288晶片上跑的Android 6.0系統隻能通過LCD0控制器輸出圖像給一個螢幕顯示。要支援同時通過LCDC0和LCDC1輸出圖像給兩個螢幕顯示,必須修改代碼。經過分析軟體實作的原理圖如下:

一種基于安卓系統的雙屏異顯技術的實作

下面是對上面原理圖的解析:

a.保證兩個螢幕單獨作為主屏的時候都可以正常顯示,這樣子保證了兩個螢幕的屏參都是正确的,而且兩個螢幕的實體連接配接都正常。

開始調試兩個螢幕同時顯示

b.雙屏顯示控制部分的實作:預設情況下rk_screen.c驅動代碼裡隻會去解析dts裡主屏的屏參,在裡面加入代碼使得根據id同時去解析副屏的屏參。然後LCDC0的驅動rk32_lvds.c以及LCDC1的驅動rk32_dp.c同時去擷取螢幕的相關參數(讀取剛才rk_screen.c裡配置的相關結構體)。這樣子兩個螢幕的LCD控制器部分的驅動都完善了。

c.雙屏顯示資料部分的實作:已知android系統抽象出FrameBuffer這個裝置來供使用者态程序實作直接寫屏。FrameBuffer機制模仿顯示卡的功能,将顯示卡硬體結構抽象掉,可以通過FrameBuffer的讀寫直接對顯存進行操作。使用者可以将FrameBuffer看成是顯示記憶體的一個映像,将其映射到程序位址空間之後,就可以直接進行讀寫操作,而寫操作可以立即反應在螢幕上。這種操作是抽象的,統一的。使用者不必關心實體顯存的位置、換頁機制等等具體細節,這些都是由FrameBuffer裝置驅動來完成的。

rk系統裡面framebuffer的驅動為rk_fb.c。主屏為/dev/graphics/fb0,副屏為/dev/graphics/fb4,上層通過打開這兩個節點傳回檔案描述符,通過檔案描述符寫入兩個螢幕的資料,然後DMA,把緩沖區裡的資料傳給螢幕。預設情況下rk_fb.c隻會去配置設定一塊緩沖區給主屏用,需要修改代碼,根據副屏的屏參配置設定緩沖區給副屏用,大小為長寬每個像素的位數。

3.在底層實作雙屏同顯的基礎上,APP中去實作雙屏異顯

通過前面底層的修改,已經實作了雙屏可以同時輸出相同的内容。為了支援兩個螢幕實作不同的内容,可以在APP中操作。

Android 的标準實作是使用 API Presentation 來實作異顯的功能。 Presentation 是擴充自 dialog.下面截取一部分官方描述:

Presentation 是 Android 針對雙屏異顯所開發的一個類。它可以做到一個 APK 裡面,通過給Presentation 單獨進行 view 的布局,來實作同一個 APK 在主屏和副屏上面顯示不同的 view,來達到異顯的效果。它的工作原理是通過調用 DisplayManagerService 的 getDisplays 方法來擷取第二個顯示裝置。将第二個顯示裝置作為參數傳給 Presentation,然後在 Presentation 裡面實作自己的 UI内容,最終調用 Presentation 的 show 方法來将 UI 内容顯示在第二個顯示裝置上面。下面是我寫的一個簡單的 Demo。

public class MainActivity extends Activity

{ private DemoPresentation mPresentation;

private Display[] displays;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

DisplayManager mDisplayManager = (DisplayManager)

getSystemService(Context.DISPLAY_SERVICE);

displays = mDisplayManager.getDisplays();

mPresentation = new DemoPresentation(MainActivity.this, displays[1]);

Button Button1=(Button) findViewById(R.id.button1);

Button1.setOnClickListener(new Button.OnClickListener() {

public void onClick(View v)

{

//這裡進行副屏顯示的調用

mPresentation.show();

}

});

}

public class DemoPresentation extends Presentation {

//private PresentationContents mContents;

public DemoPresentation(Context outerContext, Display display) {

super(outerContext, display);

}

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

Resources r=getContext().getResources();//根據 prsentation 的上下文擷取到資源檔案

setContentView(R.layout.main2);

Button Button2=(Button) findViewById(R.id.button2);

videoView1 = (VideoView) findViewById(R.id.videoView1);

if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {

File file = Environment.getExternalStorageDirectory();

File videoFile = new File(file, “testvideo.mp4”);

if (videoFile.exists()) {

uri = Uri.fromFile(videoFile);

videoView1.setVideoURI(uri);

VideoView1.setMediaController(controller);

videoView1.requestFocus();

playVideo();

}

}

}

這個 Demo 最終實作的是同一個 APK,主屏顯示一些如 button 等的 view,然後副屏可以顯示一個SurfaceView,及播放一個視訊。

繼續閱讀