1 /******************************************************************************
2 Copyright (C) 2015 by Hugh Bailey <obs.jim@gmail.com>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 ******************************************************************************/
17
18 #include <inttypes.h>
19 #include "obs-internal.h"
20 #include "util/util_uint64.h"
21
22 struct ts_info {
23 uint64_t start;
24 uint64_t end;
25 };
26
27 #define DEBUG_AUDIO 0
28 #define DEBUG_LAGGED_AUDIO 0
29 #define MAX_BUFFERING_TICKS 45
30
push_audio_tree(obs_source_t * parent,obs_source_t * source,void * p)31 static void push_audio_tree(obs_source_t *parent, obs_source_t *source, void *p)
32 {
33 struct obs_core_audio *audio = p;
34
35 if (da_find(audio->render_order, &source, 0) == DARRAY_INVALID) {
36 obs_source_t *s = obs_source_get_ref(source);
37 if (s)
38 da_push_back(audio->render_order, &s);
39 }
40
41 UNUSED_PARAMETER(parent);
42 }
43
convert_time_to_frames(size_t sample_rate,uint64_t t)44 static inline size_t convert_time_to_frames(size_t sample_rate, uint64_t t)
45 {
46 return (size_t)util_mul_div64(t, sample_rate, 1000000000ULL);
47 }
48
mix_audio(struct audio_output_data * mixes,obs_source_t * source,size_t channels,size_t sample_rate,struct ts_info * ts)49 static inline void mix_audio(struct audio_output_data *mixes,
50 obs_source_t *source, size_t channels,
51 size_t sample_rate, struct ts_info *ts)
52 {
53 size_t total_floats = AUDIO_OUTPUT_FRAMES;
54 size_t start_point = 0;
55
56 if (source->audio_ts < ts->start || ts->end <= source->audio_ts)
57 return;
58
59 if (source->audio_ts != ts->start) {
60 start_point = convert_time_to_frames(
61 sample_rate, source->audio_ts - ts->start);
62 if (start_point == AUDIO_OUTPUT_FRAMES)
63 return;
64
65 total_floats -= start_point;
66 }
67
68 for (size_t mix_idx = 0; mix_idx < MAX_AUDIO_MIXES; mix_idx++) {
69 for (size_t ch = 0; ch < channels; ch++) {
70 register float *mix = mixes[mix_idx].data[ch];
71 register float *aud =
72 source->audio_output_buf[mix_idx][ch];
73 register float *end;
74
75 mix += start_point;
76 end = aud + total_floats;
77
78 while (aud < end)
79 *(mix++) += *(aud++);
80 }
81 }
82 }
83
ignore_audio(obs_source_t * source,size_t channels,size_t sample_rate,uint64_t start_ts)84 static bool ignore_audio(obs_source_t *source, size_t channels,
85 size_t sample_rate, uint64_t start_ts)
86 {
87 size_t num_floats = source->audio_input_buf[0].size / sizeof(float);
88 const char *name = obs_source_get_name(source);
89
90 if (!source->audio_ts && num_floats) {
91 #if DEBUG_LAGGED_AUDIO == 1
92 blog(LOG_DEBUG, "[src: %s] no timestamp, but audio available?",
93 name);
94 #endif
95 for (size_t ch = 0; ch < channels; ch++)
96 circlebuf_pop_front(&source->audio_input_buf[ch], NULL,
97 source->audio_input_buf[0].size);
98 source->last_audio_input_buf_size = 0;
99 return false;
100 }
101
102 if (num_floats) {
103 /* round up the number of samples to drop */
104 size_t drop =
105 (size_t)util_mul_div64(start_ts - source->audio_ts - 1,
106 sample_rate, 1000000000ULL) +
107 1;
108 if (drop > num_floats)
109 drop = num_floats;
110
111 #if DEBUG_LAGGED_AUDIO == 1
112 blog(LOG_DEBUG,
113 "[src: %s] ignored %" PRIu64 "/%" PRIu64 " samples", name,
114 (uint64_t)drop, (uint64_t)num_floats);
115 #endif
116 for (size_t ch = 0; ch < channels; ch++)
117 circlebuf_pop_front(&source->audio_input_buf[ch], NULL,
118 drop * sizeof(float));
119
120 source->last_audio_input_buf_size = 0;
121 source->audio_ts +=
122 util_mul_div64(drop, 1000000000ULL, sample_rate);
123 blog(LOG_DEBUG, "[src: %s] ts lag after ignoring: %" PRIu64,
124 name, start_ts - source->audio_ts);
125
126 /* rounding error, adjust */
127 if (source->audio_ts == (start_ts - 1))
128 source->audio_ts = start_ts;
129
130 /* source is back in sync */
131 if (source->audio_ts >= start_ts)
132 return true;
133 } else {
134 #if DEBUG_LAGGED_AUDIO == 1
135 blog(LOG_DEBUG, "[src: %s] no samples to ignore! ts = %" PRIu64,
136 name, source->audio_ts);
137 #endif
138 }
139
140 if (!source->audio_pending || num_floats) {
141 blog(LOG_WARNING,
142 "Source %s audio is lagging (over by %.02f ms) "
143 "at max audio buffering. Restarting source audio.",
144 name, (start_ts - source->audio_ts) / 1000000.);
145 }
146
147 source->audio_pending = true;
148 source->audio_ts = 0;
149 /* tell the timestamp adjustment code in source_output_audio_data to
150 * reset everything, and hopefully fix the timestamps */
151 source->timing_set = false;
152 return false;
153 }
154
discard_if_stopped(obs_source_t * source,size_t channels)155 static bool discard_if_stopped(obs_source_t *source, size_t channels)
156 {
157 size_t last_size;
158 size_t size;
159
160 last_size = source->last_audio_input_buf_size;
161 size = source->audio_input_buf[0].size;
162
163 if (!size)
164 return false;
165
166 /* if perpetually pending data, it means the audio has stopped,
167 * so clear the audio data */
168 if (last_size == size) {
169 if (!source->pending_stop) {
170 source->pending_stop = true;
171 #if DEBUG_AUDIO == 1
172 blog(LOG_DEBUG, "doing pending stop trick: '%s'",
173 source->context.name);
174 #endif
175 return false;
176 }
177
178 for (size_t ch = 0; ch < channels; ch++)
179 circlebuf_pop_front(&source->audio_input_buf[ch], NULL,
180 source->audio_input_buf[ch].size);
181
182 source->pending_stop = false;
183 source->audio_ts = 0;
184 source->last_audio_input_buf_size = 0;
185 #if DEBUG_AUDIO == 1
186 blog(LOG_DEBUG, "source audio data appears to have "
187 "stopped, clearing");
188 #endif
189 return true;
190 } else {
191 source->last_audio_input_buf_size = size;
192 return false;
193 }
194 }
195
196 #define MAX_AUDIO_SIZE (AUDIO_OUTPUT_FRAMES * sizeof(float))
197
discard_audio(struct obs_core_audio * audio,obs_source_t * source,size_t channels,size_t sample_rate,struct ts_info * ts)198 static inline void discard_audio(struct obs_core_audio *audio,
199 obs_source_t *source, size_t channels,
200 size_t sample_rate, struct ts_info *ts)
201 {
202 size_t total_floats = AUDIO_OUTPUT_FRAMES;
203 size_t size;
204 /* debug assert only */
205 UNUSED_PARAMETER(audio);
206
207 #if DEBUG_AUDIO == 1
208 bool is_audio_source = source->info.output_flags & OBS_SOURCE_AUDIO;
209 #endif
210
211 if (source->info.audio_render) {
212 source->audio_ts = 0;
213 return;
214 }
215
216 if (ts->end <= source->audio_ts) {
217 #if DEBUG_AUDIO == 1
218 blog(LOG_DEBUG,
219 "can't discard, source "
220 "timestamp (%" PRIu64 ") >= "
221 "end timestamp (%" PRIu64 ")",
222 source->audio_ts, ts->end);
223 #endif
224 return;
225 }
226
227 if (source->audio_ts < (ts->start - 1)) {
228 if (source->audio_pending &&
229 source->audio_input_buf[0].size < MAX_AUDIO_SIZE &&
230 discard_if_stopped(source, channels))
231 return;
232
233 #if DEBUG_AUDIO == 1
234 if (is_audio_source) {
235 blog(LOG_DEBUG,
236 "can't discard, source "
237 "timestamp (%" PRIu64 ") < "
238 "start timestamp (%" PRIu64 ")",
239 source->audio_ts, ts->start);
240 }
241
242 /* ignore_audio should have already run and marked this source
243 * pending, unless we *just* added buffering */
244 assert(audio->total_buffering_ticks < MAX_BUFFERING_TICKS ||
245 source->audio_pending || !source->audio_ts ||
246 audio->buffering_wait_ticks);
247 #endif
248 return;
249 }
250
251 if (source->audio_ts != ts->start &&
252 source->audio_ts != (ts->start - 1)) {
253 size_t start_point = convert_time_to_frames(
254 sample_rate, source->audio_ts - ts->start);
255 if (start_point == AUDIO_OUTPUT_FRAMES) {
256 #if DEBUG_AUDIO == 1
257 if (is_audio_source)
258 blog(LOG_DEBUG, "can't discard, start point is "
259 "at audio frame count");
260 #endif
261 return;
262 }
263
264 total_floats -= start_point;
265 }
266
267 size = total_floats * sizeof(float);
268
269 if (source->audio_input_buf[0].size < size) {
270 if (discard_if_stopped(source, channels))
271 return;
272
273 #if DEBUG_AUDIO == 1
274 if (is_audio_source)
275 blog(LOG_DEBUG, "can't discard, data still pending");
276 #endif
277 source->audio_ts = ts->end;
278 return;
279 }
280
281 for (size_t ch = 0; ch < channels; ch++)
282 circlebuf_pop_front(&source->audio_input_buf[ch], NULL, size);
283
284 source->last_audio_input_buf_size = 0;
285
286 #if DEBUG_AUDIO == 1
287 if (is_audio_source)
288 blog(LOG_DEBUG, "audio discarded, new ts: %" PRIu64, ts->end);
289 #endif
290
291 source->pending_stop = false;
292 source->audio_ts = ts->end;
293 }
294
add_audio_buffering(struct obs_core_audio * audio,size_t sample_rate,struct ts_info * ts,uint64_t min_ts,const char * buffering_name)295 static void add_audio_buffering(struct obs_core_audio *audio,
296 size_t sample_rate, struct ts_info *ts,
297 uint64_t min_ts, const char *buffering_name)
298 {
299 struct ts_info new_ts;
300 uint64_t offset;
301 uint64_t frames;
302 size_t total_ms;
303 size_t ms;
304 int ticks;
305
306 if (audio->total_buffering_ticks == MAX_BUFFERING_TICKS)
307 return;
308
309 if (!audio->buffering_wait_ticks)
310 audio->buffered_ts = ts->start;
311
312 offset = ts->start - min_ts;
313 frames = ns_to_audio_frames(sample_rate, offset);
314 ticks = (int)((frames + AUDIO_OUTPUT_FRAMES - 1) / AUDIO_OUTPUT_FRAMES);
315
316 audio->total_buffering_ticks += ticks;
317
318 if (audio->total_buffering_ticks >= MAX_BUFFERING_TICKS) {
319 ticks -= audio->total_buffering_ticks - MAX_BUFFERING_TICKS;
320 audio->total_buffering_ticks = MAX_BUFFERING_TICKS;
321 blog(LOG_WARNING, "Max audio buffering reached!");
322 }
323
324 ms = ticks * AUDIO_OUTPUT_FRAMES * 1000 / sample_rate;
325 total_ms = audio->total_buffering_ticks * AUDIO_OUTPUT_FRAMES * 1000 /
326 sample_rate;
327
328 blog(LOG_INFO,
329 "adding %d milliseconds of audio buffering, total "
330 "audio buffering is now %d milliseconds"
331 " (source: %s)\n",
332 (int)ms, (int)total_ms, buffering_name);
333 #if DEBUG_AUDIO == 1
334 blog(LOG_DEBUG,
335 "min_ts (%" PRIu64 ") < start timestamp "
336 "(%" PRIu64 ")",
337 min_ts, ts->start);
338 blog(LOG_DEBUG, "old buffered ts: %" PRIu64 "-%" PRIu64, ts->start,
339 ts->end);
340 #endif
341
342 new_ts.start =
343 audio->buffered_ts -
344 audio_frames_to_ns(sample_rate, audio->buffering_wait_ticks *
345 AUDIO_OUTPUT_FRAMES);
346
347 while (ticks--) {
348 const uint64_t cur_ticks = ++audio->buffering_wait_ticks;
349
350 new_ts.end = new_ts.start;
351 new_ts.start =
352 audio->buffered_ts -
353 audio_frames_to_ns(sample_rate,
354 cur_ticks * AUDIO_OUTPUT_FRAMES);
355
356 #if DEBUG_AUDIO == 1
357 blog(LOG_DEBUG, "add buffered ts: %" PRIu64 "-%" PRIu64,
358 new_ts.start, new_ts.end);
359 #endif
360
361 circlebuf_push_front(&audio->buffered_timestamps, &new_ts,
362 sizeof(new_ts));
363 }
364
365 *ts = new_ts;
366 }
367
audio_buffer_insuffient(struct obs_source * source,size_t sample_rate,uint64_t min_ts)368 static bool audio_buffer_insuffient(struct obs_source *source,
369 size_t sample_rate, uint64_t min_ts)
370 {
371 size_t total_floats = AUDIO_OUTPUT_FRAMES;
372 size_t size;
373
374 if (source->info.audio_render || source->audio_pending ||
375 !source->audio_ts) {
376 return false;
377 }
378
379 if (source->audio_ts != min_ts && source->audio_ts != (min_ts - 1)) {
380 size_t start_point = convert_time_to_frames(
381 sample_rate, source->audio_ts - min_ts);
382 if (start_point >= AUDIO_OUTPUT_FRAMES)
383 return false;
384
385 total_floats -= start_point;
386 }
387
388 size = total_floats * sizeof(float);
389
390 if (source->audio_input_buf[0].size < size) {
391 source->audio_pending = true;
392 return true;
393 }
394
395 return false;
396 }
397
find_min_ts(struct obs_core_data * data,uint64_t * min_ts)398 static inline const char *find_min_ts(struct obs_core_data *data,
399 uint64_t *min_ts)
400 {
401 obs_source_t *buffering_source = NULL;
402 struct obs_source *source = data->first_audio_source;
403 while (source) {
404 if (!source->audio_pending && source->audio_ts &&
405 source->audio_ts < *min_ts) {
406 *min_ts = source->audio_ts;
407 buffering_source = source;
408 }
409
410 source = (struct obs_source *)source->next_audio_source;
411 }
412 return buffering_source ? obs_source_get_name(buffering_source) : NULL;
413 }
414
mark_invalid_sources(struct obs_core_data * data,size_t sample_rate,uint64_t min_ts)415 static inline bool mark_invalid_sources(struct obs_core_data *data,
416 size_t sample_rate, uint64_t min_ts)
417 {
418 bool recalculate = false;
419
420 struct obs_source *source = data->first_audio_source;
421 while (source) {
422 recalculate |=
423 audio_buffer_insuffient(source, sample_rate, min_ts);
424 source = (struct obs_source *)source->next_audio_source;
425 }
426
427 return recalculate;
428 }
429
calc_min_ts(struct obs_core_data * data,size_t sample_rate,uint64_t * min_ts)430 static inline const char *calc_min_ts(struct obs_core_data *data,
431 size_t sample_rate, uint64_t *min_ts)
432 {
433 const char *buffering_name = find_min_ts(data, min_ts);
434 if (mark_invalid_sources(data, sample_rate, *min_ts))
435 buffering_name = find_min_ts(data, min_ts);
436 return buffering_name;
437 }
438
release_audio_sources(struct obs_core_audio * audio)439 static inline void release_audio_sources(struct obs_core_audio *audio)
440 {
441 for (size_t i = 0; i < audio->render_order.num; i++)
442 obs_source_release(audio->render_order.array[i]);
443 }
444
audio_callback(void * param,uint64_t start_ts_in,uint64_t end_ts_in,uint64_t * out_ts,uint32_t mixers,struct audio_output_data * mixes)445 bool audio_callback(void *param, uint64_t start_ts_in, uint64_t end_ts_in,
446 uint64_t *out_ts, uint32_t mixers,
447 struct audio_output_data *mixes)
448 {
449 struct obs_core_data *data = &obs->data;
450 struct obs_core_audio *audio = &obs->audio;
451 struct obs_source *source;
452 size_t sample_rate = audio_output_get_sample_rate(audio->audio);
453 size_t channels = audio_output_get_channels(audio->audio);
454 struct ts_info ts = {start_ts_in, end_ts_in};
455 size_t audio_size;
456 uint64_t min_ts;
457
458 da_resize(audio->render_order, 0);
459 da_resize(audio->root_nodes, 0);
460
461 circlebuf_push_back(&audio->buffered_timestamps, &ts, sizeof(ts));
462 circlebuf_peek_front(&audio->buffered_timestamps, &ts, sizeof(ts));
463 min_ts = ts.start;
464
465 audio_size = AUDIO_OUTPUT_FRAMES * sizeof(float);
466
467 #if DEBUG_AUDIO == 1
468 blog(LOG_DEBUG, "ts %llu-%llu", ts.start, ts.end);
469 #endif
470
471 /* ------------------------------------------------ */
472 /* build audio render order
473 * NOTE: these are source channels, not audio channels */
474 for (uint32_t i = 0; i < MAX_CHANNELS; i++) {
475 obs_source_t *source = obs_get_output_source(i);
476 if (source) {
477 obs_source_enum_active_tree(source, push_audio_tree,
478 audio);
479 push_audio_tree(NULL, source, audio);
480 da_push_back(audio->root_nodes, &source);
481 obs_source_release(source);
482 }
483 }
484
485 pthread_mutex_lock(&data->audio_sources_mutex);
486
487 source = data->first_audio_source;
488 while (source) {
489 push_audio_tree(NULL, source, audio);
490 source = (struct obs_source *)source->next_audio_source;
491 }
492
493 pthread_mutex_unlock(&data->audio_sources_mutex);
494
495 /* ------------------------------------------------ */
496 /* render audio data */
497 for (size_t i = 0; i < audio->render_order.num; i++) {
498 obs_source_t *source = audio->render_order.array[i];
499 obs_source_audio_render(source, mixers, channels, sample_rate,
500 audio_size);
501
502 /* if a source has gone backward in time and we can no
503 * longer buffer, drop some or all of its audio */
504 if (audio->total_buffering_ticks == MAX_BUFFERING_TICKS &&
505 source->audio_ts < ts.start) {
506 if (source->info.audio_render) {
507 blog(LOG_DEBUG,
508 "render audio source %s timestamp has "
509 "gone backwards",
510 obs_source_get_name(source));
511
512 /* just avoid further damage */
513 source->audio_pending = true;
514 #if DEBUG_AUDIO == 1
515 /* this should really be fixed */
516 assert(false);
517 #endif
518 } else {
519 pthread_mutex_lock(&source->audio_buf_mutex);
520 bool rerender = ignore_audio(source, channels,
521 sample_rate,
522 ts.start);
523 pthread_mutex_unlock(&source->audio_buf_mutex);
524
525 /* if we (potentially) recovered, re-render */
526 if (rerender)
527 obs_source_audio_render(source, mixers,
528 channels,
529 sample_rate,
530 audio_size);
531 }
532 }
533 }
534
535 /* ------------------------------------------------ */
536 /* get minimum audio timestamp */
537 pthread_mutex_lock(&data->audio_sources_mutex);
538 const char *buffering_name = calc_min_ts(data, sample_rate, &min_ts);
539 pthread_mutex_unlock(&data->audio_sources_mutex);
540
541 /* ------------------------------------------------ */
542 /* if a source has gone backward in time, buffer */
543 if (min_ts < ts.start)
544 add_audio_buffering(audio, sample_rate, &ts, min_ts,
545 buffering_name);
546
547 /* ------------------------------------------------ */
548 /* mix audio */
549 if (!audio->buffering_wait_ticks) {
550 for (size_t i = 0; i < audio->root_nodes.num; i++) {
551 obs_source_t *source = audio->root_nodes.array[i];
552
553 if (source->audio_pending)
554 continue;
555
556 pthread_mutex_lock(&source->audio_buf_mutex);
557
558 if (source->audio_output_buf[0][0] && source->audio_ts)
559 mix_audio(mixes, source, channels, sample_rate,
560 &ts);
561
562 pthread_mutex_unlock(&source->audio_buf_mutex);
563 }
564 }
565
566 /* ------------------------------------------------ */
567 /* discard audio */
568 pthread_mutex_lock(&data->audio_sources_mutex);
569
570 source = data->first_audio_source;
571 while (source) {
572 pthread_mutex_lock(&source->audio_buf_mutex);
573 discard_audio(audio, source, channels, sample_rate, &ts);
574 pthread_mutex_unlock(&source->audio_buf_mutex);
575
576 source = (struct obs_source *)source->next_audio_source;
577 }
578
579 pthread_mutex_unlock(&data->audio_sources_mutex);
580
581 /* ------------------------------------------------ */
582 /* release audio sources */
583 release_audio_sources(audio);
584
585 circlebuf_pop_front(&audio->buffered_timestamps, NULL, sizeof(ts));
586
587 *out_ts = ts.start;
588
589 if (audio->buffering_wait_ticks) {
590 audio->buffering_wait_ticks--;
591 return false;
592 }
593
594 UNUSED_PARAMETER(param);
595 return true;
596 }
597