Android HDMI |
您所在的位置:网站首页 › 管状电机怎样设置遥控器开机启动键功能 › Android HDMI |
在OTT设备开发中,经常会遇到个设备状态同步联动需求,比如使用电视遥控器开关电视时,把机顶盒也进行同步开关;反之亦然,开关机顶盒时,电视也同步开关,这样使用一个遥控器就可以控制多个设备,提高用户使用体验。这个功能就需要用到HDMI-CEC协议功能。 android官方相关文档可以查看此处:HDMI-CEC 要实现CEC联动设备功能,前提是机顶盒和电视双方都支持CEC功能,不同的制造商实现上可能有差异,要根据具体设备来看。同时要区分源端和目标端,通常机顶盒等播放设备是源端,电视等显示设备是目标端,固件中通过build.prop属性区分: 源端: PRODUCT_PROPERTY_OVERRIDES += ro.hdmi.device_type=4目标端: PRODUCT_PROPERTY_OVERRIDES += ro.hdmi.device_type=0应用中需要添加cec权限: 我是基于海思Hi3798MV200方案进行开发的,下面均以此平台sdk源码为例简单介绍一下CEC联动过程。 应用层主要关注两个类:HdmiControlManager HdmiControlService 源码路径:HiSTBAndroidV600R003C00SPC020\frameworks\base\core\java\android\hardware\hdmi\HdmiControlManager.java HiSTBAndroidV600R003C00SPC020\frameworks\base\services\core\java\com\android\server\hdmi\HdmiControlService.java 所有的应用,都会间接通过HDMIControlManager或者输入通过Tv Input框架间接与HdmiControlService进行通信,HdmiControlService作为SystemServer服务的一个服务,负责处理CEC的命令并与HDMI-CEC HAl进行交互。HAL层和驱动都需要厂商去适配,最后通过CEC总线与CEC设备通信。 基本流程:获取HdmiControlManager对象,通过该对象调用HdmiControlService中的相关方法-检测设备类型源端--检测CEC功能支持--注册监听回调--发送控制命令。 简易示例: import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPlaybackClient; // 获取HdmiControlManager实例 HdmiControlManager hdmiControlManager = (HdmiControlManager) getSystemService(Context.HDMI_CONTROL_SERVICE); // 检查HDMI CEC是否可用 if (hdmiControlManager.isAvailable()) { // 获取HdmiPlaybackClient实例(源端) HdmiPlaybackClient hdmiPlaybackClient = hdmiControlManager.getPlaybackClient(); // 获取HdmiTvClient实例(目标端) // HdmiTvClient hdmiTvClient = hdmiControlManager.getTvClient(); // 检查TV是否处于开机状态 if (hdmiControlManager.getDevicePowerStatus() != HdmiControlManager.POWER_STATUS_ON) { // 发送开机命令 hdmiPlaybackClient.sendStandby(); } else { // 发送关机命令 hdmiPlaybackClient.sendPowerOff(); } }CEC除了开关机同步,也可以实现各按键功能同步,比如上下左右等功能,还可以设置音量等,除了调用封装好的功能函数,也可以采用发送指令码方式,不同厂商方式有差异,比如海思平台的: @Override public void setSystemAudioVolume(final int oldIndex, final int newIndex, final int maxIndex) { enforceAccessPermission(); runOnServiceThread(new Runnable() { @Override public void run() { HdmiCecLocalDeviceTv tv = tv(); if (tv == null) { Slog.w(TAG, "Local tv device not available"); return; } tv.changeVolume(oldIndex, newIndex - oldIndex, maxIndex); } }); } @Override public void setSystemAudioMute(final boolean mute) { enforceAccessPermission(); runOnServiceThread(new Runnable() { @Override public void run() { HdmiCecLocalDeviceTv tv = tv(); if (tv == null) { Slog.w(TAG, "Local tv device not available"); return; } tv.changeMute(mute); } }); } @Override public void setArcMode(final boolean enabled) { enforceAccessPermission(); runOnServiceThread(new Runnable() { @Override public void run() { HdmiCecLocalDeviceTv tv = tv(); if (tv == null) { Slog.w(TAG, "Local tv device not available to change arc mode."); return; } } }); } @Override public void setProhibitMode(final boolean enabled) { enforceAccessPermission(); if (!isTvDevice()) { return; } HdmiControlService.this.setProhibitMode(enabled); } @Override public void addVendorCommandListener(final IHdmiVendorCommandListener listener, final int deviceType) { enforceAccessPermission(); HdmiControlService.this.addVendorCommandListener(listener, deviceType); } @Override public void sendVendorCommand(final int deviceType, final int targetAddress, final byte[] params, final boolean hasVendorId) { enforceAccessPermission(); runOnServiceThread(new Runnable() { @Override public void run() { HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType); if (device == null) { Slog.w(TAG, "Local device not available"); return; } if (hasVendorId) { sendCecCommand(HdmiCecMessageBuilder.buildVendorCommandWithId( device.getDeviceInfo().getLogicalAddress(), targetAddress, getVendorId(), params)); } else { sendCecCommand(HdmiCecMessageBuilder.buildVendorCommand( device.getDeviceInfo().getLogicalAddress(), targetAddress, params)); } } }); } @Override public void sendStandby(final int deviceType, final int deviceId) { enforceAccessPermission(); runOnServiceThread(new Runnable() { @Override public void run() { HdmiMhlLocalDeviceStub mhlDevice = mMhlController.getLocalDeviceById(deviceId); if (mhlDevice != null) { mhlDevice.sendStandby(); return; } HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType); if (device == null) { Slog.w(TAG, "Local device not available"); return; } device.sendStandby(deviceId); } }); }常见的CEC协议指令码: HDMI-CEC命令是通过特定的操作码(Opcode)来实现的,每个操作码对应一个特定的功能。以下是一些常用的HDMI-CEC命令及其对应的操作码: 1. **One Touch Play**(一键启动) - `0x04`:Image View On - `0x34`:Tuner Step Increment (used for one touch record start) - `0x35`:Tuner Step Decrement (used for one touch record stop) 2. **System Standby**(待机) - `0x36`:Standby 3. **Preset Transfer** (不是一个典型的CEC命令,通常涉及到多个命令和状态信息的交换) 4. **One Touch Record** - `0x37`:Record On - `0x38`:Record Status - `0x39`:Record Off 5. **Timer Programming** (涉及多个命令,如设置定时器信息) - `0x97`:Timer Cleared Status - `0x43`:Timer Status 6. **System Information** - `0x83`:Give Physical Address - `0x84`:Report Physical Address - `0x85`:Request Active Source - `0x86`:Set Stream Path 7. **Deck Control** - `0x42`:Deck Control - `0x1B`:Deck Status 8. **Tuner Control** - `0x54`:Tuner Device Status - `0x55`:Give Tuner Device Status - `0x67`:Tuner Step Increment - `0x68`:Tuner Step Decrement 9. **Vendor Specific Commands** - `0x89`:Vendor Command - `0x8C`:Vendor Remote Button Down - `0x8D`:Vendor Remote Button Up - `0x8E`:Give Device Vendor ID - `0x8F`:Menu Request 10. **Menu Navigation** - `0x8B`:User Control Pressed - `0x8C`:User Control Released - `0x8D`:Give Device Power Status - `0x8E`:Report Power Status - `0x0D`:Menu Request - `0x32`:Set Menu Language 请注意,这些操作码仅是CEC协议中的一部分,而且不同厂商可能会有不同的实现或扩展。要完整地实现CEC功能,还需要考虑具体的设备逻辑地址、参数传递和状态机制等因素。而且,由于CEC协议的实现可能因厂商而异,有时候相同的操作码在不同设备间可能会有不同的行为。如果你需要更详细的信息,通常需要参考HDMI规范的官方文档。 我在实现过程中,通过上述应用层方法没有走通,CEC模块检测通不过,但实际是支持的,好在源码环境开发,我就直接到底层JNI代码中将检测条件去掉了,直接在底层实现相关功能,应用层方法最终也还是调用底层JNI方法的。 HAL层源码位置: HiSTBAndroidV600R003C00SPC020\device\hisilicon\bigfish\frameworks\hidisplaymanager\hal\hi_adp_hdmi.c 这个是所有HDMI相关控制的中间件层位置,比如分辨率设置、热插拔检测、CEC控制等 |
今日新闻 |
点击排行 |
|
推荐新闻 |
图片新闻 |
|
专题文章 |
CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭 |