static GF_Err gf_sm_encode_scene()

in src/scene_manager/encode_isom.c [424:947]


static GF_Err gf_sm_encode_scene(GF_SceneManager *ctx, GF_ISOFile *mp4, GF_SMEncodeOptions *opts, u32 scene_type)
{
	char *data;
	Bool is_in_iod, delete_desc;
	u32 i, j, di, rate, init_offset, data_len, count, track, rap_delay, flags, rap_mode;
	u64 last_rap, dur, time_slice, avg_rate, prev_dts;
	GF_Err e;
	GF_InitialObjectDescriptor *iod;
	GF_AUContext *au;
	GF_ISOSample *samp;
	GF_StreamContext *sc;
	GF_ESD *esd;
	GF_MuxInfo *mux;
#ifndef GPAC_DISABLE_BIFS_ENC
	GF_BifsEncoder *bifs_enc;
#endif
#ifndef GPAC_DISABLE_LASER
	GF_LASeRCodec *lsr_enc;
#endif

	rap_mode = 0;
	if (opts && opts->rap_freq) {
		if (opts->flags & GF_SM_ENCODE_RAP_INBAND) rap_mode = 3;
		else if (opts->flags & GF_SM_ENCODE_RAP_SHADOW) rap_mode = 2;
		else rap_mode = 1;
	}

	e = GF_OK;
	iod = (GF_InitialObjectDescriptor *) ctx->root_od;
	/*if no iod check we only have one bifs*/
	if (!iod) {
		count = 0;
		i=0;
		while ((sc = (GF_StreamContext*)gf_list_enum(ctx->streams, &i))) {
			if (sc->streamType == GF_STREAM_OD) count++;
		}
		if (!iod && count>1) return GF_NOT_SUPPORTED;
	}

	count = gf_list_count(ctx->streams);

	sc = NULL;

	flags = opts ? opts->flags : 0;
	delete_desc = 0;
	esd = NULL;


	/*configure streams*/
	j=0;
	for (i=0; i<count; i++) {
		sc = (GF_StreamContext*)gf_list_get(ctx->streams, i);
		esd = NULL;
		if (sc->streamType != GF_STREAM_SCENE) continue;
		/*NOT BIFS*/
		if (!scene_type && (sc->objectType > 2) ) continue;
		/*NOT LASeR*/
		if (scene_type && (sc->objectType != 0x09) ) continue;
		j++;
	}
	if (!j) {
		GF_Node *n = gf_sg_get_root_node(ctx->scene_graph);
		if (!n) return GF_OK;
#ifndef GPAC_DISABLE_LASER
		if ((scene_type==1) && (gf_node_get_tag(n)!=TAG_SVG_svg) ) return GF_OK;
#endif
		if ((scene_type==0) && (gf_node_get_tag(n)>GF_NODE_RANGE_LAST_X3D) ) return GF_OK;
	}

#ifndef GPAC_DISABLE_BIFS_ENC
	bifs_enc = NULL;
#endif
#ifndef GPAC_DISABLE_LASER
	lsr_enc = NULL;
#endif

	if (!scene_type) {
#ifndef GPAC_DISABLE_BIFS_ENC
		bifs_enc = gf_bifs_encoder_new(ctx->scene_graph);
		/*no streams defined, encode a RAP*/
		if (!j) {
			delete_desc = 0;
			esd = NULL;
			is_in_iod = 1;
			goto force_scene_rap;
		}
		gf_bifs_encoder_set_source_url(bifs_enc, opts->src_url);
#else
		return GF_NOT_SUPPORTED;
#endif
	}

	if (scene_type==1) {
#ifndef GPAC_DISABLE_LASER
		lsr_enc = gf_laser_encoder_new(ctx->scene_graph);
		/*no streams defined, encode a RAP*/
		if (!j) {
			delete_desc = 0;
			esd = NULL;
			is_in_iod = 1;
			goto force_scene_rap;
		}
#else
		return GF_NOT_SUPPORTED;
#endif
	}

	/*configure streams*/
	for (i=0; i<count; i++) {
		sc = (GF_StreamContext*)gf_list_get(ctx->streams, i);
		esd = NULL;
		if (sc->streamType != GF_STREAM_SCENE) continue;
		/*NOT BIFS*/
		if (!scene_type && (sc->objectType > 2) ) continue;
		/*NOT LASeR*/
		if (scene_type && (sc->objectType != 0x09) ) continue;

		delete_desc = 0;
		esd = NULL;
		is_in_iod = 1;
		if (iod) {
			is_in_iod = 0;
			j=0;
			while ((esd = (GF_ESD*)gf_list_enum(iod->ESDescriptors, &j))) {
				if (esd->decoderConfig && esd->decoderConfig->streamType == GF_STREAM_SCENE) {
					if (!sc->ESID) sc->ESID = esd->ESID;
					if (sc->ESID == esd->ESID) {
						is_in_iod = 1;
						break;
					}
				}
				/*special BIFS direct import from NHNT*/
				else if (gf_list_count(iod->ESDescriptors)==1) {
					sc->ESID = esd->ESID;
					is_in_iod = 1;
					break;
				}
				esd = NULL;
			}
		}
		if (!esd && sc->ESID) esd = gf_sm_locate_esd(ctx, sc->ESID);

		au = NULL;

#ifndef GPAC_DISABLE_VRML
		/*special BIFS direct import from NHNT*/
		au = (GF_AUContext*)gf_list_get(sc->AUs, 0);
		if (gf_list_count(sc->AUs) == 1) {
			if (gf_list_count(au->commands) == 1) {
				GF_Command *com = (GF_Command *)gf_list_get(au->commands, 0);
				/*no root node, no protos (empty replace) - that's BIFS NHNT import*/
				if ((com->tag == GF_SG_SCENE_REPLACE) && !com->node && !gf_list_count(com->new_proto_list))
					au = NULL;
			}
		}

		/*sanity check: remove first command if it is REPLACE SCENE BY NULL*/
		if (au && !au->timing && !au->timing_sec && (gf_list_count(au->commands) > 1)) {
			GF_Command *com = (GF_Command *)gf_list_get(au->commands, 0);
			if (com->tag==GF_SG_SCENE_REPLACE) {
				if (!com->node && !gf_list_count(com->new_proto_list) ) {
					gf_list_rem(au->commands, 0);
					gf_sg_command_del(com);
				}
			}
		}
#endif

		if (!au && !esd->URLString) {
			/*if not in IOD, the stream will be imported when encoding the OD stream*/
			if (!is_in_iod) continue;
#ifndef GPAC_DISABLE_MEDIA_IMPORT
			e = gf_sm_import_stream(ctx, mp4, esd, 0, NULL, 0);
#else
			e = GF_BAD_PARAM;
#endif
			if (e) goto exit;
			gf_sm_finalize_mux(mp4, esd, 0);
			gf_isom_add_track_to_root_od(mp4, gf_isom_get_track_by_id(mp4, esd->ESID));
			continue;
		}

force_scene_rap:
		if (!esd) {
			delete_desc = 1;
			esd = gf_odf_desc_esd_new(2);
			gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo);
			esd->decoderConfig->decoderSpecificInfo = NULL;
			esd->ESID = sc ? sc->ESID : 1;
			esd->decoderConfig->streamType = GF_STREAM_SCENE;
		}

		if (!esd->slConfig) esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG);
		if (sc && sc->timeScale) esd->slConfig->timestampResolution = sc->timeScale;
		if (!esd->slConfig->timestampResolution) esd->slConfig->timestampResolution = 1000;

		if (!esd->decoderConfig) esd->decoderConfig = (GF_DecoderConfig*)gf_odf_desc_new(GF_ODF_DCD_TAG);
		esd->decoderConfig->streamType = GF_STREAM_SCENE;

		/*create track*/
		track = gf_isom_new_track(mp4, sc ? sc->ESID : 1, GF_ISOM_MEDIA_SCENE, esd->slConfig->timestampResolution);
		if (!track) {
			e = gf_isom_last_error(mp4);
			goto exit;
		}
		gf_isom_set_track_enabled(mp4, track, 1);
		if (sc) {
			if (!sc->ESID) sc->ESID = gf_isom_get_track_id(mp4, track);
			esd->ESID = sc->ESID;
		}

		/*BIFS setup*/
		if (!scene_type) {
#ifndef GPAC_DISABLE_BIFS_ENC
			GF_BIFSConfig *bcfg;
			Bool delete_bcfg = 0;

			if (!esd->decoderConfig->decoderSpecificInfo) {
				bcfg = (GF_BIFSConfig*)gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG);
				delete_bcfg = 1;
			} else if (esd->decoderConfig->decoderSpecificInfo->tag == GF_ODF_BIFS_CFG_TAG) {
				bcfg = (GF_BIFSConfig *)esd->decoderConfig->decoderSpecificInfo;
			} else {
				bcfg = gf_odf_get_bifs_config(esd->decoderConfig->decoderSpecificInfo, esd->decoderConfig->objectTypeIndication);
				delete_bcfg = 1;
			}
			/*update NodeIDbits and co*/
			/*nodeID bits shall include NULL node*/
			if (!bcfg->nodeIDbits || (bcfg->nodeIDbits<gf_get_bit_size(ctx->max_node_id)) )
				bcfg->nodeIDbits = gf_get_bit_size(ctx->max_node_id);

			if (!bcfg->routeIDbits || (bcfg->routeIDbits != gf_get_bit_size(ctx->max_route_id)) )
				bcfg->routeIDbits = gf_get_bit_size(ctx->max_route_id);

			if (!bcfg->protoIDbits || (bcfg->protoIDbits != gf_get_bit_size(ctx->max_proto_id)) )
				bcfg->protoIDbits = gf_get_bit_size(ctx->max_proto_id);

			if (!bcfg->elementaryMasks) {
				bcfg->pixelMetrics = ctx->is_pixel_metrics;
				bcfg->pixelWidth = ctx->scene_width;
				bcfg->pixelHeight = ctx->scene_height;
			}
			if (bcfg->useNames) flags |= GF_SM_ENCODE_USE_NAMES;

			/*this is for safety, otherwise some players may not understand NULL node*/
			if (!bcfg->nodeIDbits) bcfg->nodeIDbits = 1;
			gf_bifs_encoder_new_stream(bifs_enc, esd->ESID, bcfg, (flags & GF_SM_ENCODE_USE_NAMES) ? 1 : 0, 0);
			if (delete_bcfg) gf_odf_desc_del((GF_Descriptor *)bcfg);
			/*create final BIFS config*/
			if (esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo);
			esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG);
			gf_bifs_encoder_get_config(bifs_enc, esd->ESID, &data, &data_len);
			esd->decoderConfig->decoderSpecificInfo->data = data;
			esd->decoderConfig->decoderSpecificInfo->dataLength = data_len;
			esd->decoderConfig->objectTypeIndication = gf_bifs_encoder_get_version(bifs_enc, esd->ESID);
