#ifndef CA_DEBUG_LEVEL #define CA_DEBUG_LEVEL 2 #endif #include #include #include #include #include #include #include #include #include #include #include #include "AmTsPlayer.h" #include #include #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, ®_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; }