/** @brief Reads the program map table * * It's used to get the differents "useful" pids of the channel * @param pmt the pmt packet * @param channel the associated channel */ int autoconf_read_pmt(mumudvb_ts_packet_t *pmt, mumudvb_channel_t *channel, char *card_base_path, int tuner, uint8_t *asked_pid, uint8_t *number_chan_asked_pid,fds_t *fds) { int section_len, descr_section_len, i,j; int pid; int pid_type; int found=0; pmt_t *header; pmt_info_t *descr_header; int program_info_length; int channel_update; //For channel update int temp_pids[MAX_PIDS]; int temp_pids_type[MAX_PIDS]; char temp_pids_language[MAX_PIDS][4]; //For channel update int temp_num_pids=0; pid_type=0; section_len=pmt->len_full; header=(pmt_t *)pmt->data_full; if(header->table_id!=0x02) { log_message( log_module, MSG_INFO,"Packet PID %d for channel \"%s\" is not a PMT PID. We remove the pmt pid for this channel\n", pmt->pid, channel->name); channel->pmt_pid=0; /** @todo : put a threshold, */ return 1; } //We check if this PMT belongs to the current channel. (Only works with autoconfiguration full for the moment because it stores the service_id) if(channel->service_id && (channel->service_id != HILO(header->program_number)) ) { log_message( log_module, MSG_DETAIL,"The PMT %d does not belongs to channel \"%s\"\n", pmt->pid, channel->name); return 1; } /*current_next_indicator – A 1-bit indicator, which when set to '1' indicates that the Program Association Table sent is currently applicable. When the bit is set to '0', it indicates that the table sent is not yet applicable and shall be the next table to become valid.*/ if(header->current_next_indicator == 0) { log_message( log_module, MSG_DEBUG,"The current_next_indicator is set to 0, this PMT is not valid for the current stream\n"); return 1; } log_message( log_module, MSG_DEBUG,"PMT (PID %d) read for autoconfiguration of channel \"%s\"\n", pmt->pid, channel->name); channel_update=channel->num_pids>1?1:0; if(channel_update) { // Update log_message( log_module, MSG_INFO,"Channel %s update\n",channel->name); temp_pids[0]=pmt->pid; temp_num_pids++; #ifdef ENABLE_CAM_SUPPORT // Reset of the CA SYS saved for the chanel for (i=0; i<32; i++) channel->ca_sys_id[i]=0; #endif } program_info_length=HILO(header->program_info_length); //program_info_length char language[4]=""; int pos=0; //we read the different descriptors included in the pmt //for more information see ITU-T Rec. H.222.0 | ISO/IEC 13818 table 2-34 for (i=program_info_length+PMT_LEN; i<=section_len-(PMT_INFO_LEN+4); i+=descr_section_len+PMT_INFO_LEN) { //we parse the part after the descriptors //we map the descriptor header descr_header=(pmt_info_t *)(pmt->data_full+i); //We get the length of the descriptor descr_section_len=HILO(descr_header->ES_info_length); //ES_info_length // Default language value if not found snprintf(language,4,"%s","---"); // Default, no position found pos=0; pid=HILO(descr_header->elementary_PID); //Depending of the stream type we'll take or not this pid switch(descr_header->stream_type) { case 0x01: pid_type=PID_VIDEO_MPEG1; log_message( log_module, MSG_DEBUG," Video MPEG1 \tpid %d\n",pid); break; case 0x02: pid_type=PID_VIDEO_MPEG2; log_message( log_module, MSG_DEBUG," Video MPEG2 \tpid %d\n",pid); break; case 0x10: /* ISO/IEC 14496-2 Visual - MPEG4 video */ pid_type=PID_VIDEO_MPEG4_ASP; log_message( log_module, MSG_DEBUG," Video MPEG4-ASP \tpid %d\n",pid); break; case 0x1b: /* AVC video stream as defined in ITU-T Rec. H.264 | ISO/IEC 14496-10 Video */ pid_type=PID_VIDEO_MPEG4_AVC; log_message( log_module, MSG_DEBUG," Video MPEG4-AVC \tpid %d\n",pid); break; case 0x03: pid_type=PID_AUDIO_MPEG1; log_message( log_module, MSG_DEBUG," Audio MPEG1 \tpid %d\n",pid); break; case 0x04: pid_type=PID_AUDIO_MPEG2; log_message( log_module, MSG_DEBUG," Audio MPEG2 \tpid %d\n",pid); break; case 0x11: /* ISO/IEC 14496-3 Audio with the LATM transport syntax as defined in ISO/IEC 14496-3 */ pid_type=PID_AUDIO_AAC_LATM; log_message( log_module, MSG_DEBUG," Audio AAC-LATM \tpid %d\n",pid); break; case 0x0f: /* ISO/IEC 13818-7 Audio with ADTS transport syntax - usually AAC */ pid_type=PID_AUDIO_AAC_ADTS; log_message( log_module, MSG_DEBUG," Audio AAC-ADTS \tpid %d\n",pid); break; case 0x81: /* Audio per ATSC A/53B [2] Annex B */ pid_type=PID_AUDIO_ATSC; log_message( log_module, MSG_DEBUG," Audio ATSC A/53B \tpid %d\n",pid); break; case 0x06: /* Descriptor defined in EN 300 468 */ if(descr_section_len) //If we have an accociated descriptor, we'll search inforation in it { if(pmt_find_descriptor(0x45,pmt->data_full+i+PMT_INFO_LEN,descr_section_len, NULL)){ log_message( log_module, MSG_DEBUG," VBI Data \tpid %d\n",pid); pid_type=PID_EXTRA_VBIDATA; }else if(pmt_find_descriptor(0x46,pmt->data_full+i+PMT_INFO_LEN,descr_section_len, NULL)){ log_message( log_module, MSG_DEBUG," VBI Teletext \tpid %d\n",pid); pid_type=PID_EXTRA_VBITELETEXT; }else if(pmt_find_descriptor(0x56,pmt->data_full+i+PMT_INFO_LEN,descr_section_len, NULL)){ log_message( log_module, MSG_DEBUG," Teletext \tpid %d\n",pid); pid_type=PID_EXTRA_TELETEXT; }else if(pmt_find_descriptor(0x59,pmt->data_full+i+PMT_INFO_LEN,descr_section_len, &pos)){ log_message( log_module, MSG_DEBUG," Subtitling \tpid %d\n",pid); pid_type=PID_EXTRA_SUBTITLE; char * lng=(char *)(pmt->data_full+i+PMT_INFO_LEN+pos+2); language[0]=lng[0]; language[1]=lng[1]; language[2]=lng[2]; language[3]=0; }else if(pmt_find_descriptor(0x6a,pmt->data_full+i+PMT_INFO_LEN,descr_section_len, NULL)){ log_message( log_module, MSG_DEBUG," AC3 (audio) \tpid %d\n",pid); pid_type=PID_AUDIO_AC3; }else if(pmt_find_descriptor(0x7a,pmt->data_full+i+PMT_INFO_LEN,descr_section_len, NULL)){ log_message( log_module, MSG_DEBUG," Enhanced AC3 (audio) \tpid %d\n",pid); pid_type=PID_AUDIO_EAC3; }else if(pmt_find_descriptor(0x7b,pmt->data_full+i+PMT_INFO_LEN,descr_section_len, NULL)){ log_message( log_module, MSG_DEBUG," DTS (audio) \tpid %d\n",pid); pid_type=PID_AUDIO_DTS; }else if(pmt_find_descriptor(0x7c,pmt->data_full+i+PMT_INFO_LEN,descr_section_len, NULL)){ log_message( log_module, MSG_DEBUG," AAC (audio) \tpid %d\n",pid); pid_type=PID_AUDIO_AAC; }else { log_message( log_module, MSG_DEBUG,"Unknown descriptor see EN 300 468 v1.9.1 table 12, pid %d descriptor tags : ", pid); pmt_print_descriptor_tags(pmt->data_full+i+PMT_INFO_LEN,descr_section_len); log_message( log_module, MSG_DEBUG,"\n"); continue; } } else { log_message( log_module, MSG_DEBUG,"PMT read : stream type 0x06 without descriptor\n"); continue; } break; //Now, the list of what we drop case 0x05: log_message( log_module, MSG_DEBUG, "Dropped pid %d, type : 0x05, ITU-T Rec. H.222.0 | ISO/IEC 13818-1 private_sections \n",pid); continue; //Digital Storage Medium Command and Control (DSM-CC) cf H.222.0 | ISO/IEC 13818-1 annex B case 0x0a: log_message( log_module, MSG_DEBUG, "Dropped pid %d, type : 0x0A ISO/IEC 13818-6 type A (DSM-CC)\n",pid); continue; case 0x0b: log_message( log_module, MSG_DEBUG, "Dropped pid %d, type : 0x0B ISO/IEC 13818-6 type B (DSM-CC)\n",pid); continue; case 0x0c: log_message( log_module, MSG_DEBUG, "Dropped pid %d, type : 0x0C ISO/IEC 13818-6 type C (DSM-CC)\n",pid); continue; case 0x0D: log_message( log_module, MSG_DEBUG, "Dropped pid %d, type : ISO/IEC 13818-6 type D",pid); continue; case 0x0E: log_message( log_module, MSG_DEBUG, "Dropped pid %d, type : ITU-T Rec. H.222.0 | ISO/IEC 13818-1 auxiliary",pid); continue; case 0x12: log_message( log_module, MSG_DEBUG, "Dropped pid %d, type : ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in PES packets",pid); continue; case 0x13: log_message( log_module, MSG_DEBUG, "Dropped pid %d, type : ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in ISO/IEC 14496_sections",pid); continue; case 0x14: log_message( log_module, MSG_DEBUG, "Dropped pid %d, type : ISO/IEC 13818-6 Synchronized Download Protocol",pid); continue; case 0x15: log_message( log_module, MSG_DEBUG, "Dropped pid %d, type : Metadata carried in PES packets",pid); continue; case 0x16: log_message( log_module, MSG_DEBUG, "Dropped pid %d, type : Metadata carried in metadata_sections",pid); continue; case 0x17: log_message( log_module, MSG_DEBUG, "Dropped pid %d, type : Metadata carried in ISO/IEC 13818-6 Data Carousel",pid); continue; case 0x18: log_message( log_module, MSG_DEBUG, "Dropped pid %d, type : Metadata carried in ISO/IEC 13818-6 Object Carousel",pid); continue; case 0x19: log_message( log_module, MSG_DEBUG, "Dropped pid %d, type : Metadata carried in ISO/IEC 13818-6 Synchronized Download Protocol",pid); continue; case 0x1A: log_message( log_module, MSG_DEBUG, "Dropped pid %d, type : IPMP stream (defined in ISO/IEC 13818-11, MPEG-2 IPMP)",pid); continue; case 0x7F: log_message( log_module, MSG_DEBUG, "Dropped pid %d, type : IPMP stream",pid); continue; default: if(descr_header->stream_type >= 0x1C && descr_header->stream_type <= 0x7E) log_message( log_module, MSG_DEBUG, "Dropped pid %d, stream type : 0x%02x : ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved",pid,descr_header->stream_type); else if(descr_header->stream_type >= 0x80) log_message( log_module, MSG_DEBUG, "Dropped pid %d, stream type : 0x%02x : User Private",pid,descr_header->stream_type); else log_message( log_module, MSG_INFO, "!!!!Unknown stream type : 0x%02x, PID : %d cf ITU-T Rec. H.222.0 | ISO/IEC 13818\n",descr_header->stream_type,pid); continue; } //We keep this pid // We try to find a 0x0a (ISO639) descriptor to have language information about the stream pos=0; if(pmt_find_descriptor(0x0a,pmt->data_full+i+PMT_INFO_LEN,descr_section_len, &pos)){ char * lng=(char *)(pmt->data_full+i+PMT_INFO_LEN+pos+2); language[0]=lng[0]; language[1]=lng[1]; language[2]=lng[2]; language[3]=0; } log_message( log_module, MSG_DEBUG," PID Language Code = %s\n",language); //For cam debugging purposes, we look if we can find a ca descriptor to display ca system ids if(descr_section_len) { int pos; int casysid; pos=0; while(pmt_find_descriptor(0x09,pmt->data_full+i+PMT_INFO_LEN,descr_section_len,&pos)) { descr_ca_t *ca_descriptor; ca_descriptor=(descr_ca_t *)(pmt->data_full+i+PMT_INFO_LEN+pos); casysid=0; while(casysid<32 && channel->ca_sys_id[casysid] && channel->ca_sys_id[casysid]!=HILO(ca_descriptor->CA_type) ) casysid++; if(casysid<32 && !channel->ca_sys_id[casysid]) { channel->ca_sys_id[casysid]=HILO(ca_descriptor->CA_type); log_message( log_module, MSG_DETAIL,"Ca system id 0x%04x : %s\n", HILO(ca_descriptor->CA_type), ca_sys_id_to_str(HILO(ca_descriptor->CA_type)));//we display it with the description } if(casysid==32) log_message( log_module, MSG_WARN,"Too much Ca system id line %d file %s\n", __LINE__,__FILE__); pos+=ca_descriptor->descriptor_length+2; } } if(channel_update) { temp_pids[temp_num_pids]=pid; temp_pids_type[temp_num_pids]=pid_type; snprintf(temp_pids_language[temp_num_pids],4,"%s",language); temp_num_pids++; if (temp_num_pids >= MAX_PIDS) { log_message( log_module, MSG_ERROR, "Too many PIDs : %d\n", temp_num_pids); temp_num_pids--; } } else { channel->pids[channel->num_pids]=pid; channel->pids_type[channel->num_pids]=pid_type; snprintf(channel->pids_language[channel->num_pids],4,"%s",language); channel->num_pids++; } } /************************** * PCR PID **************************/ channel->pcr_pid=HILO(header->PCR_PID); //The PCR pid. //we check if it's not already included (ie the pcr is carried with the video) found=0; for(i=0;i<channel->num_pids;i++) { if((channel_update && temp_pids[i]==channel->pcr_pid) || (!channel_update && channel->pids[i]==channel->pcr_pid)) found=1; } if(!found) { if(channel_update) { temp_pids[temp_num_pids]=channel->pcr_pid; temp_pids_type[temp_num_pids]=PID_PCR; snprintf(temp_pids_language[temp_num_pids],4,"%s","---"); temp_num_pids++; } else { channel->pids[channel->num_pids]=channel->pcr_pid; channel->pids_type[channel->num_pids]=PID_PCR; snprintf(channel->pids_language[channel->num_pids],4,"%s","---"); channel->num_pids++; } } log_message( log_module, MSG_DEBUG, "PCR pid %d\n",channel->pcr_pid); /************************** * PCR PID - END **************************/ //We store the PMT version useful to check for updates channel->pmt_version=header->version_number; /************************** * Channel update **************************/ //If it's a channel update we will have to update the filters if(channel_update) { log_message( log_module, MSG_DEBUG,"Channel update new number of pids %d old %d we check for changes\n", temp_num_pids, channel->num_pids); //We search for added pids for(i=0;i<temp_num_pids;i++) { found=0; for(j=0;j<channel->num_pids;j++) { if(channel->pids[j]==temp_pids[i]) found=1; } if(!found) { log_message( log_module, MSG_DETAIL, "Update : pid %d added \n",temp_pids[i]); //If the pid is not on the list we add it for the filters if(asked_pid[temp_pids[i]]==PID_NOT_ASKED) asked_pid[temp_pids[i]]=PID_ASKED; number_chan_asked_pid[temp_pids[i]]++; channel->pids[channel->num_pids]=temp_pids[i]; channel->pids_type[channel->num_pids]=temp_pids_type[i]; snprintf(channel->pids_language[channel->num_pids],4,"%s",temp_pids_language[i]); channel->num_pids++; log_message( log_module, MSG_DETAIL,"Add the new filters\n"); // we open the file descriptors if (create_card_fd (card_base_path, tuner, asked_pid, fds) < 0) { log_message( log_module, MSG_ERROR,"CANNOT open the new descriptors. Some channels will probably not work\n"); //return; //FIXME : what do we do here ? } //open the new filters set_filters(asked_pid, fds); } } //We search for suppressed pids for(i=0;i<channel->num_pids;i++) { found=0; for(j=0;j<temp_num_pids;j++) { if(channel->pids[i]==temp_pids[j]) found=1; } if(!found) { log_message( log_module, MSG_DETAIL, "Update : pid %d supressed \n",channel->pids[i]); //We check the number of channels on wich this pid is registered, if 0 it's strange we warn if((channel->pids[i]>MAX_MANDATORY_PID_NUMBER )&& (number_chan_asked_pid[channel->pids[i]])) { //We decrease the number of channels with this pid number_chan_asked_pid[channel->pids[i]]--; //If no channel need this pid anymore, we remove the filter (closing the file descriptor remove the filter associated) if(number_chan_asked_pid[channel->pids[i]]==0) { log_message( log_module, MSG_DEBUG, "Update : pid %d does not belong to any channel anymore, we close the filter \n",channel->pids[i]); close(fds->fd_demuxer[channel->pids[i]]); fds->fd_demuxer[channel->pids[i]]=0; asked_pid[channel->pids[i]]=PID_NOT_ASKED; } } else log_message( log_module, MSG_WARN, "Update : We tried to suppress pid %d in a strange way, please contact if you can reproduce\n",channel->pids[i]); //We remove the pid from this channel by swapping with the last one and decreasing the pid number channel->pids[i]=channel->pids[channel->num_pids-1]; channel->num_pids--; i--; //we wan to check the pid just moved so we force the loop to reaxamine pid i } } log_message( log_module, MSG_DETAIL, " pids : \n");/**@todo Generate a strind and call log_message after, in syslog it generates one line per pid : use the toolbox unicast*/ int ipid; for (ipid = 0; ipid < channel->num_pids; ipid++) log_message( log_module, MSG_DETAIL, " %d (%s) \n", channel->pids[ipid], pid_type_to_str(channel->pids_type[ipid])); } /** @todo : update generated conf file*/ /************************** * Channel update END **************************/ /************************* * Language template **************************/ found =0; int len=MAX_NAME_LEN; for(i=0;i<channel->num_pids && !found;i++) { if(channel->pids_language[i][0]!='-') { log_message( log_module, MSG_FLOOD, "Primary language for channel: %s",channel->pids_language[i]); mumu_string_replace(channel->name,&len,0,"%lang",channel->pids_language[i]); found=1; //we exit the loop } } //If we don't find a lang we replace by our "usual" --- if(!found) mumu_string_replace(channel->name,&len,0,"%lang",channel->pids_language[0]); /************************* * Language template END **************************/ log_message( log_module, MSG_DEBUG,"Number of pids after autoconf %d\n", channel->num_pids); return 0; }
/** @brief : List the DVB cards of the system and their capabilities * */ void list_dvb_cards () { DIR *dvb_dir; log_message( log_module, MSG_INFO,"=================================="); log_message( log_module, MSG_INFO," DVB CARDS LISTING\n"); log_message( log_module, MSG_INFO,"=================================="); dvb_dir = opendir ("/dev/dvb/"); if (dvb_dir == NULL) { log_message( log_module, MSG_ERROR, "Cannot open /dev/dvb : %s\n", strerror (errno)); return; } int card_number; int num_cards; int cards[256]; num_cards=0; struct dirent *d_adapter; while ((d_adapter=readdir(dvb_dir))!=NULL) { if(strlen(d_adapter->d_name)<8) continue; if(strncmp(d_adapter->d_name,"adapter",7)) continue; card_number= atoi(d_adapter->d_name+7); log_message( log_module, MSG_DEBUG, "found adapter %d\n", card_number); cards[num_cards]=card_number; num_cards++; if(num_cards==256) { log_message( log_module, MSG_ERROR, "Wow You have a system with more than 256 DVB cards, Please Contact me :D\n"); return; } } closedir(dvb_dir); //Basic card sorting (O(N^2)) int i,j,old_card; old_card=-1; DIR *adapter_dir; /** The path of the card */ char card_dev_path[256]; int tuner_number; struct dirent *d_tuner; for(i=0;i<num_cards;i++) { card_number=-1; for(j=0;j<num_cards;j++) if((card_number<=old_card)||((cards[j]>old_card) && (cards[j]<card_number))) card_number=cards[j]; old_card=card_number; strncpy(card_dev_path,DVB_DEV_PATH,256); char number[10]; sprintf(number,"%d",card_number); int l=sizeof(card_dev_path); mumu_string_replace(card_dev_path,&l,0,"%card",number); adapter_dir = opendir (card_dev_path); if (adapter_dir == NULL) { log_message( log_module, MSG_ERROR, "Cannot open %s : %s\n", card_dev_path, strerror (errno)); return; } while ((d_tuner=readdir(adapter_dir))!=NULL) { if(strlen(d_tuner->d_name)<(strlen(FRONTEND_DEV_NAME)+1)) continue; if(strncmp(d_tuner->d_name,FRONTEND_DEV_NAME,strlen(FRONTEND_DEV_NAME))) continue; tuner_number= atoi(d_tuner->d_name+strlen(FRONTEND_DEV_NAME)); log_message( log_module, MSG_DEBUG, "\tCard %d found Frontend %d\n", card_number, tuner_number); /** show the current tuner */ show_card_capabilities( card_number , tuner_number); } closedir(adapter_dir); } }
/** @brief : List the capabilities of one card * * */ void show_card_capabilities( int card, int tuner ) { int frontend_fd; int i_ret; int display_sr; int frequency_factor; /** The path of the card */ char card_dev_path[256]; strncpy(card_dev_path,DVB_DEV_PATH,256); char number[10]; sprintf(number,"%d",card); int l=sizeof(card_dev_path); mumu_string_replace(card_dev_path,&l,0,"%card",number); //Open the frontend if(!open_fe (&frontend_fd, card_dev_path, tuner,0)) // we open the card readonly so we can get the capabilities event when used return; //get frontend info struct dvb_frontend_info fe_info; if ( (i_ret = ioctl(frontend_fd,FE_GET_INFO, &fe_info) < 0)){ log_message( log_module, MSG_ERROR, "FE_GET_INFO: %s \n", strerror(errno)); return; } log_message( log_module, MSG_INFO, "=========== Card %d - Tuner %d ===========\n", card, tuner); log_message( log_module, MSG_INFO, " Frontend : %s\n", fe_info.name); display_sr=0; switch(fe_info.type) { case FE_OFDM: log_message( log_module, MSG_INFO, " Terrestrial (DVB-T) card\n"); break; case FE_QPSK: log_message( log_module, MSG_INFO, " Satellite (DVB-S) card\n"); display_sr=1; break; case FE_QAM: log_message( log_module, MSG_INFO, " Cable (DVB-C) card\n"); display_sr=1; break; case FE_ATSC: log_message( log_module, MSG_INFO, " ATSC card\n"); break; } if(fe_info.type==FE_QPSK) frequency_factor=1000; else frequency_factor=1; if (frequency_factor!=0) log_message( log_module, MSG_INFO, " Frequency: %d kHz to %d kHz\n",(int) fe_info.frequency_min/1000*frequency_factor,(int) fe_info.frequency_max/1000*frequency_factor); else log_message( log_module, MSG_WARN, " frequency_factor=0\n"); if(display_sr) log_message( log_module, MSG_INFO, " Symbol rate: %d k symbols/s to %d k symbols/s \n", (int)fe_info.symbol_rate_min/1000, (int)fe_info.symbol_rate_max/1000); log_message( log_module, MSG_DETAIL, " == Card capabilities =="); log_message( log_module, MSG_DEBUG, "caps 0x%x\n",fe_info.caps); frontend_cap_t caps[]={ {0x1,"FE_CAN_INVERSION_AUTO"}, {0x2,"FE_CAN_FEC_1_2"}, {0x4,"FE_CAN_FEC_2_3"}, {0x8,"FE_CAN_FEC_3_4"}, {0x10,"FE_CAN_FEC_4_5"}, {0x20,"FE_CAN_FEC_5_6"}, {0x40,"FE_CAN_FEC_6_7"}, {0x80,"FE_CAN_FEC_7_8"}, {0x100,"FE_CAN_FEC_8_9"}, {0x200,"FE_CAN_FEC_AUTO"}, {0x400,"FE_CAN_QPSK"}, {0x800,"FE_CAN_QAM_16"}, {0x1000,"FE_CAN_QAM_32"}, {0x2000,"FE_CAN_QAM_64"}, {0x4000,"FE_CAN_QAM_128"}, {0x8000,"FE_CAN_QAM_256"}, {0x10000,"FE_CAN_QAM_AUTO"}, {0x20000,"FE_CAN_TRANSMISSION_MODE_AUTO"}, {0x40000,"FE_CAN_BANDWIDTH_AUTO"}, {0x80000,"FE_CAN_GUARD_INTERVAL_AUTO"}, {0x100000,"FE_CAN_HIERARCHY_AUTO"}, {0x200000,"FE_CAN_8VSB"}, {0x400000,"FE_CAN_16VSB"}, {0x800000,"FE_HAS_EXTENDED_CAPS /* We need more bitspace for newer APIs, indicate this. */"}, {0x10000000,"FE_CAN_2G_MODULATION /* frontend supports '2nd generation modulation' (DVB-S2) */"}, {0x20000000,"FE_NEEDS_BENDING /* not supported anymore */"}, {0x40000000,"FE_CAN_RECOVER /* frontend can recover from a cable unplug automatically */"}, {0x80000000,"FE_CAN_MUTE_TS /* frontend can stop spurious TS data output */"}, }; int numcaps=28; int i; for(i=0;i<numcaps;i++) if(fe_info.caps & caps[i].flag) log_message( log_module, MSG_DETAIL, "%s\n", caps[i].descr); close (frontend_fd); log_message( log_module, MSG_INFO, "\n"); }
/** @brief This function is called when a new packet is there and the autoconf is not finished*/ int autoconf_new_packet(int pid, unsigned char *ts_packet, autoconf_parameters_t *autoconf_vars, fds_t *fds, mumudvb_chan_and_pids_t *chan_and_pids, tuning_parameters_t *tuneparams, multicast_parameters_t *multicast_vars, unicast_parameters_t *unicast_vars, int server_id) { int iRet=0; if(autoconf_vars->autoconfiguration==AUTOCONF_MODE_FULL) //Full autoconfiguration, we search the channels and their names { if(pid==0) //PAT : contains the services identifiers and the PMT PID for each service { if(get_ts_packet(ts_packet,autoconf_vars->autoconf_temp_pat)) { if(autoconf_read_pat(autoconf_vars)) { log_message( log_module, MSG_DEBUG,"It seems that we have finished to get the services list\n"); //we finish full autoconfiguration iRet = autoconf_finish_full(chan_and_pids, autoconf_vars, multicast_vars, tuneparams, fds, unicast_vars, server_id); } } } else if(pid==17) //SDT : contains the names of the services { if(get_ts_packet(ts_packet,autoconf_vars->autoconf_temp_sdt)) { autoconf_read_sdt(autoconf_vars->autoconf_temp_sdt->data_full,autoconf_vars->autoconf_temp_sdt->len_full,autoconf_vars->services); } } else if(pid==PSIP_PID && tuneparams->fe_type==FE_ATSC) //PSIP : contains the names of the services { if(get_ts_packet(ts_packet,autoconf_vars->autoconf_temp_psip)) { autoconf_read_psip(autoconf_vars); } } } else if(autoconf_vars->autoconfiguration==AUTOCONF_MODE_PIDS) //We have the channels and their PMT, we search the other pids { int curr_channel; for(curr_channel=0;curr_channel<MAX_CHANNELS;curr_channel++) { if((!chan_and_pids->channels[curr_channel].autoconfigurated) &&(chan_and_pids->channels[curr_channel].pmt_pid==pid)&& pid) { if(get_ts_packet(ts_packet,chan_and_pids->channels[curr_channel].pmt_packet)) { //Now we have the PMT, we parse it if(autoconf_read_pmt(chan_and_pids->channels[curr_channel].pmt_packet, &chan_and_pids->channels[curr_channel], tuneparams->card_dev_path, tuneparams->tuner, chan_and_pids->asked_pid, chan_and_pids->number_chan_asked_pid, fds)==0) { log_pids(log_module,&chan_and_pids->channels[curr_channel],curr_channel); chan_and_pids->channels[curr_channel].autoconfigurated=1; //We parse the NIT before finishing autoconfiguration autoconf_vars->autoconfiguration=AUTOCONF_MODE_NIT; for (curr_channel = 0; curr_channel < chan_and_pids->number_of_channels; curr_channel++) if(!chan_and_pids->channels[curr_channel].autoconfigurated) autoconf_vars->autoconfiguration=AUTOCONF_MODE_PIDS; //not finished we continue //if it's finished, we open the new descriptors and add the new filters if(autoconf_vars->autoconfiguration!=AUTOCONF_MODE_PIDS) { autoconf_set_channel_filt(tuneparams->card_dev_path, tuneparams->tuner, chan_and_pids, fds); //We free autoconf memory autoconf_freeing(autoconf_vars); if(autoconf_vars->autoconfiguration==AUTOCONF_MODE_NIT) log_message( log_module, MSG_DETAIL,"We search for the NIT\n"); else autoconf_definite_end(tuneparams->card, tuneparams->tuner, chan_and_pids, multicast_vars, unicast_vars); } } } } } } else if(autoconf_vars->autoconfiguration==AUTOCONF_MODE_NIT) //We search the NIT { if(pid==16) //NIT : Network Information Table { if(get_ts_packet(ts_packet,autoconf_vars->autoconf_temp_nit)) { log_message( log_module, MSG_FLOOD,"New NIT\n"); if(autoconf_read_nit(autoconf_vars, chan_and_pids->channels, chan_and_pids->number_of_channels)==0) { autoconf_vars->autoconfiguration=0; int curr_channel; char lcn[4]; int len=MAX_NAME_LEN; for(curr_channel=0;curr_channel<MAX_CHANNELS;curr_channel++) { if(chan_and_pids->channels[curr_channel].logical_channel_number) { sprintf(lcn,"%03d",chan_and_pids->channels[curr_channel].logical_channel_number); mumu_string_replace(chan_and_pids->channels[curr_channel].name,&len,0,"%lcn",lcn); sprintf(lcn,"%02d",chan_and_pids->channels[curr_channel].logical_channel_number); mumu_string_replace(chan_and_pids->channels[curr_channel].name,&len,0,"%2lcn",lcn); } else { mumu_string_replace(chan_and_pids->channels[curr_channel].name,&len,0,"%lcn",""); mumu_string_replace(chan_and_pids->channels[curr_channel].name,&len,0,"%2lcn",""); } } free(autoconf_vars->autoconf_temp_nit); autoconf_vars->autoconf_temp_nit=NULL; autoconf_definite_end(tuneparams->card, tuneparams->tuner, chan_and_pids, multicast_vars, unicast_vars); } } } } return Interrupted; }
/** @brief Convert the chained list of services into channels * * This function is called when We've got all the services, we now fill the channels structure * After that we go in AUTOCONF_MODE_PIDS to get audio and video pids * @param parameters The autoconf parameters * @param channels Chained list of channels * @param port The mulicast port * @param card The card number for the ip address * @param unicast_vars The unicast parameters * @param fds The file descriptors (for filters and unicast) */ int autoconf_services_to_channels(autoconf_parameters_t parameters, mumudvb_channel_t *channels, int port, int card, int tuner, unicast_parameters_t *unicast_vars, multicast_parameters_t *multicast_vars, int server_id) { mumudvb_service_t *actual_service; int channel_number=0; int found_in_service_id_list; int unicast_port_per_channel; char tempstring[256]; actual_service=parameters.services; unicast_port_per_channel=strlen(parameters.autoconf_unicast_port)?1:0; do { if(parameters.autoconf_scrambled && actual_service->free_ca_mode) log_message( log_module, MSG_DETAIL,"Service scrambled. Name \"%s\"\n", actual_service->name); //If there is a service_id list we look if we find it (option autoconf_sid_list) if(parameters.num_service_id) { int actual_service_id; found_in_service_id_list=0; for(actual_service_id=0;actual_service_id<parameters.num_service_id && !found_in_service_id_list;actual_service_id++) { if(parameters.service_id_list[actual_service_id]==actual_service->id) { found_in_service_id_list=1; log_message( log_module, MSG_DEBUG,"Service found in the service_id list. Name \"%s\"\n", actual_service->name); } } } else //No ts id list so it is found found_in_service_id_list=1; if(!parameters.autoconf_scrambled && actual_service->free_ca_mode) log_message( log_module, MSG_DETAIL,"Service scrambled, no CAM support and no autoconf_scrambled, we skip. Name \"%s\"\n", actual_service->name); else if(!actual_service->pmt_pid) log_message( log_module, MSG_DETAIL,"Service without a PMT pid, we skip. Name \"%s\"\n", actual_service->name); else if(!found_in_service_id_list) log_message( log_module, MSG_DETAIL,"Service NOT in the service_id list, we skip. Name \"%s\", id %d\n", actual_service->name, actual_service->id); else //service is ok, we make it a channel { //Cf EN 300 468 v1.9.1 Table 81 if((actual_service->type==0x01|| actual_service->type==0x11|| actual_service->type==0x16|| actual_service->type==0x19)|| ((actual_service->type==0x02|| actual_service->type==0x0a)&¶meters.autoconf_radios)) { log_message( log_module, MSG_DETAIL,"We convert a new service into a channel, sid %d pmt_pid %d name \"%s\" \n", actual_service->id, actual_service->pmt_pid, actual_service->name); display_service_type(actual_service->type, MSG_DETAIL, log_module); channels[channel_number].channel_type=actual_service->type; channels[channel_number].num_packet = 0; channels[channel_number].num_scrambled_packets = 0; channels[channel_number].scrambled_channel = 0; channels[channel_number].streamed_channel = 1; channels[channel_number].nb_bytes=0; channels[channel_number].pids[0]=actual_service->pmt_pid; channels[channel_number].pids_type[0]=PID_PMT; channels[channel_number].num_pids=1; snprintf(channels[channel_number].pids_language[0],4,"%s","---"); if(strlen(parameters.name_template)) { strcpy(channels[channel_number].name,parameters.name_template); int len=MAX_NAME_LEN; char number[10]; mumu_string_replace(channels[channel_number].name,&len,0,"%name",actual_service->name); sprintf(number,"%d",channel_number+1); mumu_string_replace(channels[channel_number].name,&len,0,"%number",number); //put LCN here } else strcpy(channels[channel_number].name,actual_service->name); if(multicast_vars->multicast) { char number[10]; char ip[80]; int len=80; if(strlen(parameters.autoconf_multicast_port)) { strcpy(tempstring,parameters.autoconf_multicast_port); sprintf(number,"%d",channel_number); mumu_string_replace(tempstring,&len,0,"%number",number); sprintf(number,"%d",card); mumu_string_replace(tempstring,&len,0,"%card",number); sprintf(number,"%d",tuner); mumu_string_replace(tempstring,&len,0,"%tuner",number); sprintf(number,"%d",server_id); mumu_string_replace(tempstring,&len,0,"%server",number); channels[channel_number].portOut=string_comput(tempstring); } else { channels[channel_number].portOut=port;//do here the job for evaluating the string } if(multicast_vars->multicast_ipv4) { strcpy(ip,parameters.autoconf_ip4); sprintf(number,"%d",channel_number); mumu_string_replace(ip,&len,0,"%number",number); sprintf(number,"%d",card); mumu_string_replace(ip,&len,0,"%card",number); sprintf(number,"%d",tuner); mumu_string_replace(ip,&len,0,"%tuner",number); sprintf(number,"%d",server_id); mumu_string_replace(ip,&len,0,"%server",number); strcpy(channels[channel_number].ip4Out,ip); log_message( log_module, MSG_DEBUG,"Channel IPv4 : \"%s\" port : %d\n",channels[channel_number].ip4Out,channels[channel_number].portOut); } if(multicast_vars->multicast_ipv6) { strcpy(ip,parameters.autoconf_ip6); sprintf(number,"%d",channel_number); mumu_string_replace(ip,&len,0,"%number",number); sprintf(number,"%d",card); mumu_string_replace(ip,&len,0,"%card",number); sprintf(number,"%d",tuner); mumu_string_replace(ip,&len,0,"%tuner",number); sprintf(number,"%d",server_id); mumu_string_replace(ip,&len,0,"%server",number); strcpy(channels[channel_number].ip6Out,ip); log_message( log_module, MSG_DEBUG,"Channel IPv6 : \"%s\" port : %d\n",channels[channel_number].ip6Out,channels[channel_number].portOut); } } //This is a scrambled channel, we will have to ask the cam for descrambling it if(parameters.autoconf_scrambled && actual_service->free_ca_mode) channels[channel_number].need_cam_ask=CAM_NEED_ASK; //We store the PMT and the service id in the channel channels[channel_number].pmt_pid=actual_service->pmt_pid; channels[channel_number].service_id=actual_service->id; init_rtp_header(&channels[channel_number]); //We init the rtp header in all cases if(channels[channel_number].pmt_packet==NULL) { channels[channel_number].pmt_packet=malloc(sizeof(mumudvb_ts_packet_t)); if(channels[channel_number].pmt_packet==NULL) { log_message( log_module, MSG_ERROR,"Problem with malloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); Interrupted=ERROR_MEMORY<<8; return -1; } memset (channels[channel_number].pmt_packet, 0, sizeof( mumudvb_ts_packet_t));//we clear it pthread_mutex_init(&channels[channel_number].pmt_packet->packetmutex,NULL); } #ifdef ENABLE_CAM_SUPPORT //We allocate the packet for storing the PMT for CAM purposes if(channels[channel_number].cam_pmt_packet==NULL) { channels[channel_number].cam_pmt_packet=malloc(sizeof(mumudvb_ts_packet_t)); if(channels[channel_number].cam_pmt_packet==NULL) { log_message( log_module, MSG_ERROR,"Problem with malloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); Interrupted=ERROR_MEMORY<<8; return -1; } memset (channels[channel_number].cam_pmt_packet, 0, sizeof( mumudvb_ts_packet_t));//we clear it pthread_mutex_init(&channels[channel_number].cam_pmt_packet->packetmutex,NULL); } #endif //We update the unicast port, the connection will be created in autoconf_finish_full if(unicast_port_per_channel && unicast_vars->unicast) { strcpy(tempstring,parameters.autoconf_unicast_port); int len;len=256; char number[10]; sprintf(number,"%d",channel_number); mumu_string_replace(tempstring,&len,0,"%number",number); sprintf(number,"%d",card); mumu_string_replace(tempstring,&len,0,"%card",number); sprintf(number,"%d",tuner); mumu_string_replace(tempstring,&len,0,"%tuner",number); sprintf(number,"%d",server_id); mumu_string_replace(tempstring,&len,0,"%server",number); channels[channel_number].unicast_port=string_comput(tempstring); log_message( log_module, MSG_DEBUG,"Channel (direct) unicast port %d\n",channels[channel_number].unicast_port); } #ifdef ENABLE_TRANSCODING //We copy the common transcode options to the new channel transcode_copy_options(&global_transcode_opt,&channels[channel_number].transcode_options); transcode_options_apply_templates(&channels[channel_number].transcode_options,card,tuner,server_id,channel_number); #endif channel_number++; } else if(actual_service->type==0x02||actual_service->type==0x0a) //service_type digital radio sound service log_message( log_module, MSG_DETAIL,"Service type digital radio sound service, no autoconfigure. (if you want add autoconf_radios=1 to your configuration file) Name \"%s\"\n",actual_service->name); else if(actual_service->type!=0) //0 is an empty service { //We show the service type log_message( log_module, MSG_DETAIL,"No autoconfigure due to service type : %s. Name \"%s\"\n",service_type_to_str(actual_service->type),actual_service->name); } } actual_service=actual_service->next; } while(actual_service && channel_number<MAX_CHANNELS); if(channel_number==MAX_CHANNELS) log_message( log_module, MSG_WARN,"Warning : We reached the maximum channel number, we drop other possible channels !\n"); return channel_number; }
/** * @brief Print a log message * * @param log_module : the name of the part of MuMuDVB which send the message * @param type : message type MSG_* * @param psz_format : the message in the printf format */ void log_message( char* log_module, int type, const char *psz_format, ... ) { va_list args; int priority=0; char *tempchar; int message_size; mumu_string_t log_string; log_string.string=NULL; log_string.length=0; /*****************************************/ //if the log header is not initialised // we do it /*****************************************/ if(log_params.log_header==NULL) { log_params.log_header=malloc((strlen(DEFAULT_LOG_HEADER)+1)*sizeof(char)); if(log_params.log_header==NULL) { if (log_params.log_type == LOGGING_FILE) fprintf( log_params.log_file,"Problem with malloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); else if (log_params.log_type == LOGGING_SYSLOG) syslog (MSG_ERROR,"Problem with malloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); else fprintf( stderr,"Problem with malloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); va_end( args ); Interrupted=ERROR_MEMORY<<8; return; } sprintf(log_params.log_header,"%s",DEFAULT_LOG_HEADER); } /*****************************************/ //We apply the templates to the header /*****************************************/ mumu_string_append(&log_string, "%s",log_params.log_header); log_string.string=mumu_string_replace(log_string.string,&log_string.length,1,"%priority",priorities(type)); if(log_module!=NULL) log_string.string=mumu_string_replace(log_string.string,&log_string.length,1,"%module",log_module); else log_string.string=mumu_string_replace(log_string.string,&log_string.length,1,"%module",""); char timestring[40]; time_t actual_time; actual_time=time(NULL); sprintf(timestring,"%jd", (intmax_t)actual_time); log_string.string=mumu_string_replace(log_string.string,&log_string.length,1,"%timeepoch",timestring); asctime_r(localtime(&actual_time),timestring); timestring[strlen(timestring)-1]='\0'; //In order to remove the final '\n' but by asctime log_string.string=mumu_string_replace(log_string.string,&log_string.length,1,"%date",timestring); char pidstring[10]; sprintf (pidstring, "%d", getpid ()); log_string.string=mumu_string_replace(log_string.string,&log_string.length,1,"%pid",pidstring); /*****************************************/ //We append the log message /*****************************************/ //The length returned by mumu_string_replace is the allocated length not the string length //If we want mumu_string_append to work we need to update the length to the string length log_string.length=strlen(log_string.string); va_start( args, psz_format ); message_size=vsnprintf(NULL, 0, psz_format, args); va_end( args ); tempchar=malloc((message_size+1)*sizeof(char)); if(tempchar==NULL) { if (log_params.log_type == LOGGING_FILE) fprintf( log_params.log_file,"Problem with malloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); else if (log_params.log_type == LOGGING_SYSLOG) syslog (MSG_ERROR,"Problem with malloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); else fprintf( stderr,"Problem with malloc : %s file : %s line %d\n",strerror(errno),__FILE__,__LINE__); va_end( args ); Interrupted=ERROR_MEMORY<<8; return; } va_start( args, psz_format ); vsprintf(tempchar, psz_format, args ); va_end( args ); //If there is no \n at the end of the message we add it char terminator='\0'; if(tempchar[strlen(tempchar)-1] != '\n') terminator='\n'; mumu_string_append(&log_string,"%s%c",tempchar,terminator); free(tempchar); /*****************************************/ //We "display" the log message /*****************************************/ if(type<log_params.verbosity) { if ( log_params.log_type & LOGGING_FILE) fprintf(log_params.log_file,"%s",log_string.string); if((log_params.log_type & LOGGING_SYSLOG) && (log_params.syslog_initialised)) { //what is the priority ? switch(type) { case MSG_ERROR: priority|=LOG_ERR; break; case MSG_WARN: priority|=LOG_WARNING; break; case MSG_INFO: priority|=LOG_INFO; break; case MSG_DETAIL: priority|=LOG_NOTICE; break; case MSG_DEBUG: case MSG_FLOOD: priority|=LOG_DEBUG; break; default: priority=LOG_USER; } syslog (priority,"%s",log_string.string); } if((log_params.log_type == LOGGING_UNDEFINED) || (log_params.log_type & LOGGING_CONSOLE) || ((log_params.log_type & LOGGING_SYSLOG) && (log_params.syslog_initialised==0))) fprintf(stderr,"%s",log_string.string); } mumu_free_string(&log_string); }