Example #1
0
GF_EXPORT
GF_Err gf_sg_command_apply_list(GF_SceneGraph *graph, GF_List *comList, Double time_offset)
{
	GF_Err e;
	GF_Command *com;
	u32 i=0;
	while ((com = (GF_Command *)gf_list_enum(comList, &i))) {
		e = gf_sg_command_apply(graph, com, time_offset);
		if (e) return e;
	}
	return GF_OK;
}
Example #2
0
void V4SceneGraph::LoadCommand(u32 commandNumber)
{
	GF_StreamContext *sc = NULL;
	for (u32 i=0; i<gf_list_count(m_pSm->streams); i++) {
		sc = (GF_StreamContext *) gf_list_get(m_pSm->streams, i);
		if (sc->streamType == GF_STREAM_SCENE) break;
		sc = NULL;
	}
	assert(sc);
	GF_AUContext *au = (GF_AUContext *) gf_list_get(sc->AUs,0);
	if (commandNumber < gf_list_count(au->commands)) {
		for (u32 j=0; j<=commandNumber; j++) {
			GF_Command *c = (GF_Command *) gf_list_get(au->commands, j);
			gf_sg_command_apply(m_pSg, c, 0);
		}
	}
}
Example #3
0
GF_EXPORT
GF_Err gf_sm_aggregate(GF_SceneManager *ctx, u16 ESID)
{
    GF_Err e;
    u32 i, stream_count;
#ifndef GPAC_DISABLE_VRML
    u32 j;
    GF_AUContext *au;
    GF_Command *com;
#endif

    e = GF_OK;

#if DEBUG_RAP
    com_count = 0;
    stream_count = gf_list_count(ctx->streams);
    for (i=0; i<stream_count; i++) {
        GF_StreamContext *sc = (GF_StreamContext *)gf_list_get(ctx->streams, i);
        if (sc->streamType == GF_STREAM_SCENE) {
            au_count = gf_list_count(sc->AUs);
            for (j=0; j<au_count; j++) {
                au = (GF_AUContext *)gf_list_get(sc->AUs, j);
                com_count += gf_list_count(au->commands);
            }
        }
    }
    GF_LOG(GF_LOG_INFO, GF_LOG_SCENE, ("[SceneManager] Making RAP with %d commands\n", com_count));
#endif

    stream_count = gf_list_count(ctx->streams);
    for (i=0; i<stream_count; i++) {
        GF_AUContext *carousel_au;
        GF_List *carousel_commands;
        GF_StreamContext *aggregate_on_stream;
        GF_StreamContext *sc = (GF_StreamContext *)gf_list_get(ctx->streams, i);
        if (ESID && (sc->ESID!=ESID)) continue;

        /*locate the AU in which our commands will be aggregated*/
        carousel_au = NULL;
        carousel_commands = NULL;
        aggregate_on_stream = sc->aggregate_on_esid ? gf_sm_get_stream(ctx, sc->aggregate_on_esid) : NULL;
        if (aggregate_on_stream==sc) {
            carousel_commands = gf_list_new();
        } else if (aggregate_on_stream) {
            if (!gf_list_count(aggregate_on_stream->AUs)) {
                carousel_au = gf_sm_stream_au_new(aggregate_on_stream, 0, 0, 1);
            } else {
                /* assert we already performed aggregation */
                assert(gf_list_count(aggregate_on_stream->AUs)==1);
                carousel_au = gf_list_get(aggregate_on_stream->AUs, 0);
            }
            carousel_commands = carousel_au->commands;
        }
        /*TODO - do this as well for ODs*/
#ifndef GPAC_DISABLE_VRML
        if (sc->streamType == GF_STREAM_SCENE) {
            Bool has_modif = 0;
            /*we check for each stream if it is a base stream (SceneReplace ...) - several streams may carry RAPs if inline nodes are used*/
            Bool base_stream_found = 0;

            /*in DIMS we use an empty initial AU with no commands to signal the RAP*/
            if (sc->objectType == GPAC_OTI_SCENE_DIMS) base_stream_found = 1;

            /*apply all commands - this will also apply the SceneReplace*/
            while (gf_list_count(sc->AUs)) {
                u32 count;
                au = (GF_AUContext *) gf_list_get(sc->AUs, 0);
                gf_list_rem(sc->AUs, 0);

                /*AU not aggregated*/
                if (au->flags & GF_SM_AU_NOT_AGGREGATED) {
                    gf_sm_au_del(sc, au);
                    continue;
                }

                count = gf_list_count(au->commands);

                for (j=0; j<count; j++) {
                    u32 store=0;
                    com = gf_list_get(au->commands, j);
                    if (!base_stream_found) {
                        switch (com->tag) {
                        case GF_SG_SCENE_REPLACE:
                        case GF_SG_LSR_NEW_SCENE:
                        case GF_SG_LSR_REFRESH_SCENE:
                            base_stream_found = 1;
                            break;
                        }
                    }

                    /*aggregate the command*/

                    /*if stream doesn't carry a carousel or carries the base carousel (scene replace), always apply the command*/
                    if (base_stream_found || !sc->aggregate_on_esid) {
                        store = 0;
                    }
                    /*otherwise, check wether the command should be kept in this stream as is, or can be aggregated on this stream*/
                    else {
                        switch (com->tag) {
                        /*the following commands do not impact a sub-tree (eg do not deal with nodes), we cannot
                        aggregate them... */
                        case GF_SG_ROUTE_REPLACE:
                        case GF_SG_ROUTE_DELETE:
                        case GF_SG_ROUTE_INSERT:
                        case GF_SG_PROTO_INSERT:
                        case GF_SG_PROTO_DELETE:
                        case GF_SG_PROTO_DELETE_ALL:
                        case GF_SG_GLOBAL_QUANTIZER:
                        case GF_SG_LSR_RESTORE:
                        case GF_SG_LSR_SAVE:
                        case GF_SG_LSR_SEND_EVENT:
                        case GF_SG_LSR_CLEAN:
                            /*todo check in which category to put these commands*/
//						case GF_SG_LSR_ACTIVATE:
//						case GF_SG_LSR_DEACTIVATE:
                            store = 1;
                            break;
                        /*other commands:
                        	!!! we need to know if the target node of the command has been inserted in this stream !!!

                        This is a tedious task, for now we will consider the following cases:
                        	- locate a similar command in the stored list: remove the similar one and aggregate on stream
                        	- by default all AUs are stored if the stream is in aggregate mode - we should fix that by checking insertion points:
                        	 if a command apllies on a node that has been inserted in this stream, we can aggregate, otherwise store
                        */
                        default:
                            /*check if we can directly store the command*/
                            assert(carousel_commands);
                            store = store_or_aggregate(sc, com, carousel_commands, &has_modif);
                            break;
                        }
                    }

                    switch (store) {
                    /*command has been merged with a previous command in carousel and needs to be destroyed*/
                    case 2:
                        gf_list_rem(au->commands, j);
                        j--;
                        count--;
                        gf_sg_command_del((GF_Command *)com);
                        break;
                    /*command shall be moved to carousel without being applied*/
                    case 1:
                        gf_list_insert(carousel_commands, com, 0);
                        gf_list_rem(au->commands, j);
                        j--;
                        count--;
                        break;
                    /*command can be applied*/
                    default:
                        e = gf_sg_command_apply(ctx->scene_graph, com, 0);
                        break;
                    }
                }
                gf_sm_au_del(sc, au);
            }

            /*and recreate scene replace*/
            if (base_stream_found) {
                au = gf_sm_stream_au_new(sc, 0, 0, 1);

                switch (sc->objectType) {
                case GPAC_OTI_SCENE_BIFS:
                case GPAC_OTI_SCENE_BIFS_V2:
                    com = gf_sg_command_new(ctx->scene_graph, GF_SG_SCENE_REPLACE);
                    break;
                case GPAC_OTI_SCENE_LASER:
                    com = gf_sg_command_new(ctx->scene_graph, GF_SG_LSR_NEW_SCENE);
                    break;
                case GPAC_OTI_SCENE_DIMS:
                /* We do not create a new command, empty AU is enough in DIMS*/
                default:
                    com = NULL;
                    break;
                }

                if (com) {
                    com->node = ctx->scene_graph->RootNode;
                    ctx->scene_graph->RootNode = NULL;
                    gf_list_del(com->new_proto_list);
                    com->new_proto_list = ctx->scene_graph->protos;
                    ctx->scene_graph->protos = NULL;
                    /*indicate the command is the aggregated scene graph, so that PROTOs and ROUTEs
                    are taken from the scenegraph when encoding*/
                    com->aggregated = 1;
                    gf_list_add(au->commands, com);
                }
            }
            /*update carousel flags of the AU*/
            else if (carousel_commands) {
                /*if current stream caries its own carousel*/
                if (!carousel_au) {
                    carousel_au = gf_sm_stream_au_new(sc, 0, 0, 1);
                    gf_list_del(carousel_au->commands);
                    carousel_au->commands = carousel_commands;
                }
                carousel_au->flags |= GF_SM_AU_RAP | GF_SM_AU_CAROUSEL;
                if (has_modif) carousel_au->flags |= GF_SM_AU_MODIFIED;
            }
        }
#endif
    }
    return e;
}
Example #4
0
void V4SceneGraph::LoadFile(const char *path) 
{
	GF_SceneLoader load;
	if (m_pSm) {
		gf_sr_set_scene(m_pSr, NULL);
		gf_sm_del(m_pSm);
		gf_sg_del(m_pSg);
	}
	
	// initializes a new scene
	// We need an GF_InlineScene, a SceneManager and an GF_ObjectManager
	m_pIs = gf_is_new(NULL);
	m_pSg = m_pIs->graph;
	m_pSm = gf_sm_new(m_pSg);

	m_pIs->root_od = gf_odm_new();

	m_pIs->root_od->parentscene = NULL;
	m_pIs->root_od->subscene = m_pIs;
	m_pIs->root_od->term = m_term;

	m_term->root_scene = m_pIs;
	
	// TODO : what's the use of this ?
	if (m_pOriginal_mp4) gf_free(m_pOriginal_mp4);
	m_pOriginal_mp4 = NULL;

	/* Loading of a file (BT, MP4 ...) and modification of the SceneManager */
	memset(&load, 0, sizeof(GF_SceneLoader));
	load.fileName = path;
	load.ctx = m_pSm;
	load.cbk = this;
	if (strstr(path, ".mp4") || strstr(path, ".MP4") ) load.isom = gf_isom_open(path, GF_ISOM_OPEN_READ, NULL);
	gf_sm_load_init(&load);
	gf_sm_load_run(&load);
	gf_sm_load_done(&load);
	if (load.isom) gf_isom_delete(load.isom);

	/* SceneManager should be initialized  and filled correctly */

	gf_sg_set_scene_size_info(m_pSg, m_pSm->scene_width, m_pSm->scene_height, m_pSm->is_pixel_metrics);

	// TODO : replace with GetBifsStream
	GF_StreamContext *sc = (GF_StreamContext *) gf_list_get(m_pSm->streams,0);

	if (sc->streamType == 3) {
		GF_AUContext *au = (GF_AUContext *) gf_list_get(sc->AUs,0);
		GF_Command *c = (GF_Command *) gf_list_get(au->commands,0);
		gf_sg_command_apply(m_pSg, c, 0);
		/* This is a patch to solve the save pb:
		    When ApplyCommand is made on a Scene Replace Command
		    The command node is set to NULL
		    When we save a BIFS stream whose first command is of this kind,
		    the file saver thinks the bifs commands should come from an NHNT file 
		   This is a temporary patch */
		if (c->tag == GF_SG_SCENE_REPLACE) { c->node = m_pSg->RootNode; }
	}
	gf_sr_set_scene(m_pSr, m_pSg);

	// retrieves all the node from the tree and adds them to the node pool
	GF_Node * root = gf_sg_get_root_node(m_pSg);
	CreateDictionnary();
}
Example #5
0
static GF_Err IS_ProcessData(GF_SceneDecoder *plug, const char *inBuffer, u32 inBufferLength, 
								u16 ES_ID, u32 AU_time, u32 mmlevel)
{
	u32 i, j, count;
	Double scene_time;
	GF_BitStream *bs;
	GF_FieldInfo *field;
	ISStack *st;
	ISPriv *priv = (ISPriv *)plug->privateStack;
	GF_Err e = GF_OK;

	/*decode data frame except if local stringSensor*/
	bs = gf_bs_new(inBuffer, inBufferLength, GF_BITSTREAM_READ);
	i=0;
	while ((field = (GF_FieldInfo *)gf_list_enum(priv->ddf, &i))) {
		/*store present flag in eventIn for command skip - this is an ugly hack but it works since DDF don't have event types*/
		field->eventType = gf_bs_read_int(bs, 1);
		/*parse val ourselves (we don't want to depend on bifs codec)*/
		if (field->eventType) {
			switch (field->fieldType) {
			case GF_SG_VRML_SFBOOL: * ((SFBool *) field->far_ptr) = (SFBool) gf_bs_read_int(bs, 1); break;
			case GF_SG_VRML_SFFLOAT: *((SFFloat *)field->far_ptr) = FLT2FIX( gf_bs_read_float(bs) ); break;
			case GF_SG_VRML_SFINT32: *((SFInt32 *)field->far_ptr) = (s32) gf_bs_read_int(bs, 32); break;
			case GF_SG_VRML_SFTIME: *((SFTime *)field->far_ptr) = gf_bs_read_double(bs); break;
			case GF_SG_VRML_SFVEC2F:
				((SFVec2f *)field->far_ptr)->x = FLT2FIX( gf_bs_read_float(bs) );
				((SFVec2f *)field->far_ptr)->y = FLT2FIX( gf_bs_read_float(bs) );
				break;
			case GF_SG_VRML_SFVEC3F:
				((SFVec3f *)field->far_ptr)->x = FLT2FIX( gf_bs_read_float(bs) );
				((SFVec3f *)field->far_ptr)->y = FLT2FIX( gf_bs_read_float(bs) );
				((SFVec3f *)field->far_ptr)->z = FLT2FIX( gf_bs_read_float(bs) );
				break;
			case GF_SG_VRML_SFCOLOR:
				((SFColor *)field->far_ptr)->red = FLT2FIX( gf_bs_read_float(bs) );
				((SFColor *)field->far_ptr)->green = FLT2FIX( gf_bs_read_float(bs) );
				((SFColor *)field->far_ptr)->blue = FLT2FIX( gf_bs_read_float(bs) );
				break;
			case GF_SG_VRML_SFVEC4F:
			case GF_SG_VRML_SFROTATION:
				((SFRotation *)field->far_ptr)->x = FLT2FIX( gf_bs_read_float(bs) );
				((SFRotation *)field->far_ptr)->y = FLT2FIX( gf_bs_read_float(bs) );
				((SFRotation *)field->far_ptr)->z = FLT2FIX( gf_bs_read_float(bs) );
				((SFRotation *)field->far_ptr)->q = FLT2FIX( gf_bs_read_float(bs) );
				break;

			case GF_SG_VRML_SFSTRING:
			{
				u32 size, length;
				size = gf_bs_read_int(bs, 5);
				length = gf_bs_read_int(bs, size);
				if (gf_bs_available(bs) < length) return GF_NON_COMPLIANT_BITSTREAM;

				if ( ((SFString *)field->far_ptr)->buffer ) gf_free( ((SFString *)field->far_ptr)->buffer);
				((SFString *)field->far_ptr)->buffer = (char*)gf_malloc(sizeof(char)*(length+1));
				memset(((SFString *)field->far_ptr)->buffer , 0, length+1);
				for (j=0; j<length; j++) {
					 ((SFString *)field->far_ptr)->buffer[j] = gf_bs_read_int(bs, 8);
				}
			}
				break;
			}
		}
	}
	gf_bs_del(bs);

	/*special case for StringSensor in local mode: lookup for special chars*/
	if ((priv->type == IS_StringSensor) && priv->is_local) {
		char tmp_utf8[5000];
		const unsigned short *ptr;
		u32 len;
		GF_FieldInfo *field1 = (GF_FieldInfo *)gf_list_get(priv->ddf, 0);
		GF_FieldInfo *field2 = (GF_FieldInfo *)gf_list_get(priv->ddf, 1);
		SFString *inText = (SFString *) field1->far_ptr;
		SFString *outText = (SFString *) field2->far_ptr;

		field1->eventType = field2->eventType = 0;
		priv->enteredText[priv->text_len] = (short) '\0';

		len = gf_utf8_wcslen(priv->enteredText);
		if (len && (priv->enteredText[len-1] == priv->termChar)) {
			ptr = priv->enteredText;
			len = gf_utf8_wcstombs(tmp_utf8, 5000, &ptr);
			if (outText->buffer) gf_free(outText->buffer);
			outText->buffer = (char*)gf_malloc(sizeof(char) * (len));
			memcpy(outText->buffer, tmp_utf8, sizeof(char) * len-1);
			outText->buffer[len-1] = 0;
			if (inText->buffer) gf_free(inText->buffer);
			inText->buffer = NULL;
			priv->text_len = 0;

			field1->eventType = field2->eventType = 1;
		} else {
			if (priv->delChar) {
				/*remove chars*/
				if ((len>1) && (priv->enteredText[len-1] == priv->delChar)) {
					priv->enteredText[len-1] = (short) '\0';
					len--;
					if (len) {
						priv->enteredText[len-1] = (short) '\0';
						len--;
					}
				}
			}
			priv->text_len = len;
			ptr = priv->enteredText;
			len = gf_utf8_wcstombs(tmp_utf8, 5000, &ptr);
			if (inText->buffer) gf_free(inText->buffer);
			inText->buffer = (char*)gf_malloc(sizeof(char) * (len+1));
			memcpy(inText->buffer, tmp_utf8, sizeof(char) * len);
			inText->buffer[len] = 0;
			field1->eventType = 1;
		}
	}

	gf_term_lock_compositor(priv->scene->root_od->term, 1);

	/*apply it*/
	i=0;
	while ((st = (ISStack*)gf_list_enum(priv->is_nodes, &i))) {
		assert(st->is);
		assert(st->mo);
		if (!st->is->enabled) continue;

		count = gf_list_count(st->is->buffer.commandList);
		scene_time = gf_scene_get_time(priv->scene);
		for (j=0; j<count; j++) {
			GF_Command *com = (GF_Command *)gf_list_get(st->is->buffer.commandList, j);
			GF_FieldInfo *field = (GF_FieldInfo *)gf_list_get(priv->ddf, j);
			GF_CommandField *info = (GF_CommandField *)gf_list_get(com->command_fields, 0);
			if (info && field && field->eventType) {
				gf_sg_vrml_field_copy(info->field_ptr, field->far_ptr, field->fieldType);
				gf_sg_command_apply(priv->scene->graph, com, scene_time);
			}
		}
	}
	gf_term_lock_compositor(priv->scene->root_od->term, 0);
	return e;
}
Example #6
0
static GF_Err CTXLoad_ProcessData(GF_SceneDecoder *plug, const char *inBuffer, u32 inBufferLength,
                                  u16 ES_ID, u32 stream_time, u32 mmlevel)
{
	GF_Err e = GF_OK;
	u32 i, j, k, nb_updates, last_rap=0;
	GF_AUContext *au;
	Bool can_delete_com;
	GF_StreamContext *sc;
	CTXLoadPriv *priv = (CTXLoadPriv *)plug->privateStack;

	/*something failed*/
	if (priv->load_flags==3) return GF_EOS;

	/*this signals main scene deconnection, destroy the context if needed*/
	assert(ES_ID);
	if (!priv->ctx) {
		e = CTXLoad_Setup((GF_BaseDecoder *)plug);
		if (e) return e;
	}


	if (stream_time==(u32)-1) {
		/*seek on root stream: destroy the context manager and reload it. We cannot seek on the main stream
		because commands may have changed node attributes/children and we d'ont track the initial value*/
		if (priv->load_flags && (priv->base_stream_id == ES_ID)) {
			if (priv->src) gf_fclose(priv->src);
			priv->src = NULL;
			gf_sm_load_done(&priv->load);
			priv->file_pos = 0;
			/*queue scene for detach*/
			gf_term_lock_media_queue(priv->scene->root_od->term, GF_TRUE);
			priv->scene->root_od->action_type = GF_ODM_ACTION_SCENE_RECONNECT;
			gf_list_add(priv->scene->root_od->term->media_queue, priv->scene->root_od);
			gf_term_lock_media_queue(priv->scene->root_od->term, GF_FALSE);

			return CTXLoad_Setup((GF_BaseDecoder *)plug);
		}
		i=0;
		while ((sc = (GF_StreamContext *)gf_list_enum(priv->ctx->streams, &i))) {
			/*not our stream*/
			if (!sc->in_root_od && (sc->ESID != ES_ID)) continue;
			/*not the base stream*/
			if (sc->in_root_od && (priv->base_stream_id != ES_ID)) continue;
			/*handle SWF media extraction*/
			if ((sc->streamType == GF_STREAM_OD) && (priv->load_flags==1)) continue;
			sc->last_au_time = 0;
		}
		return GF_OK;
	}

	if (priv->load_flags != 2) {

		if (priv->progressive_support) {
			u32 entry_time;
			char file_buf[4096+1];
			if (!priv->src) {
				priv->src = gf_fopen(priv->file_name, "rb");
				if (!priv->src) return GF_URL_ERROR;
				priv->file_pos = 0;
			}
			priv->load.type = GF_SM_LOAD_XMTA;
			e = GF_OK;
			entry_time = gf_sys_clock();
			gf_fseek(priv->src, priv->file_pos, SEEK_SET);
			while (1) {
				u32 diff;
				s32 nb_read = (s32) fread(file_buf, 1, 4096, priv->src);
				if (nb_read<0) {
					return GF_IO_ERR;
				}
				file_buf[nb_read] = 0;
				if (!nb_read) {
					if (priv->file_pos==priv->file_size) {
						gf_fclose(priv->src);
						priv->src = NULL;
						priv->load_flags = 2;
						gf_sm_load_done(&priv->load);
						break;
					}
					break;
				}

				e = gf_sm_load_string(&priv->load, file_buf, GF_FALSE);
				priv->file_pos += nb_read;
				if (e) break;
				diff = gf_sys_clock() - entry_time;
				if (diff > priv->sax_max_duration) break;
			}
			if (!priv->scene->graph_attached) {
				gf_sg_set_scene_size_info(priv->scene->graph, priv->ctx->scene_width, priv->ctx->scene_height, priv->ctx->is_pixel_metrics);
				gf_scene_attach_to_compositor(priv->scene);

				CTXLoad_CheckStreams(priv);
			}
		}
		/*load first frame only*/
		else if (!priv->load_flags) {
			/*we need the whole file*/
			if (!CTXLoad_CheckDownload(priv)) return GF_OK;

			priv->load_flags = 1;
			e = gf_sm_load_init(&priv->load);
			if (!e) {
				CTXLoad_CheckStreams(priv);
				gf_sg_set_scene_size_info(priv->scene->graph, priv->ctx->scene_width, priv->ctx->scene_height, priv->ctx->is_pixel_metrics);
				/*VRML, override base clock*/
				if ((priv->load.type==GF_SM_LOAD_VRML) || (priv->load.type==GF_SM_LOAD_X3DV) || (priv->load.type==GF_SM_LOAD_X3D)) {
					/*override clock callback*/
					gf_sg_set_scene_time_callback(priv->scene->graph, CTXLoad_GetVRMLTime);
				}
			}
		}
		/*load the rest*/
		else {
			priv->load_flags = 2;
			e = gf_sm_load_run(&priv->load);
			gf_sm_load_done(&priv->load);
			/*in case this was not set in the first pass (XMT)*/
			gf_sg_set_scene_size_info(priv->scene->graph, priv->ctx->scene_width, priv->ctx->scene_height, priv->ctx->is_pixel_metrics);
		}

		if (e<0) {
			gf_sm_load_done(&priv->load);
			gf_sm_del(priv->ctx);
			priv->ctx = NULL;
			priv->load_flags = 3;
			return e;
		}

		/*and figure out duration of root scene, and take care of XMT timing*/
		if (priv->load_flags==2) {
			CTXLoad_CheckStreams(priv);
			if (!gf_list_count(priv->ctx->streams)) {
				gf_scene_attach_to_compositor(priv->scene);
			}
		}
	}

	nb_updates = 0;

	i=0;
	while ((sc = (GF_StreamContext *)gf_list_enum(priv->ctx->streams, &i))) {
		/*not our stream*/
		if (!sc->in_root_od && (sc->ESID != ES_ID)) continue;
		/*not the base stream*/
		if (sc->in_root_od && (priv->base_stream_id != ES_ID)) continue;
		/*handle SWF media extraction*/
		if ((sc->streamType == GF_STREAM_OD) && (priv->load_flags==1)) continue;

		/*check for seek*/
		if (sc->last_au_time > 1 + stream_time) {
			sc->last_au_time = 0;
		}

		can_delete_com = GF_FALSE;
		if (sc->in_root_od && (priv->load_flags==2)) can_delete_com = GF_TRUE;

		/*we're in the right stream, apply update*/
		j=0;

		/*seek*/
		if (!sc->last_au_time) {
			while ((au = (GF_AUContext *)gf_list_enum(sc->AUs, &j))) {
				u32 au_time = (u32) (au->timing*1000/sc->timeScale);

				if (au_time > stream_time)
					break;
				if (au->flags & GF_SM_AU_RAP) last_rap = j-1;
			}
			j = last_rap;
		}

		while ((au = (GF_AUContext *)gf_list_enum(sc->AUs, &j))) {
			u32 au_time = (u32) (au->timing*1000/sc->timeScale);

			if (au_time + 1 <= sc->last_au_time) {
				/*remove first replace command*/
				if (can_delete_com && (sc->streamType==GF_STREAM_SCENE)) {
					while (gf_list_count(au->commands)) {
						GF_Command *com = (GF_Command *)gf_list_get(au->commands, 0);
						gf_list_rem(au->commands, 0);
						gf_sg_command_del(com);
					}
					j--;
					gf_list_rem(sc->AUs, j);
					gf_list_del(au->commands);
					gf_free(au);
				}
				continue;
			}

			if (au_time > stream_time) {
				nb_updates++;
				break;
			}

			if (sc->streamType == GF_STREAM_SCENE) {
				GF_Command *com;
				/*apply the commands*/
				k=0;
				while ((com = (GF_Command *)gf_list_enum(au->commands, &k))) {
					e = gf_sg_command_apply(priv->scene->graph, com, 0);
					if (e) break;
					/*remove commands on base layer*/
					if (can_delete_com) {
						k--;
						gf_list_rem(au->commands, k);
						gf_sg_command_del(com);
					}
				}
			}
			else if (sc->streamType == GF_STREAM_OD) {
				/*apply the commands*/
				while (gf_list_count(au->commands)) {
					Bool keep_com = GF_FALSE;
					GF_ODCom *com = (GF_ODCom *)gf_list_get(au->commands, 0);
					gf_list_rem(au->commands, 0);
					switch (com->tag) {
					case GF_ODF_OD_UPDATE_TAG:
					{
						GF_ODUpdate *odU = (GF_ODUpdate *)com;
						while (gf_list_count(odU->objectDescriptors)) {
							GF_ESD *esd;
							char *remote;
							GF_MuxInfo *mux = NULL;
							GF_ObjectDescriptor *od = (GF_ObjectDescriptor *)gf_list_get(odU->objectDescriptors, 0);
							gf_list_rem(odU->objectDescriptors, 0);
							/*we can only work with single-stream ods*/
							esd = (GF_ESD*)gf_list_get(od->ESDescriptors, 0);
							if (!esd) {
								if (od->URLString) {
									ODS_SetupOD(priv->scene, od);
								} else {
									gf_odf_desc_del((GF_Descriptor *) od);
								}
								continue;
							}
							/*fix OCR dependencies*/
							if (CTXLoad_StreamInRootOD(priv->ctx->root_od, esd->OCRESID)) esd->OCRESID = priv->base_stream_id;

							/*forbidden if ESD*/
							if (od->URLString) {
								gf_odf_desc_del((GF_Descriptor *) od);
								continue;
							}
							/*look for MUX info*/
							k=0;
							while ((mux = (GF_MuxInfo*)gf_list_enum(esd->extensionDescriptors, &k))) {
								if (mux->tag == GF_ODF_MUXINFO_TAG) break;
								mux = NULL;
							}
							/*we need a mux if not animation stream*/
							if (!mux || !mux->file_name) {
								/*only animation streams are handled*/
								if (!esd->decoderConfig) {
									gf_odf_desc_del((GF_Descriptor *) od);
								} else if (esd->decoderConfig->streamType==GF_STREAM_SCENE) {
									/*set ST to private scene to get sure the stream will be redirected to us*/
									esd->decoderConfig->streamType = GF_STREAM_PRIVATE_SCENE;
									esd->dependsOnESID = priv->base_stream_id;
									ODS_SetupOD(priv->scene, od);
								} else if (esd->decoderConfig->streamType==GF_STREAM_INTERACT) {
									GF_UIConfig *cfg = (GF_UIConfig *) esd->decoderConfig->decoderSpecificInfo;
									gf_odf_encode_ui_config(cfg, &esd->decoderConfig->decoderSpecificInfo);
									gf_odf_desc_del((GF_Descriptor *) cfg);
									ODS_SetupOD(priv->scene, od);
								} else {
									gf_odf_desc_del((GF_Descriptor *) od);
								}
								continue;
							}
							//solve url before import
							if (mux->src_url) {
								char *res_url = gf_url_concatenate(mux->src_url, mux->file_name);
								if (res_url) {
									gf_free(mux->file_name);
									mux->file_name = res_url;
								}
								gf_free(mux->src_url);
								mux->src_url = NULL;
							}
							/*text import*/
							if (mux->textNode) {
#ifdef GPAC_DISABLE_MEDIA_IMPORT
								gf_odf_desc_del((GF_Descriptor *) od);
								continue;
#else
								e = gf_sm_import_bifs_subtitle(priv->ctx, esd, mux);
								if (e) {
									e = GF_OK;
									gf_odf_desc_del((GF_Descriptor *) od);
									continue;
								}
								/*set ST to private scene and dependency to base to get sure the stream will be redirected to us*/
								esd->decoderConfig->streamType = GF_STREAM_PRIVATE_SCENE;
								esd->dependsOnESID = priv->base_stream_id;
								ODS_SetupOD(priv->scene, od);
								continue;
#endif
							}

							/*soundstreams are a bit of a pain, they may be declared before any data gets written*/
							if (mux->delete_file) {
								FILE *t = gf_fopen(mux->file_name, "rb");
								if (!t) {
									keep_com = GF_TRUE;
									gf_list_insert(odU->objectDescriptors, od, 0);
									break;
								}
								gf_fclose(t);
							}
							/*remap to remote URL - warning, the URL has already been resolved according to the parent path*/
							remote = (char*)gf_malloc(sizeof(char) * (strlen("gpac://")+strlen(mux->file_name)+1) );
							strcpy(remote, "gpac://");
							strcat(remote, mux->file_name);
							k = od->objectDescriptorID;
							/*if files were created we'll have to clean up (swf import)*/
							if (mux->delete_file) gf_list_add(priv->files_to_delete, gf_strdup(remote));

							gf_odf_desc_del((GF_Descriptor *) od);
							od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG);
							od->URLString = remote;
							od->objectDescriptorID = k;
							ODS_SetupOD(priv->scene, od);
						}
						if (keep_com) break;
					}
					break;
					case GF_ODF_OD_REMOVE_TAG:
					{
						GF_ODRemove *odR = (GF_ODRemove*)com;
						for (k=0; k<odR->NbODs; k++) {
							GF_ObjectManager *odm = gf_scene_find_odm(priv->scene, odR->OD_ID[k]);
							if (odm) gf_odm_disconnect(odm, 1);
						}
					}
					break;
					default:
						break;
					}
					if (keep_com) {
						gf_list_insert(au->commands, com, 0);
						break;
					} else {
						gf_odf_com_del(&com);
					}
					if (e) break;
				}

			}
			sc->last_au_time = au_time + 1;
			/*attach graph to renderer*/
			if (!priv->scene->graph_attached)
				gf_scene_attach_to_compositor(priv->scene);
			if (e) return e;

			/*for root streams remove completed AUs (no longer needed)*/
			if (sc->in_root_od && !gf_list_count(au->commands) ) {
				j--;
				gf_list_rem(sc->AUs, j);
				gf_list_del(au->commands);
				gf_free(au);
			}
		}
	}
	if (e) return e;
	if ((priv->load_flags==2) && !nb_updates) return GF_EOS;
	return GF_OK;
}