p26001-store-acs-soc-fw-app/video/hisi/mpp5/VideoDecoder.cpp
2026-09-28 21:33:04 +08:00

1000 lines
32 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "VideoDecoder.h"
#include "ResourceManager.h"
#include "Util.h"
#include "Logger.h"
#include "util/Task.h"
#include "util/Math.h"
#include "../../core/Demuxer.h"
#include "../../core/System.h"
#include "../../core/Notifications.h"
#include "../v4l2/Input.h"
#include <hi_mpi_vdec.h>
#include <hi_mpi_vpss.h>
#include <hi_mpi_sys.h>
#include <hi_buffer.h>
#include <Poco/Format.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
namespace Video
{
namespace MPP5
{
/*
*******************************************************************************
* 追帧(catch-up)阈值配置
* 说明:所有阈值均以"队列中待解码视频包个数"为单位。
* 视频典型帧率 30fps,每包约 33ms,队列上限 300,约 10s 缓冲。
*******************************************************************************
*/
namespace
{
constexpr int V_CATCH_UP_ENTER = 10;
constexpr int V_CATCH_UP_EXIT = 2;
}
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
VideoDecoder::Pipe::Pipe(const std::string& name, uint32_t id)
: MPPChannel((uint32_t)HI_ID_VPSS, id, 0), _name(Poco::format("%s(VPSS#%u)", name, id)), _width(0), _height(0)
{
}
VideoDecoder::Pipe::~Pipe()
{
}
void VideoDecoder::Pipe::start(uint32_t width, uint32_t height)
{
_width = width;
_height = height;
hi_vpss_grp_attr grpAttr = {};
grpAttr.nr_en = HI_FALSE;
grpAttr.ie_en = HI_FALSE;
grpAttr.dci_en = HI_FALSE;
grpAttr.buf_share_en = HI_FALSE;
grpAttr.buf_share_chn = 0;
grpAttr.mcf_en = HI_FALSE;
grpAttr.max_width = width;
grpAttr.max_height = height;
grpAttr.max_dei_width = width;
grpAttr.max_dei_height = height;
grpAttr.pixel_format = HI_PIXEL_FORMAT_YVU_SEMIPLANAR_420;
grpAttr.dynamic_range = HI_DYNAMIC_RANGE_SDR8;
grpAttr.frame_rate.src_frame_rate = -1;
grpAttr.frame_rate.dst_frame_rate = -1;
grpAttr.dei_mode = HI_VPSS_DEI_MODE_OFF;
int rc = hi_mpi_vpss_create_grp(groupID(), &grpAttr);
if (rc != HI_SUCCESS)
throw Poco::InvalidAccessException(Poco::format("%s: Create group FAIL: %d(%#x)", name(), rc, (uint32_t)rc),
rc);
hi_mpi_vpss_reset_grp(groupID());
rc = hi_mpi_vpss_start_grp(groupID());
if (rc != HI_SUCCESS)
throw Poco::InvalidAccessException(Poco::format("%s: Start group FAIL: %d(%#x)", name(), rc, (uint32_t)rc), rc);
hi_vpss_chn_attr chnAttr = {};
chnAttr.chn_mode = HI_VPSS_CHN_MODE_USER;
chnAttr.width = width;
chnAttr.height = height;
chnAttr.video_format = HI_VIDEO_FORMAT_LINEAR;
chnAttr.pixel_format = HI_PIXEL_FORMAT_YVU_SEMIPLANAR_422;
chnAttr.dynamic_range = HI_DYNAMIC_RANGE_SDR8;
chnAttr.compress_mode = HI_COMPRESS_MODE_NONE;
chnAttr.frame_rate.src_frame_rate = -1;
chnAttr.frame_rate.dst_frame_rate = -1;
chnAttr.mirror_en = HI_FALSE;
chnAttr.flip_en = HI_FALSE;
chnAttr.depth = 0;
chnAttr.aspect_ratio.mode = HI_ASPECT_RATIO_NONE;
rc = hi_mpi_vpss_set_chn_attr(groupID(), channelID(), &chnAttr);
if (rc != HI_SUCCESS)
throw Poco::InvalidAccessException(
Poco::format("%s: Set channel#%u attr FAIL: %d(%#x)", name(), channelID(), rc, (uint32_t)rc), rc);
rc = hi_mpi_vpss_enable_chn(groupID(), channelID());
if (rc != HI_SUCCESS)
throw Poco::InvalidAccessException(
Poco::format("%s: Enable channel#%u FAIL: %d(%#x)", name(), channelID(), rc, (uint32_t)rc), rc);
}
void VideoDecoder::Pipe::stop()
{
int rc = hi_mpi_vpss_disable_chn(groupID(), channelID());
if (rc != HI_SUCCESS)
throw Poco::InvalidAccessException(
Poco::format("%s: Disable channel#%u FAIL: %d(%#x)", name(), channelID(), rc, (uint32_t)rc), rc);
rc = hi_mpi_vpss_stop_grp(groupID());
if (rc != HI_SUCCESS)
throw Poco::InvalidAccessException(Poco::format("%s: Stop group FAIL: %d(%#x)", name(), rc, (uint32_t)rc), rc);
rc = hi_mpi_vpss_destroy_grp(groupID());
if (rc != HI_SUCCESS)
throw Poco::InvalidAccessException(Poco::format("%s: Destroy group FAIL: %d(%#x)", name(), rc, (uint32_t)rc),
rc);
}
void VideoDecoder::Pipe::write(VideoFrame& frame, uint64_t pts, uint32_t timeref, bool isFrozen,
hi_video_frame_info& frameX)
{
hi_video_frame_info* pInfo = isFrozen ? (hi_video_frame_info*)&frameX : (hi_video_frame_info*)frame.info();
pInfo->video_frame.pts = pts;
pInfo->video_frame.time_ref = timeref;
int rc = hi_mpi_vpss_send_frame(groupID(), pInfo, 100);
if (rc != HI_SUCCESS)
throw Poco::InvalidAccessException(Poco::format("%s: Send frame FAIL: %d(%#x)", name(), rc, (uint32_t)rc), rc);
}
uint32_t VideoDecoder::Pipe::groupID() const
{
return deviceID();
}
const std::string& VideoDecoder::Pipe::name() const
{
return _name;
}
const uint32_t VideoDecoder::Pipe::width()
{
return _width;
}
const uint32_t VideoDecoder::Pipe::height()
{
return _height;
}
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
Core::CodecCapabilityManager VideoDecoder::_vdhCapabilityManager(1920 * 1080 * 30 * 6);
Core::CodecCapabilityManager VideoDecoder::_jpgdCapabilityManager(4096 * 2160 * 30);
static const int poolId = 0;
VideoDecoder::VideoDecoder(const std::string& name)
: MPPChannel((uint32_t)HI_ID_VDEC, 0, ResourceManager::allocVDECChannelID()),
Core::VideoDecoder(Poco::format("%s(MPP5::VDEC#%u)", name, channelID())),
_handle(Core::CodecCapabilityManager::INVALID_HANDLE),
_pipe(Core::VideoDecoder::name(), ResourceManager::allocVPSSGroupID()), _synchronizer(name + "/sync"),
_channelId(Util::getChannelId(name)),
_lastStatus(false), _catchUpEnabled(name.find("NET") != std::string::npos ? true : false), _isCatchingUp(false)
{
setWidth(NO_SIGNAL_YUV_WIDTH);
setHeight(NO_SIGNAL_YUV_HEIGHT);
setFrameRate(30);
}
VideoDecoder::~VideoDecoder()
{
ResourceManager::freeVPSSGroupID(_pipe.groupID());
ResourceManager::freeVENCChannelID(channelID());
releaseCapabilityHandle();
}
void VideoDecoder::start()
{
task().run(
[this](std::atomic<bool>& isCancelled) -> void
{
_pipe.start(NO_SIGNAL_YUV_WIDTH, NO_SIGNAL_YUV_HEIGHT);
serialCodeWatcher().reset();
while (!isCancelled)
{
try
{
Core::Packet::Ptr pPacket = tryReadPacket(3000, true);
if (pPacket.isNull())
{
serialCodeWatcher().reset();
pPacket = runExceptionProcedure(isCancelled);
}
while (!isCancelled && !pPacket.isNull() && !handleSerialCode(pPacket->serialCode()))
{
serialCodeWatcher().reset();
pPacket = runExceptionProcedure(isCancelled);
}
if (!isCancelled && !pPacket.isNull())
decodePacket(pPacket, isCancelled);
}
catch (Poco::Exception& e)
{
logger().error("%s: %s", name(), e.displayText());
}
catch (std::exception& e)
{
logger().error("%s: %s", name(), std::string(e.what()));
}
}
releaseDecoder(true);
_pipe.stop();
});
}
void VideoDecoder::stop()
{
task().cancel();
}
void VideoDecoder::initialize()
{
}
void VideoDecoder::deinitialize()
{
}
void VideoDecoder::onConfigChanged(ParameterNames& names)
{
if (names.exists(_SignalLost))
{
}
}
Core::Frame::Ptr VideoDecoder::decode()
{
return Core::Frame::Ptr();
}
void VideoDecoder::bind(const MPPChannel& next)
{
_pipe.bind(next);
}
void VideoDecoder::unbind(const MPPChannel& next)
{
_pipe.unbind(next);
}
void VideoDecoder::sendUserFrame(VideoFrame::Ptr pFrame, uint64_t duration, uint32_t& timeref)
{
uint64_t now = Core::System::timestamp();
_pipe.write(*pFrame, now, timeref, _frozenPlay, _frameX);
timeref += 2;
while (Core::System::timestamp() - now < duration)
::usleep(1000);
}
void VideoDecoder::runSendUserFrame(VideoFrame::Ptr pFrame, std::function<bool(void)> condition)
{
uint64_t duration = (float)1000000 / getFrameRate();
while (condition())
{
sendUserFrame(pFrame, duration, _frameTimeRef);
_frameTimeRef += 2;
}
}
void VideoDecoder::runSignalLost(std::atomic<bool>& isCancelled)
{
releaseDecoder();
std::atomic<bool> isSignalLost(true);
::Util::Task probeTask;
probeTask.run(
[&](std::atomic<bool>& isCancelled) -> void
{
while (!isCancelled)
{
if (!tryReadPacket(1000, false).isNull())
{
isSignalLost = false;
break;
}
}
});
VideoFrame::Ptr pUserFrame =
VideoFrame::create(NO_SIGNAL_YUV_FORMAT, NO_SIGNAL_YUV_WIDTH, NO_SIGNAL_YUV_HEIGHT, NO_SIGNAL_YUV_PATH);
runSendUserFrame(pUserFrame, [&]() -> bool { return (!isCancelled && isSignalLost); });
probeTask.cancel();
}
void VideoDecoder::runFault(std::atomic<bool>& isCancelled)
{
releaseDecoder();
std::atomic<bool> isFaultClean(false);
::Util::Task tryTask;
tryTask.run(
[&](std::atomic<bool>& isCancelled) -> void
{
while (!isCancelled)
{
Core::Packet::Ptr pPacket = tryReadPacket(1000, true);
if (pPacket.isNull())
continue;
if (handleSerialCode(pPacket->serialCode()))
{
isFaultClean = true;
break;
}
}
});
VideoFrame::Ptr pUserFrame =
VideoFrame::create(NO_SIGNAL_YUV_FORMAT, NO_SIGNAL_YUV_WIDTH, NO_SIGNAL_YUV_HEIGHT, NO_SIGNAL_YUV_PATH);
runSendUserFrame(pUserFrame, [&]() -> bool { return (!isCancelled && !isFaultClean); });
tryTask.cancel();
}
Core::Packet::Ptr VideoDecoder::runExceptionProcedure(std::atomic<bool>& isCancelled)
{
releaseDecoder();
std::atomic<bool> isSignalLost(true);
Core::Packet::Ptr pPacket = nullptr;
::Util::Task task;
task.run(
[&](std::atomic<bool>& isCancelled) -> void
{
while (!isCancelled)
{
pPacket = tryReadPacket(1000, false);
if (!pPacket.isNull())
{
isSignalLost = false;
break;
}
}
});
VideoFrame::Ptr pUserFrame =
VideoFrame::create(NO_SIGNAL_YUV_FORMAT, NO_SIGNAL_YUV_WIDTH, NO_SIGNAL_YUV_HEIGHT, NO_SIGNAL_YUV_PATH);
runSendUserFrame(pUserFrame, [&]() -> bool { return (!isCancelled && isSignalLost); });
task.cancel();
return pPacket;
}
bool VideoDecoder::handleSerialCode(const Core::SerialCode& code)
{
bool errorOccurred = false;
if (serialCodeWatcher().isChanged(code, true))
{
try
{
handlePreviousChanged();
}
catch (Poco::Exception& e)
{
logger().error("%s: %s", name(), e.displayText());
errorOccurred = true;
}
catch (std::exception& e)
{
logger().error("%s: %s", name(), std::string(e.what()));
errorOccurred = true;
}
}
if (errorOccurred)
releaseDecoder();
return !errorOccurred;
}
void VideoDecoder::handlePreviousChanged()
{
VideoComponent::Ptr pPreviousVideo = nullptr;
Core::CodecID codecID = Core::CodecID::NB;
auto pPrevious = previousComponents().first();
auto pDemuxer = pPrevious.cast<Core::Demuxer>();
if (pDemuxer)
{
auto pEncoder = pDemuxer->videoEncoder();
if (pEncoder)
{
pPreviousVideo = pEncoder;
codecID = pEncoder->getCodecID();
}
}
if (!pPreviousVideo)
{
auto V4L2Input = pPrevious.cast<V4L2::Input>();
if (V4L2Input)
{
pPreviousVideo = V4L2Input;
codecID = V4L2Input->getCodecID();
}
}
if (!pPreviousVideo)
throw Poco::NotImplementedException(Poco::format("Unsupported previous component: %s", pPrevious->name()));
const auto width = pPreviousVideo->getWidth();
const auto height = pPreviousVideo->getHeight();
const auto framteRate = pPreviousVideo->getFrameRate();
setWidth(width);
setHeight(height);
setFrameRate(framteRate);
ParameterNames parameters;
config().getChangedParameters(parameters);
if (_pipe.width() != width || _pipe.height() != height)
{
_pipe.stop();
_pipe.start(width, height);
serialCodeManager().increment();
}
hi_vdec_chn_status status;
int rc = hi_mpi_vdec_query_status(channelID(), &status);
logger().debug("%s: Query status: %#x, parameters changed:%d, %d, %d", name(), (uint32_t)rc,
(int)!parameters.empty(), (int)status.type, (int)MPP5::Util::getPayloadType(codecID));
if (HI_ERR_VDEC_UNEXIST == rc || !parameters.empty() || status.type != MPP5::Util::getPayloadType(codecID))
{
releaseDecoder();
createDecoder(width, height, framteRate, codecID);
notifyNextComponents(sharedFromThis(), new Core::ParameterChangedNotification(parameters));
}
}
void VideoDecoder::decodePacket(Core::Packet::Ptr pPacket, std::atomic<bool>& isCancelled)
{
hi_vdec_stream st = {};
st.end_of_frame = HI_FALSE;
st.end_of_stream = HI_FALSE;
st.need_display = HI_TRUE;
st.addr = (hi_u8*)pPacket->data();
st.len = pPacket->length();
st.pts = pPacket->pts();
int rc;
do
{
rc = hi_mpi_vdec_send_stream(channelID(), &st, 1000);
} while (!isCancelled && rc == HI_ERR_VDEC_BUF_FULL);
}
void VideoDecoder::createDecoder(uint32_t width, uint32_t height, uint32_t frameRate, Core::CodecID codecID)
{
logger().debug("%s: Initialize decoder: %s %ux%u@%u", name(), Core::codecIDToString(codecID), width, height,
frameRate);
hi_pic_buf_attr pic;
pic.width = width;
pic.height = height;
pic.align = HI_DEFAULT_ALIGN;
pic.bit_width = HI_DATA_BIT_WIDTH_8;
pic.pixel_format = HI_PIXEL_FORMAT_YVU_SEMIPLANAR_420;
pic.compress_mode = HI_COMPRESS_MODE_NONE;
hi_vdec_chn_attr attr;
attr.type = MPP5::Util::getPayloadType(codecID);
attr.mode = HI_VDEC_SEND_MODE_FRAME;
attr.pic_width = width;
attr.pic_height = height;
attr.stream_buf_size = attr.pic_width * attr.pic_height * 3 / 2;
attr.frame_buf_size = hi_vdec_get_pic_buf_size(attr.type, &pic);
attr.frame_buf_cnt = 33;
attr.video_attr.ref_frame_num = 16;
attr.video_attr.temporal_mvp_en = HI_TRUE;
attr.video_attr.tmv_buf_size = hi_vdec_get_tmv_buf_size(attr.type, width, height);
int rc = hi_mpi_vdec_create_chn(channelID(), &attr);
if (rc != HI_SUCCESS)
throw Poco::InvalidAccessException(Poco::format("%s: Create chn FAIL: %#x", name(), (uint32_t)rc), rc);
hi_mpi_vdec_reset_chn(channelID());
hi_vdec_chn_param param;
hi_mpi_vdec_get_chn_param(channelID(), &param);
param.type = attr.type;
param.display_frame_num = 2;
switch (param.type)
{
case HI_PT_H264:
case HI_PT_H265:
param.video_param.composite_dec_en = HI_FALSE;
param.video_param.slice_input_en = HI_FALSE;
param.video_param.dec_mode = HI_VIDEO_DEC_MODE_IPB;
param.video_param.out_order = HI_VIDEO_OUT_ORDER_DISPLAY;
param.video_param.compress_mode = HI_COMPRESS_MODE_NONE;
param.video_param.video_format = HI_VIDEO_FORMAT_TILE_64x16;
break;
case HI_PT_JPEG:
case HI_PT_MJPEG:
param.pic_param.pixel_format = HI_PIXEL_FORMAT_YVU_SEMIPLANAR_420;
param.pic_param.alpha = 255;
break;
default:
break;
}
rc = hi_mpi_vdec_set_chn_param(channelID(), &param);
if (rc != HI_SUCCESS)
throw Poco::InvalidAccessException(Poco::format("%s: Set param FAIL: %#x", name(), (uint32_t)rc), rc);
rc = hi_mpi_vdec_set_display_mode(channelID(), HI_VIDEO_DISPLAY_MODE_PLAYBACK);
if (rc != HI_SUCCESS)
throw Poco::InvalidAccessException(Poco::format("%s: Set display mode FAIL: %#x", name(), (uint32_t)rc), rc);
rc = hi_mpi_vdec_start_recv_stream(channelID());
if (rc != HI_SUCCESS)
throw Poco::InvalidAccessException(Poco::format("%s: Start recv stream FAIL: %#x", name(), (uint32_t)rc), rc);
_postTaskControl = PostTaskControl::RUNNING;
_postTask.run(
[this](std::atomic<bool>& isCancelled) -> void
{
_isCatchingUp = false;
while (!isCancelled)
{
hi_video_frame_info frame;
bool noAudio = false;
int rc = hi_mpi_vdec_get_frame(channelID(), &frame, NULL, 100);
if (rc < 0)
{
logger().warning("%s: Read decoded frame FAIL: %#x", name(), (uint32_t)rc);
if (_postTaskControl == PostTaskControl::EXIT || _postTaskControl == PostTaskControl::INSTANT_EXIT)
break;
if (!_frozenPlay)
continue;
else
noAudio = true;
}
if (_buttonPressed != _frozenPlay)
{
_buttonPressed = _frozenPlay;
if (_frozenPlay)
{
saveSingleFrame(frame, "/customer/resources/frozen" + std::to_string(_channelId));
loadVideoFrame("/customer/resources/frozen" + std::to_string(_channelId), _frameX);
}
else
{
frameRelease(_frameX);
std::string dir = "/customer/resources/frozen" + std::to_string(_channelId);
std::string cmd = "rm -rf " + dir;
if (system(cmd.c_str()) != 0)
logger().warning("rm -rf %s failed", dir.c_str());
}
}
if (_postTaskControl == PostTaskControl::EXIT)
{
hi_vdec_chn_status status;
if (hi_mpi_vdec_query_status(channelID(), &status) == HI_SUCCESS && status.left_decoded_frames == 0)
{
logger().debug("%s: No decoded frame, exit.", name());
isCancelled = true;
}
}
else if (_postTaskControl == PostTaskControl::INSTANT_EXIT)
{
logger().debug("%s: Instant exit.", name());
isCancelled = true;
}
auto pAudio = getAssociatedAudio();
if (pAudio.isNull() || pAudio->getTimeline() == 0 || !pAudio->started() || noAudio)
{
if (!noAudio)
{
updateCatchUpState();
if (!_isCatchingUp)
_synchronizer.syncFrame(frame.video_frame.pts);
}
}
else
{
int64_t diff = frame.video_frame.pts - pAudio->getTimeline();
if (diff > 0)
{
if (diff > 1000000000000)
diff = 1;
else if (diff > 2000000)
{
logger().warning("%s: Video exceeds audio timeline by %Ld microseconds (%.1f seconds)",
name(), diff, diff / 1000000.0);
diff = 2000000;
}
std::this_thread::sleep_for(std::chrono::microseconds(diff));
}
}
frame.video_frame.pts = Core::System::timestamp();
frame.video_frame.time_ref = _frameTimeRef;
_frameTimeRef += 2;
_frameX.video_frame.pts = Core::System::timestamp();
_frameX.video_frame.time_ref = _frameTimeRef;
if (_frozenPlay)
hi_mpi_vpss_send_frame(_pipe.groupID(), &_frameX, 0);
else
{
hi_mpi_vpss_send_frame(_pipe.groupID(), &frame, 0);
}
hi_mpi_vdec_release_frame(channelID(), &frame);
}
});
setSignalLost(false);
commit();
}
void VideoDecoder::releaseDecoder(bool instant)
{
logger().debug("%s: Deinitialize decoder...", name());
hi_vdec_chn_status status;
if (hi_mpi_vdec_query_status(channelID(), &status) == HI_SUCCESS)
{
logger().debug("%s: Decoded frame left: %u", name(), status.left_decoded_frames);
}
_postTaskControl = instant ? PostTaskControl::INSTANT_EXIT : PostTaskControl::EXIT;
int wait = 0;
while (_postTask.isRunning())
{
Poco::Thread::sleep(500);
if (wait++ >= 10)
{
_postTask.cancel();
break;
}
}
releaseCapabilityHandle();
hi_mpi_vdec_stop_recv_stream(channelID());
hi_mpi_vdec_destroy_chn(channelID());
setSignalLost(true);
commit();
logger().debug("%s: Deinitialize decoder complete", name());
}
Core::Packet::Ptr VideoDecoder::tryReadPacket(uint32_t timeout, bool shuldLog)
{
Core::Packet::Ptr pPacket;
try
{
pPacket = readPacket(timeout);
}
catch (Poco::Exception& e)
{
if (shuldLog)
logger().error("%s: %s", name(), e.displayText());
}
catch (std::exception& e)
{
if (shuldLog)
logger().error("%s: %s", name(), std::string(e.what()));
}
return pPacket;
}
void VideoDecoder::releaseCapabilityHandle()
{
}
void VideoDecoder::initModule()
{
hi_vdec_mod_param param;
hi_mpi_vdec_get_mod_param(&param);
param.vb_src = HI_VB_SRC_PRIVATE;
param.mini_buf_mode = 0;
param.pic_mod_param.capacity_strategy = HI_VDEC_CAPACITY_STRATEGY_BY_CHN;
auto rc = hi_mpi_vdec_set_mod_param(&param);
if (rc != HI_SUCCESS)
throw Poco::InvalidAccessException(
Poco::format("MPP5::VDEC: Set module param FAIL: %u(%#x)", (uint32_t)rc, (uint32_t)rc));
}
void VideoDecoder::deinitModule()
{
}
void VideoDecoder::reset()
{
}
void VideoDecoder::frameCopy(hi_video_frame_info& from, hi_video_frame_info& to)
{
uint32_t imageBytes, lumaBytes, chromaBytes;
uint32_t stride = from.video_frame.stride[0];
uint32_t height = from.video_frame.height;
switch (from.video_frame.pixel_format)
{
case HI_PIXEL_FORMAT_YVU_SEMIPLANAR_420:
case HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420:
imageBytes = stride * ::Util::Math::alignup(height, 16) * 3 / 2;
lumaBytes = stride * ::Util::Math::alignup(height, 16);
chromaBytes = lumaBytes >> 1;
break;
case HI_PIXEL_FORMAT_YVU_SEMIPLANAR_422:
case HI_PIXEL_FORMAT_YUV_SEMIPLANAR_422:
imageBytes = stride * ::Util::Math::alignup(height, 16) * 2;
lumaBytes = stride * ::Util::Math::alignup(height, 16);
chromaBytes = lumaBytes;
break;
default:
throw Poco::NotImplementedException("Unsupported pixel format");
}
hi_vb_blk handle = hi_mpi_vb_get_blk(poolId, imageBytes, "anonymous");
if (HI_VB_INVALID_HANDLE == handle)
throw Poco::RuntimeException(
Poco::format("VideoFrame: Failed to get VB block: %u bytes @POOL#%d", imageBytes, (int)poolId));
hi_phys_addr_t phyAddr = hi_mpi_vb_handle_to_phys_addr(handle);
if (!phyAddr)
throw Poco::RuntimeException("VideoFrame: Failed to get VB block phys-addr");
uint8_t* pVirAddr = (uint8_t*)hi_mpi_sys_mmap(phyAddr, imageBytes);
if (!pVirAddr)
throw Poco::RuntimeException("VideoFrame: Failed to map VB block");
memset(&to, 0, sizeof(hi_video_frame_info));
to.mod_id = HI_ID_USER;
to.pool_id = poolId;
memcpy(&to.video_frame, &from.video_frame, sizeof(hi_video_frame));
to.video_frame.phys_addr[0] = phyAddr;
to.video_frame.phys_addr[1] = (phyAddr + lumaBytes);
to.video_frame.virt_addr[0] = (hi_void*)pVirAddr;
to.video_frame.virt_addr[1] = (hi_void*)(pVirAddr + lumaBytes);
to.video_frame.time_ref = 0;
to.video_frame.pts = 0;
to.video_frame.frame_flag = 0;
to.video_frame.user_data[0] = imageBytes;
to.video_frame.user_data[1] = (hi_u64)pVirAddr;
if (lumaBytes > 0)
{
hi_void* pFromVirAddr = hi_mpi_sys_mmap(from.video_frame.phys_addr[0], lumaBytes);
memcpy(to.video_frame.virt_addr[0], pFromVirAddr, lumaBytes);
hi_mpi_sys_munmap(pFromVirAddr, lumaBytes);
}
if (chromaBytes > 0)
{
hi_void* pFromVirAddr = hi_mpi_sys_mmap(from.video_frame.phys_addr[1], chromaBytes);
memcpy(to.video_frame.virt_addr[1], pFromVirAddr, chromaBytes);
hi_mpi_sys_munmap(pFromVirAddr, chromaBytes);
}
}
void VideoDecoder::frameRelease(hi_video_frame_info& frame)
{
uint32_t imageBytes = frame.video_frame.user_data[0];
hi_void* pVirAddr = (hi_void*)frame.video_frame.user_data[1];
if (imageBytes && pVirAddr)
{
hi_sys_virt_mem_info info;
if (hi_mpi_sys_get_virt_mem_info(pVirAddr, &info) == HI_SUCCESS)
{
hi_mpi_sys_munmap(pVirAddr, imageBytes);
hi_mpi_vb_release_blk(hi_mpi_vb_phys_addr_to_handle(info.phys_addr));
}
frame.video_frame.user_data[0] = 0;
frame.video_frame.user_data[1] = 0;
}
}
bool VideoDecoder::saveSingleFrame(hi_video_frame_info& frame, const std::string& dir_path)
{
mkdir("/customer/resources", 0755);
mkdir(dir_path.c_str(), 0755);
uint32_t stride = frame.video_frame.stride[0];
uint32_t width = frame.video_frame.width;
uint32_t height = frame.video_frame.height;
uint32_t pixel_format = frame.video_frame.pixel_format;
uint32_t lumaBytes, chromaBytes, imageBytes;
uint32_t aligned_height = ::Util::Math::alignup(height, 16);
switch (pixel_format)
{
case HI_PIXEL_FORMAT_YVU_SEMIPLANAR_420:
case HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420:
lumaBytes = stride * aligned_height;
chromaBytes = lumaBytes >> 1;
imageBytes = lumaBytes + chromaBytes;
break;
case HI_PIXEL_FORMAT_YVU_SEMIPLANAR_422:
case HI_PIXEL_FORMAT_YUV_SEMIPLANAR_422:
lumaBytes = stride * aligned_height;
chromaBytes = lumaBytes;
imageBytes = lumaBytes + chromaBytes;
break;
default:
throw Poco::NotImplementedException("Unsupported pixel format");
}
std::string yuv_path = dir_path + "/frame.yuv";
FILE* fp_yuv = fopen(yuv_path.c_str(), "wb");
if (!fp_yuv)
return false;
hi_void* pLuma = hi_mpi_sys_mmap(frame.video_frame.phys_addr[0], lumaBytes);
if (pLuma)
{
fwrite(pLuma, 1, lumaBytes, fp_yuv);
hi_mpi_sys_munmap(pLuma, lumaBytes);
}
hi_void* pChroma = hi_mpi_sys_mmap(frame.video_frame.phys_addr[1], chromaBytes);
if (pChroma)
{
fwrite(pChroma, 1, chromaBytes, fp_yuv);
hi_mpi_sys_munmap(pChroma, chromaBytes);
}
fclose(fp_yuv);
std::string info_path = dir_path + "/frame.bin";
int fd = open(info_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0)
return false;
hi_video_frame_info frame_to_save = frame;
for (int i = 0; i < OT_MAX_COLOR_COMPONENT; i++)
{
frame_to_save.video_frame.virt_addr[i] = NULL;
frame_to_save.video_frame.header_virt_addr[i] = NULL;
}
uint32_t extra_info[4] = {lumaBytes, chromaBytes, imageBytes, aligned_height};
write(fd, extra_info, sizeof(extra_info));
write(fd, &frame_to_save, sizeof(hi_video_frame_info));
close(fd);
sync();
logger().debug("Saved: %s and %s", yuv_path, info_path);
return true;
}
bool VideoDecoder::loadVideoFrame(const std::string& dir_path, hi_video_frame_info& frame)
{
std::string info_path = dir_path + "/frame.bin";
int fd = open(info_path.c_str(), O_RDONLY);
if (fd < 0)
return false;
uint32_t extra_info[4] = {0};
read(fd, extra_info, sizeof(extra_info));
uint32_t lumaBytes = extra_info[0];
uint32_t chromaBytes = extra_info[1];
uint32_t imageBytes = extra_info[2];
uint32_t aligned_height = extra_info[3];
hi_video_frame_info loaded_frame;
memset(&loaded_frame, 0, sizeof(loaded_frame));
read(fd, &loaded_frame, sizeof(hi_video_frame_info));
close(fd);
hi_vb_blk handle = hi_mpi_vb_get_blk(poolId, imageBytes, "loaded_frame");
if (HI_VB_INVALID_HANDLE == handle)
{
logger().warning("Failed to get VB block: %u bytes @POOL#%d", imageBytes, poolId);
return false;
}
hi_phys_addr_t phyAddr = hi_mpi_vb_handle_to_phys_addr(handle);
if (!phyAddr)
{
logger().warning("Failed to get VB block phys-addr\n");
hi_mpi_vb_release_blk(handle);
return false;
}
uint8_t* pVirAddr = (uint8_t*)hi_mpi_sys_mmap(phyAddr, imageBytes);
if (!pVirAddr)
{
logger().warning("Failed to map VB block");
hi_mpi_vb_release_blk(handle);
return false;
}
std::string yuv_path = dir_path + "/frame.yuv";
FILE* fp_yuv = fopen(yuv_path.c_str(), "rb");
if (!fp_yuv)
{
logger().warning("Failed to open: %s", yuv_path);
hi_mpi_sys_munmap(pVirAddr, imageBytes);
hi_mpi_vb_release_blk(handle);
return false;
}
size_t bytes_read = fread(pVirAddr, 1, imageBytes, fp_yuv);
fclose(fp_yuv);
if (bytes_read != imageBytes)
{
logger().warning("Read incomplete: %zu/%u bytes", bytes_read, imageBytes);
hi_mpi_sys_munmap(pVirAddr, imageBytes);
hi_mpi_vb_release_blk(handle);
return false;
}
memset(&frame, 0, sizeof(hi_video_frame_info));
memcpy(&frame.video_frame, &loaded_frame.video_frame, sizeof(hi_video_frame));
frame.video_frame.phys_addr[0] = phyAddr;
frame.video_frame.phys_addr[1] = phyAddr + lumaBytes;
frame.video_frame.virt_addr[0] = pVirAddr;
frame.video_frame.virt_addr[1] = (hi_void*)(pVirAddr + lumaBytes);
frame.video_frame.user_data[0] = imageBytes;
frame.video_frame.user_data[1] = (hi_u64)pVirAddr;
logger().debug("Loaded frame: %ux%u, pts=%Lu", frame.video_frame.width, frame.video_frame.height,
loaded_frame.video_frame.pts);
return true;
}
void VideoDecoder::updateCatchUpState()
{
if (!_catchUpEnabled)
return;
hi_vdec_chn_status status;
hi_mpi_vdec_query_status(channelID(), &status);
const int queueSize = status.left_decoded_frames;
if (!_isCatchingUp && queueSize > V_CATCH_UP_ENTER)
{
_isCatchingUp = true;
logger().information("%s: video catch-up enter (queue=%d)", name(), queueSize);
}
else if (_isCatchingUp && queueSize <= V_CATCH_UP_EXIT)
{
_isCatchingUp = false;
_synchronizer.reset();
logger().information("%s: video catch-up exit (queue=%d)", name(), queueSize);
}
}
}
}