/** @internal @This creates regularly empty urefs. * * @param upump description structure of the timer */ static void upipe_voidsrc_worker(struct upump *upump) { struct upipe *upipe = upump_get_opaque(upump, struct upipe *); struct upipe_voidsrc *upipe_voidsrc = upipe_voidsrc_from_upipe(upipe); uint64_t now; if (upipe_voidsrc->pts == UINT64_MAX) upipe_voidsrc->pts = uclock_now(upipe_voidsrc->uclock); for (now = uclock_now(upipe_voidsrc->uclock); !upipe_single(upipe) && upipe_voidsrc->pts <= now; now = uclock_now(upipe_voidsrc->uclock)) { struct uref *uref = uref_alloc(upipe_voidsrc->uref_mgr); if (unlikely(!uref)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return; } uref_clock_set_duration(uref, upipe_voidsrc->interval); uref_clock_set_pts_sys(uref, upipe_voidsrc->pts); uref_clock_set_pts_prog(uref, upipe_voidsrc->pts); upipe_voidsrc->pts += upipe_voidsrc->interval; upipe_voidsrc_output(upipe, uref, &upipe_voidsrc->timer); } if (!upipe_single(upipe)) upipe_voidsrc_wait_timer(upipe, upipe_voidsrc->pts - now, upipe_voidsrc_worker); }
/** @internal @This outputs data to the file sink. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to pump that generated the buffer * @return true if the uref was processed */ static bool upipe_fsink_output(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_fsink *upipe_fsink = upipe_fsink_from_upipe(upipe); const char *def; if (unlikely(ubase_check(uref_flow_get_def(uref, &def)))) { uint64_t latency = 0; uref_clock_get_latency(uref, &latency); if (latency > upipe_fsink->latency) upipe_fsink->latency = latency; uref_free(uref); return true; } if (unlikely(upipe_fsink->fd == -1)) { uref_free(uref); upipe_warn(upipe, "received a buffer before opening a file"); return true; } if (likely(upipe_fsink->uclock == NULL)) goto write_buffer; uint64_t cr_sys = 0; if (unlikely(!ubase_check(uref_clock_get_cr_sys(uref, &cr_sys)))) { upipe_warn(upipe, "received non-dated buffer"); goto write_buffer; } uint64_t now = uclock_now(upipe_fsink->uclock); cr_sys += upipe_fsink->latency; if (unlikely(now < cr_sys)) { upipe_fsink_wait_upump(upipe, cr_sys - now, upipe_fsink_watcher); return false; } write_buffer: for ( ; ; ) { int iovec_count = uref_block_iovec_count(uref, 0, -1); if (unlikely(iovec_count == -1)) { uref_free(uref); upipe_warn(upipe, "cannot read ubuf buffer"); break; } if (unlikely(iovec_count == 0)) { uref_free(uref); break; } struct iovec iovecs[iovec_count]; if (unlikely(!ubase_check(uref_block_iovec_read(uref, 0, -1, iovecs)))) { uref_free(uref); upipe_warn(upipe, "cannot read ubuf buffer"); break; } ssize_t ret = writev(upipe_fsink->fd, iovecs, iovec_count); uref_block_iovec_unmap(uref, 0, -1, iovecs); if (unlikely(ret == -1)) { switch (errno) { case EINTR: continue; case EAGAIN: #if EAGAIN != EWOULDBLOCK case EWOULDBLOCK: #endif upipe_fsink_poll(upipe); return false; case EBADF: case EFBIG: case EINVAL: case EIO: case ENOSPC: case EPIPE: default: break; } uref_free(uref); upipe_warn_va(upipe, "write error to %s (%m)", upipe_fsink->path); upipe_fsink_set_upump(upipe, NULL); upipe_throw_sink_end(upipe); return true; } size_t uref_size; if (ubase_check(uref_block_size(uref, &uref_size)) && uref_size == ret) { uref_free(uref); break; } uref_block_resize(uref, ret, -1); } return true; }
/** @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; } }