Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2018-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / audio output 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 :
27 : #include <gpac/filters.h>
28 : #include <gpac/constants.h>
29 : #include <gpac/modules/audio_out.h>
30 : #include <gpac/thread.h>
31 :
32 : typedef struct
33 : {
34 : //options
35 : char *drv;
36 : u32 bnum, bdur, threaded, priority;
37 : Bool clock;
38 : GF_Fraction64 dur;
39 : Double speed, start;
40 : u32 vol, pan, buffer, mbuffer, rbuffer;
41 : GF_Fraction adelay;
42 :
43 : GF_FilterPid *pid;
44 : u32 sr, afmt, nb_ch, timescale;
45 : u64 ch_cfg;
46 : GF_AudioOutput *audio_out;
47 : GF_Thread *th;
48 : u32 audio_th_state;
49 : Bool needs_recfg, wait_recfg;
50 : u32 bytes_per_sample;
51 :
52 : u32 pck_offset;
53 : u64 first_cts;
54 : Bool aborted;
55 : u32 speed_set;
56 : GF_Filter *filter;
57 : Bool is_eos, in_error;
58 : Bool first_write_done;
59 :
60 : s64 pid_delay;
61 :
62 : Bool buffer_done, no_buffering;
63 : u64 hwdelay_us, totaldelay_us;
64 :
65 : u64 rebuffer;
66 : Bool do_seek;
67 : } GF_AudioOutCtx;
68 :
69 :
70 10 : void aout_reconfig(GF_AudioOutCtx *ctx)
71 : {
72 : u32 sr, afmt, old_afmt, nb_ch;
73 : u64 ch_cfg;
74 : GF_Err e = GF_OK;
75 10 : sr = ctx->sr;
76 :
77 10 : nb_ch = ctx->nb_ch;
78 10 : afmt = old_afmt = ctx->afmt;
79 10 : ch_cfg = ctx->ch_cfg;
80 :
81 : //config not ready, wait
82 10 : if (!nb_ch || !sr || !afmt) {
83 : //force a get_packet to trigger reconfigure
84 0 : gf_filter_pid_get_packet(ctx->pid);
85 0 : return;
86 : }
87 : //we only support packed audio at output
88 10 : if (afmt>GF_AUDIO_FMT_LAST_PACKED)
89 1 : afmt -= GF_AUDIO_FMT_LAST_PACKED;
90 :
91 10 : e = ctx->audio_out->Configure(ctx->audio_out, &sr, &nb_ch, &afmt, ch_cfg);
92 10 : if (e) {
93 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[AudioOut] Failed to configure audio output: %s\n", gf_error_to_string(e) ));
94 0 : afmt = GF_AUDIO_FMT_S16;
95 0 : sr = 44100;
96 0 : nb_ch = 2;
97 : }
98 10 : if (ctx->speed == FIX_ONE) ctx->speed_set = 1;
99 :
100 10 : if (ctx->vol<=100) {
101 8 : if (ctx->audio_out->SetVolume)
102 8 : ctx->audio_out->SetVolume(ctx->audio_out, ctx->vol);
103 8 : ctx->vol = 101;
104 : }
105 10 : if (ctx->pan<=100) {
106 8 : if (ctx->audio_out->SetPan)
107 8 : ctx->audio_out->SetPan(ctx->audio_out, ctx->pan);
108 8 : ctx->pan = 101;
109 : }
110 :
111 :
112 10 : if (ctx->sr * ctx->nb_ch * old_afmt == 0) {
113 0 : ctx->needs_recfg = GF_FALSE;
114 0 : ctx->wait_recfg = GF_FALSE;
115 0 : return;
116 : }
117 :
118 10 : if ((sr != ctx->sr) || (nb_ch!=ctx->nb_ch) || (afmt!=old_afmt) || !ctx->speed_set) {
119 3 : gf_filter_pid_negociate_property(ctx->pid, GF_PROP_PID_SAMPLE_RATE, &PROP_UINT(sr));
120 3 : gf_filter_pid_negociate_property(ctx->pid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT(afmt));
121 3 : gf_filter_pid_negociate_property(ctx->pid, GF_PROP_PID_NUM_CHANNELS, &PROP_UINT(nb_ch));
122 3 : gf_filter_pid_negociate_property(ctx->pid, GF_PROP_PID_AUDIO_SPEED, &PROP_DOUBLE(ctx->speed));
123 3 : ctx->speed_set = (ctx->speed==1.0) ? 1 : 2;
124 3 : ctx->needs_recfg = GF_FALSE;
125 : //drop all packets until next reconfig
126 3 : ctx->wait_recfg = GF_TRUE;
127 3 : ctx->sr = sr;
128 3 : ctx->nb_ch = nb_ch;
129 3 : ctx->afmt = afmt;
130 3 : ctx->ch_cfg = ch_cfg;
131 7 : } else if (e==GF_OK) {
132 7 : ctx->needs_recfg = GF_FALSE;
133 7 : ctx->wait_recfg = GF_FALSE;
134 : } else {
135 0 : if (!ctx->in_error) {
136 0 : ctx->in_error = GF_TRUE;
137 0 : gf_filter_abort(ctx->filter);
138 : }
139 : return;
140 : }
141 10 : ctx->bytes_per_sample = gf_audio_fmt_bit_depth(afmt) * nb_ch / 8;
142 10 : ctx->hwdelay_us = 0;
143 10 : if (ctx->audio_out->GetAudioDelay) {
144 10 : ctx->hwdelay_us = ctx->audio_out->GetAudioDelay(ctx->audio_out);
145 10 : ctx->hwdelay_us *= 1000;
146 10 : GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[AudioOut] Hardware delay is "LLU" us\n", ctx->hwdelay_us ));
147 : }
148 10 : ctx->totaldelay_us = 0;
149 10 : if (ctx->audio_out->GetTotalBufferTime) {
150 10 : ctx->totaldelay_us = ctx->audio_out->GetTotalBufferTime(ctx->audio_out);
151 10 : ctx->totaldelay_us *= 1000;
152 10 : GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[AudioOut] Total audio delay is "LLU" ms\n", ctx->totaldelay_us ));
153 : }
154 : }
155 :
156 0 : u32 aout_th_proc(void *p)
157 : {
158 : GF_AudioOutCtx *ctx = (GF_AudioOutCtx *) p;
159 :
160 0 : ctx->audio_th_state = 1;
161 :
162 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[AudioOut] Entering audio thread ID %d\n", gf_th_id() ));
163 :
164 0 : while (ctx->audio_th_state == 1) {
165 0 : if (ctx->needs_recfg) {
166 0 : aout_reconfig(ctx);
167 0 : } else if (ctx->pid) {
168 0 : ctx->audio_out->WriteAudio(ctx->audio_out);
169 : } else {
170 0 : gf_sleep(10);
171 : }
172 : }
173 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[AudioOut] Exiting audio thread\n"));
174 0 : ctx->audio_out->Shutdown(ctx->audio_out);
175 0 : ctx->audio_th_state = 3;
176 0 : return 0;
177 : }
178 :
179 :
180 36109682 : static u32 aout_fill_output(void *ptr, u8 *buffer, u32 buffer_size)
181 : {
182 : u32 done = 0;
183 : GF_AudioOutCtx *ctx = ptr;
184 : Bool is_first_pck = GF_TRUE;
185 :
186 36109682 : memset(buffer, 0, buffer_size);
187 36109682 : if (!ctx->pid || ctx->aborted) return 0;
188 36109681 : if (!ctx->speed) return 0;
189 :
190 36109682 : if (ctx->do_seek) {
191 : GF_FilterEvent evt;
192 :
193 0 : GF_FEVT_INIT(evt, GF_FEVT_STOP, ctx->pid);
194 0 : gf_filter_pid_send_event(ctx->pid, &evt);
195 :
196 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[AudioOut] Seek request to %f speed %f\n", ctx->start, ctx->speed));
197 0 : gf_filter_pid_init_play_event(ctx->pid, &evt, ctx->start, ctx->speed, "VideoOut");
198 0 : gf_filter_pid_send_event(ctx->pid, &evt);
199 :
200 : //reinit clock
201 0 : ctx->first_write_done = GF_FALSE;
202 0 : ctx->buffer_done = GF_FALSE;
203 0 : ctx->do_seek = GF_FALSE;
204 0 : ctx->pck_offset = 0;
205 : return 0;
206 : }
207 :
208 :
209 36109682 : if (!ctx->buffer_done) {
210 : u32 size;
211 : GF_FilterPacket *pck;
212 :
213 : //query full buffer duration in us
214 34336850 : u64 dur = gf_filter_pid_query_buffer_duration(ctx->pid, GF_FALSE);
215 :
216 34336849 : GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[AudioOut] buffer %d / %d ms\r", dur/1000, ctx->buffer));
217 :
218 : /*the compositor sends empty packets after its reconfiguration to check when the config is active
219 : we therefore probe the first packet before probing the buffer fullness*/
220 34336849 : pck = gf_filter_pid_get_packet(ctx->pid);
221 68673695 : if (!pck) return 0;
222 :
223 469 : if (! gf_filter_pck_is_blocking_ref(pck)) {
224 469 : if ((dur < ctx->buffer * 1000) && !gf_filter_pid_is_eos(ctx->pid))
225 : return 0;
226 3 : gf_filter_pck_get_data(pck, &size);
227 3 : if (!size) {
228 0 : gf_filter_pid_drop_packet(ctx->pid);
229 0 : return 0;
230 : }
231 : //check the decoder output is full (avoids initial underrun)
232 3 : if (gf_filter_pid_query_buffer_duration(ctx->pid, GF_TRUE)==0)
233 : return 0;
234 : }
235 3 : ctx->buffer_done = GF_TRUE;
236 3 : if (ctx->rebuffer) {
237 0 : GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[AudioOut] rebuffer done in "LLU" ms\n", (u32) ( (gf_sys_clock_high_res() - ctx->rebuffer) /1000) ));
238 0 : ctx->rebuffer = 0;
239 : }
240 1772832 : } else if (ctx->rbuffer && !ctx->rebuffer) {
241 : //query full buffer duration in us
242 0 : u64 dur = gf_filter_pid_query_buffer_duration(ctx->pid, GF_FALSE);
243 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[AudioOut] buffer %d / %d ms\r", dur/1000, ctx->buffer));
244 0 : if ((dur < ctx->rbuffer * 1000) && !gf_filter_pid_has_seen_eos(ctx->pid)) {
245 0 : GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[AudioOut] buffer %u less than min threshold %u, rebuffering\n", (u32) (dur/1000), ctx->rbuffer));
246 0 : ctx->rebuffer = gf_sys_clock_high_res();
247 0 : ctx->buffer_done = GF_FALSE;
248 0 : return GF_OK;
249 : }
250 : #ifndef GPAC_DISABLE_LOG
251 1772832 : } else if (gf_log_tool_level_on(GF_LOG_MMIO, GF_LOG_DEBUG)) {
252 45026 : u64 dur = gf_filter_pid_query_buffer_duration(ctx->pid, GF_FALSE);
253 45026 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[AudioOut] buffer %d / %d ms\r", dur/1000, ctx->buffer));
254 : #endif
255 : }
256 :
257 : //do not throw underflow log util first packet is fetched
258 1772835 : if (ctx->first_write_done)
259 : is_first_pck = GF_FALSE;
260 :
261 1772859 : while (done < buffer_size) {
262 : const char *data;
263 : u32 size;
264 : u64 cts;
265 : s64 delay;
266 1772859 : GF_FilterPacket *pck = gf_filter_pid_get_packet(ctx->pid);
267 1772859 : if (!pck) {
268 1032914 : if (gf_filter_pid_is_eos(ctx->pid)) {
269 534142 : ctx->is_eos = GF_TRUE;
270 498772 : } else if (!is_first_pck) {
271 498731 : GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[AudioOut] buffer underflow\n"));
272 : }
273 1772835 : return done;
274 : }
275 739945 : ctx->is_eos = GF_FALSE;
276 739945 : if (ctx->needs_recfg) {
277 : return done;
278 : }
279 :
280 24 : delay = ctx->pid_delay;
281 24 : if (ctx->adelay.den)
282 24 : delay += ctx->adelay.num * (s32)ctx->timescale / (s32)ctx->adelay.den;
283 :
284 24 : cts = gf_filter_pck_get_cts(pck);
285 24 : if (delay >= 0) {
286 24 : cts += delay;
287 0 : } else if (cts < (u64) -delay) {
288 0 : gf_filter_pid_drop_packet(ctx->pid);
289 0 : continue;
290 : } else {
291 0 : cts -= (u64) -delay;
292 : }
293 :
294 24 : if (ctx->dur.num>0) {
295 24 : if (!ctx->first_cts) ctx->first_cts = cts+1;
296 :
297 24 : if ((cts - ctx->first_cts + 1) * ctx->dur.den > (u64) ctx->dur.num*ctx->timescale) {
298 0 : gf_filter_pid_drop_packet(ctx->pid);
299 0 : if (!ctx->aborted) {
300 : GF_FilterEvent evt;
301 0 : GF_FEVT_INIT(evt, GF_FEVT_STOP, ctx->pid);
302 0 : gf_filter_pid_send_event(ctx->pid, &evt);
303 :
304 0 : ctx->aborted = GF_TRUE;
305 : }
306 : return done;
307 : }
308 : }
309 :
310 24 : data = gf_filter_pck_get_data(pck, &size);
311 :
312 24 : if (!done && ctx->clock && data && size) {
313 : GF_Fraction64 timestamp;
314 24 : timestamp.num = cts;
315 24 : if (ctx->pck_offset)
316 0 : timestamp.num += ctx->pck_offset/ctx->bytes_per_sample;
317 :
318 24 : timestamp.num -= (ctx->hwdelay_us*ctx->timescale)/1000000;
319 24 : if (timestamp.num<0) timestamp.num = 0;
320 : timestamp.den = ctx->timescale;
321 24 : gf_filter_hint_single_clock(ctx->filter, gf_sys_clock_high_res(), timestamp);
322 24 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[AudioOut] At %d ms audio frame CTS "LLU" (compensated time %g s)\n", gf_sys_clock(), cts, ((Double)timestamp.num)/timestamp.den ));
323 : }
324 :
325 24 : if (data && !ctx->wait_recfg) {
326 : u32 nb_copy;
327 : assert(size >= ctx->pck_offset);
328 :
329 24 : nb_copy = (size - ctx->pck_offset);
330 24 : if (nb_copy + done > buffer_size) nb_copy = buffer_size - done;
331 24 : memcpy(buffer+done, data+ctx->pck_offset, nb_copy);
332 :
333 24 : if (!done && gf_filter_reporting_enabled(ctx->filter)) {
334 : char szStatus[1024];
335 0 : u64 bdur = gf_filter_pid_query_buffer_duration(ctx->pid, GF_FALSE);
336 0 : sprintf(szStatus, "%d Hz %d ch %s buffer %d / %d ms", ctx->sr, ctx->nb_ch, gf_audio_fmt_name(ctx->afmt), (u32) (bdur/1000), ctx->buffer);
337 0 : gf_filter_update_status(ctx->filter, -1, szStatus);
338 : }
339 :
340 :
341 24 : done += nb_copy;
342 24 : ctx->first_write_done = GF_TRUE;
343 : is_first_pck = GF_FALSE;
344 24 : if (nb_copy + ctx->pck_offset < size) {
345 0 : ctx->pck_offset += nb_copy;
346 0 : return done;
347 : }
348 24 : ctx->pck_offset = 0;
349 : }
350 24 : gf_filter_pid_drop_packet(ctx->pid);
351 : }
352 : return done;
353 : }
354 :
355 8 : void aout_set_priority(GF_AudioOutCtx *ctx, u32 prio)
356 : {
357 8 : if (prio==ctx->priority) return;
358 8 : ctx->priority = prio;
359 8 : if (ctx->th) gf_th_set_priority(ctx->th, (s32) ctx->priority);
360 8 : else if (ctx->audio_out->SelfThreaded && ctx->audio_out->SetPriority)
361 8 : ctx->audio_out->SetPriority(ctx->audio_out, ctx->priority);
362 : }
363 :
364 11 : static GF_Err aout_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
365 : {
366 : const GF_PropertyValue *p;
367 11 : GF_PropertyEntry *pe=NULL;
368 : u32 sr, nb_ch, afmt, timescale;
369 : u64 ch_cfg;
370 11 : GF_AudioOutCtx *ctx = (GF_AudioOutCtx *) gf_filter_get_udta(filter);
371 :
372 11 : if (is_remove) {
373 : assert(ctx->pid==pid);
374 0 : ctx->pid=NULL;
375 0 : return GF_OK;
376 : }
377 : assert(!ctx->pid || (ctx->pid==pid));
378 :
379 11 : if (!gf_filter_pid_check_caps(pid))
380 : return GF_NOT_SUPPORTED;
381 :
382 : sr = afmt = nb_ch = timescale = 0;
383 : ch_cfg = 0;
384 11 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
385 11 : if (p) timescale = p->value.uint;
386 :
387 11 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
388 11 : if (p) sr = p->value.uint;
389 11 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
390 11 : if (p) afmt = p->value.uint;
391 11 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_NUM_CHANNELS);
392 11 : if (p) nb_ch = p->value.uint;
393 11 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CHANNEL_LAYOUT);
394 11 : if (p) ch_cfg = p->value.longuint;
395 :
396 11 : if (ctx->audio_out->SetVolume) {
397 11 : p = gf_filter_pid_get_info(pid, GF_PROP_PID_AUDIO_VOLUME, &pe);
398 11 : if (p) ctx->audio_out->SetVolume(ctx->audio_out, p->value.uint);
399 : }
400 11 : if (ctx->audio_out->SetPan) {
401 11 : p = gf_filter_pid_get_info(pid, GF_PROP_PID_AUDIO_PAN, &pe);
402 11 : if (p) ctx->audio_out->SetPan(ctx->audio_out, p->value.uint);
403 : }
404 11 : gf_filter_release_property(pe);
405 :
406 11 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_PRIORITY);
407 11 : if (p) aout_set_priority(ctx, p->value.uint);
408 :
409 11 : if (ctx->first_cts && (ctx->timescale != timescale)) {
410 0 : ctx->first_cts-=1;
411 0 : ctx->first_cts *= timescale;
412 0 : ctx->first_cts /= ctx->timescale;
413 0 : ctx->first_cts+=1;
414 : }
415 11 : ctx->timescale = timescale;
416 :
417 11 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_PLAY_BUFFER);
418 11 : ctx->no_buffering = (p && !p->value.sint) ? GF_TRUE : GF_FALSE;
419 11 : if (ctx->no_buffering) ctx->buffer_done = GF_TRUE;
420 :
421 11 : if ((ctx->sr==sr) && (ctx->afmt == afmt) && (ctx->nb_ch == nb_ch) && (ctx->ch_cfg == ch_cfg)) {
422 1 : ctx->needs_recfg = GF_FALSE;
423 1 : ctx->wait_recfg = GF_FALSE;
424 1 : return GF_OK;
425 : }
426 :
427 : //whenever change of sample rate / format / channel, force buffer requirements and speed setup
428 10 : if ((ctx->sr!=sr) || (ctx->afmt != afmt) || (ctx->nb_ch != nb_ch)) {
429 : GF_FilterEvent evt;
430 8 : ctx->speed_set = 0;
431 :
432 : //set buffer reqs to bdur or 100 ms - we don't "buffer" in the filter, but this will allow dispatching
433 : //several input frames in the buffer (default being 1 pck / 1000 us max in buffers). Not doing so could cause
434 : //the session to end because input is blocked (no tasks posted) and output still holds a packet
435 8 : GF_FEVT_INIT(evt, GF_FEVT_BUFFER_REQ, pid);
436 8 : evt.buffer_req.max_buffer_us = ctx->buffer * 1000;
437 8 : if (ctx->bdur) {
438 8 : u64 b = ctx->bdur;
439 8 : b *= 1000;
440 8 : if (evt.buffer_req.max_buffer_us < b)
441 0 : evt.buffer_req.max_buffer_us = (u32) b;
442 : }
443 : //we have a max buffer, move our computed max to playout and setup max buffer
444 8 : if (ctx->mbuffer > evt.buffer_req.max_buffer_us / 1000 ) {
445 0 : evt.buffer_req.max_playout_us = evt.buffer_req.max_buffer_us;
446 0 : evt.buffer_req.max_buffer_us = ctx->mbuffer * 1000;
447 : }
448 : //we don't have a max buffer, set buffer requirements to PID only
449 : else {
450 8 : evt.buffer_req.pid_only = GF_TRUE;
451 : }
452 :
453 8 : gf_filter_pid_send_event(pid, &evt);
454 :
455 8 : gf_filter_pid_init_play_event(pid, &evt, ctx->start, ctx->speed, "AudioOut");
456 8 : gf_filter_pid_send_event(pid, &evt);
457 8 : ctx->speed = evt.play.speed;
458 8 : ctx->start = evt.play.start_range;
459 : }
460 :
461 10 : ctx->pid = pid;
462 10 : ctx->sr = sr;
463 10 : ctx->afmt = afmt;
464 10 : ctx->nb_ch = nb_ch;
465 10 : ctx->ch_cfg = ch_cfg;
466 :
467 10 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_PRIORITY);
468 10 : if (p) aout_set_priority(ctx, p->value.uint);
469 :
470 10 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DELAY);
471 10 : ctx->pid_delay = p ? p->value.longsint : 0;
472 :
473 10 : ctx->needs_recfg = GF_TRUE;
474 :
475 : //not threaded, request a task to restart audio (cannot do it during the audio callback)
476 10 : if (!ctx->th) gf_filter_post_process_task(filter);
477 : return GF_OK;
478 : }
479 :
480 10 : static GF_Err aout_initialize(GF_Filter *filter)
481 : {
482 : const char *sOpt;
483 : void *os_wnd_handler;
484 : GF_Err e;
485 10 : GF_AudioOutCtx *ctx = (GF_AudioOutCtx *) gf_filter_get_udta(filter);
486 :
487 10 : ctx->filter = filter;
488 :
489 :
490 10 : ctx->audio_out = (GF_AudioOutput *) gf_module_load(GF_AUDIO_OUTPUT_INTERFACE, ctx->drv);
491 : /*if not init we run with a NULL audio compositor*/
492 10 : if (!ctx->audio_out) {
493 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[AudioOut] No audio output modules found, cannot load audio output\n"));
494 : return GF_IO_ERR;
495 : }
496 10 : if (!gf_opts_get_key("core", "audio-output")) {
497 0 : gf_opts_set_key("core", "audio-output", ctx->audio_out->module_name);
498 : }
499 :
500 10 : ctx->audio_out->FillBuffer = aout_fill_output;
501 10 : ctx->audio_out->audio_renderer = ctx;
502 :
503 10 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[AudioOut] Setting up audio module %s\n", ctx->audio_out->module_name));
504 :
505 10 : if (!ctx->bnum || !ctx->bdur) ctx->bnum = ctx->bdur = 0;
506 :
507 10 : os_wnd_handler = NULL;
508 10 : sOpt = gf_opts_get_key("Temp", "OSWnd");
509 10 : if (sOpt) sscanf(sOpt, "%p", &os_wnd_handler);
510 10 : e = ctx->audio_out->Setup(ctx->audio_out, os_wnd_handler, ctx->bnum, ctx->bdur);
511 :
512 10 : if (e != GF_OK) {
513 2 : GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[AudioOut] Could not setup module %s\n", ctx->audio_out->module_name));
514 2 : gf_modules_close_interface((GF_BaseInterface *)ctx->audio_out);
515 2 : ctx->audio_out = NULL;
516 2 : return e;
517 : }
518 : /*only used for coverage for now*/
519 8 : if (ctx->audio_out->QueryOutputSampleRate) {
520 8 : u32 sr = 48000;
521 8 : u32 ch = 2;
522 8 : u32 bps = 16;
523 8 : ctx->audio_out->QueryOutputSampleRate(ctx->audio_out, &sr, &ch, &bps);
524 : }
525 8 : if (ctx->audio_out->SelfThreaded) {
526 0 : } else if (ctx->threaded) {
527 0 : ctx->th = gf_th_new("AudioOutput");
528 0 : gf_th_run(ctx->th, aout_th_proc, ctx);
529 : }
530 :
531 8 : aout_set_priority(ctx, GF_THREAD_PRIORITY_REALTIME);
532 :
533 8 : return GF_OK;
534 : }
535 :
536 9 : static void aout_finalize(GF_Filter *filter)
537 : {
538 9 : GF_AudioOutCtx *ctx = (GF_AudioOutCtx *) gf_filter_get_udta(filter);
539 :
540 : /*stop and shutdown*/
541 9 : if (ctx->audio_out) {
542 : /*kill audio thread*/
543 7 : if (ctx->th) {
544 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[AudioOut] stopping audio thread\n"));
545 0 : ctx->audio_th_state = 2;
546 0 : while (ctx->audio_th_state != 3) {
547 0 : gf_sleep(33);
548 : }
549 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[AudioOut] audio thread stopped\n"));
550 0 : gf_th_del(ctx->th);
551 : } else {
552 7 : ctx->audio_out->Shutdown(ctx->audio_out);
553 : }
554 7 : gf_modules_close_interface((GF_BaseInterface *)ctx->audio_out);
555 7 : ctx->audio_out = NULL;
556 : }
557 9 : }
558 :
559 191 : static GF_Err aout_process(GF_Filter *filter)
560 : {
561 191 : GF_AudioOutCtx *ctx = (GF_AudioOutCtx *) gf_filter_get_udta(filter);
562 :
563 191 : if (ctx->in_error)
564 : return GF_IO_ERR;
565 :
566 191 : if (!ctx->th && ctx->needs_recfg) {
567 10 : aout_reconfig(ctx);
568 : }
569 :
570 191 : if (ctx->th || ctx->audio_out->SelfThreaded) {
571 191 : if (ctx->is_eos) return GF_EOS;
572 139 : gf_filter_ask_rt_reschedule(filter, 100000);
573 139 : return GF_OK;
574 : }
575 :
576 0 : ctx->audio_out->WriteAudio(ctx->audio_out);
577 0 : return GF_OK;
578 : }
579 :
580 740401 : static Bool aout_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
581 : {
582 740401 : GF_AudioOutCtx *ctx = (GF_AudioOutCtx *) gf_filter_get_udta(filter);
583 740401 : if (!ctx->audio_out) return GF_TRUE;
584 :
585 740401 : switch (evt->base.type) {
586 2 : case GF_FEVT_PLAY:
587 2 : if (ctx->audio_out->Play) ctx->audio_out->Play(ctx->audio_out, evt->play.hw_buffer_reset ? 2 : 1);
588 : break;
589 2 : case GF_FEVT_STOP:
590 2 : if (ctx->audio_out->Play) ctx->audio_out->Play(ctx->audio_out, 0);
591 : break;
592 : default:
593 : break;
594 : }
595 : //cancel
596 : return GF_TRUE;
597 : }
598 :
599 0 : GF_Err aout_update_arg(GF_Filter *filter, const char *arg_name, const GF_PropertyValue *new_val)
600 : {
601 0 : GF_AudioOutCtx *ctx = (GF_AudioOutCtx *) gf_filter_get_udta(filter);
602 :
603 0 : if (!strcmp(arg_name, "start")) {
604 0 : if (!ctx->pid) return GF_OK;
605 0 : ctx->do_seek = GF_TRUE;
606 : }
607 0 : else if (!strcmp(arg_name, "speed")) {
608 0 : if (ctx->speed != new_val->value.number) {
609 0 : if (new_val->value.number==0) return GF_OK;
610 0 : if ((new_val->value.number==1) && ((ctx->speed==0) || (ctx->speed==1)))
611 : return GF_OK;
612 0 : if (ctx->speed_set != 2) {
613 0 : ctx->needs_recfg = GF_TRUE;
614 0 : ctx->speed_set = 0;
615 : } else {
616 : GF_FilterEvent evt;
617 0 : GF_FEVT_INIT(evt, GF_FEVT_SET_SPEED, ctx->pid)
618 0 : evt.play.speed = new_val->value.number;
619 0 : gf_filter_pid_send_event(ctx->pid, &evt);
620 : }
621 : }
622 : }
623 : return GF_OK;
624 : }
625 :
626 :
627 :
628 : #define OFFS(_n) #_n, offsetof(GF_AudioOutCtx, _n)
629 :
630 : static const GF_FilterArgs AudioOutArgs[] =
631 : {
632 : { OFFS(drv), "audio driver name", GF_PROP_NAME, NULL, NULL, 0},
633 : { OFFS(bnum), "number of audio buffers - 0 for auto", GF_PROP_UINT, "2", NULL, 0},
634 : { OFFS(bdur), "total duration of all buffers in ms - 0 for auto. The longer the audio buffer is, the longer the audio latency will be (pause/resume). The quality of fast forward audio playback will also be degradated when using large audio buffers", GF_PROP_UINT, "100", NULL, 0},
635 : { OFFS(threaded), "force dedicated thread creation if sound card driver is not threaded", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
636 : { OFFS(dur), "only play the specified duration", GF_PROP_FRACTION, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
637 : { OFFS(clock), "hint audio clock for this stream (reports system time and CTS), for other filters to use", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
638 : { OFFS(speed), "set playback speed. If speed is negative and start is 0, start is set to -1", GF_PROP_DOUBLE, "1.0", NULL, GF_FS_ARG_UPDATE},
639 : { OFFS(start), "set playback start offset. Negative value means percent of media duration with -1 equal to duration", GF_PROP_DOUBLE, "0.0", NULL, GF_FS_ARG_UPDATE},
640 : { OFFS(vol), "set default audio volume, as a percentage between 0 and 100", GF_PROP_UINT, "100", "0-100", GF_FS_ARG_UPDATE},
641 : { OFFS(pan), "set stereo pan, as a percentage between 0 and 100, 50 being centered", GF_PROP_UINT, "50", "0-100", GF_FS_ARG_UPDATE},
642 : { OFFS(buffer), "set playout buffer in ms", GF_PROP_UINT, "200", NULL, 0},
643 : { OFFS(mbuffer), "set max buffer occupancy in ms (if less than buffer, use buffer)", GF_PROP_UINT, "0", NULL, 0},
644 : { OFFS(rbuffer), "rebuffer trigger in ms (if 0 or more than buffer, disable rebuffering", GF_PROP_UINT, "0", NULL, GF_FS_ARG_UPDATE},
645 : { OFFS(adelay), "set audio delay in sec", GF_PROP_FRACTION, "0", NULL, GF_FS_ARG_HINT_ADVANCED|GF_FS_ARG_UPDATE},
646 : { OFFS(buffer_done), "buffer done indication (readonly)", GF_PROP_BOOL, NULL, NULL, GF_ARG_HINT_EXPERT},
647 : { OFFS(rebuffer), "time at which rebuffer started, 0 if not rebuffering (readonly)", GF_PROP_LUINT, NULL, NULL, GF_ARG_HINT_EXPERT},
648 : {0}
649 : };
650 :
651 : static const GF_FilterCapability AudioOutCaps[] =
652 : {
653 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
654 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_RAW),
655 : //we accept all audio formats, but will ask for input reconfiguration if sound card does not support
656 : };
657 :
658 :
659 : GF_FilterRegister AudioOutRegister = {
660 : .name = "aout",
661 : GF_FS_SET_DESCRIPTION("Audio output")
662 : GF_FS_SET_HELP("This filter outputs a single uncompressed audio PID to a sound card or other audio output device.")
663 : .private_size = sizeof(GF_AudioOutCtx),
664 : .args = AudioOutArgs,
665 : SETCAPS(AudioOutCaps),
666 : .initialize = aout_initialize,
667 : .finalize = aout_finalize,
668 : .configure_pid = aout_configure_pid,
669 : .process = aout_process,
670 : .process_event = aout_process_event,
671 : .update_arg = aout_update_arg
672 : };
673 :
674 2877 : const GF_FilterRegister *aout_register(GF_FilterSession *session)
675 : {
676 2877 : return &AudioOutRegister;
677 : }
678 :
|