Android STB HDMI开发

Posted Kevin张俊杰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android STB HDMI开发相关的知识,希望对你有一定的参考价值。

目录

在进行机顶盒ROM开发时,HDMI相关功能是常见的功能模块,本篇文章就简单介绍一些常见的HDMI相关需求开发。

常见的HDMI相关功能可分为三大块:(通过HDMI线获取/设置)分辨率(通过HDMI线获取)电视机信息(HDMI)待机

一、分辨率

机顶盒通过HDMI线与TV相连时,是通过HDMI线(获取TV的EDID)来获取TV支持的分辨率情况的,所以与此相关的功能有获取/设置分辨率、初始化默认分辨率等。

1.1、获取/设置分辨率

该功能的相关代码代码看上去和HDMI没多大关系,属于android通用性功能,其实不然,该功能与HDMI息息相关,接下来就以Hi3798MV300和Amlogic905两种平台来简单介绍。

先以Hi3798MV300为例,获取分辨率用到的接口和Android原生流程是一样的,主要来自于frameworks/base/core/java/android/os/display/DisplayManager.java,关键接口如下:

/*所有分辨率*/
private int[] mAllDisplayStandard = 
    DISPLAY_STANDARD_1080P_60,
    DISPLAY_STANDARD_1080P_50,
    DISPLAY_STANDARD_1080P_30,
    DISPLAY_STANDARD_1080P_25,
    DISPLAY_STANDARD_1080P_24,
    DISPLAY_STANDARD_1080I_60,
    DISPLAY_STANDARD_1080I_50,
    DISPLAY_STANDARD_720P_60,
    DISPLAY_STANDARD_720P_50,
    DISPLAY_STANDARD_576P_50,
    DISPLAY_STANDARD_480P_60,
    DISPLAY_STANDARD_PAL,
    DISPLAY_STANDARD_NTSC,
    DISPLAY_STANDARD_3840_2160P_24,
    DISPLAY_STANDARD_3840_2160P_25,
    DISPLAY_STANDARD_3840_2160P_30,
    DISPLAY_STANDARD_3840_2160P_50,
    DISPLAY_STANDARD_3840_2160P_60,
    DISPLAY_STANDARD_4096_2160P_24,
    DISPLAY_STANDARD_4096_2160P_25,
    DISPLAY_STANDARD_4096_2160P_30,
    DISPLAY_STANDARD_4096_2160P_50,
    DISPLAY_STANDARD_4096_2160P_60,
 ;	
 
 /*DisplayManager构造函数,该函数中会初始化当前TV所支持分辨率数组mStandard*/
