#include "saf_demuxer.h"

#define SAFD_DATA_BUFFER	800

u32 SAFDemux_Run(void *par)
{
	u32 i;
	s32 time;
	NetworkCommand com;
	SLHeader slh;
	u8 accessUnitType;
	u8 streamID;
	unsigned char *au_data;
	u32 au_length;
	LPNETCHANNEL channel;

	SAFDemux *safd = (SAFDemux *) par;
	
	memset(&com, 0, sizeof(NetworkCommand));
	com.command_type = CHAN_BUFFER_QUERY;

	memset(&slh, 0, sizeof(SLHeader));

	while (safd->is_running) {
		
		slh.accessUnitStartFlag = BS_ReadBit(safd->bs); 
		slh.accessUnitEndFlag = BS_ReadBit(safd->bs); 
		slh.packetSequenceNumber = BS_ReadInt(safd->bs, 14); 
		BS_ReadBit(safd->bs); 
		slh.degradationPriority = BS_ReadInt(safd->bs, 4); 
		slh.randomAccessPointFlag = BS_ReadBit(safd->bs); 
		BS_ReadBit(safd->bs); 
		BS_ReadBit(safd->bs); 
		slh.compositionTimeStampFlag = slh.decodingTimeStampFlag = 1;
		time = BS_ReadInt(safd->bs, 32); 
		if (time <= 0) slh.decodingTimeStamp = slh.compositionTimeStamp = 0;
		else slh.decodingTimeStamp = slh.compositionTimeStamp = time;
		slh.accessUnitLength = BS_ReadInt(safd->bs, 16); 

		accessUnitType = BS_ReadInt(safd->bs, 8); 
		streamID = BS_ReadInt(safd->bs, 8); 
	
		if (streamID == safd->laser_info.es_id) {
			channel = safd->laser_info.channel;
		}
		else if (streamID == safd->od_info.es_id) {
			channel = safd->od_info.channel;
		}
		else {
			for (i = 0; i < ChainGetCount(safd->stream_infos); i++) {
				SAFStreamInfo *stream_info = ChainGetEntry(safd->stream_infos, i);
				if (streamID == stream_info->es_id) {
					channel = stream_info->channel;
				}
				break;
			}
		}

		MX_P(safd->mx);

		switch (accessUnitType) {
			case 0x00:
				/* Reserved AU type */
				break;
			case 0x01:
				/* Scene Header */
				/* Should not appear here.
				   Parsing of the scene header is done at initialization of the service
				   when creating the IOD */
				break;
			case 0x02:
				/* End of SAF Session */
				safd->is_running = 0;
				break;
				break;
			case 0x03:
				/* End of Stream */
				NM_OnSLPRecieved(safd->service, channel, NULL, 0, NULL, M4EOF);
				break;
			case 0x04:
			case 0x05:
			case 0x06:
				/* Media Header or Font Header */
				{
					/* Creates an OD Update command with a new OD 
					   containing the SAF SimpleDecoderConfigDescriptor in the MPEG-4 DCD */
					LPODCODEC odc;
					ODCommand *com;
					ObjectDescriptor *OD;
					ESDescriptor *esd;
					SAFStreamInfo *stream_info;

					stream_info = malloc(sizeof(SAFStreamInfo));
					stream_info->channel = NULL;
					stream_info->es_id = streamID;
					stream_info->status = NM_Unavailable;
					ChainAddEntry(safd->stream_infos, stream_info);

					com = OD_NewCommand(ODUpdate_Tag);
			
					OD = (ObjectDescriptor *) OD_NewDescriptor(ObjectDescriptor_Tag);
					OD->objectDescriptorID = streamID + 1;

					esd = (ESDescriptor *) OD_NewESDescriptor(0);
					esd->ESID = streamID + 1;
					esd->OCRESID = safd->laser_info.es_id;
					esd->decoderConfig->objectTypeIndication = BS_ReadInt(safd->bs, 8);
					esd->decoderConfig->streamType = BS_ReadInt(safd->bs, 8);
					esd->decoderConfig->bufferSizeDB = BS_ReadInt(safd->bs, 16);

					esd->decoderConfig->decoderSpecificInfo->dataLength = slh.accessUnitLength - 6;		
					esd->decoderConfig->decoderSpecificInfo->tag = DecoderSpecificInfo_Tag;
					esd->decoderConfig->decoderSpecificInfo->data = malloc(esd->decoderConfig->decoderSpecificInfo->dataLength);
					memset(esd->decoderConfig->decoderSpecificInfo->data, 0, esd->decoderConfig->decoderSpecificInfo->dataLength);
					BS_ReadData(safd->bs, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength);
				
					/*we configure the Generic SL into SAF SL */
					esd->slConfig->useAccessUnitStartFlag = esd->slConfig->useAccessUnitEndFlag = 1;
					esd->slConfig->hasRandomAccessUnitsOnlyFlag = 1;
					esd->slConfig->useTimestampsFlag = 1;
					esd->slConfig->timestampResolution = 1000;
					esd->slConfig->timestampLength = 32;
					esd->slConfig->AULength = 16;
					esd->slConfig->degradationPriorityLength = 4;
					esd->slConfig->packetSeqNumLength = 14;

					OD_AddDescToDesc((Descriptor *)OD, (Descriptor *) esd);

					ChainAddEntry( ((ObjectDescriptorUpdate*)com)->objectDescriptors, OD);

					odc = OD_NewCodec(OD_WRITE);
					OD_AddCommand(odc, com);
					OD_EncodeAU(odc);
					OD_GetEncodedAU(odc, &au_data, &au_length);
					OD_DeleteCodec(odc);

					NM_OnSLPRecieved(safd->service, safd->od_info.channel, au_data, au_length, &slh, M4OK);
					free(au_data);
				}
				break;
			case 0x07:
				/* Scene Unit */
				au_length = slh.accessUnitLength - 2;
				au_data = malloc(au_length);
				memset(au_data, 0, au_length);
				BS_ReadData(safd->bs, au_data, au_length);
				NM_OnSLPRecieved(safd->service, channel, au_data, au_length, &slh, M4OK);
				free(au_data);
				break;
			case 0x08:
				/* Media Unit */
				au_length = slh.accessUnitLength - 2;
				au_data = malloc(au_length);
				memset(au_data, 0, au_length);
				BS_ReadData(safd->bs, au_data, au_length);
				//NM_OnSLPRecieved(safd->service, channel, au_data, au_length, &slh, M4OK);
				free(au_data);
				break;
			case 0x09:
				/* Image Unit */
				au_length = slh.accessUnitLength - 2;
				au_data = malloc(au_length);
				memset(au_data, 0, au_length);
				BS_ReadData(safd->bs, au_data, au_length);
				//NM_OnSLPRecieved(safd->service, channel, au_data, au_length, &slh, M4OK);
				free(au_data);
				break;
			case 0x0A:
				/* Image Unit (permanent) */
				au_length = slh.accessUnitLength - 2;
				au_data = malloc(au_length);
				memset(au_data, 0, au_length);
				BS_ReadData(safd->bs, au_data, au_length);
				//NM_OnSLPRecieved(safd->service, channel, au_data, au_length, &slh, M4OK);
				free(au_data);
				break;
			case 0x0B:
				/* Font Unit */
				au_length = slh.accessUnitLength - 2;
				au_data = malloc(au_length);
				memset(au_data, 0, au_length);
				BS_ReadData(safd->bs, au_data, au_length);
				//NM_OnSLPRecieved(safd->service, channel, au_data, au_length, &slh, M4OK);
				free(au_data);
				break;
			case 0x0C:
			case 0x0D:
			case 0x0E:
			case 0x0F:
				/* Cache Unit */
				au_length = slh.accessUnitLength - 2;
				au_data = malloc(au_length);
				memset(au_data, 0, au_length);
				BS_ReadData(safd->bs, au_data, au_length);
				//NM_OnSLPRecieved(safd->service, channel, au_data, au_length, &slh, M4OK);
				free(au_data);
				break;
		}
		

		MX_V(safd->mx);

		/*sleep untill the buffer occupancy is too low - note that this work because all streams in this
		demuxer are synchronized*/
		while (1) {
			{
				com.on_channel = safd->laser_info.channel;
				NM_OnCommand(safd->service, &com);
				if (com.buffer_occupancy < SAFD_DATA_BUFFER) break;
			}
			for (i = 0; i < ChainGetCount(safd->stream_infos); i++) {
				SAFStreamInfo *stream_info = ChainGetEntry(safd->stream_infos, i);
				com.on_channel = stream_info->channel;
				NM_OnCommand(safd->service, &com);
				if (com.buffer_occupancy < SAFD_DATA_BUFFER) break;
			}
			M4_Sleep(10);
			
			if (safd->laser_info.status != NM_Running) break;
		}
		if (safd->laser_info.status != NM_Running) break;
	}
	
	return 0;
}

