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 - 将 HDMI CEC 命令发送到电视 - minix - libCEC
我的C/C++语言学习进阶之旅介绍一下NDK开发之C的简单易用图像库stb