public DisplayManager(IDisplayManager server) 
    mdisplay = server;
    //see display.c::get_hdmi_capability
    //                  hisi format value                fmt cap index
    mMapEncFmtToIndex.put(ENC_FMT_1080P_60                  ,   1 );
    mMapEncFmtToIndex.put(ENC_FMT_1080P_50                  ,   2 );
    mMapEncFmtToIndex.put(ENC_FMT_1080P_30                  ,   3 );
    mMapEncFmtToIndex.put(ENC_FMT_1080P_25                  ,   4 );
    mMapEncFmtToIndex.put(ENC_FMT_1080P_24                  ,   5 );
    mMapEncFmtToIndex.put(ENC_FMT_1080i_60                  ,   6 );
    mMapEncFmtToIndex.put(ENC_FMT_1080i_50                  ,   7 );
    mMapEncFmtToIndex.put(ENC_FMT_720P_60                   ,   8 );
    mMapEncFmtToIndex.put(ENC_FMT_720P_50                   ,   9 );
    mMapEncFmtToIndex.put(ENC_FMT_576P_50                   ,   10);
    mMapEncFmtToIndex.put(ENC_FMT_480P_60                   ,   11);
    mMapEncFmtToIndex.put(ENC_FMT_PAL                       ,   12);
    mMapEncFmtToIndex.put(ENC_FMT_NTSC                      ,   15);
    mMapEncFmtToIndex.put(ENC_FMT_3840X2160_24              ,   44);
    mMapEncFmtToIndex.put(ENC_FMT_3840X2160_25              ,   45);
    mMapEncFmtToIndex.put(ENC_FMT_3840X2160_30              ,   46);
    mMapEncFmtToIndex.put(ENC_FMT_3840X2160_50              ,   47);
    mMapEncFmtToIndex.put(ENC_FMT_3840X2160_60              ,   48);
    mMapEncFmtToIndex.put(ENC_FMT_4096X2160_24              ,   49);
    mMapEncFmtToIndex.put(ENC_FMT_4096X2160_25              ,   50);
    mMapEncFmtToIndex.put(ENC_FMT_4096X2160_30              ,   51);
    mMapEncFmtToIndex.put(ENC_FMT_4096X2160_50              ,   52);
    mMapEncFmtToIndex.put(ENC_FMT_4096X2160_60              ,   53);

    try
        int[] dispCapability = mdisplay.getDisplayCapability();
        for(int i = 0; i < dispCapability.length; i++)
            Log.d("DisplayManager.java", "dispCapability[" + i + "]=" + dispCapability[i]);
        
        /*过滤分辨率列表,把当前TV支持的分辨率赋值给mStandard*/
        if(dispCapability != null)
            int supportFmtCnt = 0;
            int[] supportFmt = new int[mAllDisplayStandard.length];
            for(int i = 0; i < mAllDisplayStandard.length; i++)
                if(dispCapability[mMapEncFmtToIndex.get(covertCMCCFmtToHisi(mAllDisplayStandard[i]))] == 1)
                    supportFmt[supportFmtCnt] = mAllDisplayStandard[i];
                    supportFmtCnt++;
                    Log.d("DisplayManager.java", "supportFmt:" + mAllDisplayStandard[i]);
                
                Log.d("DisplayManager.java", "supportFmtCnt:" + supportFmtCnt);
            
            mStandard = new int[supportFmtCnt];
            System.arraycopy(supportFmt, 0, mStandard, 0, supportFmtCnt);
        else
            mStandard = new int[0];
        
    
    catch(Exception ex)
        mStandard = new int[0];
    


/*获取当前TV支持的所有分辨率*/
public int[] getAllSupportStandards() 
    return mStandard;


/*是否支持某一分辨率*/
public boolean isSupportStandard(int standard) 
    boolean ret = false;
    for (int i = 0; i < mStandard.length; i++) 
        if(standard == mStandard[i])
            ret = true;
            break;
        
    
    return ret;


/*设置某一分辨率*/
public void setDisplayStandard(int standard) 
    int hisiFmt = -1;
    int ret = -1;
    /*设置分辨率前,要检测当前TV是否支持该分辨率*/
    if(isSupportStandard(standard))
        try 
            hisiFmt = covertCMCCFmtToHisi(standard);
            if (hisiFmt >= ENC_FMT_1080P_60) 
                ret = mdisplay.setFmt(hisiFmt);
            
         catch(Exception ex) 
            Log.e(TAG,"setDisplayStandard: " + ex);
        
     else 
        Log.e(TAG, "setDisplayStandard: unsupport(" + standard + ")");
    
    Log.i(TAG, "setDisplayStandard: standard=" + standard + ", ret=" + ret);


/*获取当前分辨率*/
public int getCurrentStandard() 
    int hisiFmt = -1;
    int cmccFmt = -1;
    try 
        hisiFmt = mdisplay.getFmt();
        cmccFmt = covertHisiFmtToCMCC(hisiFmt);
     catch (RemoteException e) 
        Log.e(TAG, "getCurrentStandard: " + e);
    
    if(isSupportStandard(cmccFmt))
        return cmccFmt;
     else 
        Log.e(TAG, "getCurrentStandard: CMCC unsupport(" + hisiFmt + ")");
        return -1;
    

在Hi3798MV300上,使用以上接口,再结合一些其他的HDMI功能,就可以实现一个较为复杂的需求,比如:将机顶盒上的HDMI线连接到某一TV,判断该TV是否支持4K分辨率,如果支持,则切换到4K分辨率,否则不处理。关键代码示例如下:

