Пример #1
0
static void AC3_SetupObject(AC3Reader *read)
{
	GF_ESD *esd;
	GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG);
	od->objectDescriptorID = 1;
	esd = AC3_GetESD(read);
	esd->OCRESID = 0;
	gf_list_add(od->ESDescriptors, esd);
	gf_service_declare_media(read->service, (GF_Descriptor*)od, 0);
}
Пример #2
0
static void IMG_SetupObject(IMGLoader *read)
{
	if (!read->ch) {
		GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG);
		GF_ESD *esd = IMG_GetESD(read);
		od->objectDescriptorID = 1;
		gf_list_add(od->ESDescriptors, esd);
		gf_service_declare_media(read->service, (GF_Descriptor *)od, GF_FALSE);
	}
}
Пример #3
0
static void FFD_SetupObjects(FFDemux *ffd)
{
	GF_ESD *esd;
	GF_ObjectDescriptor *od;
	u32 audio_esid = 0;

	if ((ffd->audio_st>=0) && (ffd->service_type != 1)) {
		od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG);
		esd = FFD_GetESDescriptor(ffd, 1);
		od->objectDescriptorID = esd->ESID;
		audio_esid = esd->ESID;
		gf_list_add(od->ESDescriptors, esd);
		gf_service_declare_media(ffd->service, (GF_Descriptor*)od, (ffd->video_st>=0) ? 1 : 0);
	}
	if ((ffd->video_st>=0) && (ffd->service_type != 2)) {
		od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG);
		esd = FFD_GetESDescriptor(ffd, 0);
		od->objectDescriptorID = esd->ESID;
		esd->OCRESID = audio_esid;
		gf_list_add(od->ESDescriptors, esd);
		gf_service_declare_media(ffd->service, (GF_Descriptor*)od, 0);
	}
}
Пример #4
0
/* Callback functions used by a media parser when parsing events happens */
GF_Err gf_mse_proxy(GF_InputService *parser, GF_NetworkCommand *command)
{
	if (!parser || !command || !parser->proxy_udta) {
		return GF_BAD_PARAM;
	} else {
		GF_HTML_SourceBuffer *sb = (GF_HTML_SourceBuffer *)parser->proxy_udta;
		switch (command->command_type) {
		case GF_NET_SERVICE_QUERY_INIT_RANGE:
			break;
		case GF_NET_SERVICE_QUERY_NEXT:
			/* The parser is asking for the next media segment in the buffer,
			   check for the media time and give the right one */
		{
			GF_HTML_ArrayBuffer *buffer;
			/* The input buffer should not be modified by append operations at the same time, no need to protect access */
			buffer = (GF_HTML_ArrayBuffer *)gf_list_get(sb->input_buffer, 0);
			if (buffer) {
				command->url_query.discontinuity_type = 0;
				command->url_query.current_download = GF_FALSE;
				command->url_query.start_range = 0;
				command->url_query.end_range = 0;
				command->url_query.switch_start_range = 0;
				command->url_query.switch_end_range = 0;
				command->url_query.next_url_init_or_switch_segment = NULL;
				if (buffer->is_init) {
					GF_HTML_ArrayBuffer *next = (GF_HTML_ArrayBuffer *)gf_list_get(sb->input_buffer, 1);
					command->url_query.discontinuity_type = 1;
					if (next) {
						GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE] Next segment to parse %s with init \n", next->url, buffer->url));
						command->url_query.next_url = next->url;
						command->url_query.next_url_init_or_switch_segment = buffer->url;
						gf_list_rem(sb->input_buffer, 0);
						gf_list_rem(sb->input_buffer, 0);
					} else {
						GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE] Only one init segment to parse %s, need to wait\n", buffer->url));
						command->url_query.next_url = NULL;
					}
				} else {
					GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MSE] Next segment to parse %s\n", buffer->url));
					command->url_query.next_url = buffer->url;
					gf_list_rem(sb->input_buffer, 0);
				}
				sb->prev_buffer = buffer;
			} else {
				command->url_query.next_url = NULL;
				command->url_query.discontinuity_type = 0;
			}
		}
		break;
		case GF_NET_SERVICE_STATUS_PROXY:
			/* The parser is informing the proxy about its status changes:
			    - new track found
				- all tracks parsed
				- connect/disconnect
				- */
			if (command->status.is_add_media) {
				if (command->status.desc) {
					gf_mse_source_buffer_store_track_desc(sb, (GF_ObjectDescriptor *)command->status.desc);
				} else {
					/* this is the last add media, we can switch updating to false */
					/* the first init segment was correctly processed */
					gf_mse_source_buffer_set_update(sb, GF_FALSE);
					/* TODO: set active tracks and send addsourcebuffer event */
					/* TODO: send media loadedmetadata event */
				}
				gf_service_declare_media(sb->mediasource->service, command->status.desc, (command->status.desc ? GF_TRUE : GF_FALSE));
			}
			/* general connection/disconnection messages from the media parser (not track related) */
			else if (!command->status.channel) {
				/* connection message */
				if (!command->status.is_disconnect) {
					if (command->status.e == GF_OK) {
						/* nothing needs to be done. Setup is done with final add media */
						sb->parser_connected = GF_TRUE;
						sb->mediasource->durationType = DURATION_INFINITY;
						gf_mse_source_buffer_setup_tracks(sb);
					} else {
						/* wrong first init segment */
						/* TODO: fire an error event */
					}
					gf_service_connect_ack(sb->mediasource->service, command->status.channel, command->status.e);
				} else {
					gf_service_disconnect_ack(sb->mediasource->service, command->status.channel, command->status.e);
				}
			}
			/* channel (track related) specific connection/disconnection messages from the media parser */
			else {
				if (!command->status.is_disconnect) {
					gf_service_connect_ack(sb->mediasource->service, command->status.channel, command->status.e);
				} else {
					gf_service_disconnect_ack(sb->mediasource->service, command->status.channel, command->status.e);
				}
			}
			break;
		default:
			gf_service_command(sb->mediasource->service, command, GF_OK);
			break;
		}
		return GF_OK;
	}
}
Пример #5
0
void isor_declare_objects(ISOMReader *read)
{
	GF_ObjectDescriptor *od;
	GF_ESD *esd;
	const char *tag;
	u32 i, count, ocr_es_id, tlen, base_track, j, track_id;
	Bool highest_stream;

	ocr_es_id = 0;

	/*TODO check for alternate tracks*/
	count = gf_isom_get_track_count(read->mov);
	for (i=0; i<count; i++) {
		if (!gf_isom_is_track_enabled(read->mov, i+1))
			continue;

		switch (gf_isom_get_media_type(read->mov, i+1)) {
		case GF_ISOM_MEDIA_AUDIO:
		case GF_ISOM_MEDIA_VISUAL:
		case GF_ISOM_MEDIA_TEXT:
		case GF_ISOM_MEDIA_SUBT:
		case GF_ISOM_MEDIA_SCENE:
		case GF_ISOM_MEDIA_SUBPIC:
			break;
		default:
			continue;
		}
		//some subtypes are not declared as readable objects
		switch (gf_isom_get_media_subtype(read->mov, i+1, 1)) {
		case GF_ISOM_SUBTYPE_HVT1:
			continue;
		default:
			break;
		}
		/*we declare only the highest video track (i.e the track we play)*/
		highest_stream = GF_TRUE;
		track_id = gf_isom_get_track_id(read->mov, i+1);
		if (read->play_only_track_id && (read->play_only_track_id != track_id)) continue;

		for (j = 0; j < count; j++) {
			if (gf_isom_has_track_reference(read->mov, j+1, GF_ISOM_REF_SCAL, track_id) > 0) {
				highest_stream = GF_FALSE;
				break;
			}
		}
		if ((gf_isom_get_media_type(read->mov, i+1) == GF_ISOM_MEDIA_VISUAL) && !highest_stream)
			continue;
		esd = gf_media_map_esd(read->mov, i+1);
		if (esd) {
			gf_isom_get_reference(read->mov, i+1, GF_ISOM_REF_BASE, 1, &base_track);
			esd->has_ref_base = base_track ? GF_TRUE : GF_FALSE;

			if (!esd->langDesc) {
				esd->langDesc = (GF_Language *) gf_odf_desc_new(GF_ODF_LANG_TAG);
				gf_isom_get_media_language(read->mov, i+1, &esd->langDesc->full_lang_code);
			}

			od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG);
			od->service_ifce = read->input;
			od->objectDescriptorID = 0;
			if (!ocr_es_id) ocr_es_id = esd->ESID;
			esd->OCRESID = ocr_es_id;
			gf_list_add(od->ESDescriptors, esd);
			if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
				send_proxy_command(read, GF_FALSE, GF_TRUE, GF_OK, (GF_Descriptor*)od, NULL);
			} else {
				gf_service_declare_media(read->service, (GF_Descriptor*)od, GF_TRUE);
			}
		}
	}
	/*if cover art, extract it in cache*/
	if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_COVER_ART, &tag, &tlen)==GF_OK) {
		const char *cdir = gf_modules_get_option((GF_BaseInterface *)gf_service_get_interface(read->service), "General", "CacheDirectory");
		if (cdir) {
			char szName[GF_MAX_PATH];
			const char *sep;
			FILE *t;
			sep = strrchr(gf_isom_get_filename(read->mov), '\\');
			if (!sep) sep = strrchr(gf_isom_get_filename(read->mov), '/');
			if (!sep) sep = gf_isom_get_filename(read->mov);

			if ((cdir[strlen(cdir)-1] != '\\') && (cdir[strlen(cdir)-1] != '/')) {
				sprintf(szName, "%s/%s_cover.%s", cdir, sep, (tlen & 0x80000000) ? "png" : "jpg");
			} else {
				sprintf(szName, "%s%s_cover.%s", cdir, sep, (tlen & 0x80000000) ? "png" : "jpg");
			}

			t = gf_fopen(szName, "wb");

			if (t) {
				Bool isom_contains_video = GF_FALSE;

				/*write cover data*/
				assert(!(tlen & 0x80000000));
				gf_fwrite(tag, tlen & 0x7FFFFFFF, 1, t);
				gf_fclose(t);

				/*don't display cover art when video is present*/
				for (i=0; i<gf_isom_get_track_count(read->mov); i++) {
					if (!gf_isom_is_track_enabled(read->mov, i+1))
						continue;
					if (gf_isom_get_media_type(read->mov, i+1) == GF_ISOM_MEDIA_VISUAL) {
						isom_contains_video = GF_TRUE;
						break;
					}
				}

				if (!isom_contains_video) {
					od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG);
					od->service_ifce = read->input;
					od->objectDescriptorID = GF_MEDIA_EXTERNAL_ID;
					od->URLString = gf_strdup(szName);
					if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
						send_proxy_command(read, GF_FALSE, GF_TRUE, GF_OK, (GF_Descriptor*)od, NULL);
					} else {
						gf_service_declare_media(read->service, (GF_Descriptor*)od, GF_TRUE);
					}
				}
			}
		}
	}
	if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
		send_proxy_command(read, GF_FALSE, GF_TRUE, GF_OK, NULL, NULL);
	} else {
		gf_service_declare_media(read->service, NULL, GF_FALSE);
	}
}
Пример #6
0
GF_Err LIBPLAYER_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url)
{
	LibPlayerIn *read = (LibPlayerIn *) plug->priv;
#ifndef TEST_LIBPLAYER
	mrl_t *mrl = NULL;
#endif

	GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[LibPlayerIN]�Connecting\n"));

	if (!read || !serv || !url) return GF_BAD_PARAM;

	if (!strnicmp(url, "libplayer://", 12)) url+=12;

	if (!read->init) {
		read->init=1;
		/* libplayer init with default width/height */
		read->width = 80;
		read->height = 20;
		read->url = url;
		read->player_id = libplayer_id;
		read->player_type = PLAYER_FILE;

#ifndef TEST_LIBPLAYER
		read->player = player_init(PLAYER_TYPE_DUMMY, PLAYER_AO_AUTO, PLAYER_VO_AUTO, PLAYER_MSG_INFO, read->player_id, on_libplayer_event);

		if (!read->player) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[LibPlayerIN]�Failed to instanciate libplayer instance %d\n", read->player_id));
			gf_service_connect_ack(serv, NULL, GF_REMOTE_SERVICE_ERROR);
			return GF_OK;
		}
#endif
		libplayer_id++;
		GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[LibPlayerIN] Opening URL %s for Player instance %d\n", url, read->player_id));
	}