/*retuns 1 if plugin can process this URL, 0 otherwise*/
static Bool SAFD_CanHandleURL(struct _netinterface *plug, const char *url)
{
	char *sExt;
	sExt = strrchr(url, '.');
	if (!sExt) return 0;
	if (NM_CheckExtension(plug, "video/x-laser-saf", "saf lsr", sExt)) return 1;
	return 0;
}

/*connects the service to the desired URL - the service handle is used for callbacks. 
Only one service can be connected to a loaded interface.
*/
static M4Err SAFD_ConnectService (struct _netinterface *plug, LPNETSERVICE serv, const char *url)
{
	M4Err e;
	u32 i;
	SAFDemux *safd = plug->priv;
	char szName[1000];

	strcpy(szName, url);
	if (safd->stream) return M4ServiceError;

	safd->stream = fopen(url, "rb");
	
	if (safd->stream) safd->bs = NewBitStreamFromFile(safd->stream, BS_FILE_READ);

	e = M4UnsupportedURL;
	safd->service = serv;

	safd->laser_info.channel = NULL;
	safd->laser_info.es_id = 0;
	safd->laser_info.status = NM_Unavailable;

	safd->od_info.channel = NULL;
	safd->od_info.es_id = 255;
	safd->od_info.status = NM_Unavailable;
	
	if (!(safd->stream_infos)) goto err_exit;
	for (i=0; i<ChainGetCount(safd->stream_infos); i++) {
		SAFStreamInfo *info = ChainGetEntry(safd->stream_infos, i);
		info->channel = NULL;
		info->es_id	= -1;
		info->status = NM_Unavailable;
	}

	/*let's go*/
	NM_OnConnect(serv, NULL, M4OK);
	return M4OK;

err_exit:
    fclose(safd->stream);
	safd->stream = NULL;
	safd->laser_info.status = safd->od_info.status = NM_Unavailable;
	NM_OnConnect(serv, NULL, e);
	return M4OK;
}