#endif
		}
		/*LASeR setup*/
#ifndef GPAC_DISABLE_LASER
		if (scene_type==1) {
			GF_LASERConfig lsrcfg;

			if (!esd->decoderConfig->decoderSpecificInfo) {
				memset(&lsrcfg, 0, sizeof(GF_LASERConfig));
				lsrcfg.tag = GF_ODF_LASER_CFG_TAG;
			} else if (esd->decoderConfig->decoderSpecificInfo->tag == GF_ODF_LASER_CFG_TAG) {
				memcpy(&lsrcfg, (GF_LASERConfig *)esd->decoderConfig->decoderSpecificInfo, sizeof(GF_LASERConfig));
			} else {
				gf_odf_get_laser_config(esd->decoderConfig->decoderSpecificInfo, &lsrcfg);
			}
			/*create final BIFS config*/
			if (esd->decoderConfig->decoderSpecificInfo) gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo);
			esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG);

			/*this is for safety, otherwise some players may not understand NULL node*/
			if (flags & GF_SM_ENCODE_USE_NAMES) lsrcfg.force_string_ids = 1;
			/*override of default*/
			if (opts) {
				if (opts->resolution) lsrcfg.resolution = opts->resolution;
				if (opts->coord_bits) lsrcfg.coord_bits = opts->coord_bits;
				/*by default use 2 extra bits for scale*/
				lsrcfg.scale_bits_minus_coord_bits = opts->scale_bits ? opts->scale_bits : 2;
			}

			gf_laser_encoder_new_stream(lsr_enc, esd->ESID , &lsrcfg);
			/*get final config*/
			gf_laser_encoder_get_config(lsr_enc, esd->ESID, &data, &data_len);

			esd->decoderConfig->decoderSpecificInfo->data = data;
			esd->decoderConfig->decoderSpecificInfo->dataLength = data_len;
			esd->decoderConfig->objectTypeIndication = GPAC_OTI_SCENE_LASER;
		}
