This source file includes following definitions.
- gf_sm_setup_bifsenc
- gf_sm_setup_lsrenc
- gf_sm_live_setup
- gf_seng_enable_aggregation
- gf_seng_encode_dims_au
- gf_sm_check_for_modif
- gf_sm_live_encode_scene_au
- gf_seng_aggregate_context
- gf_seng_dump_rap_on
- gf_seng_save_context
- gf_seng_create_new_au
- gf_seng_encode_from_string
- gf_seng_encode_from_commands
- gf_seng_encode_from_file
- gf_seng_encode_context
- gf_seng_terminate
- gf_seng_get_stream_config
- seng_exec_conditional
- seng_conditional_activate
- seng_conditional_reverse_activate
- gf_seng_on_node_modified
- gf_seng_init
- gf_seng_init_from_context
- gf_seng_init_from_string
- gf_seng_get_stream_count
- gf_seng_get_stream_carousel_info
- gf_seng_get_base64_iod
- gf_seng_get_iod
#include <gpac/scene_engine.h>
#ifndef GPAC_DISABLE_SENG
#include <gpac/scene_manager.h>
#ifndef GPAC_DISABLE_BIFS_ENC
#include <gpac/bifs.h>
#endif
#ifndef GPAC_DISABLE_VRML
#include <gpac/nodes_mpeg4.h>
#endif
#ifndef GPAC_DISABLE_LASER
#include <gpac/laser.h>
#endif
#include <gpac/constants.h>
#include <gpac/base_coding.h>
#include <time.h>
struct __tag_scene_engine
{
GF_SceneGraph *sg;
GF_SceneManager *ctx;
GF_SceneLoader loader;
void *calling_object;
Bool owns_context;
#ifndef GPAC_DISABLE_BIFS_ENC
GF_BifsEncoder *bifsenc;
#endif
#ifndef GPAC_DISABLE_LASER
GF_LASeRCodec *lsrenc;
#endif
u32 start_time;
char *dump_path;
Bool embed_resources;
Bool dump_rap;
Bool first_dims_sent;
};
#ifndef GPAC_DISABLE_BIFS_ENC
static GF_Err gf_sm_setup_bifsenc(GF_SceneEngine *seng, GF_StreamContext *sc, GF_ESD *esd)
{
char *data;
u32 data_len;
u32 nbb;
Bool encode_names, delete_bcfg;
GF_BIFSConfig *bcfg;
if (!esd->decoderConfig || (esd->decoderConfig->streamType != GF_STREAM_SCENE)) return GF_BAD_PARAM;
if (!seng->bifsenc)
seng->bifsenc = gf_bifs_encoder_new(seng->ctx->scene_graph);
delete_bcfg = 0;
if (!esd->decoderConfig->decoderSpecificInfo) {
bcfg = (GF_BIFSConfig*)gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG);
bcfg->pixelMetrics = seng->ctx->is_pixel_metrics;
bcfg->pixelWidth = seng->ctx->scene_width;
bcfg->pixelHeight = seng->ctx->scene_height;
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;
}
nbb = gf_get_bit_size(seng->ctx->max_node_id);
if (!bcfg->nodeIDbits) bcfg->nodeIDbits = nbb;
else if (bcfg->nodeIDbits<nbb) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[BIFS] BIFSConfig.NodeIDBits too small (%d bits vs %d nodes)\n", bcfg->nodeIDbits, seng->ctx->max_node_id));
}
nbb = gf_get_bit_size(seng->ctx->max_route_id);
if (!bcfg->routeIDbits) bcfg->routeIDbits = nbb;
else if (bcfg->routeIDbits<nbb) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[BIFS] BIFSConfig.RouteIDBits too small (%d bits vs %d routes)\n", bcfg->routeIDbits, seng->ctx->max_route_id));
}
nbb = gf_get_bit_size(seng->ctx->max_proto_id);
if (!bcfg->protoIDbits) bcfg->protoIDbits=nbb;
else if (bcfg->protoIDbits<nbb) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[BIFS] BIFSConfig.ProtoIDBits too small (%d bits vs %d protos)\n", bcfg->protoIDbits, seng->ctx->max_proto_id));
}
encode_names = 0;
gf_bifs_encoder_new_stream(seng->bifsenc, esd->ESID, bcfg, encode_names, 0);
if (delete_bcfg) gf_odf_desc_del((GF_Descriptor *)bcfg);
gf_bifs_encoder_get_config(seng->bifsenc, esd->ESID, &data, &data_len);
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);
esd->decoderConfig->decoderSpecificInfo->data = data;
esd->decoderConfig->decoderSpecificInfo->dataLength = data_len;
sc->dec_cfg = gf_malloc(sizeof(char)*data_len);
memcpy(sc->dec_cfg, data, data_len);
sc->dec_cfg_len = data_len;
esd->decoderConfig->objectTypeIndication = gf_bifs_encoder_get_version(seng->bifsenc, esd->ESID);
return GF_OK;
}
#endif
#ifndef GPAC_DISABLE_LASER
static GF_Err gf_sm_setup_lsrenc(GF_SceneEngine *seng, GF_StreamContext *sc, GF_ESD *esd)
{
char *data;
u32 data_len;
GF_LASERConfig lsr_cfg;
if (!esd->decoderConfig || (esd->decoderConfig->streamType != GF_STREAM_SCENE)) return GF_BAD_PARAM;
seng->lsrenc = gf_laser_encoder_new(seng->ctx->scene_graph);
if (!esd->decoderConfig->decoderSpecificInfo) {
memset(&lsr_cfg, 0, sizeof(GF_LASERConfig));
}
else if (esd->decoderConfig->decoderSpecificInfo->tag == GF_ODF_LASER_CFG_TAG) {
memcpy(&lsr_cfg, (GF_LASERConfig *)esd->decoderConfig->decoderSpecificInfo, sizeof(GF_LASERConfig) );
}
else {
gf_odf_get_laser_config(esd->decoderConfig->decoderSpecificInfo, &lsr_cfg);
}
gf_laser_encoder_new_stream(seng->lsrenc, esd->ESID , &lsr_cfg);
gf_laser_encoder_get_config(seng->lsrenc, esd->ESID, &data, &data_len);
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);
esd->decoderConfig->decoderSpecificInfo->data = data;
esd->decoderConfig->decoderSpecificInfo->dataLength = data_len;
sc->dec_cfg = (char*)gf_malloc(sizeof(char)*data_len);
memcpy(sc->dec_cfg, data, data_len);
sc->dec_cfg_len = data_len;
return GF_OK;
}
#endif
static GF_Err gf_sm_live_setup(GF_SceneEngine *seng)
{
GF_Err e;
GF_StreamContext *sc;
GF_InitialObjectDescriptor *iod;
GF_ESD *esd;
u32 i, j;
e = GF_OK;
iod = (GF_InitialObjectDescriptor *) seng->ctx->root_od;
if (!iod) {
seng->ctx->root_od = (GF_ObjectDescriptor*) gf_odf_desc_new(GF_ODF_IOD_TAG);
iod = (GF_InitialObjectDescriptor *) seng->ctx->root_od;
i=0;
while ((sc = gf_list_enum(seng->ctx->streams, &i))) {
if (sc->streamType != GF_STREAM_SCENE) continue;
if (!sc->ESID) sc->ESID = 1;
esd = gf_odf_desc_esd_new(2);
gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo);
esd->decoderConfig->decoderSpecificInfo = NULL;
esd->ESID = sc->ESID;
esd->decoderConfig->streamType = GF_STREAM_SCENE;
esd->decoderConfig->objectTypeIndication = sc->objectType;
gf_list_add(iod->ESDescriptors, esd);
if (!sc->timeScale) sc->timeScale = 1000;
esd->slConfig->timestampResolution = sc->timeScale;
}
}
i=0;
while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
j=0;
while ((esd = gf_list_enum(seng->ctx->root_od->ESDescriptors, &j))) {
if (sc->ESID==esd->ESID) {
break;
}
}
if (!esd) continue;
if (!esd->slConfig) esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG);
if (!esd->slConfig->timestampResolution) esd->slConfig->timestampResolution = 1000;
if (!sc->timeScale) sc->timeScale = esd->slConfig->timestampResolution;
if (sc->streamType == GF_STREAM_SCENE) {
switch (sc->objectType) {
#ifndef GPAC_DISABLE_BIFS_ENC
case GPAC_OTI_SCENE_BIFS:
case GPAC_OTI_SCENE_BIFS_V2:
e = gf_sm_setup_bifsenc(seng, sc, esd);
break;
#endif
#ifndef GPAC_DISABLE_LASER
case GPAC_OTI_SCENE_LASER:
e = gf_sm_setup_lsrenc(seng, sc, esd);
break;
#endif
case GPAC_OTI_SCENE_DIMS:
break;
default:
e = GF_NOT_SUPPORTED;
break;
}
if (e) return e;
}
}
return e;
}
GF_EXPORT
GF_Err gf_seng_enable_aggregation(GF_SceneEngine *seng, u16 ESID, u16 onESID)
{
GF_StreamContext *sc;
if (ESID) {
u32 i=0;
while (NULL != (sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
if (0 != (sc->ESID==ESID)) break;
}
} else {
sc = (GF_StreamContext*)gf_list_get(seng->ctx->streams, 0);
}
if (!sc) return GF_STREAM_NOT_FOUND;
sc->aggregate_on_esid = onESID;
return GF_OK;
}
static GF_Err gf_seng_encode_dims_au(GF_SceneEngine *seng, u16 ESID, GF_List *commands, char **data, u32 *size)
{
#ifndef GPAC_DISABLE_SCENE_DUMP
GF_SceneDumper *dumper = NULL;
#endif
GF_Err e;
char rad_name[4096];
char file_name[4096];
FILE *file = NULL;
u64 fsize;
char *buffer = NULL;
GF_BitStream *bs = NULL;
u8 dims_header;
Bool compress_dims;
#ifdef DUMP_DIMS_LOG_WITH_TIME
u32 do_dump_with_time = 1;
#endif
u32 buffer_len;
char *cache_dir, *dump_name;
if (!data) return GF_BAD_PARAM;
if (!seng->dump_path) cache_dir = gf_get_default_cache_directory();
else cache_dir = seng->dump_path;
dump_name = "gpac_scene_engine_dump";
compress_dims = 1;
#ifdef DUMP_DIMS_LOG_WITH_TIME
start:
#endif
if (commands && gf_list_count(commands)) {
sprintf(rad_name, "%s%c%s%s", cache_dir, GF_PATH_SEPARATOR, dump_name, "_update");
} else {
#ifndef DUMP_DIMS_LOG_WITH_TIME
sprintf(rad_name, "%s%c%s%s", cache_dir, GF_PATH_SEPARATOR, "rap_", dump_name);
#else
char date_str[100], time_str[100];
time_t now;
struct tm *tm_tot;
now = time(NULL);
tm_tot = localtime(&now);
strftime(date_str, 100, "%Yy%mm%dd", tm_tot);
strftime(time_str, 100, "%Hh%Mm%Ss", tm_tot);
sprintf(rad_name, "%s%c%s-%s-%s%s", cache_dir, GF_PATH_SEPARATOR, date_str, time_str, "rap_", dump_name);
#endif
}
#ifndef GPAC_DISABLE_SCENE_DUMP
dumper = gf_sm_dumper_new(seng->ctx->scene_graph, rad_name, GF_FALSE, ' ', GF_SM_DUMP_SVG);
if (!dumper) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[SceneEngine] Cannot create SVG dumper for %s.svg\n", rad_name));
e = GF_IO_ERR;
goto exit;
}
if (commands && gf_list_count(commands)) {
e = gf_sm_dump_command_list(dumper, commands, 0, 0);
}
else {
e = gf_sm_dump_graph(dumper, 0, 0);
}
gf_sm_dumper_del(dumper);
if(seng->dump_rap) {
GF_SceneDumper *dumper = NULL;
sprintf(rad_name, "%s%c%s%s", cache_dir, GF_PATH_SEPARATOR, "rap_", dump_name);
dumper = gf_sm_dumper_new(seng->ctx->scene_graph, rad_name, GF_FALSE, ' ', GF_SM_DUMP_SVG);
if (!dumper) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[SceneEngine] Cannot create SVG dumper for %s.svg\n", rad_name));
e = GF_IO_ERR;
goto exit;
}
e = gf_sm_dump_graph(dumper, 0, 0);
gf_sm_dumper_del(dumper);
}
if (e) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[SceneEngine] Cannot dump DIMS Commands\n"));
goto exit;
}
#endif
#ifdef DUMP_DIMS_LOG_WITH_TIME
if (do_dump_with_time) {
do_dump_with_time = 0;
goto start;
}
#endif
sprintf(file_name, "%s.svg", rad_name);
file = gf_fopen(file_name, "rb");
if (!file) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[SceneEngine] Cannot open SVG dump file %s\n", file_name));
e = GF_IO_ERR;
goto exit;
}
gf_fseek(file, 0, SEEK_END);
fsize = gf_ftell(file);
if (fsize == 0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[SceneEngine] SVG dump %s is empty\n", file_name));
goto exit;
}
buffer = gf_malloc((size_t)fsize * sizeof(char));
gf_fseek(file, 0, SEEK_SET);
fsize = fread(buffer, sizeof(char), (size_t)fsize, file);
gf_fclose(file);
file = NULL;
if (commands && gf_list_count(commands)) {
dims_header = GF_DIMS_UNIT_P;
} else {
dims_header = GF_DIMS_UNIT_M | GF_DIMS_UNIT_S | GF_DIMS_UNIT_I | GF_DIMS_UNIT_P;
}
buffer_len = (u32)fsize;
assert(fsize < 1<<31);
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[SceneEngine] Sending DIMS data - sizes: raw (%d)", buffer_len));
if (compress_dims) {
#ifndef GPAC_DISABLE_ZLIB
dims_header |= GF_DIMS_UNIT_C;
e = gf_gz_compress_payload(&buffer, buffer_len, &buffer_len);
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("/ compressed (%d)", buffer_len));
if (e) goto exit;
#else
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Error: your version of GPAC was compile with no libz support. Abort."));
e = GF_NOT_SUPPORTED;
goto exit;
#endif
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\n"));
bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
if (buffer_len > 65535) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[SceneEngine] Warning: DIMS Unit size too big !!!\n"));
gf_bs_write_u16(bs, 0);
gf_bs_write_u32(bs, buffer_len+1);
} else {
gf_bs_write_u16(bs, buffer_len+1);
}
gf_bs_write_u8(bs, dims_header);
gf_bs_write_data(bs, buffer, buffer_len);
gf_free(buffer);
buffer = NULL;
gf_bs_get_content(bs, data, size);
gf_bs_del(bs);
exit:
if (!seng->dump_path) gf_free(cache_dir);
if (buffer) gf_free(buffer);
if (file) gf_fclose(file);
return e;
}
static Bool gf_sm_check_for_modif(GF_SceneEngine *seng, GF_AUContext *au)
{
GF_Command *com;
Bool modified=0;
u32 i=0;
if (au->flags & GF_SM_AU_MODIFIED) {
au->flags &= ~GF_SM_AU_MODIFIED;
modified=1;
}
while (NULL != (com = gf_list_enum(au->commands, &i))) {
u32 j=0;
GF_CommandField *field;
if (!com->node) continue;
if (gf_node_dirty_get(com->node)) {
modified=1;
gf_node_dirty_reset(com->node, 1);
}
while (NULL != (field = gf_list_enum(com->command_fields, &j))) {
switch (field->fieldType) {
case GF_SG_VRML_SFNODE:
if (field->new_node) {
if (gf_node_dirty_get(field->new_node)) {
modified=1;
gf_node_dirty_reset(field->new_node, 1);
}
}
break;
case GF_SG_VRML_MFNODE:
if (field->field_ptr) {
GF_ChildNodeItem *child;
child = field->node_list;
while (child) {
if (gf_node_dirty_get(child->node)) {
modified=1;
gf_node_dirty_reset(child->node, 1);
}
child = child->next;
}
}
break;
}
}
}
if (!seng->first_dims_sent) {
if (au->owner->objectType==GPAC_OTI_SCENE_DIMS) {
GF_Node *root = gf_sg_get_root_node(seng->ctx->scene_graph);
if (gf_node_dirty_get(root)) {
modified=1;
gf_node_dirty_reset(root, 1);
}
} else {
}
seng->first_dims_sent = 1;
}
return modified;
}
static GF_Err gf_sm_live_encode_scene_au(GF_SceneEngine *seng, gf_seng_callback callback, Bool from_start)
{
GF_Err e;
u32 i, j, size, count, nb_streams;
char *data;
GF_AUContext *au;
if (!callback) return GF_BAD_PARAM;
e = GF_OK;
nb_streams = gf_list_count(seng->ctx->streams);
for (i=0; i<nb_streams; i++) {
GF_StreamContext *sc = gf_list_get(seng->ctx->streams, i);
if (sc->streamType != GF_STREAM_SCENE) continue;
count = gf_list_count(sc->AUs);
j = from_start ? 0 : sc->current_au_count;
for (; j<count; j++) {
au = (GF_AUContext *)gf_list_get(sc->AUs, j);
data = NULL;
size = 0;
if (au->timing_sec) au->timing = (u64) (au->timing_sec * sc->timeScale);
if (from_start && !j && !gf_sm_check_for_modif(seng, au)) continue;
switch (sc->objectType) {
#ifndef GPAC_DISABLE_BIFS_ENC
case GPAC_OTI_SCENE_BIFS:
case GPAC_OTI_SCENE_BIFS_V2:
e = gf_bifs_encode_au(seng->bifsenc, sc->ESID, au->commands, &data, &size);
break;
#endif
#ifndef GPAC_DISABLE_LASER
case GPAC_OTI_SCENE_LASER:
e = gf_laser_encode_au(seng->lsrenc, sc->ESID, au->commands, 0, &data, &size);
break;
#endif
case GPAC_OTI_SCENE_DIMS:
e = gf_seng_encode_dims_au(seng, sc->ESID, au->commands, &data, &size);
break;
default:
GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("Cannot encode AU for Scene OTI %x\n", sc->objectType));
break;
}
callback(seng->calling_object, sc->ESID, data, size, au->timing);
gf_free(data);
data = NULL;
if (e) break;
}
}
return e;
}
GF_EXPORT
GF_Err gf_seng_aggregate_context(GF_SceneEngine *seng, u16 ESID)
{
return gf_sm_aggregate(seng->ctx, ESID);
}
GF_EXPORT
GF_Err gf_seng_dump_rap_on(GF_SceneEngine *seng, Bool dump_rap)
{
seng->dump_rap = dump_rap;
return 0;
}
GF_EXPORT
GF_Err gf_seng_save_context(GF_SceneEngine *seng, char *ctxFileName)
{
#ifdef GPAC_DISABLE_SCENE_DUMP
return GF_NOT_SUPPORTED;
#else
u32 d_mode, do_enc;
char szF[GF_MAX_PATH], *ext;
GF_Err e;
ext = NULL;
if (ctxFileName) {
strcpy(szF, ctxFileName);
ext = strrchr(szF, '.');
}
d_mode = GF_SM_DUMP_BT;
do_enc = 0;
if (ext) {
if (!stricmp(ext, ".xmt") || !stricmp(ext, ".xmta")) d_mode = GF_SM_DUMP_XMTA;
else if (!stricmp(ext, ".mp4")) do_enc = 1;
ext[0] = 0;
}
if (do_enc) {
#ifndef GPAC_DISABLE_SCENE_ENCODER
GF_ISOFile *mp4;
strcat(szF, ".mp4");
mp4 = gf_isom_open(szF, GF_ISOM_OPEN_WRITE, NULL);
e = gf_sm_encode_to_file(seng->ctx, mp4, NULL);
if (e) gf_isom_delete(mp4);
else gf_isom_close(mp4);
#else
return GF_NOT_SUPPORTED;
#endif
} else {
e = gf_sm_dump(seng->ctx, ctxFileName ? szF : NULL, GF_FALSE, d_mode);
}
return e;
#endif
}
static GF_AUContext *gf_seng_create_new_au(GF_StreamContext *sc, u32 time)
{
GF_AUContext *new_au, *last_au;
last_au = gf_list_last(sc->AUs);
if (last_au && last_au->timing == time) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCENE, ("[SceneEngine] Forcing new AU\n"));
time++;
}
new_au = gf_sm_stream_au_new(sc, time, 0, 0);
return new_au;
}
GF_EXPORT
GF_Err gf_seng_encode_from_string(GF_SceneEngine *seng, u16 ESID, Bool disable_aggregation, char *auString, gf_seng_callback callback)
{
GF_StreamContext *sc;
u32 i;
GF_Err e;
i = 0;
while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
sc->current_au_count = gf_list_count(sc->AUs);
sc->disable_aggregation = disable_aggregation;
}
seng->loader.flags |= GF_SM_LOAD_CONTEXT_READY;
seng->loader.force_es_id = ESID;
sc = gf_list_get(seng->ctx->streams, 0);
if (sc->objectType == GPAC_OTI_SCENE_DIMS) {
gf_seng_create_new_au(sc, 0);
}
e = gf_sm_load_string(&seng->loader, auString, 0);
if (e) goto exit;
i = 0;
while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
sc->disable_aggregation = 0;
}
e = gf_sm_live_encode_scene_au(seng, callback, 0);
exit:
return e;
}
GF_EXPORT
GF_Err gf_seng_encode_from_commands(GF_SceneEngine *seng, u16 ESID, Bool disable_aggregation, u32 time, GF_List *commands, gf_seng_callback callback)
{
GF_Err e;
u32 size;
char *data;
GF_StreamContext *sc;
u32 i, nb_streams;
GF_AUContext *new_au;
if (!callback) return GF_BAD_PARAM;
if (!commands || !gf_list_count(commands)) return GF_BAD_PARAM;
e = GF_OK;
sc = NULL;
nb_streams = gf_list_count(seng->ctx->streams);
for (i=0; i<nb_streams; i++) {
GF_StreamContext *tmp_sc = gf_list_get(seng->ctx->streams, i);
if (tmp_sc->streamType != GF_STREAM_SCENE) continue;
sc = tmp_sc;
if (!ESID) break;
else if (sc->ESID == ESID) break;
}
if (!sc) return GF_BAD_PARAM;
new_au = gf_seng_create_new_au(sc, time);
if (disable_aggregation) new_au->flags = GF_SM_AU_NOT_AGGREGATED;
while (gf_list_count(commands)) {
GF_Command *com = gf_list_get(commands, 0);
gf_list_rem(commands, 0);
switch (com->tag) {
#ifndef GPAC_DISABLE_VRML
case GF_SG_SCENE_REPLACE:
new_au->flags |= GF_SM_AU_RAP;
break;
#endif
case GF_SG_LSR_NEW_SCENE:
new_au->flags |= GF_SM_AU_RAP;
break;
}
gf_list_add(new_au->commands, com);
}
data = NULL;
size = 0;
switch(sc->objectType) {
#ifndef GPAC_DISABLE_BIFS_ENC
case GPAC_OTI_SCENE_BIFS:
case GPAC_OTI_SCENE_BIFS_V2:
e = gf_bifs_encode_au(seng->bifsenc, ESID, new_au->commands, &data, &size);
break;
#endif
#ifndef GPAC_DISABLE_LASER
case GPAC_OTI_SCENE_LASER:
e = gf_laser_encode_au(seng->lsrenc, ESID, new_au->commands, 0, &data, &size);
break;
#endif
case GPAC_OTI_SCENE_DIMS:
e = gf_seng_encode_dims_au(seng, ESID, new_au->commands, &data, &size);
break;
default:
GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("Cannot encode commands for Scene OTI %x\n", sc->objectType));
break;
}
callback(seng->calling_object, ESID, data, size, 0);
gf_free(data);
return e;
}
GF_EXPORT
GF_Err gf_seng_encode_from_file(GF_SceneEngine *seng, u16 ESID, Bool disable_aggregation, char *auFile, gf_seng_callback callback)
{
GF_Err e;
GF_StreamContext *sc;
u32 i;
Bool dims = 0;
seng->loader.fileName = auFile;
seng->loader.ctx = seng->ctx;
seng->loader.force_es_id = ESID;
sc = NULL;
i=0;
while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
sc->current_au_count = gf_list_count(sc->AUs);
sc->disable_aggregation = disable_aggregation;
}
sc = gf_list_get(seng->ctx->streams, 0);
if (sc->objectType == GPAC_OTI_SCENE_DIMS) {
dims = 1;
gf_seng_create_new_au(sc, 0);
}
seng->loader.flags |= GF_SM_LOAD_CONTEXT_READY;
if (dims) {
seng->loader.type |= GF_SM_LOAD_DIMS;
} else {
seng->loader.flags |= GF_SM_LOAD_MPEG4_STRICT;
}
e = gf_sm_load_run(&seng->loader);
if (e<0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneEngine] cannot load AU File %s (error %s)\n", auFile, gf_error_to_string(e)));
goto exit;
}
i = 0;
while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
sc->disable_aggregation = 0;
}
e = gf_sm_live_encode_scene_au(seng, callback, 0);
if (e) goto exit;
exit:
return e;
}
GF_EXPORT
GF_Err gf_seng_encode_context(GF_SceneEngine *seng, gf_seng_callback callback)
{
if (!seng) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneEngine] Cannot encode context. No seng provided\n"));
return GF_BAD_PARAM;
}
return gf_sm_live_encode_scene_au(seng, callback, 1);
}
GF_EXPORT
void gf_seng_terminate(GF_SceneEngine *seng)
{
#ifndef GPAC_DISABLE_BIFS_ENC
if (seng->bifsenc) gf_bifs_encoder_del(seng->bifsenc);
#endif
#ifndef GPAC_DISABLE_LASER
if (seng->lsrenc) gf_laser_encoder_del(seng->lsrenc);
#endif
gf_sm_load_done(&seng->loader);
if (seng->owns_context) {
if (seng->ctx) gf_sm_del(seng->ctx);
if (seng->sg) gf_sg_del(seng->sg);
}
gf_free(seng);
}
GF_EXPORT
GF_Err gf_seng_get_stream_config(GF_SceneEngine *seng, u32 idx, u16 *ESID, char ** const config, u32 *config_len, u32 *streamType, u32 *objectType, u32 *timeScale)
{
GF_StreamContext *sc = gf_list_get(seng->ctx->streams, idx);
if (!sc || !ESID || !config || !config_len) return GF_BAD_PARAM;
*ESID = sc->ESID;
*config = sc->dec_cfg;
*config_len = sc->dec_cfg_len;
if (streamType) *streamType = sc->streamType;
if (objectType) *objectType = sc->objectType;
if (timeScale) *timeScale = sc->timeScale;
return GF_OK;
}
#ifndef GPAC_DISABLE_VRML
static void seng_exec_conditional(M_Conditional *c, GF_SceneGraph *scene)
{
GF_List *clist = c->buffer.commandList;
c->buffer.commandList = NULL;
gf_sg_command_apply_list(gf_node_get_graph((GF_Node*)c), clist, 0.0);
if (c->buffer.commandList != NULL) {
while (gf_list_count(clist)) {
GF_Command *sub_com = (GF_Command *)gf_list_get(clist, 0);
gf_sg_command_del(sub_com);
gf_list_rem(clist, 0);
}
gf_list_del(clist);
} else {
c->buffer.commandList = clist;
}
}
static void seng_conditional_activate(GF_Node *node, GF_Route *route)
{
GF_SceneEngine *seng = (GF_SceneEngine *) gf_node_get_private(node);
M_Conditional *c = (M_Conditional*)node;
if (c->activate) seng_exec_conditional(c, seng->sg);
}
static void seng_conditional_reverse_activate(GF_Node *node, GF_Route *route)
{
GF_SceneEngine *seng = (GF_SceneEngine *) gf_node_get_private(node);
M_Conditional*c = (M_Conditional*)node;
if (!c->reverseActivate) seng_exec_conditional(c, seng->sg);
}
#endif
static void gf_seng_on_node_modified(void *_seng, u32 type, GF_Node *node, void *ctxdata)
{
switch (type) {
#ifndef GPAC_DISABLE_VRML
case GF_SG_CALLBACK_INIT:
if (gf_node_get_tag(node) == TAG_MPEG4_Conditional) {
M_Conditional*c = (M_Conditional*)node;
c->on_activate = seng_conditional_activate;
c->on_reverseActivate = seng_conditional_reverse_activate;
gf_node_set_private(node, _seng);
}
break;
#endif
case GF_SG_CALLBACK_MODIFIED:
gf_node_dirty_parents(node);
break;
}
}
GF_EXPORT
GF_SceneEngine *gf_seng_init(void *calling_object, char * inputContext, u32 load_type, char *dump_path, Bool embed_resources)
{
GF_SceneEngine *seng;
GF_Err e = GF_OK;
if (!inputContext) return NULL;
GF_SAFEALLOC(seng, GF_SceneEngine)
if (!seng) return NULL;
seng->calling_object = calling_object;
seng->sg = gf_sg_new();
gf_sg_set_node_callback(seng->sg, gf_seng_on_node_modified);
gf_sg_set_private(seng->sg, seng);
seng->dump_path = dump_path;
seng->ctx = gf_sm_new(seng->sg);
seng->owns_context = 1;
memset(&(seng->loader), 0, sizeof(GF_SceneLoader));
seng->loader.ctx = seng->ctx;
seng->loader.type = load_type;
seng->loader.flags = GF_SM_LOAD_MPEG4_STRICT;
if (embed_resources) seng->loader.flags |= GF_SM_LOAD_EMBEDS_RES;
seng->loader.fileName = inputContext;
e = gf_sm_load_init(&(seng->loader));
if (!e) e = gf_sm_load_run(&(seng->loader));
if (e<0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneEngine] Cannot load context from %s (error %s)\n", inputContext, gf_error_to_string(e)));
goto exit;
}
e = gf_sm_live_setup(seng);
if (e!=GF_OK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneEngine] cannot init scene encoder for context (error %s)\n", gf_error_to_string(e)));
goto exit;
}
return seng;
exit:
gf_seng_terminate(seng);
return NULL;
}
GF_EXPORT
GF_SceneEngine *gf_seng_init_from_context(void *calling_object, GF_SceneManager *ctx, char *dump_path)
{
GF_SceneEngine *seng;
GF_Err e = GF_OK;
if (!ctx) return NULL;
GF_SAFEALLOC(seng, GF_SceneEngine)
if (!seng) return NULL;
seng->calling_object = calling_object;
seng->dump_path = dump_path;
seng->sg = ctx->scene_graph;
seng->ctx = ctx;
seng->owns_context = 0;
e = gf_sm_live_setup(seng);
if (e!=GF_OK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneEngine] cannot init scene encoder for context (error %s)\n", gf_error_to_string(e)));
goto exit;
}
return seng;
exit:
gf_seng_terminate(seng);
return NULL;
}
GF_EXPORT
GF_SceneEngine *gf_seng_init_from_string(void *calling_object, char * inputContext, u32 load_type, u32 width, u32 height, Bool usePixelMetrics, char *dump_path)
{
GF_SceneEngine *seng;
GF_Err e = GF_OK;
if (!inputContext) return NULL;
GF_SAFEALLOC(seng, GF_SceneEngine)
if (!seng) return NULL;
seng->calling_object = calling_object;
seng->dump_path = dump_path;
seng->sg = gf_sg_new();
seng->ctx = gf_sm_new(seng->sg);
seng->owns_context = 1;
memset(& seng->loader, 0, sizeof(GF_SceneLoader));
seng->loader.ctx = seng->ctx;
seng->loader.type = load_type;
seng->loader.flags = GF_SM_LOAD_MPEG4_STRICT;
if (!seng->loader.type) {
if (inputContext[0] == '<') {
if (strstr(inputContext, "<svg ")) seng->loader.type = GF_SM_LOAD_SVG;
else if (strstr(inputContext, "<saf ")) seng->loader.type = GF_SM_LOAD_XSR;
else if (strstr(inputContext, "XMT-A") || strstr(inputContext, "X3D")) seng->loader.type = GF_SM_LOAD_XMTA;
} else {
seng->loader.type = GF_SM_LOAD_BT;
}
}
e = gf_sm_load_string(&seng->loader, inputContext, 0);
if (e) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneEngine] cannot load context from %s (error %s)\n", inputContext, gf_error_to_string(e)));
goto exit;
}
if (!seng->ctx->root_od) {
seng->ctx->is_pixel_metrics = usePixelMetrics;
seng->ctx->scene_width = width;
seng->ctx->scene_height = height;
}
e = gf_sm_live_setup(seng);
if (e!=GF_OK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneEngine] cannot init scene encoder for context (error %s)\n", gf_error_to_string(e)));
goto exit;
}
return seng;
exit:
gf_seng_terminate(seng);
return NULL;
}
GF_EXPORT
u32 gf_seng_get_stream_count(GF_SceneEngine *seng)
{
return gf_list_count(seng->ctx->streams);
}
GF_EXPORT
GF_Err gf_seng_get_stream_carousel_info(GF_SceneEngine *seng, u16 ESID, u32 *carousel_period, u16 *aggregate_on_es_id)
{
u32 i=0;
GF_StreamContext *sc;
if (carousel_period) *carousel_period = (u32) -1;
if (aggregate_on_es_id) *aggregate_on_es_id = 0;
while (NULL != (sc = gf_list_enum(seng->ctx->streams, &i))) {
if (sc->ESID==ESID) {
if (carousel_period) *carousel_period = sc->carousel_period;
if (aggregate_on_es_id) *aggregate_on_es_id = sc->aggregate_on_esid;
return GF_OK;
}
}
return GF_OK;
}
GF_EXPORT
char *gf_seng_get_base64_iod(GF_SceneEngine *seng)
{
u32 size, size64;
char *buffer, *buf64;
u32 i=0;
GF_StreamContext*sc = NULL;
if (!seng->ctx->root_od) return NULL;
while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
if ((sc->streamType == GF_STREAM_SCENE) && (sc->objectType != GPAC_OTI_SCENE_DIMS))
break;
}
if (!sc) return NULL;
size = 0;
gf_odf_desc_write((GF_Descriptor *) seng->ctx->root_od, &buffer, &size);
buf64 = gf_malloc(size*2);
size64 = gf_base64_encode( buffer, size, buf64, size*2);
buf64[size64] = 0;
gf_free(buffer);
return buf64;
}
GF_EXPORT
GF_Descriptor *gf_seng_get_iod(GF_SceneEngine *seng)
{
u32 i=0;
GF_Descriptor *out_iod = NULL;
GF_StreamContext*sc = NULL;
if (!seng->ctx->root_od) return NULL;
while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) {
if ((sc->streamType == GF_STREAM_SCENE) && (sc->objectType != GPAC_OTI_SCENE_DIMS))
break;
}
if (!sc) return NULL;
gf_odf_desc_copy((GF_Descriptor *)seng->ctx->root_od, &out_iod);
return out_iod;
}
#endif