static M4Err SAFD_CloseService(NetClientPlugin *plug, Bool immediate_shutdown)
{
	u32 i;

	SAFDemux *safd = plug->priv;

	safd->is_running = 0;

	if (safd->stream) fclose(safd->stream);
	safd->stream = NULL;

	safd->laser_info.channel = NULL;
	safd->laser_info.es_id = -1;
	safd->laser_info.status = NM_Unavailable;

	safd->od_info.channel = NULL;
	safd->od_info.es_id = -1;
	safd->od_info.status = NM_Unavailable;
	
	for (i=0; i<ChainGetCount(safd->stream_infos); i++) {
		SAFStreamInfo *info = ChainGetEntry(safd->stream_infos, i);
		info->channel = NULL;
		info->es_id	= -1;
		info->status = NM_Unavailable;
	}

	NM_OnDisconnect(safd->service, NULL, M4OK);

	return M4OK;
}

static M4Err SAFD_Get_MPEG4_IOD(NetClientPlugin *plug, u32 expect_type, const char *sub_url, char **raw_iod, u32 *raw_iod_size)
{
	M4Err e;
	InitialObjectDescriptor *iod;
	ESDescriptor *esd;
	SAFDemux *safd = plug->priv;

	if (!safd->stream) return M4ServiceError;

	iod = (InitialObjectDescriptor *) OD_NewDescriptor(InitialObjectDescriptor_Tag);
	iod->objectDescriptorID = 1;

	{
		SLHeader slh;
		u8 accessUnitType;

		slh.accessUnitStartFlag = BS_ReadBit(safd->bs); 
		slh.accessUnitEndFlag = BS_ReadBit(safd->bs); 
		slh.packetSequenceNumber = BS_ReadInt(safd->bs, 14); 
		BS_ReadBit(safd->bs); 
		slh.degradationPriority = BS_ReadInt(safd->bs, 4); 
		slh.randomAccessPointFlag = BS_ReadBit(safd->bs); 
		BS_ReadBit(safd->bs); 
		BS_ReadBit(safd->bs); 
		slh.decodingTimeStamp = slh.compositionTimeStamp = BS_ReadInt(safd->bs, 32); 
		slh.accessUnitLength = BS_ReadInt(safd->bs, 16); 

		accessUnitType = BS_ReadInt(safd->bs, 8); 
		safd->laser_info.es_id = BS_ReadInt(safd->bs, 8); 

		if (accessUnitType != 1) return M4ServiceError;

		/*setup LASeR stream*/
		esd = (ESDescriptor *) OD_NewESDescriptor(0);
		esd->ESID = safd->laser_info.es_id + 1;
		esd->OCRESID = 0;
		esd->decoderConfig->objectTypeIndication = BS_ReadInt(safd->bs, 8);
		esd->decoderConfig->streamType = BS_ReadInt(safd->bs, 8);
		esd->decoderConfig->bufferSizeDB = BS_ReadInt(safd->bs, 16);

		esd->decoderConfig->decoderSpecificInfo->dataLength = slh.accessUnitLength - 6;
		esd->decoderConfig->decoderSpecificInfo->tag = DecoderSpecificInfo_Tag;
		esd->decoderConfig->decoderSpecificInfo->data = malloc(esd->decoderConfig->decoderSpecificInfo->dataLength);
		memset(esd->decoderConfig->decoderSpecificInfo->data, 0, esd->decoderConfig->decoderSpecificInfo->dataLength);
		BS_ReadData(safd->bs, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength);
	}

	/*we configure the Generic SL into SAF SL */
	esd->slConfig->useAccessUnitStartFlag = esd->slConfig->useAccessUnitEndFlag = 1;
	esd->slConfig->hasRandomAccessUnitsOnlyFlag = 1;
	esd->slConfig->useTimestampsFlag = 1;
	esd->slConfig->timestampResolution = 1000;
	esd->slConfig->timestampLength = 32;
	esd->slConfig->AULength = 16;
	esd->slConfig->degradationPriorityLength = 4;
	esd->slConfig->packetSeqNumLength = 14;

	OD_AddDescToDesc((Descriptor *)iod, (Descriptor *) esd);

	/*setup OD stream (no DSI)*/
	esd = (ESDescriptor *) OD_NewESDescriptor(0);
	esd->ESID = safd->od_info.es_id + 1;
	esd->OCRESID = safd->laser_info.es_id + 1;
	esd->decoderConfig->streamType = M4ST_OD;
	esd->decoderConfig->objectTypeIndication = 0x01;
	
	/*we send 1 AU RAP each time a SAF media header arrives*/
	esd->slConfig->useAccessUnitStartFlag = esd->slConfig->useAccessUnitEndFlag = 0;
	esd->slConfig->hasRandomAccessUnitsOnlyFlag = 1;
	esd->slConfig->useTimestampsFlag = 1;
	esd->slConfig->timestampResolution = 1000;
	OD_AddDescToDesc((Descriptor *)iod, (Descriptor *) esd);

	e = OD_EncDesc((Descriptor *)iod, raw_iod, raw_iod_size);
	OD_DeleteDescriptor((Descriptor **) &iod);
	return e;
}

