Coverage Report

Created: 2026-06-15 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/compositor/texturing.c
Line
Count
Source
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Jean Le Feuvre
5
 *      Copyright (c) Telecom ParisTech 2000-2023
6
 *          All rights reserved
7
 *
8
 *  This file is part of GPAC / Scene Compositor sub-project
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 "texturing.h"
27
28
#if !defined(GPAC_DISABLE_COMPOSITOR)
29
30
#include "nodes_stacks.h"
31
32
static void update_texture_void(GF_TextureHandler *txh)
33
0
{
34
0
}
35
36
GF_EXPORT
37
void gf_sc_texture_setup(GF_TextureHandler *txh, GF_Compositor *compositor, GF_Node *owner)
38
0
{
39
0
  memset(txh, 0, sizeof(GF_TextureHandler));
40
0
  txh->owner = owner;
41
0
  txh->compositor = compositor;
42
  /*insert texture in reverse order, so that textures in sub documents/scenes are updated before parent ones*/
43
0
  if (gf_list_find(compositor->textures, txh)<0) {
44
0
    gf_list_insert(compositor->textures, txh, 0);
45
0
    compositor->texture_inserted = GF_TRUE;
46
0
  }
47
0
  if (!txh->update_texture_fcnt) txh->update_texture_fcnt = update_texture_void;
48
0
}
49
50
51
GF_EXPORT
52
void gf_sc_texture_destroy(GF_TextureHandler *txh)
53
0
{
54
0
#ifndef GPAC_DISABLE_THREADS
55
0
  GF_Compositor *compositor = txh->compositor;
56
0
  Bool lock = gf_mx_try_lock(compositor->mx);
57
0
#endif
58
59
0
  gf_sc_texture_release(txh);
60
0
  if (txh->is_open) gf_sc_texture_stop(txh);
61
0
  gf_list_del_item(txh->compositor->textures, txh);
62
63
0
#ifndef GPAC_DISABLE_THREADS
64
0
  if (lock) gf_mx_v(compositor->mx);
65
0
#endif
66
0
}
67
68
GF_EXPORT
69
Bool gf_sc_texture_check_url_change(GF_TextureHandler *txh, MFURL *url)
70
0
{
71
0
  if (!txh->stream) return url->count;
72
0
  return gf_mo_url_changed(txh->stream, url);
73
0
}
74
75
GF_EXPORT
76
GF_Err gf_sc_texture_open(GF_TextureHandler *txh, MFURL *url, Bool lock_scene_timeline)
77
0
{
78
0
  if (txh->is_open) return GF_BAD_PARAM;
79
80
  /*if existing texture in cache destroy it - we don't destroy it on stop to handle MovieTexture*/
81
0
  if (txh->tx_io) gf_sc_texture_release(txh);
82
83
  /*get media object*/
84
0
  txh->stream = gf_mo_register(txh->owner, url, lock_scene_timeline, 0);
85
  //consider the texture open to avoid repeatingly calling for open on bad URLs/OD
86
0
  txh->is_open = 1;
87
88
  /*bad/Empty URL*/
89
0
  if (!txh->stream) return GF_NOT_SUPPORTED;
90
91
0
  return GF_OK;
92
0
}
93
94
GF_EXPORT
95
GF_Err gf_sc_texture_play_from_to(GF_TextureHandler *txh, MFURL *url, Double start_offset, Double end_offset, Bool can_loop, Bool lock_scene_timeline)
96
0
{
97
0
  if (!txh->stream) {
98
0
    GF_Err e;
99
0
    if (!url) return GF_BAD_PARAM;
100
0
    e = gf_sc_texture_open(txh, url, lock_scene_timeline);
101
0
    if (e != GF_OK) return e;
102
0
  }
103
0
  txh->is_open = 1;
104
0
  txh->stream_finished = GF_FALSE;
105
  /*request play*/
106
0
  gf_mo_play(txh->stream, start_offset, end_offset, can_loop);
107
108
0
  txh->last_frame_time = (u32) (-1);
109
110
  /*request play*/
111
0
  return GF_OK;
112
0
}
113
114
GF_EXPORT
115
GF_Err gf_sc_texture_play(GF_TextureHandler *txh, MFURL *url)
116
0
{
117
0
  Double offset = 0;
118
0
  Bool loop = 0;
119
120
0
  if (txh->compositor->play_state != GF_STATE_PLAYING) {
121
0
    offset = gf_node_get_scene_time(txh->owner);
122
0
    loop = /*gf_mo_get_loop(gf_mo_register(txh->owner, url, 0, 0), 0)*/ 1;
123
0
  }
124
125
0
  return gf_sc_texture_play_from_to(txh, url, offset, -1, loop, 0);
126
0
}
127
128
129
GF_EXPORT
130
void gf_sc_texture_stop_no_unregister(GF_TextureHandler *txh)
131
0
{
132
0
  if (!txh->is_open) return;
133
  /*release texture WITHOUT dropping frame*/
134
0
  if (txh->needs_release) {
135
0
    gf_mo_release_data(txh->stream, 0xFFFFFFFF, 1);
136
0
    txh->needs_release = 0;
137
0
    txh->frame_ifce = NULL;
138
0
  }
139
0
  gf_sc_invalidate(txh->compositor, NULL);
140
0
  gf_mo_stop(&txh->stream);
141
0
  txh->data = NULL;
142
0
  txh->frame_ifce = NULL;
143
144
0
  txh->is_open = 0;
145
0
}
146
147
GF_EXPORT
148
void gf_sc_texture_stop(GF_TextureHandler *txh)
149
0
{
150
0
  if (!txh->is_open) return;
151
  /*release texture WITHOUT dropping frame*/
152
0
  if (txh->needs_release) {
153
0
    gf_mo_release_data(txh->stream, 0xFFFFFFFF, -1);
154
0
    txh->needs_release = 0;
155
0
    txh->frame_ifce = NULL;
156
0
  }
157
0
  gf_sc_invalidate(txh->compositor, NULL);
158
0
  gf_mo_stop(&txh->stream);
159
0
  if (!txh->stream) {
160
0
    txh->data = NULL;
161
0
    txh->frame_ifce = NULL;
162
0
  }
163
0
  txh->is_open = 0;
164
165
  /*and deassociate object*/
166
0
  gf_mo_unregister(txh->owner, txh->stream);
167
0
  txh->stream = NULL;
168
0
}
169
170
GF_EXPORT
171
void gf_sc_texture_restart(GF_TextureHandler *txh)
172
0
{
173
0
  if (!txh->is_open) return;
174
0
  gf_sc_texture_release_stream(txh);
175
0
  txh->stream_finished = GF_FALSE;
176
0
  gf_mo_restart(txh->stream);
177
0
}
178
179
180
static void setup_texture_object(GF_TextureHandler *txh, Bool private_media)
181
0
{
182
0
  if (!txh->tx_io) {
183
0
    gf_sc_texture_allocate(txh);
184
0
    if (!txh->tx_io) return;
185
186
0
    gf_mo_get_visual_info(txh->stream, &txh->width, &txh->height, &txh->stride, &txh->pixel_ar, &txh->pixelformat, &txh->is_flipped);
187
0
    gf_sc_texture_configure_conversion(txh);
188
189
0
    gf_pixel_get_size_info(txh->pixelformat, txh->width, txh->height, NULL, NULL, NULL, &txh->nb_planes, NULL);
190
191
0
    if (private_media) {
192
0
      txh->transparent = 1;
193
0
      txh->pixelformat = GF_PIXEL_ARGB;
194
0
      txh->flags |= GF_SR_TEXTURE_PRIVATE_MEDIA;
195
0
    } else {
196
0
      txh->transparent = 0;
197
0
      switch (txh->pixelformat) {
198
0
      case GF_PIXEL_ALPHAGREY:
199
0
      case GF_PIXEL_GREYALPHA:
200
0
      case GF_PIXEL_ARGB:
201
0
      case GF_PIXEL_RGBA:
202
0
      case GF_PIXEL_YUVA:
203
0
      case GF_PIXEL_RGBDS:
204
0
        txh->transparent = 1;
205
0
        break;
206
0
      }
207
0
    }
208
0
  }
209
0
}
210
211
212
GF_EXPORT
213
void gf_sc_texture_update_frame(GF_TextureHandler *txh, Bool disable_resync)
214
0
{
215
0
  Bool needs_reload = 0;
216
0
  u32 size, ts, push_time;
217
0
  s32 ms_until_pres, ms_until_next;
218
219
0
  if (!txh->stream) {
220
0
    txh->data = NULL;
221
0
    txh->frame_ifce = NULL;
222
0
    return;
223
0
  }
224
0
  if (txh->stream->config_changed) {
225
0
    txh->data = NULL;
226
0
    txh->frame_ifce = NULL;
227
0
  }
228
229
  /*already refreshed*/
230
0
  if ((txh->stream_finished && txh->tx_io) || txh->needs_refresh)
231
0
    return;
232
233
  /*should never happen!!*/
234
0
  if (txh->needs_release) {
235
0
    gf_mo_release_data(txh->stream, 0xFFFFFFFF, 0);
236
0
    txh->frame_ifce=NULL;
237
0
  }
238
239
  /*check init flag*/
240
0
  if (txh->stream->config_changed) {
241
0
    needs_reload = 1;
242
0
    txh->data = NULL;
243
0
    if (txh->tx_io) {
244
0
      gf_sc_texture_release(txh);
245
0
    }
246
0
  }
247
  //if first frame use 20ms as upload time
248
0
  push_time = txh->nb_frames ? txh->upload_time/txh->nb_frames : 20;
249
0
  txh->data = gf_mo_fetch_data(txh->stream, disable_resync ? GF_MO_FETCH : GF_MO_FETCH_RESYNC, push_time, &txh->stream_finished, &ts, &size, &ms_until_pres, &ms_until_next, &txh->frame_ifce, NULL);
250
251
0
  if (txh->stream->config_changed) {
252
0
    needs_reload = 1;
253
0
  } else if (txh->data && size && txh->size && (size != txh->size)) {
254
0
    needs_reload = 1;
255
0
  }
256
257
0
  if (needs_reload) {
258
    /*if we had a texture this means the object has changed - delete texture and resetup. Do not skip
259
    texture update as this may lead to an empty rendering pass (blank frame for this object), especially in DASH*/
260
0
    if (txh->tx_io) {
261
0
      gf_sc_texture_release(txh);
262
0
      txh->needs_refresh = 1;
263
0
    }
264
0
    txh->flags &= ~GF_SR_TEXTURE_DISABLE_BLIT;
265
266
0
  }
267
268
  /*if no frame or muted don't draw*/
269
0
  if (!txh->data && !txh->frame_ifce) {
270
0
    GF_LOG(txh->stream->connect_state ? GF_LOG_DEBUG : GF_LOG_INFO, GF_LOG_COMPOSE, ("[Texture %p] No output frame available \n", txh));
271
272
0
    if (txh->compositor->use_step_mode || !txh->compositor->player) {
273
0
      if (!txh->stream->connect_state && ((s32)txh->last_frame_time<0) ) {
274
        //if we have anything bending in the source graph, reset timeout
275
        //typical use case: a filter aggregates packets for a long time before dispatching
276
        //example: tileagg+dash (cf issue #1934)
277
        //warning, mo->odm can be null at this point (bifs & co)
278
0
        u64 buffered = 0;
279
0
        if (txh->stream->odm && txh->stream->odm->pid) {
280
0
          buffered = gf_filter_pid_query_buffer_duration(txh->stream->odm->pid, GF_FALSE);
281
0
          if (buffered) txh->stream->connect_state = MO_CONNECT_BUFFERING;
282
0
        }
283
284
0
        if (txh->compositor->timeout) {
285
0
          if (buffered || !txh->probe_time_ms) {
286
0
            txh->probe_time_ms = gf_sys_clock();
287
0
          } else if (gf_sys_clock() - txh->probe_time_ms > txh->compositor->timeout) {
288
0
            GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[Texture %p] No output frame in %d ms, considering stream not available\n", txh, txh->compositor->timeout));
289
0
            txh->stream->connect_state = MO_CONNECT_TIMEOUT;
290
0
            gf_sc_texture_stop_no_unregister(txh);
291
0
          }
292
0
        }
293
0
        txh->compositor->ms_until_next_frame = -1;
294
0
      }
295
0
    }
296
    /*TODO - check if this is needed */
297
0
    else if (txh->flags & GF_SR_TEXTURE_PRIVATE_MEDIA) {
298
      //txh->needs_refresh = 1;
299
0
      gf_sc_invalidate(txh->compositor, NULL);
300
0
    }
301
0
    return;
302
0
  }
303
304
0
  if (txh->compositor->frame_delay > ms_until_pres)
305
0
    txh->compositor->frame_delay = ms_until_pres;
306
307
  /*if setup and same frame return*/
308
0
  if (txh->tx_io && ((s32) txh->last_frame_time>=0) && (txh->stream_finished || (txh->last_frame_time==ts)) ) {
309
0
    gf_mo_release_data(txh->stream, 0xFFFFFFFF, 0);
310
0
    txh->needs_release = 0;
311
0
    if (!txh->stream_finished) {
312
0
      GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Texture %p] Same frame fetched (TS %u)\n", txh, ts));
313
0
      if (txh->compositor->ms_until_next_frame > ms_until_next)
314
0
        txh->compositor->ms_until_next_frame = ms_until_next;
315
0
    }
316
0
    return;
317
0
  }
