/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) |