static M4Err SAFD_ConnectChannel(NetClientPlugin *plug, LPNETCHANNEL channel, const char *url, Bool upstream)
{
	M4Err e;
	u32 ESID;
	u32 i;
	SAFDemux *safd = plug->priv;

	e = M4OK;
	if (upstream) {
		e = M4InvalidMP4File;
		goto exit;
	}
	if (!strstr(url, "ES_ID=")) {
		e = M4UnsupportedURL;
		goto exit;
	}
	sscanf(url, "ES_ID=%d", &ESID);
	ESID--;

	if ((s32)ESID==safd->laser_info.es_id) {
		if (safd->laser_info.channel) {
			e = M4ServiceError;
			goto exit;
		}
		safd->laser_info.channel = channel;
		safd->laser_info.status = NM_Connected;
	}
	else if ((s32)ESID==safd->od_info.es_id) {
		if (safd->od_info.channel) {
			e = M4ServiceError;
			goto exit;
		}
		safd->od_info.channel = channel;
		safd->od_info.status = NM_Connected;
	}
	else {
		for (i = 0; i < ChainGetCount(safd->stream_infos); i++) {
			SAFStreamInfo *stream_info = ChainGetEntry(safd->stream_infos, i);
			if ((s32)ESID==stream_info->es_id) {
				if (stream_info->channel) {
					e = M4ServiceError;
					goto exit;
				}
				stream_info->channel = channel;
				stream_info->status = NM_Connected;
			}
		}
	}

exit:
	NM_OnConnect(safd->service, channel, e);
	return M4OK;
}

