* 功能说明: 适配NETINT硬件视频编码器,包括编码器初始化、启动、编码、停止、销毁等
*/
#define LOG_TAG "VideoDecoderNetint"
#include "VideoDecoderNetint.h"
#include <string>
#include <algorithm>
#include <unordered_map>
#include <chrono>
#include <dlfcn.h>
#include <unistd.h>
#include "android/log.h"
#include <sys/time.h>
namespace MediaCore {
namespace {
const std::string NI_DECODER_INIT_DEFAULT_PARAMS = "ni_logan_decoder_init_default_params";
const std::string NI_RSRC_ALLOCATE_AUTO = "ni_logan_rsrc_allocate_auto";
const std::string NI_RSRC_RELEASE_RESOURCE = "ni_logan_rsrc_release_resource";
const std::string NI_RSRC_FREE_DEVICE_CONTEXT = "ni_logan_rsrc_free_device_context";
const std::string NI_DEVICE_OPEN = "ni_logan_device_open";
const std::string NI_DEVICE_CLOSE = "ni_logan_device_close";
const std::string NI_DEVICE_SESSION_CONTEXT_INIT = "ni_logan_device_session_context_init";
const std::string NI_DEVICE_SESSION_OPEN = "ni_logan_device_session_open";
const std::string NI_DEVICE_SESSION_READ = "ni_logan_device_session_read";
const std::string NI_DEVICE_SESSION_FLUSH = "ni_logan_device_session_flush";
const std::string NI_DEVICE_DEC_SESSION_FLUSH = "ni_logan_device_dec_session_flush";
const std::string NI_DEVICE_DEC_SESSION_SAVE_HDRS = "ni_logan_device_dec_session_save_hdrs";
const std::string NI_DEVICE_SESSION_WRITE = "ni_logan_device_session_write";
const std::string NI_DEVICE_SESSION_CLOSE = "ni_logan_device_session_close";
const std::string NI_PACKET_BUFFER_ALLOC = "ni_logan_packet_buffer_alloc";
const std::string NI_PACKET_COPY = "ni_logan_packet_copy";
const std::string NI_PACKET_BUFFER_FREE = "ni_logan_packet_buffer_free";
const std::string NI_DECODER_FRAME_BUFFER_ALLOC = "ni_logan_decoder_frame_buffer_alloc";
const std::string NI_DECODER_FRAME_BUFFER_FREE = "ni_logan_decoder_frame_buffer_free";
using NiDecInitDefaultParamsFunc = ni_logan_retcode_t (*)(ni_logan_encoder_params_t *param, int fpsNum,
int fpsDenom, long bitRate, int width, int height);
using NiRsrcAllocateAutoFunc = ni_logan_device_context_t* (*)(ni_logan_device_type_t devType, ni_alloc_rule_t rule,
ni_codec_t codec, int width, int height, int frameRate, unsigned long *load);
using NiRsrcReleaseResourceFunc = void (*)(ni_logan_device_context_t *devCtx, ni_codec_t codec, unsigned long load);
using NiRsrcFreeDeviceContextFunc = void (*)(ni_logan_device_context_t *devCtx);
using NiDeviceOpenFunc = ni_device_handle_t (*)(const char *dev, uint32_t *maxIoSizeOut);
using NiDeviceCloseFunc = void (*)(ni_device_handle_t devHandle);
using NiDeviceSessionContextInitFunc = void (*)(ni_logan_session_context_t *sessionCtx);
using NiDeviceSessionOpenFunc = ni_logan_retcode_t (*)(ni_logan_session_context_t *sessionCtx,
ni_logan_device_type_t devType);
using NiDeviceSessionReadFunc = int (*)(ni_logan_session_context_t *sessionCtx,
ni_logan_session_data_io_t *sessionDataIo, ni_logan_device_type_t devType);
using NiDeviceSessionFlushFunc = ni_logan_retcode_t (*)(ni_logan_session_context_t *sessionCtx,
ni_logan_device_type_t devType);
using NiDeviceDecSessionFlushFunc = ni_logan_retcode_t (*)(ni_logan_session_context_t *sessionCtx);
using NiDeviceDecSessionSaveHdrsFunc =
ni_logan_retcode_t (*)(ni_logan_session_context_t *sessionCtx, uint8_t *hdrData, uint8_t hdrSize);
using NiDeviceSessionWriteFunc = int (*)(ni_logan_session_context_t *sessionCtx,
ni_logan_session_data_io_t *data, ni_logan_device_type_t devType);
using NiDeviceSessionCloseFunc =
ni_logan_retcode_t (*)(ni_logan_session_context_t *sessionCtx, int eosRecieved, ni_logan_device_type_t devType);
using NiPacketBufferAllocFunc = ni_logan_retcode_t (*)(ni_logan_packet_t *packet, int packetSize);
using NiPacketCopyFunc =
int (*)(void *destination, const void * const source, int curSize, void *leftover, int *prevSize);
using NiPacketBufferFreeFunc = ni_logan_retcode_t (*)(ni_logan_packet_t *packet);
using NiDecoderFrameBufferAllocFunc = ni_logan_retcode_t (*)(ni_logan_buf_pool_t *pool, ni_logan_frame_t *frame,
int allocMem, int videoWidth, int videoHeight, int alignment, int factor);
using NiDecoderFrameBufferFreeFunc = ni_logan_retcode_t (*)(ni_logan_frame_t *frame);
std::unordered_map<std::string, void*> g_funcMap = {
{ NI_DECODER_INIT_DEFAULT_PARAMS, nullptr },
{ NI_RSRC_ALLOCATE_AUTO, nullptr },
{ NI_RSRC_RELEASE_RESOURCE, nullptr },
{ NI_RSRC_FREE_DEVICE_CONTEXT, nullptr },
{ NI_DEVICE_OPEN, nullptr },
{ NI_DEVICE_CLOSE, nullptr },
{ NI_DEVICE_SESSION_CONTEXT_INIT, nullptr },
{ NI_DEVICE_SESSION_OPEN, nullptr },
{ NI_DEVICE_SESSION_READ, nullptr },
{ NI_DEVICE_SESSION_FLUSH, nullptr },
{ NI_DEVICE_DEC_SESSION_FLUSH, nullptr },
{ NI_DEVICE_DEC_SESSION_SAVE_HDRS, nullptr },
{ NI_DEVICE_SESSION_WRITE, nullptr },
{ NI_DEVICE_SESSION_CLOSE, nullptr },
{ NI_PACKET_BUFFER_ALLOC, nullptr },
{ NI_PACKET_COPY, nullptr },
{ NI_PACKET_BUFFER_FREE, nullptr },
{ NI_DECODER_FRAME_BUFFER_ALLOC, nullptr },
{ NI_DECODER_FRAME_BUFFER_FREE, nullptr }
};
enum class H264NaluType {
UNSPECIFIED = 0,
SLICE = 1,
DPA = 2,
DPB = 3,
DPC = 4,
IDR_SLICE = 5,
SEI = 6,
SPS = 7,
PPS = 8,
AUD = 9,
END_SEQUENCE = 10,
END_STREAM = 11,
FILLER_DATA = 12,
SPS_EXT = 13,
PREFIX = 14,
SUB_SPS = 15,
DPS = 16,
AUXILIARY_SLICE = 19,
};
enum class H265NaluType {
TRAIL_N = 0,
TRAIL_R = 1,
TSA_N = 2,
TSA_R = 3,
STSA_N = 4,
STSA_R = 5,
RADL_N = 6,
RADL_R = 7,
RASL_N = 8,
RASL_R = 9,
VCL_N10 = 10,
VCL_R11 = 11,
VCL_N12 = 12,
VCL_R13 = 13,
VCL_N14 = 14,
VCL_R15 = 15,
BLA_W_LP = 16,
BLA_W_RADL = 17,
BLA_N_LP = 18,
IDR_W_RADL = 19,
IDR_N_LP = 20,
CRA_NUT = 21,
RSV_IRAP_VCL22 = 22,
RSV_IRAP_VCL23 = 23,
RSV_VCL24 = 24,
RSV_VCL25 = 25,
RSV_VCL26 = 26,
RSV_VCL27 = 27,
RSV_VCL28 = 28,
RSV_VCL29 = 29,
RSV_VCL30 = 30,
RSV_VCL31 = 31,
VPS = 32,
SPS = 33,
PPS = 34,
AUD = 35,
EOS_NUT = 36,
EOB_NUT = 37,
FD_NUT = 38,
SEI_PREFIX = 39,
SEI_SUFFIX = 40,
RSV_NVCL41 = 41,
RSV_NVCL42 = 42,
RSV_NVCL43 = 43,
RSV_NVCL44 = 44,
RSV_NVCL45 = 45,
RSV_NVCL46 = 46,
RSV_NVCL47 = 47,
UNSPEC48 = 48,
UNSPEC49 = 49,
UNSPEC50 = 50,
UNSPEC51 = 51,
UNSPEC52 = 52,
UNSPEC53 = 53,
UNSPEC54 = 54,
UNSPEC55 = 55,
UNSPEC56 = 56,
UNSPEC57 = 57,
UNSPEC58 = 58,
UNSPEC59 = 59,
UNSPEC60 = 60,
UNSPEC61 = 61,
UNSPEC62 = 62,
UNSPEC63 = 63,
};
constexpr uint32_t NETINT_WIDTH_ALIGN = 32;
constexpr uint32_t NETINT_HEIGHT_ALIGN_H264 = 16;
constexpr uint32_t NETINT_HEIGHT_ALIGN_H265 = 8;
constexpr long DEFAULT_BITRATE = 2000000;
constexpr uint32_t NAL_START_CODE_MIN_LEN = 3;
constexpr uint32_t NAL_START_CODE_1ST_BYTE = 0;
constexpr uint32_t NAL_START_CODE_2ST_BYTE = 1;
constexpr uint32_t NAL_START_CODE_3ST_BYTE = 2;
constexpr uint32_t NAL_START_CODE_4ST_BYTE = 3;
const std::string SHARED_LIB_NAME = "libxcoder_logan.so";
std::atomic<bool> g_netintLoaded = { false };
void *g_libHandle = nullptr;
inline uint32_t AlignUp(uint32_t val, uint32_t align)
{
return (val + (align - 1)) & ~(align - 1);
}
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define ALOGF(...) __android_log_assert("assert", LOG_TAG, __VA_ARGS__)
}
VideoDecoderNetint::~VideoDecoderNetint()
{
DestroyDecoder();
ALOGI("decoder destructed.");
}
DecoderRetCode VideoDecoderNetint::CreateDecoder(MediaStreamFormat decType)
{
ALOGI("create decoder.");
if (decType == STREAM_FORMAT_AVC) {
m_codec = EN_H264;
} else if (decType == STREAM_FORMAT_HEVC) {
m_codec = EN_H265;
} else {
ALOGE("create decoder failed!");
return VIDEO_DECODER_CREATE_FAIL;
}
ALOGI("netint decoder constructed %s", (m_codec == EN_H264) ? "h.264" : "h.265");
return VIDEO_DECODER_SUCCESS;
}
DecoderRetCode VideoDecoderNetint::InitDecoder()
{
ALOGI("init decoder.");
return VIDEO_DECODER_SUCCESS;
}
DecoderRetCode VideoDecoderNetint::SendStreamData(uint8_t *buffer, uint32_t filledLen)
{
if (m_stop) {
ALOGE("send stream data, stop status.");
return VIDEO_DECODER_DECODE_FAIL;
}
return DecoderWriteData(buffer, filledLen);
}
DecoderRetCode VideoDecoderNetint::RetrieveFrameData(uint8_t *buffer, uint32_t maxLen, uint32_t *filledLen)
{
if (m_stop) {
ALOGE("retrieve frame data, stop status.");
return VIDEO_DECODER_DECODE_FAIL;
}
return DecoderReadData(buffer, maxLen, filledLen);
}
DecoderRetCode VideoDecoderNetint::SetCallbacks(std::function<void(DecodeEventIndex, uint32_t, void *)> eventCallBack)
{
m_eventCallBack = eventCallBack;
return VIDEO_DECODER_SUCCESS;
}
DecoderRetCode VideoDecoderNetint::SetCopyFrameFunc(
std::function<uint32_t(uint8_t*, uint8_t*, const PicInfoParams &, uint32_t)> copyFrame)
{
m_copyFrame = copyFrame;
return VIDEO_DECODER_SUCCESS;
}
DecoderRetCode VideoDecoderNetint::SetDecodeParams(DecodeParamsIndex index, void *decParams)
{
ALOGI("set decode params, index:%u", index);
switch (index) {
case INDEX_PIC_INFO: {
auto params = static_cast<PicInfoParams *>(decParams);
ALOGI("set decode params, index:%u, params width:%u, params height:%u, params stride:%d",
index, params->width, params->height, params->stride);
m_writeWidth = params->width;
m_writeHeight = params->height;
m_stride = params->stride;
break;
}
default:
break;
}
return VIDEO_DECODER_SUCCESS;
}
DecoderRetCode VideoDecoderNetint::GetDecodeParams(DecodeParamsIndex index, void *decParams)
{
ALOGI("get decode params, index:%u.", index);
switch (index) {
case INDEX_PIC_INFO: {
auto params = static_cast<PicInfoParams *>(decParams);
params->width = m_writeWidth;
params->stride = static_cast<int32_t>(m_writeWidth);
params->height = params->scanLines = m_writeHeight;
break;
}
case INDEX_PORT_FORMAT_INFO: {
auto params = static_cast<PortFormatParams *>(decParams);
if (params->port == OUT_PORT) {
params->format = PIXEL_FORMAT_FLEX_YUV_420P;
} else if (params->port == IN_PORT) {
params->format = m_codec;
} else {
return VIDEO_DECODER_GET_DECODE_PARAMS_FAIL;
}
break;
}
case INDEX_ALIGN_INFO: {
auto params = static_cast<AlignInfoParams *>(decParams);
params->widthAlign = NETINT_WIDTH_ALIGN;
if (m_codec == EN_H264) {
params->heightAlign = NETINT_HEIGHT_ALIGN_H264;
} else if (m_codec == EN_H265) {
params->heightAlign = NETINT_HEIGHT_ALIGN_H265;
} else {
params->heightAlign = 1;
}
break;
}
default:
break;
}
return VIDEO_DECODER_SUCCESS;
}
DecoderRetCode VideoDecoderNetint::Flush()
{
ALOGI("decoder flush.");
auto deviceDecSessionFlush =
reinterpret_cast<NiDeviceDecSessionFlushFunc>(g_funcMap[NI_DEVICE_DEC_SESSION_FLUSH]);
ni_logan_retcode_t ret = (*deviceDecSessionFlush)(&m_sessionCtx);
if (ret != NI_LOGAN_RETCODE_SUCCESS) {
ALOGE("device dec session flush error.");
return VIDEO_DECODER_RESET_FAIL;
}
return VIDEO_DECODER_SUCCESS;
}
DecoderRetCode VideoDecoderNetint::StartDecoder()
{
ALOGI("start decoder.");
if (!LoadNetintSharedLib()) {
ALOGE("load netint so error.");
return VIDEO_DECODER_START_FAIL;
}
if (!InitContext()) {
ALOGE("init context error.");
return VIDEO_DECODER_START_FAIL;
}
m_stop = false;
ALOGI("start decoder success");
return VIDEO_DECODER_SUCCESS;
}
DecoderRetCode VideoDecoderNetint::StopDecoder()
{
if (m_stop) {
ALOGI("stop decoder, stop already.");
return VIDEO_DECODER_SUCCESS;
}
m_packet.data.packet.end_of_stream = 1;
ALOGI("stop decoder, session ctx ready to close is %u, frame end of stream is %u",
m_sessionCtx.ready_to_close, m_frame.data.frame.end_of_stream);
DestroyContext();
m_stop = true;
return VIDEO_DECODER_SUCCESS;
}
void VideoDecoderNetint::DestroyDecoder()
{
ALOGI("destroy decoder.");
if (g_libHandle == nullptr) {
ALOGI("decoder has been destroyed.");
return;
}
if (!m_stop) {
ALOGI("destroy decoder, stop decoder.");
(void) StopDecoder();
}
ALOGI("destroy decoder done.");
}
bool VideoDecoderNetint::LoadNetintSharedLib() const
{
if (g_netintLoaded) {
ALOGI("netint has loaded.");
return true;
}
ALOGI("load %s", SHARED_LIB_NAME.c_str());
g_libHandle = dlopen(SHARED_LIB_NAME.c_str(), RTLD_LAZY);
if (g_libHandle == nullptr) {
const char *errStr = dlerror();
ALOGE("load %s error:%s", SHARED_LIB_NAME.c_str(), (errStr != nullptr) ? errStr : "unknown");
return false;
}
for (auto &symbol : g_funcMap) {
void *func = dlsym(g_libHandle, symbol.first.c_str());
if (func == nullptr) {
ALOGE("failed to load %s", symbol.first.c_str());
return false;
}
symbol.second = func;
}
g_netintLoaded = true;
return true;
}
bool VideoDecoderNetint::InitContext()
{
ALOGI("init context start.");
if (!InitCtxParams()) {
ALOGE("init context params failed.");
return false;
}
m_packet = {};
m_frame = {};
m_startOfStream = 1;
auto deviceSessionContextInit =
reinterpret_cast<NiDeviceSessionContextInitFunc>(g_funcMap[NI_DEVICE_SESSION_CONTEXT_INIT]);
(*deviceSessionContextInit)(&m_sessionCtx);
m_sessionCtx.p_session_config = nullptr;
m_sessionCtx.session_id = NI_LOGAN_INVALID_SESSION_ID;
m_sessionCtx.codec_format = (m_codec == EN_H264) ? NI_LOGAN_CODEC_FORMAT_H264 : NI_LOGAN_CODEC_FORMAT_H265;
auto rsrcAllocateAuto = reinterpret_cast<NiRsrcAllocateAutoFunc>(g_funcMap[NI_RSRC_ALLOCATE_AUTO]);
m_devCtx = (*rsrcAllocateAuto)( NI_LOGAN_DEVICE_TYPE_DECODER, EN_ALLOC_LEAST_INSTANCE, m_codec, m_writeWidth,
m_writeHeight, m_frameRate, &m_load);
if (m_devCtx == nullptr) {
ALOGE("rsrc allocate auto failed.");
return false;
}
std::string xcoderGuid = m_devCtx->p_device_info->dev_name;
std::string xcoderNsid = m_devCtx->p_device_info->blk_name;
ALOGI("netint xcoder Guid: %s", xcoderGuid.c_str());
ALOGI("netint xcoder Nsid: %s", xcoderNsid.c_str());
auto deviceOpen = reinterpret_cast<NiDeviceOpenFunc>(g_funcMap[NI_DEVICE_OPEN]);
ni_device_handle_t devHandle = (*deviceOpen)(xcoderNsid.c_str(), &m_sessionCtx.max_nvme_io_size);
ni_device_handle_t blkHandle = (*deviceOpen)(xcoderNsid.c_str(), &m_sessionCtx.max_nvme_io_size);
if ((devHandle == NI_INVALID_DEVICE_HANDLE) || (blkHandle == NI_INVALID_DEVICE_HANDLE)) {
ALOGE("init context, device open failed.");
return false;
}
m_sessionCtx.device_handle = devHandle;
m_sessionCtx.blk_io_handle = blkHandle;
m_sessionCtx.hw_id = 0;
m_sessionCtx.p_session_config = &m_decApiParams;
m_sessionCtx.src_bit_depth = m_bitDepth;
m_sessionCtx.src_endian = NI_LOGAN_FRAME_LITTLE_ENDIAN;
m_sessionCtx.bit_depth_factor = 1;
auto deviceSessionOpen = reinterpret_cast<NiDeviceSessionOpenFunc>(g_funcMap[NI_DEVICE_SESSION_OPEN]);
ni_logan_retcode_t ret = (*deviceSessionOpen)(&m_sessionCtx, NI_LOGAN_DEVICE_TYPE_DECODER);
if (ret != NI_LOGAN_RETCODE_SUCCESS) {
ALOGE("init decoder failed: device session open error %d", ret);
return false;
}
return true;
}
bool VideoDecoderNetint::InitCtxParams()
{
ALOGI("init ctx params start.");
auto decInitDefaultParams = reinterpret_cast<NiDecInitDefaultParamsFunc>(g_funcMap[NI_DECODER_INIT_DEFAULT_PARAMS]);
ni_logan_retcode_t ret =
(*decInitDefaultParams)(&m_decApiParams, m_frameRate, 1, DEFAULT_BITRATE, m_writeWidth, m_writeHeight);
if (ret != NI_LOGAN_RETCODE_SUCCESS) {
ALOGE("decoder init default params error %d", ret);
return false;
}
return true;
}
int VideoDecoderNetint::InitPacketData(const uint8_t *src, const uint32_t inputSize)
{
if (src == nullptr) {
ALOGE("decoder write data: input data buffer is nullptr.");
return NI_LOGAN_RETCODE_FAILURE;
}
int sendSize;
int saveSize = 0;
bool newPacket = false;
ni_logan_packet_t *inPacket = &(m_packet.data.packet);
if (inPacket->data_len == 0) {
std::fill_n(reinterpret_cast<uint8_t *>(inPacket), sizeof(ni_logan_packet_t), 0);
inPacket->p_data = nullptr;
inPacket->data_len = inputSize;
if (inputSize + m_sessionCtx.prev_size > 0) {
auto packetBufferAlloc = reinterpret_cast<NiPacketBufferAllocFunc>(g_funcMap[NI_PACKET_BUFFER_ALLOC]);
ni_logan_retcode_t ret = (*packetBufferAlloc)(inPacket, inputSize + m_sessionCtx.prev_size);
if (ret != NI_LOGAN_RETCODE_SUCCESS) {
ALOGE("decoder write data: packet buffer alloc failed.");
return NI_LOGAN_RETCODE_FAILURE;
}
}
newPacket = true;
sendSize = inputSize + m_sessionCtx.prev_size;
saveSize = m_sessionCtx.prev_size;
} else {
sendSize = static_cast<int>(inPacket->data_len);
}
inPacket->start_of_stream = m_startOfStream;
inPacket->end_of_stream = 0;
inPacket->video_width = m_writeWidth;
inPacket->video_height = m_writeHeight;
auto packetCopy = reinterpret_cast<NiPacketCopyFunc>(g_funcMap[NI_PACKET_COPY]);
if (sendSize == 0) {
if (newPacket) {
sendSize = (*packetCopy)(inPacket->p_data, src, 0, m_sessionCtx.p_leftover, &m_sessionCtx.prev_size);
}
inPacket->data_len = static_cast<uint32_t>(sendSize);
inPacket->end_of_stream = 1;
ALOGI("decoder write data: sending last packet, size:%d + eos", sendSize);
} else {
if (newPacket) {
sendSize =
(*packetCopy)(inPacket->p_data, src, inputSize, m_sessionCtx.p_leftover, &m_sessionCtx.prev_size);
inPacket->data_len += saveSize;
}
}
return sendSize;
}
bool VideoDecoderNetint::InitFrameData()
{
uint32_t width = m_sessionCtx.active_video_width > 0 ? m_sessionCtx.active_video_width : m_writeWidth;
uint32_t height = m_sessionCtx.active_video_height > 0 ? m_sessionCtx.active_video_height : m_writeHeight;
int allocMem = (m_sessionCtx.active_video_width > 0 && m_sessionCtx.active_video_height > 0) ? 1 : 0;
auto decoderFrameBufferAlloc =
reinterpret_cast<NiDecoderFrameBufferAllocFunc>(g_funcMap[NI_DECODER_FRAME_BUFFER_ALLOC]);
if (width > INT_MAX || height > INT_MAX) {
ALOGE("receiving data error, width:%u or height:%u out of range!", width, height);
return false;
}
ni_logan_retcode_t ret = (*decoderFrameBufferAlloc)(m_sessionCtx.dec_fme_buf_pool, &(m_frame.data.frame),
allocMem, static_cast<int>(width), static_cast<int>(height),
m_sessionCtx.codec_format == NI_LOGAN_CODEC_FORMAT_H264, m_sessionCtx.bit_depth_factor);
if (ret != NI_LOGAN_RETCODE_SUCCESS) {
ALOGE("receiving data error, decoder frame buffer alloc error. ret:%d", ret);
return false;
}
return true;
}
DecoderRetCode VideoDecoderNetint::DecoderWriteData(const uint8_t *buffer, const uint32_t filledLen)
{
if (m_sessionCtx.ready_to_close != 0) {
ALOGE("decoder write data: session ctx ready to close is 1, no send.");
return VIDEO_DECODER_DECODE_FAIL;
}
int sendSize = InitPacketData(buffer, filledLen);
m_startOfStream = 0;
if (sendSize == NI_LOGAN_RETCODE_FAILURE) {
ALOGE("decoder write data: send size is failure.");
return VIDEO_DECODER_DECODE_FAIL;
}
int txSize = DeviceDecSessionWrite();
if (txSize < 0) {
ALOGE("decoder write data: sending data error. txSize:%d", txSize);
(void) StopDecoder();
return VIDEO_DECODER_DECODE_FAIL;
} else if (txSize == 0 && filledLen != 0) {
ALOGW("decoder write data: 0 byte sent this time, sleep and will re-try.");
return VIDEO_DECODER_WRITE_OVERFLOW;
} else {
auto packetBufferFree = reinterpret_cast<NiPacketBufferFreeFunc>(g_funcMap[NI_PACKET_BUFFER_FREE]);
ni_logan_retcode_t ret = (*packetBufferFree)(&(m_packet.data.packet));
if (ret != NI_LOGAN_RETCODE_SUCCESS) {
ALOGW("decoder write data: packet buffer free failed, ret:%d", ret);
}
}
return VIDEO_DECODER_SUCCESS;
}
void VideoDecoderNetint::DecodeFpsStat()
{
auto clockTimeNow = std::chrono::steady_clock::now().time_since_epoch();
auto endTime = std::chrono::duration_cast<std::chrono::milliseconds>(clockTimeNow).count();
int64_t period = endTime - m_lastTime;
if (period >= 1000) {
float fps = static_cast<float>(m_frameCount) * 1000 / period;
if (m_lastTime != 0) {
ALOGI("PERF-DEC-FPS: %0.2f", fps);
}
m_lastTime = endTime;
m_frameCount = 0;
}
}
DecoderRetCode VideoDecoderNetint::DecoderReadData(uint8_t *buffer, const uint32_t maxLen, uint32_t *filledLen)
{
if (!InitFrameData()) {
*filledLen = 0;
(void) StopDecoder();
return VIDEO_DECODER_DECODE_FAIL;
}
auto deviceSessionRead = reinterpret_cast<NiDeviceSessionReadFunc>(g_funcMap[NI_DEVICE_SESSION_READ]);
int rxSize = (*deviceSessionRead)(&m_sessionCtx, &m_frame, NI_LOGAN_DEVICE_TYPE_DECODER);
if (rxSize < 0) {
ALOGE("decoder read data: receiving data error. rxSize:%d", rxSize);
auto decoderFrameBufferFree =
reinterpret_cast<NiDecoderFrameBufferFreeFunc>(g_funcMap[NI_DECODER_FRAME_BUFFER_FREE]);
(void) (*decoderFrameBufferFree)(&(m_frame.data.frame));
*filledLen = 0;
(void) StopDecoder();
return VIDEO_DECODER_DECODE_FAIL;
}
if (rxSize == 0) {
ALOGW("decoder read data: no decoded frame is available now. rxSize:%d", rxSize);
auto decoderFrameBufferFree =
reinterpret_cast<NiDecoderFrameBufferFreeFunc>(g_funcMap[NI_DECODER_FRAME_BUFFER_FREE]);
(void) (*decoderFrameBufferFree)(&(m_frame.data.frame));
*filledLen = 0;
if (m_frame.data.frame.end_of_stream == 1) {
ALOGI("decoder read data: frame end of stream is 1, rxSize is 0.");
return VIDEO_DECODER_EOS;
}
return VIDEO_DECODER_READ_UNDERFLOW;
}
m_frameCount++;
DecodeFpsStat();
return DecoderHandleData(buffer, maxLen, filledLen);
}
DecoderRetCode VideoDecoderNetint::DecoderHandleData(uint8_t *buffer, const uint32_t maxLen, uint32_t *filledLen)
{
uint8_t *dst = reinterpret_cast<uint8_t *>(m_frame.data.frame.p_data);
m_planeWidth = AlignUp(m_frame.data.frame.video_width, NETINT_WIDTH_ALIGN);
m_planeHeight = m_frame.data.frame.video_height;
if (m_planeWidth != m_writeWidth || m_planeHeight != m_writeHeight) {
PicInfoParams decParams = {
.width = m_planeWidth,
.height = m_planeHeight,
.stride = static_cast<int32_t>(m_planeWidth),
.scanLines = m_planeHeight,
.cropWidth = m_frame.data.frame.crop_right,
.cropHeight = m_frame.data.frame.crop_bottom
};
ALOGI("decoder handle data, plane width is %u, plane height is %u", m_planeWidth, m_planeHeight);
m_eventCallBack(INDEX_PIC_INFO_CHANGE, 0, &decParams);
return VIDEO_DECODER_BAD_PIC_SIZE;
}
PicInfoParams params = {m_writeWidth, m_writeHeight, m_stride, m_writeHeight};
auto convertSize = m_copyFrame(dst, buffer, params, maxLen);
*filledLen = convertSize;
auto decoderFrameBufferFree =
reinterpret_cast<NiDecoderFrameBufferFreeFunc>(g_funcMap[NI_DECODER_FRAME_BUFFER_FREE]);
(void) (*decoderFrameBufferFree)(&(m_frame.data.frame));
if (m_frame.data.frame.end_of_stream == 1) {
ALOGI("Receiving data end! frame end of stream is %u", m_frame.data.frame.end_of_stream);
return VIDEO_DECODER_EOS;
}
return VIDEO_DECODER_SUCCESS;
}
void VideoDecoderNetint::DestroyContext()
{
ALOGI("destroy context.");
auto deviceSessionFlush = reinterpret_cast<NiDeviceSessionFlushFunc>(g_funcMap[NI_DEVICE_SESSION_FLUSH]);
auto deviceSessionClose = reinterpret_cast<NiDeviceSessionCloseFunc>(g_funcMap[NI_DEVICE_SESSION_CLOSE]);
(void) (*deviceSessionFlush)(&m_sessionCtx, NI_LOGAN_DEVICE_TYPE_DECODER);
(void) (*deviceSessionClose)(&m_sessionCtx, 1, NI_LOGAN_DEVICE_TYPE_DECODER);
if (m_devCtx != nullptr) {
ALOGI("destroy rsrc start.");
auto rsrcReleaseResource = reinterpret_cast<NiRsrcReleaseResourceFunc>(g_funcMap[NI_RSRC_RELEASE_RESOURCE]);
auto rsrcFreeDeviceContext =
reinterpret_cast<NiRsrcFreeDeviceContextFunc>(g_funcMap[NI_RSRC_FREE_DEVICE_CONTEXT]);
(*rsrcReleaseResource)(m_devCtx, m_codec, m_load);
(*rsrcFreeDeviceContext)(m_devCtx);
m_devCtx = nullptr;
ALOGI("destroy rsrc done.");
}
auto packetBufferFree = reinterpret_cast<NiPacketBufferFreeFunc>(g_funcMap[NI_PACKET_BUFFER_FREE]);
auto decoderFrameBufferFree =
reinterpret_cast<NiDecoderFrameBufferFreeFunc>(g_funcMap[NI_DECODER_FRAME_BUFFER_FREE]);
auto deviceClose = reinterpret_cast<NiDeviceCloseFunc>(g_funcMap[NI_DEVICE_CLOSE]);
(void) (*packetBufferFree)(&(m_packet.data.packet));
(void) (*decoderFrameBufferFree)(&(m_frame.data.frame));
(*deviceClose)(m_sessionCtx.device_handle);
(*deviceClose)(m_sessionCtx.blk_io_handle);
ALOGI("destroy context done.");
}
int VideoDecoderNetint::DeviceDecSessionWrite()
{
uint8_t streamHeaders[4 * 1024];
uint8_t *hdr = streamHeaders;
uint8_t *buf = reinterpret_cast<uint8_t *>(m_packet.data.packet.p_data);
uint32_t dataSize = m_packet.data.packet.data_len;
int nalType = -1;
int headerSize = 0;
bool spsFound = false;
bool ppsFound = false;
bool vpsFound = false;
bool headersFound = false;
int nalSize = FindNextNonVclNalu(std::pair<uint8_t*, uint32_t>(buf, dataSize), m_sessionCtx.codec_format, nalType);
while (dataSize > NAL_START_CODE_MIN_LEN && nalSize > 0) {
if (m_sessionCtx.codec_format == NI_LOGAN_CODEC_FORMAT_H264) {
spsFound = (spsFound || (nalType == static_cast<int>(H264NaluType::SPS)));
ppsFound = (ppsFound || (nalType == static_cast<int>(H264NaluType::PPS)));
headersFound = (headersFound || (spsFound && ppsFound));
} else if (m_sessionCtx.codec_format == NI_LOGAN_CODEC_FORMAT_H265) {
vpsFound = (vpsFound || (nalType == static_cast<int>(H265NaluType::VPS)));
spsFound = (spsFound || (nalType == static_cast<int>(H265NaluType::SPS)));
ppsFound = (ppsFound || (nalType == static_cast<int>(H265NaluType::PPS)));
headersFound = (headersFound || (vpsFound && spsFound && ppsFound));
}
if ((m_sessionCtx.codec_format == NI_LOGAN_CODEC_FORMAT_H264 &&
(nalType == static_cast<int>(H264NaluType::SPS) || nalType == static_cast<int>(H264NaluType::PPS))) ||
(m_sessionCtx.codec_format == NI_LOGAN_CODEC_FORMAT_H265 &&
(nalType == static_cast<int>(H265NaluType::VPS) || nalType == static_cast<int>(H265NaluType::SPS) ||
nalType == static_cast<int>(H265NaluType::PPS)))) {
headerSize += nalSize;
(void)std::copy_n(buf, nalSize, hdr);
hdr += nalSize;
}
buf += nalSize;
dataSize -= nalSize;
if (headersFound) {
auto deviceDecSessionSaveHdrs =
reinterpret_cast<NiDeviceDecSessionSaveHdrsFunc>(g_funcMap[NI_DEVICE_DEC_SESSION_SAVE_HDRS]);
ni_logan_retcode_t ret = (*deviceDecSessionSaveHdrs)(&m_sessionCtx, streamHeaders, headerSize);
if (ret != NI_LOGAN_RETCODE_SUCCESS) {
ALOGE("DeviceDecSessionWrite save hdrs failed: %d", ret);
}
break;
}
nalSize = FindNextNonVclNalu(std::pair<uint8_t*, uint32_t>(buf, dataSize), m_sessionCtx.codec_format, nalType);
}
auto deviceSessionWrite = reinterpret_cast<NiDeviceSessionWriteFunc>(g_funcMap[NI_DEVICE_SESSION_WRITE]);
int txSize = (*deviceSessionWrite)(&m_sessionCtx, &m_packet, NI_LOGAN_DEVICE_TYPE_DECODER);
return txSize;
}
int VideoDecoderNetint::FindNextNonVclNalu(std::pair<uint8_t*, uint32_t> inBuf, uint32_t codec, int &nalType)
{
uint8_t *inData = inBuf.first;
uint32_t inSize = inBuf.second;
nalType = -1;
if (inSize <= NAL_START_CODE_MIN_LEN) {
return 0;
}
int i = FindNalStartCode(inBuf);
if (i == -1) {
return 0;
}
if (inData[i + NAL_START_CODE_3ST_BYTE] != 0x01) {
i++;
}
i += NAL_START_CODE_MIN_LEN;
if (codec == NI_LOGAN_CODEC_FORMAT_H264) {
nalType = (inData[i] & 0x1f);
if (nalType >= static_cast<int>(H264NaluType::SLICE) &&
nalType <= static_cast<int>(H264NaluType::IDR_SLICE)) {
return 0;
}
} else if (codec == NI_LOGAN_CODEC_FORMAT_H265) {
nalType = (inData[i] >> 1);
} else {
ALOGE("Codec format is invalid");
return 0;
}
while ((inData[i + NAL_START_CODE_1ST_BYTE] != 0x00 || inData[i + NAL_START_CODE_2ST_BYTE] != 0x00 ||
inData[i + NAL_START_CODE_3ST_BYTE] != 0x00) &&
(inData[i + NAL_START_CODE_1ST_BYTE] != 0x00 || inData[i + NAL_START_CODE_2ST_BYTE] != 0x00 ||
inData[i + NAL_START_CODE_3ST_BYTE] != 0x01)) {
i++;
if (i + NAL_START_CODE_MIN_LEN > inSize) {
return inSize;
}
}
return i;
}
int VideoDecoderNetint::FindNalStartCode(std::pair<uint8_t*, uint32_t> &inBuf)
{
uint8_t *inData = inBuf.first;
uint32_t inSize = inBuf.second;
int i = 0;
while ((inData[i + NAL_START_CODE_1ST_BYTE] != 0x00 || inData[i + NAL_START_CODE_2ST_BYTE] != 0x00 ||
inData[i + NAL_START_CODE_3ST_BYTE] != 0x01) &&
(inData[i + NAL_START_CODE_1ST_BYTE] != 0x00 || inData[i + NAL_START_CODE_2ST_BYTE] != 0x00 ||
inData[i + NAL_START_CODE_3ST_BYTE] != 0x00 || inData[i + NAL_START_CODE_4ST_BYTE] != 0x01)) {
i++;
if (i + NAL_START_CODE_MIN_LEN > inSize) {
return -1;
}
}
return i;
}
}