You've already forked M5Stack_Linux_Libs
mirror of
https://github.com/m5stack/M5Stack_Linux_Libs.git
synced 2026-05-20 11:01:38 -07:00
488 lines
18 KiB
C++
488 lines
18 KiB
C++
/**************************************************************************************************
|
|
*
|
|
* Copyright (c) 2019-2024 Axera Semiconductor Co., Ltd. All Rights Reserved.
|
|
*
|
|
* This source file is the property of Axera Semiconductor Co., Ltd. and
|
|
* may not be copied or distributed in any isomorphic form without the prior
|
|
* written consent of Axera Semiconductor Co., Ltd.
|
|
*
|
|
**************************************************************************************************/
|
|
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <chrono>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <thread>
|
|
#include "axcl.h"
|
|
#include "cmdline.h"
|
|
#include "demux/ffmpeg.hpp"
|
|
#include "dma_buffer.hpp"
|
|
#include "event.hpp"
|
|
#include "sample_vdec.hpp"
|
|
#include "sys/sample_sys.hpp"
|
|
#include "threadx.hpp"
|
|
#include "utils/def.h"
|
|
#include "utils/logger.h"
|
|
|
|
#define MAX_STREAM_COUNT (32)
|
|
static int g_exit = 0;
|
|
static void signal_handler(int s) {
|
|
printf("\n====================== caught signal: %d ======================\n", s);
|
|
g_exit = 1;
|
|
}
|
|
std::string output_to_file;
|
|
|
|
static void on_receive_demux_stream_data(const struct stream_data *data, uint64_t userdata);
|
|
static SAMPLE_VDEC_ATTR sample_get_vdec_attr_from_stream_info(const struct stream_info *info);
|
|
static void sample_get_decoded_image_thread(AX_VDEC_GRP grp, int32_t device_id, axcl::event *eof_event, SAMPLE_VDEC_CHN_INFO chn_info,
|
|
int32_t dump);
|
|
static AX_S32 sample_vdec_set_attr(SAMPLE_VDEC_ATTR *vdec_attr, int32_t chn_id, int32_t w, int32_t h);
|
|
|
|
int main(int argc, char *argv[]) {
|
|
SAMPLE_LOG_I("============== %s sample started %s %s ==============\n", AXCL_BUILD_VERSION, __DATE__, __TIME__);
|
|
|
|
signal(SIGINT, signal_handler);
|
|
|
|
cmdline::parser a;
|
|
a.add<std::string>("url", 'i', "mp4|.264|.265 file path", true);
|
|
a.add<uint32_t>("device", 'd', "device index from 0 to connected device num - 1", false, 0,
|
|
cmdline::range(0, AXCL_MAX_DEVICE_COUNT - 1));
|
|
a.add<int32_t>("count", '\0', "grp count", false, 1, cmdline::range(1, MAX_STREAM_COUNT));
|
|
a.add<std::string>("json", '\0', "axcl.json path", false, "./axcl.json");
|
|
a.add<int32_t>("width", 'w', "frame width", false, 1920, cmdline::range(AX_VDEC_MIN_WIDTH, AX_VDEC_MAX_WIDTH));
|
|
a.add<int32_t>("height", 'h', "frame height", false, 1080, cmdline::range(AX_VDEC_MIN_HEIGHT, AX_VDEC_MAX_HEIGHT));
|
|
a.add<int32_t>("VdChn", '\0', "channel id", false, 0);
|
|
a.add<int32_t>("yuv", '\0', "transfer nv12 from device", false, 0);
|
|
a.add<std::string>("output", 'o', "output to file", false, "");
|
|
a.parse_check(argc, argv);
|
|
const uint32_t device_index = a.get<uint32_t>("device");
|
|
const std::string url = a.get<std::string>("url");
|
|
const int32_t count = a.get<int32_t>("count");
|
|
const std::string json = a.get<std::string>("json");
|
|
int32_t w = a.get<int32_t>("width");
|
|
int32_t h = a.get<int32_t>("height");
|
|
int32_t chn_id = a.get<int32_t>("VdChn");
|
|
const int32_t dump = a.get<int32_t>("yuv");
|
|
output_to_file = a.get<std::string>("output");
|
|
/* step01: axcl initialize */
|
|
SAMPLE_LOG_I("json: %s", json.c_str());
|
|
if (axclError ret = axclInit(json.c_str()); AXCL_SUCC != ret) {
|
|
SAMPLE_LOG_E("axcl init fail, ret = 0x%x", ret);
|
|
return ret;
|
|
}
|
|
|
|
axclrtDeviceList device_list;
|
|
if (axclError ret = axclrtGetDeviceList(&device_list); AXCL_SUCC != ret || 0 == device_list.num) {
|
|
SAMPLE_LOG_E("no device is connected");
|
|
axclFinalize();
|
|
return ret;
|
|
}
|
|
|
|
if (device_index >= device_list.num) {
|
|
SAMPLE_LOG_E("device index %d is out of connected device num %d", device_index, device_list.num);
|
|
axclFinalize();
|
|
return 1;
|
|
}
|
|
|
|
const int32_t device_id = device_list.devices[device_index];
|
|
SAMPLE_LOG_I("device index: %d, bus number: %d", device_index, device_id);
|
|
|
|
/* step02: active device */
|
|
if (axclError ret = axclrtSetDevice(device_id); AXCL_SUCC != ret) {
|
|
SAMPLE_LOG_E("active device, ret = 0x%x", ret);
|
|
axclFinalize();
|
|
return ret;
|
|
}
|
|
|
|
ffmpeg_demuxer demuxers[MAX_STREAM_COUNT];
|
|
|
|
/* delegating demuxer memory destorier by smart pointer */
|
|
std::function<void(void *)> demux_deleter = [](void *p) { ffmpeg_destory_demuxer(static_cast<ffmpeg_demuxer>(p)); };
|
|
std::unique_ptr<void, std::function<void(void *)>> demuxers_sp[MAX_STREAM_COUNT];
|
|
|
|
/* step03: create ffmpeg demuxers */
|
|
for (int32_t i = 0; i < count; ++i) {
|
|
const AX_VDEC_GRP grp = static_cast<AX_VDEC_GRP>(i);
|
|
if (0 != ffmpeg_create_demuxer(&demuxers[i], url.c_str(), grp, device_id, {on_receive_demux_stream_data}, 0)) {
|
|
axclrtResetDevice(device_id);
|
|
axclFinalize();
|
|
return -1;
|
|
}
|
|
|
|
demuxers_sp[i] = std::unique_ptr<void, std::function<void(void *)>>(demuxers[i], demux_deleter);
|
|
}
|
|
|
|
/* step04: init sys module */
|
|
SAMPLE_LOG_I("init sys");
|
|
if (AX_S32 ret = sample_sys_init(); 0 != ret) {
|
|
SAMPLE_LOG_E("init sys, ret = 0x%x", ret);
|
|
axclrtResetDevice(device_id);
|
|
axclFinalize();
|
|
return ret;
|
|
}
|
|
|
|
/* step05: init vdec module */
|
|
SAMPLE_LOG_I("init vdec");
|
|
if (AX_S32 ret = sample_vdec_init(); 0 != ret) {
|
|
SAMPLE_LOG_E("init vdec, ret = 0x%x", ret);
|
|
sample_sys_deinit();
|
|
axclrtResetDevice(device_id);
|
|
axclFinalize();
|
|
return ret;
|
|
}
|
|
|
|
/* step06: start vdec */
|
|
axcl::threadx decode_threads[MAX_STREAM_COUNT];
|
|
axcl::event decode_eof_events[MAX_STREAM_COUNT];
|
|
for (int32_t i = 0; i < count; ++i) {
|
|
const AX_VDEC_GRP grp = static_cast<AX_VDEC_GRP>(i);
|
|
SAMPLE_LOG_I("start decoder %d", grp);
|
|
|
|
SAMPLE_VDEC_ATTR attr = sample_get_vdec_attr_from_stream_info(ffmpeg_get_stream_info(demuxers[i]));
|
|
|
|
if (chn_id) {
|
|
sample_vdec_set_attr(&attr, chn_id, w, h);
|
|
}
|
|
|
|
if (AX_S32 ret = sample_vdec_start(grp, &attr); 0 != ret) {
|
|
SAMPLE_LOG_E("start vdec %d fail, ret = 0x%x", grp, ret);
|
|
|
|
for (int32_t j = 0; j < i; ++j) {
|
|
sample_vdec_stop(j);
|
|
}
|
|
|
|
sample_vdec_deinit();
|
|
sample_sys_deinit();
|
|
axclrtResetDevice(device_id);
|
|
axclFinalize();
|
|
}
|
|
|
|
/* step07: start decoded get thread */
|
|
char name[32];
|
|
SAMPLE_VDEC_CHN_INFO chn_info;
|
|
memset(&chn_info, 0, sizeof(chn_info));
|
|
chn_info.u32ChnId = chn_id;
|
|
for (int32_t j = 0; j < AX_VDEC_MAX_CHN_NUM; ++j) {
|
|
if (attr.chn_attr[j].enable) {
|
|
chn_info.u32PicWidth = attr.chn_attr[j].attr.u32PicWidth;
|
|
chn_info.u32PicHeight = attr.chn_attr[j].attr.u32PicHeight;
|
|
break;
|
|
}
|
|
}
|
|
sprintf(name, "decode%d", grp);
|
|
decode_threads[i].start(name, sample_get_decoded_image_thread, grp, device_id, &decode_eof_events[i], chn_info, dump);
|
|
|
|
/* step08: start to demux video */
|
|
SAMPLE_LOG_I("start demuxer %d", i);
|
|
ffmpeg_start_demuxer(demuxers[i]);
|
|
}
|
|
|
|
int32_t eofs[MAX_STREAM_COUNT];
|
|
memset(eofs, 0, sizeof(eofs));
|
|
int32_t eof_count = 0;
|
|
|
|
while (!g_exit) {
|
|
/* wait video eof */
|
|
for (int32_t i = 0; i < count; ++i) {
|
|
if (eofs[i]) {
|
|
continue;
|
|
}
|
|
|
|
if (0 == ffmpeg_wait_demuxer_eof(demuxers[i], 0)) {
|
|
if (!decode_eof_events[i].wait(1000)) {
|
|
continue;
|
|
}
|
|
|
|
const AX_VDEC_GRP grp = static_cast<AX_VDEC_GRP>(i);
|
|
SAMPLE_LOG_I("stop decoder %d", grp);
|
|
sample_vdec_stop(grp);
|
|
|
|
eofs[i] = 1;
|
|
++eof_count;
|
|
SAMPLE_LOG_I("decoder %d is eof", grp);
|
|
}
|
|
}
|
|
|
|
if (eof_count == count) {
|
|
/* all streams and decoders are eof */
|
|
g_exit = 1;
|
|
break;
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
}
|
|
|
|
/* step09: stop vdec and demuxer */
|
|
for (int32_t i = 0; i < count; ++i) {
|
|
/**
|
|
* bugfix:
|
|
* Stop vdec first; otherwise `sample_vdec_send_stream(-1)` may hang.
|
|
*/
|
|
if (!eofs[i]) {
|
|
const AX_VDEC_GRP grp = static_cast<AX_VDEC_GRP>(i);
|
|
SAMPLE_LOG_I("stop decoder %d", grp);
|
|
sample_vdec_stop(grp);
|
|
}
|
|
|
|
SAMPLE_LOG_I("stop demuxer %d", i);
|
|
ffmpeg_stop_demuxer(demuxers[i]);
|
|
|
|
decode_threads[i].stop();
|
|
decode_threads[i].join();
|
|
}
|
|
|
|
/* step10: deinit vdec module */
|
|
SAMPLE_LOG_I("deinit vdec");
|
|
sample_vdec_deinit();
|
|
|
|
/* step11: deinit sys module */
|
|
SAMPLE_LOG_I("deinit sys");
|
|
sample_sys_deinit();
|
|
|
|
/* step12: deinit axcl */
|
|
SAMPLE_LOG_I("axcl deinit");
|
|
axclrtResetDevice(device_id);
|
|
axclFinalize();
|
|
|
|
SAMPLE_LOG_I("============== %s sample exited %s %s ==============\n", AXCL_BUILD_VERSION, __DATE__, __TIME__);
|
|
return 0;
|
|
}
|
|
|
|
static SAMPLE_VDEC_ATTR sample_get_vdec_attr_from_stream_info(const struct stream_info *info) {
|
|
SAMPLE_LOG_I("stream info: %dx%d payload %d fps %d", info->video.width, info->video.height, info->video.payload, info->video.fps);
|
|
|
|
SAMPLE_VDEC_ATTR vdec_attr;
|
|
memset(&vdec_attr, 0, sizeof(vdec_attr));
|
|
|
|
vdec_attr.decoded_mode = VIDEO_DEC_MODE_IPB;
|
|
vdec_attr.output_order = AX_VDEC_OUTPUT_ORDER_DEC;
|
|
vdec_attr.display_mode = AX_VDEC_DISPLAY_MODE_PLAYBACK;
|
|
|
|
AX_VDEC_GRP_ATTR_T &grp_attr = vdec_attr.grp_attr;
|
|
grp_attr.enCodecType = info->video.payload;
|
|
grp_attr.enInputMode = AX_VDEC_INPUT_MODE_FRAME;
|
|
grp_attr.u32MaxPicWidth = ALIGN_UP(info->video.width, 16);
|
|
grp_attr.u32MaxPicHeight = ALIGN_UP(info->video.height, 16);
|
|
grp_attr.u32StreamBufSize = grp_attr.u32MaxPicWidth * grp_attr.u32MaxPicHeight * 2;
|
|
grp_attr.bSdkAutoFramePool = AX_TRUE;
|
|
|
|
vdec_attr.chn_attr[0].enable = AX_TRUE;
|
|
vdec_attr.chn_attr[1].enable = AX_FALSE;
|
|
vdec_attr.chn_attr[2].enable = AX_FALSE;
|
|
AX_VDEC_CHN_ATTR_T &chn_attr = vdec_attr.chn_attr[0].attr;
|
|
chn_attr.u32PicWidth = info->video.width;
|
|
chn_attr.u32PicHeight = info->video.height;
|
|
chn_attr.u32FrameStride = ALIGN_UP(chn_attr.u32PicWidth, VDEC_STRIDE_ALIGN);
|
|
chn_attr.u32OutputFifoDepth = 3;
|
|
chn_attr.u32FrameBufCnt = 8;
|
|
chn_attr.stCompressInfo.enCompressMode = AX_COMPRESS_MODE_NONE;
|
|
chn_attr.stCompressInfo.u32CompressLevel = 0;
|
|
chn_attr.enOutputMode = AX_VDEC_OUTPUT_ORIGINAL;
|
|
chn_attr.enImgFormat = AX_FORMAT_YUV420_SEMIPLANAR;
|
|
chn_attr.u32FrameBufSize = sample_vdec_calc_blk_size(chn_attr.u32PicWidth, chn_attr.u32PicHeight, info->video.payload,
|
|
&chn_attr.stCompressInfo, chn_attr.enImgFormat);
|
|
|
|
return vdec_attr;
|
|
}
|
|
|
|
static AX_S32 sample_vdec_set_attr(SAMPLE_VDEC_ATTR *vdec_attr, int32_t chn_id, int32_t w, int32_t h) {
|
|
AX_VDEC_GRP_ATTR_T &grp_attr = vdec_attr->grp_attr;
|
|
|
|
vdec_attr->chn_attr[0].enable = AX_FALSE;
|
|
vdec_attr->chn_attr[chn_id].enable = AX_TRUE;
|
|
|
|
AX_VDEC_CHN_ATTR_T &chn_attr = vdec_attr->chn_attr[chn_id].attr;
|
|
chn_attr.u32PicWidth = w;
|
|
chn_attr.u32PicHeight = h;
|
|
chn_attr.u32FrameStride = ALIGN_UP(chn_attr.u32PicWidth, VDEC_STRIDE_ALIGN);
|
|
chn_attr.u32OutputFifoDepth = 3;
|
|
chn_attr.u32FrameBufCnt = 8;
|
|
chn_attr.stCompressInfo.enCompressMode = AX_COMPRESS_MODE_NONE;
|
|
chn_attr.stCompressInfo.u32CompressLevel = 0;
|
|
chn_attr.enOutputMode = AX_VDEC_OUTPUT_SCALE;
|
|
chn_attr.enImgFormat = AX_FORMAT_YUV420_SEMIPLANAR;
|
|
chn_attr.u32FrameBufSize = sample_vdec_calc_blk_size(chn_attr.u32PicWidth, chn_attr.u32PicHeight, grp_attr.enCodecType,
|
|
&chn_attr.stCompressInfo, chn_attr.enImgFormat);
|
|
return 0;
|
|
}
|
|
|
|
void NV12toI420_stride(
|
|
const uint8_t* nv12_ptr, // NV12原始数据指针
|
|
uint8_t* i420_ptr, // I420输出数据指针
|
|
int width, // 图像有效宽度
|
|
int height, // 图像有效高度
|
|
int picStride // 每行实际存储字节数(对齐)
|
|
) {
|
|
int y_size = width * height;
|
|
int uv_size = (width / 2) * (height / 2);
|
|
|
|
const uint8_t* src_y = nv12_ptr;
|
|
const uint8_t* src_uv = nv12_ptr + picStride * height;
|
|
uint8_t* dst_y = i420_ptr;
|
|
uint8_t* dst_u = i420_ptr + y_size;
|
|
uint8_t* dst_v = i420_ptr + y_size + uv_size;
|
|
|
|
// 1. 拷贝Y分量
|
|
for (int y = 0; y < height; y++) {
|
|
memcpy(dst_y + y * width, src_y + y * picStride, width);
|
|
}
|
|
|
|
// 2. 拆分UV分量为U、V分量
|
|
for (int j = 0; j < height / 2; j++) {
|
|
const uint8_t* uv_line = src_uv + j * picStride;
|
|
for (int i = 0; i < width / 2; i++) {
|
|
dst_u[j * (width / 2) + i] = uv_line[2 * i]; // U
|
|
dst_v[j * (width / 2) + i] = uv_line[2 * i + 1]; // V
|
|
}
|
|
}
|
|
}
|
|
|
|
static void sample_get_decoded_image_thread(AX_VDEC_GRP grp, int32_t device_id, axcl::event *eof_event, SAMPLE_VDEC_CHN_INFO chn_info,
|
|
int32_t dump) {
|
|
SAMPLE_LOG_I("[decoder %2d] decode thread +++", grp);
|
|
|
|
/* step01: create thread context */
|
|
axclrtContext context;
|
|
if (axclError ret = axclrtCreateContext(&context, device_id); AXCL_SUCC != ret) {
|
|
return;
|
|
}
|
|
|
|
const AX_VDEC_CHN chn = chn_info.u32ChnId;
|
|
AX_S32 ret;
|
|
|
|
const size_t size = ALIGN_UP(chn_info.u32PicWidth, VDEC_STRIDE_ALIGN) * chn_info.u32PicHeight * 3 / 2;
|
|
dma_buffer allocator(device_id);
|
|
|
|
if (dump) {
|
|
if (size <= 0) {
|
|
axclrtDestroyContext(context);
|
|
SAMPLE_LOG_E("[decoder %2d] decode nv12 frame size = %ld", grp, size);
|
|
return;
|
|
}
|
|
|
|
if (!allocator.alloc(size)) {
|
|
SAMPLE_LOG_E("[decoder %2d] alloc cma mem size %ld fail", grp, size);
|
|
axclrtDestroyContext(context);
|
|
return;
|
|
}
|
|
|
|
SAMPLE_LOG_I("[decoder %2d] alloc cma mem size %ld", grp, size);
|
|
}
|
|
|
|
const struct dma_mem &mem = allocator.get();
|
|
FILE *output_file = NULL;
|
|
if(!output_to_file.empty())
|
|
{
|
|
output_file = fopen(output_to_file.c_str(), "wb");
|
|
}
|
|
|
|
AX_U64 count = 0;
|
|
AX_VIDEO_FRAME_INFO_T frame;
|
|
memset(&frame, 0, sizeof(frame));
|
|
while (!g_exit) {
|
|
/* step02: get decoded image */
|
|
ret = sample_vdec_get_frame(grp, chn, &frame, -1);
|
|
if (0 != ret) {
|
|
if (AX_ERR_VDEC_UNEXIST == ret) {
|
|
SAMPLE_LOG_I("[decoder %2d] grp is destoried", grp);
|
|
break;
|
|
} else if (AX_ERR_VDEC_STRM_ERROR == ret) {
|
|
SAMPLE_LOG_W("[decoder %2d] stream is undecodeable", grp);
|
|
continue;
|
|
} else if (AX_ERR_VDEC_FLOW_END == ret) {
|
|
SAMPLE_LOG_W("[decoder %2d] flow end", grp);
|
|
eof_event->set();
|
|
break;
|
|
} else {
|
|
if (g_exit) {
|
|
break;
|
|
}
|
|
|
|
SAMPLE_LOG_E("[decoder %2d] get frame fail, ret = 0x%x", grp, ret);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* TODO: */
|
|
if (0 == ret) {
|
|
++count;
|
|
|
|
if (dump) {
|
|
if (axclError Ret =
|
|
axclrtMemcpy(reinterpret_cast<void *>(mem.blks[0].phy), (void*)frame.stVFrame.u64PhyAddr[0],
|
|
size, AXCL_MEMCPY_DEVICE_TO_HOST_PHY);
|
|
AXCL_SUCC != Ret) {
|
|
SAMPLE_LOG_E("axclrt memcpy device phy to host phy error grp:%d, chn:%d", grp, chn);
|
|
}
|
|
}
|
|
// int data_size = chn_info.u32PicWidth * chn_info.u32PicHeight * 3 / 2;
|
|
SAMPLE_LOG_I("[decoder %d] got frame %lld, %dx%d stride %d size %u, pts %lld, phy %llx", grp, frame.stVFrame.u64SeqNum,
|
|
frame.stVFrame.u32Width, frame.stVFrame.u32Height, frame.stVFrame.u32PicStride[0], frame.stVFrame.u32FrameSize,
|
|
frame.stVFrame.u64PTS, frame.stVFrame.u64PhyAddr[0]);
|
|
if(output_file)
|
|
{
|
|
std::vector<uint8_t> yuv_buf(frame.stVFrame.u32FrameSize);
|
|
if (axclError Ret =
|
|
axclrtMemcpy((void*)yuv_buf.data(), reinterpret_cast<void *>(frame.stVFrame.u64PhyAddr[0]),frame.stVFrame.u32FrameSize, AXCL_MEMCPY_DEVICE_TO_HOST);
|
|
AXCL_SUCC != Ret) {
|
|
SAMPLE_LOG_E("axclrt memcpy device phy to host phy error grp:%d, chn:%d", grp, chn);
|
|
}
|
|
std::vector<uint8_t> i420_buf((frame.stVFrame.u32Height * 3 / 2)*frame.stVFrame.u32Width);
|
|
NV12toI420_stride(yuv_buf.data(), i420_buf.data(), frame.stVFrame.u32Width, frame.stVFrame.u32Height, frame.stVFrame.u32PicStride[0]);
|
|
|
|
|
|
// for(int y = 0; y < frame.stVFrame.u32Height * 3 / 2; y++)
|
|
// {
|
|
// fwrite(yuv_buf.data() + y * frame.stVFrame.u32PicStride[0], 1, frame.stVFrame.u32Width, output_file);
|
|
// }
|
|
|
|
|
|
// for(int y = 0; y < frame.stVFrame.u32Height; y++)
|
|
// {
|
|
// fwrite(yuv_buf.data() + y * frame.stVFrame.u32PicStride[0], 1, frame.stVFrame.u32Width, output_file);
|
|
// }
|
|
|
|
// for(int y = frame.stVFrame.u32Height; y < frame.stVFrame.u32Height * 3 / 2; y++)
|
|
// {
|
|
// fwrite(yuv_buf.data() + y * frame.stVFrame.u32PicStride[0], 1, frame.stVFrame.u32Width, output_file);
|
|
// }
|
|
fwrite(i420_buf.data(), 1, i420_buf.size(), output_file);
|
|
}
|
|
|
|
/* step03: release decoded image */
|
|
sample_vdec_release_frame(grp, chn, &frame);
|
|
}
|
|
}
|
|
if(output_file)
|
|
{
|
|
fclose(output_file);
|
|
}
|
|
SAMPLE_LOG_I("[decoder %2d] total decode %lld frames", grp, count);
|
|
|
|
/* step04: destory thread context */
|
|
axclrtDestroyContext(context);
|
|
allocator.free();
|
|
|
|
SAMPLE_LOG_I("[decoder %2d] dfecode thread ---", grp);
|
|
}
|
|
|
|
static void on_receive_demux_stream_data(const struct stream_data *data, uint64_t userdata) {
|
|
AX_VDEC_GRP grp = static_cast<AX_VDEC_GRP>(data->cookie);
|
|
AX_VDEC_STREAM_T stream;
|
|
memset(&stream, 0, sizeof(stream));
|
|
stream.u64PTS = data->video.pts;
|
|
stream.u32StreamPackLen = data->video.size;
|
|
stream.pu8Addr = data->video.data;
|
|
stream.bEndOfFrame = AX_TRUE;
|
|
if (0 == data->video.size) {
|
|
stream.bEndOfStream = AX_TRUE;
|
|
}
|
|
|
|
AX_S32 ret = sample_vdec_send_stream(grp, &stream, -1);
|
|
if (0 != ret && !g_exit) {
|
|
SAMPLE_LOG_E("[decoder %2d] send stream (id: %ld, size: %u) fail, ret = 0x%x", grp, data->video.seq_num, data->video.size, ret);
|
|
}
|
|
}
|