Files
2026-08-24 10:03:24 +08:00

445 lines
14 KiB
C

#ifndef CA_DEBUG_LEVEL
#define CA_DEBUG_LEVEL 2
#endif
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <pthread.h>
#include "AmTsPlayer.h"
#include <stdbool.h>
#include <sys/time.h>
#ifdef UNUSED
#undef UNUSED
#endif
#include "am_cas.h"
#define INF(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__)
#define ERR(fmt, ...) fprintf(stderr, "error:" fmt, ##__VA_ARGS__)
static pthread_t gInjectThread;
static am_tsplayer_handle tsplayer_handle;
static CasSession cas_session;
static SecMemHandle secmem_session;
static void *sec_buf;
static int gInjectRunning = 1;
static AM_CA_StoreRegion_t store_reg[16];
static int32_t seclev = AM_TSPLAYER_DMX_FILTER_SEC_LEVEL2;
extern int g_nosmp;
extern int g_indexer;
#ifdef MEDIASYNC
extern bool CreateVideoTunnelId(int* id);
static int VideoTunnelId = 0;
#endif
#define INJECT_LENGTH (188*1024)
static void *inject_thread(void *arg)
{
int ret;
int fd;
uint8_t *buf = NULL;
uint32_t blksize = 0;
char *tspath = (char *)arg;
const int kRwTimeout = 30000;
uint32_t sec_buf_size = 0;
AM_CA_CryptoPara_t crypto_para;
struct timespec start_ts, end_ts;
am_tsplayer_input_buffer ibuf = {TS_INPUT_BUFFER_TYPE_SECURE, NULL, 0};
if (g_nosmp == 1 || g_indexer == 1) {
ibuf.buf_type = TS_INPUT_BUFFER_TYPE_NORMAL;
}
uint8_t i = 0;
uint8_t reg_cnt = 0;
uint8_t curr_reg_idx = 0;
fd = open(tspath, O_RDONLY);
INF("%s open %s, fd:%d\n", __func__, tspath, fd);
if (fd == -1) {
return NULL;
}
memset(&store_reg[0], 0, sizeof(store_reg));
memset(&crypto_para, 0, sizeof(crypto_para));
crypto_para.offset = 0;
crypto_para.type = CRYPTO_TYPE_DECRYPT;
if (strlen(tspath) > 512) {
INF("%s tspath size is too large: %s, size:%#zx\n", __func__, tspath, strlen(tspath));
close(fd);
return NULL;
}
memcpy(crypto_para.location, tspath, strlen(tspath));
if (AM_CA_DVRReplay(cas_session, &crypto_para)) {
INF("replay failed\n");
close(fd);
return NULL;
}
ret = AM_CA_GetStoreRegion(cas_session, store_reg, &reg_cnt);
if (ret) {
INF("error! must get store region first\n");
}
blksize = crypto_para.buf_in.size;
INF("blksize:%#x\n", blksize);
if (blksize == 0) {
blksize = INJECT_LENGTH;
sec_buf_size = INJECT_LENGTH;
}
buf = malloc(blksize);
if (buf == NULL) {
ERR("malloc error!");
close(fd);
return NULL;
}
if (g_indexer ) {
sec_buf = buf;
sec_buf_size = blksize;
} else if (g_nosmp) {
sec_buf = malloc(blksize *2);
if (sec_buf == NULL) {
ERR("malloc error!");
close(fd);
return NULL;
}
sec_buf_size = blksize *2;
} else {
secmem_session = AM_CA_CreateSecmem(
cas_session,
SERVICE_PVR_PLAY,
&sec_buf,
&sec_buf_size);
if (!secmem_session) {
INF("cas playback failed. secmem_session:%#zx\n", secmem_session);
close(fd);
return NULL;
}
}
crypto_para.buf_out.addr = (size_t)sec_buf;
crypto_para.buf_out.size = sec_buf_size;
crypto_para.buf_in.addr = (size_t)buf;
ibuf.buf_data = sec_buf;
for (i = 0; i < reg_cnt; i++) {
INF("Region[%d] %lld ~ %lld\n",
i,
store_reg[i].start,
store_reg[i].end);
}
while (gInjectRunning) {
int retry = 100;
int kRwSize = 0;
int unaligned_len = 0;
am_tsplayer_result res;
if (curr_reg_idx < reg_cnt - 1) {
if (crypto_para.offset + blksize >= store_reg[curr_reg_idx +1].start) {
unaligned_len = store_reg[curr_reg_idx + 1].start - crypto_para.offset;
curr_reg_idx ++;
}
}
if (unaligned_len > 0) {
kRwSize = read(fd, buf, unaligned_len);
} else {
kRwSize = read(fd, buf, blksize);
}
if (kRwSize <= 0) {
INF("%s read end of file\n", __func__);
while (gInjectRunning) {
sleep(1);
}
break;
}
crypto_para.buf_in.size = kRwSize;
clock_gettime(CLOCK_MONOTONIC, &start_ts);
ret = AM_CA_DVRDecrypt(cas_session, &crypto_para);
if (ret) {
INF("Decrypt failed:%d\n", ret);
continue;
}
clock_gettime(CLOCK_MONOTONIC, &end_ts);
CA_DEBUG(1,"%s AM_CA_DVRDecrypt use (%lu) ms for (0x%x) bytes.\n ", __func__,
(end_ts.tv_sec*1000 + end_ts.tv_nsec/1000000) -
(start_ts.tv_sec*1000 + start_ts.tv_nsec/1000000),
crypto_para.buf_len);
ibuf.buf_size = kRwSize;
//INF("streampos: %lld, curr_reg_idx:%d\n", crypto_para.offset, curr_reg_idx);
do {
res = AmTsPlayer_writeData(tsplayer_handle, &ibuf, kRwTimeout);
if (res == AM_TSPLAYER_ERROR_RETRY) {
usleep(50000);
//INF("tsplayer write retry\n");
} else {
//INF("%#x Bytes injected\n", ibuf.buf_size);
break;
}
} while(retry-- > 0);
crypto_para.offset += kRwSize;
INF("inject length: 0x%x, next offset is 0x 0x%llx\n", kRwSize, crypto_para.offset);
}
free(buf);
if (g_nosmp) {
free(sec_buf);
}
close(fd);
INF("exit %s\n", __func__);
return NULL;
}
static void tsplayer_callback(void *user_data, am_tsplayer_event *event)
{
UNUSED(user_data);
if (event)
{
switch (event->type)
{
case AM_TSPLAYER_EVENT_TYPE_PTS:
{
INF("[evt] AM_TSPLAYER_EVENT_TYPE_PTS: stream_type:%d, pts[%llu]\n",
event->event.pts.stream_type,
event->event.pts.pts);
break;
}
case AM_TSPLAYER_EVENT_TYPE_DTV_SUBTITLE:
{
uint8_t* pbuf = event->event.mpeg_user_data.data;
uint32_t size = event->event.mpeg_user_data.len;
INF("[evt] AM_TSPLAYER_EVENT_TYPE_DTV_SUBTITLE: %x-%x-%x-%x ,size %d\n",
pbuf[0], pbuf[1], pbuf[2], pbuf[3], size);
break;
}
case AM_TSPLAYER_EVENT_TYPE_USERDATA_CC:
{
uint8_t* pbuf = event->event.mpeg_user_data.data;
uint32_t size = event->event.mpeg_user_data.len;
INF("[evt] AM_TSPLAYER_EVENT_TYPE_USERDATA_CC: %x-%x-%x-%x ,size %d\n",
pbuf[0], pbuf[1], pbuf[2], pbuf[3], size);
break;
}
case AM_TSPLAYER_EVENT_TYPE_USERDATA_AFD:
{
uint8_t* pbuf = event->event.mpeg_user_data.data;
uint32_t size = event->event.mpeg_user_data.len;
INF("[evt] AM_TSPLAYER_EVENT_TYPE_USERDATA_AFD: %x-%x-%x-%x ,size %d\n",
pbuf[0], pbuf[1], pbuf[2], pbuf[3], size);
//USERDATA_AFD_t afd = *((USERDATA_AFD_t *)pbuf);
//afd.reserved = afd.pts = 0;
//INF("[evt] video afd changed: flg[0x%x] fmt[0x%x]\n", afd.af_flag, afd.af);
break;
}
case AM_TSPLAYER_EVENT_TYPE_VIDEO_CHANGED:
{
INF("[evt] AM_TSPLAYER_EVENT_TYPE_VIDEO_CHANGED: [width:height] [%d x %d] @%d aspectratio[%d]\n",
event->event.video_format.frame_width,
event->event.video_format.frame_height,
event->event.video_format.frame_rate,
event->event.video_format.frame_aspectratio);
break;
}
case AM_TSPLAYER_EVENT_TYPE_AUDIO_CHANGED:
{
INF("[evt] AM_TSPLAYER_EVENT_TYPE_AUDIO_CHANGED: sample_rate:%d, channels:%d\n",
event->event.audio_format.sample_rate,
event->event.audio_format.channels);
break;
}
case AM_TSPLAYER_EVENT_TYPE_DATA_LOSS:
{
INF("[evt] AM_TSPLAYER_EVENT_TYPE_DATA_LOSS\n");
break;
}
case AM_TSPLAYER_EVENT_TYPE_DATA_RESUME:
{
INF("[evt] AM_TSPLAYER_EVENT_TYPE_DATA_RESUME\n");
break;
}
case AM_TSPLAYER_EVENT_TYPE_SCRAMBLING:
{
INF("[evt] AM_TSPLAYER_EVENT_TYPE_SCRAMBLING: stream_type:%d is_scramling[%d]\n",
event->event.scramling.stream_type,
event->event.scramling.scramling);
break;
}
case AM_TSPLAYER_EVENT_TYPE_FIRST_FRAME:
{
INF("[evt] AM_TSPLAYER_EVENT_TYPE_FIRST_FRAME: ## VIDEO_AVAILABLE ##\n");
break;
}
default:
break;
}
}
}
#define VIDEO_PID 0x44
#define AUDIO_PID 0x45
int ext_dvr_playback(const char *path, CasHandle cas_handle)
{
int vpid = VIDEO_PID, apid = AUDIO_PID;
int vfmt = AV_VIDEO_CODEC_MPEG2, afmt = AV_AUDIO_CODEC_MP3;
int error;
am_tsplayer_video_params vparam;
am_tsplayer_audio_params aparam;
uint32_t versionM = 0, versionL = 0;
uint32_t video_tunnel_id = 0;
am_tsplayer_init_params init_param =
{
.source = TS_MEMORY,
.dmx_dev_id = 0,
.event_mask = 0,
/*AM_TSPLAYER_EVENT_TYPE_PTS_MASK
| AM_TSPLAYER_EVENT_TYPE_DTV_SUBTITLE_MASK
| AM_TSPLAYER_EVENT_TYPE_USERDATA_AFD_MASK
| AM_TSPLAYER_EVENT_TYPE_VIDEO_CHANGED_MASK
| AM_TSPLAYER_EVENT_TYPE_AUDIO_CHANGED_MASK
| AM_TSPLAYER_EVENT_TYPE_DATA_LOSS_MASK
| AM_TSPLAYER_EVENT_TYPE_DATA_RESUME_MASK
| AM_TSPLAYER_EVENT_TYPE_SCRAMBLING_MASK
| AM_TSPLAYER_EVENT_TYPE_FIRST_FRAME_MASK,*/
.drmmode = TS_INPUT_BUFFER_TYPE_SECURE,
};
if (g_indexer) {
init_param.drmmode =TS_INPUT_BUFFER_TYPE_TVP;
} else if (g_nosmp) {
init_param.drmmode =TS_INPUT_BUFFER_TYPE_NORMAL;
}
INF("external vpid:%#x vfmt:%d apid:%#x afmt:%d\n", vpid, vfmt, apid, afmt);
error = AM_CA_OpenSession(
cas_handle,
&cas_session,
SERVICE_PVR_PLAY);
INF("%s open cas session:%#zx, start cas\n", __func__, cas_session);
if (error) {
ERR("CAS open session failed. ret = %d\r\n", error);
return -1;
}
AM_CA_PreParam_t preParam;
preParam.dmx_dev = 0;
preParam.stream_pids[0] = vpid;
preParam.stream_pids[1] = apid;
preParam.stream_num =2;
AM_CA_DVRSetPreParam(cas_session, &preParam);
/*open TsPlayer*/
{
am_tsplayer_result result =
AmTsPlayer_create(init_param, &tsplayer_handle);
INF( "open TsPlayer %s, result(%d)\n", (result)? "FAIL" : "OK", result);
#ifdef MEDIASYNC
INF("Set VideoTunnelId \n");
if (CreateVideoTunnelId(&VideoTunnelId) == true) {
INF("CreateVideoTunnelId 's value: %d \n", VideoTunnelId);
result = AmTsPlayer_setSurface(tsplayer_handle,(void*)&VideoTunnelId);
} else {
INF("CreateVideoTunnelId error.\n");
return -1;
}
#else
result = AmTsPlayer_setSurface(tsplayer_handle, (void *)&video_tunnel_id);
#endif
INF( "TsPlayer set surface %s, result(%d)\n", (result)? "FAIL" : "OK", result);
result = AmTsPlayer_getVersion(&versionM, &versionL);
INF( "TsPlayer version(%d.%d) %s, result(%d)\n",
versionM, versionL,
(result)? "FAIL" : "OK",
result);
if (!g_nosmp) {
AmTsPlayer_setParams(tsplayer_handle, AM_TSPLAYER_KEY_VIDEO_SECLEVEL, &seclev);
AmTsPlayer_setParams(tsplayer_handle, AM_TSPLAYER_KEY_AUDIO_SECLEVEL, &seclev);
}
result = AmTsPlayer_registerCb(tsplayer_handle,
tsplayer_callback,
"tsp0");
INF( " AmTsPlayer_registerCb %s, result(%d)\n", (result)? "FAIL" : "OK", result);
result = AmTsPlayer_setWorkMode(tsplayer_handle, TS_PLAYER_MODE_NORMAL);
INF( " TsPlayer set Workmode NORMAL %s, result(%d)\n", (result)? "FAIL" : "OK", result);
result = AmTsPlayer_setSyncMode(tsplayer_handle, TS_SYNC_VMASTER );
INF( " TsPlayer set Syncmode VMASTER %s, result(%d)\n", (result)? "FAIL" : "OK", result);
// result = AmTsPlayer_setVideoWindow(tsplayer_handle, 0, 0, 1280, 720);
memset(&vparam, 0, sizeof(vparam));
vparam.codectype = vfmt;
vparam.pid = vpid;
result = AmTsPlayer_setVideoParams(tsplayer_handle, &vparam);
INF( " TsPlayer set video params %s, result(%d)\n", (result)? "FAIL" : "OK", result);
result = AmTsPlayer_startVideoDecoding(tsplayer_handle);
INF( " TsPlayer start video decoding %s, result(%d)\n", (result)? "FAIL" : "OK", result);
memset(&aparam, 0, sizeof(aparam));
aparam.codectype = afmt;
aparam.pid = apid;
//aparam.seclevel = 10;
result = AmTsPlayer_setAudioParams(tsplayer_handle, &aparam);
INF( " TsPlayer set audio params %s, result(%d)\n", (result)? "FAIL" : "OK", result);
result = AmTsPlayer_startAudioDecoding(tsplayer_handle);
INF( " TsPlayer start audio decoding %s, result(%d)\n", (result)? "FAIL" : "OK", result);
result = AmTsPlayer_showVideo(tsplayer_handle);
INF( " TsPlayer show video %s, result(%d)\n", (result)? "FAIL" : "OK", result);
result = AmTsPlayer_setTrickMode(tsplayer_handle, AV_VIDEO_TRICK_MODE_NONE);
INF( " TsPlayer show audio decoding %s, result(%d)\n", (result)? "FAIL" : "OK", result);
}
INF( "Starting playback\n");
pthread_create(&gInjectThread, NULL, inject_thread, (void *)path);
return 0;
}
int ext_dvr_playback_stop(void)
{
gInjectRunning = 0;
if (gInjectThread) {
pthread_join(gInjectThread, NULL);
}
if (cas_session) {
if (g_indexer) {
/* do nothing. */
}else if (g_nosmp) {
if (sec_buf) {
free (sec_buf);
}
}
else {
AM_CA_DestroySecmem(cas_session, secmem_session);
}
AM_CA_CloseSession(cas_session);
}
AmTsPlayer_stopAudioDecoding(tsplayer_handle);
AmTsPlayer_stopVideoDecoding(tsplayer_handle);
AmTsPlayer_release(tsplayer_handle);
return 0;
}