/*检测、设置4K分辨率*/
private BroadcastReceiver mHdmiReceiver = new BroadcastReceiver() 
    @Override
    public void onReceive(Context context, Intent intent) 
        String action = intent.getAction();
        /*监听HDMI插拔广播*/
        if (action.equals("android.intent.action.HDMI_PLUGGED")) 
            boolean state = intent.getBooleanExtra("state", false);
			Log.d(TAG,"HDMI status:"+state+",getHdmiSwitchSet():"+getHdmiSwitchSet());
            if (state && getHdmiSwitchSet()) 
                checkTVResolution();
            
        
    
;

/*获取当前TV所支持的所有分辨率,如果支持4K,则设置为2060P50hz*/
private void checkTVResolution()
    int DISPLAY_STANDARD_3840_2160P_24 = 256;
    boolean is4kResolutionSupported = false;
    int[] supportList =  mDisplayManager.getAllSupportStandards();
    Arrays.sort(supportList);
    
    for(int i=0;i<supportList.length;i++)
        Log.d(TAG,"supportList["+i+"]:"+supportList[i]);
        /*所支持的分辨率列表中包含大于DISPLAY_STANDARD_3840_2160P_24的数值,则代表支持2160P,即假4K分辨率*/
        if(supportList[i] >= DISPLAY_STANDARD_3840_2160P_24)
            is4kResolutionSupported = true; 
            break;
        
    
    
    if(is4kResolutionSupported)
        int best4kResolution = 260;
        if(supportList[supportList.length-1] >= 260)
                best4kResolution = 260;
        mDisplayManager.setDisplayStandard(best4kResolution);
        mDisplayManager.saveParams();
    	


/*检测HDMI插拔状态*/
private static boolean getHdmiSwitchSet() 
    File switchFile = new File("/sys/devices/virtual/switch/hdmi/state");
    if (!switchFile.exists()) 
        switchFile = new File("/sys/class/switch/hdmi/state");
    
    try 
        Scanner switchFileScanner = new Scanner(switchFile);
        int switchValue = switchFileScanner.nextInt();
        switchFileScanner.close();
        return switchValue > 0;
     catch (Exception e) 
        return false;
    

在Amlogic905上,用的不是原生的DisplayManager来处理分辨率,有一套别的实现方式,主要实现代码在frameworks/base/services/java/com/android/server/MboxOutputModeService.java,关键接口如下:

/*获取当前TV支持的分辨率列表,供上层调用*/
public String getSupportResoulutionList() 
    if (isHDMIPlugged()) 
        ArrayList<OutputMode> mOutputModeList = readSupportList();
        Slog.w(TAG, "getSupportResoulutionList error, output list is null!");
        return null;
     


/*读取当前TV支持的分辨率列表*/
private ArrayList<OutputMode> readSupportList() 
    String str = null;
    ArrayList<OutputMode> mOutputModeList = new ArrayList<OutputMode>();
    try 
        /*从相关节点读取分辨率*/
        FileReader fr = new FileReader(HDMI_SUPPORT_LIST_SYSFS);//HDMI_SUPPORT_LIST_SYSFS = /sys/class/amhdmitx/amhdmitx0/disp_cap
        BufferedReader br = new BufferedReader(fr);
        try 
            while ((str = br.readLine()) != null) 
                if(str != null)
                    //if(DEBUG) Slog.i(TAG, "Output: " + str);
                    boolean filter = false;
                    OutputMode output = new OutputMode();
                    if(str.contains("null edid")) 
                        Slog.w(TAG, "readSupportList error, disp_cap: " + str);
                        return null;
                    
                    if(str.contains("*")) 
                        output.mode = new String(str.substring(0, str.length()-1));
                        output.isBestMode = true;
                     else 
                        output.mode = new String(str);
                        output.isBestMode = false;
                    
                    //if(DEBUG) Slog.i(TAG, "readSupportList, Output: " + output.mode + ", isBestMode: " + output.isBestMode);
                    if(isOutputFilter(output.mode)) 
                        Slog.w(TAG, "readSupportList, filter this mode: " + output.mode);
                     else 
                        mOutputModeList.add(output);
                    
                
            ;
            fr.close();
            br.close();
            return resolutionSort(mOutputModeList);
         catch (IOException e) 
            e.printStackTrace();
            return null;
        
     catch (FileNotFoundException e) 
        e.printStackTrace();
        return null;
    


