Coverage Report

Created: 2024-09-08 06:18

/src/gpac/src/filters/compose.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Jean Le Feuvre
5
 *      Copyright (c) Telecom ParisTech 2017-2023
6
 *          All rights reserved
7
 *
8
 *  This file is part of GPAC / compositor filter
9
 *
10
 *  GPAC is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU Lesser General Public License as published by
12
 *  the Free Software Foundation; either version 2, or (at your option)
13
 *  any later version.
14
 *
15
 *  GPAC is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU Lesser General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU Lesser General Public
21
 *  License along with this library; see the file COPYING.  If not, write to
22
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23
 *
24
 */
25
26
#include <gpac/filters.h>
27
#include <gpac/config_file.h>
28
#include <gpac/internal/compositor_dev.h>
29
//to set caps in filter session, to cleanup!
30
#include "../filter_core/filter_session.h"
31
32
#ifndef GPAC_DISABLE_COMPOSITOR
33
34
GF_Err compose_bifs_dec_config_input(GF_Scene *scene, GF_FilterPid *pid, u32 oti, Bool is_remove);
35
GF_Err compose_bifs_dec_process(GF_Scene *scene, GF_FilterPid *pid);
36
37
GF_Err compose_odf_dec_config_input(GF_Scene *scene, GF_FilterPid *pid, u32 oti, Bool is_remove);
38
GF_Err compose_odf_dec_process(GF_Scene *scene, GF_FilterPid *pid);
39
40
41
static GF_Err compose_process(GF_Filter *filter)
42
0
{
43
0
  u32 i, nb_sys_streams_active;
44
0
  s32 ms_until_next = 0;
45
0
  Bool ret;
46
0
  GF_Compositor *ctx = (GF_Compositor *) gf_filter_get_udta(filter);
47
0
  if (!ctx) return GF_BAD_PARAM;
48
  //if acting as a source, we may not have a vout setup yet if we have not traversed the graph
49
0
  if (!ctx->vout && !ctx->src) {
50
0
    return GF_OK;
51
0
  }
52
0
  if (ctx->check_eos_state == 2)
53
0
    return GF_EOS;
54
55
  /*need to reload*/
56
0
  if (ctx->reload_state == 1) {
57
0
    ctx->reload_state = 0;
58
0
    gf_sc_disconnect(ctx);
59
0
    ctx->reload_state = 2;
60
0
  }
61
0
  if (ctx->reload_state == 2) {
62
0
    if (!ctx->root_scene) {
63
0
      ctx->reload_state = 0;
64
0
      if (ctx->reload_url) {
65
0
        gf_sc_connect_from_time(ctx, ctx->reload_url, 0, 0, 0, NULL);
66
0
        gf_free(ctx->reload_url);
67
0
        ctx->reload_url = NULL;
68
0
      }
69
0
    }
70
0
  }
71
72
0
  ctx->last_error = GF_OK;
73
0
  if (ctx->reload_config) {
74
0
    ctx->reload_config = GF_FALSE;
75
0
    gf_sc_reload_config(ctx);
76
0
  }
77
78
0
  nb_sys_streams_active = gf_list_count(ctx->systems_pids);
79
0
  for (i=0; i<nb_sys_streams_active; i++) {
80
0
    GF_FilterPacket *pck;
81
0
    GF_Err e;
82
0
    GF_FilterPid *pid = gf_list_get(ctx->systems_pids, i);
83
0
    GF_ObjectManager *odm = gf_filter_pid_get_udta(pid);
84
85
0
    assert (odm);
86
87
0
    e = GF_OK;
88
0
    pck = gf_filter_pid_get_packet(pid);
89
0
    if (!pck && gf_filter_pid_is_eos(pid)) {
90
0
      e = GF_EOS;
91
0
    }
92
0
    if (pck)
93
0
      gf_filter_pid_drop_packet(pid);
94
95
96
0
    if (e==GF_EOS) {
97
0
      gf_list_rem(ctx->systems_pids, i);
98
0
      i--;
99
0
      nb_sys_streams_active--;
100
0
      gf_odm_on_eos(odm, pid);
101
0
    }
102
0
    if (ctx->reload_scene_size) {
103
0
      u32 w, h;
104
0
      gf_sg_get_scene_size_info(ctx->root_scene->graph, &w, &h);
105
0
      if ((ctx->scene_width!=w) || (ctx->scene_height!=h)) {
106
0
        gf_sc_set_scene_size(ctx, w, h, GF_TRUE);
107
0
      }
108
0
    }
109
0
  }
110
111
0
  ret = gf_sc_draw_frame(ctx, GF_FALSE, &ms_until_next);
112
113
0
  if (!ctx->player) {
114
0
    Bool forced_eos = GF_FALSE;
115
0
    Bool was_over = GF_FALSE;
116
    /*remember to check for eos*/
117
0
    if (ctx->dur<0) {
118
0
      if (ctx->frame_number >= (u32) -ctx->dur) {
119
0
        ctx->check_eos_state = 2;
120
0
        gf_filter_abort(filter);
121
0
      }
122
0
    } else if (ctx->dur>0) {
123
0
      Double n = ctx->scene_sampled_clock;
124
0
      n /= 1000;
125
0
      if (n>=ctx->dur) {
126
0
        ctx->check_eos_state = 2;
127
0
        gf_filter_abort(filter);
128
0
      } else if (!ret && ctx->vfr && !ctx->check_eos_state && !nb_sys_streams_active && ctx->scene_sampled_clock && !ctx->validator_mode) {
129
0
        ctx->check_eos_state = 1;
130
0
        ctx->last_check_pass = 0;
131
0
        if (!ctx->validator_mode)
132
0
          ctx->force_next_frame_redraw = GF_TRUE;
133
0
      }
134
0
    } else if (!ret && !ctx->frame_was_produced && !ctx->audio_frames_sent && !ctx->check_eos_state && !nb_sys_streams_active && !ctx->event_pending) {
135
0
      ctx->check_eos_state = 1;
136
0
      was_over = GF_TRUE;
137
0
    } else if (ctx->sys_frames_pending) {
138
0
      ctx->check_eos_state = 0;
139
0
    } else if (gf_filter_end_of_session(filter)) {
140
0
      ctx->check_eos_state = 2;
141
0
      gf_filter_abort(filter);
142
0
    }
143
144
0
    if (ctx->timeout && (ctx->check_eos_state == 1) && !gf_filter_connections_pending(filter)) {
145
0
      u32 now = gf_sys_clock();
146
0
      if (!ctx->last_check_pass)
147
0
        ctx->last_check_pass = now;
148
149
0
      if (now - ctx->last_check_pass > ctx->timeout) {
150
0
        ctx->check_eos_state = 2;
151
0
        if (!gf_filter_end_of_session(filter)) {
152
0
          GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[Compositor] Could not detect end of stream(s) in the %d ms, aborting\n", ctx->timeout));
153
0
          forced_eos = GF_TRUE;
154
0
        }
155
0
        gf_filter_abort(filter);
156
0
      }
157
0
    } else {
158
0
      ctx->last_check_pass = 0;
159
0
    }
160
161
0
    if ((ctx->check_eos_state==2) || (ctx->check_eos_state && gf_sc_check_end_of_scene(ctx, GF_TRUE))) {
162
0
      u32 count;
163
0
      ctx->force_next_frame_redraw = GF_FALSE;
164
0
      count = gf_filter_get_ipid_count(ctx->filter);
165
0
      if (ctx->root_scene) {
166
0
        gf_filter_pid_set_eos(ctx->vout);
167
0
        if (ctx->audio_renderer && ctx->audio_renderer->aout)
168
0
          gf_filter_pid_set_eos(ctx->audio_renderer->aout);
169
0
      }
170
      //send stop
171
0
      if (ctx->dur) {
172
0
        for (i=0; i<count; i++) {
173
0
          GF_FilterPid *pid = gf_filter_get_ipid(ctx->filter, i);
174
0
          if (!gf_filter_pid_is_eos(pid)) {
175
0
            GF_FilterEvent evt;
176
0
            GF_FEVT_INIT(evt, GF_FEVT_PLAY, pid);
177
0
            gf_filter_pid_send_event(pid, &evt);
178
0
            GF_FEVT_INIT(evt, GF_FEVT_STOP, pid);
179
0
            gf_filter_pid_send_event(pid, &evt);
180
            //and discard every incoming packet
181
0
            gf_filter_pid_set_discard(pid, GF_TRUE);
182
0
          }
183
0
        }
184
0
      }
185
0
      return forced_eos ? GF_SERVICE_ERROR : GF_EOS;
186
0
    }
187
0
    ctx->check_eos_state = was_over ? 1 : 0;
188
    //always repost a process task since we maye have things to draw even though no new input
189
0
    gf_filter_post_process_task(filter);
190
0
    return ctx->last_error;
191
0
  }
192
193
  //player mode
194
  
195
  //quit event seen or session is ending, do not flush, just abort and return last error
196
0
  if (ctx->check_eos_state || gf_filter_end_of_session(filter)) {
197
0
    gf_filter_abort(filter);
198
0
    return ctx->last_error ? ctx->last_error : GF_EOS;
199
0
  }
200
201
202
  //to clean up,depending on whether we use a thread to poll user inputs, etc...
203
0
  if ((u32) ms_until_next > 100)
204
0
    ms_until_next = 100;
205
206
  //ask for real-time reschedule
207
0
  gf_filter_ask_rt_reschedule(filter, ms_until_next ? ms_until_next*1000 : 1);
208
209
0
  return ctx->last_error;
210
0
}
211
212
static void merge_properties(GF_Compositor *ctx, GF_FilterPid *pid, u32 mtype, GF_Scene *parent_scene)
213
0
{
214
0
  const GF_PropertyValue *p;
215
0
  if (!ctx->vout) return;
216
217
0
  p = gf_filter_pid_get_property(pid, GF_PROP_PID_URL);
218
0
  if (!p) return;
219
220
0
  if (mtype==GF_STREAM_SCENE) {
221
0
    if (!parent_scene || !parent_scene->is_dynamic_scene) {
222
0
      gf_filter_pid_set_property(ctx->vout, GF_PROP_PID_URL, p);
223
0
    }
224
0
  } else if (parent_scene && parent_scene->is_dynamic_scene) {
225
0
    if (mtype==GF_STREAM_VISUAL)
226
0
      gf_filter_pid_set_property(ctx->vout, GF_PROP_PID_URL, p);
227
0
  }
228
0
}
229
230
static void compositor_setup_vout(GF_Compositor *ctx)
231
0
{
232
  //declare video output pid
233
0
  GF_FilterPid *pid;
234
0
  pid = ctx->vout = gf_filter_pid_new(ctx->filter);
235
0
  gf_filter_pid_set_name(pid, "vout");
236
  //compositor initiated for RT playback, vout pid may not be connected
237
0
  if (ctx->player)
238
0
    gf_filter_pid_set_loose_connect(pid);
239
240
0
  gf_filter_pid_set_property(pid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_RAW) );
