/** @internal @This handles input. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to upump structure */ static void upipe_filter_ebur128_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_filter_ebur128 *upipe_filter_ebur128 = upipe_filter_ebur128_from_upipe(upipe); double loud = 0, lra = 0, global = 0; size_t samples; if (unlikely(!ubase_check(uref_sound_size(uref, &samples, NULL)))) { upipe_warn(upipe, "invalid sound buffer"); uref_free(uref); return; } const char *channel = NULL; const int16_t *buf = NULL; if (ubase_check(uref_sound_plane_iterate(uref, &channel)) && channel) { if (unlikely(!ubase_check(uref_sound_plane_read_int16_t(uref, channel, 0, -1, &buf)))) { upipe_warn(upipe, "error mapping sound buffer"); uref_free(uref); return; } if (unlikely((uintptr_t)buf & 1)) { upipe_warn(upipe, "unaligned buffer"); } ebur128_add_frames_short(upipe_filter_ebur128->st, buf, samples); uref_sound_plane_unmap(uref, channel, 0, -1); } ebur128_loudness_momentary(upipe_filter_ebur128->st, &loud); ebur128_loudness_range(upipe_filter_ebur128->st, &lra); ebur128_loudness_global(upipe_filter_ebur128->st, &global); uref_ebur128_set_momentary(uref, loud); uref_ebur128_set_lra(uref, lra); uref_ebur128_set_global(uref, global); upipe_verbose_va(upipe, "loud %f lra %f global %f", loud, lra, global); upipe_filter_ebur128_output(upipe, uref, upump_p); }
/** @internal @This handles input. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to upump structure */ static void upipe_filter_ebur128_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_filter_ebur128 *upipe_filter_ebur128 = upipe_filter_ebur128_from_upipe(upipe); double loud = 0, lra = 0, global = 0; if (unlikely(upipe_filter_ebur128->output_flow == NULL)) { upipe_err_va(upipe, "invalid input"); uref_free(uref); return; } size_t samples; uint8_t sample_size; if (unlikely(!ubase_check(uref_sound_size(uref, &samples, &sample_size)))) { upipe_warn(upipe, "invalid sound buffer"); uref_free(uref); return; } void *buf = NULL; const char *channel = NULL; if (upipe_filter_ebur128->planes == 1) { if (ubase_check(uref_sound_plane_iterate(uref, &channel)) && channel) { if (unlikely(!ubase_check(uref_sound_plane_read_void(uref, channel, 0, -1, (const void **)&buf)))) { upipe_warn(upipe, "error mapping sound buffer"); uref_free(uref); return; } } } else { buf = malloc(sample_size * upipe_filter_ebur128->channels * samples); if (buf == NULL) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); uref_free(uref); return; } if (!ubase_check(uref_sound_interleave(uref, (uint8_t *)buf, 0, samples, sample_size, upipe_filter_ebur128->planes))) { upipe_warn(upipe, "error mapping sound buffer"); uref_free(uref); return; } } if (unlikely((uintptr_t)buf & 1)) upipe_warn(upipe, "unaligned buffer"); switch (upipe_filter_ebur128->fmt) { case UPIPE_FILTER_EBUR128_SHORT: ebur128_add_frames_short(upipe_filter_ebur128->st, (short *)buf, samples); break; case UPIPE_FILTER_EBUR128_INT: ebur128_add_frames_int(upipe_filter_ebur128->st, (int *)buf, samples); break; case UPIPE_FILTER_EBUR128_FLOAT: ebur128_add_frames_float(upipe_filter_ebur128->st, (float *)buf, samples); break; case UPIPE_FILTER_EBUR128_DOUBLE: ebur128_add_frames_double(upipe_filter_ebur128->st, (double *)buf, samples); break; default: upipe_warn_va(upipe, "unknown sample format %d", upipe_filter_ebur128->fmt); break; } if (upipe_filter_ebur128->planes == 1) uref_sound_plane_unmap(uref, channel, 0, -1); else free(buf); ebur128_loudness_momentary(upipe_filter_ebur128->st, &loud); ebur128_loudness_range(upipe_filter_ebur128->st, &lra); ebur128_loudness_global(upipe_filter_ebur128->st, &global); uref_ebur128_set_momentary(uref, loud); uref_ebur128_set_lra(uref, lra); uref_ebur128_set_global(uref, global); upipe_verbose_va(upipe, "loud %f lra %f global %f", loud, lra, global); upipe_filter_ebur128_output(upipe, uref, upump_p); }
/** @internal @This handles data from sound allocator. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to pump that generated the buffer */ static void upipe_tblk_handle_sound(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_tblk *upipe_tblk = upipe_tblk_from_upipe(upipe); /* Always operate on the first channel plane. */ const char *channel = NULL; if (unlikely(uref->ubuf == NULL || !ubase_check(ubuf_sound_plane_iterate(uref->ubuf, &channel)) || channel == NULL)) { uref_free(uref); upipe_throw_error(upipe, UBASE_ERR_INVALID); return; } /* First try the ubuf_mem_shared method. */ struct ubuf *ubuf = ubuf_block_mem_alloc_from_sound(upipe_tblk->ubuf_mgr, uref->ubuf, channel); if (unlikely(ubuf == NULL)) { /* We have to memcpy the thing. */ size_t samples; uint8_t sample_size; if (unlikely(!ubase_check(uref_sound_size(uref, &samples, &sample_size)))) { uref_free(uref); upipe_throw_error(upipe, UBASE_ERR_INVALID); return; } size_t size = samples * sample_size; ubuf = ubuf_block_alloc(upipe_tblk->ubuf_mgr, size); if (unlikely(ubuf == NULL)) { uref_free(uref); upipe_throw_error(upipe, UBASE_ERR_ALLOC); return; } const uint8_t *r; if (unlikely(!ubase_check(uref_sound_plane_read_uint8_t(uref, channel, 0, -1, &r)))) { ubuf_free(ubuf); uref_free(uref); upipe_throw_error(upipe, UBASE_ERR_ALLOC); return; } uint8_t *w; int end = -1; if (unlikely(!ubase_check(ubuf_block_write(ubuf, 0, &end, &w)))) { uref_sound_plane_unmap(uref, channel, 0, -1); ubuf_free(ubuf); uref_free(uref); upipe_throw_error(upipe, UBASE_ERR_ALLOC); return; } memcpy(w, r, size); ubuf_block_unmap(ubuf, 0); uref_sound_plane_unmap(uref, channel, 0, -1); } uref_attach_ubuf(uref, ubuf); upipe_tblk_output(upipe, uref, upump_p); }
/** @internal @This is called back to fill NaCl audio buffer. * Please note that this function runs in a different thread. * * @param sample_buffer buffer to fill * @param buffer_size size of the buffer in octets * @param latency how long before the audio data is to be presented * @param user_data opaque pointing to the pipe */ static void upipe_nacl_audio_worker(void *sample_buffer, uint32_t buffer_size, PP_TimeDelta latency, void *user_data) { struct upipe *upipe = (struct upipe *)user_data; struct upipe_nacl_audio *upipe_nacl_audio = upipe_nacl_audio_from_upipe(upipe); uint64_t next_pts = UINT64_MAX; if (likely(upipe_nacl_audio->uclock != NULL)) { /* This is slightly off. */ next_pts = uclock_now(upipe_nacl_audio->uclock) + (uint64_t)(latency * UCLOCK_FREQ); } uint32_t frames = buffer_size / 4; while (frames > 0) { if (upipe_nacl_audio->uref == NULL) upipe_nacl_audio->uref = uqueue_pop(&upipe_nacl_audio->uqueue, struct uref *); if (unlikely(upipe_nacl_audio->uref == NULL)) { upipe_dbg_va(upipe, "playing %u frames of silence (empty)", frames); memset(sample_buffer, 0, frames * 4); break; } struct uref *uref = upipe_nacl_audio->uref; if (next_pts != UINT64_MAX) { uint64_t uref_pts; if (unlikely(!ubase_check(uref_clock_get_pts_sys(uref, &uref_pts)))) { upipe_nacl_audio->uref = NULL; uref_free(uref); upipe_warn(upipe, "non-dated uref received"); continue; } int64_t tolerance = uref_pts - next_pts; if (tolerance > (int64_t)PTS_TOLERANCE) { uint32_t silence_frames = tolerance * SAMPLE_RATE / UCLOCK_FREQ; if (silence_frames > frames) silence_frames = frames; upipe_dbg_va(upipe, "playing %u frames of silence (wait)", silence_frames); memset(sample_buffer, 0, silence_frames * 4); sample_buffer += silence_frames * 4; frames -= silence_frames; continue; } else if (-tolerance > (int64_t)PTS_TOLERANCE) { uint32_t dropped_frames = (-tolerance) * SAMPLE_RATE / UCLOCK_FREQ; upipe_warn_va(upipe, "late buffer received, dropping %u frames", dropped_frames); upipe_nacl_audio_consume(upipe, dropped_frames); continue; } } size_t size; const void *uref_buffer; if (unlikely(!ubase_check(uref_sound_size(uref, &size, NULL)) || !ubase_check(uref_sound_plane_read_void(uref, "lr", 0, -1, &uref_buffer)))) { upipe_nacl_audio->uref = NULL; uref_free(uref); upipe_warn(upipe, "cannot read ubuf buffer"); continue; } uint32_t copied_frames = size < frames ? size : frames; memcpy(sample_buffer, uref_buffer, copied_frames * 4); uref_sound_plane_unmap(uref, "lr", 0, -1); sample_buffer += copied_frames * 4; upipe_nacl_audio_consume(upipe, copied_frames); frames -= copied_frames; } }