403 lines
13 KiB
C++
403 lines
13 KiB
C++
/*
|
|
*******************************************************************************
|
|
* INCLUDE
|
|
*******************************************************************************
|
|
*/
|
|
#include "HDMIVideoOutput.h"
|
|
#include "Logger.h"
|
|
#include "util/Math.h"
|
|
#include <array>
|
|
|
|
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<TimingDesc, 27> 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<HDMIVideoOutput*>(p)->onHotPlug();
|
|
break;
|
|
|
|
case HI_HDMI_EVENT_NO_PLUG:
|
|
static_cast<HDMIVideoOutput*>(p)->onNoPlug();
|
|
break;
|
|
|
|
case HI_HDMI_EVENT_EDID_FAIL:
|
|
static_cast<HDMIVideoOutput*>(p)->onEDIDFail();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|