Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2012
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 :
27 : #include "nodes_stacks.h"
28 :
29 : #ifndef GPAC_DISABLE_VRML
30 :
31 :
32 190 : static void audioclip_activate(AudioClipStack *st, M_AudioClip *ac)
33 : {
34 190 : if (gf_sc_audio_open(&st->input, &ac->url, 0, -1, GF_FALSE) != GF_OK) {
35 184 : st->failure = GF_TRUE;
36 184 : return;
37 : }
38 6 : ac->isActive = 1;
39 6 : gf_node_event_out((GF_Node *)ac, 7/*"isActive"*/);
40 :
41 6 : gf_mo_set_speed(st->input.stream, st->input.speed);
42 : /*traverse all graph to get parent audio group*/
43 6 : gf_sc_invalidate(st->input.compositor, NULL);
44 : }
45 :
46 : static void audioclip_deactivate(AudioClipStack *st, M_AudioClip *ac)
47 : {
48 1 : gf_sc_audio_stop(&st->input);
49 1 : ac->isActive = 0;
50 1 : gf_node_event_out((GF_Node *)ac, 7/*"isActive"*/);
51 :
52 1 : st->time_handle.needs_unregister = GF_TRUE;
53 : }
54 :
55 2091 : static void audioclip_traverse(GF_Node *node, void *rs, Bool is_destroy)
56 : {
57 : GF_TraverseState *tr_state = (GF_TraverseState *)rs;
58 : M_AudioClip *ac = (M_AudioClip *)node;
59 2091 : AudioClipStack *st = (AudioClipStack *)gf_node_get_private(node);
60 :
61 2091 : if (is_destroy) {
62 218 : gf_sc_audio_predestroy(&st->input);
63 218 : if (st->time_handle.is_registered) {
64 34 : gf_sc_unregister_time_node(st->input.compositor, &st->time_handle);
65 : }
66 218 : gf_free(st);
67 218 : return;
68 : }
69 1873 : if (st->failure) return;
70 :
71 : /*check end of stream*/
72 480 : if (st->input.stream && st->input.stream_finished) {
73 6 : if (gf_mo_get_loop(st->input.stream, ac->loop)) {
74 6 : gf_sc_audio_restart(&st->input);
75 0 : } else if (ac->isActive && gf_mo_should_deactivate(st->input.stream)) {
76 : /*deactivate*/
77 : audioclip_deactivate(st, ac);
78 : }
79 : }
80 480 : if (ac->isActive) {
81 469 : gf_sc_audio_register(&st->input, (GF_TraverseState*)rs);
82 : }
83 480 : if (st->set_duration && st->input.stream && st->input.stream->odm) {
84 5 : ac->duration_changed = gf_mo_get_duration(st->input.stream);
85 5 : gf_node_event_out(node, 6/*"duration_changed"*/);
86 5 : st->set_duration = GF_FALSE;
87 : }
88 :
89 : /*store mute flag*/
90 480 : st->input.is_muted = tr_state->switched_off;
91 : }
92 :
93 1070 : static void audioclip_update_time(GF_TimeNode *tn)
94 : {
95 : Double time;
96 1070 : M_AudioClip *ac = (M_AudioClip *)tn->udta;
97 1070 : AudioClipStack *st = (AudioClipStack *)gf_node_get_private((GF_Node*)tn->udta);
98 :
99 1070 : if (st->failure) {
100 184 : st->time_handle.needs_unregister = GF_TRUE;
101 184 : return;
102 : }
103 886 : if (! ac->isActive) {
104 190 : st->start_time = ac->startTime;
105 190 : st->input.speed = ac->pitch;
106 : }
107 886 : time = gf_node_get_scene_time((GF_Node*)tn->udta);
108 886 : if ((time<st->start_time) || (st->start_time<0)) return;
109 :
110 886 : if (ac->isActive) {
111 696 : if ( (ac->stopTime > st->start_time) && (time>=ac->stopTime)) {
112 : audioclip_deactivate(st, ac);
113 : return;
114 : }
115 : }
116 885 : if (!ac->isActive) audioclip_activate(st, ac);
117 : }
118 :
119 :
120 218 : void compositor_init_audioclip(GF_Compositor *compositor, GF_Node *node)
121 : {
122 : AudioClipStack *st;
123 218 : GF_SAFEALLOC(st, AudioClipStack);
124 218 : if (!st) {
125 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate style group stack\n"));
126 : return;
127 : }
128 218 : gf_sc_audio_setup(&st->input, compositor, node);
129 :
130 218 : st->time_handle.UpdateTimeNode = audioclip_update_time;
131 218 : st->time_handle.udta = node;
132 218 : st->set_duration = GF_TRUE;
133 :
134 218 : gf_node_set_private(node, st);
135 218 : gf_node_set_callback_function(node, audioclip_traverse);
136 218 : gf_sc_register_time_node(compositor, &st->time_handle);
137 : }
138 :
139 :
140 4 : void compositor_audioclip_modified(GF_Node *node)
141 : {
142 : M_AudioClip *ac = (M_AudioClip *)node;
143 4 : AudioClipStack *st = (AudioClipStack *) gf_node_get_private(node);
144 4 : if (!st) return;
145 :
146 4 : st->failure = GF_FALSE;
147 :
148 : /*MPEG4 spec is not clear about that , so this is not forbidden*/
149 4 : if (st->input.is_open) {
150 3 : if (gf_sc_audio_check_url(&st->input, &ac->url)) {
151 1 : gf_sc_audio_stop(&st->input);
152 1 : gf_sc_audio_open(&st->input, &ac->url, 0, -1, GF_FALSE);
153 : /*force unregister to resetup audio cfg*/
154 1 : gf_sc_audio_unregister(&st->input);
155 1 : gf_sc_invalidate(st->input.compositor, NULL);
156 : }
157 : }
158 :
159 : //update state if we're active
160 4 : if (ac->isActive) {
161 3 : audioclip_update_time(&st->time_handle);
162 : /*we're no longer active fon't check for reactivation*/
163 3 : if (!ac->isActive) return;
164 : }
165 :
166 : /*make sure we are still registered*/
167 3 : if (!st->time_handle.is_registered && !st->time_handle.needs_unregister)
168 1 : gf_sc_register_time_node(st->input.compositor, &st->time_handle);
169 : else
170 2 : st->time_handle.needs_unregister = GF_FALSE;
171 : }
172 :
173 :
174 373 : static void audiosource_activate(AudioSourceStack *st, M_AudioSource *as)
175 : {
176 373 : if (gf_sc_audio_open(&st->input, &as->url, 0, -1, GF_FALSE) != GF_OK)
177 : return;
178 13 : st->is_active = GF_TRUE;
179 13 : gf_mo_set_speed(st->input.stream, st->input.speed);
180 : /*traverse all graph to get parent audio group*/
181 13 : gf_sc_invalidate(st->input.compositor, NULL);
182 : }
183 :
184 : static void audiosource_deactivate(AudioSourceStack *st, M_AudioSource *as)
185 : {
186 2 : gf_sc_audio_stop(&st->input);
187 2 : st->is_active = GF_FALSE;
188 2 : st->time_handle.needs_unregister = GF_TRUE;
189 : }
190 :
191 1195 : static void audiosource_traverse(GF_Node *node, void *rs, Bool is_destroy)
192 : {
193 : GF_TraverseState*tr_state = (GF_TraverseState*)rs;
194 : M_AudioSource *as = (M_AudioSource *)node;
195 1195 : AudioSourceStack *st = (AudioSourceStack *)gf_node_get_private(node);
196 :
197 :
198 1195 : if (is_destroy) {
199 16 : gf_sc_audio_predestroy(&st->input);
200 16 : if (st->time_handle.is_registered) {
201 15 : gf_sc_unregister_time_node(st->input.compositor, &st->time_handle);
202 : }
203 16 : gf_free(st);
204 16 : return;
205 : }
206 :
207 :
208 : /*check end of stream*/
209 1179 : if (st->input.stream && st->input.stream_finished) {
210 35 : if (gf_mo_get_loop(st->input.stream, GF_FALSE)) {
211 0 : gf_sc_audio_restart(&st->input);
212 35 : } else if (st->is_active && gf_mo_should_deactivate(st->input.stream)) {
213 : /*deactivate*/
214 : audiosource_deactivate(st, as);
215 : }
216 : }
217 1179 : if (st->is_active) {
218 1055 : gf_sc_audio_register(&st->input, (GF_TraverseState*)rs);
219 : }
220 :
221 : /*store mute flag*/
222 1179 : st->input.is_muted = tr_state->switched_off;
223 : }
224 :
225 1919 : static void audiosource_update_time(GF_TimeNode *tn)
226 : {
227 : Double time;
228 1919 : M_AudioSource *as = (M_AudioSource *)tn->udta;
229 1919 : AudioSourceStack *st = (AudioSourceStack *)gf_node_get_private((GF_Node*)tn->udta);
230 :
231 1919 : if (! st->is_active) {
232 373 : st->start_time = as->startTime;
233 373 : st->input.speed = as->speed;
234 : }
235 1919 : time = gf_node_get_scene_time((GF_Node*)tn->udta);
236 1919 : if ((time<st->start_time) || (st->start_time<0)) return;
237 :
238 1919 : if (st->input.input_ifce.GetSpeed(st->input.input_ifce.callback) && st->is_active) {
239 1539 : if ( (as->stopTime > st->start_time) && (time>=as->stopTime)) {
240 : audiosource_deactivate(st, as);
241 : return;
242 : }
243 : }
244 1917 : if (!st->is_active) audiosource_activate(st, as);
245 : }
246 :
247 :
248 16 : void compositor_init_audiosource(GF_Compositor *compositor, GF_Node *node)
249 : {
250 : AudioSourceStack *st;
251 16 : GF_SAFEALLOC(st, AudioSourceStack);
252 16 : if (!st) {
253 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate style group stack\n"));
254 : return;
255 : }
256 16 : gf_sc_audio_setup(&st->input, compositor, node);
257 :
258 16 : st->time_handle.UpdateTimeNode = audiosource_update_time;
259 16 : st->time_handle.udta = node;
260 :
261 16 : gf_node_set_private(node, st);
262 16 : gf_node_set_callback_function(node, audiosource_traverse);
263 16 : gf_sc_register_time_node(compositor, &st->time_handle);
264 : }
265 :
266 :
267 6 : void compositor_audiosource_modified(GF_Node *node)
268 : {
269 : M_AudioSource *as = (M_AudioSource *)node;
270 6 : AudioSourceStack *st = (AudioSourceStack *) gf_node_get_private(node);
271 6 : if (!st) return;
272 :
273 : /*MPEG4 spec is not clear about that , so this is not forbidden*/
274 6 : if (gf_sc_audio_check_url(&st->input, &as->url)) {
275 1 : if (st->input.is_open) gf_sc_audio_stop(&st->input);
276 : /*force unregister to resetup audio cfg*/
277 1 : gf_sc_audio_unregister(&st->input);
278 1 : gf_sc_invalidate(st->input.compositor, NULL);
279 :
280 1 : if (st->is_active) gf_sc_audio_open(&st->input, &as->url, 0, -1, GF_FALSE);
281 : }
282 :
283 : //update state if we're active
284 6 : if (st->is_active) {
285 3 : audiosource_update_time(&st->time_handle);
286 3 : if (!st->is_active) return;
287 : }
288 :
289 : /*make sure we are still registered*/
290 5 : if (!st->time_handle.is_registered && !st->time_handle.needs_unregister)
291 1 : gf_sc_register_time_node(st->input.compositor, &st->time_handle);
292 : else
293 4 : st->time_handle.needs_unregister = GF_FALSE;
294 : }
295 :
296 :
297 : typedef struct
298 : {
299 : AUDIO_GROUP_NODE
300 :
301 : GF_TimeNode time_handle;
302 : Double start_time;
303 : Bool set_duration;
304 : /*AudioBuffer mixes its children*/
305 : GF_AudioMixer *am;
306 : Bool is_init, is_muted;
307 : /*buffer audio data*/
308 : char *buffer;
309 : u32 buffer_size;
310 :
311 : Bool done;
312 : /*read/write position in buffer and associated read time (CTS)*/
313 : u32 read_pos, write_pos, cur_cts;
314 : /*list of audio children after a traverse*/
315 : GF_List *new_inputs;
316 : } AudioBufferStack;
317 :
318 :
319 :
320 : /*we have no choice but always browsing the children, since a src can be replaced by a new one
321 : without the parent being modified. We just collect the src and check against the current mixer inputs
322 : to reset the mixer or not - the spec is not clear about that btw, shall rebuffering happen if a source is modified or not ...*/
323 40 : static void audiobuffer_traverse(GF_Node *node, void *rs, Bool is_destroy)
324 : {
325 : u32 j;
326 : Bool update_mixer;
327 : GF_ChildNodeItem *l;
328 : GF_AudioGroup *parent;
329 40 : AudioBufferStack *st = (AudioBufferStack *)gf_node_get_private(node);
330 : M_AudioBuffer *ab = (M_AudioBuffer *)node;
331 : GF_TraverseState*tr_state = (GF_TraverseState*) rs;
332 :
333 40 : if (is_destroy) {
334 5 : gf_sc_audio_unregister(&st->output);
335 5 : if (st->time_handle.is_registered)
336 5 : gf_sc_unregister_time_node(st->output.compositor, &st->time_handle);
337 :
338 5 : gf_mixer_del(st->am);
339 5 : if (st->buffer) gf_free(st->buffer);
340 5 : gf_list_del(st->new_inputs);
341 5 : gf_free(st);
342 5 : return;
343 : }
344 35 : parent = tr_state->audio_parent;
345 35 : tr_state->audio_parent = (GF_AudioGroup *) st;
346 35 : l = ab->children;
347 105 : while (l) {
348 35 : gf_node_traverse(l->node, tr_state);
349 35 : l = l->next;
350 : }
351 :
352 35 : gf_mixer_lock(st->am, GF_TRUE);
353 :
354 : /*if no new inputs don't change mixer config*/
355 35 : update_mixer = gf_list_count(st->new_inputs) ? GF_TRUE : GF_FALSE;
356 :
357 35 : if (gf_mixer_get_src_count(st->am) == gf_list_count(st->new_inputs)) {
358 0 : u32 count = gf_list_count(st->new_inputs);
359 : update_mixer = GF_FALSE;
360 0 : for (j=0; j<count; j++) {
361 0 : GF_AudioInput *cur = (GF_AudioInput *)gf_list_get(st->new_inputs, j);
362 0 : if (!gf_mixer_is_src_present(st->am, &cur->input_ifce)) {
363 : update_mixer = GF_TRUE;
364 : break;
365 : }
366 : }
367 : }
368 :
369 35 : if (update_mixer) {
370 1 : gf_mixer_remove_all(st->am);
371 1 : gf_mixer_force_channel_out(st->am, ab->numChan);
372 : }
373 :
374 36 : while (gf_list_count(st->new_inputs)) {
375 1 : GF_AudioInput *src = (GF_AudioInput *)gf_list_get(st->new_inputs, 0);
376 1 : gf_list_rem(st->new_inputs, 0);
377 1 : if (update_mixer) gf_mixer_add_input(st->am, &src->input_ifce);
378 : }
379 :
380 35 : gf_mixer_lock(st->am, GF_FALSE);
381 35 : tr_state->audio_parent = parent;
382 :
383 : /*Note the audio buffer is ALWAYS registered until destroyed since buffer filling shall happen even when inactive*/
384 35 : if (!st->output.register_with_parent || !st->output.register_with_renderer)
385 35 : gf_sc_audio_register(&st->output, tr_state);
386 :
387 : /*store mute flag*/
388 35 : st->is_muted = tr_state->switched_off;
389 : }
390 :
391 :
392 :
393 5 : static void audiobuffer_activate(AudioBufferStack *st, M_AudioBuffer *ab)
394 : {
395 5 : ab->isActive = 1;
396 5 : gf_node_event_out((GF_Node *)ab, 17/*"isActive"*/);
397 : /*rerender all graph to get parent audio group*/
398 5 : gf_sc_invalidate(st->output.compositor, NULL);
399 5 : st->done = GF_FALSE;
400 5 : st->read_pos = 0;
401 5 : }
402 :
403 : static void audiobuffer_deactivate(AudioBufferStack *st, M_AudioBuffer *ab)
404 : {
405 0 : ab->isActive = 0;
406 0 : gf_node_event_out((GF_Node *)ab, 17/*"isActive"*/);
407 0 : st->time_handle.needs_unregister = GF_TRUE;
408 : }
409 :
410 512 : static void audiobuffer_update_time(GF_TimeNode *tn)
411 : {
412 : Double time;
413 512 : M_AudioBuffer *ab = (M_AudioBuffer *)tn->udta;
414 512 : AudioBufferStack *st = (AudioBufferStack *)gf_node_get_private((GF_Node*)tn->udta);
415 :
416 512 : if (! ab->isActive) {
417 5 : st->start_time = ab->startTime;
418 : }
419 512 : time = gf_node_get_scene_time((GF_Node*)tn->udta);
420 512 : if ((time<st->start_time) || (st->start_time<0)) return;
421 :
422 512 : if (ab->isActive) {
423 507 : if ( (ab->stopTime > st->start_time) && (time>=ab->stopTime)) {
424 : audiobuffer_deactivate(st, ab);
425 : return;
426 : }
427 : /*THIS IS NOT NORMATIVE*/
428 507 : if ( !ab->loop && st->done) {
429 : audiobuffer_deactivate(st, ab);
430 : return;
431 : }
432 : }
433 512 : if (!ab->isActive) audiobuffer_activate(st, ab);
434 : }
435 :
436 :
437 :
438 :
439 151 : static u8 *audiobuffer_fetch_frame(void *callback, u32 *size, u32 *planar_stride, u32 audio_delay_ms)
440 : {
441 : u32 blockAlign;
442 151 : AudioBufferStack *st = (AudioBufferStack *) gf_node_get_private( ((GF_AudioInput *) callback)->owner);
443 151 : M_AudioBuffer *ab = (M_AudioBuffer*)st->output.owner;
444 :
445 151 : if (!st->is_init) return NULL;
446 151 : if (!st->buffer) {
447 1 : st->done = GF_FALSE;
448 1 : st->buffer_size = (u32) ceil(FIX2FLT(ab->length) * gf_audio_fmt_bit_depth(st->output.input_ifce.afmt) * st->output.input_ifce.samplerate*st->output.input_ifce.chan/8);
449 1 : blockAlign = gf_mixer_get_block_align(st->am);
450 : /*BLOCK ALIGN*/
451 1 : while (st->buffer_size%blockAlign) st->buffer_size++;
452 1 : st->buffer = (char*)gf_malloc(sizeof(char) * st->buffer_size);
453 1 : memset(st->buffer, 0, sizeof(char) * st->buffer_size);
454 1 : st->read_pos = st->write_pos = 0;
455 : }
456 151 : if (st->done) return NULL;
457 :
458 : /*even if not active, fill the buffer*/
459 151 : if (st->write_pos < st->buffer_size) {
460 : u32 written;
461 : while (1) {
462 : /*just try to completely fill it*/
463 21 : written = gf_mixer_get_output(st->am, st->buffer + st->write_pos, st->buffer_size - st->write_pos, 0);
464 16 : if (!written) break;
465 5 : st->write_pos += written;
466 : assert(st->write_pos<=st->buffer_size);
467 : }
468 : }
469 : /*not playing*/
470 151 : if (! ab->isActive) return NULL;
471 151 : *size = st->write_pos - st->read_pos;
472 151 : return st->buffer + st->read_pos;
473 : }
474 :
475 145 : static void audiobuffer_release_frame(void *callback, u32 nb_bytes)
476 : {
477 145 : AudioBufferStack *st = (AudioBufferStack *) gf_node_get_private( ((GF_AudioInput *) callback)->owner);
478 145 : st->read_pos += nb_bytes;
479 : assert(st->read_pos<=st->write_pos);
480 145 : if (st->read_pos==st->write_pos) {
481 1 : if (st->write_pos<st->buffer_size) {
482 : /*reading faster than buffering - let's still attempt to fill the buffer*/
483 : #if 0
484 : st->write_pos = st->buffer_size;
485 : GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[AudioBuffer] done playing before buffer filling done\n"));
486 : #endif
487 1 : } else if ( ((M_AudioBuffer*)st->output.owner)->loop) {
488 1 : st->read_pos = 0;
489 : } else {
490 0 : st->done = GF_TRUE;
491 : }
492 : }
493 145 : }
494 :
495 :
496 181 : static Fixed audiobuffer_get_speed(void *callback)
497 : {
498 181 : M_AudioBuffer *ab = (M_AudioBuffer *) ((GF_AudioInput *) callback)->owner;
499 181 : return ab->pitch;
500 : }
501 :
502 150 : static Bool audiobuffer_get_volume(void *callback, Fixed *vol)
503 : {
504 : GF_AudioInput *ai = (GF_AudioInput *) callback;
505 150 : if (ai->snd->GetChannelVolume) {
506 150 : return ai->snd->GetChannelVolume(ai->snd->owner, vol);
507 : } else {
508 : // u32 i;
509 : // for (i=0; i<GF_AUDIO_MIXER_MAX_CHANNELS; i++) vol[i] = FIX_ONE;
510 : return GF_FALSE;
511 : }
512 : }
513 :
514 212 : static Bool audiobuffer_is_muted(void *callback)
515 : {
516 212 : AudioBufferStack *st = (AudioBufferStack *) gf_node_get_private( ((GF_AudioInput *) callback)->owner);
517 212 : return st->is_muted;
518 : }
519 :
520 188 : static Bool audiobuffer_get_config(GF_AudioInterface *aifc, Bool for_reconf)
521 : {
522 188 : AudioBufferStack *st = (AudioBufferStack *) gf_node_get_private( ((GF_AudioInput *) aifc->callback)->owner);
523 :
524 188 : if (gf_mixer_must_reconfig(st->am)) {
525 : Bool force_config = GF_TRUE;
526 6 : if (gf_mixer_reconfig(st->am)) {
527 0 : if (st->buffer) gf_free(st->buffer);
528 0 : st->buffer = NULL;
529 0 : st->buffer_size = 0;
530 : }
531 :
532 6 : gf_mixer_get_config(st->am, &aifc->samplerate, &aifc->chan, &aifc->afmt, &aifc->ch_layout);
533 : //we only work with packed formats
534 6 : switch (aifc->afmt) {
535 0 : case GF_AUDIO_FMT_U8P:
536 0 : aifc->afmt = GF_AUDIO_FMT_U8;
537 : break;
538 0 : case GF_AUDIO_FMT_S16P:
539 0 : aifc->afmt = GF_AUDIO_FMT_S16;
540 : break;
541 0 : case GF_AUDIO_FMT_S24P:
542 0 : aifc->afmt = GF_AUDIO_FMT_S24;
543 : break;
544 0 : case GF_AUDIO_FMT_S32P:
545 0 : aifc->afmt = GF_AUDIO_FMT_S32;
546 : break;
547 0 : case GF_AUDIO_FMT_FLTP:
548 0 : aifc->afmt = GF_AUDIO_FMT_FLT;
549 : break;
550 0 : case GF_AUDIO_FMT_DBLP:
551 0 : aifc->afmt = GF_AUDIO_FMT_DBL;
552 : break;
553 : default:
554 : force_config = GF_FALSE;
555 : break;
556 : }
557 : if (force_config) {
558 0 : gf_mixer_set_config(st->am, aifc->samplerate, aifc->chan, aifc->afmt, aifc->ch_layout);
559 : }
560 6 : st->is_init = (aifc->samplerate && aifc->chan && aifc->afmt) ? GF_TRUE : GF_FALSE;
561 : assert(st->is_init);
562 6 : if (!st->is_init) {
563 0 : aifc->samplerate = aifc->chan = aifc->afmt = 0;
564 0 : aifc->ch_layout = 0;
565 : }
566 : /*this will force invalidation*/
567 6 : return (for_reconf && st->is_init) ? GF_TRUE : GF_FALSE;
568 : }
569 182 : return st->is_init;
570 : }
571 :
572 1 : void audiobuffer_add_source(GF_AudioGroup *_this, GF_AudioInput *src)
573 : {
574 : AudioBufferStack *st = (AudioBufferStack *)_this;
575 1 : if (!src) return;
576 : /*just collect the input, reconfig is done once all children are rendered*/
577 1 : gf_list_add(st->new_inputs, src);
578 : }
579 :
580 :
581 0 : void setup_audiobuffer(GF_AudioInput *ai, GF_Compositor *compositor, GF_Node *node)
582 : {
583 : memset(ai, 0, sizeof(GF_AudioInput));
584 5 : ai->owner = node;
585 5 : ai->compositor = compositor;
586 : /*NEVER used for audio buffer*/
587 5 : ai->stream = NULL;
588 : /*setup io interface*/
589 5 : ai->input_ifce.FetchFrame = audiobuffer_fetch_frame;
590 5 : ai->input_ifce.ReleaseFrame = audiobuffer_release_frame;
591 5 : ai->input_ifce.GetConfig = audiobuffer_get_config;
592 5 : ai->input_ifce.GetChannelVolume = audiobuffer_get_volume;
593 5 : ai->input_ifce.GetSpeed = audiobuffer_get_speed;
594 5 : ai->input_ifce.IsMuted = audiobuffer_is_muted;
595 5 : ai->input_ifce.callback = ai;
596 5 : ai->speed = FIX_ONE;
597 0 : }
598 :
599 5 : void compositor_init_audiobuffer(GF_Compositor *compositor, GF_Node *node)
600 : {
601 : AudioBufferStack *st;
602 5 : GF_SAFEALLOC(st, AudioBufferStack);
603 5 : if (!st) {
604 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate audiobuffer stack\n"));
605 : return;
606 : }
607 :
608 : /*use our private input*/
609 5 : setup_audiobuffer(&st->output, compositor, node);
610 5 : st->add_source = audiobuffer_add_source;
611 :
612 5 : st->time_handle.UpdateTimeNode = audiobuffer_update_time;
613 5 : st->time_handle.udta = node;
614 5 : st->set_duration = GF_TRUE;
615 :
616 5 : st->am = gf_mixer_new(NULL);
617 5 : st->new_inputs = gf_list_new();
618 :
619 5 : gf_node_set_private(node, st);
620 5 : gf_node_set_callback_function(node, audiobuffer_traverse);
621 5 : gf_sc_register_time_node(compositor, &st->time_handle);
622 : }
623 :
624 :
625 1 : void compositor_audiobuffer_modified(GF_Node *node)
626 : {
627 : M_AudioBuffer *ab = (M_AudioBuffer *)node;
628 1 : AudioBufferStack *st = (AudioBufferStack *) gf_node_get_private(node);
629 1 : if (!st) return;
630 :
631 : //update state if we're active
632 1 : if (ab->isActive)
633 1 : audiobuffer_update_time(&st->time_handle);
634 :
635 : /*make sure we are still registered*/
636 1 : if (!st->time_handle.is_registered && !st->time_handle.needs_unregister)
637 0 : gf_sc_register_time_node(st->output.compositor, &st->time_handle);
638 : else
639 1 : st->time_handle.needs_unregister = GF_FALSE;
640 : }
641 :
642 : #endif /*GPAC_DISABLE_VRML*/
|