static M4Err SAFD_DisconnectChannel(NetClientPlugin *plug, LPNETCHANNEL channel)
{
	M4Err e;
	u32 i;
	SAFDemux *safd = plug->priv;

	e = M4ChannelNotFound;	
	if (safd->laser_info.channel == channel) {
		e = M4OK;
		safd->laser_info.channel = NULL;
		safd->laser_info.status = NM_Disconnected;
	}
	else if (safd->od_info.channel == channel) {
		e = M4OK;
		safd->od_info.channel = NULL;
		safd->od_info.status = NM_Disconnected;
	}
	else {
		for (i = 0; i < ChainGetCount(safd->stream_infos); i++) {
			SAFStreamInfo *stream_info = ChainGetEntry(safd->stream_infos, i);
			if (stream_info->channel == channel) {
				e = M4OK;
				stream_info->channel = NULL;
				stream_info->status = NM_Disconnected;
			}
		}
	}

	NM_OnDisconnect(safd->service, channel, e);
	return M4OK;
}

static M4Err SAFD_GetStatus(NetClientPlugin *plug, LPNETCHANNEL channel, u32 *status)
{
	M4Err e;
	u32 i;
	SAFDemux *safd = plug->priv;

	if (!channel) {
		*status = safd->laser_info.status;
		return M4OK;
	}

	e = M4ChannelNotFound;
	if (safd->laser_info.channel == channel) {
		e = M4OK;
		*status = safd->laser_info.status;
	}
	else if (safd->od_info.channel == channel) {
		e = M4OK;
		*status = safd->od_info.status;
	}
	else {
		for (i = 0; i < ChainGetCount(safd->stream_infos); i++) {
			SAFStreamInfo *stream_info = ChainGetEntry(safd->stream_infos, i);
			if (stream_info->channel == channel) {
				e = M4OK;
				*status = stream_info->status;
			}
		}
	}
	return e;
}