#ifndef TEST_LIBPLAYER
	mrl = NULL;

	// dvb
	if (!strnicmp(url, "dvb://", 6)) {
		read->player_type = PLAYER_DVB;
		mrl_resource_dvb_args_t *mrl_dvb_args;
		mrl_dvb_args = calloc(1, sizeof(mrl_resource_dvb_args_t));
		char *frequency;

		// fetch frequency
		if (frequency = strchr(url+6, '@')) {
			char *enc, *pid;
			mrl_dvb_args->frequency = atoi(frequency+1);

			/*
			 * video
			 */
			// video codec
			if (enc = strstr(url+6, "mpeg2")) {
				mrl_dvb_args->video_enc = PLAYER_VIDEO_MPEG2;
			}
			else if (enc = strstr(url+6, "h264")) {
				mrl_dvb_args->video_enc = PLAYER_VIDEO_H264;
			}
			else {
				GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[LibPlayerIN]�Unknown video encoding\n"));
				mrl_dvb_args->video_enc = PLAYER_VIDEO_UNKNOWN;
			}

			// video PID
			if (mrl_dvb_args->video_enc != PLAYER_VIDEO_UNKNOWN) {
				pid = strchr(enc, ':');
				mrl_dvb_args->video_pid = atoi(pid+1);
			}

			/*
			 * audio
			 */
			// audio codec
			if (enc = strstr(url+6, "mp2")) {
				mrl_dvb_args->audio_enc = PLAYER_AUDIO_MP2;
			}
			else if (enc = strstr(url+6, "ac3")) {
				mrl_dvb_args->audio_enc = PLAYER_AUDIO_AC3;
			}
			else {
				GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[LibPlayerIN]�Unknown audio encoding\n"));
				mrl_dvb_args->audio_enc = PLAYER_AUDIO_UNKNOWN;
			}

			// audio PID
			if (mrl_dvb_args->audio_enc != PLAYER_AUDIO_UNKNOWN) {
				pid = strchr(enc, ':');
				mrl_dvb_args->audio_pid = atoi(pid+1);
			}

			if (mrl_dvb_args->video_enc == PLAYER_VIDEO_UNKNOWN && mrl_dvb_args->audio_enc == PLAYER_AUDIO_UNKNOWN) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[LibPlayerIN]�Unknown video and audio encoding\n"));
				free(mrl_dvb_args);
				return GF_BAD_PARAM;
			}
		}
		else {
			GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[LibPlayerIN]�Unknown frequency\n"));
			free(mrl_dvb_args);
			return GF_BAD_PARAM;
		}

		// player instance 1 has not starter yet in dvb case <=> player instance 1 is not created yet
		if (start_dvb == 0) {
			mrl = mrl_new(read->player, MRL_RESOURCE_DVB, mrl_dvb_args);
			GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[LibPlayerIN] MRL created for DVB\n"));

			// player has already started, zapping case, make sure the player is not recreated
		} else {
			GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[LibPlayerIN] Starting DVB PES filtering\n"));
			player_dvb_pes_filter_start(read->player, mrl_dvb_args->video_enc, mrl_dvb_args->video_pid, mrl_dvb_args->audio_enc, mrl_dvb_args->audio_pid);
		}

	}
	else if (!strnicmp(url, "file://", 7) || !strstr(url, "://")) {
		mrl_resource_local_args_t *mrl_args;
		mrl_args = calloc(1, sizeof(mrl_resource_local_args_t));
		if (!strnicmp(url, "file://", 7)) {
			mrl_args->location = strdup(url + 7);
		}  else {
			mrl_args->location = strdup(url);
		}
		mrl = mrl_new (read->player, MRL_RESOURCE_FILE, mrl_args);

	}

	// only for DVB case to make sure player is only set once
	if (start_dvb == 0) {
		if (!mrl) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[LibPlayerIN] Failed to create MRL for url %s\n", url));
			gf_service_connect_ack(serv, NULL, GF_URL_ERROR);
			return GF_OK;
		}

		player_mrl_set(read->player, mrl);
	}

