/* ******************************************************************************* * INCLUDE ******************************************************************************* */ #include "HDMIVideoOutput.h" #include "Logger.h" #include "util/Math.h" #include namespace Video { namespace MPP5 { /* ******************************************************************************* * CONSTANTS ******************************************************************************* */ #define VO_DEVICE_ID 0 #define VIDEO_LAYER_ID 0 #define HDMI_ID HI_HDMI_ID_0 /* ******************************************************************************* * TYPEDEFS ******************************************************************************* */ struct TimingDesc { std::string desc; uint32_t width; uint32_t height; uint32_t frameRate; hi_vo_intf_sync intf; hi_hdmi_video_format format; }; /* ******************************************************************************* * LOCAL VARIABLES ******************************************************************************* */ static const std::array timingDescTable = {{ {"3840x2160P30", 3840, 2160, 30, HI_VO_OUT_3840x2160_30, HI_HDMI_VIDEO_FORMAT_3840X2160P_30}, {"2560x1600P60", 2560, 1600, 60, HI_VO_OUT_2560x1600_60, HI_HDMI_VIDEO_FORMAT_2560x1600_60}, {"2560x1440P60", 2560, 1440, 60, HI_VO_OUT_2560x1440_60, HI_HDMI_VIDEO_FORMAT_2560x1440_60}, {"2560x1440P30", 2560, 1440, 30, HI_VO_OUT_2560x1440_30, HI_HDMI_VIDEO_FORMAT_2560x1440_30}, {"1920x1080P60", 1920, 1080, 60, HI_VO_OUT_1080P60, HI_HDMI_VIDEO_FORMAT_1080P_60}, {"1920x1080I60", 1920, 1080, 60, HI_VO_OUT_1080I60, HI_HDMI_VIDEO_FORMAT_1080i_60}, {"1920x1080P50", 1920, 1080, 50, HI_VO_OUT_1080P50, HI_HDMI_VIDEO_FORMAT_1080P_50}, {"1920x1080I50", 1920, 1080, 50, HI_VO_OUT_1080I50, HI_HDMI_VIDEO_FORMAT_1080i_50}, {"1920x1080P30", 1920, 1080, 30, HI_VO_OUT_1080P30, HI_HDMI_VIDEO_FORMAT_1080P_30}, {"1920x1080P25", 1920, 1080, 25, HI_VO_OUT_1080P25, HI_HDMI_VIDEO_FORMAT_1080P_25}, {"1920x1080P24", 1920, 1080, 24, HI_VO_OUT_1080P24, HI_HDMI_VIDEO_FORMAT_1080P_24}, {"1280x720P60", 1280, 720, 60, HI_VO_OUT_720P60, HI_HDMI_VIDEO_FORMAT_720P_60}, {"1280x720P50", 1280, 720, 50, HI_VO_OUT_720P50, HI_HDMI_VIDEO_FORMAT_720P_50}, {"720x576P50", 720, 576, 50, HI_VO_OUT_576P50, HI_HDMI_VIDEO_FORMAT_576P_50}, {"720x480P50", 720, 480, 60, HI_VO_OUT_480P60, HI_HDMI_VIDEO_FORMAT_480P_60}, {"1920x2160P30", 1920, 2160, 30, HI_VO_OUT_1920x2160_30, HI_HDMI_VIDEO_FORMAT_1920x2160_30}, {"1920x1200P60", 1920, 1200, 60, HI_VO_OUT_1920x1200_60, HI_HDMI_VIDEO_FORMAT_VESA_1920X1200_60}, {"1680x1050P60", 1680, 1050, 60, HI_VO_OUT_1680x1050_60, HI_HDMI_VIDEO_FORMAT_VESA_1680X1050_60}, {"1600x1200P60", 1600, 1200, 60, HI_VO_OUT_1600x1200_60, HI_HDMI_VIDEO_FORMAT_VESA_1600X1200_60}, {"1440x900P60", 1440, 900, 60, HI_VO_OUT_1440x900_60, HI_HDMI_VIDEO_FORMAT_VESA_1440X900_60}, {"1366x768P60", 1366, 768, 60, HI_VO_OUT_1366x768_60, HI_HDMI_VIDEO_FORMAT_VESA_1366X768_60}, {"1280x1024P60", 1280, 1024, 60, HI_VO_OUT_1280x1024_60, HI_HDMI_VIDEO_FORMAT_VESA_1280X1024_60}, {"1280x800P60", 1280, 800, 60, HI_VO_OUT_1280x800_60, HI_HDMI_VIDEO_FORMAT_VESA_1280X800_60}, {"1024x768P60", 1024, 768, 60, HI_VO_OUT_1024x768_60, HI_HDMI_VIDEO_FORMAT_VESA_1024X768_60}, {"800x600P60", 800, 600, 60, HI_VO_OUT_800x600_60, HI_HDMI_VIDEO_FORMAT_VESA_800X600_60}, }}; const TimingDesc& defaultTime = timingDescTable[4]; /* ******************************************************************************* * LOCAL FUNCTIONS ******************************************************************************* */ static const TimingDesc& getTimingDesc(const std::string& desc) { for (const auto& t : timingDescTable) { if (t.desc == desc) return t; } logger().warning("HDMIVideoOutput: Could NOT found '%s', using %s", desc, defaultTime.desc); return defaultTime; } /* ******************************************************************************* * CLASS DEFINE ******************************************************************************* */ HDMIVideoOutput::HDMIVideoOutput(const std::string& name) : Core::VideoOutput(name + "(MPP5::HDMI)"), MPPChannel((uint32_t)HI_ID_VO, VO_DEVICE_ID, 0) { hi_mpi_hdmi_init(); stopOutput(); } HDMIVideoOutput::~HDMIVideoOutput() { stopOutput(); hi_mpi_hdmi_deinit(); } void HDMIVideoOutput::start() { hi_mpi_hdmi_open(HDMI_ID); onHotPlug(); } void HDMIVideoOutput::stop() { onNoPlug(); hi_mpi_hdmi_close(HDMI_ID); } void HDMIVideoOutput::onConfigChanged(ParameterNames& names) { if (!getHDMIStarted()) return; if (names.exists(_Timing)) { try { stopOutput(); startOutput(); } catch (Poco::Exception& e) { logger().error(e.displayText()); stopOutput(); } } if (names.exists(_Mode)) { hi_mpi_hdmi_stop(HDMI_ID); hi_hdmi_attr attr; int rc = hi_mpi_hdmi_get_attr(HDMI_ID, &attr); if (rc != HI_SUCCESS) logger().error("%s: get HDMI attr FAIL: %d(%#x)", name(), rc, (uint32_t)rc); attr.hdmi_en = getUsingHDMIMode(); rc = hi_mpi_hdmi_set_attr(HDMI_ID, &attr); if (rc != HI_SUCCESS) logger().error("%s: set HDMI attr FAIL: %d(%#x)", name(), rc, (uint32_t)rc); rc = hi_mpi_hdmi_start(HDMI_ID); if (rc != HI_SUCCESS) logger().error("%s: start HDMI FAIL: %d(%#x)", name(), rc, (uint32_t)rc); } if (names.exists(_Luma) || names.exists(_Contrast) || names.exists(_Hue) || names.exists(_Saturation) || names.exists(_ColorType)) { hi_vo_hdmi_param param; if (names.exists(_ColorType)) hi_mpi_hdmi_stop(HDMI_ID); hi_mpi_vo_get_hdmi_param(deviceID(), ¶m); if (names.exists(_ColorType)) param.csc.csc_matrix = getCSCMatrix(); param.csc.luma = ::Util::Math::clamp(getLuma(), 0, 100); param.csc.contrast = ::Util::Math::clamp(getContrast(), 0, 100); param.csc.hue = ::Util::Math::clamp(getHue(), 0, 100); param.csc.saturation = ::Util::Math::clamp(getSaturation(), 0, 100); hi_mpi_vo_set_hdmi_param(deviceID(), ¶m); if (names.exists(_ColorType)) hi_mpi_hdmi_start(HDMI_ID); } if (names.exists(_Mirror)) hi_mpi_vo_set_chn_mirror(VIDEO_LAYER_ID, channelID(), getMirror() ? HI_VO_MIRROR_HOR : HI_VO_MIRROR_NONE); if (names.exists(_Rotation)) hi_mpi_vo_set_chn_rotation(VIDEO_LAYER_ID, channelID(), getRotationValue()); } void HDMIVideoOutput::onLink(Core::Component::Ptr pPrevious) { MPPChannel::doLink(pPrevious); } void HDMIVideoOutput::onUnlink(Core::Component::Ptr pPrevious) { MPPChannel::doUnlink(pPrevious); } void HDMIVideoOutput::onHotPlug() { logger().debug("%s: HDMI HOTPLUG", name()); setPluged(true); try { startOutput(); } catch (Poco::Exception& e) { logger().error(e.displayText()); } } void HDMIVideoOutput::onNoPlug() { logger().debug("%s: HDMI NOPLUG", name()); setPluged(false); stopOutput(); } void HDMIVideoOutput::onEDIDFail() { logger().debug("EDID FAIL"); } void HDMIVideoOutput::startOutput() { const TimingDesc& timing = getTimingDesc(getTiming()); hi_vo_pub_attr attr; attr.bg_color = 0x000000; attr.intf_type = HI_VO_INTF_HDMI; attr.intf_sync = timing.intf; int rc = hi_mpi_vo_set_pub_attr(deviceID(), &attr); if (rc != HI_SUCCESS) throw Poco::InvalidAccessException(Poco::format("%s: set-pub FAIL: %d(%#x)", name(), rc, (uint32_t)rc), rc); rc = hi_mpi_vo_enable(deviceID()); if (rc != HI_SUCCESS) throw Poco::InvalidAccessException(Poco::format("%s: enable VO FAIL: %d(%#x)", name(), rc, (uint32_t)rc), rc); { hi_vo_video_layer_attr attr; attr.display_rect.x = 0; attr.display_rect.y = 0; attr.display_rect.width = timing.width; attr.display_rect.height = timing.height; attr.img_size.width = timing.width; attr.img_size.height = timing.height; attr.display_frame_rate = timing.frameRate; attr.pixel_format = HI_PIXEL_FORMAT_YVU_SEMIPLANAR_422; attr.double_frame_en = HI_FALSE; attr.cluster_mode_en = HI_FALSE; attr.dst_dynamic_range = HI_DYNAMIC_RANGE_SDR8; attr.display_buf_len = 4; attr.partition_mode = HI_VO_PARTITION_MODE_SINGLE; attr.compress_mode = HI_COMPRESS_MODE_NONE; rc = hi_mpi_vo_set_video_layer_attr(VIDEO_LAYER_ID, &attr); if (rc != HI_SUCCESS) throw Poco::InvalidAccessException( Poco::format("%s: set video layer attr FAIL: %d(%#x)", name(), rc, (uint32_t)rc), rc); rc = hi_mpi_vo_enable_video_layer(VIDEO_LAYER_ID); if (rc != HI_SUCCESS) throw Poco::InvalidAccessException( Poco::format("%s: enable video layer FAIL: %d(%#x)", name(), rc, (uint32_t)rc), rc); } { hi_vo_chn_attr attr; attr.priority = 0; attr.rect.x = 0; attr.rect.y = 0; attr.rect.width = timing.width; attr.rect.height = timing.height; attr.deflicker_en = HI_FALSE; rc = hi_mpi_vo_set_chn_attr(VIDEO_LAYER_ID, channelID(), &attr); if (rc != HI_SUCCESS) throw Poco::InvalidAccessException(Poco::format("%s: set chn attr FAIL: %d(%#x)", name(), rc, (uint32_t)rc), rc); rc = hi_mpi_vo_enable_chn(VIDEO_LAYER_ID, channelID()); if (rc != HI_SUCCESS) throw Poco::InvalidAccessException(Poco::format("%s: enable FAIL: %d(%#x)", name(), rc, (uint32_t)rc), rc); hi_mpi_vo_set_chn_mirror(VIDEO_LAYER_ID, channelID(), getMirror() ? HI_VO_MIRROR_HOR : HI_VO_MIRROR_NONE); hi_mpi_vo_set_chn_rotation(VIDEO_LAYER_ID, channelID(), getRotationValue()); } { hi_hdmi_attr attr; rc = hi_mpi_hdmi_get_attr(HDMI_ID, &attr); if (rc != HI_SUCCESS) throw Poco::InvalidAccessException( Poco::format("%s: get HDMI attr FAIL: %d(%#x)", name(), rc, (uint32_t)rc), rc); attr.hdmi_en = getUsingHDMIMode(); attr.video_format = timing.format; attr.deep_color_mode = HI_HDMI_DEEP_COLOR_24BIT; attr.audio_en = attr.hdmi_en ? HI_TRUE : HI_FALSE; attr.sample_rate = HI_HDMI_SAMPLE_RATE_48K; attr.bit_depth = HI_HDMI_BIT_DEPTH_16; attr.auth_mode_en = HI_TRUE; attr.deep_color_adapt_en = HI_TRUE; rc = hi_mpi_hdmi_set_attr(HDMI_ID, &attr); if (rc != HI_SUCCESS) throw Poco::InvalidAccessException( Poco::format("%s: set HDMI attr FAIL: %d(%#x)", name(), rc, (uint32_t)rc), rc); hi_vo_hdmi_param param; hi_mpi_vo_get_hdmi_param(deviceID(), ¶m); param.csc.csc_matrix = getCSCMatrix(); param.csc.luma = ::Util::Math::clamp(getLuma(), 0, 100); param.csc.contrast = ::Util::Math::clamp(getContrast(), 0, 100); param.csc.hue = ::Util::Math::clamp(getHue(), 0, 100); param.csc.saturation = ::Util::Math::clamp(getSaturation(), 0, 100); hi_mpi_vo_set_hdmi_param(deviceID(), ¶m); rc = hi_mpi_hdmi_start(HDMI_ID); if (rc != HI_SUCCESS) throw Poco::InvalidAccessException(Poco::format("%s: start HDMI FAIL: %d(%#x)", name(), rc, (uint32_t)rc), rc); } setHDMIStarted(true); } void HDMIVideoOutput::stopOutput() { hi_mpi_hdmi_stop(HDMI_ID); hi_mpi_vo_disable_chn(VIDEO_LAYER_ID, channelID()); hi_mpi_vo_disable_video_layer(VIDEO_LAYER_ID); hi_mpi_vo_disable(VO_DEVICE_ID); setHDMIStarted(false); } hi_bool HDMIVideoOutput::getUsingHDMIMode() { return getMode() == Core::VideoOutput::Mode::HDMI ? HI_TRUE : HI_FALSE; } hi_vo_csc_matrix HDMIVideoOutput::getCSCMatrix() { if (getMode() == Core::VideoOutput::Mode::DVI || getColorType() == Core::VideoOutput::ColorType::RGB) return HI_VO_CSC_MATRIX_BT709LIMIT_TO_RGBFULL; return HI_VO_CSC_MATRIX_BT709LIMIT_TO_BT709FULL; } hi_rotation HDMIVideoOutput::getRotationValue() { switch (getRotation()) { default: case 0: return HI_ROTATION_0; case 90: return HI_ROTATION_90; case 180: return HI_ROTATION_180; case 270: return HI_ROTATION_270; } } void HDMIVideoOutput::eventCallback(hi_hdmi_event_type event, td_void* p) { switch (event) { case HI_HDMI_EVENT_HOTPLUG: static_cast(p)->onHotPlug(); break; case HI_HDMI_EVENT_NO_PLUG: static_cast(p)->onNoPlug(); break; case HI_HDMI_EVENT_EDID_FAIL: static_cast(p)->onEDIDFail(); break; default: break; } } } }