This source file includes following definitions.
- M2TS_CanHandleURL
- M2TS_CanHandleURLInService
- MP2TS_GetESD
- MP2TS_GetOD
- MP2TS_DeclareStream
- MP2TS_SetupProgram
- MP2TS_SendPacket
- MP2TS_SendSLPacket
- M2TS_GenerateEPG_OD
- M2TS_FlushRequested
- forward_m2ts_event
- M2TS_OnEvent
- m2ts_net_io
- M2TS_QueryNextFile
- m2ts_flush_data
- M2TS_ConnectService
- M2TS_CloseService
- M2TS_GetServiceDesc
- M2TS_ConnectChannel
- M2TS_GetChannel
- M2TS_GetFirstRunningChannel
- M2TS_DisconnectChannel
- gf_m2ts_switch_quality
- M2TS_ServiceCommand
- M2TS_RegisterMimeTypes
- M2TS_GetNetworkType
- NewM2TSReader
- DeleteM2TSReader
- QueryInterfaces
- LoadInterface
- ShutdownInterface
#include <gpac/modules/service.h>
#include <gpac/modules/codec.h>
#include <gpac/mpegts.h>
#include <gpac/thread.h>
#include <gpac/network.h>
#include <gpac/constants.h>
#ifndef GPAC_DISABLE_MPEG2TS
static const char * MIMES[] = { "video/mpeg-2", "video/mp2t", "video/mpeg", NULL};
static const char * M2TS_EXTENSIONS = "ts m2t mts dmb trp";
#define M2TS_MAX_SLEEP 200
typedef struct {
char *fragment;
u32 id;
u32 pid;
} M2TSIn_Prog;
typedef struct
{
GF_M2TS_Demuxer *ts;
GF_InputService *owner;
GF_ClientService *service;
Bool ts_setup;
Bool request_all_pids;
Bool is_connected;
Bool low_latency_mode;
Bool in_segment_download, first_chunk_in_seg_parsed;
Bool first_segment_fetched;
Bool epg_requested;
Bool has_eit;
LPNETCHANNEL eit_channel;
GF_Mutex *mx;
Bool mpeg4on2_scene_only;
char * network_buffer;
u32 network_buffer_size;
u32 regulation_pcr_pid;
Bool skip_regulation;
Bool has_pending_segments;
Bool in_data_flush;
Bool hybrid_on;
Bool flush_sdt;
u64 pcr_last;
u32 stb_at_last_pcr;
u32 nb_paused;
Bool file_regulate;
u32 nb_playing;
u32 nb_prog_to_setup;
u32 map_media_time_on_prog_id;
Double media_start_range;
const char *force_temi_url;
} M2TSIn;
static void M2TS_GetNetworkType(GF_InputService *plug,M2TSIn *reader);
static Bool M2TS_CanHandleURL(GF_InputService *plug, const char *url)
{
char *sExt;
if (!plug || !url)
return 0;
if (!strnicmp(url, "udp://", 6)
|| !strnicmp(url, "mpegts-udp://", 13)
|| !strnicmp(url, "mpegts-tcp://", 13)
#ifdef GPAC_HAS_LINUX_DVB
|| !strnicmp(url, "dvb://", 6)
#endif
) {
return 1;
}
sExt = strrchr(url, '.');
{
int i=0;
for (i = 0 ; NULL != MIMES[i]; i++)
if (gf_service_check_mime_register(plug, MIMES[i], M2TS_EXTENSIONS, "MPEG-2 TS", sExt))
return 1;
}
return 0;
}
static Bool M2TS_CanHandleURLInService(GF_InputService *plug, const char *url)
{
Bool ret = 0;
M2TSIn *m2ts;
if (!plug || !url)
return 0;
m2ts = (M2TSIn *)plug->priv;
if (!m2ts)
return 0;
if (!strnicmp(url, "pid://", 6)) {
u32 pid = atoi(url+6);
if (pid>=GF_M2TS_MAX_STREAMS) return 0;
if (m2ts->ts->ess[pid]) return 1;
return 0;
}
#ifdef GPAC_HAS_LINUX_DVB
if (!stricmp(url, "dvb://EPG")) return 1;
if (!strnicmp(url, "dvb://", 6)) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[DVBIn] Checking reuse of the same tuner for %s\n", url));
const char *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"));
ret = 0;
}
else if (m2ts->ts->tuner->freq != 0) {
char *frag = strchr(url, '#');
if (frag) frag[0] = 0;
if (m2ts->ts->tuner->freq == gf_dvb_get_freq_from_url(chan_conf, url)) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[DVBIn] Reusing the same tuner for %s\n", url));
ret = 1;
}
if (frag) frag[0] = '#';
}
} else
#endif
if (!strnicmp(url, "udp://", 6)
|| !strnicmp(url, "mpegts-udp://", 13)
|| !strnicmp(url, "mpegts-tcp://", 13))
{
ret = 0;
} else {
char *frag = strchr(url, '#');
if (frag) frag[0] = 0;
if (!strlen(url) || !strcmp(url, m2ts->ts->filename)) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[DVBIn] Reusing the same input file for %s\n", url));
ret = 1;
}
if (frag) frag[0] = '#';
}
return ret;
}
static GF_ESD *MP2TS_GetESD(M2TSIn *m2ts, GF_M2TS_PES *stream, char *dsi, u32 dsi_size)
{
GF_ESD *esd;
esd = gf_odf_desc_esd_new(0);
esd->ESID = stream->mpeg4_es_id ? stream->mpeg4_es_id : stream->pid;
switch (stream->stream_type) {
case GF_M2TS_VIDEO_MPEG1:
esd->decoderConfig->streamType = GF_STREAM_VISUAL;
esd->decoderConfig->objectTypeIndication = GPAC_OTI_VIDEO_MPEG1;
break;
case GF_M2TS_VIDEO_MPEG2:
case GF_M2TS_VIDEO_DCII:
esd->decoderConfig->streamType = GF_STREAM_VISUAL;
esd->decoderConfig->objectTypeIndication = GPAC_OTI_VIDEO_MPEG2_422;
break;
case GF_M2TS_VIDEO_MPEG4:
esd->decoderConfig->streamType = GF_STREAM_VISUAL;
esd->decoderConfig->objectTypeIndication = GPAC_OTI_VIDEO_MPEG4_PART2;
break;
case GF_M2TS_VIDEO_H264:
esd->decoderConfig->streamType = GF_STREAM_VISUAL;
esd->decoderConfig->objectTypeIndication = GPAC_OTI_VIDEO_AVC;
break;
case GF_M2TS_VIDEO_SVC:
esd->decoderConfig->streamType = GF_STREAM_VISUAL;
esd->decoderConfig->objectTypeIndication = GPAC_OTI_VIDEO_SVC;
break;
case GF_M2TS_VIDEO_HEVC:
esd->decoderConfig->streamType = GF_STREAM_VISUAL;
esd->decoderConfig->objectTypeIndication = GPAC_OTI_VIDEO_HEVC;
break;
case GF_M2TS_VIDEO_SHVC:
case GF_M2TS_VIDEO_SHVC_TEMPORAL:
case GF_M2TS_VIDEO_MHVC:
case GF_M2TS_VIDEO_MHVC_TEMPORAL:
esd->decoderConfig->streamType = GF_STREAM_VISUAL;
esd->decoderConfig->objectTypeIndication = GPAC_OTI_VIDEO_LHVC;
break;
case GF_M2TS_AUDIO_MPEG1:
esd->decoderConfig->streamType = GF_STREAM_AUDIO;
esd->decoderConfig->objectTypeIndication = GPAC_OTI_AUDIO_MPEG1;
break;
case GF_M2TS_AUDIO_MPEG2:
esd->decoderConfig->streamType = GF_STREAM_AUDIO;
esd->decoderConfig->objectTypeIndication = GPAC_OTI_AUDIO_MPEG2_PART3;
break;
case GF_M2TS_AUDIO_LATM_AAC:
case GF_M2TS_AUDIO_AAC:
case GPAC_OTI_AUDIO_AAC_MPEG2_MP:
case GPAC_OTI_AUDIO_AAC_MPEG2_LCP:
case GPAC_OTI_AUDIO_AAC_MPEG2_SSRP:
if (!dsi) {
if (m2ts->ts->file) {
m2ts->file_regulate = 2;
gf_m2ts_pause_demux(m2ts->ts, 0);
}
gf_m2ts_set_pes_framing(stream, GF_M2TS_PES_FRAMING_DEFAULT);
gf_odf_desc_del((GF_Descriptor *)esd);
return NULL;
}
esd->decoderConfig->streamType = GF_STREAM_AUDIO;
esd->decoderConfig->objectTypeIndication = GPAC_OTI_AUDIO_AAC_MPEG4;
break;
case GF_M2TS_AUDIO_AC3:
esd->decoderConfig->streamType = GF_STREAM_AUDIO;
esd->decoderConfig->objectTypeIndication = GPAC_OTI_AUDIO_AC3;
break;
case GF_M2TS_AUDIO_EC3:
esd->decoderConfig->streamType = GF_STREAM_AUDIO;
esd->decoderConfig->objectTypeIndication = GPAC_OTI_AUDIO_EAC3;
break;
case GF_M2TS_SYSTEMS_MPEG4_SECTIONS:
default:
gf_odf_desc_del((GF_Descriptor *)esd);
return NULL;
}
esd->decoderConfig->bufferSizeDB = 0;
esd->slConfig->useAccessUnitStartFlag = 1;
esd->slConfig->useAccessUnitEndFlag = 0;
esd->slConfig->useRandomAccessPointFlag = 1;
esd->slConfig->AUSeqNumLength = 0;
esd->slConfig->timestampResolution = 90000;
esd->OCRESID = stream->program->pcr_pid;
if (stream->pid == stream->program->pcr_pid) {
esd->slConfig->OCRResolution = 27000000;
}
if (dsi) {
esd->decoderConfig->decoderSpecificInfo->data = gf_malloc(sizeof(char)*dsi_size);
memcpy(esd->decoderConfig->decoderSpecificInfo->data, dsi, sizeof(char)*dsi_size);
esd->decoderConfig->decoderSpecificInfo->dataLength = dsi_size;
}
return esd;
}
static GF_ObjectDescriptor *MP2TS_GetOD(M2TSIn *m2ts, GF_M2TS_PES *stream, char *dsi, u32 dsi_size, u32 *streamType)
{
GF_ObjectDescriptor *od;
GF_ESD *esd;
u32 cur_ES, i, count;
esd = MP2TS_GetESD(m2ts, stream, dsi, dsi_size);
if (!esd) return NULL;
if (stream->program->is_scalable)
esd->has_scalable_layers = GF_TRUE;
od = (GF_ObjectDescriptor*)gf_odf_desc_new(GF_ODF_OD_TAG);
gf_list_add(od->ESDescriptors, esd);
od->objectDescriptorID = 0;
if (streamType) *streamType = esd->decoderConfig->streamType;
od->ServiceID = stream->program->number;
od->service_ifce = m2ts->owner;
cur_ES = esd->ESID;
count = gf_list_count(stream->program->streams);
for (i = 0; i < count; i++) {
GF_M2TS_ES *es = (GF_M2TS_ES *)gf_list_get(stream->program->streams, i);
if ((es->flags & GF_M2TS_ES_IS_PES) && ( ((GF_M2TS_PES *)es)->depends_on_pid == cur_ES)) {
GF_ESD *the_esd;
the_esd = MP2TS_GetESD(m2ts, (GF_M2TS_PES *)es, dsi, dsi_size);
if (the_esd) {
if ((esd->decoderConfig->objectTypeIndication==GPAC_OTI_VIDEO_AVC) && (the_esd->decoderConfig->objectTypeIndication==GPAC_OTI_VIDEO_LHVC)) {
esd->has_scalable_layers = GF_FALSE;
}
the_esd->has_scalable_layers = GF_TRUE;
the_esd->dependsOnESID = cur_ES;
gf_list_add(od->ESDescriptors, the_esd);
}
cur_ES = the_esd->ESID;
}
}
return od;
}
static void MP2TS_DeclareStream(M2TSIn *m2ts, GF_M2TS_PES *stream, char *dsi, u32 dsi_size)
{
GF_ObjectDescriptor *od = MP2TS_GetOD(m2ts, stream, dsi, dsi_size, NULL);
if (!od) return;
gf_service_declare_media(m2ts->service, (GF_Descriptor*)od, 1);
}
static void MP2TS_SetupProgram(M2TSIn *m2ts, GF_M2TS_Program *prog, Bool regenerate_scene, Bool no_declare)
{
u32 i, count;
count = gf_list_count(prog->streams);
#ifdef GPAC_HAS_LINUX_DVB
if (m2ts->ts->tuner) {
Bool found = 0;
for (i=0; i<count; i++) {
GF_M2TS_PES *pes = gf_list_get(prog->streams, i);
if (pes->pid==m2ts->ts->tuner->vpid) found = 1;
else if (pes->pid==m2ts->ts->tuner->apid) found = 1;
}
if (!found) return;
}
#endif
if (m2ts->ts->file || m2ts->ts->dnload) {
m2ts->file_regulate = 1;
if (!m2ts->nb_playing) gf_m2ts_pause_demux(m2ts->ts, 1);
}
for (i=0; i<count; i++) {
GF_M2TS_ES *es = gf_list_get(prog->streams, i);
if (es->pid==prog->pmt_pid) continue;
if (((GF_M2TS_PES *)es)->depends_on_pid ) {
prog->is_scalable = GF_TRUE;
break;
}
}
for (i=0; i<count; i++) {
GF_M2TS_ES *es = gf_list_get(prog->streams, i);
if (es->pid==prog->pmt_pid) continue;
if ((es->flags & GF_M2TS_ES_IS_PES) && ((GF_M2TS_PES *)es)->depends_on_pid )
continue;
if (!es->user)
gf_m2ts_set_pes_framing((GF_M2TS_PES *)es, GF_M2TS_PES_FRAMING_SKIP);
if (!prog->pmt_iod && !no_declare) {
if (! (es->flags & GF_M2TS_ES_ALREADY_DECLARED)) {
MP2TS_DeclareStream(m2ts, (GF_M2TS_PES *)es, NULL, 0);
es->flags |= GF_M2TS_ES_ALREADY_DECLARED;
}
}
}
if (!prog->pmt_iod && regenerate_scene)
gf_service_declare_media(m2ts->service, NULL, 0);
if (m2ts->nb_prog_to_setup) {
m2ts->nb_prog_to_setup--;
}
}
static void MP2TS_SendPacket(M2TSIn *m2ts, GF_M2TS_PES_PCK *pck)
{
GF_SLHeader slh;
if (! pck->stream->program->first_dts) return;
if (!pck->stream->user) return;
memset(&slh, 0, sizeof(GF_SLHeader));
slh.accessUnitStartFlag = (pck->flags & GF_M2TS_PES_PCK_AU_START) ? 1 : 0;
if (slh.accessUnitStartFlag) {
slh.compositionTimeStampFlag = 1;
slh.compositionTimeStamp = pck->PTS;
if (pck->DTS != pck->PTS) {
slh.decodingTimeStampFlag = 1;
slh.decodingTimeStamp = pck->DTS;
}
slh.randomAccessPointFlag = (pck->flags & GF_M2TS_PES_PCK_RAP) ? 1 : 0;
}
gf_service_send_packet(m2ts->service, pck->stream->user, pck->data, pck->data_len, &slh, GF_OK);
}
static GFINLINE void MP2TS_SendSLPacket(M2TSIn *m2ts, GF_M2TS_SL_PCK *pck)
{
GF_SLHeader SLHeader, *slh = NULL;
u32 SLHdrLen = 0;
if (((GF_M2TS_ES*)pck->stream)->slcfg) {
gf_sl_depacketize(((GF_M2TS_ES*)pck->stream)->slcfg, &SLHeader, pck->data, pck->data_len, &SLHdrLen);
SLHeader.m2ts_version_number_plus_one = pck->version_number + 1;
slh = &SLHeader;
}
gf_service_send_packet(m2ts->service, pck->stream->user, pck->data+SLHdrLen, pck->data_len-SLHdrLen, slh, GF_OK);
}
static GF_ObjectDescriptor *M2TS_GenerateEPG_OD(M2TSIn *m2ts)
{
GF_ObjectDescriptor *od;
GF_ESD *esd;
esd = gf_odf_desc_esd_new(0);
esd->ESID = GF_M2TS_PID_EIT_ST_CIT;
esd->OCRESID = GF_M2TS_PID_EIT_ST_CIT;
esd->decoderConfig->streamType = GF_STREAM_PRIVATE_SCENE;
esd->decoderConfig->objectTypeIndication = GPAC_OTI_PRIVATE_SCENE_EPG;
esd->decoderConfig->bufferSizeDB = 0;
esd->slConfig->useAccessUnitStartFlag = 1;
esd->slConfig->useRandomAccessPointFlag = 1;
esd->slConfig->timestampResolution = 90000;
od = (GF_ObjectDescriptor*)gf_odf_desc_new(GF_ODF_OD_TAG);
gf_list_add(od->ESDescriptors, esd);
od->objectDescriptorID = 0;
od->service_ifce = m2ts->owner;
return od;
}
static void M2TS_FlushRequested(M2TSIn *m2ts)
{
u32 i, j, req_prog_count, count, prog_id, found;
gf_mx_p(m2ts->mx);
count = gf_list_count(m2ts->ts->programs);
for (j=0; j<count; j++) {
GF_M2TS_Program *ts_prog = gf_list_get(m2ts->ts->programs, j);
if (!ts_prog->pcr_pid) {
m2ts->file_regulate = 0;
gf_m2ts_pause_demux(m2ts->ts, 0);
gf_mx_v(m2ts->mx);
return;
}
}
found = 0;
count = gf_list_count(m2ts->ts->requested_pids);
for (i=0; i<count; i++) {
M2TSIn_Prog *req_pid = gf_list_get(m2ts->ts->requested_pids, i);
GF_M2TS_ES *es = m2ts->ts->ess[req_pid->pid];
if (es==NULL) continue;
if (!(es->flags & GF_M2TS_ES_IS_SECTION) && !es->user)
gf_m2ts_set_pes_framing((GF_M2TS_PES *)es, GF_M2TS_PES_FRAMING_SKIP);
MP2TS_DeclareStream(m2ts, (GF_M2TS_PES *)es, NULL, 0);
gf_list_rem(m2ts->ts->requested_pids, i);
gf_free(req_pid);
i--;
count--;
found++;
}
req_prog_count = gf_list_count(m2ts->ts->requested_progs);
for (i = 0; i < req_prog_count; i++) {
M2TSIn_Prog *req_prog = gf_list_get(m2ts->ts->requested_progs, i);
prog_id = atoi(req_prog->fragment);
count = gf_list_count(m2ts->ts->SDTs);
for (j=0; j<count; j++) {
GF_M2TS_SDT *sdt = gf_list_get(m2ts->ts->SDTs, j);
if (!stricmp((const char *) sdt->service, req_prog->fragment)) req_prog->id = sdt->service_id;
else if (sdt->service_id==prog_id) req_prog->id = sdt->service_id;
}
if (!req_prog->id) {
count = gf_list_count(m2ts->ts->programs);
for (j=0; j<count; j++) {
GF_M2TS_Program *ts_prog = gf_list_get(m2ts->ts->programs, j);
if (prog_id==ts_prog->number) {
req_prog->id = prog_id;
break;
}
}
}
if (req_prog->id) {
GF_M2TS_Program *ts_prog;
count = gf_list_count(m2ts->ts->programs);
for (j=0; j<count; j++) {
ts_prog = gf_list_get(m2ts->ts->programs, j);
if (ts_prog->number==req_prog->id) {
MP2TS_SetupProgram(m2ts, ts_prog, 0, 0);
found++;
gf_free(req_prog->fragment);
gf_free(req_prog);
gf_list_rem(m2ts->ts->requested_progs, i);
req_prog_count--;
i--;
break;
}
}
}
}
if (m2ts->epg_requested) {
if (!m2ts->has_eit) {
GF_ObjectDescriptor *od = M2TS_GenerateEPG_OD(m2ts);
gf_service_declare_media(m2ts->service, (GF_Descriptor*)od, 0);
m2ts->has_eit = 1;
}
} else {
if (found)
gf_service_declare_media(m2ts->service, NULL, 0);
}
gf_mx_v(m2ts->mx);
}
static void forward_m2ts_event(M2TSIn *m2ts, u32 evt_type, void *param)
{
GF_NetworkCommand com;
memset(&com, 0, sizeof(com));
com.command_type = GF_NET_SERVICE_EVENT;
com.send_event.evt.type = GF_EVENT_FROM_SERVICE;
com.send_event.evt.from_service.forward_type = GF_EVT_MPEG2;
com.send_event.evt.from_service.service_event_type = evt_type;
com.send_event.evt.from_service.param = param;
gf_service_command(m2ts->service, &com, GF_OK);
}
static void M2TS_OnEvent(GF_M2TS_Demuxer *ts, u32 evt_type, void *param)
{
M2TSIn *m2ts = (M2TSIn *) ts->user;
switch (evt_type) {
case GF_M2TS_EVT_PAT_UPDATE:
#if 0
send_m2ts_event(m2ts, evt_type, param);
#endif
break;
case GF_M2TS_EVT_AIT_FOUND:
forward_m2ts_event(m2ts, evt_type, param);
break;
case GF_M2TS_EVT_PAT_FOUND:
if (gf_list_count(m2ts->ts->programs) != 1) {
gf_service_connect_ack(m2ts->service, NULL, GF_OK);
m2ts->is_connected = 1;
}
forward_m2ts_event(m2ts, evt_type, param);
m2ts->nb_prog_to_setup = gf_list_count(m2ts->ts->programs) ;
break;
case GF_M2TS_EVT_DSMCC_FOUND:
forward_m2ts_event(m2ts, evt_type, param);
break;
case GF_M2TS_EVT_PMT_FOUND:
if (gf_list_count(m2ts->ts->programs) == 1) {
gf_service_connect_ack(m2ts->service, NULL, GF_OK);
m2ts->is_connected = 1;
}
MP2TS_SetupProgram(m2ts, param, m2ts->request_all_pids, m2ts->request_all_pids ? 0 : 1);
M2TS_FlushRequested(m2ts);
forward_m2ts_event(m2ts, evt_type, param);
break;
case GF_M2TS_EVT_PMT_REPEAT:
M2TS_FlushRequested(m2ts);
break;
case GF_M2TS_EVT_PMT_UPDATE:
MP2TS_SetupProgram(m2ts, param, GF_TRUE, GF_FALSE);
break;
case GF_M2TS_EVT_SDT_FOUND:
case GF_M2TS_EVT_SDT_UPDATE:
m2ts->flush_sdt = 1;
case GF_M2TS_EVT_SDT_REPEAT:
M2TS_FlushRequested(m2ts);
if (m2ts->flush_sdt) {
GF_NetworkCommand com;
m2ts->flush_sdt = 0;
memset(&com, 0, sizeof(GF_NetworkCommand));
com.base.command_type = GF_NET_SERVICE_INFO;
gf_service_command(m2ts->service, &com, GF_OK);
}
break;
case GF_M2TS_EVT_DVB_GENERAL:
if (m2ts->eit_channel) {
GF_M2TS_SL_PCK *pck = (GF_M2TS_SL_PCK *)param;
gf_service_send_packet(m2ts->service, m2ts->eit_channel, pck->data, pck->data_len, NULL, GF_OK);
}
break;
case GF_M2TS_EVT_PES_PCK:
MP2TS_SendPacket(m2ts, param);
break;
case GF_M2TS_EVT_SL_PCK:
MP2TS_SendSLPacket(m2ts, param);
break;
case GF_M2TS_EVT_EOS:
gf_service_send_packet(m2ts->service, ((GF_M2TS_PES *)param)->user, NULL, 0, NULL, GF_EOS);
break;
case GF_M2TS_EVT_AAC_CFG:
{
GF_M2TS_PES_PCK *pck = (GF_M2TS_PES_PCK*)param;
if (!pck->stream->first_dts) {
gf_m2ts_set_pes_framing(pck->stream, GF_M2TS_PES_FRAMING_SKIP_NO_RESET);
MP2TS_DeclareStream(m2ts, pck->stream, pck->data, pck->data_len);
if (ts->file || ts->dnload) {
m2ts->file_regulate = 1;
if (!m2ts->nb_playing) gf_m2ts_pause_demux(m2ts->ts, 1);
}
pck->stream->first_dts=1;
gf_service_declare_media(m2ts->service, NULL, 0);
}
}
break;
case GF_M2TS_EVT_PES_PCR:
{
Bool discontinuity = ( ((GF_M2TS_PES_PCK *) param)->flags & GF_M2TS_PES_PCK_DISCONTINUITY) ? 1 : 0;
GF_M2TS_PES_PCK *pck;
if (((GF_M2TS_PES_PCK *) param)->stream && ((GF_M2TS_PES_PCK *) param)->stream->user) {
GF_SLHeader slh;
memset(&slh, 0, sizeof(GF_SLHeader) );
slh.OCRflag = 1;
slh.m2ts_pcr = discontinuity ? 2 : 1;
slh.objectClockReference = ((GF_M2TS_PES_PCK *) param)->PTS;
if (m2ts->file_regulate && discontinuity) {
GF_NetworkCommand com;
memset(&com, 0, sizeof(GF_NetworkCommand));
com.command_type = GF_NET_BUFFER_QUERY;
com.base.on_channel = NULL;
while (ts->run_state) {
gf_service_command(m2ts->service, &com, GF_OK);
if (!com.buffer.occupancy || com.buffer.buffering) {
break;
}
gf_sleep(1);
}
}
gf_service_send_packet(m2ts->service, ((GF_M2TS_PES_PCK *) param)->stream->user, NULL, 0, &slh, GF_OK);
if ( m2ts->map_media_time_on_prog_id && !ts->start_range && (m2ts->map_media_time_on_prog_id==((GF_M2TS_PES_PCK *) param)->stream->program->number)) {
GF_NetworkCommand com;
memset(&com, 0, sizeof(com));
com.command_type = GF_NET_CHAN_SET_MEDIA_TIME;
com.map_time.media_time = m2ts->media_start_range;
com.map_time.timestamp = slh.objectClockReference / 300;
com.base.on_channel = ((GF_M2TS_PES_PCK *) param)->stream->user;
gf_service_command(m2ts->service, &com, GF_OK);
m2ts->map_media_time_on_prog_id = 0;
m2ts->pcr_last = 0;
}
}
pck = (GF_M2TS_PES_PCK *) param;
if (pck->stream && pck->stream->program) pck->stream->program->first_dts = 1;
if (discontinuity) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[M2TS In] PCR discontinuity - switching from old STB "LLD" to new one "LLD"\n", m2ts->pcr_last, ((GF_M2TS_PES_PCK *) param)->PTS));
if (m2ts->pcr_last) {
m2ts->pcr_last = pck->PTS;
m2ts->stb_at_last_pcr = gf_sys_clock();
}
return;
}
if (m2ts->file_regulate) {
u64 pcr = pck->PTS;
u32 stb = gf_sys_clock();
if (pck->stream) {
if (m2ts->regulation_pcr_pid==0) {
m2ts->regulation_pcr_pid = pck->stream->pid;
} else if (m2ts->regulation_pcr_pid != pck->stream->pid) {
return;
}
}
if (m2ts->pcr_last) {
s32 diff;
if (pcr < m2ts->pcr_last) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[M2TS In] PCR "LLU" less than previous PCR "LLU"\n", ((GF_M2TS_PES_PCK *) param)->PTS, m2ts->pcr_last));
m2ts->pcr_last = pcr;
m2ts->stb_at_last_pcr = gf_sys_clock();
diff = 0;
} else {
u64 pcr_diff = (pcr - m2ts->pcr_last);
pcr_diff /= 27000;
if (pcr_diff>1000) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[M2TS In] PCR diff too big: "LLU" ms - PCR "LLU" - previous PCR "LLU" - error in TS ?\n", pcr_diff, ((GF_M2TS_PES_PCK *) param)->PTS, m2ts->pcr_last));
diff = 100;
} else {
diff = (u32) pcr_diff - (stb - m2ts->stb_at_last_pcr);
}
}
if (diff<-400) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[M2TS In] Demux not going fast enough according to PCR (drift %d, pcr: "LLU", last pcr: "LLU")\n", diff, pcr, m2ts->pcr_last));
} else if (diff>0) {
u32 sleep_for=1;
#ifndef GPAC_DISABLE_LOG
u32 nb_sleep=0;
#endif
GF_NetworkCommand com;
com.command_type = GF_NET_BUFFER_QUERY;
com.base.on_channel = NULL;
while (ts->run_state) {
gf_service_command(m2ts->service, &com, GF_OK);
if (!com.buffer.occupancy || (com.buffer.occupancy < com.buffer.max)) {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[M2TS In] Demux not going to sleep: buffer occupancy %d ms\n", com.buffer.occupancy));
break;
}
sleep_for = MIN(com.buffer.occupancy/2, M2TS_MAX_SLEEP);
#ifndef GPAC_DISABLE_LOG
if (!nb_sleep) {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[M2TS In] Demux going to sleep for %d ms (buffer occupancy %d ms)\n", sleep_for, com.buffer.occupancy));
}
nb_sleep++;
#endif
gf_sleep(sleep_for);
}
#ifndef GPAC_DISABLE_LOG
if (nb_sleep) {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[M2TS In] Demux resume after %d ms - current buffer occupancy %d ms\n", sleep_for*nb_sleep, com.buffer.occupancy));
}
#endif
m2ts->pcr_last = pcr;
m2ts->stb_at_last_pcr = gf_sys_clock();
} else {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[M2TS In] Demux drift according to PCR (drift %d, pcr: "LLD", last pcr: "LLD")\n", diff, pcr, m2ts->pcr_last));
}
} else {
m2ts->pcr_last = pcr;
m2ts->stb_at_last_pcr = gf_sys_clock();
}
}
}
break;
case GF_M2TS_EVT_TDT:
if (m2ts->hybrid_on) {
u32 i, count;
GF_M2TS_TDT_TOT *tdt = (GF_M2TS_TDT_TOT *)param;
GF_NetworkCommand com;
memset(&com, 0, sizeof(com));
com.command_type = GF_NET_CHAN_MAP_TIME;
com.map_time.media_time = tdt->hour*3600+tdt->minute*60+tdt->second;
com.map_time.reset_buffers = 0;
count = gf_list_count(ts->programs);
for (i=0; i<count; i++) {
GF_M2TS_Program *prog = gf_list_get(ts->programs, i);
u32 j, count2;
if (prog->tdt_found || !prog->last_pcr_value)
continue;
prog->tdt_found = 1;
count2 = gf_list_count(prog->streams);
com.map_time.timestamp = prog->last_pcr_value/300;
for (j=0; j<count2; j++) {
GF_M2TS_ES * stream = gf_list_get(prog->streams, j);
if (stream->user) {
com.base.on_channel = stream->user;
gf_service_command(m2ts->service, &com, GF_OK);
}
}
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[M2TS In] Mapping TDT Time %04d/%02d/%02d %02d:%02d:%02d and PCR time "LLD" on program %d\n",
tdt->year, tdt->month, tdt->day, tdt->hour, tdt->minute, tdt->second, com.map_time.timestamp, prog->number));
}
}
break;
case GF_M2TS_EVT_TOT:
break;
case GF_M2TS_EVT_DURATION_ESTIMATED:
{
u32 i, count;
GF_NetworkCommand com;
memset(&com, 0, sizeof(com));
com.command_type = GF_NET_CHAN_DURATION;
com.duration.duration = (Double) ((GF_M2TS_PES_PCK *) param)->PTS;
com.duration.duration /= 1000;
count = gf_list_count(ts->programs);
for (i=0; i<count; i++) {
GF_M2TS_Program *prog = gf_list_get(ts->programs, i);
u32 j, count2;
count2 = gf_list_count(prog->streams);
for (j=0; j<count2; j++) {
GF_M2TS_ES * stream = gf_list_get(prog->streams, j);
if (stream->user) {
com.base.on_channel = stream->user;
gf_service_command(m2ts->service, &com, GF_OK);
}
}
}
}
break;
case GF_M2TS_EVT_TEMI_LOCATION:
{
GF_NetworkCommand com;
memset(&com, 0, sizeof(com));
com.addon_info.command_type = GF_NET_ASSOCIATED_CONTENT_LOCATION;
com.addon_info.external_URL = ((GF_M2TS_TemiLocationDescriptor*)param)->external_URL;
if (m2ts->force_temi_url)
com.addon_info.external_URL = m2ts->force_temi_url;
com.addon_info.is_announce = ((GF_M2TS_TemiLocationDescriptor*)param)->is_announce;
com.addon_info.is_splicing = ((GF_M2TS_TemiLocationDescriptor*)param)->is_splicing;
com.addon_info.activation_countdown = ((GF_M2TS_TemiLocationDescriptor*)param)->activation_countdown;
com.addon_info.timeline_id = ((GF_M2TS_TemiLocationDescriptor*)param)->timeline_id;
gf_service_command(m2ts->service, &com, GF_OK);
}
break;
case GF_M2TS_EVT_TEMI_TIMECODE:
{
GF_NetworkCommand com;
memset(&com, 0, sizeof(com));
com.addon_time.command_type = GF_NET_ASSOCIATED_CONTENT_TIMING;
com.addon_time.timeline_id = ((GF_M2TS_TemiTimecodeDescriptor*)param)->timeline_id;
com.addon_time.media_pts = ((GF_M2TS_TemiTimecodeDescriptor*)param)->pes_pts;
com.addon_time.media_timescale = ((GF_M2TS_TemiTimecodeDescriptor*)param)->media_timescale;
com.addon_time.media_timestamp = ((GF_M2TS_TemiTimecodeDescriptor*)param)->media_timestamp;
com.addon_time.force_reload = ((GF_M2TS_TemiTimecodeDescriptor*)param)->force_reload;
com.addon_time.is_paused = ((GF_M2TS_TemiTimecodeDescriptor*)param)->is_paused;
com.addon_time.ntp = ((GF_M2TS_TemiTimecodeDescriptor*)param)->ntp;
gf_service_command(m2ts->service, &com, GF_OK);
}
break;
}
}
void m2ts_net_io(void *cbk, GF_NETIO_Parameter *param)
{
GF_Err e;
M2TSIn *m2ts = (M2TSIn *) cbk;
assert( m2ts );
gf_service_download_update_stats(m2ts->ts->dnload);
if (param->msg_type==GF_NETIO_DATA_TRANSFERED) {
e = GF_EOS;
} else if (param->msg_type==GF_NETIO_DATA_EXCHANGE) {
e = GF_OK;
assert( m2ts->ts);
if (param->size > 0) {
assert(param->data);
if (m2ts->network_buffer_size < param->size) {
m2ts->network_buffer = gf_realloc(m2ts->network_buffer, sizeof(char) * param->size);
m2ts->network_buffer_size = param->size;
}
assert( m2ts->network_buffer );
memcpy(m2ts->network_buffer, param->data, param->size);
gf_m2ts_process_data(m2ts->ts, m2ts->network_buffer, param->size);
}
if (m2ts->ts->paused) {
while (m2ts->ts->run_state && m2ts->ts->paused ) {
gf_sleep(50);
continue;
}
} else {
gf_sleep(1);
}
#if 1
if (!m2ts->ts->run_state) {
if (m2ts->ts->dnload)
gf_service_download_del( m2ts->ts->dnload );
m2ts->ts->dnload = NULL;
}
#endif
} else {
e = param->error;
}
switch (e) {
case GF_EOS:
if (!m2ts->is_connected) {
gf_service_connect_ack(m2ts->service, NULL, GF_OK);
}
return;
case GF_OK:
return;
default:
if (!m2ts->ts_setup) {
m2ts->ts_setup = 1;
}
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER,("[MPEGTSIn] : Error while getting data : %s\n", gf_error_to_string(e)));
gf_service_connect_ack(m2ts->service, NULL, e);
}
}
typedef enum {
INIT_RANGE = 0,
NEXT_SEGMENT,
NEXT_SEGMENT_EXCEPT_DL,
DROP_NEXT_SEGMENT,
} DASHQueryType;
static GF_Err M2TS_QueryNextFile(void *udta, DASHQueryType query_type, const char **out_url, u64 *out_start_range, u64 *out_end_range, u32 *refresh_type)
{
GF_NetworkCommand param;
GF_Err query_ret;
M2TSIn *m2ts = (M2TSIn *) udta;
assert(m2ts->owner);
assert( m2ts->owner->query_proxy);
if (out_url) *out_url = NULL;
if (out_start_range) *out_start_range = 0;
if (out_end_range) *out_end_range = 0;
memset(¶m, 0, sizeof(GF_NetworkCommand));
param.command_type = (query_type == INIT_RANGE) ? GF_NET_SERVICE_QUERY_INIT_RANGE : GF_NET_SERVICE_QUERY_NEXT;
param.url_query.drop_first_segment = (query_type == DROP_NEXT_SEGMENT) ? 1 : 0;
param.url_query.current_download = (query_type == NEXT_SEGMENT_EXCEPT_DL) ? 0 : 1;
if (m2ts->in_segment_download)
param.url_query.drop_first_segment = 0;
query_ret = m2ts->owner->query_proxy(m2ts->owner, ¶m);
if ((query_ret==GF_BUFFER_TOO_SMALL) && query_type && !param.url_query.next_url) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[M2TS In] Cannot query next file: not yet downloaded\n"));
} else if ((query_ret==GF_OK) && query_type && !param.url_query.next_url) {
GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[M2TS In] Cannot query next file: no file provided but no error raised\n"));
} else if (query_ret) {
GF_LOG((query_ret<0) ? GF_LOG_ERROR : GF_LOG_INFO, GF_LOG_DASH, ("[M2TS In] Cannot query next file: error: %s\n", gf_error_to_string(query_ret)));
} else {
if (out_url) *out_url = param.url_query.next_url;
if (out_start_range) *out_start_range = param.url_query.start_range;
if (out_end_range) *out_end_range = param.url_query.end_range;
if (param.url_query.current_download) {
m2ts->low_latency_mode = 1;
if (refresh_type) *refresh_type = 1;
if (!m2ts->in_segment_download || param.url_query.has_new_data) {
GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[M2TS In] progressive loading of TS segments\n"));
}
m2ts->in_segment_download = 1;
if (!m2ts->first_chunk_in_seg_parsed) {
m2ts->first_chunk_in_seg_parsed=1;
m2ts->ts->pos_in_stream = 0;
}
} else {
if (refresh_type) {
if (m2ts->in_segment_download) {
GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[M2TS In] End of progressive loading of TS segments\n"));
*refresh_type = 2;
} else {
*refresh_type = 0;
}
}
m2ts->first_chunk_in_seg_parsed=0;
m2ts->in_segment_download = 0;
}
}
return query_ret;
}
enum
{
GF_M2TS_PUSH_SEGMENT,
GF_M2TS_PUSH_CHUNK,
GF_M2TS_FLUSH_DATA
};
void m2ts_flush_data(M2TSIn *m2ts, u32 flush_type)
{
u64 start_byterange, end_byterange;
GF_Err e = GF_OK;
u32 refresh_type = 0;
const char *url;
if (m2ts->in_data_flush) {
if (flush_type==GF_M2TS_PUSH_SEGMENT)
m2ts->has_pending_segments++;
return;
}
if (! gf_mx_try_lock(m2ts->mx)) {
if (flush_type==GF_M2TS_PUSH_SEGMENT)
m2ts->has_pending_segments++;
return;
}
m2ts->in_data_flush = 1;
if (flush_type<=GF_M2TS_PUSH_CHUNK) {
GF_NetworkCommand com;
memset(&com, 0, sizeof(GF_NetworkCommand));
com.command_type = GF_NET_BUFFER_QUERY;
gf_service_command(m2ts->service, &com, GF_OK);
if (com.buffer.occupancy && (com.buffer.occupancy >= com.buffer.max)) {
if (flush_type==GF_M2TS_PUSH_SEGMENT)
m2ts->has_pending_segments++;
m2ts->in_data_flush = 0;
gf_mx_v(m2ts->mx);
return;
}
}
else if (0 && flush_type==GF_M2TS_FLUSH_DATA) {
if (! m2ts->has_pending_segments) {
m2ts->in_data_flush = 0;
gf_mx_v(m2ts->mx);
return;
}
}
e = M2TS_QueryNextFile(m2ts, (flush_type==GF_M2TS_FLUSH_DATA) ? NEXT_SEGMENT_EXCEPT_DL : NEXT_SEGMENT, &url, &start_byterange, &end_byterange, &refresh_type);
if (e) {
m2ts->in_data_flush = 0;
if (e==GF_EOS) {
gf_m2ts_demux_file(m2ts->ts, NULL, 0, 0, 0, 1);
}
gf_mx_v(m2ts->mx);
return;
}
gf_m2ts_demux_file(m2ts->ts, url, start_byterange, end_byterange, refresh_type, 0);
if (!m2ts->in_segment_download) {
e = M2TS_QueryNextFile(m2ts, DROP_NEXT_SEGMENT, &url, &start_byterange, &end_byterange, &refresh_type);
if (m2ts->has_pending_segments)
m2ts->has_pending_segments--;
if (e==GF_EOS) {
gf_m2ts_demux_file(m2ts->ts, NULL, 0, 0, 0, 1);
}
else if (m2ts->in_segment_download && (flush_type != GF_M2TS_FLUSH_DATA) ) {
gf_m2ts_demux_file(m2ts->ts, url, start_byterange, end_byterange, refresh_type, 0);
}
}
m2ts->in_data_flush = 0;
gf_mx_v(m2ts->mx);
}
static GF_Err M2TS_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url)
{
GF_Err e;
const char *opt;
M2TSIn *m2ts = plug->priv;
M2TS_GetNetworkType(plug,m2ts);
m2ts->owner = plug;
opt = gf_modules_get_option((GF_BaseInterface *)m2ts->owner, "HybRadio", "Activated");
if (opt && !strcmp(opt, "true")) {
m2ts->hybrid_on = 1;
}
m2ts->ts->record_to = gf_modules_get_option((GF_BaseInterface *)m2ts->owner, "M2TS", "RecordTo");
m2ts->service = serv;
m2ts->force_temi_url = gf_modules_get_option((GF_BaseInterface *)m2ts->owner, "M2TS", "ForceTEMILocation");
if (m2ts->force_temi_url && !strlen(m2ts->force_temi_url)) m2ts->force_temi_url = NULL;
opt = gf_modules_get_option((GF_BaseInterface *)m2ts->owner, "DSMCC", "Activated");
if (opt && !strcmp(opt, "yes")) {
gf_m2ts_demux_dmscc_init(m2ts->ts);
}
if (url && !strnicmp(url, "http://", 7)) {
m2ts->ts->dnload = gf_service_download_new(m2ts->service, url, GF_NETIO_SESSION_NOT_THREADED | GF_NETIO_SESSION_NOT_CACHED | GF_NETIO_SESSION_NOTIFY_DATA, m2ts_net_io, m2ts);
if (!m2ts->ts->dnload) {
gf_service_connect_ack(m2ts->service, NULL, GF_NOT_SUPPORTED);
return GF_OK;
} else {
e = gf_m2ts_demuxer_play(m2ts->ts);
}
} else {
if (plug->query_proxy) {
if (url) {
u64 start_byterange, end_byterange;
gf_mx_p(m2ts->mx);
m2ts->in_data_flush = 1;
M2TS_QueryNextFile(m2ts, INIT_RANGE, NULL, &start_byterange, &end_byterange, NULL);
e = gf_m2ts_demux_file(m2ts->ts, url, start_byterange, end_byterange, 0, 0);
M2TS_QueryNextFile(m2ts, DROP_NEXT_SEGMENT, NULL, NULL, NULL, NULL);
m2ts->in_data_flush = 0;
gf_mx_v(m2ts->mx);
} else {
e = GF_OK;
}
m2ts->ts->run_state = 1;
} else {
e = gf_m2ts_demuxer_setup(m2ts->ts,url,0);
}
}
if (e) {
gf_service_connect_ack(m2ts->service, NULL, e);
}
return e;
}
static GF_Err M2TS_CloseService(GF_InputService *plug)
{
M2TSIn *m2ts = plug->priv;
GF_M2TS_Demuxer* ts = m2ts->ts;
if (!plug->query_proxy)
gf_m2ts_demuxer_close(ts);
m2ts->ts->run_state = 0;
if (ts->dnload) gf_service_download_del(ts->dnload);
ts->dnload = NULL;
gf_service_disconnect_ack(m2ts->service, NULL, GF_OK);
return GF_OK;
}
static GF_Descriptor *M2TS_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url)
{
M2TSIn *m2ts = plug->priv;
GF_Descriptor *desc = NULL;
char *frag;
M2TSIn_Prog *prog;
if (sub_url && !strnicmp(sub_url, "pid://", 6)) {
GF_ObjectDescriptor *od;
u32 pid = atoi(sub_url+6);
if (pid>=GF_M2TS_MAX_STREAMS) return NULL;
od = MP2TS_GetOD(m2ts, (GF_M2TS_PES*) m2ts->ts->ess[pid], NULL, 0, NULL);
return (GF_Descriptor *) od;
}
frag = sub_url ? strrchr(sub_url, '#') : NULL;
if (frag) frag++;
if (!frag && sub_url && !strncmp(sub_url, "dvb://", 6)) {
frag = (char*)sub_url + 6;
}
if (!frag) {
m2ts->request_all_pids = 1;
} else {
gf_mx_p(m2ts->mx);
if (!strnicmp(frag, "pid=", 4)) {
GF_SAFEALLOC(prog, M2TSIn_Prog);
if (!prog) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[M2TSIn] Fail to allocate pid playback request"));
return NULL;
}
prog->pid = atoi(frag+4);
gf_list_add(m2ts->ts->requested_pids, prog);
} else if (!strnicmp(frag, "EPG", 3)) {
m2ts->epg_requested = 1;
} else {
u32 i, count;
count = gf_list_count(m2ts->ts->requested_progs);
prog = NULL;
for (i=0; i<count; i++) {
prog = gf_list_get(m2ts->ts->requested_progs, i);
if (!strcmp(prog->fragment, frag))
break;
prog = NULL;
}
if (!prog) {
GF_SAFEALLOC(prog, M2TSIn_Prog);
if (!prog) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[M2TSIn] Fail to allocate URI fragment playback request"));
return NULL;
}
gf_list_add(m2ts->ts->requested_progs, prog);
prog->fragment = gf_strdup(frag);
}
}
gf_mx_v(m2ts->mx);
}
if (expect_type<=GF_MEDIA_OBJECT_SCENE) {
if (gf_list_count(m2ts->ts->programs) == 1) {
GF_M2TS_Program *prog = gf_list_get(m2ts->ts->programs, 0);
if (prog->pmt_iod) {
m2ts->request_all_pids = 0;
gf_odf_desc_copy((GF_Descriptor *)prog->pmt_iod, &desc);
((GF_InitialObjectDescriptor *)desc)->service_ifce = m2ts->owner;
return desc;
}
}
if (expect_type==GF_MEDIA_OBJECT_SCENE)
return NULL;
if (m2ts->epg_requested) {
GF_ObjectDescriptor *od = M2TS_GenerateEPG_OD(m2ts);
m2ts->epg_requested = 0;
return (GF_Descriptor *)od;
} else {
desc = gf_odf_desc_new(GF_ODF_IOD_TAG);
((GF_ObjectDescriptor *) desc)->objectDescriptorID = 1;
return desc;
}
}
if (!plug->query_proxy) {
if (m2ts->ts->run_state == 2) {
m2ts->file_regulate = 0;
gf_m2ts_demuxer_play(m2ts->ts);
}
}
return NULL;
}
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);
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)
|| (!pes->mpeg4_es_id && (pes->pid == ES_ID))
) {
if (pes->user) {
e = GF_SERVICE_ERROR;
gf_service_connect_ack(m2ts->service, channel, e);
return e;
} else {
pes->user = channel;
e = GF_OK;
gf_service_connect_ack(m2ts->service, channel, e);
return e;
}
}
}
return e;
}
}
if ((ES_ID == 18) && (!m2ts->ts->ess[18] || !(m2ts->ts->ess[18]->flags & GF_M2TS_ES_IS_PES)) ) {
e = GF_OK;
m2ts->eit_channel = channel;
} else if (ES_ID<GF_M2TS_MAX_STREAMS) {
u32 i, j, count, count2;
e = GF_SERVICE_ERROR;
count = gf_list_count(m2ts->ts->programs);
for (i=0; i<count; i++) {
GF_M2TS_Program *prog = gf_list_get(m2ts->ts->programs, i);
count2 = gf_list_count(prog->streams);
for (j=0; j<count2; j++) {
GF_M2TS_PES *pes = (GF_M2TS_PES *)gf_list_get(prog->streams, j);
if ((pes->pid == ES_ID) && !pes->user) {
pes->user = channel;
if (pes->program->pid_playing < pes->pid)
pes->program->pid_playing = pes->pid;
e = GF_OK;
break;
}
}
}
}
}
gf_service_connect_ack(m2ts->service, channel, e);
return e;
}
static GF_M2TS_PES *M2TS_GetChannel(M2TSIn *m2ts, LPNETCHANNEL channel)
{
u32 i, j, count, count2;
count = gf_list_count(m2ts->ts->programs);
for (i=0; i<count; i++) {
GF_M2TS_Program *prog = gf_list_get(m2ts->ts->programs, i);
count2 = gf_list_count(prog->streams);
for (j=0; j<count2; j++) {
GF_M2TS_PES *pes = (GF_M2TS_PES *)gf_list_get(prog->streams, j);
if (pes->user == channel) return pes;
}
}
return NULL;
}
static GF_M2TS_PES *M2TS_GetFirstRunningChannel(M2TSIn *m2ts)
{
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) continue;
if (gf_m2ts_pes_get_framing_mode(pes) >= GF_M2TS_PES_FRAMING_DEFAULT)
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_service_disconnect_ack(m2ts->service, channel, e);
return GF_OK;
}
static void gf_m2ts_switch_quality(GF_M2TS_Program *prog, GF_M2TS_Demuxer *ts, Bool switch_up)
{
GF_M2TS_ES *es;
u32 i, count;
GF_NetworkCommand com;
if (!prog->is_scalable)
return;
if (switch_up) {
for (i = 0; i < GF_M2TS_MAX_STREAMS; i++) {
es = ts->ess[i];
if (es && (es->flags & GF_M2TS_ES_IS_PES) && (((GF_M2TS_PES *)es)->depends_on_pid == prog->pid_playing)) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("Turn on ES%d\n", es->pid));
gf_m2ts_set_pes_framing((GF_M2TS_PES *)ts->ess[es->pid], GF_M2TS_PES_FRAMING_DEFAULT);
prog->pid_playing = es->pid;
return;
}
}
}
else {
count = gf_list_count(prog->streams);
for (i = 0; i < count; i++) {
es = (GF_M2TS_ES *)gf_list_get(prog->streams, i);
if (es && (es->pid == prog->pid_playing) && ((GF_M2TS_PES *)es)->depends_on_pid) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("Turn off ES%d - playing ES%d\n", es->pid, ((GF_M2TS_PES *)es)->depends_on_pid));
gf_m2ts_set_pes_framing((GF_M2TS_PES *)ts->ess[es->pid], GF_M2TS_PES_FRAMING_SKIP);
memset(&com, 0, sizeof(com));
com.command_type = GF_NET_CHAN_RESET;
com.base.on_channel = ((GF_M2TS_PES *)es)->user;
gf_service_command(((M2TSIn *)ts->user)->service, &com, GF_OK);
prog->pid_playing = ((GF_M2TS_PES *)es)->depends_on_pid;
return;
}
}
}
}
static GF_Err M2TS_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com)
{
GF_M2TS_PES *pes;
M2TSIn *m2ts = plug->priv;
GF_M2TS_Demuxer *ts = m2ts->ts;
if (com->command_type==GF_NET_SERVICE_HAS_AUDIO) {
char *frag = strchr(com->audio.base_url, '#');
if (frag && !strnicmp(frag, "#pid=", 5)) return GF_NOT_SUPPORTED;
return GF_OK;
}
if (com->command_type == GF_NET_SERVICE_QUALITY_SWITCH) {
u32 i, count;
count = gf_list_count(ts->programs);
for (i = 0; i < count; i++) {
GF_M2TS_Program *prog = (GF_M2TS_Program *)gf_list_get(ts->programs, i);
gf_m2ts_switch_quality(prog, ts, com->switch_quality.up);
}
return GF_OK;
}
if (com->command_type == GF_NET_SERVICE_PROXY_DATA_RECEIVE) {
m2ts_flush_data(m2ts, com->proxy_data.is_chunk ? GF_M2TS_PUSH_CHUNK : GF_M2TS_PUSH_SEGMENT);
return GF_OK;
}
if (com->command_type == GF_NET_SERVICE_FLUSH_DATA) {
if (plug->query_proxy)
m2ts_flush_data(m2ts, GF_M2TS_FLUSH_DATA);
return GF_OK;
}
if (com->command_type==GF_NET_SERVICE_INFO) {
u32 id = com->info.service_id;
if (!id) {
pes = M2TS_GetFirstRunningChannel(m2ts);
if (pes) id = pes->program->number;
}
if (id) {
GF_M2TS_SDT *sdt = gf_m2ts_get_sdt_info(ts, id);
if (sdt) {
com->info.name = sdt->service;
com->info.provider = sdt->provider;
}
} else {
m2ts->flush_sdt = 1;
}
return GF_OK;
}
if (!com->base.on_channel) return GF_NOT_SUPPORTED;
switch (com->command_type) {
case GF_NET_CHAN_SET_PULL:
return GF_NOT_SUPPORTED;
case GF_NET_CHAN_INTERACTIVE:
if (m2ts->ts->file) return GF_OK;
return GF_NOT_SUPPORTED;
case GF_NET_CHAN_BUFFER:
if (ts->dnload || plug->query_proxy) {
if (!com->buffer.max) com->buffer.max = 1000;
} else if (ts->file) {
com->buffer.max = com->buffer.occupancy = M2TS_MAX_SLEEP;
}
return GF_OK;
case GF_NET_CHAN_DURATION:
com->duration.duration = ts->duration;
return GF_OK;
case GF_NET_CHAN_PLAY:
pes = M2TS_GetChannel(m2ts, com->base.on_channel);
if (!pes) {
if (com->base.on_channel == m2ts->eit_channel) {
return GF_OK;
}
return GF_STREAM_NOT_FOUND;
}
if (pes->program->pcr_pid==pes->pid) pes->program->first_dts=0;
gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_DEFAULT);
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[M2TSIn] Setting default reframing for PID %d\n", pes->pid));
if (!m2ts->nb_playing) {
if (ts->file) {
ts->start_range = (u32) (com->play.start_range*1000);
ts->end_range = (com->play.end_range>0) ? (u32) (com->play.end_range*1000) : 0;
m2ts->media_start_range = com->play.start_range;
}
if (!m2ts->map_media_time_on_prog_id)
m2ts->map_media_time_on_prog_id = pes->program->number;
if (com->play.dash_segment_switch) {
m2ts->media_start_range = com->play.start_range;
gf_m2ts_abort_parsing(ts, GF_TRUE);
}
if (!plug->query_proxy) {
if (ts->run_state!=1) {
gf_m2ts_demuxer_play(ts);
}
}
}
else if (!m2ts->map_media_time_on_prog_id) {
m2ts->media_start_range = com->play.start_range;
m2ts->map_media_time_on_prog_id = pes->program->number;
}
if (!m2ts->nb_playing) {
gf_m2ts_pause_demux(ts, 0);
}
m2ts->nb_playing++;
return GF_OK;
case GF_NET_CHAN_STOP:
pes = M2TS_GetChannel(m2ts, com->base.on_channel);
if (!pes) {
if (com->base.on_channel == m2ts->eit_channel) {
return GF_OK;
}
return GF_STREAM_NOT_FOUND;
}
if (m2ts->nb_playing)
m2ts->nb_playing--;
if (!plug->query_proxy) {
if (!m2ts->nb_playing && (ts->run_state==1) && !m2ts->nb_prog_to_setup) {
ts->run_state=0;
while (ts->run_state!=2) gf_sleep(2);
if (gf_list_count(m2ts->ts->requested_progs)) {
m2ts->file_regulate = 0;
gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_SKIP);
return gf_m2ts_demuxer_play(ts);
}
}
}
gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_SKIP);
return GF_OK;
case GF_NET_CHAN_CONFIG:
pes = M2TS_GetChannel(m2ts, com->base.on_channel);
if (pes && pes->flags & GF_M2TS_ES_IS_SECTION) {
if (pes->slcfg) gf_free(pes->slcfg);
pes->slcfg = gf_malloc(sizeof(GF_SLConfig));
memcpy(pes->slcfg, &com->cfg.sl_config, sizeof(GF_SLConfig));
com->cfg.use_m2ts_sections = 1;
pes->flags |= GF_M2TS_ES_SEND_REPEATED_SECTIONS;
}
return GF_OK;
case GF_NET_CHAN_PAUSE:
if (!m2ts->nb_paused) {
gf_m2ts_pause_demux(m2ts->ts, 1);
}
m2ts->nb_paused++;
return GF_OK;
case GF_NET_CHAN_RESUME:
if (m2ts->nb_paused) {
m2ts->nb_paused--;
if (!m2ts->nb_paused) {
m2ts->pcr_last = 0;
gf_m2ts_pause_demux(m2ts->ts, 0);
}
}
return GF_OK;
default:
return GF_OK;
}
}
static u32 M2TS_RegisterMimeTypes(const GF_InputService * service) {
int i;
if (service == NULL)
return 0;
for (i = 0 ; MIMES[i]; i++)
gf_service_register_mime( service, MIMES[i], M2TS_EXTENSIONS, "MPEG-2 TS");
return i;
}
static void M2TS_GetNetworkType(GF_InputService *plug,M2TSIn *reader)
{
const char *mob_on;
const char *mcast_ifce;
mob_on = gf_modules_get_option((GF_BaseInterface*)plug, "Network", "MobileIPEnabled");
if(mob_on && !strcmp(mob_on, "yes")) {
reader->ts->MobileIPEnabled = 1;
reader->ts->network_type = gf_modules_get_option((GF_BaseInterface*)plug, "Network", "MobileIP");
}
mcast_ifce = gf_modules_get_option((GF_BaseInterface*)plug, "Network", "DefaultMCastInterface");
if(mcast_ifce) reader->ts->network_type = mcast_ifce;
}
GF_InputService *NewM2TSReader()
{
M2TSIn *reader;
GF_InputService *plug = gf_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->CanHandleURLInService = M2TS_CanHandleURLInService;
plug->ConnectService = M2TS_ConnectService;
plug->CloseService = M2TS_CloseService;
plug->GetServiceDescriptor = M2TS_GetServiceDesc;
plug->ConnectChannel = M2TS_ConnectChannel;
plug->DisconnectChannel = M2TS_DisconnectChannel;
plug->ServiceCommand = M2TS_ServiceCommand;
plug->RegisterMimeTypes = M2TS_RegisterMimeTypes;
reader = gf_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;
reader->ts->demux_and_play = 1;
reader->ts->th = gf_th_new("MPEG-2 TS Demux");
reader->mx = gf_mx_new("MPEG2 Demux");
return plug;
}
void DeleteM2TSReader(void *ifce)
{
u32 i, count;
M2TSIn *m2ts;
GF_InputService *plug = (GF_InputService *) ifce;
if (!ifce)
return;
m2ts = plug->priv;
if (!m2ts)
return;
if( m2ts->ts->requested_progs ) {
count = gf_list_count(m2ts->ts->requested_progs);
for (i = 0; i < count; i++) {
M2TSIn_Prog *prog = gf_list_get(m2ts->ts->requested_progs, i);
gf_free(prog->fragment);
gf_free(prog);
}
gf_list_del(m2ts->ts->requested_progs);
m2ts->ts->requested_progs = NULL;
}
if( m2ts->ts->requested_pids ) {
count = gf_list_count(m2ts->ts->requested_pids);
for (i = 0; i < count; i++) {
M2TSIn_Prog *prog = gf_list_get(m2ts->ts->requested_pids, i);
gf_free(prog);
}
gf_list_del(m2ts->ts->requested_pids);
m2ts->ts->requested_pids = NULL;
}
if (m2ts->network_buffer)
gf_free(m2ts->network_buffer);
m2ts->network_buffer = NULL;
gf_m2ts_demux_del(m2ts->ts);
gf_mx_del(m2ts->mx);
gf_free(m2ts);
gf_free(plug);
}
#endif
GPAC_MODULE_EXPORT
const u32 *QueryInterfaces()
{
static u32 si [] = {
#ifndef GPAC_DISABLE_MPEG2TS
GF_NET_CLIENT_INTERFACE,
#endif
0
};
return si;
}
GPAC_MODULE_EXPORT
GF_BaseInterface *LoadInterface(u32 InterfaceType)
{
switch (InterfaceType) {
#ifndef GPAC_DISABLE_MPEG2TS
case GF_NET_CLIENT_INTERFACE:
return (GF_BaseInterface *) NewM2TSReader();
#endif
default:
return NULL;
}
}
GPAC_MODULE_EXPORT
void ShutdownInterface(GF_BaseInterface *ifce)
{
switch (ifce->InterfaceType) {
#ifndef GPAC_DISABLE_MPEG2TS
case GF_NET_CLIENT_INTERFACE:
DeleteM2TSReader(ifce);
break;
#endif
}
}
GPAC_MODULE_STATIC_DECLARATION( mpegts_in )