LCOV - code coverage report
Current view: top level - filters - compose.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 335 396 84.6 %
Date: 2021-04-29 23:48:07 Functions: 11 12 91.7 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2017-2021
       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_PLAYER
      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             : #define COMPOSITOR_MAGIC        GF_4CC('c','o','m','p')
      41             : //a bit ugly, used by terminal (old APIs)
      42           7 : GF_Compositor *gf_sc_from_filter(GF_Filter *filter)
      43             : {
      44           7 :         GF_Compositor *ctx = (GF_Compositor *) gf_filter_get_udta(filter);
      45           7 :         if (ctx->magic != COMPOSITOR_MAGIC) return NULL;
      46           7 :         if (ctx->magic_ptr != ctx) return NULL;
      47             : 
      48           7 :         return ctx;
      49             : }
      50             : 
      51      131980 : static GF_Err compose_process(GF_Filter *filter)
      52             : {
      53             :         u32 i, nb_sys_streams_active;
      54      131980 :         s32 ms_until_next = 0;
      55             :         Bool ret;
      56      131980 :         GF_Compositor *ctx = (GF_Compositor *) gf_filter_get_udta(filter);
      57      131980 :         if (!ctx) return GF_BAD_PARAM;
      58             : 
      59      131980 :         if (ctx->check_eos_state == 2)
      60             :                 return GF_EOS;
      61             : 
      62      124704 :         ctx->last_error = GF_OK;
      63      124704 :         if (ctx->reload_config) {
      64         599 :                 ctx->reload_config = GF_FALSE;
      65         599 :                 gf_sc_reload_config(ctx);
      66             :         }
      67             : 
      68      124704 :         nb_sys_streams_active = gf_list_count(ctx->systems_pids);
      69      134252 :         for (i=0; i<nb_sys_streams_active; i++) {
      70             :                 GF_FilterPacket *pck;
      71             :                 GF_Err e;
      72        9548 :                 GF_FilterPid *pid = gf_list_get(ctx->systems_pids, i);
      73        9548 :                 GF_ObjectManager *odm = gf_filter_pid_get_udta(pid);
      74             : 
      75             :                 assert (odm);
      76             : 
      77             :                 e = GF_OK;
      78        9548 :                 pck = gf_filter_pid_get_packet(pid);
      79        9548 :                 if (!pck && gf_filter_pid_is_eos(pid)) {
      80             :                         e = GF_EOS;
      81             :                 }
      82        9548 :                 if (pck)
      83           0 :                         gf_filter_pid_drop_packet(pid);
      84             : 
      85             : 
      86        9548 :                 if (e==GF_EOS) {
      87         483 :                         gf_list_rem(ctx->systems_pids, i);
      88         483 :                         i--;
      89         483 :                         nb_sys_streams_active--;
      90         483 :                         gf_odm_on_eos(odm, pid);
      91             :                 }
      92        9548 :                 if (ctx->reload_scene_size) {
      93             :                         u32 w, h;
      94          93 :                         gf_sg_get_scene_size_info(ctx->root_scene->graph, &w, &h);
      95          93 :                         if ((ctx->scene_width!=w) || (ctx->scene_height!=h)) {
      96          62 :                                 gf_sc_set_scene_size(ctx, w, h, GF_TRUE);
      97             :                         }
      98             :                 }
      99             :         }
     100             : 
     101      124704 :         ret = gf_sc_draw_frame(ctx, GF_FALSE, &ms_until_next);
     102             : 
     103      124704 :         if (!ctx->player) {
     104             :                 Bool forced_eos = GF_FALSE;
     105             :                 /*remember to check for eos*/
     106      124340 :                 if (ctx->dur<0) {
     107           0 :                         if (ctx->frame_number >= (u32) -ctx->dur)
     108           0 :                                 ctx->check_eos_state = 2;
     109      124340 :                 } else if (ctx->dur>0) {
     110       59263 :                         Double n = ctx->scene_sampled_clock;
     111       59263 :                         n /= 1000;
     112       59263 :                         if (n>=ctx->dur)
     113         223 :                                 ctx->check_eos_state = 2;
     114       59040 :                         else if (!ret && ctx->vfr && !ctx->check_eos_state && !nb_sys_streams_active && ctx->scene_sampled_clock && !ctx->validator_mode) {
     115       14170 :                                 ctx->check_eos_state = 1;
     116             :                                 if (!ctx->validator_mode)
     117       14170 :                                         ctx->force_next_frame_redraw = GF_TRUE;
     118             :                         }
     119       65077 :                 } else if (!ret && !ctx->frame_was_produced && !ctx->check_eos_state && !nb_sys_streams_active) {
     120         447 :                         ctx->check_eos_state = 1;
     121             :                 }
     122      124340 :                 if (ctx->check_eos_state == 1) {
     123       14666 :                         ctx->last_check_pass++;
     124       14666 :                         if (ctx->last_check_pass > 10000) {
     125           0 :                                 ctx->check_eos_state = 2;
     126           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[Compositor] Could not detect end of stream(s) in %d render pass, aborting\n", ctx->last_check_pass));
     127             :                                 forced_eos = GF_TRUE;
     128             :                         }
     129             :                 } else {
     130      109674 :                         ctx->last_check_pass = 0;
     131             :                 }
     132             : 
     133      124340 :                 if ((ctx->check_eos_state==2) || (ctx->check_eos_state && gf_sc_check_end_of_scene(ctx, GF_TRUE))) {
     134             :                         u32 count;
     135         851 :                         ctx->force_next_frame_redraw = GF_FALSE;
     136         851 :                         count = gf_filter_get_ipid_count(ctx->filter);
     137         851 :                         if (ctx->root_scene) {
     138         599 :                                 gf_filter_pid_set_eos(ctx->vout);
     139         599 :                                 if (ctx->audio_renderer && ctx->audio_renderer->aout)
     140          15 :                                         gf_filter_pid_set_eos(ctx->audio_renderer->aout);
     141             :                         }
     142             :                         //send stop
     143         851 :                         if (ctx->dur) {
     144         528 :                                 for (i=0; i<count; i++) {
     145         528 :                                         GF_FilterPid *pid = gf_filter_get_ipid(ctx->filter, i);
     146         528 :                                         if (!gf_filter_pid_is_eos(pid)) {
     147             :                                                 GF_FilterEvent evt;
     148          72 :                                                 GF_FEVT_INIT(evt, GF_FEVT_PLAY, pid);
     149          72 :                                                 gf_filter_pid_send_event(pid, &evt);
     150          72 :                                                 GF_FEVT_INIT(evt, GF_FEVT_STOP, pid);
     151          72 :                                                 gf_filter_pid_send_event(pid, &evt);
     152             :                                                 //and discard every incoming packet
     153          72 :                                                 gf_filter_pid_set_discard(pid, GF_TRUE);
     154             :                                         }
     155             :                                 }
     156             :                         }
     157         851 :                         return forced_eos ? GF_SERVICE_ERROR : GF_EOS;
     158             :                 }
     159      123489 :                 ctx->check_eos_state = 0;
     160             :                 //always repost a process task since we maye have things to draw even though no new input
     161      123489 :                 gf_filter_post_process_task(filter);
     162      123489 :                 return ctx->last_error;
     163             :         }
     164             : 
     165             : 
     166             :         //to clean up,depending on whether we use a thread to poll user inputs, etc...
     167         364 :         if ((u32) ms_until_next > 100)
     168           0 :                 ms_until_next = 100;
     169             : 
     170             :         //ask for real-time reschedule
     171         364 :         gf_filter_ask_rt_reschedule(filter, ms_until_next ? ms_until_next*1000 : 1);
     172             : 
     173         364 :         return ctx->last_error;
     174             : }
     175             : 
     176        1096 : static void merge_properties(GF_Compositor *ctx, GF_FilterPid *pid, u32 mtype, GF_Scene *parent_scene)
     177             : {
     178             :         const GF_PropertyValue *p;
     179        1096 :         if (!ctx->vout) return;
     180             : 
     181        1096 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_URL);
     182        1096 :         if (!p) return;
     183             : 
     184        1096 :         if (mtype==GF_STREAM_SCENE) {
     185         473 :                 if (!parent_scene || !parent_scene->is_dynamic_scene) {
     186         473 :                         gf_filter_pid_set_property(ctx->vout, GF_PROP_PID_URL, p);
     187             :                 }
     188         623 :         } else if (parent_scene && parent_scene->is_dynamic_scene) {
     189         274 :                 if (mtype==GF_STREAM_VISUAL)
     190         269 :                         gf_filter_pid_set_property(ctx->vout, GF_PROP_PID_URL, p);
     191             :         }
     192             : }
     193             : 
     194        1161 : static GF_Err compose_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
     195             : {
     196             :         GF_ObjectManager *odm;
     197             :         const GF_PropertyValue *prop;
     198             :         u32 mtype, codecid;
     199             :         u32 i, count;
     200             :         GF_Scene *scene = NULL;
     201             :         GF_Scene *top_scene = NULL;
     202        1161 :         GF_Compositor *ctx = (GF_Compositor *) gf_filter_get_udta(filter);
     203             :         GF_FilterEvent evt;
     204             :         Bool in_iod = GF_FALSE;
     205             :         Bool was_dyn_scene = GF_FALSE;
     206        1161 :         if (is_remove) {
     207             :                 u32 ID=0;
     208          65 :                 odm = gf_filter_pid_get_udta(pid);
     209             :                 //already disconnected
     210          65 :                 if (!odm) return GF_OK;
     211           0 :                 ID = odm->ID;
     212           0 :                 scene = odm->parentscene;
     213           0 :                 if (scene && !scene->is_dynamic_scene)
     214             :                         scene = NULL;
     215             :                 //destroy the object
     216           0 :                 gf_odm_disconnect(odm, 2);
     217           0 :                 if (scene) {
     218           0 :                         if (scene->visual_url.OD_ID == ID) {
     219           0 :                                 scene->visual_url.OD_ID = 0;
     220           0 :                                 gf_scene_regenerate(scene);
     221           0 :                         } else if (scene->audio_url.OD_ID == ID) {
     222           0 :                                 scene->audio_url.OD_ID = 0;
     223           0 :                                 gf_scene_regenerate(scene);
     224           0 :                         } else if (scene->text_url.OD_ID == ID) {
     225           0 :                                 scene->text_url.OD_ID = 0;
     226           0 :                                 gf_scene_regenerate(scene);
     227           0 :                         } else if (scene->dims_url.OD_ID == ID) {
     228           0 :                                 scene->dims_url.OD_ID = 0;
     229           0 :                                 gf_scene_regenerate(scene);
     230             :                         }
     231             :                 }
     232             :                 return GF_OK;
     233             :         }
     234             : 
     235        1096 :         prop = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
     236        1096 :         if (!prop) return GF_NOT_SUPPORTED;
     237        1096 :         mtype = prop->value.uint;
     238             : 
     239        1096 :         prop = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
     240        1096 :         if (!prop) return GF_NOT_SUPPORTED;
     241        1096 :         codecid = prop->value.uint;
     242             : 
     243        1096 :         odm = gf_filter_pid_get_udta(pid);
     244        1096 :         if (odm) {
     245         209 :                 if (mtype==GF_STREAM_SCENE) { }
     246         209 :                 else if (mtype==GF_STREAM_OD) { }
     247             :                 //change of stream type for a given object, no use case yet
     248             :                 else {
     249         209 :                         if (odm->type != mtype)
     250             :                                 return GF_NOT_SUPPORTED;
     251         209 :                         if (odm->mo) {
     252         209 :                                 odm->mo->config_changed = GF_TRUE;
     253         209 :                                 if ((odm->type == GF_STREAM_VISUAL) && odm->parentscene && odm->parentscene->is_dynamic_scene) {
     254          65 :                                         gf_scene_force_size_to_video(odm->parentscene, odm->mo);
     255          65 :                                         odm->mo->config_changed = GF_TRUE;
     256             :                                 }
     257             :                         }
     258         209 :                         gf_odm_update_duration(odm, pid);
     259         209 :                         gf_odm_check_clock_mediatime(odm);
     260             :                 }
     261         209 :                 merge_properties(ctx, pid, mtype, odm->parentscene);
     262         209 :                 return GF_OK;
     263             :         }
     264             : 
     265             :         //create a default scene
     266         887 :         if (!ctx->root_scene) {
     267             :                 const char *service_url = "unknown";
     268         598 :                 const GF_PropertyValue *p = gf_filter_pid_get_property(pid, GF_PROP_PID_URL);
     269         598 :                 if (p) service_url = p->value.string;
     270             :                 
     271         598 :                 ctx->root_scene = gf_scene_new(ctx, NULL);
     272         598 :                 ctx->root_scene->root_od = gf_odm_new();
     273         598 :                 ctx->root_scene->root_od->scene_ns = gf_scene_ns_new(ctx->root_scene, ctx->root_scene->root_od, service_url, NULL);
     274         598 :                 ctx->root_scene->root_od->subscene = ctx->root_scene;
     275         598 :                 ctx->root_scene->root_od->scene_ns->nb_odm_users++;
     276             :                 switch (mtype) {
     277         395 :                 case GF_STREAM_SCENE:
     278             :                 case GF_STREAM_PRIVATE_SCENE:
     279             :                 case GF_STREAM_OD:
     280         395 :                         ctx->root_scene->is_dynamic_scene = GF_FALSE;
     281         395 :                         break;
     282         203 :                 default:
     283         203 :                         ctx->root_scene->is_dynamic_scene = GF_TRUE;
     284         203 :                         break;
     285             :                 }
     286             : 
     287         598 :                 if (!ctx->root_scene->root_od->scene_ns->url_frag) {
     288         598 :                         p = gf_filter_pid_get_property(pid, GF_PROP_PID_ORIG_FRAG_URL);
     289         598 :                         if (p && p->value.string)
     290           0 :                                 ctx->root_scene->root_od->scene_ns->url_frag = gf_strdup(p->value.string);
     291             :                 }
     292             : 
     293         598 :                 if (!ctx->player)
     294         598 :                         gf_filter_post_process_task(filter);
     295             :         }
     296             : 
     297             :         //default scene is root one
     298         887 :         scene = ctx->root_scene;
     299             :         top_scene = ctx->root_scene;
     300             : 
     301             :         //browse all scene namespaces and figure out our parent scene
     302         887 :         count = gf_list_count(top_scene->namespaces);
     303        1970 :         for (i=0; i<count; i++) {
     304        1360 :                 GF_SceneNamespace *sns = gf_list_get(top_scene->namespaces, i);
     305        1360 :                 if (!sns->source_filter) {
     306         962 :                         if (sns->connect_ack && sns->owner) {
     307          58 :                                 scene = sns->owner->subscene ? sns->owner->subscene : sns->owner->parentscene;
     308             :                                 break;
     309             :                         }
     310         904 :                         continue;
     311             :                 }
     312             :                 assert(sns->owner);
     313         398 :                 if (gf_filter_pid_is_filter_in_parents(pid, sns->source_filter)) {
     314         219 :                         if (!sns->owner->subscene && sns->owner->parentscene && (mtype!=GF_STREAM_OD) && (mtype!=GF_STREAM_SCENE)) {
     315             :                                 u32 j;
     316         267 :                                 for (j=0; j<gf_list_count(sns->owner->parentscene->scene_objects); j++) {
     317         269 :                                         GF_MediaObject *mo = gf_list_get(sns->owner->parentscene->scene_objects, j);
     318         269 :                                         if (mo->OD_ID == GF_MEDIA_EXTERNAL_ID) continue;
     319         191 :                                         if (mo->OD_ID != sns->owner->ID) continue;
     320             : 
     321         120 :                                         if (mo->type != GF_MEDIA_OBJECT_SCENE) continue;
     322             :                                         //this is a pid from a subservice (inline) inserted through OD commands, create the subscene
     323           2 :                                         sns->owner->subscene = gf_scene_new(NULL, sns->owner->parentscene);
     324           2 :                                         sns->owner->subscene->root_od = sns->owner;
     325             :                                         //scenes are by default dynamic
     326           2 :                                         sns->owner->subscene->is_dynamic_scene = GF_TRUE;
     327           2 :                                         sns->owner->mo = mo;
     328           2 :                                         mo->odm = sns->owner;
     329           2 :                                         break;
     330             :                                 }
     331             :                         }
     332             :                         //we are attaching an inline, create the subscene if not done already
     333         219 :                         if (!sns->owner->subscene && ((mtype==GF_STREAM_OD) || (mtype==GF_STREAM_SCENE)) ) {
     334             :                                 //ignore system PIDs from subservice - this is typically the case when playing a bt/xmt file
     335             :                                 //created from a container (mp4) and still referring to that container for the media streams
     336           5 :                                 if (sns->owner->ignore_sys) {
     337           0 :                                         GF_FEVT_INIT(evt, GF_FEVT_PLAY, pid);
     338           0 :                                         gf_filter_pid_send_event(pid, &evt);
     339           0 :                                         GF_FEVT_INIT(evt, GF_FEVT_STOP, pid);
     340           0 :                                         gf_filter_pid_send_event(pid, &evt);
     341           0 :                                         return GF_OK;
     342             :                                 }
     343             : 
     344             :                                 assert(sns->owner->parentscene);
     345           5 :                                 sns->owner->subscene = gf_scene_new(ctx, sns->owner->parentscene);
     346           5 :                                 sns->owner->subscene->root_od = sns->owner;
     347             :                         }
     348         219 :                         scene = sns->owner->subscene ? sns->owner->subscene : sns->owner->parentscene;
     349             :                         break;
     350             :                 }
     351             :         }
     352             :         assert(scene);
     353             : 
     354         887 :         GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Configuring PID %s\n", gf_stream_type_name(mtype)));
     355             : 
     356         887 :         was_dyn_scene = scene->is_dynamic_scene;
     357             : 
     358             :         //pure OCR streams are handled by dispatching OCR on the PID(s)
     359         887 :         if (codecid != GF_CODECID_RAW)
     360             :                 return GF_NOT_SUPPORTED;
     361             : 
     362         887 :         switch (mtype) {
     363         479 :         case GF_STREAM_SCENE:
     364             :         case GF_STREAM_OD:
     365             :                 //we have an MPEG-4 ESID defined for the PID, this is MPEG-4 systems
     366         479 :                 prop = gf_filter_pid_get_property(pid, GF_PROP_PID_ESID);
     367         479 :                 if (prop && scene->is_dynamic_scene) {
     368           2 :                         scene->is_dynamic_scene = GF_FALSE;
     369             :                 }
     370         479 :                 prop = gf_filter_pid_get_property(pid, GF_PROP_PID_IN_IOD);
     371         479 :                 if (prop && prop->value.boolean) {
     372         479 :                         scene->is_dynamic_scene = GF_FALSE;
     373             :                         in_iod = GF_TRUE;
     374             :                 }
     375             :                 break;
     376             :         }
     377             : 
     378         887 :         if ((mtype==GF_STREAM_OD) && !in_iod) return GF_NOT_SUPPORTED;
     379             : 
     380             :         //we inserted a root scene (bt/svg/...) after a pid (passthrough mode), we need to create a new namesapce for
     381             :         //the scene and reassign the old namespace to the previously created ODM
     382         887 :         if (!scene->root_od->parentscene && was_dyn_scene && (was_dyn_scene != scene->is_dynamic_scene)) {
     383             :                 GF_SceneNamespace *new_sns=NULL;
     384             :                 const char *service_url = "unknown";
     385          34 :                 const GF_PropertyValue *p = gf_filter_pid_get_property(pid, GF_PROP_PID_URL);
     386          34 :                 if (p) service_url = p->value.string;
     387          34 :                 new_sns = gf_scene_ns_new(ctx->root_scene, ctx->root_scene->root_od, service_url, NULL);
     388             : 
     389          34 :                 for (i=0; i<gf_list_count(scene->resources); i++) {
     390          29 :                         GF_ObjectManager *anodm = gf_list_get(scene->resources, i);
     391             : 
     392          29 :                         if (new_sns && (anodm->scene_ns == scene->root_od->scene_ns) && (scene->root_od->scene_ns->owner==scene->root_od)) {
     393          29 :                                 scene->root_od->scene_ns->owner = anodm;
     394          29 :                                 break;
     395             :                         }
     396             :                 }
     397          34 :                 scene->root_od->scene_ns = new_sns;
     398          34 :                 gf_sc_set_scene(ctx, NULL);
     399          34 :                 gf_sg_reset(scene->graph);
     400          34 :                 gf_sc_set_scene(ctx, scene->graph);
     401          34 :                 ctx->reload_scene_size = GF_TRUE;
     402             :         }
     403             : 
     404             :         //setup object (clock) and playback requests
     405         887 :         gf_scene_insert_pid(scene, scene->root_od->scene_ns, pid, in_iod);
     406             : 
     407         887 :         if (was_dyn_scene != scene->is_dynamic_scene) {
     408          29 :                 for (i=0; i<gf_list_count(scene->resources); i++) {
     409          29 :                         GF_ObjectManager *anodm = gf_list_get(scene->resources, i);
     410          29 :                         if (anodm->mo)
     411          29 :                                 anodm->flags |= GF_ODM_PASSTHROUGH;
     412             :                 }
     413             :         }
     414             : 
     415             : 
     416             :         //attach scene to input filters - may be true for dynamic scene (text rendering) and regular scenes
     417         887 :         if ((mtype==GF_STREAM_OD) || (mtype==GF_STREAM_SCENE) || (mtype==GF_STREAM_TEXT) ) {
     418             :                 void gf_filter_pid_exec_event(GF_FilterPid *pid, GF_FilterEvent *evt);
     419             : 
     420         489 :                 GF_FEVT_INIT(evt, GF_FEVT_ATTACH_SCENE, pid);
     421         489 :                 evt.attach_scene.object_manager = gf_filter_pid_get_udta(pid);
     422         489 :                 gf_filter_pid_exec_event(pid, &evt);
     423             :         }
     424             :         //scene is dynamic
     425         887 :         if (scene->is_dynamic_scene) {
     426             :                 Bool reset = GF_FALSE;
     427             :                 u32 scene_vr_type = 0;
     428         209 :                 char *sep = scene->root_od->scene_ns->url_frag;
     429         209 :                 if (sep && ( !strnicmp(sep, "LIVE360", 7) || !strnicmp(sep, "360", 3) || !strnicmp(sep, "VR", 2) ) ) {
     430             :                         scene_vr_type = 1;
     431             :                 }
     432         209 :                 if (!sep) {
     433         207 :                         prop = gf_filter_pid_get_property(pid, GF_PROP_PID_PROJECTION_TYPE);
     434         207 :                         if (prop && (prop->value.uint==GF_PROJ360_EQR)) scene_vr_type = 1;
     435             :                 }
     436         209 :                 if (scene_vr_type) {
     437           2 :                         if (scene->vr_type != scene_vr_type) reset = GF_TRUE;
     438           2 :                         scene->vr_type = scene_vr_type;
     439             :                 }
     440             :                 if (reset)
     441           2 :                         gf_sg_reset(scene->graph);
     442             : 
     443         209 :                 gf_scene_regenerate(scene);
     444             :         }
     445             : 
     446         887 :         merge_properties(ctx, pid, mtype, scene);
     447         887 :         return GF_OK;
     448             : }
     449             : 
     450             : #include "../compositor/visual_manager.h"
     451             : 
     452          35 : static GF_Err compose_reconfig_output(GF_Filter *filter, GF_FilterPid *pid)
     453             : {
     454             :         const GF_PropertyValue *p;
     455             :         u32 sr, o_fmt, nb_ch, afmt;
     456             :         u64 cfg;
     457             :         Bool needs_reconfigure = GF_FALSE;
     458          35 :         GF_Compositor *ctx = (GF_Compositor *) gf_filter_get_udta(filter);
     459             : 
     460          35 :         if (ctx->vout == pid) {
     461             :                 u32 w, h;
     462          34 :                 p = gf_filter_pid_caps_query(pid, GF_PROP_PID_PIXFMT);
     463          34 :                 if (p) {
     464             :                         u32 stride;
     465             : #ifndef GPAC_DISABLE_3D
     466          34 :                         if (ctx->scene && (ctx->hybrid_opengl || ctx->visual->type_3d)) {
     467           0 :                                 switch (p->value.uint) {
     468             :                                 case GF_PIXEL_RGBA:
     469             :                                 case GF_PIXEL_RGB:
     470             :                                         break;
     471           0 :                                 default:
     472           0 :                                         return GF_NOT_SUPPORTED;
     473             :                                 }
     474          34 :                         }
     475             : #endif
     476          34 :                         ctx->opfmt = p->value.uint;
     477          34 :                         gf_filter_pid_set_property(ctx->vout, GF_PROP_PID_PIXFMT, &PROP_UINT(ctx->opfmt) );
     478          34 :                         gf_pixel_get_size_info(ctx->opfmt, ctx->display_width, ctx->display_height, NULL, &stride, NULL, NULL, NULL);
     479          34 :                         gf_filter_pid_set_property(ctx->vout, GF_PROP_PID_STRIDE, &PROP_UINT(stride) );
     480             :                 }
     481             :                 
     482             :                 w = h = 0;
     483          34 :                 p = gf_filter_pid_caps_query(pid, GF_PROP_PID_WIDTH);
     484          34 :                 if (p) w = p->value.uint;
     485          34 :                 p = gf_filter_pid_caps_query(pid, GF_PROP_PID_HEIGHT);
     486          34 :                 if (p) h = p->value.uint;
     487             : 
     488          34 :                 if (w && h) {
     489           0 :                         ctx->osize.x = w;
     490           0 :                         ctx->osize.y = h;
     491             : /*                      gf_filter_pid_set_property(ctx->vout, GF_PROP_PID_WIDTH, &PROP_UINT(w) );
     492             :                         gf_filter_pid_set_property(ctx->vout, GF_PROP_PID_HEIGHT, &PROP_UINT(h) );
     493             : */              }
     494             :                 return GF_OK;
     495             :         }
     496             : 
     497           1 :         if (ctx->audio_renderer->aout == pid) {
     498             : 
     499           1 :                 gf_mixer_get_config(ctx->audio_renderer->mixer, &sr, &nb_ch, &o_fmt, &cfg);
     500           1 :                 p = gf_filter_pid_caps_query(pid, GF_PROP_PID_SAMPLE_RATE);
     501           1 :                 if (p && (p->value.uint != sr)) {
     502           0 :                         sr = p->value.uint;
     503             :                         needs_reconfigure = GF_TRUE;
     504             :                 }
     505           1 :                 p = gf_filter_pid_caps_query(pid, GF_PROP_PID_NUM_CHANNELS);
     506           1 :                 if (p && (p->value.uint != nb_ch)) {
     507           0 :                         nb_ch = p->value.uint;
     508             :                         needs_reconfigure = GF_TRUE;
     509             :                 }
     510           1 :                 p = gf_filter_pid_caps_query(pid, GF_PROP_PID_AUDIO_FORMAT);
     511           1 :                 if (p) afmt = p->value.uint;
     512             :                 else afmt = GF_AUDIO_FMT_S16;
     513             : 
     514           1 :                 if (o_fmt != afmt) {
     515             :                         needs_reconfigure = GF_TRUE;
     516             :                 }
     517           0 :                 if (!needs_reconfigure) return GF_OK;
     518             : 
     519           1 :                 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) ));
     520           1 :                 gf_mixer_set_config(ctx->audio_renderer->mixer, sr, nb_ch, afmt, 0);
     521           1 :                 ctx->audio_renderer->need_reconfig = GF_TRUE;
     522           1 :                 return GF_OK;
     523             :         }
     524             :         return GF_NOT_SUPPORTED;
     525             : }
     526             : 
     527        6428 : static Bool compose_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
     528             : {
     529        6428 :         switch (evt->base.type) {
     530             :         //event(s) we trigger on ourselves to go up the filter chain
     531             :         case GF_FEVT_CAPS_CHANGE:
     532             :                 return GF_FALSE;
     533           8 :         case GF_FEVT_CONNECT_FAIL:
     534             :         {
     535           8 :                 GF_Compositor *ctx = (GF_Compositor *) gf_filter_get_udta(filter);
     536           8 :                 if (ctx->audio_renderer && (evt->base.on_pid == ctx->audio_renderer->aout))
     537           1 :                         ctx->audio_renderer->non_rt_output = GF_FALSE;
     538             :         }
     539             :                 return GF_FALSE;
     540           0 :         case GF_FEVT_BUFFER_REQ:
     541           0 :                 return GF_TRUE;
     542             :                 
     543        5783 :         case GF_FEVT_INFO_UPDATE:
     544             :         {
     545             :                 u32 bps=0;
     546             :                 u64 tot_size=0, down_size=0;
     547        5783 :                 GF_ObjectManager *odm = gf_filter_pid_get_udta(evt->base.on_pid);
     548        5783 :                 GF_PropertyEntry *pe=NULL;
     549        5783 :                 GF_PropertyValue *p = (GF_PropertyValue *) gf_filter_pid_get_info(evt->base.on_pid, GF_PROP_PID_TIMESHIFT_STATE, &pe);
     550        5783 :                 if (p && p->value.uint) {
     551             :                         GF_Event an_evt;
     552             :                         memset(&an_evt, 0, sizeof(GF_Event));
     553           0 :                         GF_Compositor *ctx = (GF_Compositor *) gf_filter_get_udta(filter);
     554           0 :                         if (p->value.uint==1) {
     555           0 :                                 an_evt.type = GF_EVENT_TIMESHIFT_UNDERRUN;
     556           0 :                                 gf_sc_send_event(ctx, &an_evt);
     557           0 :                         } else if (p->value.uint==2) {
     558           0 :                                 an_evt.type = GF_EVENT_TIMESHIFT_OVERFLOW;
     559           0 :                                 gf_sc_send_event(ctx, &an_evt);
     560             :                         }
     561           0 :                         p->value.uint = 0;
     562             :                 }
     563             : 
     564        5783 :                 p = (GF_PropertyValue *) gf_filter_pid_get_info(evt->base.on_pid, GF_PROP_PID_DOWN_RATE, &pe);
     565        5783 :                 if (p) bps = p->value.uint;
     566        5783 :                 p = (GF_PropertyValue *) gf_filter_pid_get_info(evt->base.on_pid, GF_PROP_PID_DOWN_SIZE, &pe);
     567        5783 :                 if (p) tot_size = p->value.longuint;
     568             : 
     569        5783 :                 p = (GF_PropertyValue *) gf_filter_pid_get_info(evt->base.on_pid, GF_PROP_PID_DOWN_BYTES, &pe);
     570        5783 :                 if (p) down_size = p->value.longuint;
     571             : 
     572        5783 :                 if (bps && down_size && tot_size)  {
     573          22 :                         odm = gf_filter_pid_get_udta(evt->base.on_pid);
     574          22 :                         if ((down_size!=odm->last_filesize_signaled) || (down_size != tot_size)) {
     575          10 :                                 odm->last_filesize_signaled = down_size;
     576          10 :                                 gf_odm_service_media_event_with_download(odm, GF_EVENT_MEDIA_PROGRESS, down_size, tot_size, bps/8, 0, 0);
     577             :                         }
     578             :                 }
     579             : 
     580        5783 :                 gf_filter_release_property(pe);
     581        5783 :                 gf_odm_check_clock_mediatime(odm);
     582             :         }
     583        5783 :                 return GF_TRUE;
     584             : 
     585           0 :         case GF_FEVT_USER:
     586           0 :                 return gf_sc_user_event(gf_filter_get_udta(filter), (GF_Event *) &evt->user_event.event);
     587             : 
     588             :         default:
     589             :                 break;
     590             :         }
     591             :         //all events cancelled (play/stop/etc...)
     592         637 :         return GF_TRUE;
     593             : }
     594             : 
     595           0 : static GF_Err compose_update_arg(GF_Filter *filter, const char *arg_name, const GF_PropertyValue *arg_val)
     596             : {
     597         599 :         GF_Compositor *compositor = gf_filter_get_udta(filter);
     598         599 :         compositor->reload_config = GF_TRUE;
     599           0 :         return GF_OK;
     600             : }
     601             : 
     602         604 : static void compose_finalize(GF_Filter *filter)
     603             : {
     604         604 :         GF_Compositor *ctx = gf_filter_get_udta(filter);
     605             : 
     606         604 :         if (ctx) {
     607         604 :                 gf_sc_set_scene(ctx, NULL);
     608         604 :                 if (ctx->root_scene) {
     609         598 :                         gf_odm_disconnect(ctx->root_scene->root_od, GF_TRUE);
     610             :                 }
     611         604 :                 gf_sc_unload(ctx);
     612             :         }
     613         604 : }
     614          28 : void compositor_setup_aout(GF_Compositor *ctx)
     615             : {
     616          28 :         if (! (ctx->init_flags & GF_TERM_NO_AUDIO) && ctx->audio_renderer && !ctx->audio_renderer->aout) {
     617          20 :                 GF_FilterPid *pid = ctx->audio_renderer->aout = gf_filter_pid_new(ctx->filter);
     618          20 :                 gf_filter_pid_set_udta(pid, ctx);
     619          20 :                 gf_filter_pid_set_name(pid, "aout");
     620          20 :                 gf_filter_pid_set_property(pid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_AUDIO) );
     621          20 :                 gf_filter_pid_set_property(pid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_RAW) );
     622          20 :                 gf_filter_pid_set_property(pid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT(GF_AUDIO_FMT_S16) );
     623          20 :                 gf_filter_pid_set_property(pid, GF_PROP_PID_TIMESCALE, &PROP_UINT(44100) );
     624          20 :                 gf_filter_pid_set_property(pid, GF_PROP_PID_SAMPLE_RATE, &PROP_UINT(44100) );
     625          20 :                 gf_filter_pid_set_property(pid, GF_PROP_PID_NUM_CHANNELS, &PROP_UINT(2) );
     626          20 :                 gf_filter_pid_set_max_buffer(ctx->audio_renderer->aout, 1000*ctx->abuf);
     627          20 :                 gf_filter_pid_set_loose_connect(pid);
     628             :         }
     629          28 : }
     630             : 
     631         605 : static GF_Err compose_initialize(GF_Filter *filter)
     632             : {
     633             :         GF_Err e;
     634             :         GF_FilterSessionCaps sess_caps;
     635             :         GF_FilterPid *pid;
     636         605 :         GF_Compositor *ctx = gf_filter_get_udta(filter);
     637             : 
     638         605 :         ctx->magic = COMPOSITOR_MAGIC;
     639         605 :         ctx->magic_ptr = (void *) ctx;
     640         605 :         ctx->filter = filter;
     641             : 
     642         605 :         if (gf_filter_is_dynamic(filter)) {
     643          12 :                 ctx->dyn_filter_mode = GF_TRUE;
     644          12 :                 ctx->vfr = GF_TRUE;
     645             :         }
     646             : 
     647             :         //playout buffer not greater than max buffer
     648         605 :         if (ctx->buffer > ctx->mbuffer)
     649           0 :                 ctx->buffer = ctx->mbuffer;
     650             : 
     651             :         //rebuffer level not greater than playout buffer
     652         605 :         if (ctx->rbuffer >= ctx->buffer)
     653           0 :                 ctx->rbuffer = 0;
     654             : 
     655             : 
     656         605 :     if (ctx->player) {
     657             :                 //explicit disable of openGL
     658           7 :                 if (ctx->drv==GF_SC_DRV_OFF)
     659           0 :                         ctx->ogl = GF_SC_GLMODE_OFF;
     660             : 
     661           7 :                 if (ctx->ogl == GF_SC_GLMODE_AUTO)
     662           7 :                         ctx->ogl = GF_SC_GLMODE_HYBRID;
     663             : 
     664             :                 //we operate video output directly and dispatch audio output, we need to disable blocking mode
     665             :                 //otherwise we will only get called when audio output is not blocking, and we will likely missed video frames
     666           7 :                 gf_filter_prevent_blocking(filter, GF_TRUE);
     667             :         }
     668             : 
     669         605 :     e = gf_sc_load(ctx);
     670         605 :         if (e) return e;
     671             : 
     672         605 :         gf_filter_get_session_caps(filter, &sess_caps);
     673             : 
     674         605 :         sess_caps.max_screen_width = ctx->video_out->max_screen_width;
     675         605 :         sess_caps.max_screen_height = ctx->video_out->max_screen_height;
     676         605 :         sess_caps.max_screen_bpp = ctx->video_out->max_screen_bpp;
     677             : 
     678         605 :         gf_filter_set_session_caps(filter, &sess_caps);
     679             : 
     680         605 :         if (ctx->player) {
     681             :                 //make filter sticky (no shutdown at eos)
     682           7 :                 gf_filter_make_sticky(filter);
     683             : 
     684             :                 //load audio filter chain, declaring audio output pid first
     685           7 :                 if (! (ctx->init_flags & (GF_TERM_NO_AUDIO|GF_TERM_NO_DEF_AUDIO_OUT)) ) {
     686           5 :                         GF_Filter *audio_out = gf_filter_load_filter(filter, "aout", &e);
     687           5 :                         ctx->audio_renderer->non_rt_output = GF_FALSE;
     688           5 :                         if (!audio_out) {
     689           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Terminal] Failed to load audio output filter (%s) - audio disabled\n", gf_error_to_string(e) ));
     690             :                         }
     691             : //                      else {
     692             : //                              gf_filter_reconnect_output(filter);
     693             : //                      }
     694             :                 }
     695           7 :                 compositor_setup_aout(ctx);
     696             :         }
     697             :         
     698             :         //declare video output pid
     699         605 :         pid = ctx->vout = gf_filter_pid_new(filter);
     700         605 :         gf_filter_pid_set_name(pid, "vout");
     701             :         //compositor initiated for RT playback, vout pid may not be connected
     702         605 :         if (! (ctx->init_flags & GF_TERM_NO_DEF_AUDIO_OUT))
     703         605 :                 gf_filter_pid_set_loose_connect(pid);
     704             : 
     705         605 :         gf_filter_pid_set_property(pid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_RAW) );
     706         605 :         gf_filter_pid_set_property(pid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_VISUAL) );
     707         605 :         if (ctx->timescale)
     708           0 :                 gf_filter_pid_set_property(pid, GF_PROP_PID_TIMESCALE, &PROP_UINT(ctx->timescale) );
     709             :         else
     710         605 :                 gf_filter_pid_set_property(pid, GF_PROP_PID_TIMESCALE, &PROP_UINT(ctx->fps.num) );
     711             : 
     712         605 :         gf_filter_pid_set_property(pid, GF_PROP_PID_PIXFMT, &PROP_UINT(ctx->opfmt ? ctx->opfmt : GF_PIXEL_RGB) );
     713         605 :         gf_filter_pid_set_property(pid, GF_PROP_PID_WIDTH, &PROP_UINT(ctx->output_width) );
     714         605 :         gf_filter_pid_set_property(pid, GF_PROP_PID_HEIGHT, &PROP_UINT(ctx->output_height) );
     715             : 
     716         605 :         gf_filter_pid_set_property(pid, GF_PROP_PID_FPS, &PROP_FRAC(ctx->fps) );
     717             : 
     718             :         //for coverage
     719             : #ifdef GPAC_ENABLE_COVERAGE
     720         605 :         if (gf_sys_is_cov_mode()) {
     721             :                 compose_update_arg(filter, NULL, NULL);
     722             :         }
     723             : #endif
     724             : 
     725             :         //always request a process task since we don't depend on input packets arrival (animations, pure scene presentations)
     726         605 :         gf_filter_post_process_task(filter);
     727             : 
     728         605 :         gf_filter_set_event_target(filter, GF_TRUE);
     729         605 :         if (ctx->player==2) {
     730           0 :                 const char *gui_path = gf_opts_get_key("General", "StartupFile");
     731           0 :                 if (gui_path) {
     732           0 :                         gf_sc_connect_from_time_ex(ctx, gui_path, 0, 0, 0, NULL);
     733           0 :                         gf_opts_set_key("temp", "gui_load_url", ctx->src);
     734             :                 }
     735             :         }
     736             :         //src set, connect it (whether player mode or not)
     737         605 :         else if (ctx->src) {
     738           0 :                 gf_sc_connect_from_time_ex(ctx, ctx->src, 0, 0, 0, NULL);
     739             :         }
     740             :         return GF_OK;
     741             : }
     742             : 
     743        2939 : GF_FilterProbeScore compose_probe_url(const char *url, const char *mime)
     744             : {
     745             :         //check all our builtin URL schemes
     746        2939 :         if (!strnicmp(url, "mosaic://", 9)) {
     747             :                 return GF_FPROBE_FORCE;
     748             :         }
     749        2939 :         else if (!strnicmp(url, "views://", 8)) {
     750             :                 return GF_FPROBE_FORCE;
     751             :         }
     752        2939 :         return GF_FPROBE_NOT_SUPPORTED;
     753             : }
     754             : 
     755             : 
     756             : #define OFFS(_n)        #_n, offsetof(GF_Compositor, _n)
     757             : static GF_FilterArgs CompositorArgs[] =
     758             : {
     759             :         { 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"\
     760             :                 "- none: no anti-aliasing\n"\
     761             :         "- text: anti-aliasing for text only\n"\
     762             :         "- all: complete anti-aliasing", GF_PROP_UINT, "all", "none|text|all", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
     763             :         { OFFS(hlfill), "set highlight fill color (ARGB)", GF_PROP_UINT, "0x0", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
     764             :         { OFFS(hlline), "set highlight stroke color (ARGB)", GF_PROP_UINT, "0xFF000000", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
     765             :         { OFFS(hllinew), "set highlight stroke width", GF_PROP_FLOAT, "1.0", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
     766             :         { 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},
     767             :         { 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},
     768             :         { OFFS(yuvhw), "enable YUV hardware for 2D blits", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
     769             :         { OFFS(blitp), "partial hardware blits (if not set, will force more redraw)", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
     770             :         { 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},
     771             : 
     772             :         { 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},
     773             :         { 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},
     774             :         { OFFS(bvol), "draw bounding volume of objects\n"\
     775             :                         "- no: disable bounding box\n"\
     776             :                         "- box: draws a rectangle (2D) or box (3D mode)\n"\
     777             :                         "- aabb: draws axis-aligned bounding-box tree (3D only)", GF_PROP_UINT, "no", "no|box|aabb", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     778             :         { 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"\
     779             :                 "- default: use texturing for OpenGL rendering, no texture for 2D rasterizer\n"\
     780             :                 "- never: never uses text textures\n"\
     781             :                 "- always: always render text to texture before drawing"\
     782             :                 "", GF_PROP_UINT, "default", "default|never|always", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
     783             :         { 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},
     784             :         { OFFS(drop), "drop late frame when drawing. By default frames are not dropped until a heavy desync of 1 sec is observed", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE},
     785             :         { OFFS(sclock), "force synchronizing all streams on a single clock", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     786             :         { OFFS(sgaze), "simulate gaze events through mouse", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     787             :         { 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},
     788             :         { OFFS(timeout), "timeout in ms after which a source is considered dead", GF_PROP_UINT, "10000", NULL, GF_FS_ARG_UPDATE},
     789             :         { 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},
     790             :         { 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},
     791             :         { OFFS(autofps), "use video input fps for output. If no video or not set, uses [-fps](). Ignored in player mode", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
     792             :         { 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},
     793             : 
     794             :         { 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},
     795             :         { 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},
     796             :         { OFFS(mode2d), "specify whether immediate drawing should be used or not\n"\
     797             :         "- immediate: the screen is completely redrawn at each frame (always on if passthrough mode is detected)\n"\
     798             :         "- 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"\
     799             :         "- debug: only renders changed areas, reseting other areas\n"\
     800             :          "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},
     801             :         { OFFS(amc), "audio multichannel support; if disabled always downmix to stereo. Useful if the multichannel output does not work properly", GF_PROP_BOOL, "true", NULL, 0},
     802             :         { OFFS(asr), "force output sample rate - 0 for auto", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
     803             :         { OFFS(ach), "force output channels - 0 for auto", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
     804             :         { OFFS(alayout), "force output channel layout - 0 for auto", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
     805             :         { OFFS(afmt), "force output channel format - 0 for auto", GF_PROP_PCMFMT, "s16", NULL, GF_FS_ARG_HINT_ADVANCED},
     806             :         { OFFS(asize), "audio output packet size in samples", GF_PROP_UINT, "1024", NULL, GF_FS_ARG_HINT_EXPERT},
     807             :         { 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, 
     808             : #ifdef GPAC_CONFIG_ANDROID
     809             :                 "200"
     810             : #else
     811             :                 "100"
     812             : #endif
     813             :                 , NULL, GF_FS_ARG_HINT_EXPERT},
     814             :         { OFFS(avol), "audio volume in percent", GF_PROP_UINT, "100", NULL, GF_FS_ARG_UPDATE},
     815             :         { OFFS(apan), "audio pan in percent, 50 is no pan", GF_PROP_UINT, "50", NULL, GF_FS_ARG_UPDATE},
     816             :         { 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},
     817             :         { OFFS(max_aspeed), "silence audio if playback speed is greater than sepcified value", GF_PROP_DOUBLE, "2.0", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     818             :         { OFFS(max_vspeed), "move to i-frame only decoding if playback speed is greater than sepcified value", GF_PROP_DOUBLE, "4.0", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     819             : 
     820             :         { OFFS(buffer), "playout buffer in ms. overridden by BufferLenth property of input pid", GF_PROP_UINT, "3000", NULL, GF_FS_ARG_UPDATE},
     821             :         { OFFS(rbuffer), "rebuffer trigger in ms. overridden by RebufferLenth property of input pid", GF_PROP_UINT, "1000", NULL, GF_FS_ARG_UPDATE},
     822             :         { 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},
     823             :         { 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},
     824             : 
     825             :         { OFFS(nojs), "disable javascript", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
     826             :         { OFFS(noback), "ignore background nodes and viewport fill (useful when dumping to PNG)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
     827             : 
     828             : #ifndef GPAC_DISABLE_3D
     829             :         { OFFS(ogl), "specify 2D rendering mode\n"\
     830             :                                 "- auto: automatically decides between on, off and hybrid based on content\n"\
     831             :                                 "- off: disables OpenGL; 3D will not be rendered\n"\
     832             :                                 "- on: uses OpenGL for all graphics; this will involve polygon tesselation and 2D graphics will not look as nice as 2D mode\n"\
     833             :                                 "- 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"\
     834             :                                 , GF_PROP_UINT, "auto", "auto|off|hybrid|on", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
     835             :         { 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},
     836             :         { OFFS(nav), "override the default navigation mode of MPEG-4/VRML (Walk) and X3D (Examine)\n"\
     837             :         "- none: disables navigation\n"\
     838             :         "- walk: 3D world walk\n"\
     839             :         "- fly: 3D world fly (no ground detection)\n"\
     840             :         "- pan: 2D/3D world zomm/pan\n"\
     841             :         "- game: 3D world game (mouse gives walk direction)\n"\
     842             :         "- slide: 2D/3D world slide\n"\
     843             :         "- exam: 2D/3D object examine\n"\
     844             :         "- orbit: 3D object orbit\n"\
     845             :         "- vr: 3D world VR (yaw/pitch/roll)"\
     846             :         "", GF_PROP_UINT, "none", "none|walk|fly|pan|game|slide|exam|orbit|vr", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
     847             :         { 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},
     848             :         { OFFS(epow2), "emulate power-of-2 textures for openGL (old hardware). Ignored if OpenGL rectangular texture extension is enabled\n"\
     849             :         "- yes: video texture is not resized but emulated with padding. This usually speeds up video mapping on shapes but disables texture transformations\n"\
     850             :         "- 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},
     851             :         { 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},
     852             :         { OFFS(bcull), "indicate whether backface culling shall be disable or not\n"\
     853             :                                 "- on: enables backface culling\n"\
     854             :                                 "- off: disables backface culling\n"\
     855             :                                 "- alpha: only enables backface culling for transparent meshes"\
     856             :                 "", GF_PROP_UINT, "on", "off|on|alpha", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     857             :         { OFFS(wire), "wireframe mode\n"\
     858             :         "- none: objects are drawn as solid\n"\
     859             :     "- only: objects are drawn as wireframe only\n"\
     860             :     "- 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},
     861             :         { OFFS(norms), "normal vector drawing for debug\n"
     862             :         "- none: no normals drawn\n"
     863             :         "- face: one normal per face drawn\n"
     864             :         "- vertex: one normal per vertex drawn"
     865             :         "", GF_PROP_UINT, "none", "none|face|vertex", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
     866             :         { 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},
     867             :         { 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},
     868             :         { OFFS(depth_gl_scale), "set depth scaler", GF_PROP_FLOAT, "100", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     869             :         { OFFS(depth_gl_type), "set geometry type used to draw depth video\n"
     870             :         "- none: no geometric conversion\n"
     871             :         "- point: compute point cloud from pixel+depth\n"
     872             :         "- strip: same as point but thins point set"
     873             :         "", GF_PROP_UINT, "none", "none|point|strip", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     874             :         { OFFS(nbviews), "number of views to use in stereo mode", GF_PROP_UINT, "0", NULL, GF_FS_ARG_UPDATE},
     875             :         { OFFS(stereo), "stereo output type. If your graphic card does not support OpenGL shaders, only `top` and `side` modes will be available\n"\
     876             :                 "- none: no stereo\n"\
     877             :                 "- side: images are displayed side by side from left to right\n"\
     878             :                 "- top: images are displayed from top (laft view) to bottom (right view)\n"\
     879             :                 "- hmd: same as side except that view aspect ratio is not changed\n"\
     880             :                 "- ana: standard color anaglyph (red for left view, green and blue for right view) is used (forces views=2)\n"\
     881             :                 "- cols: images are interleaved by columns, left view on even columns and left view on odd columns (forces views=2)\n"\
     882             :                 "- rows: images are interleaved by columns, left view on even rows and left view on odd rows (forces views=2)\n"\
     883             :                 "- spv5: images are interleaved by for SpatialView 5 views display, fullscreen mode (forces views=5)\n"\
     884             :                 "- alio8: images are interleaved by for Alioscopy 8 views displays, fullscreen mode (forces views=8)\n"\
     885             :                 "- 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},
     886             :         { OFFS(mvshader), "file path to the custom multiview interleaving shader", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     887             :         { OFFS(fpack), "default frame packing of input video\n"
     888             :                 "- none: no frame packing\n"
     889             :                 "- top: top bottom frame packing\n"
     890             :                 "- side: side by side packing"
     891             :         "", GF_PROP_UINT, "none", "none|top|side", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     892             :         { OFFS(camlay), "camera layout in multiview modes\n"
     893             :                 "- straight: camera is moved along a straight line, no rotation\n"
     894             :                 "- offaxis: off-axis projection is used\n"
     895             :                 "- linear: camera is moved along a straight line with rotation\n"
     896             :                 "- circular: camera is moved along a circle with rotation"
     897             :         "", GF_PROP_UINT, "offaxis", "straight|offaxis|linear|circular", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_ADVANCED},
     898             :         { OFFS(iod), "inter-occular distance (eye separation) in cm (distance between the cameras). ", GF_PROP_FLOAT, "6.4", NULL, GF_FS_ARG_UPDATE},
     899             :         { OFFS(rview), "reverse view order", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     900             : 
     901             :         { OFFS(tvtn), "number of point sampling for tile visibility algo", GF_PROP_UINT, "30", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     902             :         { 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},
     903             :         { OFFS(tvtd), "debug tiles and full coverage SRD\n"
     904             :                 "- off: regular draw\n"
     905             :                 "- partial: only displaying partial tiles, not the full sphere video\n"
     906             :                 "- full: only display the full sphere video", GF_PROP_UINT, "off", "off|partial|full", GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     907             :         { 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},
     908             :         { OFFS(fov), "default field of view for VR", GF_PROP_FLOAT, "1.570796326794897", NULL, GF_FS_ARG_UPDATE},
     909             :         { OFFS(vertshader), "path to vertex shader file", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT },
     910             :         { OFFS(fragshader), "path to fragment shader file", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT },
     911             : #endif
     912             : 
     913             : #ifdef GF_SR_USE_DEPTH
     914             :         { OFFS(autocal), "auto callibration of znear/zfar in depth rendering mode", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     915             :         { OFFS(dispdepth), "display depth, negative value uses default screen height", GF_PROP_SINT, "-1", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     916             :         { 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},
     917             : #ifndef GPAC_DISABLE_3D
     918             :         { OFFS(focdist), "distance of focus point", GF_PROP_FLOAT, "0", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     919             : #endif
     920             : #endif
     921             : 
     922             : #ifdef GF_SR_USE_VIDEO_CACHE
     923             :         { 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},
     924             :         { 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},
     925             :         { 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},
     926             : #endif
     927             :         { 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},
     928             :         { OFFS(dpi), "default dpi if not indicated by video output", GF_PROP_VEC2I, "96x96", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     929             :         { OFFS(dbgpvr), "debug scene used by PVR addon", GF_PROP_FLOAT, "0", NULL, GF_FS_ARG_UPDATE|GF_FS_ARG_HINT_EXPERT},
     930             :         { OFFS(player), "set compositor in player mode, see filter help\n"
     931             :         "- no: regular mode\n"
     932             :         "- base: player mode\n"
     933             :         "- gui: player mode with GUI auto-start", GF_PROP_UINT, "no", "no|base|gui", GF_FS_ARG_HINT_EXPERT},
     934             :         { OFFS(noaudio), "disable audio output", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
     935             :         { OFFS(opfmt), "pixel format to use for output. Ignored in [-player]() mode", GF_PROP_PIXFMT, "none", NULL, GF_FS_ARG_HINT_EXPERT},
     936             :         { OFFS(drv), "indicate if graphics driver should be used\n"\
     937             :                                 "- no: never loads a graphics driver, software blitting is used, no 3D possible (in player mode, disables OpenGL)\n"\
     938             :                                 "- yes: always loads a graphics driver, output pixel format will be RGB (in player mode, same to `auto`)\n"\
     939             :                                 "- auto: decides based on the loaded content"\
     940             :                         , GF_PROP_UINT, "auto", "no|yes|auto", GF_FS_ARG_HINT_EXPERT},
     941             :         { OFFS(src), "URL of source content", GF_PROP_NAME, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
     942             : 
     943             :         { OFFS(gaze_x), "horizontal gaze coordinate (0=left, width=right)", GF_PROP_SINT, "0", NULL, GF_FS_ARG_HINT_EXPERT|GF_FS_ARG_UPDATE},
     944             :         { OFFS(gaze_y), "vertical gaze coordinate (0=top, height=bottom)", GF_PROP_SINT, "0", NULL, GF_FS_ARG_HINT_EXPERT|GF_FS_ARG_UPDATE},
     945             :         { OFFS(gazer_enabled), "enable gaze event dispatch", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT|GF_FS_ARG_UPDATE},
     946             :         {0}
     947             : };
     948             : 
     949             : static const GF_FilterCapability CompositorCaps[] =
     950             : {
     951             :         /*first cap bundle for explicitly loaded compositor: accepts audio and video as well as scene/od*/
     952             :         CAP_UINT(GF_CAPS_INPUT|GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_STREAM_TYPE, GF_STREAM_SCENE),
     953             :         CAP_UINT(GF_CAPS_INPUT|GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_STREAM_TYPE, GF_STREAM_OD),
     954             :         CAP_UINT(GF_CAPS_INPUT|GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_STREAM_TYPE, GF_STREAM_TEXT),
     955             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED|GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
     956             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT|GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
     957             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT|GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
     958             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT|GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_CODECID, GF_CODECID_RAW),
     959             :         {0},
     960             :         /*second cap bundle for dynmac loaded compositor: only accepts text/scene/od*/
     961             :         CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_TEXT),
     962             :         CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_SCENE),
     963             :         CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_OD),
     964             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
     965             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
     966             : };
     967             : 
     968             : 
     969             : const GF_FilterRegister CompositorFilterRegister = {
     970             :         .name = "compositor",
     971             :         GF_FS_SET_DESCRIPTION("Compositor")
     972             :         GF_FS_SET_HELP("The GPAC compositor allows mixing audio, video, text and graphics in a timed fashion.\n"
     973             :         "The compositor operates either in media-client or filter-only mode.\n"
     974             :         "\n"
     975             :         "# Media-client mode\n"
     976             :         "In this mode, the compositor acts as a pseudo-sink for the video side and creates its own output window.\n"
     977             :         "The video frames are dispatched to the output video pid in the form of frame pointers requiring later GPU read if used.\n"
     978             :         "The audio part acts as a regular filter, potentially mixing and resampling the audio inputs to generate its output.\n"
     979             :         "User events are directly processed by the filter in this mode.\n"
     980             :         "\n"
     981             :         "# Filter mode\n"
     982             :         "In this mode, the compositor acts as a regular filter generating frames based on the loaded scene.\n"
     983             :         "It will generate its outputs based on the input video frames and will not process any user event.\n"
     984             :         "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"
     985             :         "It will stop generating frames as soon as all input streams are done, unless extended/reduced by [-dur]().\n"
     986             :         "If audio streams are loaded, an audio output pid is created.\n"
     987             :         "\n"
     988             :         "The default output pixel format in filter mode is:\n"
     989             :         "- `rgb` when the filter is explicitly loaded by the application\n"
     990             :         "- `rgba` when the filter is loaded during a link resolution\n"
     991             :         "This can be changed by assigning the [-opfmt]() option.\n"
     992             :         "\n"
     993             :         "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"
     994             :         "When such a pid is associated to a `Background2D` node in BIFS (no SVG mapping yet), the compositor operates in passthrough mode.\n"
     995             :         "In this mode, only new input frames on the passthrough pid will generate new frames, and the scene clock matches the input packet time.\n"
     996             :         "The output size and pixel format will be set to the input size and pixel format, unless specified otherwise in the filter options.\n"
     997             :         "\n"
     998             :         "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"
     999             :         "In this case, inplace processing (rasterizing over the input frame data) will be used whenever allowed by input data.\n"\
    1000             :         "\n"
    1001             :         "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"
    1002             :         "\n"
    1003             :         "# Specific URL syntaxes\n"
    1004             :         "The compositor accepts any URL type supported by GPAC. It also accepts the following schemes for URLs:\n"
    1005             :         "- views:// : creates an auto-stereo scene of N views from `views://v1:.:vN`. vN can be any type of URL supported by GPAC.\n"
    1006             :         "- mosaic:// : creates a mosaic of N views from `mosaic://v1:.:vN`. vN can be any type of URL supported by GPAC.\n"
    1007             :         "\n"
    1008             :         "The compositor can act as a source filter when the [-src]() option is explicitly set, independently from the operating mode:\n"
    1009             :         "EX gpac compositor:src=source.mp4 vout\n"
    1010             :         "\n"
    1011             :         "The compositor can act as a source filter when the source url uses one of the compositor built-in protocol schemes:\n"
    1012             :         "EX gpac -i mosaic://URL1:URL2 vout\n"
    1013             :         "\n"
    1014             :         )
    1015             :         .private_size = sizeof(GF_Compositor),
    1016             :         .flags = GF_FS_REG_MAIN_THREAD,
    1017             :         .max_extra_pids = (u32) -1,
    1018             :         SETCAPS(CompositorCaps),
    1019             :         .args = CompositorArgs,
    1020             :         .initialize = compose_initialize,
    1021             :         .finalize = compose_finalize,
    1022             :         .process = compose_process,
    1023             :         .process_event = compose_process_event,
    1024             :         .configure_pid = compose_configure_pid,
    1025             :         .reconfigure_output = compose_reconfig_output,
    1026             :         .update_arg = compose_update_arg,
    1027             :         .probe_url = compose_probe_url,
    1028             : };
    1029             : 
    1030        2877 : const GF_FilterRegister *compose_filter_register(GF_FilterSession *session)
    1031             : {
    1032             :         u32 i=0;
    1033             :         u32 nb_args = sizeof(CompositorArgs) / sizeof(GF_FilterArgs) - 1;
    1034             : 
    1035      250299 :         for (i=0; i<nb_args; i++) {
    1036      247422 :                 if (!strcmp(CompositorArgs[i].arg_name, "afmt")) {
    1037        2877 :                         CompositorArgs[i].min_max_enum = gf_audio_fmt_all_names();
    1038             :                 }
    1039      244545 :                 else if (!strcmp(CompositorArgs[i].arg_name, "opfmt")) {
    1040        2877 :                         CompositorArgs[i].min_max_enum =  gf_pixel_fmt_all_names();
    1041             :                 }
    1042             :         }
    1043        2877 :         return &CompositorFilterRegister;
    1044             : }
    1045             : #else
    1046             : const GF_FilterRegister *compose_filter_register(GF_FilterSession *session)
    1047             : {
    1048             :         return NULL;
    1049             : }
    1050             : #endif // GPAC_DISABLE_PLAYER
    1051             : 

Generated by: LCOV version 1.13