📄 mpegts_in.c
字号:
/*FIXME: we assume only simple RTP packaging (no CSRC nor extensions)*/ if ((data[0] != 0x47) && ((data[1] & 0x7F) == 33) ) { is_rtp = 1; //fprintf(stdout, "MPEG-TS over RTP detected\n", size); } } /*process chunk*/ if (is_rtp) { gf_m2ts_process_data(m2ts->ts, data+12, size-12); } else { gf_m2ts_process_data(m2ts->ts, data, size); } } } else { u32 pos = 0; if (m2ts->start_range && m2ts->duration) { Double perc = m2ts->start_range / (1000 * m2ts->duration); pos = (u32) (s64) (perc * m2ts->file_size); /*align to TS packet size*/ while (pos%188) pos++; if (pos>=m2ts->file_size) { m2ts->start_range = 0; pos = 0; } } fseek(m2ts->file, pos, SEEK_SET); while (m2ts->run_state && !feof(m2ts->file) ) { /*m2ts chunks by chunks*/ size = fread(data, 1, 188, m2ts->file); if (!size) break; /*process chunk*/ gf_m2ts_process_data(m2ts->ts, data, size); /*regulate file reading*/ M2TS_Regulate(m2ts); } fprintf(stdout, "\nEOS reached\n"); if (m2ts->nb_playing) { for (i=0; i<GF_M2TS_MAX_STREAMS; i++) { GF_M2TS_PES *pes = (GF_M2TS_PES *)m2ts->ts->ess[i]; if (!pes || (pes->pid==pes->program->pmt_pid)) continue; if (!pes->user || !pes->reframe) continue; gf_term_on_sl_packet(m2ts->service, pes->user, NULL, 0, NULL, GF_EOS); gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_SKIP); } } } m2ts->run_state = 2; return 0;}static void M2TS_OnEventPCR(GF_M2TS_Demuxer *ts, u32 evt_type, void *param){ if (evt_type==GF_M2TS_EVT_PES_PCR) { M2TSIn *m2ts = ts->user; GF_M2TS_PES_PCK *pck = param; if (!m2ts->nb_playing) { m2ts->nb_playing = pck->stream->pid; m2ts->end_range = (u32) (pck->PTS / 90); } else if (m2ts->nb_playing == pck->stream->pid) { m2ts->start_range = (u32) (pck->PTS / 90); } }}#ifdef GPAC_HAS_LINUX_DVBvoid M2TS_SetupDVB(GF_InputService *plug, M2TSIn *m2ts, char *url){ GF_Err e = GF_OK; char *str; const char *chan_conf; if (strnicmp(url, "dvb://", 6)) { e = GF_NOT_SUPPORTED; goto exit; } chan_conf = gf_modules_get_option((GF_BaseInterface *)plug, "DVB", "ChannelsFile"); if (!chan_conf) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[DVBIn] Cannot locate channel configuration file\n")); e = GF_SERVICE_ERROR; goto exit; } if (m2ts->tuner == NULL) { m2ts->tuner = malloc(sizeof(GF_Tuner)); } e = gf_dvb_tune(m2ts->tuner, url, chan_conf); if (e) goto exit; m2ts->th = gf_th_new(); /*start playing for tune-in*/ gf_th_run(m2ts->th, M2TS_Run, m2ts);exit: if (e) { gf_term_on_connect(m2ts->service, NULL, e); }}#endifvoid M2TS_SetupLive(M2TSIn *m2ts, char *url){ GF_Err e = GF_OK; char *str; u16 port; u32 sock_type = 0; if (!strnicmp(url, "udp://", 6) || !strnicmp(url, "mpegts-udp://", 13)) { sock_type = GF_SOCK_TYPE_UDP; } else if (!strnicmp(url, "mpegts-tcp://", 13) ) { sock_type = GF_SOCK_TYPE_TCP; } else { e = GF_NOT_SUPPORTED; goto exit; } url = strchr(url, ':'); url += 3; m2ts->sock = gf_sk_new(sock_type); if (!m2ts->sock) { e = GF_IO_ERR; goto exit; } /*setup port and src*/ port = 1234; str = strrchr(url, ':'); /*take care of IPv6 address*/ if (str && strchr(str, ']')) str = strchr(url, ':'); if (str) { port = atoi(str+1); str[0] = 0; } /*do we have a source ?*/ if (strlen(url) && strcmp(url, "localhost") ) { if (gf_sk_is_multicast_address(url)) { gf_sk_setup_multicast(m2ts->sock, url, port, 0, 0, NULL); } else { gf_sk_bind(m2ts->sock, port, url, 0, GF_SOCK_REUSE_PORT); } } if (str) str[0] = ':'; gf_sk_set_buffer_size(m2ts->sock, 0, UDP_BUFFER_SIZE); gf_sk_set_block_mode(m2ts->sock, 0); m2ts->th = gf_th_new(); gf_th_set_priority(m2ts->th, GF_THREAD_PRIORITY_HIGHEST); /*start playing for tune-in*/ gf_th_run(m2ts->th, M2TS_Run, m2ts);exit: if (e) { gf_term_on_connect(m2ts->service, NULL, e); }}void M2TS_SetupFile(M2TSIn *m2ts, char *url){#if 0 char data[188]; u32 size, fsize; s32 nb_rwd;#endif m2ts->file = fopen(url, "rb"); if (!m2ts->file) { gf_term_on_connect(m2ts->service, NULL, GF_URL_ERROR); } fseek(m2ts->file, 0, SEEK_END); m2ts->file_size = ftell(m2ts->file); #if 0 /* estimate duration by reading the end of the file m2ts->end_range is initialized to the PTS of the last TS packet m2ts->nb_playing is initialized to the PID of the last TS packet */ m2ts->nb_playing = 0; m2ts->ts->on_event = M2TS_OnEventPCR; m2ts->end_range = 0; nb_rwd = 1; fsize = m2ts->file_size; while (fsize % 188) fsize--; while (1) { fseek(m2ts->file, fsize - 188 * nb_rwd, SEEK_SET); /*m2ts chunks by chunks*/ size = fread(data, 1, 188, m2ts->file); if (!size) break; /*process chunk*/ gf_m2ts_process_data(m2ts->ts, data, size); if (m2ts->nb_playing) break; nb_rwd ++; } /* reset of the file initialization of m2ts->start_range to the PTS of the first TS packet with PID = m2ts->nb_playing */ fseek(m2ts->file, 0, SEEK_SET); gf_m2ts_reset_parsers(m2ts->ts); m2ts->start_range = 0; while (1) { /*m2ts chunks by chunks*/ size = fread(data, 1, 188, m2ts->file); if (!size) break; /*process chunk*/ gf_m2ts_process_data(m2ts->ts, data, size); if (m2ts->start_range) break; } m2ts->duration = (m2ts->end_range - m2ts->start_range) / 300000.0; gf_m2ts_demux_del(m2ts->ts); /* Creation of the real demuxer for playback */ m2ts->ts = gf_m2ts_demux_new(); m2ts->ts->user = m2ts;#endif /* reinitialization for seek */ m2ts->end_range = m2ts->start_range = 0; m2ts->nb_playing = 0; m2ts->th = gf_th_new(); /*start playing for tune-in*/ gf_th_run(m2ts->th, M2TS_Run, m2ts);}static GF_Err M2TS_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url){ char szURL[2048]; char *ext; M2TSIn *m2ts = plug->priv; m2ts->service = serv; if (m2ts->program) free(m2ts->program); m2ts->program = NULL; m2ts->prog_id = 0; strcpy(szURL, url); ext = strrchr(szURL, '#'); if (ext) { m2ts->program = strdup(ext+1); ext[0] = 0; } m2ts->do_regulate = 0; m2ts->duration = 0; if (!strnicmp(url, "udp://", 6) || !strnicmp(url, "mpegts-udp://", 13) || !strnicmp(url, "mpegts-tcp://", 13) ) { M2TS_SetupLive(m2ts, (char *) szURL); } #ifdef GPAC_HAS_LINUX_DVB else if (!strnicmp(url, "dvb://", 6)) { // DVB Setup M2TS_SetupDVB(plug, m2ts, (char *) szURL); } #endif else { M2TS_SetupFile(m2ts, (char *) szURL); } return GF_OK;}static GF_Err M2TS_CloseService(GF_InputService *plug){ M2TSIn *m2ts = plug->priv; if (m2ts->th) { if (m2ts->run_state == 1) { m2ts->run_state = 0; while (m2ts->run_state!=2) gf_sleep(0); } gf_th_del(m2ts->th); m2ts->th = NULL; } if (m2ts->file) fclose(m2ts->file); m2ts->file = NULL; gf_term_on_disconnect(m2ts->service, NULL, GF_OK); return GF_OK;}static GF_Descriptor *M2TS_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url){ u32 i=0; M2TSIn *m2ts = plug->priv; GF_Descriptor *desc; if (gf_list_count(m2ts->ts->programs) == 1) { GF_M2TS_Program *prog = gf_list_get(m2ts->ts->programs, 0); if (prog->pmt_iod) { m2ts->do_regulate = 1; gf_odf_desc_copy((GF_Descriptor *)prog->pmt_iod, &desc); return desc; } } /*returning an empty IOD means "no scene description", let the terminal handle all media objects*/ desc = gf_odf_desc_new(GF_ODF_IOD_TAG); ((GF_ObjectDescriptor *) desc)->objectDescriptorID = 1; return desc;}static GF_Err M2TS_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream){ u32 ES_ID; GF_Err e; M2TSIn *m2ts = plug->priv; e = GF_STREAM_NOT_FOUND; if (strstr(url, "ES_ID")) { sscanf(url, "ES_ID=%d", &ES_ID); /* In case there is a real IOD, we need to translate PID into ESID */ if (gf_list_count(m2ts->ts->programs) == 1) { GF_M2TS_Program *prog = gf_list_get(m2ts->ts->programs, 0); if (prog->pmt_iod) { u32 i; for (i=0; i<GF_M2TS_MAX_STREAMS; i++) { GF_M2TS_PES *pes = (GF_M2TS_PES *)m2ts->ts->ess[i]; if (!pes || (pes->pid==pes->program->pmt_pid)) continue; if (pes->mpeg4_es_id == ES_ID) { if (pes->user) { e = GF_SERVICE_ERROR; gf_term_on_connect(m2ts->service, channel, e); return e; } else { pes->user = channel; e = GF_OK; gf_term_on_connect(m2ts->service, channel, e); return e; } } } } } if ((ES_ID<GF_M2TS_MAX_STREAMS) && m2ts->ts->ess[ES_ID]) { GF_M2TS_PES *pes = (GF_M2TS_PES *)m2ts->ts->ess[ES_ID]; if (pes->user) e = GF_SERVICE_ERROR; else { pes->user = channel; e = GF_OK; } } } gf_term_on_connect(m2ts->service, channel, e); return e;}static GF_M2TS_PES *M2TS_GetChannel(M2TSIn *m2ts, LPNETCHANNEL channel){ u32 i; for (i=0; i<GF_M2TS_MAX_STREAMS; i++) { GF_M2TS_PES *pes = (GF_M2TS_PES *)m2ts->ts->ess[i]; if (!pes || (pes->pid==pes->program->pmt_pid)) continue; if (pes->user == channel) return pes; } return NULL;}static GF_Err M2TS_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel){ M2TSIn *m2ts = plug->priv; GF_Err e = GF_STREAM_NOT_FOUND; GF_M2TS_PES *pes = M2TS_GetChannel(m2ts, channel); if (pes) { pes->user = NULL; e = GF_OK; } gf_term_on_disconnect(m2ts->service, channel, e); return GF_OK;}static GF_Err M2TS_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com){ GF_M2TS_PES *pes; M2TSIn *m2ts = plug->priv; if (!com->base.on_channel) return GF_NOT_SUPPORTED; switch (com->command_type) { /*we cannot pull complete AUs from the stream*/ case GF_NET_CHAN_SET_PULL: return GF_NOT_SUPPORTED; /*we cannot seek stream by stream*/ case GF_NET_CHAN_INTERACTIVE: return GF_NOT_SUPPORTED; case GF_NET_CHAN_BUFFER: com->buffer.max = REGULATE_TIME_SLOT; com->buffer.min = 0; return GF_OK; case GF_NET_CHAN_DURATION: com->duration.duration = m2ts->duration; return GF_OK; case GF_NET_CHAN_PLAY: pes = M2TS_GetChannel(m2ts, com->base.on_channel); if (!pes) return GF_STREAM_NOT_FOUND; gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_DEFAULT); /*this is a multplex, only trigger the play command for the first stream activated*/ if (!m2ts->nb_playing) { m2ts->start_range = (u32) (com->play.start_range*1000); m2ts->end_range = (com->play.end_range>0) ? (u32) (com->play.end_range*1000) : 0; /*start demuxer*/ if (m2ts->run_state!=1) { gf_th_run(m2ts->th, M2TS_Run, m2ts); } } m2ts->nb_playing++; return GF_OK; case GF_NET_CHAN_STOP: pes = M2TS_GetChannel(m2ts, com->base.on_channel); if (!pes) return GF_STREAM_NOT_FOUND; gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_SKIP); /*FIXME HOORIBLE HACK*/ return GF_OK; assert(m2ts->nb_playing); m2ts->nb_playing--; /*stop demuxer*/ if (!m2ts->nb_playing && (m2ts->run_state==1)) { m2ts->run_state=0; while (m2ts->run_state!=2) gf_sleep(2); } return GF_OK; default: return GF_OK; }}GF_InputService *NewM2TSReader(){ M2TSIn *reader; GF_InputService *plug = malloc(sizeof(GF_InputService)); memset(plug, 0, sizeof(GF_InputService)); GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC MPEG-2 TS Reader", "gpac distribution") plug->CanHandleURL = M2TS_CanHandleURL; plug->ConnectService = M2TS_ConnectService; plug->CloseService = M2TS_CloseService; plug->GetServiceDescriptor = M2TS_GetServiceDesc; plug->ConnectChannel = M2TS_ConnectChannel; plug->DisconnectChannel = M2TS_DisconnectChannel; plug->ServiceCommand = M2TS_ServiceCommand; reader = malloc(sizeof(M2TSIn)); memset(reader, 0, sizeof(M2TSIn)); plug->priv = reader; reader->ts = gf_m2ts_demux_new(); reader->ts->on_event = M2TS_OnEvent; reader->ts->user = reader; return plug;}void DeleteM2TSReader(void *ifce){ GF_InputService *plug = (GF_InputService *) ifce; M2TSIn *m2ts = plug->priv; gf_m2ts_demux_del(m2ts->ts); free(m2ts); free(plug);}Bool QueryInterface(u32 InterfaceType){ switch (InterfaceType) { case GF_NET_CLIENT_INTERFACE: return 1; default: return 0; }}GF_BaseInterface *LoadInterface(u32 InterfaceType){ switch (InterfaceType) { case GF_NET_CLIENT_INTERFACE: return (GF_BaseInterface *) NewM2TSReader(); default: return NULL; }}void ShutdownInterface(GF_BaseInterface *ifce){ switch (ifce->InterfaceType) { case GF_NET_CLIENT_INTERFACE: DeleteM2TSReader(ifce); break; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -