/*************************************************************************** * Copyright (c) 2003-2013, Broadcom Corporation * All Rights Reserved * Confidential Property of Broadcom Corporation * * THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE * AGREEMENT BETWEEN THE USER AND BROADCOM. YOU HAVE NO RIGHT TO USE OR * EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. * * $brcm_Workfile: $ * $brcm_Revision: $ * $brcm_Date: $ * * Module Description: * * Revision History: * * $brcm_Log: $ * ***************************************************************************/ #include "chan_mgr.h" #include "bstd.h" #include "bavc.h" #include "ts_psi.h" #include "ts_pat.h" #include "ts_pmt.h" #include "ts_scte_18.h" #include "bapp.h" #include "psip_ett.h" #include "si.h" #include "psip_rrt.h" #include "bsettop_smessage.h" #include "image_recv.h" #include "ca_parser.h" #include "bos_task_priorities.h" #include "bapp_util.h" #include "ch_map.h" #include "bsettop_tuner.h" /* defined in tsid_to_utc_off.c */ extern int tsid_to_utc_offset(unsigned short tsid, int *p_offset, char *p_dst); /* defined in bscreen_dolby.c */ extern void bapp_user_freq_table_get(bapp_t *p_app, unsigned int *freq_table[], unsigned int *num_freq); #define CHECK_PROGRAM #ifdef CHECK_PROGRAM int chm_check_program(chm_mgr_t *p_chm); #endif #ifndef TS_PSI_DT_ISO639Language #define TS_PSI_DT_ISO639Language 0x0A #endif #ifndef isalpha #define isalpha(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z')) #define isdigit(c) ((c) >= '0' && (c) <= '9') #endif #ifdef FOR_DOLBY_CERTIFICATION #include "bchp_hifidac_ctrl0.h" #include "projinc.h" #endif #include "ram_header.h" BDBG_MODULE(chan_mgr); /* Register software module with debug interface */ #ifdef BCM_DEBUG static const char *s_chm_cmd_name[] = { "eCHM_CANCEL", "eCHM_TUNE", "eCHM_INFO", "eCHM_STOP", "eCHM_CHECK_SIGNAL", "eCHM_HUNT", "eCHM_GUIDE", "eCHM_SAP" }; static const char *s_chm_state[] = { "eCHM_GET_STT", "eCHM_GET_VCT", "eCHM_GET_MGT", "eCHM_PROCESS_MGT", "eCHM_PROG_IDLE" }; #endif #define CURR_CH 0 #define NEXT_CH 1 #define PREV_CH 2 #define CHM_INT_STK_SIZE 1024 /* in words */ #define CHM_CHECK_SIGNAL_FREQ 1000 #define CHM_CHECK_CHMAP_FREQ 5000 #define CHM_CHECK_PROG_FREQ 1000 #define VCT_BUF_LEN (1024 + 188) #define EIT_BUF_LEN (4400 + 188) #define ETT_BUF_LEN (4400 + 188) #define MGT_BUF_LEN (4400 + 188) #define VCT_TIMEOUT 1200 /* current 1200, should be 400 ms */ #define MSC_TIMEOUT 800 #define MGT_TIMEOUT 500 #define VCT_ETT_TIMEOUT 1200 #define ETT_TIMEOUT 1500 #define EIT_TIMEOUT 2000 #define CHM_CHECK_SNR_INTERVAL 500 #define RRT_TIMEOUT 5000 /* in milliseconds */ #define CHM_MUTEX_TIMEOUT 10 #define MAX_TIME_SLOTS 4 #define MGT_CYCLE_TIMEOUT 1 /* in seconds */ #define RRT_CYCLE_TIME (20) /* in seconds */ #define CHM_MIN(x,y) ((x < y) ? x : y) #define PAT_BUF_LEN (1024 + 188) #define CAT_BUF_LEN (1024 + 188) #define PMT_BUF_LEN (1024 + 188) #define STT_BUF_LEN (256 + 188) #define EAS_BUF_LEN (1024 + 188) #define EMM_BUF_LEN (1024 + 188) #define CVT_BUF_LEN (1024 + 188) #define CHM_IDLE_TIMEOUT 30 #define CAT_TIMEOUT 3000 #define PAT_TIMEOUT 800 #define PMT_TIMEOUT 800 #define STT_TIMEOUT 1200 #define EAS_TIMEOUT 1200 #define SCTE_18_ALERT_PRIORITY 0 /* all priority */ #define EAS_INFINITY 157680000 /* About 5 years in seconds, which is "infinity" for EAS purposes */ /* frequency vector is encoded in 0.25MHz increments */ #define CHM_VECTOR_MULTIPLIER (25 * 10000) #ifndef toupper #define toupper(x) (x&0xDF) #endif typedef struct iso_lang_map_t { char iso_lang_code[3]; bapp_lang_t lang; }iso_lang_map_t; typedef enum chm_err_t { eCHM_ERR_OK = 0, eCHM_ERR_FAILED = -1, eCHM_ERR_TIMEOUT = -2, eCHM_ERR_CANCELED = -3, eCHM_ERR_NO_PAT = -4, eCHM_ERR_PARAMS = -5 }chm_err_t; #ifndef ACB612 const char g_tz_map[] = { 0, 5, 6, 7, 8, 0}; #else /* note that now we don't support auto zone for Mexico NTIA platform */ const char g_tz_map[] = { 6, 7, 8, 0 }; #endif const static iso_lang_map_t s_iso_lang_map[] = { { { 'e','n','g'},eLANG_ENGLISH}, #ifndef ACB612 { { 'f','r','a'},eLANG_FRENCH}, { { 'f','r','e'},eLANG_FRENCH}, #endif { { 'e','s','l'},eLANG_SPANISH}, { { 's','p','a'},eLANG_SPANISH}, }; const static int s_iso_lang_map_num = sizeof(s_iso_lang_map)/sizeof(iso_lang_map_t); /* These files are in flash_install_.c and are assumed to be RAM only and * are therefore not access through dispatch table. */ extern void flash_install(uint32_t base, uint32_t offset, uint8_t * data, size_t len); extern void chip_reset(void); static int chm_post_app_event(chm_mgr_t *p_chm, /* Channel manager reference */ chm_event_t *p_event /* Event to post to app queue */ ); static int chm_stop_decode(chm_mgr_t *p_chm); static int chm_tune_freq(chm_mgr_t *p_chm, unsigned int freq_hz); static int chm_rotate_audio(chm_mgr_t *p_chm); static int chm_stt_start(chm_mgr_t *p_chm, unsigned short network_pid); static void chm_stt_stop(chm_mgr_t *p_chm); static int chm_rrt_start(chm_mgr_t *p_chm, unsigned short network_pid); static void chm_rrt_stop(chm_mgr_t *p_chm); static int chm_prog_idle(chm_mgr_t *p_chm); static void chm_post_progress(chm_mgr_t *p_chm, int cnt); bresult chm_write_software(struct image_t * image); static bool chm_check_sc(chm_mgr_t *p_chm, bband_t band, unsigned short pid); static void chm_stop_stream(chm_mgr_t *p_chm, chm_stream_t *p_stream); #ifndef NO_VCHIP static int chm_psip_block(bapp_t *p_app, unsigned char region,unsigned char dim,unsigned char val, char *rating_str); #endif static int chm_get_utctime(chm_mgr_t *p_chm, unsigned int *time); static int chm_get_guide(chm_mgr_t *p_chm); #ifdef ACB612 #if 0 #define FACTORY_CHN_NUMBER 3 unsigned int F_chaSet[FACTORY_CHN_NUMBER] = {2,13,51}; #else #define FACTORY_CHN_NUMBER 2 unsigned int F_chaSet[FACTORY_CHN_NUMBER] = {4,48}; #endif bool isScanStatusFinish = false; #endif /* we should add this function in either ministd.c or include the libc.c which has the function */ char *strchr(const char *s, int c) { int len = strlen(s), i; char *p = (char *)s; for (i = 0; i < len; i++, p++) { if (c == *p) return p; } return NULL; } /* Summary: go through all the dimensions to find out if the given rating from CAD is existed in thre downloadable RRT table or not Returnes: true if existed false if not */ bool is_rating_supported(unsigned char *prrt, unsigned char *p, int size) { PSIP_RRT_header header; PSIP_RRT_dimension dimension; PSIP_RRT_value value; int i, j, k; /*RLQ, TODO we may need to increase the stack size for chm task */ char string[2048]; int stringSize = sizeof(string); PSIP_RRT_getHeader(prrt, &header); for(i=0; PSIP_RRT_getDimension(prrt, i, &dimension)==BERR_SUCCESS; i++) { if (dimension.values_defined) { for(j=0; PSIP_RRT_getValue(prrt, i, j, &value)==BERR_SUCCESS; j++) { //if (value.abbrev_rating_value_text_len > 8) { for(k=0; PSIP_MSS_getString(value.p_abbrev_rating_value_text, k, &stringSize, string)==BERR_SUCCESS; stringSize = sizeof(string), k++ ) { if (stringSize == size && 0 == memcmp(p, string, size)) return true; } //} } } } return false; } /* Summary: find each rating value from given CAD rating and check them against current downloadable RRT to see if they are in the table or no a ponter with rating string that are supported (after filtering out unsupported ones) and the *size will contains the length of the string returend NULL if not */ unsigned char *cad_rating_filter(unsigned char *prrt, unsigned char *rating_str, int *size) { static char buf[64]; char *p; int i = 0, j, valid = 0; if (rating_str) { memset(&buf, 0, sizeof(buf)); p = rating_str; while (*size) { j = 0; /* skip white space if any */ while (0x20 == (*(p + j)) && (j < *size)) { p++; *size -= 1; } /* check letter */ while (isalpha(*(p + j)) && (j < *size)) { j++; } /* check number */ while (isdigit(*(p + j)) && (j < *size)) { j++; } if (is_rating_supported(prrt, p, j)) { for (i = 0; i < j; i++) { buf[valid++] = *p++; } } else { p += j; } *size -= j; } } if (valid) { *size = valid; return buf; } else return NULL; } //#define DBG_DUMP #ifdef DBG_DUMP static void DBG_HEXDUMP(unsigned char* ptr, unsigned long len) { unsigned long i; for (i=0; ip_app; bapp_ch_t *pch; int i; for (i = 0; i < p_app->settings.num_channels; i++) { pch = &p_app->settings.ch[i]; if ((pch->major == major) && (pch->minor == minor) && (pch->source_id == source_id)) return i; } return -1; } /* Summary: check audio pid */ uint16_t chm_check_audio(chm_mgr_t *p_chm, bapp_ch_t *pch) { uint16_t apid, i; apid = pch->audio_pid[pch->cur_audio]; if (0 == apid && pch->num_audio) { for (i = 0; i < MAX_AUDIO_PIDS; i++) { pch->cur_audio++; pch->cur_audio %= MAX_AUDIO_PIDS; apid = pch->audio_pid[pch->cur_audio]; if (apid) break; } } return apid; } /* Summary: Set next channel information. */ bool chm_ch_set_next(chm_mgr_t *p_chm, bapp_ch_t **pch) { bapp_t *p_app = (bapp_t*)p_chm->p_app; int idx = 0; idx = p_app->cur_ch_num + 1; if ((idx % p_app->settings.num_channels) != p_app->cur_ch_num) { *pch = &p_app->settings.ch[idx]; return true; } else *pch = &p_app->settings.ch[p_app->cur_ch_num]; return false; } /* Summary: Set next channel information. */ bool chm_ch_set_prev(chm_mgr_t *p_chm, bapp_ch_t **pch) { bapp_t *p_app = (bapp_t*)p_chm->p_app; int idx = 0; idx = p_app->cur_ch_num - 1; if (idx < 0) { idx = p_app->settings.num_channels - 1; } if (idx != p_app->cur_ch_num) { *pch = &p_app->settings.ch[idx]; return true; } else *pch = &p_app->settings.ch[p_app->cur_ch_num]; return false; } /* Summary: Get section crc. */ static inline unsigned int chm_section_crc(unsigned char *buf,size_t length) { unsigned int crc32; memcpy(&crc32, &(buf[length-4]),4); return crc32; } /* Summary: Clear the current channel info. */ static void chm_make_current(chm_mgr_t *p_chm, chm_info_t *p_info) { /* Clear current info */ if (bos_acquire_mutex(&p_chm->mutex,CHM_MUTEX_TIMEOUT) == b_ok) { memcpy(&p_chm->cur_info,p_info,sizeof(chm_info_t)); bos_release_mutex(&p_chm->mutex); } } /* Summary: Clear the current channel info. */ int chm_clear_current(chm_mgr_t *p_chm) { bapp_t *p_app = (bapp_t*)p_chm->p_app; /* Clear current info */ if (bos_acquire_mutex(&p_chm->mutex,CHM_MUTEX_TIMEOUT) == b_ok) { memset(&p_chm->cur_info,0,sizeof(chm_info_t)); p_app->prog_rating_str[0] = 0; p_app->psip_rating_str[0] = 0; BDBG_WRN(("%s reset rating string @ %d\n",__func__,__LINE__)); p_app->tv_rating = 0; /* do we need to clear STT? */ //p_chm->got_stt = false; bos_release_mutex(&p_chm->mutex); } return 0; } /* Summary: Add a chanell to the channel map. Description: Add the channel to the channel map in sorted order. */ static void chm_add_vsb_channel( chm_mgr_t *p_chm, /* Channel manager reference */ bapp_ch_t *p_ch ) { bapp_t *p_app = (bapp_t*)p_chm->p_app; int idx,idx2; bool found_slot = false; if (p_app->settings.num_channels >= (MAX_CHANNELS - 1)) { BDBG_ERR(("%s: maximum channel allowed %d reached, can't add more", __func__, MAX_CHANNELS)); return; } for (idx = 0; idx < p_app->settings.num_channels; idx++) { if ((p_ch->major < p_app->settings.ch[idx].major) || ((p_ch->major == p_app->settings.ch[idx].major) && (p_ch->minor < p_app->settings.ch[idx].minor))) { #if 0 if ((p_ch->major == p_app->settings.ch[idx].major) && (p_ch->minor < p_app->settings.ch[idx].minor)) { if (p_ch->minor == p_app->settings.ch[idx].minor) { /* found duplicate, drop it */ BDBG_WRN(("Found duplicate channel, drop it (%d.%d)", p_ch->major, p_ch->minor)); return; } } #endif for (idx2 = p_app->settings.num_channels - 1; idx2 >= idx; idx2--) { memcpy(&p_app->settings.ch[idx2 + 1],&p_app->settings.ch[idx2], sizeof(bapp_ch_t)); } memcpy(&p_app->settings.ch[idx],p_ch, sizeof(bapp_ch_t)); found_slot = 1; break; } } if (!found_slot) { memcpy(&p_app->settings.ch[p_app->settings.num_channels],p_ch, sizeof(bapp_ch_t)); } p_app->settings.num_channels++; } /* Summary: Get the current utc time in seconds. Return non-zero on failure. */ static void chm_send_status(chm_mgr_t *p_chm,unsigned int freq, bool locked) { bapp_t *p_app = (bapp_t*)p_chm->p_app; chm_signal_event_t *p_sig = &(p_chm->signal_evt); memset(&(p_chm->signal_evt),0,sizeof(chm_signal_event_t)); p_sig->type = eCHM_EVT_SIGNAL; if (locked) { if (btuner_get_status(p_chm->tuner,&p_app->tuner_status) == b_ok) { /* Send message to app */ p_sig->freq_hz = p_app->tuner_status.freq; p_sig->SNR = p_app->tuner_status.snr; p_sig->power = p_app->tuner_status.power; p_sig->lock = p_app->tuner_status.lock; p_sig->signal_quality = p_app->tuner_status.PreRS; } } chm_post_app_event(p_chm,(chm_event_t*)&(p_chm->signal_evt)); } /* Summary: Check for a valid language code for the current language settings. Returns the bapp_lang_t number or < 0 if not a valid language. */ static int chm_valid_lang(chm_mgr_t *p_chm, /* Channel manager reference */ char *p_code /* Three byte iso 639-2 language code */ ) { bapp_lang_t lang = iso_639_to_lang(p_code); if (eLANG_MAX != lang) BDBG_MSG(("ISO 639-2 language supported %c%c%c\n",p_code[0],p_code[1],p_code[2])); else BDBG_MSG(("ISO 639-2 language not found %c%c%c\n",p_code[0],p_code[1],p_code[2])); return (lang == eLANG_MAX) ? eCHM_ERR_FAILED : (int)lang; } /* Summary: Check for cancel, returns true if a cancel occured. */ #ifdef BCM_DEBUG static int chm_check_cancel_ex(chm_mgr_t *p_chm ,const char* func, int line) #define chm_check_cancel(chm) chm_check_cancel_ex(chm,__func__,__LINE__) #else static int chm_check_cancel(chm_mgr_t *p_chm ) #endif { chm_cmd_event_t *p_cmd; if (p_chm->cmd == eCHM_CANCEL) return 1; p_cmd = (chm_cmd_event_t*)bos_pend_event(p_chm->queue,0); if (!p_cmd) return 0; if (p_cmd->cmd_id == eCHM_CANCEL) { p_chm->cmd = eCHM_CANCEL; BDBG_WRN(("Command canceled(%s:%d)...\n",func,line)); //RLQ, do we need this? #if 0 /* Send message to app */ p_chm->chm_evt.type = eCHM_EVT_CANCEL; p_chm->chm_evt.id = 0; chm_post_app_event(p_chm,&p_chm->chm_evt); #endif } else { if (p_cmd->cmd_id == eCHM_SAP) { chm_rotate_audio(p_chm); } else { BDBG_WRN(("Throwing away command %d(%s:%d)...\n", p_chm->cmd ,func,line)); } } return(p_chm->cmd == eCHM_CANCEL) ? 1 : 0; } static void * chm_smessage_callback(void *context, size_t data_size) { chm_mgr_t *p_chm = (chm_mgr_t *)context; /* ignore return value */ bos_post_event(p_chm->gen_queue, (b_event_t*)data_size); return NULL; } static int chm_getmessage(chm_mgr_t *p_chm, /* Channel manager reference */ void *buf, /* buffer to copy section into */ unsigned int *len, /* [in] max size of buffer, [out] section size copied into buffer */ unsigned int timeout /* timeout in milliseconds */ ) { bresult bres; size_t msg_size; int result = eCHM_ERR_FAILED; smessage_stream_params_t params; smessage_stream_params_init(¶ms, NULL); params.band = p_chm->msg_params.band; params.pid = p_chm->msg_params.pid; params.filter = p_chm->msg_params.filter; params.buffer = buf; params.buffer_size = *len; params.crc_disabled = p_chm->msg_params.crc_disabled; params.data_ready_callback = chm_smessage_callback; params.overflow = NULL; params.callback_context = (void *)p_chm; params.priv = (void *)4; BDBG_MSG(("pid %x fil %x tim %u len %u", params.pid, params.filter.coef[0], timeout, *len)); bres = smessage_start(¶ms, p_chm->smsg); if (b_ok != bres) { BDBG_ERR(("%s:%d",__FILE__, __LINE__)); goto ExitFunc; } msg_size = (size_t) bos_pend_event(p_chm->gen_queue, timeout); if (0 == msg_size) { result = eCHM_ERR_TIMEOUT; } else { result = eCHM_ERR_OK; } bres = smessage_stop(p_chm->smsg); if (b_ok != bres) { BDBG_ERR(("%s:%d",__FILE__, __LINE__)); result = eCHM_ERR_FAILED; goto ExitFunc; } ExitFunc: return result; } /* Summary: Stop EAS monitoring.. */ static void chm_eas_stop(chm_mgr_t *p_chm) { if (NULL != p_chm->eas_msg) { if (b_ok != smessage_stop(p_chm->eas_msg)) { BDBG_ERR(("%s:%d",__FILE__, __LINE__)); return; } smessage_close(p_chm->eas_msg); p_chm->eas_msg = NULL; p_chm->eas_timer_started = false; } } #ifdef CHECK_PROGRAM /* Summary: Get current program PID information from PAT and PMT Description: Get the PAT/PMT for the program Returns: non-zero on failure. */ static int chm_get_program( chm_mgr_t *p_chm, bapp_ch_t *pch) { unsigned int section_size, pmt_section_size; TS_PMT_stream pmt; TS_PAT_program pat; int result = 0; int idx,p_idx,num_prog, i; unsigned int prog_ticks, crc; bsettop_av_stream_type_t *stream_type; prog_ticks = bos_getticks(); smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = p_chm->band; p_chm->msg_params.pid = (unsigned short)0; /* PAT */ section_size = PAT_BUF_LEN; if ((result = chm_getmessage(p_chm,p_chm->pat_buf,§ion_size, PAT_TIMEOUT)) != 0) { BDBG_WRN(("%s Error getting PAT (err=%d)",__func__, result)); return result; } p_chm->pat_cnt++; if (TS_PAT_validate(p_chm->pat_buf, section_size) != true) { BDBG_MSG(("TS_PAT_validate failed\n")); return eCHM_ERR_FAILED; } num_prog = TS_PAT_getNumPrograms(p_chm->pat_buf); if (num_prog == 0) { BDBG_WRN(("%s No program?", __func__)); return eCHM_ERR_FAILED; } /* check to make sure the user doesn't cancel during the PAT/PMT checking process */ if (chm_check_cancel(p_chm)) { return eCHM_ERR_CANCELED; } /* move it to caller function with mutex protection of program num to avoid possible race condition */ //memset(pch, 0, sizeof(bapp_ch_t)); /* remember PAT CRC for later comparison for changes */ crc = chm_section_crc(p_chm->pat_buf,section_size); if (crc == p_chm->pat_crc) { /* PAT doesn't change, simply return */ return eCHM_ERR_OK; } p_chm->pat_crc = crc; BDBG_MSG(("TS_PAT_getNumPrograms %d", num_prog)); for (idx = 0; idx < num_prog; idx++) { if (TS_PAT_getProgram( p_chm->pat_buf, section_size, idx, &pat ) != b_ok) { BDBG_WRN(("TS_PAT_validate failed 0x%04x\n",idx)); p_chm->pat_crc = 0; /* invalide it */ return eCHM_ERR_FAILED; } BDBG_MSG(("TS_PAT_getProgram(%d,0x%04x)\n", pat.program_number,pat.PID)); /* Check for new network PID */ if (pat.program_number == 0) { /* we don't use network PID */ continue; } if (pch->program_num != pat.program_number) continue; smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = p_chm->band; p_chm->msg_params.pid = (unsigned short)pat.PID; /* PMT */ p_chm->msg_params.filter.coef[0] = 0x2; p_chm->msg_params.filter.mask[0] = 0x0; p_chm->msg_params.filter.coef[3] = pch->program_num >> 8; p_chm->msg_params.filter.mask[3] = 0x0; p_chm->msg_params.filter.coef[4] = pch->program_num & 0xff; p_chm->msg_params.filter.mask[4] = 0x0; pmt_section_size = PMT_BUF_LEN; if ((result = chm_getmessage(p_chm,p_chm->pmt_buf,&pmt_section_size, PMT_TIMEOUT)) != 0) { BDBG_ERR(("%s Error getting PMT 0x%04x = %d",__func__,pch->program_num, result)); return result; } p_chm->pmt_cnt++; crc = chm_section_crc(p_chm->pmt_buf,pmt_section_size); if (crc == p_chm->pmt_crc) { /* PMT doesn't change, simply return */ return eCHM_ERR_OK; } p_chm->pmt_crc = crc; pch->pcr_pid = TS_PMT_getPcrPid(p_chm->pmt_buf,pmt_section_size); for (p_idx = 0; p_idx < TS_PMT_getNumStreams(p_chm->pmt_buf,pmt_section_size); p_idx++) { TS_PSI_descriptor psi_desc,lang_desc; int strm_idx; int num_aud, aidx; if (TS_PMT_getStream( p_chm->pmt_buf, pmt_section_size, p_idx, &pmt ) != b_ok) { BDBG_WRN(("Failure processing PMT %d",p_idx)); return eCHM_ERR_FAILED; } strm_idx = 0; lang_desc = NULL; /* remember how many audio channels from VCT */ num_aud = pch->num_audio; while ((psi_desc = TS_PMT_getStreamDescriptor(p_chm->pmt_buf,pmt_section_size, p_idx,strm_idx)) != NULL) { if (psi_desc[0] == TS_PSI_DT_ISO_639_Language) /* 0x0A ISO-639 Language descriptor */ { BDBG_MSG(("ISO-639 DESC\n" )); lang_desc = psi_desc; break; } strm_idx++; if (strm_idx > 0xFF) { BDBG_ERR(("PMT Stream Desc limit exceeded %d\n",strm_idx)); break; } if (chm_check_cancel(p_chm)) { return eCHM_ERR_CANCELED; } } if ((stream_type = bdecode_supported_video(pmt.stream_type)) != NULL) { BDBG_MSG(("Video PID[%d] = 0x%04x\n",p_idx,pmt.elementary_PID )); if (!pch->video_pid || (pch->video_pid != pmt.elementary_PID)) { pch->video_pid = pmt.elementary_PID; pch->video_type = stream_type->codec_id; } } else if ((stream_type = bdecode_supported_audio(pmt.stream_type)) != NULL) { BDBG_MSG(("Audio PID[%d] = 0x%04x\n",p_idx,pmt.elementary_PID )); if (lang_desc) { /* MSS format, so point to right offset */ aidx = chm_valid_lang(p_chm, (char *)&lang_desc[2]); if ((aidx >= 0) && (aidx < MAX_AUDIO_PIDS)) { /* will audio PID changed in the PMT other than missing? */ /* for now just check missing one */ if (!pch->audio_pid[aidx]) { pch->has_lang |= 1 << aidx; pch->audio_pid[aidx] = pmt.elementary_PID; pch->audio_type[aidx] = stream_type->codec_id; /* initialize audio selection if match */ #ifdef USE_LANGUAGE if (aidx == p_app->settings.language) pch->cur_audio = aidx; #else /* assume that first audio PID always has audio */ if (0 == pch->num_audio) pch->cur_audio = aidx; #endif pch->num_audio++; memcpy(&pch->audio_lang[aidx][0],(char *)&lang_desc[2], 3); } else { /* handle duplicate case which should be pretty common in Mexico broadcast */ num_aud = aidx; i = aidx; do { i++; i %= MAX_AUDIO_PIDS; if (0 == pch->audio_pid[i]) { pch->has_lang |= 1 << i; pch->audio_pid[i] = pmt.elementary_PID; pch->audio_type[i] = stream_type->codec_id; pch->num_audio++; memcpy(&pch->audio_lang[i][0],(char *)&lang_desc[2], 3); break; } } while (i != num_aud); } } else { /* drop audio? */ BDBG_WRN(("Audio PID[%d] = 0x%04x dropped due to language not supported", aidx, pmt.elementary_PID )); } } else { /* no language descriptor, just put them in the order in PMT table */ if (num_aud < MAX_AUDIO_PIDS) { i = num_aud; do { if (!pch->audio_pid[i]) { pch->has_lang |= 1 << i; pch->audio_pid[i] = pmt.elementary_PID; pch->audio_type[i] = stream_type->codec_id; if (0 == pch->num_audio) pch->cur_audio = i; pch->num_audio++; break; } i++; i %= MAX_AUDIO_PIDS; } while (i != num_aud); } } } else { BDBG_MSG(("Unsupported stream type[0x%02x]\n", pmt.stream_type)); } } /* we should find the match program */ break; } /* final check to make sure the user doesn't cancel during the PAT/PMT checking process */ if (chm_check_cancel(p_chm)) { return eCHM_ERR_CANCELED; } prog_ticks = bos_getticks() - prog_ticks; p_chm->status_evt.type = eCHM_EVT_STATUS; p_chm->status_evt.id = eCHM_STATUS_SI_MS; p_chm->status_evt.ticks = TICKS_TO_MS(prog_ticks); chm_post_app_event(p_chm,&p_chm->status_evt); /* one more check */ if (0 == pch->video_pid && 0 == pch->pcr_pid && 0 == pch->audio_pid[0]) result = eCHM_ERR_FAILED; return result; } #endif /* Summary: Get the program info for a given program number. Description: Get the PAT/PMT for the program. Returns non-zero on failure. */ static int chm_get_program_info(chm_mgr_t *p_chm, /* Channel manager reference */ bband_t band, unsigned short prog_number, bapp_ch_t *p_ch ) { unsigned int section_size, pmt_section_size; TS_PMT_stream pmt; TS_PAT_program pat; int result = 0; int idx,p_idx; unsigned short audio_pid[MAX_AUDIO_PIDS]; unsigned short audio_type[MAX_AUDIO_PIDS]; int num_aud = 0, num_aud_tmp = 0; smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = band; p_chm->msg_params.pid = (unsigned short)0; /* PAT */ /* All PSIP tables have protocol_version which must be = 0 */ //p_chm->msg_params.filter.coef[8] = 0x00; /* protocol_version */ //p_chm->msg_params.filter.mask[8] = 0x00; section_size = PAT_BUF_LEN; if ((result = chm_getmessage(p_chm,p_chm->pat_buf,§ion_size, PAT_TIMEOUT)) != 0) { BDBG_WRN(("Error getting PAT 0x%04x = %d\n",prog_number, result)); return result; } if (TS_PAT_validate(p_chm->pat_buf, section_size) != true) { BDBG_WRN(("TS_PAT_validate failed 0x%04x\n",prog_number)); return eCHM_ERR_FAILED; } for (idx = 0; idx < TS_PAT_getNumPrograms(p_chm->pat_buf); idx++) { if (TS_PAT_getProgram( p_chm->pat_buf, section_size, idx, &pat ) != b_ok) { BDBG_WRN(("TS_PAT_validate failed 0x%04x\n",prog_number)); return eCHM_ERR_FAILED; } /* network, skip it */ if (pat.program_number != prog_number) continue; smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = band; p_chm->msg_params.pid = (unsigned short)pat.PID; /* PMT */ p_chm->msg_params.filter.coef[0] = 0x2; p_chm->msg_params.filter.mask[0] = 0x00; //p_chm->msg_params.filter.excl[0] = 0xff; /* All PSIP tables have protocol_version which must be = 0 */ //p_chm->msg_params.filter.coef[8] = 0x00; /* protocol_version */ //p_chm->msg_params.filter.mask[8] = 0x00; pmt_section_size = PMT_BUF_LEN; if ((result = chm_getmessage(p_chm,p_chm->pmt_buf,&pmt_section_size, PMT_TIMEOUT)) != 0) { BDBG_ERR(("Error getting PAT 0x%04x\n",prog_number)); return result; } p_ch->pcr_pid = TS_PMT_getPcrPid(p_chm->pmt_buf,pmt_section_size); for (p_idx = 0; p_idx < TS_PMT_getNumStreams(p_chm->pmt_buf,pmt_section_size); p_idx++) { TS_PSI_descriptor psi_desc,lang_desc; bsettop_av_stream_type_t *stream_type; int strm_idx, aidx = 0; if (TS_PMT_getStream( p_chm->pmt_buf, pmt_section_size, p_idx, &pmt ) != b_ok) { BDBG_WRN(("Failure processing PMT %d\n",p_idx)); return eCHM_ERR_FAILED; } strm_idx = 0; lang_desc = NULL; num_aud = 0; num_aud_tmp = 0; p_ch->has_lang = 0; while ((psi_desc = TS_PMT_getStreamDescriptor(p_chm->pmt_buf,pmt_section_size, p_idx,strm_idx)) != NULL) { if (psi_desc[0] == TS_PSI_DT_ISO639Language) /* 0x0A ISO-639 Language descriptor */ { BDBG_WRN(("ISO-639 DESC\n" )); lang_desc = psi_desc; break; } strm_idx++; if (strm_idx > 0xFF) { BDBG_ERR(("PMT Stream Desc limit exceeded %d\n",strm_idx)); break; } if (chm_check_cancel(p_chm)) { return eCHM_ERR_CANCELED; } } if ((stream_type = bdecode_supported_video(pmt.stream_type)) != NULL) { BDBG_WRN(("Video PID[%d] = 0x%04x\n",p_idx,pmt.elementary_PID )); if (!p_ch->video_pid) { p_ch->video_pid = pmt.elementary_PID; p_ch->video_type = stream_type->codec_id; } } else if ((stream_type = bdecode_supported_audio(pmt.stream_type)) != NULL) { BDBG_MSG(("Audio PID[%d] = 0x%04x\n",p_idx,pmt.elementary_PID )); if (lang_desc) { aidx = chm_valid_lang(p_chm, (char *)&lang_desc[2]); if ((aidx >= 0) && (aidx < MAX_AUDIO_PIDS)) { if (!p_ch->audio_pid[aidx]) { p_ch->has_lang |= 1 < aidx; p_ch->audio_pid[aidx] = pmt.elementary_PID; p_ch->audio_type[aidx] = stream_type->codec_id; #ifdef USE_LANGUAGE if (aidx == p_app->settings.language) pch->cur_audio = aidx; #else /* assume that first audio PID always has audio */ if (0 == num_aud) p_ch->cur_audio = aidx; #endif num_aud++; } } else { /* remember it first */ if ((num_aud_tmp < MAX_AUDIO_PIDS) && !audio_pid[num_aud_tmp]) { audio_pid[num_aud_tmp] = pmt.elementary_PID; audio_type[num_aud_tmp] = stream_type->codec_id; num_aud_tmp++; } } } else { if ((num_aud < MAX_AUDIO_PIDS) && !p_ch->audio_pid[num_aud]) { p_ch->audio_pid[num_aud] = pmt.elementary_PID; p_ch->audio_type[num_aud] = pmt.stream_type; if (0 == p_ch->num_audio) p_ch->cur_audio = aidx; num_aud++; } } } } if (0 == num_aud && num_aud_tmp) { for (idx = 0; idx < num_aud_tmp; idx++) { p_ch->has_lang |= 1 < idx; p_ch->audio_pid[idx] = audio_pid[idx]; p_ch->audio_type[idx] = audio_type[idx]; } p_ch->num_audio = num_aud_tmp; p_ch->cur_audio = 0; } break; } return result; } /* Summary: Scan for channels using VCT for given time, and if no VCT, then will using PSI scan instead Description: Scan for channels using VCT. Returns: number of channels added. */ static int chm_scan_vct( chm_mgr_t *p_chm, /* Channel manager reference */ bband_t band, int freq_idx) { unsigned int section_size; int minor_idx, channels; bool got_vct; unsigned char section_number; int result = 0; /* we just track 3 audio channels currently */ bapp_ch_t ch; bapp_t *p_app = (bapp_t*)p_chm->p_app; PSIP_VCT_channel vct; unsigned int timeout; bool have_program; timeout = bos_getticks() + MS_TO_TICKS(15 * VCT_TIMEOUT); memset(&vct,0,sizeof(vct)); /* Get the VCT */ section_number = 0; do { smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = band; p_chm->msg_params.pid = 0x1ffb; /* VCT */ p_chm->msg_params.filter.coef[0] = 0xc8; /* Table ID for terrestrial */ p_chm->msg_params.filter.mask[0] = 0x00; p_chm->msg_params.filter.coef[6] = section_number; /* section number */ p_chm->msg_params.filter.mask[6] = 0x00; /* All PSIP tables have protocol_version which must be = 0 */ p_chm->msg_params.filter.coef[8] = 0x00; /* protocol_version */ p_chm->msg_params.filter.mask[8] = 0x00; got_vct = false; memset(&p_chm->prev_vct, 0, sizeof(p_chm->prev_vct)); do { section_size = VCT_BUF_LEN; if (chm_getmessage(p_chm,p_chm->vct_buf,§ion_size, VCT_TIMEOUT) != 0) { /* timeout, clear section size in case there is no vct in the stream at all */ p_chm->vct_buf[7] = 0; BDBG_WRN(("Timeout waiting for VCT, section_number=%d", section_number)); if (!p_chm->not_check_cancel && chm_check_cancel(p_chm)) { return eCHM_ERR_CANCELED; } if (timeout > bos_getticks()) { p_chm->msg_params.crc_disabled = true; continue; } else { break; } } channels = PSIP_VCT_getNumChannels(p_chm->vct_buf); if (0 == channels) { BDBG_WRN(("No VCT Found!")); break; } BDBG_MSG(("PSIP_VCT_getNumChannels %d fount!", channels)); for (minor_idx = 0; minor_idx < channels; ++minor_idx) { int desc_idx; TS_PSI_descriptor psi_desc; have_program = false; bsettop_av_stream_type_t *stream_type; if (PSIP_VCT_getChannel( (const uint8_t *)p_chm->vct_buf, minor_idx, &vct ) == BERR_SUCCESS) { /* if hidden or analog, skip it */ if ((vct.hidden) || (vct.minor_channel_number == 0) || (vct.service_type!=2 && vct.service_type!=3)) { /* RLQ, TODO, we can set hidden in ch stucture instead of skip it */ BDBG_MSG(("Channel hidden or analog channel %d, %d",vct.hidden,vct.minor_channel_number)); /* got vct, but hidden or analog, need to skip do loop if reach end of VCT */ if ((minor_idx + 1) >= channels) { got_vct = true; break; } else continue; } /* note the vct can't duplicate in same station, but can if it is different station with different frequency */ if (0 == memcmp((void *)&vct, (void *)&p_chm->prev_vct, sizeof(vct))){ BDBG_WRN(("Duplicate VCT entry (%d.%d), discard",vct.major_channel_number, vct.minor_channel_number)); continue; } /* remember it for next comparison */ p_chm->prev_vct = vct; got_vct = true; memset(&ch,0,sizeof(ch)); ch.freq_idx = freq_idx; /* remember the frequency to use it directly instead using the frequency index */ ch.major = vct.major_channel_number; ch.minor = vct.minor_channel_number; ch.psi = false; ch.source_id = vct.source_id; memcpy(ch.ch_name, vct.short_name, sizeof(ch.ch_name)); /* need to remember program number so that we can check the program number for PAT/PMT checking */ ch.program_num = vct.program_number; desc_idx = 0; while ((psi_desc = PSIP_VCT_getChannelDescriptor(p_chm->vct_buf, minor_idx,desc_idx)) != NULL) { if (psi_desc[0] == 0xA1) /* Service location descriptor */ { PSIP_SLD_header sld_header; PSIP_SLD_element sld_elem; int sld_idx,lidx; PSIP_SLD_getHeader(psi_desc,&sld_header); have_program = true; ch.pcr_pid = sld_header.PCR_PID; ch.has_lang = 0; ch.num_audio = 0; BDBG_MSG(("SLD number_elements = %d\n",sld_header.number_elements )); for (sld_idx = 0; sld_idx < sld_header.number_elements; ++sld_idx) { if (PSIP_SLD_getElement(psi_desc,sld_idx,&sld_elem) != BERR_SUCCESS) { BDBG_WRN(("Error processing SLD Desc %d of %d",sld_idx,sld_header.number_elements)); break; } if ((stream_type = bdecode_supported_video(sld_elem.stream_type)) != NULL) { BDBG_WRN(("Video PID[%d] = 0x%04x\n",sld_idx,sld_elem.elementary_PID )); if (!ch.video_pid) { ch.video_pid = sld_elem.elementary_PID; ch.video_type = stream_type->codec_id; } } else { if ((stream_type = bdecode_supported_audio(sld_elem.stream_type)) != NULL) { BDBG_MSG(("Audio PID[%d] = 0x%04x, lang => %c%c%c\n",sld_idx,sld_elem.elementary_PID, sld_elem.ISO_639_language_code[0],sld_elem.ISO_639_language_code[1],sld_elem.ISO_639_language_code[2] )); if ((lidx = chm_valid_lang(p_chm,sld_elem.ISO_639_language_code)) >= 0 ) { if ((lidx >= 0) && (lidx < MAX_AUDIO_PIDS) && (ch.num_audio < MAX_AUDIO_PIDS)) { if (ch.audio_pid[lidx] != 0) { int i = lidx, j = lidx; BDBG_WRN(("Duplicate language Audio PID[%d] = 0x%04x, lang => %c%c%c\n",sld_idx,sld_elem.elementary_PID, sld_elem.ISO_639_language_code[0],sld_elem.ISO_639_language_code[1],sld_elem.ISO_639_language_code[2] )); /* we need to handle it as it happens often in Mexico broadcast */ do { i++; i %= MAX_AUDIO_PIDS; if (0 == ch.audio_pid[i]) { ch.audio_pid[i] = sld_elem.elementary_PID; ch.audio_type[i] = stream_type->codec_id; ch.has_lang |= 1 << i; if (0 == ch.num_audio) ch.cur_audio = i; ch.num_audio++; ch.audio_lang[i][0] = toupper(sld_elem.ISO_639_language_code[0]); ch.audio_lang[i][1] = toupper(sld_elem.ISO_639_language_code[1]); ch.audio_lang[i][2] = toupper(sld_elem.ISO_639_language_code[2]); break; } } while (i != j); } else { ch.audio_pid[lidx] = sld_elem.elementary_PID; ch.audio_type[lidx] = stream_type->codec_id; ch.has_lang |= 1 << lidx; #ifdef USE_LANGUAGE /* initialize audio selection if match in scan time */ if (lidx == p_app->settings.language) ch.cur_audio = lidx; #else /* assume that first audio PID always has audio */ if (0 == ch.num_audio) ch.cur_audio = lidx; #endif ch.num_audio++; } } } else { BDBG_WRN(("Throwing out Audio PID[%d] = 0x%04x, lang => %c%c%c\n",sld_idx,sld_elem.elementary_PID, sld_elem.ISO_639_language_code[0],sld_elem.ISO_639_language_code[1],sld_elem.ISO_639_language_code[2] )); } } } } /* Backup plan if there is no valid language code */ if (0 == ch.num_audio) { BDBG_WRN(("No valid audio PID found for languages in audio descs...\n")); for (sld_idx = 0; sld_idx < sld_header.number_elements && (ch.num_audio < MAX_AUDIO_PIDS); ++sld_idx) { if (PSIP_SLD_getElement(psi_desc,sld_idx,&sld_elem) != BERR_SUCCESS) { BDBG_WRN(("Error processing SLD Desc %d of %d",sld_idx,sld_header.number_elements)); break; } if ((stream_type = bdecode_supported_audio(sld_elem.stream_type)) != NULL) { if (ch.num_audio < MAX_AUDIO_PIDS) { BDBG_WRN(("Audio PID[%d] = 0x%04x\n",sld_idx,sld_elem.elementary_PID)); ch.audio_pid[ch.num_audio] = sld_elem.elementary_PID; ch.audio_type[ch.num_audio] = stream_type->codec_id; if (0 == ch.num_audio) ch.cur_audio = 0; ch.num_audio++; } } } } } desc_idx++; if (desc_idx > 0xFF) { BDBG_ERR(("VCT Channel Desc limit exceeded %d\n",desc_idx)); break; } if (!p_chm->not_check_cancel && chm_check_cancel(p_chm)) { return eCHM_ERR_CANCELED; } } if (!have_program) { BDBG_WRN(("Don't have program, look for PAT/PMT instead ")); if (chm_get_program_info(p_chm,band,vct.program_number,&ch) == 0) { if ((0 == ch.video_pid) && (0 == ch.num_audio)) { /* data program, skip it */ } else { BDBG_MSG(("program video 0x%04x, audio 0x%04x, PCR 0x%04x",ch.video_pid,ch.audio_pid,ch.pcr_pid)); have_program = true; } } } if (p_chm->local_time_source == eTS_NONE) { p_chm->local_time_source = eTS_FIXED; p_chm->local_offset = 0 - (int)g_tz_map[p_app->settings.time_zone]; p_chm->local_offset *= 3600; p_chm->local_dst_obs = p_app->settings.dst; BDBG_WRN(("### Local time %d Fixed ###\n",p_chm->local_offset)); } if (have_program) { ch.frequency_khz = p_chm->freq_table[p_chm->select_idx]; chm_add_vsb_channel(p_chm,&ch); result += 1; p_chm->chm_pat_evt.type = eCHM_EVT_STATUS; p_chm->chm_pat_evt.id = eCHM_STATUS_PAT; p_chm->chm_pat_evt.ticks = (unsigned int)(((p_chm->select_idx)*100)/p_chm->num_freq); chm_post_app_event(p_chm,&p_chm->chm_pat_evt); if (p_app->screen_id == eSCREEN_CH_SCAN_PROGRESS) { //bos_sleep(100); bos_sleep(50); } } } else { if ((0 == ch.video_pid) && (0 == ch.num_audio)) { BDBG_WRN(("Skip data program %d.%d",ch.major, ch.minor)); } else BDBG_WRN(("PSIP_VCT_getChannel %d failed",minor_idx)); } } if (p_chm->not_check_cancel) break; } while ((section_size > 0) && !got_vct); if (!p_chm->not_check_cancel && chm_check_cancel(p_chm)) { return eCHM_ERR_CANCELED; } section_number++; #if 1 //LAST_SECTION_BUG_FIX if (section_number <= p_chm->vct_buf[7]) #else if (section_number < p_chm->vct_buf[7]) #endif timeout = bos_getticks() + MS_TO_TICKS(15 * VCT_TIMEOUT); } #if 1 //LAST_SECTION_BUG_FIX while (section_number <= p_chm->vct_buf[7]); // hschang_130613 should also include last section #else //while ((section_number < p_chm->vct_buf[7]) && (timeout < bos_getticks())); while (section_number < p_chm->vct_buf[7]); #endif return((result > 0) || (timeout < bos_getticks())) ? result : eCHM_ERR_TIMEOUT; } /* Summary: Scan for channels using PSI for given frequency index Description: Scan for channels using PSI. Returns: Number of channels added Or error code if < 0 */ static int chm_scan_psi( chm_mgr_t *p_chm, /* Channel manager reference */ bband_t band, int freq_idx) { unsigned int section_size, pmt_section_size; TS_PMT_stream pmt; TS_PAT_program pat; int result = 0; int idx,p_idx,tresult, desc_idx = 0, programs; TS_PSI_descriptor psi_desc; unsigned short ca_pid = 0, minor = 1; bapp_ch_t ch; /* Get the PAT */ smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = band; p_chm->msg_params.pid = (unsigned short)0; /* PAT */ /* All PSIP tables have protocol_version which must be = 0 */ //p_chm->msg_params.filter.coef[8] = 0x00; /* protocol_version */ //p_chm->msg_params.filter.mask[8] = 0x00; section_size = PAT_BUF_LEN; if ((result = chm_getmessage(p_chm,p_chm->pat_buf,§ion_size, PAT_TIMEOUT)) != 0) { BDBG_WRN(("%s Timeout getting PAT\n",__func__)); return result; } if (TS_PAT_validate(p_chm->pat_buf, section_size) != true) { BDBG_WRN(("TS_PAT_validate failed\n")); return eCHM_ERR_FAILED; } programs = TS_PAT_getNumPrograms(p_chm->pat_buf); BDBG_MSG(("Programs %d in PAT", programs)); if (0 == programs) { BDBG_WRN(("%s TS_PAT_getNumPrograms = 0,section_size = %d\n",__func__,section_size)); return eCHM_ERR_FAILED; } p_chm->pat_crc = chm_section_crc(p_chm->pat_buf,section_size); for (idx = 0; idx < programs; idx++) { if (chm_check_cancel(p_chm)) { return eCHM_ERR_CANCELED; } section_size = PAT_BUF_LEN; if ((tresult = TS_PAT_getProgram( p_chm->pat_buf, section_size, idx, &pat )) != b_ok) { BDBG_WRN(("TS_PAT_getProgram failed 0x%x", tresult)); return eCHM_ERR_FAILED; } smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = band; p_chm->msg_params.pid = (unsigned short)pat.PID; /* PMT */ /*RLQ*/ p_chm->msg_params.filter.coef[0] = 0x2; p_chm->msg_params.filter.mask[0] = 0x00; pmt_section_size = PMT_BUF_LEN; if ((tresult = chm_getmessage(p_chm,p_chm->pmt_buf,&pmt_section_size, PMT_TIMEOUT)) != 0) { BDBG_WRN(("Failed getting PMT 0x%04x\n",pat.PID)); continue; } p_chm->pmt_crc = chm_section_crc(p_chm->pmt_buf,pmt_section_size); memset(&ch,0,sizeof(ch)); ch.major = (freq_idx == 0x7F) ? 0 : freq_idx + 2; /* freq_idx is off by 2 */ ch.minor = minor++; ch.freq_idx = freq_idx; ch.pcr_pid = TS_PMT_getPcrPid(p_chm->pmt_buf,pmt_section_size); ch.program_num = pat.program_number; ch.psi = true; /* we got this entry using PSI scan vs VCT */ /* Get CA pid if */ while ((psi_desc = TS_PMT_getDescriptor(p_chm->pmt_buf, pmt_section_size,desc_idx++)) != NULL) { if (desc_idx > 0xFF) { BDBG_ERR(("PMT Desc limit exceeded %d\n",desc_idx)); break; } if (chm_check_cancel(p_chm)) { return eCHM_ERR_CANCELED; } if (psi_desc[0] == 9) { ca_pid = (((unsigned short)(psi_desc[4] & 0x1F)) << 8) | (unsigned short)psi_desc[5]; BDBG_MSG(("CA PID = 0x%04x\n",ca_pid)); break; } } if (ca_pid != 0x0000) { BDBG_MSG(("Program has CA PID 0x%02x check for scrambling control bit.\n",ca_pid)); } BDBG_MSG(("Streams = %d\n",TS_PMT_getNumStreams(p_chm->pmt_buf,pmt_section_size))); for (p_idx = 0; p_idx < TS_PMT_getNumStreams(p_chm->pmt_buf,pmt_section_size); p_idx++) { bsettop_av_stream_type_t *stream_type; if (TS_PMT_getStream( p_chm->pmt_buf, pmt_section_size, p_idx, &pmt ) != b_ok) { BDBG_WRN(("Failure processing PMT %d\n",p_idx)); return eCHM_ERR_FAILED; } if ((stream_type = bdecode_supported_video(pmt.stream_type)) != NULL) { BDBG_MSG(("Video PID[%d] = 0x%04x\n",p_idx,pmt.elementary_PID )); if (!ch.video_pid) { ch.video_pid = pmt.elementary_PID; ch.video_type = stream_type->codec_id; } } else if ((stream_type = bdecode_supported_audio(pmt.stream_type)) != NULL) { BDBG_MSG(("Audio PID[%d] = 0x%04x\n",ch.num_audio,pmt.elementary_PID )); if (!ch.audio_pid[ch.num_audio] && (ch.num_audio < MAX_AUDIO_PIDS)) { ch.audio_pid[ch.num_audio] = pmt.elementary_PID; ch.audio_type[ch.num_audio] = stream_type->codec_id; if (0 == ch.num_audio) ch.cur_audio = 0; ch.num_audio++; } } } if (ch.video_pid || ch.audio_pid[0]) { bool clear = true; unsigned short pid; /* if has video, use video first */ if (ch.video_pid) pid = ch.video_pid; else pid = ch.audio_pid[0]; /* correct pid in case video pid carries too few frames */ if(ch.pcr_pid == ch.audio_pid[0]) pid = ch.audio_pid[0]; if (ca_pid != 0x0000) clear = chm_check_sc(p_chm, band, pid); if (clear) { bapp_t *p_app = (bapp_t *)p_chm->p_app; p_chm->chm_pat_evt.type = eCHM_EVT_STATUS; p_chm->chm_pat_evt.id = eCHM_STATUS_PAT; p_chm->chm_pat_evt.ticks = (unsigned int)(((p_chm->select_idx)*100)/p_chm->num_freq); chm_post_app_event(p_chm,&p_chm->chm_pat_evt); ch.frequency_khz = p_chm->freq_table[p_chm->select_idx]; chm_add_vsb_channel(p_chm,&ch); result += 1; /* in the chm_scan_psi, it will post STATUS_PAT. but sometimes the event is missed.. don't know why.. */ if (p_app->screen_id == eSCREEN_CH_SCAN_PROGRESS) { bos_sleep(100); } } } } return result; } /* Summary: Process the EIT event descriptors and return > 0 if a change was made to the info structure, 0 if no changes are required and < 0 if an error occured. */ static int chm_process_eit_desc( chm_mgr_t *p_chm, /* Channel manager reference */ unsigned char *msg_buf, /* section buffer containing the EIT table */ int eit_idx, /* EIT event index */ chm_eit_t *p_eit ) { TS_PSI_descriptor eit_desc; int eit_desc_idx,result = 0; bapp_t *p_app = (bapp_t*)p_chm->p_app; char * rating_str; /* Process EIT entry descriptors */ eit_desc_idx = 0; memset(p_eit->cc_service,0,MAX_CC_SERVICES * sizeof(PSIP_CSD_service)); while ((eit_desc = PSIP_EIT_getEventDescriptor(p_chm->eit_buf,eit_idx,eit_desc_idx++)) != NULL) { switch (eit_desc[0]) { case 0x87: /* Content advisory descriptor */ { int rr_idx,rd_idx,psip_str_len; PSIP_CAD_rating_region rr; PSIP_CAD_rating_dimension rd; static chm_event_t chm_evt; PSIP_RRT_header rrt_header; int blocked = 0; if (p_app->settings.rrt_settings.rrt_status == eRRT_AVAILABLE) { PSIP_RRT_getHeader(p_app->settings.rrt_settings.rrt, &rrt_header); } else { rrt_header.rating_region = 1; } p_app->psip_rating_str[0] = 0; for (rr_idx = 0; rr_idx < PSIP_CAD_getRatingRegionCount(eit_desc); ++rr_idx) { if (PSIP_CAD_getRatingRegion(eit_desc,rr_idx,&rr) != BERR_SUCCESS) continue; if ((rr.rating_region != 1) && (rr.rating_region != rrt_header.rating_region)) { BDBG_WRN(("CAD skip %d of %d, rating_region = %d, current = %d\n", rr_idx,PSIP_CAD_getRatingRegionCount(eit_desc),rr.rating_region,rrt_header.rating_region)); continue; } if (rr_idx > 0) { if (strlen(p_app->psip_rating_str) + 4 < MAX_RATING_STR_CHARS) { psip_str_len = strlen(p_app->psip_rating_str); p_app->psip_rating_str[psip_str_len] = ','; p_app->psip_rating_str[psip_str_len + 1] = ' '; p_app->psip_rating_str[psip_str_len + 2] = 0; } } psip_str_len = strlen(p_app->psip_rating_str); if (chm_process_mss(p_chm,rr.p_rating_description_text,&(p_app->psip_rating_str[psip_str_len]), MAX_RATING_STR_CHARS - psip_str_len) == 0) { BDBG_MSG(("CAD[%d,%d] %s %d\n",rr_idx,rr.rating_region,p_app->psip_rating_str,rr.rated_dimensions)); if (p_app->settings.rrt_settings.rrt_status == eRRT_AVAILABLE && (1 != rr.rating_region)) { unsigned char *p = strchr(p_app->psip_rating_str, (int)','), *p1; if (p) { int size; /* remember current point at ',' */ p1 = p; p++; /* skip ',' */ if (0x20 == *p) /* skip ' ' */ p++; if (!isdigit(*p)) { psip_str_len = strlen(p_app->psip_rating_str); size = psip_str_len - (int)(p - p_app->psip_rating_str); p = cad_rating_filter(p_app->settings.rrt_settings.rrt, p, &size); if (p) { p1++; strcpy(p1, p); } else { /* NULL, remove ',' as well */ *p1 = '\0'; } psip_str_len = strlen(p_app->psip_rating_str); } } } rating_str = NULL; } else { BDBG_MSG(("CAD[%d] chm_process_mss failed\n",rr_idx)); rating_str = &(p_app->psip_rating_str[psip_str_len]); } for (rd_idx = 0; rd_idx < rr.rated_dimensions; ++rd_idx) { if (PSIP_CAD_getRatingDimension(eit_desc,rr_idx,rd_idx,&rd) != BERR_SUCCESS) { BDBG_WRN(("PSIP_CAD_getRatingDimension(%d,%d,%d) failed\n",rr.rating_region,rr_idx,rd_idx)); continue; } if (rating_str != NULL) { psip_str_len = strlen(p_app->psip_rating_str); rating_str = &(p_app->psip_rating_str[psip_str_len]); } #ifndef NO_VCHIP BDBG_MSG(("*** CAD_RD[%d,%d] region %d, %d-%d\n",rr_idx, rd_idx, rr.rating_region, rd.rating_dimension_j,rd.rating_value)); blocked |= chm_psip_block(p_app, rr.rating_region, rd.rating_dimension_j, rd.rating_value,rating_str); BDBG_MSG(("--- dim:%d value:%d blocked:%d, %s\n", rd.rating_dimension_j, rd.rating_value, blocked,p_app->psip_rating_str)); #endif } if (chm_check_cancel(p_chm)) return eCHM_ERR_CANCELED; } chm_evt.type = eCHM_EVT_CAD; chm_evt.id = (((unsigned int)blocked) << 24) | (((unsigned int)rr.rating_region) << 16) | (((unsigned int)rd.rating_dimension_j) << 8) | ((unsigned int)rd.rating_value); chm_evt.ticks = bos_getticks(); result++; bos_post_event(p_app->msg_queue,(b_event_t*)&chm_evt); bos_sleep(30); /* yield so other thread can update */ } break; case 0x86: /* Caption Service descriptor */ { int csd_idx; PSIP_CSD_service tmpCSD; for (csd_idx = 0; csd_idx < PSIP_CSD_getNumServices(eit_desc); ++csd_idx) { if (PSIP_CSD_getService(eit_desc,csd_idx,&tmpCSD) != BERR_SUCCESS) { BDBG_WRN(("Error getting CSD %d\n", csd_idx)); continue; } BDBG_MSG(("CSD-%d) %d, %d, %c%c%c\n", csd_idx,tmpCSD.cc_type, tmpCSD.cc.caption_service_number,tmpCSD.language[0],tmpCSD.language[1],tmpCSD.language[2])); if (!tmpCSD.cc_type || (tmpCSD.cc.caption_service_number == 0) || (tmpCSD.cc.caption_service_number > MAX_CC_SERVICES)) continue; memcpy(&(p_eit->cc_service[tmpCSD.cc.caption_service_number - 1]),&tmpCSD,sizeof(PSIP_CSD_service)); } } break; case 0x81: /* AC-3 Audio descriptor */ case 0xAA: /* Redistribution control descriptor */ case 0xB6: /* Content identifier descriptor */ case 0xAB: /* Genre descriptor */ default: BDBG_MSG((" EIT_DESC[0x%02x]\n",eit_desc[0])); break; } } return result; } /* Summary: Get the EIT section and return > 0 if a change was made to the info structure, 0 if no changes are required and < 0 if an error occured. */ static int chm_get_eit( chm_mgr_t *p_chm, /* Channel manager reference */ chm_info_t *p_info, /* info structure to populate */ unsigned short source_id, /* channel source ID from VCT channel */ unsigned short pid /* table type pid */ ) { int eit_events,eit_idx,update,result; unsigned int eit_section_size,utc_time; PSIP_EIT_event eit; unsigned char *p_source_id = (unsigned char*)&source_id; if (chm_check_cancel(p_chm)) return eCHM_ERR_CANCELED; if ((result = chm_get_utctime(p_chm,&utc_time)) != 0) return result; BDBG_MSG(("UTC TIME(%u), source_id=0x%x\n",utc_time, source_id)); update = eEIT_NONE; smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = p_chm->band; p_chm->msg_params.pid = pid; /* base pid */ /* Source ID for channel (assumes little endian system!!! )*/ p_chm->msg_params.filter.coef[0] = 0xCB; p_chm->msg_params.filter.mask[0] = 0x00; p_chm->msg_params.filter.coef[3] = p_source_id[1]; p_chm->msg_params.filter.mask[3] = 0x00; p_chm->msg_params.filter.coef[4] = p_source_id[0]; p_chm->msg_params.filter.mask[4] = 0x00; /* All PSIP tables have protocol_version which must be = 0 */ p_chm->msg_params.filter.coef[8] = 0x00; /* protocol_version */ p_chm->msg_params.filter.mask[8] = 0x00; eit_section_size = EIT_BUF_LEN; if ((result = chm_getmessage(p_chm,p_chm->eit_buf,&eit_section_size, EIT_TIMEOUT)) != 0) { BDBG_MSG(("%s failed, result=0x%x", __func__, result)); return result; } else { int eit_cnt = 2; /* track eit entry index */ chm_get_utctime(p_chm,&utc_time); utc_time += p_chm->stt.GPS_UTC_offset; eit_events = PSIP_EIT_getNumEvents(p_chm->eit_buf); for (eit_idx = 0; eit_idx < eit_events; eit_idx++) { if (chm_check_cancel(p_chm)) return eCHM_ERR_CANCELED; if (PSIP_EIT_getEvent(p_chm->eit_buf,eit_idx,&eit) != BERR_SUCCESS) continue; if ((utc_time >= eit.start_time) && (utc_time < (eit.start_time + eit.length_in_seconds))) { chm_process_mss(p_chm,eit.p_title_text, p_info->eit_info[EIT_CURRENT].prog_title,MAX_TITLE_CHARS); p_info->eit_info[EIT_CURRENT].event_id = eit.event_id; p_info->eit_info[EIT_CURRENT].length = eit.length_in_seconds; p_info->eit_info[EIT_CURRENT].start_time = eit.start_time; BDBG_MSG(("EIT cur=%s(%d,%u,%u)\n",p_info->eit_info[EIT_CURRENT].prog_title,eit.event_id,eit.start_time,eit.length_in_seconds)); update |= eEIT_CURRENT; chm_process_eit_desc(p_chm,p_chm->eit_buf,eit_idx,&(p_info->eit_info[EIT_CURRENT])); /* got current event */ p_chm->got_eit = true; } else if (((p_info->eit_info[EIT_CURRENT].start_time + p_info->eit_info[EIT_CURRENT].length) >= eit.start_time) && ((p_info->eit_info[EIT_CURRENT].start_time + p_info->eit_info[EIT_CURRENT].length) < (eit.start_time + eit.length_in_seconds))) { chm_process_mss(p_chm,eit.p_title_text, p_info->eit_info[EIT_NEXT].prog_title,MAX_TITLE_CHARS); p_info->eit_info[EIT_NEXT].event_id = eit.event_id; p_info->eit_info[EIT_NEXT].length = eit.length_in_seconds; p_info->eit_info[EIT_NEXT].start_time = eit.start_time; BDBG_MSG(("EIT next=%s(%d,%u,%u)\n",p_info->eit_info[EIT_NEXT].prog_title, p_info->eit_info[EIT_NEXT].event_id,eit.start_time,eit.length_in_seconds)); update |= eEIT_NEXT; } else { /* update other entries */ if ((eit_idx < MAX_EIT_NUM) && (eit_cnt < MAX_EIT_NUM)) { /* same batch of EIT data after NEXT */ if (update & eEIT_NEXT) { chm_process_mss(p_chm,eit.p_title_text, p_info->eit_info[eit_cnt].prog_title,MAX_TITLE_CHARS); p_info->eit_info[eit_cnt].event_id = eit.event_id; p_info->eit_info[eit_cnt].length = eit.length_in_seconds; p_info->eit_info[eit_cnt].start_time = eit.start_time; BDBG_WRN(("EIT entry %d = %s(%d,%u,%u, utc_time=%u)\n",eit_idx,p_info->eit_info[eit_cnt].prog_title, p_info->eit_info[eit_cnt].event_id,eit.start_time,eit.length_in_seconds,utc_time)); eit_cnt++; update |= eEIT_CONT; /* beyond current and next entries */ } } } /* exit if both are found */ //if (update == eEIT_BOTH) /* don't quit since we need to get more EPG */ // break; } } return update; } /* Summary: Get the ETT section and return > 0 if a change was made to the info structure, 0 if no changes are required and < 0 if an error occured. */ static int chm_get_ett( chm_mgr_t *p_chm, /* Channel manager reference */ chm_info_t *p_info, /* info structure to populate */ unsigned short source_id, /* channel source ID from VCT channel */ unsigned short pid /* type type pid */ ) { int update; unsigned int ett_section_size; unsigned char *p_source_id = (unsigned char*)&source_id; PSIP_ETT_header ett; int result; unsigned short event_id; update = eEIT_NONE; smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = p_chm->band; p_chm->msg_params.pid = pid; /* base pid */ /* All PSIP tables have protocol_version which must be = 0 */ p_chm->msg_params.filter.coef[8] = 0x00; /* protocol_version */ p_chm->msg_params.filter.mask[8] = 0x00; /* Source ID for channel (assumes little endian system!!! )*/ p_chm->msg_params.filter.coef[9] = p_source_id[1]; p_chm->msg_params.filter.mask[9] = 0x00; p_chm->msg_params.filter.coef[10] = p_source_id[0]; p_chm->msg_params.filter.mask[10] = 0x00; /* just check current one */ event_id = (p_info->eit_info[EIT_CURRENT].event_id << 2) | 0x2; p_chm->msg_params.filter.coef[11] = (event_id >> 8); p_chm->msg_params.filter.mask[11] = 0x00; p_chm->msg_params.filter.coef[12] = event_id & 0xff; p_chm->msg_params.filter.mask[12] = 0x03; ett_section_size = ETT_BUF_LEN; if ((result = chm_getmessage(p_chm,p_chm->ett_buf,&ett_section_size, ETT_TIMEOUT)) != 0) return result; else { p_chm->ett_cnt++; PSIP_ETT_getHeader(p_chm->ett_buf,&ett); if (ett.ETM_id.event_id == p_info->eit_info[EIT_CURRENT].event_id) { chm_process_mss(p_chm,ett.p_extended_text_message, p_info->eit_info[EIT_CURRENT].prog_desc,MAX_DESC_CHARS); update |= eEIT_CURRENT; BDBG_MSG(("ETT: 0x%04x(%d) %s\n",pid, EIT_CURRENT,p_info->eit_info[EIT_CURRENT].prog_desc)); p_chm->got_ett = true; } else if (ett.ETM_id.event_id == p_info->eit_info[EIT_NEXT].event_id) { chm_process_mss(p_chm,ett.p_extended_text_message, p_info->eit_info[EIT_NEXT].prog_desc,MAX_DESC_CHARS); update |= eEIT_NEXT; BDBG_MSG(("ETT: 0x%04x(%d) %s\n",pid, EIT_NEXT,p_info->eit_info[EIT_NEXT].prog_desc)); } else { #ifdef BCM_DEBUG chm_process_mss(p_chm,ett.p_extended_text_message, p_chm->tmp_text,MAX_TITLE_CHARS); BDBG_MSG(("ETT: 0x%04x(%d != %d or %d) %s\n",pid,ett.ETM_id.event_id, p_info->eit_info[EIT_CURRENT].event_id,p_info->eit_info[EIT_NEXT].event_id ,p_chm->tmp_text)); #endif } } return update; } /* Summary: Process the MMS string and associated tables. Returns non-zero on failure. */ int chm_process_mss( chm_mgr_t *p_chm, /* Channel manager reference */ PSIP_MSS_string p_mms, /* Pointer to MMS */ unsigned char* mms_buf, /* string buffer */ unsigned int mms_len /* string buffer length */ ) { int str_idx,lsize; char *p_code; BERR_Code retcode; int num_str; bapp_t *p_app; if (!p_mms) return eCHM_ERR_FAILED; num_str = PSIP_MSS_getNumStrings(p_mms); p_app = (bapp_t*)p_chm->p_app; memset(mms_buf,0,mms_len); for (str_idx = 0; str_idx < num_str; ++str_idx) { if ((retcode = PSIP_MSS_getCode(p_mms,str_idx, &p_code)) != BERR_SUCCESS) { BDBG_MSG(("PSIP_MSS_getCode:%d failed %d\n",__LINE__,retcode)); continue; } if (chm_valid_lang(p_chm,p_code) != p_app->lang) { continue; } lsize = mms_len; if ((retcode = PSIP_MSS_getString(p_mms,str_idx,&lsize, mms_buf)) == BERR_SUCCESS) { /* got one matched to current language */ return 0; } } /* in case no valid language exists just use first string */ if (num_str > 0) { lsize = mms_len; if ((retcode = PSIP_MSS_getString(p_mms,0,&lsize, mms_buf)) == BERR_SUCCESS) { BDBG_WRN(("No language match using first language in the MMS list\n")); return eCHM_ERR_OK; } else { BDBG_WRN(("PSIP_MSS_getString failed %d\n",retcode)); } } BDBG_WRN(("%s: no valid string found, num_str = %d\n",__func__,num_str)); return eCHM_ERR_FAILED; } /* Summary: Pocess the VCT in background manner? Returns non-zero on failure. */ static int chm_process_vct( chm_mgr_t *p_chm, /* Channel manager reference */ unsigned char *msg_buf, /* Buffer containing the MGT table */ unsigned int msg_size, /* PSI buffer size */ chm_info_t *p_info, /* info structure to populate */ unsigned short minor /* minor ch number */ ) { int minor_idx,sld_idx,total; TS_PSI_descriptor psi_desc; PSIP_VCT_channel vct; bapp_t *p_app = (bapp_t*)p_chm->p_app; total = PSIP_VCT_getNumChannels(msg_buf); if (total < 1) return eCHM_ERR_FAILED; for (minor_idx = 0; minor_idx < total; ++minor_idx) { /* if user cancel is recevied */ if (chm_check_cancel(p_chm)) return eCHM_ERR_CANCELED; memset(&vct,0,sizeof(vct)); if (PSIP_VCT_getChannel( (const uint8_t *)msg_buf, minor_idx, &vct ) != BERR_SUCCESS) { BDBG_WRN(("Failure processing VCT %d\n",minor_idx)); return eCHM_ERR_FAILED; } /* for digital, start from 1 */ if (vct.minor_channel_number != minor) { BDBG_MSG(("VCT minor number %d not match to %d\n",vct.minor_channel_number, minor)); continue; } p_info->major = vct.major_channel_number; p_info->minor = vct.minor_channel_number; p_info->source_id = vct.source_id; p_info->program_number = vct.program_number; memcpy(p_info->ch_name, vct.short_name,sizeof(uint16_t) * MAX_CH_NAME_CHARS); p_info->got_vct = true; if (p_chm->local_time_source == eTS_NONE) { p_chm->local_time_source = eTS_FIXED; p_chm->local_offset = 0 - (int)g_tz_map[p_app->settings.time_zone]; p_chm->local_offset *= 3600; p_chm->local_dst_obs = p_app->settings.dst; BDBG_MSG(("### Local time %d Fixed ###\n",p_chm->local_offset)); } sld_idx = 0; while ((psi_desc = PSIP_VCT_getChannelDescriptor(msg_buf, minor_idx,sld_idx)) != NULL) { if (psi_desc[0] == 0xA0) /* Extended channel name descriptor */ { PSIP_MSS_string lstr = PSIP_ECND_getLongChannelName(psi_desc); chm_process_mss(p_chm,lstr,p_info->ext_ch_name,MAX_LONG_NAME_CHARS); } sld_idx++; } return eCHM_ERR_OK; } return eCHM_ERR_FAILED; /* channel not found */ } /* Summary: Process the MGT and associated tables. Returns non-zero on failure. */ static int chm_process_mgt( chm_mgr_t *p_chm, /* Channel manager reference */ unsigned char *msg_buf, /* Buffer containing the MGT table */ unsigned int msg_size, /* PSI buffer size */ chm_info_t *p_info, /* info structure to populate */ bool ext_text, /* Get extended text */ bool redraw /* Send redraw event */ ) { PSIP_MGT_table table; int num_tables, update = eEIT_NONE,result = 0; unsigned short source_id; unsigned int utc_time; bapp_t *p_app = (bapp_t*)p_chm->p_app; if ((result = chm_get_utctime(p_chm,&utc_time)) != 0) return result; gettimeofday(&p_chm->mgt_tv); source_id = p_info->source_id; num_tables = PSIP_MGT_getTablesDefined(msg_buf); switch (p_chm->mgt_state) { case eMGT_EIT: /* try to get either EIT or ETT */ for (p_chm->mgt_idx = 0; p_chm->mgt_idx < num_tables; p_chm->mgt_idx++) { if (chm_check_cancel(p_chm)) return eCHM_ERR_CANCELED; PSIP_MGT_getTable(msg_buf,p_chm->mgt_idx,&table); /* currently we only care current and next program instead of 128 eits (0x017F) */ //if ((table.table_type >= 0x0100) && (table.table_type < (0x0100 + MAX_EIT_NUM)) && (eEIT_BOTH != update)) if ((table.table_type >= 0x0100) && (table.table_type < (0x0100 + MAX_EIT_NUM))) { BDBG_MSG(("EIT: PID=0x%04x, type=0x%04x",table.table_type_PID,table.table_type)); result = chm_get_eit(p_chm,p_info,source_id,table.table_type_PID); if (result < 0) { BDBG_MSG(("chm_get_eit failed, ret=%d", result)); if (update) result = 0; break; } #ifndef NO_VCHIP if (p_app->psip_rating_str[0] && !(0 == result || eEIT_CONT == result)) { memcpy(&p_info->psip_rating_str, p_app->psip_rating_str, strlen(p_info->psip_rating_str)); p_info->psip_rating_str[strlen(p_info->psip_rating_str)] = 0; } #endif if (eEIT_NONE == result) { /* continue check until find the EIT entry corresponding to current */ /* current should be EIT-0 but some station encode it in different order */ /* since now we have a new eEIT_CONT, so if it is not, then don't need to continue */ if (update) break; } /* clear CONT flag if any */ result &= ~eEIT_CONT; update |= result; result = 0; if (update != 0) { if (redraw) { chm_make_current(p_chm,p_info); p_chm->redraw_evt.type = eCHM_EVT_REDRAW; p_chm->redraw_evt.id = p_app->cur_ch_num; p_chm->redraw_evt.ticks = 1; /* force to redraw if in banner screen */ chm_post_app_event(p_chm,&p_chm->redraw_evt); //bos_sleep(50); } } //if (update == eEIT_BOTH) // break; } #if 1 if (((table.table_type >= 0x0200) && (table.table_type < 0x0201)) && !p_chm->got_ett && (update & eEIT_CURRENT)) { BDBG_MSG(("ETT: PID=0x%04x, type=0x%04x",table.table_type_PID,table.table_type)); chm_get_ett(p_chm,p_info,source_id,table.table_type_PID); /* update UI if in case BIG banner screen is shown */ if ((update & eEIT_CURRENT) && p_chm->got_ett) { if (redraw) { chm_make_current(p_chm,p_info); p_chm->redraw_evt.type = eCHM_EVT_REDRAW; p_chm->redraw_evt.id = p_app->cur_ch_num; p_chm->redraw_evt.ticks = 1; /* force to redraw if in banner screen */ chm_post_app_event(p_chm,&p_chm->redraw_evt); //bos_sleep(50); } } } #endif } if (update & eEIT_CURRENT) { /* have current event, so clear previous ETT if any to get ETT again */ if (!p_chm->got_ett) { p_info->eit_info[EIT_CURRENT].prog_desc[0] = 0; p_chm->mgt_state = eMGT_ETT; result = 0; } else { p_chm->state = eCHM_PROG_IDLE; } } break; case eMGT_ETT: if (!ext_text) { p_chm->state = eCHM_PROG_IDLE; break; } for (p_chm->mgt_idx = 0; p_chm->mgt_idx < num_tables; p_chm->mgt_idx++) { if (chm_check_cancel(p_chm)) return eCHM_ERR_CANCELED; PSIP_MGT_getTable(msg_buf,p_chm->mgt_idx,&table); /* currently we only care current and next program instead of 128 eits (0x027F) */ //if ((table.table_type >= 0x0200) && (table.table_type < (0x0200 + MAX_EIT_NUM))) if ((table.table_type >= 0x0200) && (table.table_type < (0x0201))) { BDBG_MSG(("ETT: PID=0x%04x, type=0x%04x",table.table_type_PID,table.table_type)); result = chm_get_ett(p_chm,p_info,source_id,table.table_type_PID); if (result < 0) { BDBG_WRN(("chm_get_ett failed, ret=%d", result)); continue; } update |= result; result = 0; if (update & eEIT_CURRENT) { if (redraw) { chm_make_current(p_chm,p_info); p_chm->redraw_evt.type = eCHM_EVT_REDRAW; p_chm->redraw_evt.id = p_app->cur_ch_num; p_chm->redraw_evt.ticks = 1; /* force to redraw if in banner screen */ chm_post_app_event(p_chm,&p_chm->redraw_evt); //bos_sleep(50); } p_chm->state = eCHM_PROG_IDLE; break; } } } break; } return result; } /* Summary: Get the VCT callback, and process it */ void * chm_smessage_vct_callback(void * context, size_t size) { chm_mgr_t * p_chm; bapp_t *p_app; int total; p_chm = (chm_mgr_t*)context; p_app = (bapp_t*)p_chm->p_app; if (chm_check_cancel(p_chm)) return(void*)NULL; total = PSIP_VCT_getNumChannels(p_chm->vct_buf); if (total >= p_app->settings.ch[p_chm->cur_ch_num].minor) { /*TODO need add one more state to process VCT instead process it in callback */ /* VERIFY slow channel change */ #if 0 if (eCHM_ERR_OK == chm_process_vct(p_chm,p_chm->vct_buf, size, &p_chm->p_info[p_chm->cur_ch_num], p_app->settings.ch[p_chm->cur_ch_num].minor)) { p_chm->got_vct = true; } #endif } //return(void*)p_chm->vct_buf; return (p_chm->got_vct ? (void *)NULL : (void*)p_chm->vct_buf); } /* Summary: Get the VCT. return non-zero on failure */ static int chm_get_vct( chm_mgr_t *p_chm, /* Channel manager reference */ chm_info_t *p_info /* info structure to populate */ ) { bapp_t *p_app= (bapp_t*)p_chm->p_app; unsigned int section_size,section_number; int result = eCHM_ERR_OK; if (p_info->got_vct) { p_chm->state = eCHM_GET_MGT; return result; } section_number = 0; do { if (chm_check_cancel(p_chm)) return eCHM_ERR_CANCELED; /* Get the VCT */ smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = p_chm->band; p_chm->msg_params.pid = 0x1ffb; /* VCT */ section_size = VCT_BUF_LEN; p_chm->msg_params.filter.coef[0] = 0xc8; /* Table ID for terrestrial */ p_chm->msg_params.filter.mask[0] = 0x00; p_chm->msg_params.filter.coef[6] = section_number; /* section number */ p_chm->msg_params.filter.mask[6] = 0x00; /* All PSIP tables have protocol_version which must be = 0 */ p_chm->msg_params.filter.coef[8] = 0x00; /* protocol_version */ p_chm->msg_params.filter.mask[8] = 0x00; if ((result = chm_getmessage(p_chm,p_chm->vct_buf, §ion_size, VCT_TIMEOUT)) != 0) { BDBG_WRN(("%s Timeout waiting for VCT (section # %d)\n",__func__,section_number)); return result; } if (chm_check_cancel(p_chm)) return eCHM_ERR_CANCELED; if (chm_process_vct(p_chm,p_chm->vct_buf, section_size, p_info,p_app->settings.ch[p_chm->cur_ch_num].minor) != 0) { BDBG_WRN(("VCT entry not found %d\n",section_number)); section_number++; continue; } break; } while (section_size != 0); p_chm->state = eCHM_GET_MGT; return result; } /* * Summary: * Stop the VCT message filter. */ static void chm_mgt_stop(chm_mgr_t *p_chm) { if (NULL != p_chm->mgt_msg) { if (b_ok != smessage_stop(p_chm->mgt_msg)) { BDBG_ERR(("%s:%d",__func__, __LINE__)); return; } smessage_close(p_chm->mgt_msg); p_chm->mgt_msg = NULL; } } /* * Summary: * MGT callback, and process it */ void *chm_smessage_mgt_callback(void * context, size_t size) { chm_mgr_t * p_chm; bapp_t *p_app; int total; uint32_t crc; p_chm = (chm_mgr_t*)context; p_app = (bapp_t*)p_chm->p_app; if (chm_check_cancel(p_chm)) return(void*)NULL; total = PSIP_MGT_getTablesDefined(p_chm->mgt_buf); if (total > 0) { /* new MGT? */ crc = chm_section_crc(p_chm->mgt_buf, size); if (p_chm->mgt_crc != crc) { p_chm->mgt_crc = crc; p_chm->mgt_size = size; p_chm->got_mgt = true; p_chm->mgt_state = eMGT_EIT; p_chm->state = eCHM_PROCESS_MGT; } } return(void*)p_chm->mgt_buf; } static int chm_mgt_start( chm_mgr_t *p_chm, unsigned short network_pid ) { int cerr = eCHM_ERR_OK; chm_mgt_stop(p_chm); p_chm->mgt_msg = smessage_open(smessage_format_psi); if (NULL == p_chm->mgt_msg) { BDBG_ERR(("%s failed",__func__)); cerr = eCHM_ERR_FAILED; goto ExitFunc; } smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = p_chm->band; p_chm->msg_params.pid = (uint16_t)network_pid; /* 0x1ffb */ p_chm->msg_params.filter.coef[0] = 0xC7; p_chm->msg_params.filter.mask[0] = 0x00; /* All PSIP tables have protocol_version which must be = 0 */ p_chm->msg_params.filter.coef[8] = 0x00; /* protocol_version */ p_chm->msg_params.filter.mask[8] = 0x00; p_chm->msg_params.buffer = p_chm->mgt_buf; p_chm->msg_params.buffer_size = MGT_BUF_LEN; /* processing is done in callback */ p_chm->msg_params.data_ready_callback = chm_smessage_mgt_callback; p_chm->msg_params.overflow = NULL; p_chm->msg_params.callback_context = (void *)p_chm; if (b_ok != smessage_start(&p_chm->msg_params, p_chm->mgt_msg)) { BDBG_ERR(("%s:%d",__FILE__, __LINE__)); cerr = eCHM_ERR_FAILED; } ExitFunc: return cerr; } /* Summary: Get the MGT. return non-zero on failure */ static int chm_get_mgt(chm_mgr_t *p_chm) { int result; smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = p_chm->band; p_chm->msg_params.pid = (unsigned short)0x1FFB; /* base pid */ p_chm->mgt_size = MGT_BUF_LEN; p_chm->msg_params.filter.coef[0] = 0xC7; p_chm->msg_params.filter.mask[0] = 0x00; /* All PSIP tables have protocol_version which must be = 0 */ p_chm->msg_params.filter.coef[8] = 0x00; /* protocol_version */ p_chm->msg_params.filter.mask[8] = 0x00; if ((result = chm_getmessage(p_chm,p_chm->mgt_buf,&p_chm->mgt_size, MGT_TIMEOUT)) != 0) { BDBG_WRN(("Timeout waiting for MGT\n")); return result; } p_chm->got_mgt = true; p_chm->mgt_state = eMGT_EIT; p_chm->state = eCHM_PROCESS_MGT; p_chm->mgt_cnt++; return eCHM_ERR_OK; } /* Summary: Generic tuner cancel callback. */ void chm_tune_cancel_callback(void *context) { chm_mgr_t *p_chm = (chm_mgr_t *)context; if (chm_check_cancel(p_chm)) { BDBG_WRN(("### CANCEL TUNE ###\n")); p_chm->tuner_params.cancel = true; } } /* Summary: Return the hunt mode progress in percent so that the UI can be updated accordingly. */ int chm_get_hunt_progress(chm_mgr_t *p_chm) { bapp_t *p_app = (bapp_t *)p_chm->p_app; if (0 == p_app->settings.num_channels) { if (eFREQ_TABLE_USER_DEFINED == p_chm->fe_type) bapp_user_freq_table_get(p_app, &p_chm->freq_table, &p_chm->num_freq); else bapp_freq_table_get(p_chm->fe_type,(const unsigned int **)&(p_chm->freq_table),&(p_chm->num_freq)); } if (p_chm->num_freq == 0) { BDBG_WRN(("%s p_chm->num_freq = %d",__func__,p_chm->num_freq)); return 0; } return (p_chm->select_idx * 100)/p_chm->num_freq; } #ifdef ACB612 bool chm_GetScanStatusFinish(void) { return isScanStatusFinish; } #endif /* Summary: Reset the channel and do a scan for channels to create a new channel map. Description: Scan for all available channels. */ static void chm_scan(chm_mgr_t *p_chm) { bapp_t *p_app = (bapp_t*)p_chm->p_app; unsigned int freq; int num; #ifdef ACB612 int nFCnt = 0; isScanStatusFinish = false; #endif bband_t rc; if (eFREQ_TABLE_USER_DEFINED == p_chm->fe_type) bapp_user_freq_table_get(p_app, &p_chm->freq_table, &p_chm->num_freq); else bapp_freq_table_get(p_chm->fe_type,(const unsigned int **)&(p_chm->freq_table),&(p_chm->num_freq)); /* current we do a full scan always, so clear channel map information */ p_app->settings.num_channels = 0; p_app->cur_ch_num = 0; p_chm->cur_ch_num = 0; memset(p_chm->p_info,0,p_chm->num_freq * sizeof(chm_info_t)); p_chm->band = TUNER_BAND; #ifdef ACB612 if(p_app->factory_test) nFCnt = 0; #endif for (p_chm->select_idx = 0; p_chm->select_idx < p_chm->num_freq; p_chm->select_idx++) { #ifdef ACB612 if(p_app->factory_test) { if(nFCnt >= FACTORY_CHN_NUMBER) break; p_chm->select_idx = (F_chaSet[nFCnt] - 2); nFCnt++; } #endif freq = (unsigned int)(p_chm->freq_table[p_chm->select_idx]); freq *= 1000; /* convert to Hz */ /* need to show frequency in scanning on UI */ p_chm->last_freq_hz = freq; BDBG_WRN(("Scan[%d] freq = %d\n",p_chm->select_idx,freq)); /* Check to see if scan was canceled */ if (chm_check_cancel(p_chm)) break; if (eFREQ_TABLE_USER_DEFINED != p_chm->fe_type) { /* output progress event id will be % done */ p_chm->chm_pat_evt.type = eCHM_EVT_STATUS; p_chm->chm_pat_evt.id = eCHM_STATUS_NOPAT; p_chm->chm_pat_evt.ticks = (unsigned int)(((p_chm->select_idx)*100)/p_chm->num_freq); chm_post_app_event(p_chm,&p_chm->chm_pat_evt); } /* Try tuning */ btuner_params_init(&p_chm->tuner_params, p_chm->tuner); p_chm->tuner_params.cancel_callback = chm_tune_cancel_callback; p_chm->tuner_params.cancel_callback_context = p_chm; p_chm->tuner_params.wait_for_lock = true; rc = btuner_tune(p_chm->tuner, freq, &p_chm->tuner_params); if ( rc< 0) { BDBG_MSG(("FREQ[%d] freq = %d not locked, try next freq",p_chm->select_idx,freq)); chm_send_status(p_chm,freq,false); continue; } else { //printf("%15s@%5d:%10s ===> FREQ [%3d] freq = %4dMhz locked\n", strrchr(__FILE__,'/')? strrchr(__FILE__,'/')+1 : __FILE__,__LINE__,__FUNCTION__,p_chm->select_idx,freq/1000000); chm_send_status(p_chm,freq,true); } if ((num = chm_scan_vct(p_chm,p_chm->band,p_chm->select_idx)) <= 0) { BDBG_WRN(("no VCT found, perform PSI scan instead")); if ((num = chm_scan_psi(p_chm,p_chm->band,p_chm->select_idx)) <= 0) { BDBG_WRN(("chm_scan_psi found no channels\n")); } else { BDBG_MSG(("PSI channels %d \n", num)); } } } /* clear it after scanning */ p_chm->last_freq_hz = 0; if (eFREQ_TABLE_USER_DEFINED != p_chm->fe_type) { p_chm->chm_pat_evt.type = eCHM_EVT_STATUS; p_chm->chm_pat_evt.id = eCHM_STATUS_PAT; p_chm->chm_pat_evt.ticks = 100; /* update only meet following condition */ if (false == p_app->scan_in_progress && 0 == p_app->settings.num_channels) { /* user cancel the scan before getting any channel yet, bail out */ } else { chm_post_app_event(p_chm,&p_chm->chm_pat_evt); //if (p_app->screen_id == eSCREEN_CH_SCAN_PROGRESS) { bos_sleep(200); //} } } if (eRRT_AVAILABLE != p_app->settings.rrt_settings.rrt_status) p_chm->rrt_size = 0; p_chm->cmd = eCHM_CANCEL; p_app->cur_ch_num = 0; /* update p_info */ if (p_app->settings.num_channels > 0) { bapp_ch_t *pch; chm_info_t *p_info; for (num = 0; num < p_app->settings.num_channels; num++) { pch = &p_app->settings.ch[num]; p_info = &p_chm->p_info[num]; p_info->source_id = pch->source_id; p_info->major = pch->major; p_info->minor = pch->minor; p_info->program_number = pch->program_num; memcpy(p_info->ch_name, p_info->ch_name, sizeof(pch->ch_name)); p_info->got_vct = !pch->psi; } } #ifdef ACB612 isScanStatusFinish = true; #endif } /* Summary: Initial or rescan of VSB channels Description: Scan for all available VSB channels. */ static void chm_hunt(chm_mgr_t *p_chm) { bapp_t *p_app = (bapp_t*)p_chm->p_app; chm_stop_decode(p_chm); /* stop all streams */ chm_stop_stream(p_chm,&(p_chm->chm_stream[CURR_CH])); chm_stop_stream(p_chm,&(p_chm->chm_stream[PREV_CH])); chm_stop_stream(p_chm,&(p_chm->chm_stream[NEXT_CH])); p_app->scan_in_progress = true; chm_scan(p_chm); /* channel scan done, all the channel map are added during scan time, so to update the channel map */ p_chm->chm_pat_evt.type = eCHM_EVT_STATUS; p_chm->chm_pat_evt.id = eCHM_STATUS_CHMAP; /* to update channel map if changed */ p_chm->chm_pat_evt.ticks = p_app->settings.num_channels ? 2 : 0; if (0 == p_app->settings.num_channels && false == p_app->scan_in_progress) { /* if no channel scanned and scan_in_progress is false, it should be cancelled by the user */ /* bail out */ } else chm_post_app_event(p_chm,&p_chm->chm_pat_evt); p_app->scan_in_progress = false; p_chm->cmd = eCHM_CANCEL; bos_sleep(200); p_chm->hunt_cnt++; } /* Summary: Post an event the app queue to provide status. */ static int chm_post_app_event_cb(chm_mgr_t *p_chm, /* Channel manager reference */ chm_event_t *p_event /* Event to post to app queue */ ) { bapp_t *p_app = (bapp_t*)p_chm->p_app; bos_post_event(p_app->msg_queue,(b_event_t*)p_event); bos_sleep(10); return 0; } /* Summary: Post an event the app queue to provide status. */ static int chm_post_app_event(chm_mgr_t *p_chm, /* Channel manager reference */ chm_event_t *p_event /* Event to post to app queue */ ) { int result = chm_post_app_event_cb(p_chm,p_event); return result; } /****************************************************************************** * INPUTS: context * OUTPUTS: none. * RETURNS: none * FUNCTION: message_callback * DESCRIPTION: Callback function normally called when a complete message has * been received and passed the filter criteria. ******************************************************************************/ void * chm_smessage_eas_callback(void * context, size_t size) { chm_mgr_t * p_chm = (chm_mgr_t*)context; bapp_t *p_app; TS_SCTE_18_header header; /* header structure */ unsigned int timegpssec, event_end_time; unsigned short source_id; b_timeval cur; bool found_sourceid = false; size_t ex_size,i, j; uint8_t *pOffset; p_app = (bapp_t*)p_chm->p_app; int eas_ch_num = -1; BDBG_WRN(("#### EAS Event %d ####", p_chm->eas_cnt)); p_chm->eas_cnt++; if (!p_app->power) { BDBG_WRN(("####EAS Event: Box in standby state: ignore. #### \n")); goto ExitFunc; } if (TS_SCTE_18_getSectionHeader(p_chm->eas_buf, size, &header) != 0) { BDBG_ERR(("#### TS_SCTE_18_getSectionHeader failed ####\n")); goto ExitFunc; } if (0 == header.alert_priority) { BDBG_MSG(("#### reset EAS sequence %d ####\n",header.sequence_number)); /* establish a new sequence number */ p_chm->sequence_number = header.sequence_number; /* eas reset, so don't check once get other alert priority */ p_chm->eas_reset = true; goto ExitFunc; } if (p_chm->sequence_number == header.sequence_number) { if (!p_chm->eas_reset) { BDBG_WRN(("#### EAS Event %d duplicate ####\n",header.sequence_number)); /* the test stream also uses same sequence number all the time */ goto ExitFunc; } } if ((int8_t)header.alert_priority < SCTE_18_ALERT_PRIORITY) { BDBG_WRN(("#### EAS Event alert priority %d < %d, ignore.#### \n", header.alert_priority, SCTE_18_ALERT_PRIORITY)); goto ExitFunc; } if ((int8_t)header.alert_message_time_remaining < 0 || header.alert_message_time_remaining > 120) { BDBG_WRN(("#### EAS Event alert_message_time_remaining %d out of range ####\n",header.alert_message_time_remaining)); goto ExitFunc; } p_chm->sequence_number = header.sequence_number; if (p_chm->EAS_event_ID == header.EAS_event_ID) { if (!p_chm->eas_reset) { /* same EAS message, but only time remaining changed */ GETTIMEOFDAY(&cur); if (header.alert_message_time_remaining) p_app->eas_timeout = cur.tv_sec + header.alert_message_time_remaining; else p_app->eas_timeout = 0; BDBG_MSG(("#### EAS Event %d already processed ####\n",header.EAS_event_ID)); goto ExitFunc; } } /* new EAS message is distributed */ p_chm->EAS_event_ID = header.EAS_event_ID; /* it is the point that we will do sanity check after here */ p_chm->eas_reset = false; if (0 == header.event_duration) { BDBG_WRN(("#### EAS Event event_duration set to infinity ####")); /*RLQ, need to check what to do with infinity value as it may cause problem */ header.event_duration = (uint16_t)(EAS_INFINITY / 60); } if (header.event_start_time && header.event_duration) { /* 15 mins to 100 hours */ if (header.event_duration < 15 || header.event_duration > 6000 ) { if ((uint16_t)(EAS_INFINITY / 60) != header.event_duration) { BDBG_WRN(("#### EAS Event event_duration %d out of range ####\n",header.event_duration)); goto ExitFunc; } } GETTIMEOFDAY(&cur); timegpssec = p_app->system_time + cur.tv_sec; event_end_time = header.event_start_time+(header.event_duration * 60); if (event_end_time < timegpssec) { BDBG_WRN(("#### EAS Event has expired %d < %d, ignore.#### \n", event_end_time, timegpssec)); goto ExitFunc; } } /* if exception list existed, check to see if current channel is in exception list */ if (header.exception_count) { BDBG_WRN(("#### EAS exception count = %d\n",header.exception_count)); pOffset = TS_SCTE_18_getExpectionOffset((const char *)p_chm->eas_buf, &ex_size); if (pOffset) { chm_info_t *p_info; TS_SCTE_18_exception s; for (i = 0; i < header.exception_count; i++) { if (TS_SCTE_18_getException(pOffset, i, &s)) { BDBG_WRN(("#### EAS exception (%d,0x%04x,0x%04x)\n",s.in_band_reference,s.major_channel_number,s.minor_channel_number)); for (j = 0; j < p_app->settings.num_channels; j++) { p_info = &p_chm->p_info[j]; if ((unsigned char)s.major_channel_number == p_info[j].major && (unsigned short)s.minor_channel_number == p_info[j].minor) { if (s.in_band_reference) { BDBG_WRN(("#### 0x%04x.0x%04x in exception list, discard\n", s.major_channel_number, s.minor_channel_number)); goto ExitFunc; } else { /* do we support OOB in ATSC? or do further check for source id within given major and minor? */ BDBG_WRN(("#### OOB 0x%04x.0x%04x in exception list, discard\n",s.major_channel_number,s.minor_channel_number)); goto ExitFunc; } } } } else { BDBG_WRN(("#### EAS TS_SCTE_18_getException %d failed\n",i)); goto ExitFunc; } } } } #if 0 /* try details OOB source ID first */ if (header.details_OOB_source_ID) { BDBG_WRN(("#### EAS Attempt to set details source ID = 0x%04x.#### \n", header.details_OOB_source_ID)); source_id = header.details_OOB_source_ID; found_sourceid = ch_map_set(bapp_cur_ch_map(p_app),source_id); } #endif /* check if the EAS channel is in the channel map? */ if (header.details_major_channel_number && header.details_minor_channel_number) { BDBG_WRN(("#### EAS Attempt to set major.minor [0x%04x.0x%04x] #### \n", header.details_major_channel_number, header.details_minor_channel_number)); eas_ch_num = chm_find_match_channel(p_chm, header.details_major_channel_number, header.details_minor_channel_number, header.details_OOB_source_ID); if (eas_ch_num < 0) { /* try audio only */ if (15 == header.alert_priority && header.audio_OOB_source_ID) { BDBG_WRN(("#### EAS Attempt to set audio source ID = 0x%04x.#### \n", header.audio_OOB_source_ID)); if ((eas_ch_num = chm_find_match_channel(p_chm, header.details_major_channel_number, header.details_minor_channel_number, header.audio_OOB_source_ID)) < 0) { BDBG_WRN(("#### EAS Attempt to set audio source ID = 0x%04x failed.#### \n", header.audio_OOB_source_ID)); /*RLQ, if the channel is not in the channel map, just scroll EAS text if any */ if (header.alert_text_length) goto SCROLL_ONLY; goto ExitFunc; } } else { BDBG_WRN(("#### EAS no details source ID available\n")); /*RLQ*/ if (header.alert_text_length) goto SCROLL_ONLY; goto ExitFunc; } } found_sourceid = true; } /* TODO until find a way to map the RF_channel and program_number to major/minor/source_id, then we can enable this part */ #if 0 /* Check for Channel Descriptor */ if (!found_sourceid) { /* try channel description */ TS_SCTE_18_in_band_channel_descriptor ib_channel_desc; pOffset = TS_SCTE_18_getDescriptorOffset((const char *)p_chm->eas_buf, &ex_size); if (pOffset == NULL) { BDBG_WRN(("#### EAS Channel Descriptor not found #### \n")); goto ExitFunc; } if (!TS_SCTE_18_getInBandChannelDescriptor(pOffset, &ib_channel_desc,ex_size)) { BDBG_WRN(("#### EAS Channel Desc not found #### \n")); goto ExitFunc; } if (!ch_map_find_soure_id(bapp_cur_ch_map(p_app), (unsigned char)ib_channel_desc.exception_RF_channel, (unsigned short)ib_channel_desc.exception_program_number, &source_id)) { BDBG_WRN(("#### EAS sourceID not found for ib channel desc [0x%04x.0x%04x] #### \n", ib_channel_desc.exception_RF_channel, ib_channel_desc.exception_program_number)); goto ExitFunc; } } #endif if ( ((header.location_code_count) < 1) || ((header.location_code_count) > 31) ) { BDBG_WRN(("#### EAS location code count invalid %d #### \n",header.location_code_count)); //goto ExitFunc; } else { pOffset = TS_SCTE_18_getLocationOffset((const char *)p_chm->eas_buf, &size); if (pOffset) { TS_SCTE_18_location_code s; for (i = 0; i < header.location_code_count; i++) { if (TS_SCTE_18_getLocationCode(pOffset, i, &s)) { if ( (((int8_t)s.state_code) < 0) || ((s.state_code) > 99) || (((int8_t)s.county_subdivision) < 0) || ((s.county_subdivision) > 9) || (((int8_t)s.county_code) < 0) || ((s.county_code) > 999) ) { BDBG_WRN(("#### EAS state code %d or subdivision %d country_code %d invalid#### \n",s.state_code, s.county_subdivision, s.county_code)); goto ExitFunc; } } else { goto ExitFunc; } } } else { goto ExitFunc; } } /* if we are here, then it is good to switch to EAS channel */ SCROLL_ONLY: if ((eas_ch_num >= 0) || header.alert_text_length) { BDBG_WRN(("#### EAS source ID = 0x%04x found (timeout %d secs).#### \n", source_id,header.alert_message_time_remaining)); p_chm->chm_eas_evt.type = eCHM_EVT_EAS; p_chm->chm_eas_evt.ticks = (0 == header.alert_message_time_remaining) ? EAS_INFINITY : header.alert_message_time_remaining; p_chm->chm_eas_evt.id = eas_ch_num; p_chm->chm_eas_evt.data[0] = (eas_ch_num >= 0)? true : false; p_chm->eas_timer_started = true; memcpy(p_app->eas_buf, p_chm->eas_buf, size); p_app->eas_buf_size = size; chm_post_app_event_cb(p_chm,&p_chm->chm_eas_evt); } else { BDBG_WRN(("#### EAS source ID = 0x%04x not found.#### \n", source_id)); } ExitFunc: return(void*)p_chm->eas_buf; } /* Summary: Stop the STT. return non-zero on failure */ static void chm_stt_stop(chm_mgr_t *p_chm) { if (NULL != p_chm->stt_msg) { if (b_ok != smessage_stop(p_chm->stt_msg)) { BDBG_ERR(("%s:%d",__func__, __LINE__)); return; } smessage_close(p_chm->stt_msg); p_chm->stt_msg = NULL; } } /****************************************************************************** * INPUTS: context * OUTPUTS: none. * RETURNS: none * FUNCTION: message_callback * DESCRIPTION: Callback function normally called when a complete message has * been received and passed the filter criteria. ******************************************************************************/ void * chm_smessage_stt_callback(void * context, size_t size) { chm_mgr_t * p_chm; b_tm utc_time; b_timeval tv; p_chm = (chm_mgr_t*)context; p_chm->stt_cnt++; PSIP_STT_getHeader(p_chm->stt_buf, &p_chm->stt); /* current time in seconds when STT was received */ GETTIMEOFDAY(&tv); p_chm->sys_offset = tv.tv_sec; p_chm->sys_UTC_offset = p_chm->stt.GPS_UTC_offset; /* Current UTC time according to STT */ p_chm->sys_time = p_chm->stt.system_time - p_chm->stt.GPS_UTC_offset; utctime(p_chm->sys_time,&utc_time); /* Current UTC time according to STT */ p_chm->time_evt.type = eCHM_EVT_TIME; p_chm->time_evt.system_time = p_chm->sys_time; p_chm->time_evt.system_offset = tv.tv_sec; p_chm->time_evt.utc_offset = p_chm->sys_UTC_offset; /* will see if we need to post the STT? */ chm_post_app_event(p_chm,(chm_event_t*)&p_chm->time_evt); utctime(p_chm->time_evt.system_time,&utc_time); BDBG_MSG(("STT UTC = %2d/%2d/%4d %2d:%2d:%2d\n", utc_time.tm_mon + 1,utc_time.tm_mday,utc_time.tm_year + 1980, utc_time.tm_hour,utc_time.tm_min,utc_time.tm_sec)); p_chm->got_stt = true; return(void*)p_chm->stt_buf; } /* Summary: Get the STT. return non-zero on failure */ static int chm_get_stt(chm_mgr_t *p_chm) { int result = eCHM_ERR_FAILED, stt_size; b_tm utc_time; b_timeval tv; smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = p_chm->band; p_chm->msg_params.pid = (unsigned short)p_chm->network_pid; p_chm->msg_params.buffer_size = STT_BUF_LEN; stt_size = STT_BUF_LEN; p_chm->msg_params.filter.coef[0] = 0xCD; p_chm->msg_params.filter.mask[0] = 0x00; /* All PSIP tables have protocol_version which must be = 0 */ p_chm->msg_params.filter.coef[8] = 0x00; /* protocol_version */ p_chm->msg_params.filter.mask[8] = 0x00; if ((result = chm_getmessage(p_chm,p_chm->stt_buf,&stt_size, STT_TIMEOUT)) != 0) { BDBG_MSG(("Timeout waiting for STT\n")); return result; } p_chm->stt_cnt++; PSIP_STT_getHeader(p_chm->stt_buf, &p_chm->stt); /* current time in seconds when STT was received */ GETTIMEOFDAY(&tv); p_chm->sys_offset = tv.tv_sec; p_chm->sys_UTC_offset = p_chm->stt.GPS_UTC_offset; /* Current UTC time according to STT */ p_chm->sys_time = p_chm->stt.system_time - p_chm->stt.GPS_UTC_offset; utctime(p_chm->sys_time,&utc_time); /* Current UTC time according to STT */ p_chm->time_evt.type = eCHM_EVT_TIME; p_chm->time_evt.system_time = p_chm->sys_time; p_chm->time_evt.system_offset = tv.tv_sec; p_chm->time_evt.utc_offset = p_chm->sys_UTC_offset; /* will see if we need to post the STT? */ chm_post_app_event(p_chm,(chm_event_t*)&p_chm->time_evt); utctime(p_chm->time_evt.system_time,&utc_time); BDBG_MSG(("STT UTC = %2d/%2d/%4d %2d:%2d:%2d\n", utc_time.tm_mon + 1,utc_time.tm_mday,utc_time.tm_year + 1980, utc_time.tm_hour,utc_time.tm_min,utc_time.tm_sec)); p_chm->got_stt = true; return result; } /* Summary: Start the STT message filtering. return non-zero on failure */ static int chm_stt_start( chm_mgr_t *p_chm, /* Channel manager reference */ unsigned short network_pid) { int cerr; cerr = eCHM_ERR_OK; BDBG_MSG(("%s:%d (0x%04x)",__func__, __LINE__,network_pid)); chm_stt_stop(p_chm); p_chm->stt_msg = smessage_open(smessage_format_psi); if (NULL == p_chm->stt_msg) { BDBG_ERR(("%s:%d",__FILE__, __LINE__)); cerr = eCHM_ERR_FAILED; goto ExitFunc; } smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = p_chm->band; p_chm->msg_params.pid = (uint16_t)network_pid; p_chm->msg_params.filter.coef[0] = 0xCD; p_chm->msg_params.filter.mask[0] = 0x00; /* All PSIP tables have protocol_version which must be = 0 */ p_chm->msg_params.filter.coef[8] = 0x00; /* protocol_version */ p_chm->msg_params.filter.mask[8] = 0x00; p_chm->msg_params.buffer = p_chm->stt_buf; p_chm->msg_params.buffer_size = STT_BUF_LEN; /* processing is done in callback */ p_chm->msg_params.data_ready_callback = chm_smessage_stt_callback; p_chm->msg_params.overflow = NULL; p_chm->msg_params.callback_context = (void *)p_chm; if (b_ok != smessage_start(&p_chm->msg_params, p_chm->stt_msg)) { BDBG_ERR(("%s:%d",__FILE__, __LINE__)); cerr = eCHM_ERR_FAILED; } ExitFunc: return cerr; } #ifdef DEBUG_RRT static void chm_output_rrt(chm_mgr_t *p_chm) { PSIP_RRT_header header; PSIP_RRT_dimension dim; PSIP_RRT_value val; int dim_idx,val_idx; char tmp_str[64]; if (p_chm->rrt_cnt <= 0) return; PSIP_RRT_getHeader(p_chm->rrt_buf, &header); if (chm_process_mss(p_chm,header.p_rating_region_name_text,tmp_str,64) == 0) { BDBG_WRN(("RRT %8s",tmp_str)); } for (dim_idx = 0; dim_idx < header.dimensions_defined; ++dim_idx) { if (PSIP_RRT_getDimension(p_chm->rrt_buf,dim_idx,&dim) != BERR_SUCCESS) continue; if (chm_process_mss(p_chm,dim.p_dimension_name_text,tmp_str,sizeof(tmp_str)) == 0) { BDBG_WRN((" RRT[%d] %8s(%d)",dim_idx,tmp_str,dim.graduated_scale)); } for (val_idx = 0; val_idx < dim.values_defined; ++val_idx) { if (PSIP_RRT_getValue(p_chm->rrt_buf,dim_idx,val_idx,&val) != BERR_SUCCESS) continue; if (chm_process_mss(p_chm,val.p_rating_value_text,tmp_str,sizeof(tmp_str)) == 0) { BDBG_WRN((" RRT[%d,%d] %8s",dim_idx,val_idx,tmp_str)); } if (chm_process_mss(p_chm,val.p_abbrev_rating_value_text,tmp_str,sizeof(tmp_str)) == 0) { BDBG_WRN((" RRT[%d,%d] %8s",dim_idx,val_idx,tmp_str)); } } } } #endif /* * Summary: * Callback function normally called when a complete message has * been received and passed the filter criteria. */ static void * chm_smessage_rrt_callback(void * context, size_t size) { chm_mgr_t * p_chm = (chm_mgr_t*)context; bapp_t *p_app = (bapp_t*)p_chm->p_app; uint32_t crc; PSIP_RRT_header header; b_timeval tv; p_chm->rrt_cnt++; GETTIMEOFDAY(&tv); p_chm->last_rrt = tv.tv_sec; PSIP_RRT_getHeader(p_chm->rrt_buf, &header); crc = chm_section_crc(p_chm->rrt_buf, size); //if ((header.rating_region == 1) || ((size == p_app->settings.rrt_settings.rrt_size) && (memcmp(p_chm->rrt_buf,p_app->settings.rrt_settings.rrt, size) == 0))) if ((header.rating_region == 1) || ((size == p_app->settings.rrt_settings.rrt_size) && (crc == p_chm->rrt_crc))) { BDBG_WRN(("RRTs region 1 or they match so don't update\n")); p_chm->last_rrt += (2 * 60); return(void*)p_chm->rrt_buf; } p_chm->rrt_crc = crc; #ifdef DEBUG_RRT chm_output_rrt(p_chm); /* DEBUG ONLY */ #endif p_chm->rrt_evt.type = eCHM_EVT_RRT; p_chm->rrt_evt.size = size; memcpy(p_chm->rrt_evt.rrt,p_chm->rrt_buf,size); chm_post_app_event_cb(p_chm,(chm_event_t*)&p_chm->rrt_evt); return(void*)p_chm->rrt_buf; } /* Summary: Stop the RRT. return non-zero on failure */ static void chm_rrt_stop(chm_mgr_t *p_chm) { if (NULL != p_chm->rrt_msg) { if (b_ok != smessage_stop(p_chm->rrt_msg)) { BDBG_ERR(("%s:%d",__FUNCTION__, __LINE__)); return; } smessage_close(p_chm->rrt_msg); p_chm->rrt_msg = NULL; } } /* Summary: Get the RRT. return non-zero on failure */ static int chm_rrt_start(chm_mgr_t *p_chm, unsigned short network_pid) { int cerr; cerr = eCHM_ERR_OK; BDBG_MSG(("%s: (0x%04x)",__FUNCTION__, network_pid)); chm_rrt_stop(p_chm); p_chm->rrt_msg = smessage_open(smessage_format_psi); if (NULL == p_chm->rrt_msg) { BDBG_ERR(("%s: get RRT failed",__func__)); cerr = eCHM_ERR_FAILED; goto ExitFunc; } smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = p_chm->band; p_chm->msg_params.pid = (uint16_t)network_pid; p_chm->msg_params.filter.coef[0] = 0xCA; p_chm->msg_params.filter.mask[0] = 0x00; p_chm->msg_params.buffer = p_chm->rrt_buf; p_chm->msg_params.buffer_size = RRT_BUF_LEN; /* processing is done in callback */ p_chm->msg_params.data_ready_callback = chm_smessage_rrt_callback; p_chm->msg_params.overflow = NULL; p_chm->msg_params.callback_context = (void *)p_chm; if (b_ok != smessage_start(&p_chm->msg_params, p_chm->rrt_msg)) { BDBG_ERR(("%s:%d",__FILE__, __LINE__)); cerr = eCHM_ERR_FAILED; } ExitFunc: return cerr; } /* Summary: Tune to the given frequency. */ static int chm_tune_freq(chm_mgr_t *p_chm,unsigned int freq_hz) { bapp_t *p_app = (bapp_t*)p_chm->p_app; unsigned int tune_ticks; BDBG_MSG(("%s frequency=%d, last_freq_hz=%d\n",__func__, freq_hz, p_chm->last_freq_hz)); if (p_chm->force_tune) { p_chm->last_freq_hz = 0; p_chm->force_tune = false; } if (p_chm->last_freq_hz != freq_hz) { btuner_params_init(&p_chm->tuner_params, p_chm->tuner); p_chm->tuner_params.cancel_callback = chm_tune_cancel_callback; p_chm->tuner_params.cancel_callback_context = p_chm; p_chm->tuner_params.wait_for_lock = true; BDBG_MSG(("Tune to frequency = %d",freq_hz)); timing_profile_start(p_app->ptp_tune); tune_ticks = bos_getticks(); p_chm->band = btuner_tune(p_chm->tuner, freq_hz, &p_chm->tuner_params); tune_ticks = bos_getticks() - tune_ticks; timing_profile_stop(p_app->ptp_tune); p_chm->status_evt.type = eCHM_EVT_STATUS; p_chm->status_evt.id = eCHM_STATUS_TUNE_MS; p_chm->status_evt.ticks = TICKS_TO_MS(tune_ticks); chm_post_app_event(p_chm,(chm_event_t*)&p_chm->status_evt); if (p_chm->band < 0) { BDBG_MSG(("Tune failed (freq = %d)", freq_hz)); p_chm->last_freq_hz = 0; /* Send message to app */ chm_send_status(p_chm,freq_hz,false); return eCHM_ERR_FAILED; } p_chm->last_freq_hz = freq_hz; } else { BDBG_MSG(("Already tuned to frequency = %d",freq_hz)); } //RLQ, do it after calling start decode function since we don't want to switch task before channel change //chm_send_status(p_chm,freq_hz,true); return eCHM_ERR_OK; } /* Summary: Rotate audio pids. */ static int chm_rotate_audio(chm_mgr_t *p_chm) { bapp_t *p_app = (bapp_t*)p_chm->p_app; bapp_ch_t *pch; if (!p_chm->curr_stream || !p_chm->curr_stream->stream) return eCHM_ERR_FAILED; pch = &p_app->settings.ch[p_app->cur_ch_num]; BDBG_MSG(("%s from 0x%04x to 0x%04x\n",__func__,p_chm->mpeg[CURR_CH].audio[0].pid, pch->audio_pid[pch->cur_audio])); if (p_app->audio && (p_chm->mpeg[CURR_CH].audio[0].pid != 0)) { BDBG_WRN(("baudio_decode_stop 0x%04x\n",p_chm->mpeg[CURR_CH].audio[0].pid)); baudio_decode_stop(p_app->audio); } p_chm->mpeg[CURR_CH].audio[0].pid = pch->audio_pid[pch->cur_audio]; if (p_app->audio) { if (p_chm->mpeg[CURR_CH].audio[0].pid != 0) { BDBG_WRN(("baudio_decode_start 0x%04x\n",p_chm->mpeg[CURR_CH].audio[0].pid)); bstream_update_mpeg(p_chm->curr_stream->stream,&p_chm->mpeg[CURR_CH]); if (baudio_decode_start(p_app->audio, (void *)1, p_chm->curr_stream->stream)) { BDBG_WRN(("baudio_decode_start failed\n")); return eCHM_ERR_FAILED; } } } return eCHM_ERR_OK; } /* Summary: Open a stream given an mpeg program structure. */ static int chm_start_stream(chm_mgr_t *p_chm, chm_stream_t *p_stream,bstream_mpeg *p_mpeg) { BDBG_ASSERT(p_stream); BDBG_ASSERT(p_mpeg); if (p_stream->stream) { bstream_close(p_stream->stream); p_stream->stream = NULL; } p_stream->stream = bstream_open(p_chm->band,p_mpeg); if (p_stream->stream) { p_stream->freq_hz = p_chm->last_freq_hz; p_stream->pid = p_mpeg->video[0].pid; } return (p_stream->stream) ? eCHM_ERR_OK : eCHM_ERR_FAILED; } /* Summary: Close the stream. */ void chm_stop_stream(chm_mgr_t *p_chm, chm_stream_t *p_stream) { BDBG_ASSERT(p_stream); if (p_stream->stream) { bstream_close(p_stream->stream); p_stream->stream = NULL; p_stream->pid = 0; } } /* Summary: Stop unused stream. */ static void chm_stop_unused_streams(chm_mgr_t *p_chm) { int mpeg_idx,stream_idx; bool match; for (stream_idx = 0; stream_idx < BSETTOP_MAX_STREAMS; ++stream_idx) { match = false; for (mpeg_idx = 0; mpeg_idx < BSETTOP_MAX_STREAMS; ++mpeg_idx) { if (p_chm->mpeg[mpeg_idx].video[0].pid == p_chm->chm_stream[stream_idx].pid) { match = true; continue; } } if (!match) { chm_stop_stream(p_chm,&(p_chm->chm_stream[stream_idx])); } } } /* Summary: Find the stream with this PID . */ static chm_stream_t *chm_find_stream(chm_mgr_t *p_chm, unsigned short pid) { int stream_idx; BDBG_ASSERT(p_chm); for (stream_idx = 0; stream_idx < BSETTOP_MAX_STREAMS; ++stream_idx) { if (pid == p_chm->chm_stream[stream_idx].pid) { return &(p_chm->chm_stream[stream_idx]); } } return NULL; } /* Summary: Find an unused stream. */ static chm_stream_t *chm_find_unused_stream(chm_mgr_t *p_chm) { int stream_idx; BDBG_ASSERT(p_chm); for (stream_idx = 0; stream_idx < BSETTOP_MAX_STREAMS; ++stream_idx) { if (p_chm->chm_stream[stream_idx].stream == NULL) { return &(p_chm->chm_stream[stream_idx]); } } return NULL; } /* Summary: Start audio and video decode. Description: Handle tuning to the current channel. Returns non-zero on failure. */ static int chm_start_decode(chm_mgr_t *p_chm, bool force_restart) { bapp_t *p_app = (bapp_t*)p_chm->p_app; chm_stream_t *tmp_stream; if ((p_chm->mpeg[CURR_CH].audio[0].pid == 0x0000) && (p_chm->mpeg[CURR_CH].video[0].pid == 0x0000)) { BDBG_WRN(("%s (0x%04x,0x%04x,0x%04x,%d) failed \n", __func__,p_chm->mpeg[CURR_CH].video[0].pid,p_chm->mpeg[CURR_CH].pcr_pid, p_chm->mpeg[CURR_CH].audio[0].pid,p_chm->band)); return eCHM_ERR_FAILED; } if (p_chm->mpeg[CURR_CH].video[0].pid == 0x0000) { bdecode_config cfg; bdecode_get_config(p_app->decode,&cfg); cfg.mute = 1; cfg.channel_change = 0; bdecode_set_config(p_app->decode,&cfg); } /* Stop the current decode (but not the streams) */ chm_stop_decode(p_chm); /* Handle audio only decode */ if (p_app->audio && (p_chm->mpeg[CURR_CH].audio[0].pid != 0x0000) && (p_chm->mpeg[CURR_CH].video[0].pid == 0x0000)) { BDBG_WRN(("%s (0x%04x,0x%04x,0x%04x,%d) audio only decode \n", __func__,p_chm->mpeg[CURR_CH].video[0].pid,p_chm->mpeg[CURR_CH].pcr_pid, p_chm->mpeg[CURR_CH].audio[0].pid,p_chm->band)); chm_stop_stream(p_chm,&(p_chm->chm_stream[CURR_CH])); chm_stop_stream(p_chm,&(p_chm->chm_stream[PREV_CH])); chm_stop_stream(p_chm,&(p_chm->chm_stream[NEXT_CH])); if (chm_start_stream(p_chm,&(p_chm->chm_stream[CURR_CH]),&(p_chm->mpeg[CURR_CH]))) { BDBG_WRN(("chm_start_stream failed\n")); return eCHM_ERR_FAILED; } p_chm->curr_stream = &(p_chm->chm_stream[CURR_CH]); if (baudio_decode_start(p_app->audio, (void *)1, p_chm->curr_stream->stream)) { BDBG_WRN(("baudio_decode_start failed\n")); return eCHM_ERR_FAILED; } p_chm->decoding_audio = true; return eCHM_ERR_OK; } /* Check if all streams need to be restarted */ if (force_restart) { chm_stop_stream(p_chm,&(p_chm->chm_stream[CURR_CH])); chm_stop_stream(p_chm,&(p_chm->chm_stream[PREV_CH])); chm_stop_stream(p_chm,&(p_chm->chm_stream[NEXT_CH])); if (chm_start_stream(p_chm,&(p_chm->chm_stream[CURR_CH]),&(p_chm->mpeg[CURR_CH]))) { BDBG_WRN(("chm_start_stream failed\n")); return eCHM_ERR_FAILED; } p_chm->curr_stream = &(p_chm->chm_stream[CURR_CH]); if (p_chm->mpeg[NEXT_CH].video[0].pid != 0x0000) { if (chm_start_stream(p_chm,&(p_chm->chm_stream[NEXT_CH]),&(p_chm->mpeg[NEXT_CH]))) { BDBG_WRN(("chm_start_stream next failed\n")); } } if (p_chm->mpeg[PREV_CH].video[0].pid != 0x0000) { if (chm_start_stream(p_chm,&(p_chm->chm_stream[PREV_CH]),&(p_chm->mpeg[PREV_CH]))) { BDBG_WRN(("chm_start_stream prev failed\n")); } } } else { chm_stop_unused_streams(p_chm); p_chm->curr_stream = chm_find_stream(p_chm,p_chm->mpeg[CURR_CH].video[0].pid); if (p_chm->curr_stream == NULL) { if (chm_start_stream(p_chm,&(p_chm->chm_stream[CURR_CH]),&(p_chm->mpeg[CURR_CH]))) { BDBG_WRN(("chm_start_stream current failed\n")); return eCHM_ERR_FAILED; } p_chm->curr_stream = &(p_chm->chm_stream[CURR_CH]); } if (p_chm->mpeg[NEXT_CH].video[0].pid != 0x0000) { tmp_stream = chm_find_stream(p_chm,p_chm->mpeg[NEXT_CH].video[0].pid); if (tmp_stream == NULL) { tmp_stream = chm_find_unused_stream(p_chm); if (tmp_stream) { if (chm_start_stream(p_chm,tmp_stream,&(p_chm->mpeg[NEXT_CH]))) { BDBG_WRN(("chm_start_stream next failed\n")); } } } } if (p_chm->mpeg[PREV_CH].video[0].pid != 0x0000) { tmp_stream = chm_find_stream(p_chm,p_chm->mpeg[PREV_CH].video[0].pid); if (tmp_stream == NULL) { tmp_stream = chm_find_unused_stream(p_chm); if (tmp_stream) { if (chm_start_stream(p_chm,tmp_stream,&(p_chm->mpeg[PREV_CH]))) { BDBG_WRN(("chm_start_stream next failed\n")); } } } } } if (!p_chm->curr_stream) { BDBG_WRN(("%s current stream NULL\n",__func__)); return eCHM_ERR_FAILED; } BDBG_MSG(("%s(0x%04x,0x%04x,0x%04x,%d)\n", __func__,p_chm->mpeg[CURR_CH].video[0].pid,p_chm->mpeg[CURR_CH].pcr_pid, p_chm->mpeg[CURR_CH].audio[0].pid,p_chm->band)); timing_profile_start(p_app->ptp_first_pts); timing_profile_start(p_app->ptp_decode); if (bdecode_start(p_app->decode, p_chm->curr_stream->stream, p_app->window[0])) { BDBG_WRN(("bdecode_start failed\n")); return eCHM_ERR_FAILED; } p_chm->decoding_video = true; if (p_app->audio && (p_chm->mpeg[CURR_CH].audio[0].pid != 0)) { if (baudio_decode_start(p_app->audio, (void *)1, p_chm->curr_stream->stream)) { BDBG_WRN(("baudio_decode_start failed\n")); return eCHM_ERR_FAILED; } p_chm->decoding_audio = true; } return eCHM_ERR_OK; } /* Summary: Stop audio and video decode. */ static int chm_stop_decode(chm_mgr_t *p_chm) { bapp_t *p_app = (bapp_t*)p_chm->p_app; chm_eas_stop(p_chm); if (p_app->decode && p_chm->decoding_video) bdecode_stop(p_app->decode); if (p_app->audio && p_chm->decoding_audio) baudio_decode_stop(p_app->audio); p_chm->decoding_video = false; p_chm->decoding_audio = false; return 0; } /* Summary: To check if adjacent channel (prev or next have same pids as current, and it yes, don't set them Description: To check if adjacent channel (prev or next have same pids as current, and it yes, don't set them */ static int chm_pids_duplicated(chm_mgr_t *p_chm, bapp_ch_t *cur_pch, bapp_ch_t *cmp_pch) { /* some adjacent channels have same pids as current, which will cause lost audio due to duplication and XPT will be failed */ /* e.g 11.2 -> 13.1 in Mexico City has same pids on PCR and audio */ if (cur_pch->pcr_pid) { if (cmp_pch->pcr_pid == cur_pch->pcr_pid || cmp_pch->video_pid == cur_pch->pcr_pid || cmp_pch->audio_pid[cmp_pch->cur_audio] == cur_pch->pcr_pid) return true; } if (cur_pch->video_pid) { if (cmp_pch->pcr_pid == cur_pch->video_pid || cmp_pch->video_pid == cur_pch->video_pid || cmp_pch->audio_pid[cmp_pch->cur_audio] == cur_pch->video_pid) return true; } if (cur_pch->audio_pid[cur_pch->cur_audio]) { if (cmp_pch->pcr_pid == cur_pch->audio_pid[cur_pch->cur_audio] || cmp_pch->video_pid == cur_pch->audio_pid[cur_pch->cur_audio] || cmp_pch->audio_pid[cmp_pch->cur_audio] == cur_pch->audio_pid[cur_pch->cur_audio]) return true; } return false; } /* Summary: Configure mpeg with current video and audio pids Description: Tune to the current frequency and get the VCT. Returns non-zero on failure. */ static int chm_config_mpeg(chm_mgr_t *p_chm, bapp_ch_t *pch) { bapp_ch_t *tmp_pch; bapp_t *p_app = (bapp_t*)p_chm->p_app; memset(&(p_chm->mpeg[CURR_CH]),0,sizeof(bstream_mpeg)); memset(&(p_chm->mpeg[PREV_CH]),0,sizeof(bstream_mpeg)); memset(&(p_chm->mpeg[NEXT_CH]),0,sizeof(bstream_mpeg)); p_chm->mpeg[CURR_CH].pcr_pid = pch->pcr_pid; p_chm->mpeg[CURR_CH].video[0].pid = pch->video_pid; p_chm->mpeg[CURR_CH].video[0].format = pch->video_type; #if 0 /* if always reset to audio same as language */ p_chm->mpeg[CURR_CH].audio[0].pid = pch->audio_pid[p_app->lang] ? pch->audio_pid[p_app->lang] : pch->audio_pid[pch->cur_audio]; p_chm->mpeg[CURR_CH].audio[0].format = pch->audio_type[p_app->lang] ? pch->audio_type[p_app->lang] : pch->audio_type[pch->cur_audio]; #else /* if follows the user change */ p_chm->mpeg[CURR_CH].audio[0].pid = pch->audio_pid[pch->cur_audio] ? pch->audio_pid[pch->cur_audio] : pch->audio_pid[p_app->lang]; p_chm->mpeg[CURR_CH].audio[0].format = pch->audio_type[pch->cur_audio] ? pch->audio_type[pch->cur_audio] : pch->audio_type[p_app->lang]; #endif if (p_chm->mpeg[CURR_CH].audio[0].pid == 0) { BDBG_WRN(("### Current audio PID is zero ###\n")); } if (chm_ch_set_next(p_chm, &tmp_pch)) { p_chm->mpeg[NEXT_CH].video[0].pid = tmp_pch->video_pid; p_chm->mpeg[NEXT_CH].video[0].format = tmp_pch->video_type; p_chm->mpeg[NEXT_CH].pcr_pid = tmp_pch->pcr_pid; if (tmp_pch->audio_pid[p_app->lang]) { p_chm->mpeg[NEXT_CH].audio[0].pid = tmp_pch->audio_pid[p_app->lang]; p_chm->mpeg[NEXT_CH].audio[0].format = tmp_pch->audio_type[p_app->lang]; } else { p_chm->mpeg[NEXT_CH].audio[0].pid = tmp_pch->audio_pid[tmp_pch->cur_audio]; p_chm->mpeg[NEXT_CH].audio[0].format = tmp_pch->audio_type[tmp_pch->cur_audio]; } if (chm_pids_duplicated(p_chm, pch, tmp_pch)) { BDBG_WRN(("%s pids duplicated",__func__)); p_chm->mpeg[NEXT_CH].pcr_pid = 0; p_chm->mpeg[NEXT_CH].video[0].pid = 0; p_chm->mpeg[NEXT_CH].audio[0].pid = 0; } } if (chm_ch_set_prev(p_chm, &tmp_pch)) { p_chm->mpeg[PREV_CH].video[0].pid = tmp_pch->video_pid; p_chm->mpeg[PREV_CH].video[0].format = tmp_pch->video_type; p_chm->mpeg[PREV_CH].pcr_pid = tmp_pch->pcr_pid; if (tmp_pch->audio_pid[p_app->lang]) { p_chm->mpeg[PREV_CH].audio[0].pid = tmp_pch->audio_pid[p_app->lang]; p_chm->mpeg[PREV_CH].audio[0].format = tmp_pch->audio_type[p_app->lang]; } else { p_chm->mpeg[PREV_CH].audio[0].pid = tmp_pch->audio_pid[tmp_pch->cur_audio]; p_chm->mpeg[PREV_CH].audio[0].format = tmp_pch->audio_type[tmp_pch->cur_audio]; } if (chm_pids_duplicated(p_chm, pch, tmp_pch)) { BDBG_WRN(("%s pids duplicated",__func__)); p_chm->mpeg[PREV_CH].pcr_pid = 0; p_chm->mpeg[PREV_CH].video[0].pid = 0; p_chm->mpeg[PREV_CH].audio[0].pid = 0; } } //TODO, check audio pid and type for giving language return 0; } /* Summary: Tune to the current frequency and enter steady channel processing state.. Description: Tune to the current frequency and enter steady channel processing state.. */ int chm_tune(chm_mgr_t *p_chm) { bapp_t *p_app = (bapp_t*)p_chm->p_app; int result = eCHM_ERR_FAILED; bool force_restart = false; bapp_ch_t *pch; int freq; uint16_t apid; bresult rc = b_ok; if (chm_check_cancel(p_chm)) { BDBG_WRN(("%s cancel detected at line %d\n",__func__,__LINE__)); return eCHM_ERR_CANCELED; } if (!p_app->settings.num_channels) return result; chm_stop_filter_monitoring(p_chm, eCHM_MSG_FILTER_RRT | eCHM_MSG_FILTER_EAS); chm_clear_current(p_chm); /* check frequency to tune */ rc = bos_acquire_mutex(&p_chm->mutex,CHM_MUTEX_TIMEOUT); if (b_ok != rc) { BDBG_WRN(("####%s bos_acquire_mutex timeout###",__func__)); } p_chm->cur_ch_num = p_app->cur_ch_num; p_app->cur_ch = p_app->settings.ch[p_app->cur_ch_num]; if (b_ok == rc) { bos_release_mutex(&p_chm->mutex); } pch = &p_app->cur_ch; freq = pch->frequency_khz * 1000; force_restart = (freq == p_chm->last_freq_hz) ? false : true; if (force_restart) chm_stop_decode(p_chm); if (chm_tune_freq(p_chm, freq) != 0) { BDBG_MSG(("%s tune frequency %d failed", __func__, freq)); return eCHM_ERR_FAILED; } if (chm_check_cancel(p_chm)) { BDBG_WRN(("%s cancel detected at line %d\n",__func__,__LINE__)); return eCHM_ERR_CANCELED; } apid = chm_check_audio(p_chm, pch); /* if no pids exist get them */ if ((0 == pch->video_pid) && (0 == apid) && (0 == pch->pcr_pid)) { BDBG_WRN(("%s no PIDS\n",__func__)); return result; } timing_profile_start(p_app->ptp_psi); /* Do not start decode if PIDs are all 0. Doing so can prevent SI filtering from operating correctly. */ if (pch->video_pid || apid || (0 != pch->pcr_pid)) { chm_config_mpeg(p_chm,pch); if ((result = chm_start_decode(p_chm,force_restart)) != 0) { BDBG_WRN(("chm_start_decode failed\n")); timing_profile_stop(p_app->ptp_psi); return result; } } timing_profile_stop(p_app->ptp_psi); if (result == 0) { p_chm->pmt_crc = p_chm->pat_crc = 0; /* for SAP handling */ p_app->num_sap = pch->num_audio; p_app->current_sap = pch->cur_audio; chm_make_current(p_chm, &p_chm->p_info[p_app->cur_ch_num]); /* start decode success so transition to info acquire state */ p_chm->cmd = eCHM_INFO; /* alwasy get STT in case it is recorded source */ p_chm->state = eCHM_GET_STT; /* we use this count */ p_chm->mgt_cnt = 0; p_chm->got_eit = false; /* we use this to track ETT */ p_chm->got_ett = false; /* have not check PAT/PMT yet */ p_chm->first_pat_pmt = false; if (0 == p_app->settings.time_zone) { //p_chm->local_time_source = eTS_NONE; } chm_start_filter_monitoring(p_chm, eCHM_MSG_FILTER_EAS); p_chm->eas_cnt = 0; /* use this variable instead of eas_cnt to track new eas message */ p_chm->eas_reset = true; } /* RLQ */ chm_send_status(p_chm,freq,true); return result; } /* Summary: Do a tune for the purpose of checking the signal strength. */ int chm_check_signal(chm_mgr_t *p_chm) { int result = eCHM_ERR_FAILED; //btuner_status status; bapp_t *p_app = (bapp_t*)p_chm->p_app; unsigned int freq_hz = 0; if (chm_check_cancel(p_chm)) return eCHM_ERR_CANCELED; #if 0 freq_hz = p_app->cur_ch.frequency_khz * 1000; if (chm_tune_freq(p_chm, freq_hz) != 0) { p_chm->signal_evt.type = eCHM_EVT_SIGNAL; p_chm->signal_evt.power = 0; p_chm->signal_evt.SNR = 0; p_chm->signal_evt.freq_hz = freq_hz; p_chm->signal_evt.lock = 0; p_chm->signal_evt.signal_quality = 0; chm_post_app_event(p_chm,(chm_event_t*)&p_chm->signal_evt); return result; } #endif if (chm_check_cancel(p_chm)) return result; if (btuner_get_status(p_chm->tuner,&p_app->tuner_status) == b_ok) { p_chm->signal_evt.type = eCHM_EVT_SIGNAL; p_chm->signal_evt.power = p_app->tuner_status.power; p_chm->signal_evt.SNR = p_app->tuner_status.snr; p_chm->signal_evt.freq_hz = freq_hz; p_chm->signal_evt.lock = p_app->tuner_status.lock; p_chm->signal_evt.signal_quality = p_app->tuner_status.PreRS; chm_post_app_event(p_chm,(chm_event_t*)&p_chm->signal_evt); } return result; } /* Summary: Start EAS monitoring.. */ static int chm_eas_start(chm_mgr_t *p_chm, unsigned short network_pid) { int cerr = eCHM_ERR_OK; chm_eas_stop(p_chm); p_chm->eas_msg = smessage_open(smessage_format_psi); if (NULL == p_chm->eas_msg) { BDBG_ERR(("%s:%d",__FILE__, __LINE__)); cerr = eCHM_ERR_FAILED; goto ExitFunc; } smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = p_chm->band; p_chm->msg_params.pid = 0x1ffb; p_chm->msg_params.filter.coef[0] = 0xD8; p_chm->msg_params.filter.mask[0] = 0x00; //p_chm->msg_params.filter.excl[0] = 0xff; /* All PSIP tables have protocol_version which must be = 0 */ p_chm->msg_params.filter.coef[8] = 0x00; /* protocol_version */ p_chm->msg_params.filter.mask[8] = 0x00; p_chm->msg_params.buffer = p_chm->eas_buf; p_chm->msg_params.buffer_size = EAS_BUF_LEN; /* processing is done in callback */ p_chm->msg_params.data_ready_callback = chm_smessage_eas_callback; p_chm->msg_params.overflow = NULL; p_chm->msg_params.callback_context = (void *)p_chm; if (b_ok != smessage_start(&p_chm->msg_params, p_chm->eas_msg)) { BDBG_ERR(("%s:%d",__FILE__, __LINE__)); cerr = eCHM_ERR_FAILED; } p_chm->eas_timer_started = false; ExitFunc: return cerr; } /* Summary: to start filter monitoring */ void chm_start_filter_monitoring(chm_mgr_t *p_chm, chm_msg_filter_t which) { if (which & eCHM_MSG_FILTER_STT) chm_stt_start(p_chm,p_chm->network_pid); if (which & eCHM_MSG_FILTER_RRT) chm_rrt_start(p_chm,p_chm->network_pid); if (which & eCHM_MSG_FILTER_MGT) chm_mgt_start(p_chm,p_chm->network_pid); if (which & eCHM_MSG_FILTER_EAS) chm_eas_start(p_chm,p_chm->network_pid); } /* Summary: to stop filter monitoring */ void chm_stop_filter_monitoring(chm_mgr_t *p_chm, chm_msg_filter_t which) { if (which & eCHM_MSG_FILTER_STT) chm_stt_stop(p_chm); if (which & eCHM_MSG_FILTER_RRT) chm_rrt_stop(p_chm); if (which & eCHM_MSG_FILTER_MGT) chm_mgt_stop(p_chm); if (which & eCHM_MSG_FILTER_EAS) chm_eas_stop(p_chm); } /* * This routine will return valid wide_aspect_ratio for given channel's current digital caption service * * Return: * true if has valid wide_aspect_ratio for given channel's current digital caption service * false if not */ bool chm_has_valid_cc_service(chm_mgr_t *p_chm, bool *wide_aspect_ratio) { bapp_t *p_app = (bapp_t*)p_chm->p_app; uint32_t utc_time; /* if not get STT */ if (eCHM_ERR_OK != chm_get_utctime(p_chm, &utc_time)) return false; utc_time += p_chm->stt.GPS_UTC_offset; /* if now match current time, then we have valid wide_aspect_ratio information */ if ((p_app->captions_basic > 0) && ((utc_time >= (p_chm->cur_info.eit_info[EIT_CURRENT].start_time) && (utc_time <= (p_chm->cur_info.eit_info[EIT_CURRENT].start_time + p_chm->cur_info.eit_info[EIT_CURRENT].length))))) { *wide_aspect_ratio = p_chm->cur_info.eit_info[EIT_CURRENT].cc_service[p_app->captions_basic - 1].wide_aspect_ratio; return true; } return false; } /* * This routine is used to check if there is valid EIT or not. * * Return: * true if current program is completed * false otherwise */ bool chm_check_program_end_time(chm_mgr_t *p_chm) { uint32_t utc_time; unsigned int temp1,temp2; bool isTimeToTrue = false; static unsigned int old_time,Cnt_time; /* if got STT */ if (eCHM_ERR_OK == chm_get_utctime(p_chm, &utc_time)) { if (p_chm->cur_info.eit_info[EIT_CURRENT].start_time && p_chm->cur_info.eit_info[EIT_CURRENT].length) { utc_time += p_chm->stt.GPS_UTC_offset; temp1 = (p_chm->cur_info.eit_info[EIT_NEXT].start_time); temp2 = (p_chm->cur_info.eit_info[EIT_CURRENT].start_time + p_chm->cur_info.eit_info[EIT_CURRENT].length); if (utc_time >= temp1) { isTimeToTrue = true; } if (utc_time >= temp2) { isTimeToTrue = true; } } } if(isTimeToTrue) { if(Cnt_time == 0) { old_time = utc_time; Cnt_time = 1; } if(utc_time != 0 && old_time != 0) Cnt_time += (utc_time -old_time); old_time = utc_time; } else { Cnt_time = 0; old_time = utc_time; } if(Cnt_time > 6) { Cnt_time = 0; old_time = utc_time; return true; } return false; } /* * This routine is used to check if there is a need to update MGT and its associated tables * * Return: * true if need * false if not */ static bool chm_need_mgt_update(chm_mgr_t *p_chm) { uint32_t utc_time; bool need_update; bapp_t *p_app = (bapp_t*)p_chm->p_app; /* if not got STT yet */ if (eCHM_ERR_OK != chm_get_utctime(p_chm, &utc_time)) return false; utc_time += p_chm->stt.GPS_UTC_offset; /* if now does not match or have not get the ETT, then need MGT update */ need_update = (utc_time >= (p_chm->cur_info.eit_info[EIT_CURRENT].start_time + p_chm->cur_info.eit_info[EIT_CURRENT].length)); /* check if current setting is end of program? */ if (2 == p_app->settings.auto_power && need_update) { /* make sure current is valid */ if (p_chm->cur_info.eit_info[EIT_CURRENT].start_time && p_chm->cur_info.eit_info[EIT_CURRENT].length) { /* TODO confirm the logic if the user change channel around transition time? */ if (p_chm->got_eit && (p_chm->mgt_cnt > 0)) { /* current program is end, don't update to new EIT so that is can be powered off as expected */ need_update = false; } } } /* don't check program desc here as we have to deal with end of program case */ //need_update |= (0 == strlen(p_chm->cur_info.eit_info[EIT_CURRENT].prog_desc)); return need_update; } /* * Summary: * Get channel detailed program info. * Description: * Get channel detailed program info. * Returns non-zero on failure. * */ static int chm_get_prog_details(chm_mgr_t *p_chm) { bapp_t *p_app = (bapp_t*)p_chm->p_app; chm_state_t orig_state; int result = 0; //BDBG_MSG(("CHM State %s ",s_chm_state[p_chm->state])); /* check it in case we tune to a channel from CLI */ if (p_app->settings.num_channels < 1) { return 0; } orig_state = p_chm->state; if (chm_check_cancel(p_chm)) return result; switch (p_chm->state) { case eCHM_GET_STT: chm_get_stt(p_chm); p_chm->state = eCHM_GET_VCT; #ifdef CHECK_PROGRAM /* workaround for the channels that programed wrong pcr_pid */ chm_check_program(p_chm); p_chm->first_pat_pmt = true; p_chm->state = eCHM_GET_VCT; #endif break; case eCHM_GET_VCT: /* if PSI scan, don't check VCT */ if (p_app->settings.ch[p_chm->cur_ch_num].psi) { p_chm->state = eCHM_PROG_IDLE; break; } if (p_chm->local_time_source == eTS_NONE) { /* force to check VCT again until we get local_time_source */ p_chm->p_info[p_chm->cur_ch_num].got_vct = false; p_chm->got_vct = false; } if (!p_chm->p_info[p_chm->cur_ch_num].got_vct) { /* TODO using callback to process VCT to reduce channel change time */ result = chm_get_vct(p_chm, &p_chm->p_info[p_chm->cur_ch_num]); #ifdef CHECK_PROGRAM if (eCHM_ERR_TIMEOUT == result && !p_chm->first_pat_pmt) { p_chm->first_pat_pmt = true; chm_check_program(p_chm); /* chm_check_program will change state if get pat/pmt successfully, so reset the state */ p_chm->state = eCHM_GET_VCT; } #endif } else p_chm->state = eCHM_GET_MGT; break; case eCHM_GET_MGT: if (chm_need_mgt_update(p_chm)) { p_chm->got_ett = 0; p_chm->p_info->eit_info[EIT_CURRENT].prog_desc[0] = 0; result = chm_get_mgt(p_chm); } else { /* if just missing program descriptor, then just update ETT */ if (0 == strlen(p_chm->cur_info.eit_info[EIT_CURRENT].prog_desc)) { p_chm->got_ett = false; result = chm_get_mgt(p_chm); if (eCHM_ERR_OK == result) { /* just check ETT entry */ p_chm->mgt_state = eMGT_ETT; } } else { result = eCHM_ERR_OK; p_chm->state = eCHM_PROG_IDLE; } } #ifdef CHECK_PROGRAM if (eCHM_ERR_TIMEOUT == result && !p_chm->first_pat_pmt) { p_chm->first_pat_pmt = true; chm_check_program(p_chm); /* chm_check_program will change state if get pat/pmt successfully, so reset the state */ p_chm->state = eCHM_GET_MGT; } #endif break; case eCHM_PROCESS_MGT: if (eMGT_EIT == p_chm->mgt_state) { if (chm_process_mgt(p_chm, p_chm->mgt_buf,p_chm->mgt_size,&p_chm->p_info[p_chm->cur_ch_num],false,true) != 0) { BDBG_MSG(("Failed to process EIT")); p_chm->state = eCHM_PROG_IDLE; break; } else { /* we should get the valid EIT with cc_service information, update wide_aspect_ratio information */ bapp_set_wide_aspect_ratio(p_app); } } if (eMGT_ETT == p_chm->mgt_state) { if (chm_process_mgt(p_chm, p_chm->mgt_buf,p_chm->mgt_size,&p_chm->p_info[p_chm->cur_ch_num],true,true) != 0) { BDBG_WRN(("Failed to process ETT")); p_chm->state = eCHM_PROG_IDLE; } } /* a patch to fix no audio issue that no audio info given in VCT */ if ((1 == p_chm->mgt_cnt) && (0 == p_app->cur_ch.num_audio)) { /* check PAT/PMT immediately */ p_chm->state = eCHM_PROG_IDLE; } /* if transited to IDLE means we got the EIT and most likely the ETT */ if (eCHM_PROG_IDLE == p_chm->state) { chm_make_current(p_chm, &p_chm->p_info[p_chm->cur_ch_num]); } #ifndef NO_VCHIP /* indicate that we get eit information, so update current to so that it can update the Banner screen as well */ if (p_app->psip_rating_str[0]) { memcpy(&p_chm->p_info[p_chm->cur_ch_num].psip_rating_str, &p_app->psip_rating_str, sizeof(p_app->psip_rating_str)); chm_make_current(p_chm, &p_chm->p_info[p_chm->cur_ch_num]); } #endif break; case eCHM_PROG_IDLE: result = chm_prog_idle(p_chm); break; default: break; } if (orig_state != p_chm->state) { BDBG_MSG(("CHM State changed from %s to %s\n",s_chm_state[orig_state],s_chm_state[p_chm->state])); } return result; } /* * Summary: * background handling in idle time */ static int chm_prog_idle(chm_mgr_t *p_chm) { b_timeval tv; gettimeofday(&tv); if (tv.tv_sec > (p_chm->mgt_tv.tv_sec + CHM_IDLE_TIMEOUT)) { bapp_t *p_app = (bapp_t*)p_chm->p_app; #ifndef NO_VCHIP if (p_app->settings.ratings.ratings_lock == 2) { if (!p_chm->rrt_msg) { /* downloadable RRT */ chm_start_filter_monitoring(p_chm, eCHM_MSG_FILTER_RRT); } } #endif if (p_app->chm.local_time_source == eTS_NONE) { p_chm->state = eCHM_GET_VCT; p_chm->p_info[p_chm->cur_ch_num].got_vct = false; } else { if (chm_need_mgt_update(p_chm)) { p_chm->state = eCHM_GET_MGT; } else p_chm->state = eCHM_GET_STT; } /* check signal */ chm_check_signal(p_chm); } else { #if 1 static unsigned int last_signal_check = 0; static unsigned int last_prog_check = 0; if (last_prog_check < bos_getticks()) { #ifdef CHECK_PROGRAM p_chm->state = chm_check_program(p_chm); #endif last_prog_check = bos_getticks() + MS_TO_TICKS(CHM_CHECK_PROG_FREQ); } if (last_signal_check < bos_getticks()) { chm_send_status(p_chm,p_chm->last_freq_hz,true); last_signal_check = bos_getticks() + MS_TO_TICKS(CHM_CHECK_SIGNAL_FREQ); } p_chm->chmap_evt.ticks = 0; #endif } return 0; } #ifdef CHECK_PROGRAM /* Summary: check the program for changes.. */ int chm_check_program(chm_mgr_t *p_chm) { int result = eCHM_PROG_IDLE, i; bapp_t *p_app = (bapp_t*)p_chm->p_app; bapp_ch_t ch, *pch, cur_ch; unsigned int pmt_crc,pat_crc; int restart = 0, cur_ch_num; /* will use bit map */ /* serialize the update process to avoid a race condition */ if (b_ok != bos_acquire_mutex(&p_chm->mutex,CHM_MUTEX_TIMEOUT)) { BDBG_WRN(("bos_acquire_mutex failed")); return eCHM_PROG_IDLE; } /* verify the cur_ch_num? */ if (p_chm->cur_ch_num != p_app->cur_ch_num) { /* race condiction? */ BDBG_WRN(("%d, cur_ch_num not match, race condition?", __func__)); bos_release_mutex(&p_chm->mutex); return eCHM_PROG_IDLE; } /* remember current PAT and PMT CRC so that we know if PAT/PMT changes or not */ pmt_crc = p_chm->pmt_crc; pat_crc = p_chm->pat_crc; cur_ch_num = p_chm->cur_ch_num; /* make a local copy instead use p_app->cur_ch*/ //pch = &p_app->cur_ch; memcpy(&cur_ch, &p_app->settings.ch[cur_ch_num], sizeof(bapp_ch_t)); pch = &cur_ch; /* initalize it here and also assign the program_num with mutex protection to avoid possible race condition */ memset(&ch, 0, sizeof(ch)); ch.program_num = pch->program_num; bos_release_mutex(&p_chm->mutex); BDBG_MSG(("***** chm_check_program *****")); if ((result = chm_get_program(p_chm, &ch)) != b_ok) { BDBG_WRN(("chm_get_program failed %d",result)); return eCHM_PROG_IDLE; /* return state expected */ } /* Check if pat/pmt changed */ if ((pat_crc == p_chm->pat_crc) && (pmt_crc == p_chm->pmt_crc)) return eCHM_PROG_IDLE; /* serialize the update process to avoid a race condition */ if (b_ok != bos_acquire_mutex(&p_chm->mutex,CHM_MUTEX_TIMEOUT)) { BDBG_WRN(("bos_acquire_mutex failed")); return eCHM_PROG_IDLE; } if (ch.pcr_pid && (ch.pcr_pid != pch->pcr_pid)) { BDBG_WRN(("PCR PID changed (new 0x%04x, current 0x%04x)",ch.pcr_pid, pch->pcr_pid)); pch->pcr_pid = ch.pcr_pid; pch->psi = true; restart |= 0x01; /* need to restart video */ } if (ch.video_pid && ((ch.video_pid != pch->video_pid) || (ch.video_type != pch->video_type))) { if (ch.video_pid != pch->video_pid) { BDBG_WRN(("Video PID changed (new 0x%04x, current 0x%04x)",ch.video_pid, pch->video_pid)); pch->video_pid = ch.video_pid; } if (ch.video_type != pch->video_type) { BDBG_WRN(("Video type changed (new 0x%04x, current 0x%04x)",ch.video_type, pch->video_type)); pch->video_type = ch.video_type; } restart |= 0x01; /* need to restart video */ pch->psi = true; } for (i = 0; i < MAX_AUDIO_PIDS && i < ch.num_audio; i++) { if (ch.audio_pid[i] && ((ch.audio_pid[i] != pch->audio_pid[i]) || (ch.audio_type[i] != pch->audio_type[i]))) { if (ch.audio_pid != pch->audio_pid) { BDBG_WRN(("Audio PID[%d] changed (new 0x%04x, current 0x%04x)", i, ch.audio_pid[i], pch->audio_pid[i])); pch->audio_pid[i] = ch.audio_pid[i]; if (i == pch->cur_audio) restart |= 0x02; /* need to restart audio */ } if (ch.audio_type != pch->audio_type) { BDBG_WRN(("Audio type [%d] changed (new 0x%04x, current 0x%04x)", i, ch.audio_type[i], pch->audio_type[i])); pch->audio_type[i] = ch.audio_type[i]; if (i == pch->cur_audio) restart |= 0x02; /* need to restart audio */ } memcpy(&pch->audio_lang[i][0],&ch.audio_lang[i][0], 3); pch->psi = true; } } if (ch.num_audio != pch->num_audio || ch.has_lang != pch->has_lang || ch.cur_audio != pch->cur_audio) { pch->num_audio = ch.num_audio; p_app->num_sap = ch.num_audio; /* need to update this as well so that it can select audio channel from UI */ pch->has_lang = ch.has_lang; pch->cur_audio = ch.cur_audio; } /* if changed, resync the changes */ if (0 != memcmp(&p_app->settings.ch[cur_ch_num], pch, sizeof(bapp_ch_t))) { p_app->settings.ch[cur_ch_num] = *pch; } bos_release_mutex(&p_chm->mutex); if (restart) { chm_config_mpeg(p_chm, pch); if (0x01 & restart) { result = chm_start_decode(p_chm,true); } else { /* if video PID/PCR PID are not changed, then just restart audio to avoid video flushing */ bstream_update_mpeg(p_chm->curr_stream->stream,&p_chm->mpeg[CURR_CH]); result = baudio_decode_start(p_app->audio, (void *)1, p_chm->curr_stream->stream); } if (result) { BDBG_WRN(("%s %s failed\n",__func__, (0x01 & restart) ? "chm_start_decode" : "baudio_decode_start")); } /* sync with current cur_ch as some of process refer to that cur_ch structure */ p_app->cur_ch = p_app->settings.ch[cur_ch_num]; } return eCHM_PROG_IDLE; } #endif /*CHECK_PROGRAM*/ /* Summary: Channel Manager Task. */ static void chm_handler(void *data) { chm_cmd_t orig_cmd; chm_mgr_t *p_chm = (chm_mgr_t *)data; chm_cmd_event_t *p_cmd; while (1) { orig_cmd = p_chm->cmd; switch (p_chm->cmd) { case eCHM_CANCEL: p_cmd = (chm_cmd_event_t*)bos_pend_event(p_chm->queue,-1); if (p_cmd == NULL) break; p_chm->cmd = (chm_cmd_t)p_cmd->cmd_id; break; case eCHM_SAP: p_chm->cmd = eCHM_CANCEL; break; case eCHM_STOP: chm_stop_decode(p_chm); p_chm->cmd = eCHM_CANCEL; break; case eCHM_TUNE: chm_tune(p_chm); break; case eCHM_HUNT: chm_hunt(p_chm); break; case eCHM_GUIDE: chm_get_guide(p_chm); break; case eCHM_INFO: chm_get_prog_details(p_chm); break; case eCHM_CHECK_SIGNAL: chm_check_signal(p_chm); break; } if (orig_cmd != p_chm->cmd) { BDBG_WRN(("CHM CMD changed from %s to %s\n", s_chm_cmd_name[orig_cmd],s_chm_cmd_name[p_chm->cmd])); } /* Yield a little */ bos_sleep(40); } } /* Summary: Initialize the channel manager. */ void chm_init(chm_mgr_t *p_chm, void *p_app) { b_task_params params; bresult rc; memset(p_chm,0,sizeof(chm_mgr_t)); p_chm->p_app = p_app; /* for NTIA project */ #ifdef CONFIG_ISDB p_chm->fe_type = eFREQ_TABLE_ISDB; #else p_chm->fe_type = eFREQ_TABLE_NTIA_VSB; #endif rc = bos_create_queue(&p_chm->queue,p_chm->event,MAX_CHM_EVENT); BDBG_ASSERT(rc == b_ok); rc = bos_create_queue(&p_chm->gen_queue,p_chm->gen_event,MAX_MSG_EVENTS); BDBG_ASSERT(rc == b_ok); rc = bos_create_mutex(&p_chm->mutex); BDBG_ASSERT(rc == b_ok); rc = bos_create_mutex(&p_chm->ch_map_mutex); BDBG_ASSERT(rc == b_ok); /* will remove ch_map related functions and variables */ //ch_map_init(&(p_chm->ch_map),&(p_chm->ch_map_mutex)); /* round to nearest chm_info_t size */ p_chm->max_info_num = MAX_CHANNELS; p_chm->p_info = (chm_info_t*)malloc(p_chm->max_info_num * sizeof(chm_info_t)); BDBG_ASSERT(p_chm->p_info); memset(p_chm->p_info,0,p_chm->max_info_num * sizeof(chm_info_t)); BDBG_MSG(("Maximum guide entries = %d.\n",p_chm->max_info_num)); p_chm->stack = malloc(CHM_INT_STK_SIZE * 4); BDBG_ASSERT(p_chm->stack); params.priority = CHM_INT_PRIORITY; params.stack = p_chm->stack; params.name = "chan_mgr"; params.stack_size = CHM_INT_STK_SIZE; p_chm->pat_buf = malloc(PAT_BUF_LEN); p_chm->pmt_buf = malloc(PMT_BUF_LEN); p_chm->stt_buf = malloc(STT_BUF_LEN); p_chm->vct_buf = malloc(VCT_BUF_LEN); p_chm->eit_buf = malloc(EIT_BUF_LEN); p_chm->ett_buf = malloc(ETT_BUF_LEN); p_chm->mgt_buf = malloc(MGT_BUF_LEN); p_chm->eas_buf = malloc(EAS_BUF_LEN); BDBG_ASSERT(p_chm->pat_buf && p_chm->pmt_buf && p_chm->mgt_buf && p_chm->eit_buf && p_chm->stt_buf && p_chm->vct_buf && p_chm->eas_buf && p_chm->ett_buf); bstream_mpeg_init(&p_chm->mpeg[CURR_CH]); bstream_mpeg_init(&p_chm->mpeg[NEXT_CH]); bstream_mpeg_init(&p_chm->mpeg[PREV_CH]); p_chm->tuner = btuner_open((0)); BAPP_ASSERT(p_chm->tuner); p_chm->smsg = smessage_open(smessage_format_psi); BAPP_ASSERT(p_chm->smsg); p_chm->vendor_id = DEF_VENDOR_ID; p_chm->hardware_version_id = DEF_HARDWARE_ID; p_chm->cmd = eCHM_CANCEL; p_chm->hunt_cnt = 0; /* set default network pid */ p_chm->network_pid = 0x1ffb; bos_start_task(&p_chm->task,¶ms,chm_handler,p_chm); #ifdef BCM_DEBUG BDBG_SetModuleLevel("chan_mgr",BDBG_eWrn); #endif } /* Summary: Function to pass a command to the channel manager. Any results and notification is handled by passing events to the app via the msg_queue. */ void chm_cmd(chm_mgr_t *p_chm,chm_cmd_event_t *p_cmd_evt) { chm_cmd_t cmd; BAPP_ASSERT(p_chm); BAPP_ASSERT(p_cmd_evt); cmd = p_cmd_evt->cmd_id; /* Handle special case to rotate audio */ if ((cmd == eCHM_SAP) && (p_chm->cmd != eCHM_CANCEL)) { BDBG_WRN(("Command %s, during %s\n",s_chm_cmd_name[cmd], s_chm_cmd_name[p_chm->cmd])); bos_post_event(p_chm->queue,(b_event_t*)p_cmd_evt); return; } if ((cmd != eCHM_CANCEL) && (p_chm->cmd != eCHM_CANCEL)) { BDBG_WRN(("Canceling previous command %s for %s\n", s_chm_cmd_name[p_chm->cmd],s_chm_cmd_name[cmd])); p_chm->cancel_cmd.cmd_id = eCHM_CANCEL; bos_post_event(p_chm->queue,(b_event_t*)&p_chm->cancel_cmd); bos_sleep(40); /* yield so thread can process event right away */ } bos_post_event(p_chm->queue,(b_event_t*)p_cmd_evt); bos_sleep(10); /* yield so thread can process event right away */ } bresult chm_write_software(struct image_t * image) { return image_write(image); } #define CHM_SC_MASK 0xc0 #define CHM_SC_BUFFER_SIZE (188*10) static unsigned char chm_sc_buffer[CHM_SC_BUFFER_SIZE]; static void * chm_sc_callback(void * c, size_t msg_size) { unsigned int * pdata_ready = (unsigned int *)c; *pdata_ready = msg_size; return NULL; } bool chm_check_sc(chm_mgr_t *p_chm, bband_t band, unsigned short pid) { smessage_stream_t ms; smessage_stream_params_t params; bresult bres; bool result; unsigned int count; volatile unsigned int data_ready; unsigned char * walker; result = false; ms = smessage_open(smessage_format_tsc); if (NULL == ms) { BDBG_ERR(("%s : smessage_open failed", __PRETTY_FUNCTION__)); goto ExitFunc; } smessage_stream_params_init(¶ms, NULL); params.band = band; params.pid = pid; params.buffer_size = CHM_SC_BUFFER_SIZE; params.buffer = chm_sc_buffer; params.data_ready_callback = chm_sc_callback; params.overflow = chm_sc_callback; params.callback_context = (void *)&data_ready; data_ready = 0; bres = smessage_start(¶ms, ms); if (b_ok != bres) { smessage_close(ms); BDBG_ERR(("%s : smessage_start", __PRETTY_FUNCTION__)); goto ExitFunc; } /* Updating unsigned int is atomic operation on mips. We can just wait for callback to update our flag when data is ready */ count = 3; do { bos_sleep(50); count--; }while ((0 == data_ready)&&(0 != count)); bres = smessage_stop(ms); if (b_ok != bres) { smessage_close(ms); BDBG_ERR(("%s : smessage_stop", __PRETTY_FUNCTION__)); goto ExitFunc; } smessage_close(ms); /* Figure out if we got data and if sc flags are set */ if (0 != data_ready) { /* We have data. Check few packets for sc bits */ walker = chm_sc_buffer; for (count = 0; count < 5; count++) { if (0 != (walker[3] & CHM_SC_MASK)) { goto ExitFunc; } walker += 188; } result = true; } ExitFunc: BDBG_ERR(("pid %d %s", pid, (result == true ? "clear" : "encr"))); return result; } #ifndef NO_VCHIP #define BAPP_MPAA_RATING_STR_NUM 8 static const char *bapp_mpa_rating_str[] = { "", /* unrated */ "G", "PG", "PG-13", "R", "NC-17", "X", "NR" /* Not Rated */ }; #define BAPP_TV_RATING_STR_NUM 8 static const char *bapp_tv_rating_str[BAPP_TV_RATING_STR_NUM] = { "", /* unrated */ "TV-Y", "TV-Y7", "TV-G", "TV-PG", "TV-14", "TV-MA", "" /* unrated */ }; /* Summary: Use dimension and value to make a ratings decision based on the static ratings configuration in the UI. This function assumes region matches currently received RRT or current region == 1 (USA). */ static int chm_psip_block(bapp_t *p_app, unsigned char region,unsigned char dim,unsigned char val, char *rating_str) { int blocked = 0; int pos = 0; BDBG_MSG(("%s - region:%d\n", __func__, region)); /* handle RRT ratings */ /* only take effect if ratings is set for downloadable level */ if ((p_app->settings.rrt_settings.rrt_status == eRRT_AVAILABLE) && (region != 1) && (2 == p_app->settings.ratings.ratings_lock)) { if (p_app->settings.rrt_settings.ratings_rrt[dim] & (1 << val)) blocked |= 1; } BDBG_WRN(("RRT psip rating check - rrt dim:%d rrt val:%d blocked:%d\n", dim, val, blocked)); if ((region == 1) && (!blocked))/* US ratings are fixed */ { BDBG_WRN(("%s (movie:%x tv:%x) dim:%d val:%d\n",__func__,p_app->settings.ratings.ratings_movies,p_app->settings.ratings.ratings_tv,dim,val)); if (val == 0) { BDBG_WRN(("US PSIP rating value = 0 so assume unblocked!\n")); return 0; } if (dim == 7) /* check settings for MPAA ratings */ { pos = 0; /* map to xds values */ if (val > 0) val--; p_app->movie_rating = val; pos = screen_ratings_movie_xds2pos(p_app->movie_rating); /* update rating string */ if (rating_str) { if (val < BAPP_MPAA_RATING_STR_NUM) strcpy(rating_str, bapp_mpa_rating_str[val]); } if (pos >= 0) blocked |= ((p_app->settings.ratings.ratings_movies >> pos) & (0 | (0x1))); } else /* check settings for TV ratings */ { switch (dim) { case 0: /* entire audience */ { int pos = 0; /* map to xds values */ if (val <= 1) { /* none (1) mapped to unrated */ p_app->tv_rating = 0; } else { /* TV-G, TV-PG, TV-14, and TV-MA map to corresponding XDS TV ratings */ p_app->tv_rating = val + 1; } pos = screen_ratings_tv_xds2pos(p_app->tv_rating); if (rating_str) { if (p_app->tv_rating < BAPP_TV_RATING_STR_NUM) strcpy(rating_str, bapp_tv_rating_str[p_app->tv_rating]); } if (pos >= 0) blocked |= ((p_app->settings.ratings.ratings_tv >> pos) & (0 | (0x1))); } break; case 1: /* dialogue */ { if (val == 0) break; pos = screen_ratings_tv_xds2pos(p_app->tv_rating); if (pos == ePOS_TV_14) blocked |= ((p_app->settings.ratings.ratings_tv >> ePOS_TV_14_D) & (0 | (0x1))); else if (pos == ePOS_TV_PG) blocked |= ((p_app->settings.ratings.ratings_tv >> ePOS_TV_PG_D) & (0 | (0x1))); if (rating_str) { strcpy(rating_str, "D"); } } break; case 2: /* language */ { if (val == 0) break; pos = screen_ratings_tv_xds2pos(p_app->tv_rating); if (pos == ePOS_TV_MA) blocked |= ((p_app->settings.ratings.ratings_tv >> ePOS_TV_MA_L) & (0 | (0x1))); else if (pos == ePOS_TV_14) blocked |= ((p_app->settings.ratings.ratings_tv >> ePOS_TV_14_L) & (0 | (0x1))); else if (pos == ePOS_TV_PG) blocked |= ((p_app->settings.ratings.ratings_tv >> ePOS_TV_PG_L) & (0 | (0x1))); if (rating_str) { strcpy(rating_str, "L"); } } break; case 3: /* sex */ { if (val == 0) break; pos = screen_ratings_tv_xds2pos(p_app->tv_rating); if (pos == ePOS_TV_MA) blocked |= ((p_app->settings.ratings.ratings_tv >> ePOS_TV_MA_S) & (0 | (0x1))); else if (pos == ePOS_TV_14) blocked |= ((p_app->settings.ratings.ratings_tv >> ePOS_TV_14_S) & (0 | (0x1))); else if (pos == ePOS_TV_PG) blocked |= ((p_app->settings.ratings.ratings_tv >> ePOS_TV_PG_S) & (0 | (0x1))); if (rating_str) { strcpy(rating_str, "S"); } } break; case 4: /* violence */ { if (val == 0) break; pos = screen_ratings_tv_xds2pos(p_app->tv_rating); if (pos == ePOS_TV_MA) blocked |= ((p_app->settings.ratings.ratings_tv >> ePOS_TV_MA_V) & (0 | (0x1))); else if (pos == ePOS_TV_14) blocked |= ((p_app->settings.ratings.ratings_tv >> ePOS_TV_14_V) & (0 | (0x1))); else if (pos == ePOS_TV_PG) blocked |= ((p_app->settings.ratings.ratings_tv >> ePOS_TV_PG_V) & (0 | (0x1))); if (rating_str) { strcpy(rating_str, "V"); } } break; case 5: /* children */ { if (val == 0) break; pos = 0; /* TV-Y7 and TV-Y map to corresponding XDS TV ratings */ p_app->tv_rating = val; pos = screen_ratings_tv_xds2pos(p_app->tv_rating); if (rating_str) { if (p_app->tv_rating < BAPP_TV_RATING_STR_NUM) strcpy(rating_str, bapp_tv_rating_str[p_app->tv_rating]); } if (pos >= 0) blocked |= ((p_app->settings.ratings.ratings_tv >> pos) & (0 | (0x1))); } break; case 6: /* fantasy violence */ { if (val == 0) break; pos = screen_ratings_tv_xds2pos(p_app->tv_rating); if (pos == ePOS_TV_Y7) blocked |= ((p_app->settings.ratings.ratings_tv >> ePOS_TV_Y7_FV) & (0 | (0x1))); if (rating_str) { strcpy(rating_str, "FV"); } } break; default: break; } } } if (p_app->settings.ratings.ratings_lock == 0) blocked = 0; return blocked; } #endif /* Summary: Get the current utc time in seconds. Return non-zero on failure. */ static int chm_get_utctime(chm_mgr_t *p_chm, unsigned int *time ) { b_timeval tv; if (!p_chm->got_stt) return eCHM_ERR_FAILED; GETTIMEOFDAY(&tv); *time = p_chm->sys_time + (tv.tv_sec - p_chm->sys_offset); return 0; } /* Summary: Get the current UTC time, local offset and dst flag. Returns non-zero when it is not possible to determine local time. Currently this function uses XDS local offset over one obtained from the TSIDs */ unsigned int g_xds_time_offset = 0; bool g_xds_dst = false; int chm_get_time_info(chm_mgr_t *p_chm, unsigned int *utc_secs, /* Current UTC time in seconds */ int *local_offset, /* Local time offset from UTC time in seconds */ bool *dst /* Daylight savings flag */ ) { int result; if ((result = chm_get_utctime(p_chm,utc_secs)) != 0) return result; /* Use XDS local time over time obtained from a TSID */ if (g_xds_time_offset != 0) { p_chm->local_offset = 3600 * g_xds_time_offset; p_chm->local_time_source = eTS_XDS; p_chm->local_dst_obs = g_xds_dst; } if (p_chm->local_time_source == eTS_NONE) return eCHM_ERR_FAILED; *local_offset = p_chm->local_offset; *dst = ((p_chm->stt.daylight_savings.DS_status != 0) && p_chm->local_dst_obs) ? true : false; BDBG_MSG(("STT system_time = %u\n",p_chm->stt.system_time)); BDBG_MSG(("STT GPS_UTC_offset = %u\n",p_chm->stt.GPS_UTC_offset)); BDBG_MSG(("STT DS_status = %d\n",p_chm->stt.daylight_savings.DS_status)); if (*dst) { *utc_secs += 3600; } return 0; } static void chm_post_progress(chm_mgr_t *p_chm, int cnt) { bapp_t *p_app = (bapp_t*)p_chm->p_app; if (p_app->settings.num_channels > 0) { p_chm->redraw_evt.type = eCHM_EVT_PROGRESS; cnt %= p_app->settings.num_channels; p_chm->redraw_evt.id = (100 * cnt) / p_app->settings.num_channels; chm_post_app_event(p_chm,&p_chm->redraw_evt); bos_sleep(10); } } /* Summary: Get program guide info. Description: Get program guide info in Program List screen Returns non-zero on failure. */ static int chm_get_guide(chm_mgr_t *p_chm) { unsigned int utc_secs,local_offset,idx, freq; bool dst; bapp_t *p_app = (bapp_t*)p_chm->p_app; chm_info_t *p_info; b_timeval tv; int num_channels; chm_stop_decode(p_chm); /* Check to see if scan was canceled */ if (chm_check_cancel(p_chm)) return eCHM_ERR_FAILED; if (p_app->settings.num_channels < 1) { /* if no channels */ p_chm->cmd = eCHM_CANCEL; return eCHM_ERR_OK; } num_channels = p_app->settings.num_channels; /* Completely reset and start over */ BDBG_MSG(("To get Guide for all %d channels",num_channels)); if ((chm_get_time_info(p_chm,&utc_secs,&local_offset,&dst) != 0) || (p_chm->local_time_source == eTS_NONE)) { BDBG_WRN(("Need local time and UTC offset before guide can be displayed with time info.\n")); } gettimeofday(&tv); if (tv.tv_sec > (p_chm->guide_tv.tv_sec + (60 * 15))) { /*RLQ, TODO check if EIT is valid and reset only invalid one */ for (idx = 0; idx < num_channels; ++idx) { p_info = &p_chm->p_info[idx]; memset(p_info,0,sizeof(chm_info_t)); p_chm->redraw_evt.type = eCHM_EVT_REDRAW; p_chm->redraw_evt.id = idx; p_chm->redraw_evt.ticks = 1; chm_post_app_event(p_chm,&p_chm->redraw_evt); //bos_sleep(50); } } gettimeofday(&p_chm->guide_tv); p_chm->cur_ch_num = p_app->chm.epg_update_ch_num; while (p_chm->cmd == eCHM_GUIDE) { if (p_chm->cur_ch_num >= num_channels) { p_chm->cur_ch_num = 0; } BDBG_MSG(("Get program info for CH %d of %d, vpid=0x%04x, apid=0x%04x",p_chm->cur_ch_num,num_channels, p_app->settings.ch[p_chm->cur_ch_num].video_pid, p_app->settings.ch[p_chm->cur_ch_num].audio_pid[0])); /* Check to see if scan was canceled */ if (chm_check_cancel(p_chm)) { BDBG_WRN(("Get Guide canceled")); break; } freq = p_app->settings.ch[p_chm->cur_ch_num].frequency_khz * 1000; if (freq != p_chm->last_freq_hz) { if (chm_tune_freq(p_chm, freq) != 0) { BDBG_WRN(("Tune to channel[%d] %d.%d failed\n",p_chm->cur_ch_num, p_app->settings.ch[p_chm->cur_ch_num].major,p_app->settings.ch[p_chm->cur_ch_num].minor)); goto POST_PROG; } } #if 0 /* Some channels are sending STT correctly for DST and others do not. To work around this get STT for each channel */ if (chm_get_stt(p_chm)) { goto POST_PROG; } #endif p_info = &p_chm->p_info[p_chm->cur_ch_num]; BDBG_MSG(("### Get VCT")); if (!p_info->got_vct && eCHM_ERR_OK != chm_get_vct(p_chm, p_info)) { BDBG_MSG(("Failed to get VCT for guide")); goto POST_PROG; } BDBG_MSG(("### Get MGT for channel %d\n", p_chm->cur_ch_num)); if (eCHM_ERR_OK != chm_get_mgt(p_chm)) { BDBG_WRN(("Failed to get MGT for guide")); goto POST_PROG; } BDBG_MSG(("### Process MGT ###")); p_chm->mgt_state = eMGT_EIT; chm_process_mgt(p_chm, p_chm->mgt_buf,p_chm->mgt_size,p_info,false,true); POST_PROG: chm_post_progress(p_chm, p_chm->cur_ch_num); /* if updated from GUIDE UI by the user, resync it */ if (p_chm->epg_update_ch_num != p_chm->cur_ch_num) { p_chm->cur_ch_num = p_chm->epg_update_ch_num; } p_chm->cur_ch_num++; p_chm->cur_ch_num %= num_channels; p_chm->epg_update_ch_num = p_chm->cur_ch_num; /* rescan ? */ if (p_app->settings.num_channels != num_channels) break; } /* leave guide state */ p_chm->cmd = eCHM_CANCEL; return 0; } /* * Summary: * Get a copy of the current channel manager info structure. * * */ void chm_get_cur_ch_info( chm_mgr_t *p_chm, chm_info_t *p_info, bstream_mpeg *p_mpeg) { bapp_t *p_app = (bapp_t*)p_chm->p_app; memset(p_info,0,sizeof(chm_info_t)); memset(p_mpeg,0,sizeof(bstream_mpeg)); if (bos_acquire_mutex(&p_chm->mutex,CHM_MUTEX_TIMEOUT) == b_ok) { *p_mpeg = p_chm->mpeg[CURR_CH]; memcpy(&p_chm->cur_info,&p_chm->p_info[p_app->cur_ch_num],sizeof(chm_info_t)); *p_info = p_chm->cur_info; bos_release_mutex(&p_chm->mutex); } else { BDBG_WRN(("%s mutex timeout\n", __func__)); } } #ifdef CONFIG_NXP_TDA182I4 void NXP_TDA182I4_SetStandby() { NXP_TDA182I4_SetPowerState(); return; } #endif #ifdef TUNER_DEBUG /* Summary: Scan for channels using PSI for given frequency Description: Scan for channels using PSI. Returns: Number of channels added Or error code if < 0 */ int chm_scan_psi_a_freq( chm_mgr_t *p_chm, /* Channel manager reference */ unsigned int freq_khz) { bapp_t *p_app = (bapp_t*)p_chm->p_app; unsigned int section_size, pmt_section_size; TS_PMT_stream pmt; TS_PAT_program pat; int result = 0; int idx,p_idx,tresult, desc_idx = 0, programs, freq_idx = -1; TS_PSI_descriptor psi_desc; unsigned short ca_pid = 0, minor = 1; bapp_ch_t ch; /* stop decoder */ chm_stop_decode(p_chm); p_chm->cmd = eCHM_CANCEL; bos_sleep(200); if (eFREQ_TABLE_USER_DEFINED == p_chm->fe_type) bapp_user_freq_table_get(p_app, &p_chm->freq_table, &p_chm->num_freq); else bapp_freq_table_get(p_chm->fe_type,(const unsigned int **)&(p_chm->freq_table),&(p_chm->num_freq)); /* search for the frequency from the frequency list */ for (idx = 0; idx < p_chm->num_freq; idx++) { if (freq_khz == (int)p_chm->freq_table[idx]) { freq_idx = idx; break; } } /* can't find the given frequency */ if (idx >= p_chm->num_freq) return -1; /* clear channel map for a new scan */ p_app->settings.num_channels = 0; memset(p_app->settings.ch, 0, sizeof(p_app->settings.ch)); memset(p_chm->p_info,0,p_chm->num_freq * sizeof(chm_info_t)); p_chm->cmd = eCHM_CANCEL; bos_sleep(200); /* tuning to given frequency */ btuner_params_init(&p_chm->tuner_params, p_chm->tuner); //p_chm->tuner_params.cancel_callback = chm_tune_cancel_callback; //p_chm->tuner_params.cancel_callback_context = p_chm; p_chm->tuner_params.wait_for_lock = true; if (btuner_tune(p_chm->tuner, freq_khz * 1000, &p_chm->tuner_params) < 0) { BDBG_WRN(("### tune freqency %d failed\n", freq_khz * 1000)); return -1; } p_chm->band = TUNER_BAND; p_chm->select_idx = freq_idx; p_chm->not_check_cancel = true; result = chm_scan_vct(p_chm,p_chm->band,p_chm->select_idx); p_chm->not_check_cancel = false; if (result > 0) { BDBG_WRN(("### Got VCT")); /* we got the channels, set first channel to start after a new scan */ p_app->cur_ch_num = 0; return result; } BDBG_WRN(("### VCT scan on frequency %d failed, perform PSI scan instead\n", freq_khz * 1000)); /* Get the PAT */ smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = p_chm->band; p_chm->msg_params.pid = (unsigned short)0; /* PAT */ /* All PSIP tables have protocol_version which must be = 0 */ //p_chm->msg_params.filter.coef[8] = 0x00; /* protocol_version */ //p_chm->msg_params.filter.mask[8] = 0x00; section_size = PAT_BUF_LEN; if ((result = chm_getmessage(p_chm,p_chm->pat_buf,§ion_size, PAT_TIMEOUT)) != 0) { BDBG_WRN(("%s Timeout getting PAT\n",__func__)); return result; } if (TS_PAT_validate(p_chm->pat_buf, section_size) != true) { BDBG_WRN(("TS_PAT_validate failed\n")); return eCHM_ERR_FAILED; } programs = TS_PAT_getNumPrograms(p_chm->pat_buf); BDBG_MSG(("Programs %d in PAT", programs)); if (0 == programs) { BDBG_WRN(("%s TS_PAT_getNumPrograms = 0,section_size = %d\n",__func__,section_size)); return eCHM_ERR_FAILED; } p_chm->pat_crc = chm_section_crc(p_chm->pat_buf,section_size); for (idx = 0; idx < programs; idx++) { section_size = PAT_BUF_LEN; if ((tresult = TS_PAT_getProgram( p_chm->pat_buf, section_size, idx, &pat )) != b_ok) { BDBG_WRN(("TS_PAT_getProgram failed 0x%x", tresult)); return eCHM_ERR_FAILED; } smessage_stream_params_init(&p_chm->msg_params, NULL); p_chm->msg_params.band = p_chm->band; p_chm->msg_params.pid = (unsigned short)pat.PID; /* PMT */ /* All PSIP tables have protocol_version which must be = 0 */ //p_chm->msg_params.filter.coef[8] = 0x00; /* protocol_version */ //p_chm->msg_params.filter.mask[8] = 0x00; pmt_section_size = PMT_BUF_LEN; if ((tresult = chm_getmessage(p_chm,p_chm->pmt_buf,&pmt_section_size, PMT_TIMEOUT * 2)) != 0) { BDBG_WRN(("Failed getting PMT 0x%04x\n",pat.PID)); continue; } p_chm->pmt_crc = chm_section_crc(p_chm->pmt_buf,pmt_section_size); memset(&ch,0,sizeof(ch)); ch.major = (freq_idx == 0x7F) ? 0 : freq_idx + 2; /* freq_idx is off by 2 */ ch.minor = minor++; ch.freq_idx = freq_idx; ch.pcr_pid = TS_PMT_getPcrPid(p_chm->pmt_buf,pmt_section_size); ch.program_num = pat.program_number; ch.psi = true; /* we got this entry using PSI scan vs VCT */ /* Get CA pid if */ while ((psi_desc = TS_PMT_getDescriptor(p_chm->pmt_buf, pmt_section_size,desc_idx++)) != NULL) { if (desc_idx > 0xFF) { BDBG_ERR(("PMT Desc limit exceeded %d\n",desc_idx)); break; } if (psi_desc[0] == 9) { ca_pid = (((unsigned short)(psi_desc[4] & 0x1F)) << 8) | (unsigned short)psi_desc[5]; BDBG_MSG(("CA PID = 0x%04x\n",ca_pid)); break; } } if (ca_pid != 0x0000) { BDBG_MSG(("Program has CA PID 0x%02x check for scrambling control bit.\n",ca_pid)); } for (p_idx = 0; p_idx < TS_PMT_getNumStreams(p_chm->pmt_buf,pmt_section_size); p_idx++) { bsettop_av_stream_type_t *stream_type; if (TS_PMT_getStream( p_chm->pmt_buf, pmt_section_size, p_idx, &pmt ) != b_ok) { BDBG_WRN(("Failure processing PMT %d\n",p_idx)); return eCHM_ERR_FAILED; } if ((stream_type = bdecode_supported_video(pmt.stream_type)) != NULL) { BDBG_MSG(("Video PID[%d] = 0x%04x\n",p_idx,pmt.elementary_PID )); if (!ch.video_pid) { ch.video_pid = pmt.elementary_PID; ch.video_type = stream_type->codec_id; } } else if ((stream_type = bdecode_supported_audio(pmt.stream_type)) != NULL) { BDBG_MSG(("Audio PID[%d] = 0x%04x\n",ch.num_audio,pmt.elementary_PID )); if (!ch.audio_pid[ch.num_audio] && (ch.num_audio < MAX_AUDIO_PIDS)) { ch.audio_pid[ch.num_audio] = pmt.elementary_PID; ch.audio_type[ch.num_audio] = stream_type->codec_id; if (0 == ch.num_audio) ch.cur_audio = 0; ch.num_audio++; } } } if (ch.video_pid || ch.audio_pid[0]) { bool clear = true; unsigned short pid; /* if has video, use video first */ if (ch.video_pid) pid = ch.video_pid; else pid = ch.audio_pid[0]; /* correct pid in case video pid carries too few frames */ if(ch.pcr_pid == ch.audio_pid[0]) pid = ch.audio_pid[0]; if (ca_pid != 0x0000) clear = chm_check_sc(p_chm, p_chm->band, pid); if (clear) { #if 0 p_chm->chm_pat_evt.type = eCHM_EVT_STATUS; p_chm->chm_pat_evt.id = eCHM_STATUS_PAT; p_chm->chm_pat_evt.ticks = 0; chm_post_app_event(p_chm,&p_chm->chm_pat_evt); #endif ch.frequency_khz = freq_khz; chm_add_vsb_channel(p_chm,&ch); result += 1; #if 0 /* in the chm_scan_psi, it will post STATUS_PAT. but sometimes the event is missed.. don't know why.. */ if (p_app->screen_id == eSCREEN_CH_SCAN_PROGRESS) { bos_sleep(100); } #endif } } } if (result > 0) { /* first channel to start after a new scan */ p_app->cur_ch_num = 0; } return result; } #endif