#endif
	read->state = 0;
	read->service = serv;

	/*ACK connection is OK*/
	gf_service_connect_ack(serv, NULL, GF_OK);


	/*setup LIBPLAYER object descriptor*/
	{
		GF_ESD *esd;
		GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG);
		od->objectDescriptorID = 1+read->player_id;

		esd = gf_odf_desc_esd_new(0);
		esd->ESID = 1+read->player_id;
		esd->slConfig->timestampResolution = 1000;
		esd->decoderConfig->streamType = GF_STREAM_PRIVATE_MEDIA;
		esd->decoderConfig->objectTypeIndication = GPAC_OTI_PRIVATE_MEDIA_LIBPLAYER;
#ifndef TEST_LIBPLAYER
		esd->decoderConfig->decoderSpecificInfo->data = read;
#endif

		gf_list_add(od->ESDescriptors, esd);
		gf_service_declare_media(read->service, (GF_Descriptor*)od, 0);
	}

	return GF_OK;
}
Пример #7
0
static void SAF_NetIO(void *cbk, GF_NETIO_Parameter *param)
{
	GF_Err e;
	Bool is_rap, go;
	SAFChannel *ch;
	u32 cts, au_sn, au_size, type, i, stream_id;
	u64 bs_pos;
	GF_BitStream *bs;
	GF_SLHeader sl_hdr;

	SAFIn *read = (SAFIn *) cbk;

	e = param->error;
	/*done*/
	if (param->msg_type==GF_NETIO_DATA_TRANSFERED) {
		if (read->stream && (read->saf_type==SAF_FILE_REMOTE)) read->saf_type = SAF_FILE_LOCAL;
		return;
	} else {
		/*handle service message*/
		gf_service_download_update_stats(read->dnload);
		if (param->msg_type!=GF_NETIO_DATA_EXCHANGE) {
			if (e<0) {
				if (read->needs_connection) {
					read->needs_connection = 0;
					gf_service_connect_ack(read->service, NULL, e);
				}
				return;
			}
			if (read->needs_connection) {
				u32 total_size;
				gf_dm_sess_get_stats(read->dnload, NULL, NULL, &total_size, NULL, NULL, NULL);
				if (!total_size) read->saf_type = SAF_LIVE_STREAM;
			}
			return;
		}
	}
	if (!param->size) return;

	if (!read->run_state) return;

	if (read->alloc_size < read->saf_size + param->size) {
		read->saf_data = (char*)gf_realloc(read->saf_data, sizeof(char)*(read->saf_size + param->size) );
		read->alloc_size = read->saf_size + param->size;
	}
	memcpy(read->saf_data + read->saf_size, param->data, sizeof(char)*param->size);
	read->saf_size += param->size;

	/*first AU not complete yet*/
	if (read->saf_size<10) return;

	bs = gf_bs_new(read->saf_data, read->saf_size, GF_BITSTREAM_READ);
	bs_pos = 0;

	go = 1;
	while (go) {
		u64 avail = gf_bs_available(bs);
		bs_pos = gf_bs_get_position(bs);

		if (avail<10) break;

		is_rap = gf_bs_read_int(bs, 1);
		au_sn = gf_bs_read_int(bs, 15);
		gf_bs_read_int(bs, 2);
		cts = gf_bs_read_int(bs, 30);
		au_size = gf_bs_read_int(bs, 16);
		avail-=8;

		if (au_size > avail) break;
		assert(au_size>=2);

		is_rap = 1;

		type = gf_bs_read_int(bs, 4);
		stream_id = gf_bs_read_int(bs, 12);
		au_size -= 2;

		ch = saf_get_channel(read, stream_id, NULL);
		switch (type) {
		case 1:
		case 2:
		case 7:
			if (ch) {
				gf_bs_skip_bytes(bs, au_size);
			} else {
				SAFChannel *first = (SAFChannel *)gf_list_get(read->channels, 0);
				GF_SAFEALLOC(ch, SAFChannel);
				ch->stream_id = stream_id;
				ch->esd = gf_odf_desc_esd_new(0);
				ch->esd->ESID = stream_id;
				ch->esd->OCRESID = first ? first->stream_id : stream_id;
				ch->esd->slConfig->useRandomAccessPointFlag = 1;
				ch->esd->slConfig->AUSeqNumLength = 0;
				ch->esd->decoderConfig->objectTypeIndication = gf_bs_read_u8(bs);
				ch->esd->decoderConfig->streamType = gf_bs_read_u8(bs);
				ch->ts_res = ch->esd->slConfig->timestampResolution = gf_bs_read_u24(bs);
				ch->esd->decoderConfig->bufferSizeDB = gf_bs_read_u16(bs);
				au_size -= 7;
				if ((ch->esd->decoderConfig->objectTypeIndication == 0xFF) && (ch->esd->decoderConfig->streamType == 0xFF) ) {
					u16 mimeLen = gf_bs_read_u16(bs);
					gf_bs_skip_bytes(bs, mimeLen);
					au_size -= mimeLen+2;
				}
				if (type==7) {
					u16 urlLen = gf_bs_read_u16(bs);
					ch->esd->URLString = (char*)gf_malloc(sizeof(char)*(urlLen+1));
					gf_bs_read_data(bs, ch->esd->URLString, urlLen);
					ch->esd->URLString[urlLen] = 0;
					au_size -= urlLen+2;
				}
				if (au_size) {
					ch->esd->decoderConfig->decoderSpecificInfo->dataLength = au_size;
					ch->esd->decoderConfig->decoderSpecificInfo->data = (char*)gf_malloc(sizeof(char)*au_size);
					gf_bs_read_data(bs, ch->esd->decoderConfig->decoderSpecificInfo->data, au_size);
				}
				if (ch->esd->decoderConfig->streamType==4) ch->buffer_min=100;
				else if (ch->esd->decoderConfig->streamType==5) ch->buffer_min=400;
				else ch->buffer_min=0;

				if (read->needs_connection && (ch->esd->decoderConfig->streamType==GF_STREAM_SCENE)) {
					gf_list_add(read->channels, ch);
					read->needs_connection = 0;
					gf_service_connect_ack(read->service, NULL, GF_OK);
				} else if (read->needs_connection) {
					gf_odf_desc_del((GF_Descriptor *) ch->esd);
					gf_free(ch);
					ch = NULL;
				} else {
					GF_ObjectDescriptor *od;
					gf_list_add(read->channels, ch);

					od = (GF_ObjectDescriptor*)gf_odf_desc_new(GF_ODF_OD_TAG);
					gf_list_add(od->ESDescriptors, ch->esd);
					ch->esd = NULL;
					od->objectDescriptorID = ch->stream_id;
					gf_service_declare_media(read->service, (GF_Descriptor*)od, 0);

				}
			}
			break;
		case 4:
			if (ch) {
				bs_pos = gf_bs_get_position(bs);
				memset(&sl_hdr, 0, sizeof(GF_SLHeader));
				sl_hdr.accessUnitLength = au_size;
				sl_hdr.AU_sequenceNumber = au_sn;
				sl_hdr.compositionTimeStampFlag = 1;
				sl_hdr.compositionTimeStamp = cts;
				sl_hdr.randomAccessPointFlag = is_rap;
				if (read->start_range && (read->start_range*ch->ts_res>cts*1000)) {
					sl_hdr.compositionTimeStamp = read->start_range*ch->ts_res/1000;
				}
				gf_service_send_packet(read->service, ch->ch, read->saf_data+bs_pos, au_size, &sl_hdr, GF_OK);
			}
			gf_bs_skip_bytes(bs, au_size);
			break;
		case 3:
			if (ch) gf_service_send_packet(read->service, ch->ch, NULL, 0, NULL, GF_EOS);
			break;
		case 5:
			go = 0;
			read->run_state = 0;
			i=0;
			while ((ch = (SAFChannel *)gf_list_enum(read->channels, &i))) {
				gf_service_send_packet(read->service, ch->ch, NULL, 0, NULL, GF_EOS);
			}
			break;
		}
	}

	gf_bs_del(bs);
	if (bs_pos) {
		u32 remain = (u32) (read->saf_size - bs_pos);
		if (remain) memmove(read->saf_data, read->saf_data+bs_pos, sizeof(char)*remain);
		read->saf_size = remain;
	}
	SAF_Regulate(read);
}