318
0
  GF_LOG(GF_LOG_INFO, GF_LOG_COMPTIME, ("[Texture %p] Updated new frame at clock time %u (%u ms) time %u ms\n", txh, gf_clock_time(txh->stream->odm->ck), gf_sys_clock(), ts));
319
0
  txh->stream_finished = GF_FALSE;
320
0
  txh->needs_release = 1;
321
0
  txh->last_frame_time = ts;
322
0
  txh->size = size;
323
0
  if (gf_mo_is_muted(txh->stream)) return;
324
325
326
0
  if (txh->nb_frames) {
327
0
    s32 push_delay = txh->upload_time / txh->nb_frames;
328
0
    if (push_delay > ms_until_pres) ms_until_pres = 0;
329
0
    else ms_until_pres -= push_delay;
330
0
  }
331
332
0
  if (txh->compositor->ms_until_next_frame > ms_until_next)
333
0
    txh->compositor->ms_until_next_frame = ms_until_next;
334
335
0
  if (!txh->tx_io) {
336
0
    setup_texture_object(txh, 0);
337
0
  }
338
339
  
340
  
341
  /*try to push texture on graphics but don't complain if failure*/
342
0
  gf_sc_texture_set_data(txh);
343
344
0
  txh->needs_refresh = 1;
345
0
  gf_sc_invalidate(txh->compositor, NULL);