241
0
  gf_filter_pid_set_property(pid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_VISUAL) );
242
0
  if (ctx->timescale)
243
0
    gf_filter_pid_set_property(pid, GF_PROP_PID_TIMESCALE, &PROP_UINT(ctx->timescale) );
244
0
  else
245
0
    gf_filter_pid_set_property(pid, GF_PROP_PID_TIMESCALE, &PROP_UINT(ctx->fps.num) );
246
247
0
  gf_filter_pid_set_property(pid, GF_PROP_PID_PIXFMT, &PROP_UINT(ctx->opfmt ? ctx->opfmt : GF_PIXEL_RGB) );
248
0
  gf_filter_pid_set_property(pid, GF_PROP_PID_WIDTH, &PROP_UINT(ctx->output_width) );
249
0
  gf_filter_pid_set_property(pid, GF_PROP_PID_HEIGHT, &PROP_UINT(ctx->output_height) );
250
251
0
  gf_filter_pid_set_property(pid, GF_PROP_PID_FPS, &PROP_FRAC(ctx->fps) );
252
0
  gf_filter_pid_set_property(pid, GF_PROP_PID_DELAY, NULL);
253
0
}
254
255
static GF_Err compose_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
256
0
{
257
0
  GF_ObjectManager *odm;
258
0
  const GF_PropertyValue *prop;
259
0
  u32 mtype, codecid;
260
0
  u32 i, count;
261
0
  GF_Scene *def_scene = NULL;
262
0
  GF_Scene *scene = NULL;
263
0
  GF_Scene *top_scene = NULL;
264
0
  GF_Compositor *ctx = (GF_Compositor *) gf_filter_get_udta(filter);
265
0
  GF_FilterEvent evt;
266
0
  Bool in_iod = GF_FALSE;
267
0
  Bool was_dyn_scene = GF_FALSE;
268
0
  if (is_remove) {
269
0
    u32 ID=0;
270
0
    odm = gf_filter_pid_get_udta(pid);
271
    //already disconnected
272
0
    if (!odm) return GF_OK;
273
0
    ID = odm->ID;
274
0
    scene = odm->parentscene;
275
0
    if (scene && !scene->is_dynamic_scene)
276
0
      scene = NULL;
277
    //destroy the object
278
0
    gf_odm_disconnect(odm, 2);
279
0
    if (scene) {
280
0
      if (scene->visual_url.OD_ID == ID) {
281
0
        scene->visual_url.OD_ID = 0;
282
0
        gf_scene_regenerate(scene);
283
0
      } else if (scene->audio_url.OD_ID == ID) {
284
0
        scene->audio_url.OD_ID = 0;
285
0
        gf_scene_regenerate(scene);
286
0
      } else if (scene->text_url.OD_ID == ID) {
287
0
        scene->text_url.OD_ID = 0;
288
0
        gf_scene_regenerate(scene);
289
0
      } else if (scene->dims_url.OD_ID == ID) {
290
0
        scene->dims_url.OD_ID = 0;
291
0
        gf_scene_regenerate(scene);
292
0
      } else if (scene->subs_url.OD_ID == ID) {
293
0
        scene->subs_url.OD_ID = 0;
294
0
        gf_scene_regenerate(scene);
295
0
      }
296
0
    }
297
0
    return GF_OK;
298
0
  }
299
300
0
  if (!ctx->vout) {
301
0
    compositor_setup_vout(ctx);
302
0
  }
303
304
0
  prop = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
305
0
  if (!prop) return GF_NOT_SUPPORTED;
306
0
  mtype = prop->value.uint;
307
308
0
  prop = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
309
0
  if (!prop) return GF_NOT_SUPPORTED;
310
0
  codecid = prop->value.uint;
311
312
0
  odm = gf_filter_pid_get_udta(pid);
313
314
  //in filter mode, check we can handle creating a canvas from input video format. If not, negotiate a supported format
315
0
  if (!ctx->player) {
316
0
    prop = gf_filter_pid_get_property(pid, GF_PROP_PID_PIXFMT);
317
0
    if (prop && (!odm || (odm->mo && (odm->mo->pixelformat != prop->value.uint)))) {
318
0
      GF_EVGSurface *test_c = gf_evg_surface_new(GF_FALSE);
319
0
      GF_Err e = gf_evg_surface_attach_to_buffer(test_c, (u8 *) prop, 48, 48, 0, 0, prop->value.uint);
320
0
      gf_evg_surface_delete(test_c);
321
0
      if (e) {
322
0
        u32 new_fmt;
323
0
        Bool transparent = gf_pixel_fmt_is_transparent(prop->value.uint);
324
0
        if (gf_pixel_fmt_is_yuv(prop->value.uint)) {
325
0
          if (!transparent && gf_pixel_is_wide_depth(prop->value.uint)>8) {
326
0
            new_fmt = GF_PIXEL_YUV444_10;
327
0
          } else {
328
0
            new_fmt = transparent ? GF_PIXEL_YUVA444_PACK : GF_PIXEL_YUV444_PACK;
329
0
          }
330
0
        } else {
331
0
          new_fmt = transparent ? GF_PIXEL_RGBA : GF_PIXEL_RGB;
332
0
        }
333
0
        gf_filter_pid_negotiate_property(pid, GF_PROP_PID_PIXFMT, &PROP_UINT(new_fmt) );
334
0
        return GF_OK;
335
0
      }
336
0
    }
337
0
  }
338
339
0
  if (odm) {
340
0
    Bool notify_quality = GF_FALSE;
341
342
0
    if (gf_filter_pid_is_sparse(pid)) {
343
0
      odm->flags |= GF_ODM_IS_SPARSE;
344
0
    } else {
345
0
      odm->flags &= ~GF_ODM_IS_SPARSE;
346
0
    }
347
348
0
    if (mtype==GF_STREAM_SCENE) { }
349
0
    else if (mtype==GF_STREAM_OD) { }
350
    //change of stream type for a given object, no use case yet
351
0
    else {
352
0
      if (odm->type != mtype)
353
0
        return GF_NOT_SUPPORTED;
354
0
      if (odm->mo) {
355
0
        odm->mo->config_changed = GF_TRUE;
356
0
        if (gf_filter_pid_get_property(pid, GF_PROP_PID_SRD_MAP))
357
0
          odm->mo->srd_map_changed  = GF_TRUE;
358
359
0
        gf_mo_update_caps_ex(odm->mo, GF_TRUE);
360
0
        if (odm->mo->config_changed && (odm->type == GF_STREAM_VISUAL) && odm->parentscene && odm->parentscene->is_dynamic_scene) {
361
0
          gf_scene_force_size_to_video(odm->parentscene, odm->mo);
362
0
        }
363
0
      }
364
0
      gf_odm_update_duration(odm, pid);
365
      //we can safely call this here since we are in reconfigure
366
0
      gf_odm_check_clock_mediatime(odm);
367
0
      notify_quality = GF_TRUE;
368
0
    }
369
0
    merge_properties(ctx, pid, mtype, odm->parentscene);
370
371
0
    if (notify_quality) {
372
0
      GF_Event gevt;
373
0
      memset(&gevt, 0, sizeof(GF_Event));
374
0
      gevt.type = GF_EVENT_QUALITY_SWITCHED;
375
0
      gf_filter_forward_gf_event(filter, &gevt, GF_FALSE, GF_FALSE);
376
0
    }
377
378
0
    return GF_OK;
379
0
  }
380
381
  //create a default scene
382
0
  if (!ctx->root_scene) {
383
0
    const char *service_url = "unknown";
384
0
    const GF_PropertyValue *p = gf_filter_pid_get_property(pid, GF_PROP_PID_URL);
385
0
    if (p) service_url = p->value.string;
386
    
387
0
    ctx->root_scene = gf_scene_new(ctx, NULL);
388
0
    ctx->root_scene->root_od = gf_odm_new();
389
0
    ctx->root_scene->root_od->scene_ns = gf_scene_ns_new(ctx->root_scene, ctx->root_scene->root_od, service_url, NULL);
390
0
    ctx->root_scene->root_od->subscene = ctx->root_scene;
391
0
    ctx->root_scene->root_od->scene_ns->nb_odm_users++;
392
0
    switch (mtype) {
393
0
    case GF_STREAM_SCENE:
394
0
    case GF_STREAM_PRIVATE_SCENE:
395
0
    case GF_STREAM_OD:
396
0
      ctx->root_scene->is_dynamic_scene = GF_FALSE;
397
0
      break;
398
0
    default:
399
0
      ctx->root_scene->is_dynamic_scene = GF_TRUE;
400
0
      break;
401
0
    }
402
403
0
    if (!ctx->root_scene->root_od->scene_ns->url_frag) {
404
0
      p = gf_filter_pid_get_property(pid, GF_PROP_PID_ORIG_FRAG_URL);
405
0
      if (p && p->value.string)
406
0
        ctx->root_scene->root_od->scene_ns->url_frag = gf_strdup(p->value.string);
407
0
    }
408
409
0
    if (!ctx->player)
410
0
      gf_filter_post_process_task(filter);
411
0
  }
412
413
  //default scene is root one
414
0
  scene = ctx->root_scene;
415
0
  top_scene = ctx->root_scene;
416
417
0
  switch (mtype) {
418
0
  case GF_STREAM_SCENE:
419
0
  case GF_STREAM_OD:
420
0
    prop = gf_filter_pid_get_property(pid, GF_PROP_PID_IN_IOD);
421
0
    if (prop && prop->value.boolean) {
422
0
      in_iod = GF_TRUE;
423
0
    }
424
0
    break;
425
0
  }
426
427
428
  //browse all scene namespaces and figure out our parent scene
429
0
  count = gf_list_count(top_scene->namespaces);
430
0
  for (i=0; i<count; i++) {
431
0
    GF_SceneNamespace *sns = gf_list_get(top_scene->namespaces, i);
432
0
    if (!sns->source_filter) {
433
0
      if (sns->connect_ack && sns->owner && !def_scene) {
434
0
        def_scene = sns->owner->subscene ? sns->owner->subscene : sns->owner->parentscene;
435
0
      }
436
0
      continue;
437
0
    }
438
0
    gf_fatal_assert(sns->owner);
439
0
    if (gf_filter_pid_is_filter_in_parents(pid, sns->source_filter)) {
440
0
      Bool scene_setup = GF_FALSE;
441
0
      if (!sns->owner->subscene && sns->owner->parentscene && (mtype!=GF_STREAM_OD) && (mtype!=GF_STREAM_SCENE)) {
442
0
        u32 j;
443
0
        for (j=0; j<gf_list_count(sns->owner->parentscene->scene_objects); j++) {
444
0
          GF_MediaObject *mo = gf_list_get(sns->owner->parentscene->scene_objects, j);
445
0
          if (mo->OD_ID == GF_MEDIA_EXTERNAL_ID) continue;
446
0
          if (mo->OD_ID != sns->owner->ID) continue;
447
448
0
          if (mo->type != GF_MEDIA_OBJECT_SCENE) continue;
449
          //this is a pid from a subservice (inline) inserted through OD commands, create the subscene
450
0
          sns->owner->subscene = gf_scene_new(NULL, sns->owner->parentscene);
451
0
          sns->owner->subscene->root_od = sns->owner;
452
          //scenes are by default dynamic
453
0
          sns->owner->subscene->is_dynamic_scene = GF_TRUE;
454
0
          sns->owner->mo = mo;
455
0
          mo->odm = sns->owner;
456
0
          break;
457
0
        }
458
0
      }
459
      //this is an animation stream
460
0
      if (!in_iod && ((mtype==GF_STREAM_OD) || (mtype==GF_STREAM_SCENE)) ) {
461
0
        scene_setup = GF_TRUE;
462
0
      }
463
      //otherwise if parent scene is setup and root object type is scene or OD, do not create an inline
464
      //inline nodes using od:// must trigger subscene creation by using gf_scene_get_media_object with object type GF_MEDIA_OBJECT_SCENE
465
0
      else if (sns->owner->parentscene
466
0
        && sns->owner->parentscene->root_od
467
0
        && sns->owner->parentscene->root_od->pid
468
0
        && ((sns->owner->parentscene->root_od->type==GF_STREAM_SCENE) || (sns->owner->parentscene->root_od->type==GF_STREAM_OD))
469
0
      ) {
470
0
        scene_setup = GF_TRUE;
471
0
      }
472
473
      //we are attaching an inline, create the subscene if not done already
474
0
      if (!scene_setup && !sns->owner->subscene && ((mtype==GF_STREAM_OD) || (mtype==GF_STREAM_SCENE))  ) {
475
        //ignore system PIDs from subservice - this is typically the case when playing a bt/xmt file
476
        //created from a container (mp4) and still referring to that container for the media streams
477
0
        if (sns->owner->ignore_sys) {
478
0
          GF_FEVT_INIT(evt, GF_FEVT_PLAY, pid);
479
0
          gf_filter_pid_send_event(pid, &evt);
480
0
          GF_FEVT_INIT(evt, GF_FEVT_STOP, pid);
481
0
          gf_filter_pid_send_event(pid, &evt);
482
0
          return GF_OK;
483
0
        }
484
485
0
        gf_fatal_assert(sns->owner->parentscene);
486
0
        sns->owner->subscene = gf_scene_new(ctx, sns->owner->parentscene);
487
0
        sns->owner->subscene->root_od = sns->owner;
488
0
      }
489
0
      scene = sns->owner->subscene ? sns->owner->subscene : sns->owner->parentscene;
490
0
      break;
491
0
    }
492
0
  }
493
0
  if (!scene) scene = def_scene;
494
0
  if (!scene) return GF_SERVICE_ERROR;
495
496
0
  GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Configuring PID %s\n", gf_stream_type_name(mtype)));
497
498
0
  was_dyn_scene = scene->is_dynamic_scene;
499
500
  //pure OCR streams are handled by dispatching OCR on the PID(s)
501
0
  if (codecid != GF_CODECID_RAW)
502
0
    return GF_NOT_SUPPORTED;
503
504
0
  switch (mtype) {
505
0
  case GF_STREAM_SCENE:
506
0
  case GF_STREAM_OD:
507
0
    if (in_iod) {
508
0
      scene->is_dynamic_scene = GF_FALSE;
509
0
    } else {
510
      //we have an MPEG-4 ESID defined for the PID, this is MPEG-4 systems
511
0
      prop = gf_filter_pid_get_property(pid, GF_PROP_PID_ESID);
512
0
      if (prop && scene->is_dynamic_scene) {
513
0
        scene->is_dynamic_scene = GF_FALSE;
514
0
      }
515
0
    }
516
0
    break;
517
0
  }
518
519
0
  if ((mtype==GF_STREAM_OD) && !in_iod) return GF_NOT_SUPPORTED;
520
521
  //we inserted a root scene (bt/svg/...) after a pid (passthrough mode), we need to create a new namespace for
522
  //the scene and reassign the old namespace to the previously created ODM
523
0
  if (scene->root_od && !scene->root_od->parentscene && was_dyn_scene && (was_dyn_scene != scene->is_dynamic_scene)) {
524
0
    GF_SceneNamespace *new_sns=NULL;
525
0
    const char *service_url = "unknown";
526
0
    const GF_PropertyValue *p = gf_filter_pid_get_property(pid, GF_PROP_PID_URL);
527
0
    if (p) service_url = p->value.string;
528
0
    new_sns = gf_scene_ns_new(ctx->root_scene, ctx->root_scene->root_od, service_url, NULL);
529
530
0
    for (i=0; i<gf_list_count(scene->resources); i++) {
531
0
      GF_ObjectManager *anodm = gf_list_get(scene->resources, i);
532
533
0
      if (new_sns && (anodm->scene_ns == scene->root_od->scene_ns) && (scene->root_od->scene_ns->owner==scene->root_od)) {
534
0
        scene->root_od->scene_ns->owner = anodm;
535
0
        break;
536
0
      }
537
0
    }
538
0
    scene->root_od->scene_ns = new_sns;
539
0
    gf_sc_set_scene(ctx, NULL);
540
0
    gf_sg_reset(scene->graph);
541
0
    gf_sc_set_scene(ctx, scene->graph);
542
    //do not reload scene size in GUI mode, let the gui decide
543
0
    if (ctx->player<2)
544
0
      ctx->reload_scene_size = GF_TRUE;
545
    //force clock to NULL, will resetup based on OCR_ES_IDs
546
0
    scene->root_od->ck = NULL;
547
0
  }
548
549
  //setup object (clock) and playback requests
550
0
  gf_scene_insert_pid(scene, scene->root_od->scene_ns, pid, in_iod);
551
552
0
  if (was_dyn_scene != scene->is_dynamic_scene) {
553
0
    for (i=0; i<gf_list_count(scene->resources); i++) {
554
0
      GF_ObjectManager *anodm = gf_list_get(scene->resources, i);
555
0
      if (anodm->mo)
556
0
        anodm->flags |= GF_ODM_PASSTHROUGH;
557
0
    }
558
0
  }
559
560
561
  //attach scene to input filters - may be true for dynamic scene (text rendering) and regular scenes
562
0
  if ((mtype==GF_STREAM_OD) || (mtype==GF_STREAM_SCENE) || (mtype==GF_STREAM_TEXT) ) {
563
0
    void gf_filter_pid_exec_event(GF_FilterPid *pid, GF_FilterEvent *evt);
564
565
0
    GF_FEVT_INIT(evt, GF_FEVT_ATTACH_SCENE, pid);
566
0
    evt.attach_scene.object_manager = gf_filter_pid_get_udta(pid);
567
0
    gf_filter_pid_exec_event(pid, &evt);
568
0
  }
569
  //scene is dynamic
570
0
  if (scene->is_dynamic_scene) {
571
0
    Bool reset = GF_FALSE;
572
0
    u32 scene_vr_type = 0;
573
0
    char *sep = scene->root_od->scene_ns->url_frag;
574
0
    if (sep && ( !strnicmp(sep, "LIVE360", 7) || !strnicmp(sep, "360", 3) || !strnicmp(sep, "VR", 2) ) ) {
575
0
      scene_vr_type = 1;
576
0
    }
577
0
    if (!sep) {
578
0
      prop = gf_filter_pid_get_property(pid, GF_PROP_PID_PROJECTION_TYPE);
579
0
      if (prop && (prop->value.uint==GF_PROJ360_EQR)) scene_vr_type = 1;
580
0
    }
581
0
    if (scene_vr_type) {
582
0
      prop = gf_filter_pid_get_property(pid, GF_PROP_PID_SRD_MAP);
583
0
      if (prop) scene_vr_type = 2;
584
0
    }
585
0
    if (scene_vr_type) {
586
0
      if (scene->vr_type != scene_vr_type) reset = GF_TRUE;
587
0
      scene->vr_type = scene_vr_type;
588
0
    }
589
0
    if (reset)
590
0
      gf_sg_reset(scene->graph);
591
592
0
    gf_scene_regenerate(scene);
593
594
0
    if (!ctx->player)
595
0
      gf_filter_pid_set_property_str(ctx->vout, "InteractiveScene", scene_vr_type ? &PROP_UINT(2) : NULL);
596
0
  }
597
0
  else if (!ctx->player)
598
0
    gf_filter_pid_set_property_str(ctx->vout, "InteractiveScene", &PROP_UINT(1));
599
600
0
  merge_properties(ctx, pid, mtype, scene);
601
0
  return GF_OK;
602
0
}
603
604
#include "../compositor/visual_manager.h"
605
606
static GF_Err compose_reconfig_output(GF_Filter *filter, GF_FilterPid *pid)
607
0
{
608
0
  const GF_PropertyValue *p;
609
0
  u32 sr, o_fmt, nb_ch, afmt;
610
0
  u64 cfg;
611
0
  Bool needs_reconfigure = GF_FALSE;
612
0
  GF_Compositor *ctx = (GF_Compositor *) gf_filter_get_udta(filter);
613
614
0
  if (ctx->vout == pid) {
615
0
    u32 w, h;
616
0
    p = gf_filter_pid_caps_query(pid, GF_PROP_PID_PIXFMT);
617
0
    if (p) {
618
0
      u32 stride;
619
#ifndef GPAC_DISABLE_3D
620
      if (ctx->scene && (ctx->hybrid_opengl || ctx->visual->type_3d)) {
621
        switch (p->value.uint) {
622
        case GF_PIXEL_RGBA:
623
        case GF_PIXEL_RGB:
624
          break;
625
        default:
626
          return GF_NOT_SUPPORTED;
627
        }
628
      }
629
#endif
630
0
      if (ctx->opfmt != p->value.uint) {
631
0
        ctx->opfmt = p->value.uint;
632
0
        gf_filter_pid_set_property(ctx->vout, GF_PROP_PID_PIXFMT, &PROP_UINT(ctx->opfmt) );
633
0
        gf_pixel_get_size_info(ctx->opfmt, ctx->display_width, ctx->display_height, NULL, &stride, NULL, NULL, NULL);
634
0
        gf_filter_pid_set_property(ctx->vout, GF_PROP_PID_STRIDE, &PROP_UINT(stride) );
635
0
        if (!ctx->player) {
636
0
          ctx->new_width = ctx->display_width;
637
0
          ctx->new_height = ctx->display_height;
638
0
          ctx->msg_type |= GF_SR_CFG_INITIAL_RESIZE;
639
0
        }
640
0
      }
641
0
    }
642
    
643
0
    w = h = 0;
644
0
    p = gf_filter_pid_caps_query(pid, GF_PROP_PID_WIDTH);
645
0
    if (p) w = p->value.uint;
646
0
    p = gf_filter_pid_caps_query(pid, GF_PROP_PID_HEIGHT);
647
0
    if (p) h = p->value.uint;
648
649
0
    if (w && h) {
650
0
      ctx->osize.x = w;
651
0
      ctx->osize.y = h;
652
/*      gf_filter_pid_set_property(ctx->vout, GF_PROP_PID_WIDTH, &PROP_UINT(w) );
653
      gf_filter_pid_set_property(ctx->vout, GF_PROP_PID_HEIGHT, &PROP_UINT(h) );
654
0
*/    }
655
0
    return GF_OK;
656
0
  }
657
658
0
  if (ctx->audio_renderer->aout == pid) {
659
660
0
    gf_mixer_get_config(ctx->audio_renderer->mixer, &sr, &nb_ch, &o_fmt, &cfg);
661
0
    p = gf_filter_pid_caps_query(pid, GF_PROP_PID_SAMPLE_RATE);
662
0
    if (p && (p->value.uint != sr)) {
663
0
      sr = p->value.uint;
664
0
      needs_reconfigure = GF_TRUE;
665
0
    }
666
0
    p = gf_filter_pid_caps_query(pid, GF_PROP_PID_NUM_CHANNELS);
667
0
    if (p && (p->value.uint != nb_ch)) {
668
0
      nb_ch = p->value.uint;
669
0
      needs_reconfigure = GF_TRUE;
670
0
    }
671
0
    p = gf_filter_pid_caps_query(pid, GF_PROP_PID_AUDIO_FORMAT);
672
0
    if (p) afmt = p->value.uint;
673
0
    else afmt = GF_AUDIO_FMT_S16;
674
675
0
    if (o_fmt != afmt) {
676
0
      needs_reconfigure = GF_TRUE;
677
0
    }
678
0
    if (!needs_reconfigure) return GF_OK;
679
680
0
    GF_LOG(GF_LOG_INFO, GF_LOG_AUDIO, ("[Compositor] Audio output caps negotiated to %d Hz %d channels %s \n", sr, nb_ch, gf_audio_fmt_name(afmt) ));
681
0
    gf_mixer_set_config(ctx->audio_renderer->mixer, sr, nb_ch, afmt, 0);
682
0
    ctx->audio_renderer->need_reconfig = GF_TRUE;
683
0
    return GF_OK;
684
0
  }
685
0
  return GF_NOT_SUPPORTED;
686
0
}
687
688
static Bool compose_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
689
0
{
690
0
  switch (evt->base.type) {
691
  //event(s) we trigger on ourselves to go up the filter chain
692
0
  case GF_FEVT_CAPS_CHANGE:
693
0
    return GF_FALSE;
694
0
  case GF_FEVT_CONNECT_FAIL:
695
0
  {
696
0
    GF_Compositor *ctx = (GF_Compositor *) gf_filter_get_udta(filter);
697
0
    if (ctx->audio_renderer && (evt->base.on_pid == ctx->audio_renderer->aout))
698
0
      ctx->audio_renderer->non_rt_output = 0;
699
0
  }
700
0
    return GF_FALSE;
701
0
  case GF_FEVT_BUFFER_REQ:
702
0
    return GF_TRUE;
703
    
704
0
  case GF_FEVT_INFO_UPDATE:
705
0
  {
706
0
    u32 bps=0;
707
0
    u64 tot_size=0, down_size=0;
708
0
    GF_PropertyEntry *pe=NULL;
709
0
    GF_PropertyValue *p = (GF_PropertyValue *) gf_filter_pid_get_info(evt->base.on_pid, GF_PROP_PID_TIMESHIFT_STATE, &pe);
710
0
    if (p && p->value.uint) {
711
0
      GF_Event an_evt;
712
0
      memset(&an_evt, 0, sizeof(GF_Event));
713
0
      GF_Compositor *ctx = (GF_Compositor *) gf_filter_get_udta(filter);
714
0
      if (p->value.uint==1) {
715
0
        an_evt.type = GF_EVENT_TIMESHIFT_UNDERRUN;
716
0
        gf_sc_send_event(ctx, &an_evt);
717
0
      } else if (p->value.uint==2) {
718
0
        an_evt.type = GF_EVENT_TIMESHIFT_OVERFLOW;
719
0
        gf_sc_send_event(ctx, &an_evt);
720
0
      }
721
0
      p->value.uint = 0;
722
0
    }
723
724
0
    p = (GF_PropertyValue *) gf_filter_pid_get_info(evt->base.on_pid, GF_PROP_PID_DOWN_RATE, &pe);
725
0
    if (p) bps = p->value.uint;
726
0
    p = (GF_PropertyValue *) gf_filter_pid_get_info(evt->base.on_pid, GF_PROP_PID_DOWN_SIZE, &pe);
727
0
    if (p) tot_size = p->value.longuint;
728
729
0
    p = (GF_PropertyValue *) gf_filter_pid_get_info(evt->base.on_pid, GF_PROP_PID_DOWN_BYTES, &pe);
730
0
    if (p) down_size = p->value.longuint;
731
732
0
    if (bps && down_size && tot_size)  {
733
0
      GF_ObjectManager *odm = gf_filter_pid_get_udta(evt->base.on_pid);
734
0
      if ((down_size!=odm->last_filesize_signaled) || (down_size != tot_size)) {
735
0
        odm->last_filesize_signaled = down_size;
736
0
        gf_odm_service_media_event_with_download(odm, GF_EVENT_MEDIA_PROGRESS, down_size, tot_size, bps/8, 0, 0);
737
0
      }
738
0
    }
739
0
    gf_filter_release_property(pe);
740
0
  }
741
0
    return GF_TRUE;
742
743
0
  case GF_FEVT_USER:
744
0
    return gf_sc_user_event(gf_filter_get_udta(filter), (GF_Event *) &evt->user_event.event);
745
746
  //handle play for non-player mode, dynamic scenes only
747
0
  case GF_FEVT_PLAY:
748
0
  {
749
0
    GF_Compositor *compositor = gf_filter_get_udta(filter);
750
0
    s32 diff = (s32) (evt->play.start_range*1000);
751
0
    diff -= (s32) gf_sc_get_time_in_ms(compositor);
752
0
    if (!compositor->player && compositor->root_scene->is_dynamic_scene && !evt->play.initial_broadcast_play
753
0
      && (abs(diff)>=1000)
754
0
    ) {
755
0
      gf_sc_play_from_time(compositor, (u64) (evt->play.start_range*1000), GF_FALSE);
756
0
    }
757
0
  }
758
0
    break;
759
  //handle stop for non-player mode, dynamic scenes only
760
0
  case GF_FEVT_STOP:
761
0
  {
762
0
    GF_Compositor *compositor = gf_filter_get_udta(filter);
763
0
    if (!compositor->player && !evt->play.initial_broadcast_play) {
764
0
      if (compositor->root_scene->is_dynamic_scene) {
765
0
        u32 i, count = gf_list_count(compositor->root_scene->resources);
766
0
        for (i=0; i<count; i++) {
767
0
          GF_ObjectManager *odm = gf_list_get(compositor->root_scene->resources, i);
768
0
          gf_odm_stop(odm, GF_TRUE);
769
0
        }
770
0
      } else {
771
0
        gf_odm_stop(compositor->root_scene->root_od, 1);
772
0
      }
773
0
    }
774
0
  }
775
0
    break;
776
777
0
  default:
778
0
    break;
779
0
  }
780
  //all events cancelled (play/stop/etc...)
781
0
  return GF_TRUE;
782
0
}
783
784
static GF_Err compose_update_arg(GF_Filter *filter, const char *arg_name, const GF_PropertyValue *arg_val)
785
0
{
786
0
  GF_Compositor *compositor = gf_filter_get_udta(filter);
787
0
  compositor->reload_config = GF_TRUE;
788
0
  return GF_OK;
789
0
}
790
791
static void compose_finalize(GF_Filter *filter)
792
0
{
793
0
  GF_Compositor *ctx = gf_filter_get_udta(filter);
794
795
0
  if (ctx) {
796
0
    gf_sc_set_scene(ctx, NULL);
797
0
    if (ctx->root_scene) {
798
0
      gf_odm_disconnect(ctx->root_scene->root_od, GF_TRUE);
799
0
    }
800
0
    gf_sc_unload(ctx);
801
0
  }
802
0
}
803
void compositor_setup_aout(GF_Compositor *ctx)
804
0
{
805
0
  if (!ctx->noaudio && ctx->audio_renderer && !ctx->audio_renderer->aout) {
806
0
    GF_FilterPid *pid = ctx->audio_renderer->aout = gf_filter_pid_new(ctx->filter);
807
0
    gf_filter_pid_set_udta(pid, ctx);
808
0
    gf_filter_pid_set_name(pid, "aout");
809
0
    gf_filter_pid_set_property(pid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_AUDIO) );
810
0
    gf_filter_pid_set_property(pid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_RAW) );