#endif
		/*create stream description*/
		gf_isom_new_mpeg4_description(mp4, track, esd, NULL, NULL, &di);
		if (is_in_iod) {
			gf_isom_add_track_to_root_od(mp4, track);
			if (ctx->scene_width && ctx->scene_height)
				gf_isom_set_visual_info(mp4, track, di, ctx->scene_width, ctx->scene_height);
		}
		if (esd->URLString) continue;

		if (!sc) {
			samp = gf_isom_sample_new();
			samp->IsRAP = RAP;

#ifndef GPAC_DISABLE_BIFS_ENC
			if (bifs_enc)
				e = gf_bifs_encoder_get_rap(bifs_enc, &samp->data, &samp->dataLength);
#endif

#ifndef GPAC_DISABLE_LASER
			if (lsr_enc)
				e = gf_laser_encoder_get_rap(lsr_enc, &samp->data, &samp->dataLength);
#endif
			if (!e && samp->dataLength) e = gf_isom_add_sample(mp4, track, di, samp);
			gf_isom_sample_del(&samp);
			goto exit;
		}

		dur = 0;
		avg_rate = 0;
		esd->decoderConfig->bufferSizeDB = 0;
		esd->decoderConfig->maxBitrate = 0;
		rate = 0;
		time_slice = 0;
		last_rap = 0;
		rap_delay = 0;
		if (opts) rap_delay = opts->rap_freq * esd->slConfig->timestampResolution / 1000;

		prev_dts = 0;
		init_offset = 0;
		j=0;
		while ((au = (GF_AUContext *)gf_list_enum(sc->AUs, &j))) {
			u32 samp_size;
			samp = gf_isom_sample_new();
			/*time in sec conversion*/
			if (au->timing_sec) au->timing = (u64) (au->timing_sec * esd->slConfig->timestampResolution + 0.0005);

			if (j==1) init_offset = (u32) au->timing;

			samp->DTS = au->timing - init_offset;
			if ((j>1) && (samp->DTS == prev_dts)) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[OD-SL] Same sample time %d for Access Unit %d and %d\n", au->timing, j, j-1));
				e = GF_BAD_PARAM;
				goto exit;
			}
			samp->IsRAP = au->flags & GF_SM_AU_RAP;
			if (samp->IsRAP) last_rap = au->timing;