/*获取当前分辨率*/
public String getCurrentOutPutMode() 
    String curMode = readSysfs(OutputModeFile);//OutputModeFile = "/sys/class/display/mode"
	Slog.e(TAG,"getCurrentOutPutMode:" + curMode);
	return curMode;		

从上面的代码可以大致看出Amlogic905在分辨率方面的代码特征,即把相关信息存储在不同的节点中。

1.2、初始化默认分辨率

机顶盒设置的初始分辨率策略,一般有两种:最优分辨率与最大分辨率。

1.2.1、最优分辨率

此处的最优并不是TV支持的最大分辨率,而是ROM评估出来的、适合的一个分辨率,一般为720P或1080P,该策略一般也是ROM中的默认设置。
以Hi3798MV300为例,该策略的主要实现代码在device/hisilicon/bigfish/frameworks/hidisplaymanager/hal/hi_adp_hdmi.c,以Hdmicap_NativeFormat_Strategy方法为例,关键代码如下:

   else if (strcmp("p50hz", perfer) == 0)
    
        if (HI_TRUE == is_format_support(&stSinkCap,HI_UNF_ENC_FMT_4096X2160_50))
        
            stSinkCap.enNativeFormat = HI_UNF_ENC_FMT_4096X2160_50;
        
        else if (HI_TRUE == is_format_support(&stSinkCap,HI_UNF_ENC_FMT_4096X2160_25))
        
            stSinkCap.enNativeFormat = HI_UNF_ENC_FMT_4096X2160_25;
        
        //since tv capability is upgrade now , add max fmt lever to 3840X2160 P50
        else if (HI_TRUE == is_format_support(&stSinkCap,HI_UNF_ENC_FMT_3840X2160_50))
        
            stSinkCap.enNativeFormat = HI_UNF_ENC_FMT_3840X2160_50;
        
        else if (HI_TRUE == is_format_support(&stSinkCap,HI_UNF_ENC_FMT_3840X2160_25))
        
            stSinkCap.enNativeFormat = HI_UNF_ENC_FMT_3840X2160_25;
        
        else if (HI_TRUE == is_format_support(&stSinkCap,HI_UNF_ENC_FMT_1080P_50))
        
            stSinkCap.enNativeFormat = HI_UNF_ENC_FMT_1080P_50;
        
        else if (HI_TRUE == is_format_support(&stSinkCap,HI_UNF_ENC_FMT_720P_50))
        
            stSinkCap.enNativeFormat = HI_UNF_ENC_FMT_720P_50;
        
        else //if(stSinkCap.enNativeFormat = HI_UNF_ENC_FMT_576P_50)
        
            ALOGI("Lowest default to 576p");
            stSinkCap.enNativeFormat = HI_UNF_ENC_FMT_576P_50;
        
    

该段代码中,is_format_support函数是芯片的最优分辨率策略实现函数,一般当persist.sys.optimalfmt.perfer设置为p50hz时,默认分辨率是720P50hz

Amlogic905的最优分辨率的实现方式也较为底层,framework层代码frameworks/base/services/java/com/android/server/MboxOutputModeService.java是根据节点的值来实现的,具体如下:

/*获取最适合分辨率*/
public String getBestMatchResolution() 
    /*获取支持的分辨率列表*/
    ArrayList<OutputMode> mOutputModeList = readSupportList();
    if (mOutputModeList != null && isHDMIPlugged())
            int size = mOutputModeList.size();
            if(DEBUG) Slog.i(TAG, "getBestMatchResolution, output size: " + size);
            for (以上是关于Android STB HDMI开发的主要内容,如果未能解决你的问题,请参考以下文章

iTOP-iMX6开发板Android系统下LVDS和HDMI双屏异显方法

Android STB 遥控器适配

Android - 将 HDMI CEC 命令发送到电视 - minix - libCEC

我的C/C++语言学习进阶之旅介绍一下NDK开发之C的简单易用图像库stb

我的C/C++语言学习进阶之旅介绍一下NDK开发之C的简单易用图像库stb

检测安卓设备中的HDMI端口