811
0
    gf_filter_pid_set_property(pid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT(GF_AUDIO_FMT_S16) );
812
0
    gf_filter_pid_set_property(pid, GF_PROP_PID_TIMESCALE, &PROP_UINT(44100) );
813
0
    gf_filter_pid_set_property(pid, GF_PROP_PID_SAMPLE_RATE, &PROP_UINT(44100) );
814
0
    gf_filter_pid_set_property(pid, GF_PROP_PID_NUM_CHANNELS, &PROP_UINT(2) );
815
0
    gf_filter_pid_set_max_buffer(ctx->audio_renderer->aout, 1000*ctx->abuf);
816
0
    gf_filter_pid_set_property(pid, GF_PROP_PID_DELAY, NULL);
817
0
    gf_filter_pid_set_loose_connect(pid);
818
0
  }
819
0
}
820
821
static GF_Err compose_initialize(GF_Filter *filter)
822
0
{
823
0
  GF_Err e;
824
0
  GF_FilterSessionCaps sess_caps;
825
0
  GF_Compositor *ctx = gf_filter_get_udta(filter);
826
827
0
  ctx->filter = filter;
828
829
0
  if (gf_filter_is_dynamic(filter)) {
830
0
    ctx->forced_alpha = GF_TRUE;
831
0
    ctx->vfr = GF_TRUE;
832
0
  } else if (ctx->bc && !GF_COL_A(ctx->bc)) {
833
0
    ctx->forced_alpha = GF_TRUE;
834
0
  } else if ((ctx->opfmt == GF_PIXEL_RGBA) || (ctx->opfmt == GF_PIXEL_ARGB) || (ctx->opfmt == GF_PIXEL_YUVA)) {
835
0
    ctx->forced_alpha = GF_TRUE;
836
0
  } else if (ctx->noback) {
837
0
    ctx->forced_alpha = GF_TRUE;
838
0
  }
839
0
  if (ctx->src)
840
0
    ctx->vfr = GF_TRUE;
841
842
  //playout buffer not greater than max buffer
843
0
  if (ctx->buffer > ctx->mbuffer)
844
0
    ctx->buffer = ctx->mbuffer;
845
846
  //rebuffer level not greater than playout buffer
847
0
  if (ctx->rbuffer >= ctx->buffer)
848
0
    ctx->rbuffer = 0;
849
850
851
0
    if (ctx->player) {
852
    //explicit disable of OpenGL
853
0
    if (ctx->drv==GF_SC_DRV_OFF)
854
0
      ctx->ogl = GF_SC_GLMODE_OFF;
855
856
0
    if (ctx->ogl == GF_SC_GLMODE_AUTO)
857
0
      ctx->ogl = GF_SC_GLMODE_HYBRID;
858
859
    //we operate video output directly and dispatch audio output, we need to disable blocking mode
860
    //otherwise we will only get called when audio output is not blocking, and we will likely missed video frames
861
0
    gf_filter_prevent_blocking(filter, GF_TRUE);
862
0
  }
863
864
0
    e = gf_sc_load(ctx);
865
0
  if (e) return e;
866
867
0
  gf_filter_get_session_caps(filter, &sess_caps);
868
869
0
  sess_caps.max_screen_width = ctx->video_out->max_screen_width;
870
0
  sess_caps.max_screen_height = ctx->video_out->max_screen_height;
871
0
  sess_caps.max_screen_bpp = ctx->video_out->max_screen_bpp;
872
873
0
  gf_filter_set_session_caps(filter, &sess_caps);
874
875
  //make filter sticky (no shutdown if all inputs removed)
876
0
  gf_filter_make_sticky(filter);
877
878
0
  if (ctx->player) {
879
880
    //load audio filter chain, declaring audio output pid first
881
0
    if (!ctx->noaudio) {
882
0
      GF_Filter *audio_out = gf_filter_load_filter(filter, "aout", &e);
883
0
      ctx->audio_renderer->non_rt_output = 0;
884
0
      if (!audio_out) {
885
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to load audio output filter (%s) - audio disabled\n", gf_error_to_string(e) ));
886
0
      }
887
//      else {
888
//        gf_filter_reconnect_output(filter);
889
//      }
890
0
    }
891
0
    compositor_setup_aout(ctx);
892
893
    //create vout right away
894
0
    compositor_setup_vout(ctx);
895
896
    //always request a process task since we don't depend on input packets arrival (animations, pure scene presentations)
897
0
    gf_filter_post_process_task(filter);
898
0
  }
899
  //if not player mode, wait for pid connection to create vout, otherwise pid linking could fail
900
901
  //for coverage
902
#ifdef GPAC_ENABLE_COVERAGE
903
  if (gf_sys_is_cov_mode()) {
904
    compose_update_arg(filter, NULL, NULL);
905
  }
906
#endif
907
908
0
  gf_filter_set_event_target(filter, GF_TRUE);
909
0
  if (ctx->player==2) {
910
0
    const char *gui_path = gf_opts_get_key("core", "startup-file");
911
0
    if (gui_path) {
912
0
      gf_sc_connect_from_time(ctx, gui_path, 0, 0, 0, NULL);
913
0
      if (ctx->src)
914
0
        gf_opts_set_key("temp", "gui_load_urls", ctx->src);
915
0
    }
916
0
  }
917
  //src set, connect it (whether player mode or not)
918
0
  else if (ctx->src) {
919
0
    gf_sc_connect_from_time(ctx, ctx->src, 0, 0, 0, NULL);
920
0
  }
921
922
0
  gf_opts_set_key("temp", "compositor", "yes");
923
0
  return GF_OK;
924
0
}
925
926
GF_FilterProbeScore compose_probe_url(const char *url, const char *mime)
927
9.00k
{
928
  //check all our builtin URL schemes
929
9.00k
  if (!strnicmp(url, "mosaic://", 9)) {
930
0
    return GF_FPROBE_FORCE;
931
0
  }
932
9.00k
  else if (!strnicmp(url, "views://", 8)) {
933
0
    return GF_FPROBE_FORCE;
934
0
  }
935
9.00k
  return GF_FPROBE_NOT_SUPPORTED;
936
9.00k
}
937
938
939
#define OFFS(_n)  #_n, offsetof(GF_Compositor, _n)
940
static GF_FilterArgs CompositorArgs[] =
941
{
942
  { OFFS(aa), "set anti-aliasing mode for raster graphics; whether the setting is applied or not depends on the graphics module or graphic card\n"
943
    "- none: no anti-aliasing\n"
944
    "- text: anti-aliasing for text only\n"
945
    "- all: complete anti-aliasing", GF_PROP_UINT, "all", "none|text|all", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
946
  { OFFS(hlfill), "set highlight fill color (ARGB)", GF_PROP_UINT, "0x0", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
947
  { OFFS(hlline), "set highlight stroke color (ARGB)", GF_PROP_UINT, "0xFF000000", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
948
  { OFFS(hllinew), "set highlight stroke width", GF_PROP_FLOAT, "1.0", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
949
  { OFFS(sz), "enable scalable zoom. When scalable zoom is enabled, resizing the output window will also recompute all vectorial objects. Otherwise only the final buffer is stretched", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
950
  { OFFS(bc), "default background color to use when displaying transparent images or video with no scene composition instructions", GF_PROP_UINT, "0", NULL, GF_FS_ARG_UPDATE},
951
  { OFFS(yuvhw), "enable YUV hardware for 2D blit", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
952
  { OFFS(blitp), "partial hardware blit. If not set, will force more redraw", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
953
  { OFFS(softblt), "enable software blit/stretch in 2D. If disabled, vector graphics rasterizer will always be used", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_EXPERT},
954
955
  { OFFS(stress), "enable stress mode of compositor (rebuild all vector graphics and texture states at each frame)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
956
  { OFFS(fast), "enable speed optimization - whether the setting is applied or not depends on the graphics module / graphic card", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE},
957
  { OFFS(bvol), "draw bounding volume of objects\n"
958
      "- no: disable bounding box\n"
959
      "- box: draws a rectangle (2D) or box (3D)\n"
960
      "- aabb: draws axis-aligned bounding-box tree (3D) or rectangle (2D)", GF_PROP_UINT, "no", "no|box|aabb", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
961
  { OFFS(textxt), "specify whether text shall be drawn to a texture and then rendered or directly rendered. Using textured text can improve text rendering in 3D and also improve text-on-video like content\n"
962
    "- default: use texturing for OpenGL rendering, no texture for 2D rasterizer\n"
963
    "- never: never uses text textures\n"
964
    "- always: always render text to texture before drawing"
965
    "", GF_PROP_UINT, "default", "default|never|always", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
966
  { OFFS(out8b), "convert 10-bit video to 8 bit texture before GPU upload", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
967
  { OFFS(drop), "drop late frame when drawing. If not set, frames are not dropped until a desynchronization of 1 second or more is observed", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE},
968
  { OFFS(sclock), "force synchronizing all streams on a single clock", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
969
  { OFFS(sgaze), "simulate gaze events through mouse", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
970
  { OFFS(ckey), "color key to use in windowless mode (0xFFRRGGBB). GPAC currently does not support true alpha blitting to desktop due to limitations in most windowing toolkit, it therefore uses color keying mechanism. The alpha part of the key is used for global transparency of the output, if supported", GF_PROP_UINT, "0", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
971
  { OFFS(timeout), "timeout in ms after which a source is considered dead (0 disable timeout)", GF_PROP_UINT, "10000", NULL, GF_FS_ARG_UPDATE},
972
  { OFFS(fps), "simulation frame rate when animation-only sources are played (ignored when video is present)", GF_PROP_FRACTION, "30/1", NULL, GF_FS_ARG_UPDATE},
973
  { OFFS(timescale), "timescale used for output packets when no input video PID. A value of 0 means fps numerator", GF_PROP_UINT, "0", NULL, GF_FS_ARG_UPDATE},
974
  { OFFS(autofps), "use video input fps for output, ignored in player mode. If no video or not set, uses [-fps]()", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
975
  { OFFS(vfr), "only emit frames when changes are detected. (always true in player mode and when filter is dynamically loaded)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
976
977
  { OFFS(dur), "duration of generation. Mostly used when no video input is present. Negative values mean number of frames, positive values duration in second, 0 stops as soon as all streams are done", GF_PROP_DOUBLE, "0", NULL, GF_FS_ARG_UPDATE},
978
  { OFFS(fsize), "force the scene to resize to the biggest bitmap available if no size info is given in the BIFS configuration", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
979
  { OFFS(mode2d), "specify whether immediate drawing should be used or not\n"
980
  "- immediate: the screen is completely redrawn at each frame (always on if pass-through mode is detected)\n"
981
  "- defer: object positioning is tracked from frame to frame and dirty rectangles info is collected in order to redraw the minimal amount of the screen buffer\n"
982
  "- debug: only renders changed areas, resetting other areas\n"
983
   "Whether the setting is applied or not depends on the graphics module and player mode", GF_PROP_UINT, "defer", "defer|immediate|debug", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
984
  { OFFS(amc), "audio multichannel support; if disabled always down-mix to stereo. Useful if the multichannel output does not work properly", GF_PROP_BOOL, "true", NULL, 0},
985
  { OFFS(asr), "force output sample rate (0 for auto)", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
986
  { OFFS(ach), "force output channels (0 for auto)", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
987
  { OFFS(alayout), "force output channel layout (0 for auto)", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
988
  { OFFS(afmt), "force output channel format (0 for auto)", GF_PROP_PCMFMT, "s16", NULL, GF_FS_ARG_HINT_ADVANCED},
989
  { OFFS(asize), "audio output packet size in samples", GF_PROP_UINT, "1024", NULL, GF_FS_ARG_HINT_EXPERT},
990
  { OFFS(abuf), "audio output buffer duration in ms - the audio renderer fills the output PID up to this value. A too low value will lower latency but can have real-time playback issues", GF_PROP_UINT,
991
#ifdef GPAC_CONFIG_ANDROID
992
    "200"
993
#else
994
    "100"
995
#endif
996
    , NULL, GF_FS_ARG_HINT_EXPERT},
997
  { OFFS(avol), "audio volume in percent", GF_PROP_UINT, "100", NULL, GF_FS_ARG_UPDATE},
998
  { OFFS(apan), "audio pan in percent, 50 is no pan", GF_PROP_UINT, "50", NULL, GF_FS_ARG_UPDATE},
999
  { OFFS(async), "audio resynchronization; if disabled, audio data is never dropped but may get out of sync", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1000
  { OFFS(max_aspeed), "silence audio if playback speed is greater than specified value", GF_PROP_DOUBLE, "2.0", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1001
  { OFFS(max_vspeed), "move to i-frame only decoding if playback speed is greater than specified value", GF_PROP_DOUBLE, "4.0", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1002
1003
  { OFFS(buffer), "playout buffer in ms (overridden by `BufferLength` property of input PID)", GF_PROP_UINT, "3000", NULL, GF_FS_ARG_UPDATE},
1004
  { OFFS(rbuffer), "rebuffer trigger in ms (overridden by `RebufferLength` property of input PID)", GF_PROP_UINT, "1000", NULL, GF_FS_ARG_UPDATE},
1005
  { OFFS(mbuffer), "max buffer in ms, must be greater than playout buffer (overridden by `BufferMaxOccupancy` property of input PID)", GF_PROP_UINT, "3000", NULL, GF_FS_ARG_UPDATE},
1006
  { OFFS(ntpsync), "ntp resync threshold in ms (drops frame if their NTP is more than the given threshold above local ntp), 0 disables ntp drop", GF_PROP_UINT, "0", NULL, GF_FS_ARG_UPDATE},
1007
1008
  { OFFS(nojs), "disable javascript", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1009
  { OFFS(noback), "ignore background nodes and viewport fill (useful when dumping to PNG)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1010
1011
#ifndef GPAC_DISABLE_3D
1012
  { OFFS(ogl), "specify 2D rendering mode\n"
1013
        "- auto: automatically decides between on, off and hybrid based on content\n"
1014
        "- off: disables OpenGL; 3D will not be rendered\n"
1015
        "- on: uses OpenGL for all graphics; this will involve polygon tesselation and 2D graphics will not look as nice as 2D mode\n"
1016
        "- hybrid: the compositor performs software drawing of 2D graphics with no textures (better quality) and uses OpenGL for all 2D objects with textures and 3D objects"
1017
        , GF_PROP_UINT, "auto", "auto|off|hybrid|on", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
1018
  { OFFS(pbo), "enable PixelBufferObjects to push YUV textures to GPU in OpenGL Mode. This may slightly increase the performances of the playback", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1019
  { OFFS(nav), "override the default navigation mode of MPEG-4/VRML (Walk) and X3D (Examine)\n"
1020
  "- none: disables navigation\n"
1021
  "- walk: 3D world walk\n"
1022
  "- fly: 3D world fly (no ground detection)\n"
1023
  "- pan: 2D/3D world zoom/pan\n"
1024
  "- game: 3D world game (mouse gives walk direction)\n"
1025
  "- slide: 2D/3D world slide\n"
1026
  "- exam: 2D/3D object examine\n"
1027
  "- orbit: 3D object orbit\n"
1028
  "- vr: 3D world VR (yaw/pitch/roll)"
1029
  "", GF_PROP_UINT, "none", "none|walk|fly|pan|game|slide|exam|orbit|vr", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
1030
  { OFFS(linegl), "indicate that outlining shall be done through OpenGL pen width rather than vectorial outlining", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1031
  { OFFS(epow2), "emulate power-of-2 textures for OpenGL (old hardware). Ignored if OpenGL rectangular texture extension is enabled\n"
1032
  "- yes: video texture is not resized but emulated with padding. This usually speeds up video mapping on shapes but disables texture transformations\n"
1033
  "- no: video is resized to a power of 2 texture when mapping to a shape", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1034
  { OFFS(paa), "indicate whether polygon antialiasing should be used in full antialiasing mode. If not set, only lines and points antialiasing are used", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1035
  { OFFS(bcull), "indicate whether backface culling shall be disable or not\n"
1036
        "- on: enables backface culling\n"
1037
        "- off: disables backface culling\n"
1038
        "- alpha: only enables backface culling for transparent meshes"
1039
    "", GF_PROP_UINT, "on", "off|on|alpha", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1040
  { OFFS(wire), "wireframe mode\n"
1041
  "- none: objects are drawn as solid\n"
1042
    "- only: objects are drawn as wireframe only\n"
1043
    "- solid: objects are drawn as solid and wireframe is then drawn", GF_PROP_UINT, "none", "none|only|solid", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
1044
  { OFFS(norms), "normal vector drawing for debug\n"
1045
  "- none: no normals drawn\n"
1046
  "- face: one normal per face drawn\n"
1047
  "- vertex: one normal per vertex drawn"
1048
  "", GF_PROP_UINT, "none", "none|face|vertex", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
1049
  { OFFS(rext), "use non power of two (rectangular) texture GL extension", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1050
  { OFFS(cull), "use aabb culling: large objects are rendered in multiple calls when not fully in viewport", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1051
  { OFFS(depth_gl_scale), "set depth scaler", GF_PROP_FLOAT, "100", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1052
  { OFFS(depth_gl_type), "set geometry type used to draw depth video\n"
1053
  "- none: no geometric conversion\n"
1054
  "- point: compute point cloud from pixel+depth\n"
1055
  "- strip: same as point but thins point set"
1056
  "", GF_PROP_UINT, "none", "none|point|strip", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1057
  { OFFS(nbviews), "number of views to use in stereo mode", GF_PROP_UINT, "0", NULL, GF_FS_ARG_UPDATE},
1058
  { OFFS(stereo), "stereo output type. If your graphic card does not support OpenGL shaders, only `top` and `side` modes will be available\n"
1059
    "- none: no stereo\n"
1060
    "- side: images are displayed side by side from left to right\n"
1061
    "- top: images are displayed from top (laft view) to bottom (right view)\n"
1062
    "- hmd: same as side except that view aspect ratio is not changed\n"
1063
    "- ana: standard color anaglyph (red for left view, green and blue for right view) is used (forces views=2)\n"
1064
    "- cols: images are interleaved by columns, left view on even columns and left view on odd columns (forces views=2)\n"
1065
    "- rows: images are interleaved by columns, left view on even rows and left view on odd rows (forces views=2)\n"
1066
    "- spv5: images are interleaved by for SpatialView 5 views display, fullscreen mode (forces views=5)\n"
1067
    "- alio8: images are interleaved by for Alioscopy 8 views displays, fullscreen mode (forces views=8)\n"
1068
    "- custom: images are interleaved according to the shader file indicated in [-mvshader](). The shader is exposed each view as uniform sampler2D gfViewX, where X is the view number starting from the left", GF_PROP_UINT, "none", "none|top|side|hmd|custom|cols|rows|ana|spv5|alio8", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1069
  { OFFS(mvshader), "file path to the custom multiview interleaving shader", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1070
  { OFFS(fpack), "default frame packing of input video\n"
1071
    "- none: no frame packing\n"
1072
    "- top: top bottom frame packing\n"
1073
    "- side: side by side packing"
1074
  "", GF_PROP_UINT, "none", "none|top|side", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1075
  { OFFS(camlay), "camera layout in multiview modes\n"
1076
    "- straight: camera is moved along a straight line, no rotation\n"
1077
    "- offaxis: off-axis projection is used\n"
1078
    "- linear: camera is moved along a straight line with rotation\n"
1079
    "- circular: camera is moved along a circle with rotation"
1080
  "", GF_PROP_UINT, "offaxis", "straight|offaxis|linear|circular", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
1081
  { OFFS(iod), "inter-ocular distance (eye separation) in cm (distance between the cameras). ", GF_PROP_FLOAT, "6.4", NULL, GF_FS_ARG_UPDATE},
1082
  { OFFS(rview), "reverse view order", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1083
  { OFFS(dbgpack), "view packed stereo video as single image (show all)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1084
1085
1086
  { OFFS(tvtn), "number of point sampling for tile visibility algorithm", GF_PROP_UINT, "30", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1087
  { OFFS(tvtt), "number of points above which the tile is considered visible", GF_PROP_UINT, "8", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1088
  { OFFS(tvtd), "debug tiles and full coverage SRD\n"
1089
    "- off: regular draw\n"
1090
    "- partial: only displaying partial tiles, not the full sphere video\n"
1091
    "- full: only display the full sphere video", GF_PROP_UINT, "off", "off|partial|full", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1092
  { OFFS(tvtf), "force all tiles to be considered visible, regardless of viewpoint", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1093
  { OFFS(fov), "default field of view for VR", GF_PROP_FLOAT, "1.570796326794897", NULL, GF_FS_ARG_UPDATE},
1094
  { OFFS(vertshader), "path to vertex shader file", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT },
1095
  { OFFS(fragshader), "path to fragment shader file", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT },
1096
#endif
1097
1098
#ifdef GF_SR_USE_DEPTH
1099
  { OFFS(autocal), "auto calibration of znear/zfar in depth rendering mode", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1100
  { OFFS(dispdepth), "display depth, negative value uses default screen height", GF_PROP_SINT, "-1", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1101
  { OFFS(dispdist), "distance in cm between the camera and the zero-disparity plane. There is currently no automatic calibration of depth in GPAC", GF_PROP_FLOAT, "50", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1102
#ifndef GPAC_DISABLE_3D
1103
  { OFFS(focdist), "distance of focus point", GF_PROP_FLOAT, "0", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1104
#endif
1105
#endif
1106
1107
#ifdef GF_SR_USE_VIDEO_CACHE
1108
  { OFFS(vcsize), "visual cache size when storing raster graphics to memory", GF_PROP_UINT, "0", "0,+I", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1109
  { OFFS(vcscale), "visual cache scale factor in percent when storing raster graphics to memory", GF_PROP_UINT, "100", "0,100", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1110
  { OFFS(vctol), "visual cache tolerance when storing raster graphics to memory. If the difference between the stored version scale and the target display scale is less than tolerance, the cache will be used, otherwise it will be recomputed", GF_PROP_UINT, "30", "0,100", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1111
#endif
1112
  { OFFS(osize), "force output size. If not set, size is derived from inputs", GF_PROP_VEC2I, "0x0", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1113
  { OFFS(dpi), "default dpi if not indicated by video output", GF_PROP_VEC2I, "96x96", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1114
  { OFFS(dbgpvr), "debug scene used by PVR addon", GF_PROP_FLOAT, "0", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
1115
  { OFFS(player), "set compositor in player mode\n"
1116
  "- no: regular mode\n"
1117
  "- base: player mode\n"
1118
  "- gui: player mode with GUI auto-start", GF_PROP_UINT, "no", "no|base|gui", GF_FS_ARG_HINT_EXPERT},
1119
  { OFFS(noaudio), "disable audio output", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
1120
  { OFFS(opfmt), "pixel format to use for output. Ignored in [-player]() mode", GF_PROP_PIXFMT, "none", NULL, GF_FS_ARG_HINT_EXPERT},
1121
  { OFFS(drv), "indicate if graphics driver should be used\n"
1122
        "- no: never loads a graphics driver, software blit is used, no 3D possible (in player mode, disables OpenGL)\n"
1123
        "- yes: always loads a graphics driver, output pixel format will be RGB (in player mode, same as `auto`)\n"
1124
        "- auto: decides based on the loaded content"
1125
      , GF_PROP_UINT, "auto", "no|yes|auto", GF_FS_ARG_HINT_EXPERT},
1126
  { OFFS(src), "URL of source content", GF_PROP_NAME, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
1127
1128
  { OFFS(gaze_x), "horizontal gaze coordinate (0=left, width=right)", GF_PROP_SINT, "0", NULL, GF_FS_ARG_HINT_EXPERT|GF_FS_ARG_UPDATE},
1129
  { OFFS(gaze_y), "vertical gaze coordinate (0=top, height=bottom)", GF_PROP_SINT, "0", NULL, GF_FS_ARG_HINT_EXPERT|GF_FS_ARG_UPDATE},
1130
  { OFFS(gazer_enabled), "enable gaze event dispatch", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT|GF_FS_ARG_UPDATE},
1131
1132
  { OFFS(subtx), "horizontal translation in pixels towards right for subtitles renderers", GF_PROP_SINT, "0", NULL, GF_FS_ARG_HINT_EXPERT|GF_FS_ARG_UPDATE},
1133
  { OFFS(subty), "vertical translation in pixels towards top for subtitles renderers", GF_PROP_SINT, "0", NULL, GF_FS_ARG_HINT_EXPERT|GF_FS_ARG_UPDATE},
1134
  { OFFS(subfs), "font size for subtitles renderers (0 means automatic)", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_EXPERT|GF_FS_ARG_UPDATE},
1135
  { OFFS(subd), "subtitle delay in milliseconds for subtitles renderers", GF_PROP_SINT, "0", NULL, GF_FS_ARG_HINT_EXPERT|GF_FS_ARG_UPDATE},
1136
  { OFFS(audd), "audio delay in milliseconds", GF_PROP_SINT, "0", NULL, GF_FS_ARG_HINT_EXPERT|GF_FS_ARG_UPDATE},
1137
  { OFFS(clipframe), "visual output is clipped to bounding rectangle", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
1138
  {0}
1139
};
1140
1141
static const GF_FilterCapability CompositorCaps[] =
1142
{
1143
  /*first cap bundle for explicitly loaded compositor: accepts audio and video as well as scene/od*/
1144
  CAP_UINT(GF_CAPS_INPUT|GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_STREAM_TYPE, GF_STREAM_SCENE),
1145
  CAP_UINT(GF_CAPS_INPUT|GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_STREAM_TYPE, GF_STREAM_OD),
1146
  CAP_UINT(GF_CAPS_INPUT|GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_STREAM_TYPE, GF_STREAM_TEXT),
1147
  CAP_UINT(GF_CAPS_INPUT_EXCLUDED|GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
1148
  CAP_UINT(GF_CAPS_INPUT_OUTPUT|GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
1149
  CAP_UINT(GF_CAPS_INPUT_OUTPUT|GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
1150
  CAP_UINT(GF_CAPS_INPUT_OUTPUT|GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_CODECID, GF_CODECID_RAW),
1151
  {0},
1152
  /*second cap bundle for dynmac loaded compositor: only accepts text/scene/od*/
1153
  CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_TEXT),
1154
  CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_SCENE),
1155
  CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_OD),
1156
  CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
1157
  CAP_UINT(GF_CAPS_INPUT_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
1158
};
1159
1160
1161
const GF_FilterRegister CompositorFilterRegister = {
1162
  .name = "compositor",
1163
  GF_FS_SET_DESCRIPTION("Compositor")
1164
  GF_FS_SET_HELP("The GPAC compositor allows mixing audio, video, text and graphics in a timed fashion.\n"
1165
  "The compositor operates either in media-client or filter-only mode.\n"
1166
  "\n"
1167
  "# Media-client mode\n"
1168
  "In this mode, the compositor acts as a pseudo-sink for the video side and creates its own output window.\n"
1169
  "The video frames are dispatched to the output video PID in the form of frame pointers requiring later GPU read if used.\n"
1170
  "The audio part acts as a regular filter, potentially mixing and resampling the audio inputs to generate its output.\n"
1171
  "User events are directly processed by the filter in this mode.\n"
1172
  "\n"
1173
  "# Filter mode\n"
1174
  "In this mode, the compositor acts as a regular filter generating frames based on the loaded scene.\n"
1175
  "It will generate its outputs based on the input video frames, and will process user event sent by consuming filter(s).\n"
1176
  "If no input video frames (e.g. pure BIFS / SVG / VRML), the filter will generate frames based on the [-fps](), at constant or variable frame rate.\n"
1177
  "It will stop generating frames as soon as all input streams are done, unless extended/reduced by [-dur]().\n"
1178
  "If audio streams are loaded, an audio output PID is created.\n"
1179
  "\n"
1180
  "The default output pixel format in filter mode is:\n"
1181
  "- `rgb` when the filter is explicitly loaded by the application\n"
1182
  "- `rgba` when the filter is loaded during a link resolution\n"
1183
  "This can be changed by assigning the [-opfmt]() option.\n"
1184
  "If either [-opfmt]() specifies alpha channel or [-bc]() is not 0 but has alpha=0, background creation in default scene will be skipped.\n"
1185
  "\n"
1186
  "In filter-only mode, the special URL `gpid://` is used to locate PIDs in the scene description, in order to design scenes independently from source media.\n"
1187
  "When such a PID is associated to a `Background2D` node in BIFS (no SVG mapping yet), the compositor operates in pass-through mode.\n"
1188
  "In this mode, only new input frames on the pass-through PID will generate new frames, and the scene clock matches the input packet time.\n"
1189
  "The output size and pixel format will be set to the input size and pixel format, unless specified otherwise in the filter options.\n"
1190
  "\n"
1191
  "If only 2D graphics are used and display driver is not forced, 2D rasterizer will happen in the output pixel format (including YUV pixel formats).\n"
1192
  "In this case, in-place processing (rasterizing over the input frame data) will be used whenever allowed by input data.\n"
1193
  "\n"
1194
  "If 3D graphics are used or display driver is forced, OpenGL will be used on offscreen surface and the output packet will be an OpenGL texture.\n"
1195
  "\n"
1196
  "# Specific URL syntaxes\n"
1197
  "The compositor accepts any URL type supported by GPAC. It also accepts the following schemes for URLs:\n"
1198
  "- views:// : creates an auto-stereo scene of N views from `views://v1::.::vN`\n"
1199
  "- mosaic:// : creates a mosaic of N views from `mosaic://v1::.::vN`\n"
1200
  "\n"
1201
  "For both syntaxes, `vN` can be any type of URL supported by GPAC.\n"
1202
  "For `views://` syntax, the number of rendered views is set by [-nbviews]():\n"
1203
  "- If the URL gives less views than rendered, the views will be repeated\n"
1204
  "- If the URL gives more views than rendered, the extra views will be ignored\n"
1205
  "\n"
1206
  "The compositor can act as a source filter when the [-src]() option is explicitly set, independently from the operating mode:\n"
1207
  "EX gpac compositor:src=source.mp4 vout\n"
1208
  "\n"
1209
  "The compositor can act as a source filter when the source url uses one of the compositor built-in protocol schemes:\n"
1210
  "EX gpac -i mosaic://URL1:URL2 vout\n"
1211
  "\n"
1212
  )
1213
  .private_size = sizeof(GF_Compositor),
1214
  .flags = GF_FS_REG_MAIN_THREAD,
1215
  .max_extra_pids = (u32) -1,
1216
  SETCAPS(CompositorCaps),
1217
  .args = CompositorArgs,
1218
  .initialize = compose_initialize,
1219
  .finalize = compose_finalize,
1220
  .process = compose_process,
1221
  .process_event = compose_process_event,
1222
  .configure_pid = compose_configure_pid,
1223
  .reconfigure_output = compose_reconfig_output,
1224
  .update_arg = compose_update_arg,
1225
  .probe_url = compose_probe_url,
1226
};
1227
1228
const GF_FilterRegister *compositor_register(GF_FilterSession *session)
1229
7.07k
{
1230
7.07k
  u32 i=0;
1231
7.07k
  u32 nb_args = sizeof(CompositorArgs) / sizeof(GF_FilterArgs) - 1;
1232
1233
460k
  for (i=0; i<nb_args; i++) {
1234
452k
    if (!strcmp(CompositorArgs[i].arg_name, "afmt")) {
1235
7.07k
      CompositorArgs[i].min_max_enum = gf_audio_fmt_all_names();
1236
7.07k
    }
1237
445k
    else if (!strcmp(CompositorArgs[i].arg_name, "opfmt")) {
1238
7.07k
      CompositorArgs[i].min_max_enum =  gf_pixel_fmt_all_names();
1239
7.07k
    }
1240
452k
  }
1241
7.07k
  return &CompositorFilterRegister;
1242
7.07k
}
1243
#else
1244
const GF_FilterRegister *compositor_register(GF_FilterSession *session)
1245
{
1246
  return NULL;
1247
}
1248
#endif // GPAC_DISABLE_COMPOSITOR
1249