#ifndef GPAC_DISABLE_BIFS_ENC
			if (bifs_enc)
				e = gf_bifs_encode_au(bifs_enc, sc->ESID, au->commands, &samp->data, &samp->dataLength);
#endif
#ifndef GPAC_DISABLE_LASER
			if (lsr_enc)
				e = gf_laser_encode_au(lsr_enc, sc->ESID, au->commands, 0, &samp->data, &samp->dataLength);
#endif

			samp_size = samp->dataLength;

			/*inband RAP */
			if (rap_mode==3) {
				/*current sample before or at the next rep - apply commands*/
				if (samp->DTS <= last_rap + rap_delay) {
					e = gf_sg_command_apply_list(ctx->scene_graph, au->commands, 0);
				}

				/*current sample is after or at next rap, insert rap*/
				while (samp->DTS >= last_rap + rap_delay) {
					GF_ISOSample *rap_sample = gf_isom_sample_new();

#ifndef GPAC_DISABLE_BIFS_ENC
					if (bifs_enc)
						e = gf_bifs_encoder_get_rap(bifs_enc, &rap_sample->data, &rap_sample->dataLength);
#endif

#ifndef GPAC_DISABLE_LASER
					if (lsr_enc)
						e = gf_laser_encoder_get_rap(lsr_enc, &rap_sample->data, &rap_sample->dataLength);
#endif

					rap_sample->DTS = last_rap + rap_delay;
					rap_sample->IsRAP = RAP;
					last_rap = rap_sample->DTS;


					if (!e) e = gf_isom_add_sample(mp4, track, di, rap_sample);
					if (samp_size < rap_sample->dataLength) samp_size = rap_sample->dataLength;

					gf_isom_sample_del(&rap_sample);
					/*same timing, don't add sample*/
					if (last_rap == samp->DTS) {
						if (samp->data) gf_free(samp->data);
						samp->data = NULL;
						samp->dataLength = 0;
					}
				}

				/*apply commands */
				if (samp->DTS > last_rap + rap_delay) {
					e = gf_sg_command_apply_list(ctx->scene_graph, au->commands, 0);
				}
			}

			/*carousel generation*/
			if (!e && (rap_mode == 1)) {
				if (samp->DTS - last_rap > rap_delay) {
					GF_ISOSample *car_samp = gf_isom_sample_new();
					u64 r_dts = samp->DTS;

					/*then get RAP*/
#ifndef GPAC_DISABLE_BIFS_ENC
					if (bifs_enc)
						e = gf_bifs_encoder_get_rap(bifs_enc, &car_samp->data, &car_samp->dataLength);
#endif

#ifndef GPAC_DISABLE_LASER
					if (lsr_enc)
						e = gf_laser_encoder_get_rap(lsr_enc, &car_samp->data, &car_samp->dataLength);
#endif
					car_samp->IsRAP = RAP_REDUNDANT;
					while (1) {
						car_samp->DTS = last_rap+rap_delay;
						if (car_samp->DTS==prev_dts) car_samp->DTS++;
						e = gf_isom_add_sample(mp4, track, di, car_samp);
						if (e) break;
						last_rap+=rap_delay;
						if (last_rap + rap_delay >= r_dts) break;
					}
					gf_isom_sample_del(&car_samp);
				}
				if (!e && samp->dataLength) e = gf_isom_add_sample(mp4, track, di, samp);
				/*accumulate commmands*/
				e = gf_sg_command_apply_list(ctx->scene_graph, au->commands, 0);
			} else {
				/*if no commands don't add the AU*/
				if (!e && samp->dataLength) e = gf_isom_add_sample(mp4, track, di, samp);
			}

			dur = au->timing;
			avg_rate += samp_size;
			rate += samp_size;
			if (esd->decoderConfig->bufferSizeDB < samp_size) esd->decoderConfig->bufferSizeDB = samp_size;
			if (samp->DTS - time_slice > esd->slConfig->timestampResolution) {
				if (esd->decoderConfig->maxBitrate < rate) esd->decoderConfig->maxBitrate = rate;
				rate = 0;
				time_slice = samp->DTS;
			}

			prev_dts = samp->DTS;
			gf_isom_sample_del(&samp);
			if (e) goto exit;
		}

		if (dur) {
			esd->decoderConfig->avgBitrate = (u32) (avg_rate * esd->slConfig->timestampResolution * 8 / dur);
			esd->decoderConfig->maxBitrate *= 8;
		} else {
			esd->decoderConfig->avgBitrate = 0;
			esd->decoderConfig->maxBitrate = 0;
		}
		gf_isom_change_mpeg4_description(mp4, track, 1, esd);

		/*sync shadow generation*/
		if (rap_mode==2) {
			GF_AUContext *au;
			u32 au_count = gf_list_count(sc->AUs);
			last_rap = 0;
			for (j=0; j<au_count; j++) {
				au = (GF_AUContext *)gf_list_get(sc->AUs, j);
				e = gf_sg_command_apply_list(ctx->scene_graph, au->commands, 0);
				if (!j) continue;
				/*force a RAP shadow on last sample*/
				if ((au->timing - last_rap < rap_delay) && (j+1<au_count) ) continue;

				samp = gf_isom_sample_new();
				last_rap = samp->DTS = au->timing - init_offset;
				samp->IsRAP = RAP;
				/*RAP generation*/
#ifndef GPAC_DISABLE_BIFS_ENC
				if (bifs_enc)
					e = gf_bifs_encoder_get_rap(bifs_enc, &samp->data, &samp->dataLength);
#endif

				if (!e) e = gf_isom_add_sample_shadow(mp4, track, samp);
				gf_isom_sample_del(&samp);
				if (e) goto exit;
			}
		}

		/*if offset add edit list*/
		gf_sm_finalize_mux(mp4, esd, (u32) init_offset);
		gf_isom_set_last_sample_duration(mp4, track, 0);

		mux = gf_sm_get_mux_info(esd);
		if (mux && mux->duration) {
			u64 tot_dur = mux->duration * esd->slConfig->timestampResolution / 1000;
			u64 dur = gf_isom_get_media_duration(mp4, track);
			if (dur <= tot_dur)
				gf_isom_set_last_sample_duration(mp4, track, (u32) (tot_dur - dur));
		}

		if (delete_desc) {
			gf_odf_desc_del((GF_Descriptor *) esd);
			esd = NULL;
		}
	}

	/*to do - proper PL setup according to node used...*/
	gf_isom_set_pl_indication(mp4, GF_ISOM_PL_SCENE, 1);
	gf_isom_set_pl_indication(mp4, GF_ISOM_PL_GRAPHICS, 1);

exit:
#ifndef GPAC_DISABLE_BIFS_ENC
	if (bifs_enc) gf_bifs_encoder_del(bifs_enc);
#endif
#ifndef GPAC_DISABLE_LASER
	if (lsr_enc) gf_laser_encoder_del(lsr_enc);
#endif
	if (esd && delete_desc) gf_odf_desc_del((GF_Descriptor *) esd);
	return e;
}