346
0
}
347
348
GF_EXPORT
349
void gf_sc_texture_release_stream(GF_TextureHandler *txh)
350
0
{
351
0
  txh->needs_refresh = 0;
352
0
  if (!txh->stream) return;
353
0
  if (txh->needs_release) {
354
0
    gf_mo_release_data(txh->stream, 0xFFFFFFFF, (txh->needs_release==2) ? 3 :0);
355
0
    if (txh->needs_release==2) {
356
0
      txh->last_frame_time = -1;
357
0
    }
358
0
    txh->needs_release = 0;
359
0
    txh->frame_ifce = NULL;
360
361
0
  }
362
0
  txh->stream->config_changed = GF_FALSE;
363
0
}
364
365
366
GF_EXPORT
367
GF_TextureHandler *gf_sc_texture_get_handler(GF_Node *n)
368
0
{
369
0
  if (!n) return NULL;
370
0
  switch (gf_node_get_tag(n)) {
371
0
#ifndef GPAC_DISABLE_VRML
372
0
  case TAG_MPEG4_ImageTexture:
373
0
  case TAG_MPEG4_CacheTexture:
374
0
    return it_get_texture(n);
375
0
  case TAG_MPEG4_MovieTexture:
376
0
    return mt_get_texture(n);
377
0
  case TAG_MPEG4_PixelTexture:
378
0
    return pt_get_texture(n);
379
380
0
  case TAG_MPEG4_CompositeTexture2D:
381
0
  case TAG_MPEG4_CompositeTexture3D:
382
0
    return compositor_get_composite_texture(n);
383
0
  case TAG_MPEG4_LinearGradient:
384
0
  case TAG_MPEG4_RadialGradient:
385
0
    return compositor_mpeg4_get_gradient_texture(n);
386
387
0
  case TAG_MPEG4_MatteTexture:
388
0
  {
389
0
    GF_TextureHandler *hdl = gf_sc_texture_get_handler( ((M_MatteTexture*)n)->surfaceB );
390
0
    if (hdl) hdl->matteTexture = n;
391
0
    return hdl;
392
0
  }
393
0
#endif /*GPAC_DISABLE_VRML*/
394
395
0
#ifndef GPAC_DISABLE_X3D
396
0
  case TAG_X3D_ImageTexture:
397
0
    return it_get_texture(n);
398
0
  case TAG_X3D_MovieTexture:
399
0
    return mt_get_texture(n);
400
0
  case TAG_X3D_PixelTexture:
401
0
    return pt_get_texture(n);
402
0
#endif
403
404
405
0
#ifndef GPAC_DISABLE_SVG
406
0
  case TAG_SVG_linearGradient:
407
0
  case TAG_SVG_radialGradient:
408
0
    return compositor_svg_get_gradient_texture(n);
409
0
  case TAG_SVG_image:
410
0
  case TAG_SVG_video:
411
0
    return compositor_svg_get_image_texture(n);
412
0
#endif
413
414
0
#ifndef GPAC_DISABLE_VRML
415
0
    case TAG_ProtoNode:
416
0
        return gf_sc_hardcoded_proto_get_texture_handler(n);
417
0
#endif
418
0
  default:
419
    return NULL;
420
0
  }
421
0
}
422
423
#endif //!defined(GPAC_DISABLE_COMPOSITOR)