static M4Err SAFD_ServiceCommand(NetClientPlugin *plug, NetworkCommand *com)
{
	u32 i;
	SAFDemux *safd = plug->priv;

	if (!com->on_channel) return M4OK;

	switch (com->command_type) {
	/*no stream work in pull mode */
	case CHAN_SET_PULL:
		return M4NotSupported;
	case CHAN_INTERACTIVE:
		return M4OK;
	case CHAN_BUFFER:
		com->buffer_max = com->buffer_min = 0;
		return M4OK;
	case CHAN_DURATION:
		com->duration = 0;
		return M4OK;
	/*fetch start time*/
	case CHAN_PLAY:
		if (com->speed<0) return M4NotSupported;

		if (safd->laser_info.channel == com->on_channel) {
			safd->laser_info.status = NM_Running;

			MX_P(safd->mx);
			safd->seek_time = (com->start_range>=0) ? com->start_range : 0;
			if (!safd->is_running) {
				safd->is_running = 1;
				TH_Run(safd->thread, SAFDemux_Run, safd);
			}
			MX_V(safd->mx);
		} else if (safd->od_info.channel == com->on_channel) {
			safd->od_info.status = NM_Running;
		} else {
			for (i = 0; i < ChainGetCount(safd->stream_infos); i++) {
				SAFStreamInfo *stream_info = ChainGetEntry(safd->stream_infos, i);
				if (stream_info->channel == com->on_channel) {
					stream_info->status = NM_Running;
				}
			}
		}
		return M4OK;
	case CHAN_STOP:
		if (safd->laser_info.channel == com->on_channel) {
			safd->laser_info.status = NM_Connected;
		} else if (safd->od_info.channel == com->on_channel) {
			safd->od_info.status = NM_Connected;
		} else {
			for (i = 0; i < ChainGetCount(safd->stream_infos); i++) {
				SAFStreamInfo *stream_info = ChainGetEntry(safd->stream_infos, i);
				if (stream_info->channel == com->on_channel) {
					stream_info->status = NM_Connected;
				}
			}
		}
		return M4OK;

	default:
		return M4OK;
	}

	return M4OK;
}


M4Err SAFD_ChannelGetSLP(NetClientPlugin *plug, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, struct tagSLHeader *out_sl_hdr, Bool *sl_compressed, M4Err *out_reception_status, Bool *is_new_data)
{
/*
	LPODCODEC odc;
	ODCommand *com;
	ObjectDescriptor *OD;
	ESDescriptor *esd;

	SAFDemux *safd = plug->priv;
	if (safd->status==NM_Disconnected) return M4BadParam;
	if (channel==safd->audio_ch) return M4BadParam;
	if (channel==safd->video_ch) return M4BadParam;

	*sl_compressed = 0;
	memset(out_sl_hdr, 0, sizeof(struct tagSLHeader));
	out_sl_hdr->accessUnitEndFlag = 1;
	out_sl_hdr->accessUnitStartFlag = 1;
	out_sl_hdr->compositionTimeStampFlag = 1;
	out_sl_hdr->compositionTimeStamp = (u32) (1000 * safd->seek_time);

	*out_reception_status = (safd->od_state == 2) ? M4EOF : M4OK;
	if (!safd->od_au && (safd->od_state != 2)) {
		com = OD_NewCommand(ODUpdate_Tag);

		if ((safd->service_type!=1) && (safd->audio_st>=0)) {
			OD = (ObjectDescriptor *) OD_NewDescriptor(ObjectDescriptor_Tag);
			OD->objectDescriptorID = ISMA_AUDIO_OD_ID;
			esd = SAFD_GetESDescriptor(safd, 1);
			esd->OCRESID = safd->bifs_es_id;
			OD_AddDescToDesc((Descriptor*)OD, (Descriptor *) esd);
			ChainAddEntry( ((ObjectDescriptorUpdate*)com)->objectDescriptors, OD);

		}
		if ((safd->service_type!=2) && (safd->video_st>=0)) {
			OD = (ObjectDescriptor *) OD_NewDescriptor(ObjectDescriptor_Tag);
			OD->objectDescriptorID = ISMA_VIDEO_OD_ID;
			esd = SAFD_GetESDescriptor(safd, 0);
			esd->OCRESID = safd->bifs_es_id;
			OD_AddDescToDesc((Descriptor*)OD, (Descriptor *) esd);
			ChainAddEntry( ((ObjectDescriptorUpdate*)com)->objectDescriptors, OD);
		}
		odc = OD_NewCodec(OD_WRITE);
		OD_AddCommand(odc, com);
		OD_EncodeAU(odc);
		OD_GetEncodedAU(odc, &safd->od_au, &safd->od_au_size);
		OD_DeleteCodec(odc);
		*is_new_data = 1;

	} else {
		*is_new_data = 0;
	}
	*out_data_ptr = safd->od_au;
	*out_data_size = safd->od_au_size;
	if (!safd->od_state) safd->od_state = 1;
*/
	return M4OK;
}

