一.技術的背景
随着電梯傳媒行業的飛速發展,雙屏視訊播放機應運而生,客戶可以根據自己的需求,制作兩個螢幕的内容,分别播放适合螢幕顯示方式播放的内容,如上面播放視訊,下面播放圖檔,使得宣傳的效果多樣化。基于市場的需求,研發出了基于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,及播放一個視訊。