M4Err SAFD_ChannelReleaseSLP(NetClientPlugin *plug, LPNETCHANNEL channel)
{
/*
	SAFDemux *safd = plug->priv;
	if (safd->status==NM_Disconnected) return M4BadParam;
	if (channel==safd->audio_ch) return M4BadParam;
	if (channel==safd->video_ch) return M4BadParam;

	if (safd->bifs_ch==channel) {
		safd->bifs_state = 2;
		return M4OK;
	}

	safd->od_state = 2;
	if (safd->od_au) free(safd->od_au);
	safd->od_au = NULL;
	safd->od_au_size = 0;
*/
	return M4OK;
}

static Bool SAFD_CanHandleURLInService(NetClientPlugin *plug, const char *url)
{
	char szURL[2048], *sep;
	SAFDemux *safd = (SAFDemux *)plug->priv;
	const char *this_url = NM_GetServiceURL(safd->service);
	if (!this_url || !url) return 0;

	strcpy(szURL, this_url);
	sep = strrchr(szURL, '#');
	if (sep) sep[0] = 0;

	/*ok this is ugly but will do it untill we have proper file association in place*/
	sep = strrchr(szURL, '.');
	if (sep) {
		if (!stricmp(sep, ".lsr") || !stricmp(sep, ".saf")) return 1;
	}

	return 0;
}

void *NewSAFDemuxer() 
{
	SAFDemux *safd;
	NetClientPlugin *plug = malloc(sizeof(NetClientPlugin));
	memset(plug, 0, sizeof(NetClientPlugin));

	safd = malloc(sizeof(SAFDemux));
	memset(safd, 0, sizeof(SAFDemux));
	plug->priv = safd;

	safd->thread = NewThread();
	safd->stream = NULL;
	safd->bs = NULL;
	safd->stream_infos = NewChain();
	safd->is_running = 0;
	safd->mx = NewMutex();
	
	plug->CanHandleURL = SAFD_CanHandleURL;
	plug->CloseService = SAFD_CloseService;
	plug->ConnectChannel = SAFD_ConnectChannel;
	plug->ConnectService = SAFD_ConnectService;
	plug->DisconnectChannel = SAFD_DisconnectChannel;
	plug->Get_MPEG4_IOD = SAFD_Get_MPEG4_IOD;
	plug->GetStatus = SAFD_GetStatus;
	plug->ServiceCommand = SAFD_ServiceCommand;

	/*Not used*/
	plug->ChannelGetSLP = SAFD_ChannelGetSLP;
	plug->ChannelReleaseSLP = SAFD_ChannelReleaseSLP;

	plug->CanHandleURLInService = SAFD_CanHandleURLInService;

	M4_REG_PLUG(plug, M4STREAMINGCLIENT, "SAF demuxer", "gpac distribution", 0);

	return plug;
}


void DeleteSAFDemuxer(void *ifce)
{
	SAFDemux *safd;
	NetClientPlugin *ptr = (NetClientPlugin *)ifce;

	safd = ptr->priv;

	TH_Delete(safd->thread);
	MX_Delete(safd->mx);

	DeleteChain(safd->stream_infos);
	DeleteBitStream(safd->bs);

	free(safd);
	free(ptr);
}
