diff options
Diffstat (limited to 'src/buffer')
| -rw-r--r-- | src/buffer/audio.c | 79 | ||||
| -rw-r--r-- | src/buffer/audio.h | 4 | ||||
| -rw-r--r-- | src/buffer/clock.c | 43 | ||||
| -rw-r--r-- | src/buffer/clock.h | 7 | ||||
| -rw-r--r-- | src/buffer/video.c | 16 |
5 files changed, 88 insertions, 61 deletions
diff --git a/src/buffer/audio.c b/src/buffer/audio.c index ffb10dc..3daeee2 100644 --- a/src/buffer/audio.c +++ b/src/buffer/audio.c @@ -1,17 +1,19 @@ #include <al/log.h> #include "audio.h" +#include "common.h" #define BUFFER_USEC (12 * 1000000L) #define BUFFER_WATERMARK_LOW (3 * 1000000L) // Must be a most half of the buffer size. -#define BUFFER_WATERMARK_HIGH (0.75 * 1000000L) +#define BUFFER_WATERMARK_HIGH (4 * 1000000L) #define DESYNC_PTS 0.022 #ifdef CAMU_AUDIO_BUFFER_FADE #define FADE 1.0 -#define FADE_LENGTH 6 -#define FADE_STEP(rate) ((FADE / ((f64)rate)) / (FADE_LENGTH - 1.0)) +#define FADE_LENGTH 18000 +#define FADE_STEP(rate) (FADE / FADE_LENGTH) +//#define FADE_STEP(rate) ((FADE / FADE_LENGTH) / (FADE_LENGTH - 1.0)) #endif enum { @@ -22,7 +24,7 @@ enum { enum { PAUSE_PRE = 0, - PAUSE_UNPAUSED, + PAUSE_PAUSED, #ifdef CAMU_AUDIO_BUFFER_FADE PAUSE_FADING, #endif @@ -33,7 +35,8 @@ static bool setup_optimal_resampler(struct camu_audio_buffer *buf, struct camu_l struct camu_lav_resample_fmt *fmt) { /* - switch ((enum AVSampleFormat)fmt->in_format) { + sw + // Update node start?itch ((enum AVSampleFormat)fmt->in_format) { case AV_SAMPLE_FMT_S16P: fmt->req_format = AV_SAMPLE_FMT_S16; break; @@ -56,9 +59,11 @@ static bool setup_optimal_resampler(struct camu_audio_buffer *buf, struct camu_l } fmt->req_format = fmt->in_format; + fmt->req_channel_count = fmt->in_channel_count; fmt->req_sample_rate = fmt->in_sample_rate; */ + //buf->fade_rate = buf->fmt.req_sample_rate / 10; buf->fade_rate = buf->fmt.req_sample_rate / 10; buf->bytes_per_sample = (s32)camu_lav_resample_fmt_bytes_per_sample(fmt); @@ -70,9 +75,9 @@ bool camu_audio_buffer_init(struct camu_audio_buffer *buf, struct camu_clock *cl { buf->mixer = mixer; buf->clock = clock; - buf->buffered = false; buf->pause = PAUSE_PRE; buf->ignore_desync = false; + buf->buffered = false; #ifdef CAMU_AUDIO_BUFFER_FADE buf->fade_period = 0; buf->fade_offset = 0; @@ -93,7 +98,6 @@ bool camu_audio_buffer_configure(struct camu_audio_buffer *buf, struct camu_stre buf->fmt.in_sample_rate = stream->av.stream->codecpar->sample_rate; camu_mixer_pick_format(buf->mixer, &buf->fmt); - av_channel_layout_default(&buf->fmt.req_channel_layout, buf->fmt.req_channel_count); if (!setup_optimal_resampler(buf, &buf->resamp, &buf->fmt)) { return false; } @@ -142,8 +146,7 @@ static bool push_internal(struct camu_audio_buffer *buf, u8 **data, s32 sample_c camu_peak_buffer_push(&buf->peak, data[0], have); if (peak >= buf->watermark.low) { // Discard the peak buffer. If this happens the user of this buffer is - // taking _way_ too long to stop. We could attempt to recover by - // re-seeking the whole stream. + // taking _way_ too long to stop. camu_peak_buffer_flush(&buf->peak, &size); } // Continuing based on an outdated continue_mark value is safe as long as the @@ -167,12 +170,13 @@ static bool push_internal(struct camu_audio_buffer *buf, u8 **data, s32 sample_c #ifdef HAVE_FFMPEG static void push_av_frame_internal(struct camu_audio_buffer *buf, AVFrame *frame) { + // It doesn't matter if there's a race here with flush(), as long + // as any frame is from the same stream. + // A reset() must finish before any data is pushed. if (al_atomic_u8_load(&buf->flow, AL_ATOMIC_RELAXED) == FLOWING) { - u8 **data = frame->data; - s32 sample_count = frame->nb_samples; AVStream *stream = buf->stream->av.stream; f64 pts = (frame->best_effort_timestamp - stream->start_time) * av_q2d(stream->time_base); - push_internal(buf, data, sample_count, pts); + push_internal(buf, frame->data, frame->nb_samples, pts); } av_frame_free(&frame); } @@ -198,8 +202,8 @@ void camu_audio_buffer_reset(struct camu_audio_buffer *buf) al_atomic_size_t_store(&buf->continue_mark, 0, AL_ATOMIC_RELAXED); al_atomic_u8_store(&buf->flow, FLOWING, AL_ATOMIC_RELAXED); buf->pts = camu_clock_get_base_pts(buf->clock) + camu_mixer_get_latency(buf->mixer); - buf->buffered = false; buf->pause = PAUSE_PRE; + buf->buffered = false; al_ring_buffer_reset(&buf->rb); } @@ -248,45 +252,48 @@ static void handle_fade(struct camu_audio_buffer *buf, u8 *data, size_t size, bo if ((i + 1) % channel_count == 0) { if (out) buf->volume = AL_CLAMP((buf->volume - FADE_STEP(buf->fade_rate)), 0.0, 1.0); else buf->volume = AL_CLAMP((buf->volume + FADE_STEP(buf->fade_rate)), 0.0, 1.0); + if (buf->fade_period > 0) buf->fade_period--; } data += buf->bytes_per_sample; } } #endif -#define NOT_UNPAUSED(pause) (pause != PAUSE_UNPAUSED && pause != PAUSE_PRE) +#define NOT_PAUSED(pause) (pause != PAUSE_PAUSED && pause != PAUSE_PRE) +// PAUSE_PAUSED means the last read was silence, meaning it's safe to skip. +// The sole purpose of PAUSE_PRE is to avoid a fade in at the start of the stream. size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t req) { - f64 pts = camu_clock_get_pts(buf->clock); + f64 pts = camu_clock_get_pts(buf->clock, camu_mixer_get_latency(buf->mixer)); if (pts < 0.0) { #ifdef CAMU_AUDIO_BUFFER_FADE if (buf->pause == PAUSE_PLAYING) { - // Fade out. Signified by >0 fade_period and PAUSE_FADING. + // Fade out. Signified by >0 fade_period and pause = PAUSE_FADING. buf->volume = FADE; buf->fade_period = FADE_LENGTH; buf->fade_offset = 0; buf->pause = PAUSE_FADING; - } else if (buf->fade_period == 0) { + } else if (buf->fade_period == 0) { // Fade out done. al_memset(data, 0, req); - if (NOT_UNPAUSED(buf->pause)) { - buf->pause = PAUSE_UNPAUSED; + if (NOT_PAUSED(buf->pause)) { + buf->pause = PAUSE_PAUSED; buf->callback(buf->userdata, CAMU_BUFFER_PAUSED); } return req; } - } else if (buf->pause == PAUSE_FADING || buf->pause == PAUSE_UNPAUSED) { - // Fade in. Signified by >0 fade_period and PAUSE_PLAYING. + } else if (buf->pause == PAUSE_FADING || buf->pause == PAUSE_PAUSED) { + // Fade in. Signified by >0 fade_period and pause = PAUSE_PLAYING. buf->volume = 1.0 - FADE; buf->fade_period = FADE_LENGTH; buf->fade_offset = 0; // We don't want to consider sync if reversing an active - // fade but we do if PAUSE_UNPAUSED. + // fade but we do if pause = PAUSE_PAUSED. if (buf->pause == PAUSE_FADING) buf->pause = PAUSE_PLAYING; #else al_memset(data, 0, req); - if (NOT_UNPAUSED(buf->pause)) { - buf->pause = PAUSE_UNPAUSED; + if (NOT_PAUSED(buf->pause)) { + buf->pause = PAUSE_PAUSED; buf->callback(buf->userdata, CAMU_BUFFER_PAUSED); } return req; @@ -294,15 +301,16 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re } size_t ret, signal = req; size_t size = al_ring_buffer_occupied(&buf->rb); + // Cut off fade if it's reaching too far. if (size < buf->fade_offset) size = 0; else size -= buf->fade_offset; #ifdef CAMU_AUDIO_BUFFER_FADE - if (buf->pause != PAUSE_FADING) { + if (buf->pause != PAUSE_FADING && buf->pause != PAUSE_PLAYING) { #endif pts -= buf->pts; - // We can assume a call to audio_buffer_read will happen before - // the user NEEDS the data, so, we can't accurately assess the sync here. - if (UNLIKELY(!buf->ignore_desync && (buf->pause == PAUSE_PRE || buf->pause == PAUSE_UNPAUSED))) { + // Possibly attempt syncing to the clock. For this to work the mixer must + // report a reasonably accurate value for latency. + if (UNLIKELY(!buf->ignore_desync && (buf->pause == PAUSE_PRE || buf->pause == PAUSE_PAUSED))) { if (fabs(pts) >= 0.322) { al_log_warn("audio_buffer", "Abnormally large audio desync of %.5fs", pts); } @@ -330,31 +338,42 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re } #endif u8 flow = al_atomic_u8_load(&buf->flow, AL_ATOMIC_RELAXED); - if (size < req) { + if (size < req) { // We don't have enough data to fulfill our request. if (flow == FLUSHED) { + // If flow = FLUSHED, we know we've recieved all the data + // this stream wants to send. Signal that we are done. signal = size; al_atomic_u8_store(&buf->flow, SIGNALED, AL_ATOMIC_RELAXED); al_log_debug("audio_buffer", "Flushed (signal: %zu).", signal); buf->callback(buf->userdata, CAMU_BUFFER_EOF); } else { + // Put silence into the remainder of the request buffer. al_memset(data + size, 0, req - size); if (flow == FLOWING) { + // Our stream hit an underrun because data wasn't coming in fast + // enough. It's also possible to underrun if this function takes too + // long which isn't checked here. That is unlikely though. al_log_warn("audio_buffer", "Underrun (req: %zu, have: %zu).", req, size); } + // If flow = SIGNALED, we are just waiting to be removed or reset. } req = size; } if (req > 0) { #ifdef CAMU_AUDIO_BUFFER_FADE if (buf->pause == PAUSE_FADING) { + // Peek into the ring buffer. This won't consume the data, so when + // we resume it will fade in from the same position the fade out started. ret = al_ring_buffer_peek(&buf->rb, data, buf->fade_offset, req); buf->fade_offset += ret; } else { #endif + // Read as much as we can from the ring buffer. ret = al_ring_buffer_read(&buf->rb, data, req); buf->pts += camu_lav_resample_fmt_bytes_to_sec(&buf->fmt, ret); if (flow == FLOWING) { ret = al_atomic_size_t_load(&buf->continue_mark, AL_ATOMIC_RELAXED); + // Request to uncork if we are below the mark. if (ret && ((buf->size - size) - req) >= ret) { buf->callback(buf->userdata, CAMU_BUFFER_UNCORK); } @@ -362,11 +381,11 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re #ifdef CAMU_AUDIO_BUFFER_FADE } if (buf->fade_period > 0) { - buf->fade_period--; handle_fade(buf, data, req, buf->pause == PAUSE_FADING); } #endif } + // Never return less than req, unless we hit the end of the stream. return signal; } diff --git a/src/buffer/audio.h b/src/buffer/audio.h index 7bd30a2..1a8baf0 100644 --- a/src/buffer/audio.h +++ b/src/buffer/audio.h @@ -6,13 +6,11 @@ #include "../codec/codec.h" #include "../codec/libav/resampler.h" #include "../mixer/mixer.h" -#include "../mixer/audio.h" #include "clock.h" -#include "common.h" #include "peak_buffer.h" -#define CAMU_AUDIO_BUFFER_FADE +//#define CAMU_AUDIO_BUFFER_FADE struct camu_audio_buffer { struct camu_stream *stream; diff --git a/src/buffer/clock.c b/src/buffer/clock.c index a1c3e83..1e7883c 100644 --- a/src/buffer/clock.c +++ b/src/buffer/clock.c @@ -2,26 +2,38 @@ #include "clock.h" -void camu_clock_set(struct camu_clock *clock, u64 base, u64 start) +void camu_clock_set(struct camu_clock *clock, u64 base, u64 ts, u64 delay) { al_atomic_f64_store(&clock->base, base / 1000000.f, AL_ATOMIC_RELAXED); al_atomic_f64_store(&clock->tick, 0.0, AL_ATOMIC_RELAXED); - al_atomic_u64_store(&clock->start, start, AL_ATOMIC_RELAXED); - camu_clock_resume(clock); + al_atomic_u64_store(&clock->start, ts, AL_ATOMIC_RELAXED); + clock->delay = delay; } -void camu_clock_resume(struct camu_clock *clock) +bool camu_clock_calc_tick(struct camu_clock *clock) { - u64 start = al_atomic_u64_load(&clock->start, AL_ATOMIC_ACQUIRE); - if (start == 0L) return; - u64 ts = aki_get_timestamp(); - al_assert(start < ts); - f64 diff = (ts - start) / 1000000.f; + u64 start = al_atomic_u64_load(&clock->start, AL_ATOMIC_RELAXED); + if (start == 0L) return false; + u64 ts = clock->delay == 0 ? start : aki_get_timestamp(); + al_assert(start <= ts); + u64 diff = ts - start; f64 tick = al_atomic_f64_load(&clock->tick, AL_ATOMIC_RELAXED); if (tick == 0.0) { + if (!(diff >= clock->delay && clock->delay >= (diff - clock->delay))) { + // We are past the requested start time. + return false; + } + diff = clock->delay - (diff - clock->delay); tick = aki_get_tick() - al_atomic_f64_load(&clock->base, AL_ATOMIC_RELAXED); } - al_atomic_f64_store(&clock->tick, tick + diff, AL_ATOMIC_RELAXED); + al_atomic_f64_store(&clock->tick, tick + (diff / 1000000.f), AL_ATOMIC_RELAXED); + return true; +} + +void camu_clock_resume(struct camu_clock *clock) +{ + u64 start = al_atomic_u64_load(&clock->start, AL_ATOMIC_ACQUIRE); + if (start == 0L) return; al_atomic_u64_store(&clock->start, 0L, AL_ATOMIC_RELEASE); } @@ -39,13 +51,6 @@ void camu_clock_pause(struct camu_clock *clock) al_atomic_u64_store(&clock->start, aki_get_timestamp(), AL_ATOMIC_RELEASE); } -void camu_clock_seek(struct camu_clock *clock, u64 base, u64 start) -{ - (void)clock; - (void)base; - (void)start; -} - bool camu_clock_is_paused(struct camu_clock *clock) { u64 start = al_atomic_u64_load(&clock->start, AL_ATOMIC_RELAXED); @@ -57,12 +62,12 @@ f64 camu_clock_get_base_pts(struct camu_clock *clock) return al_atomic_f64_load(&clock->base, AL_ATOMIC_RELAXED); } -f64 camu_clock_get_pts(struct camu_clock *clock) +f64 camu_clock_get_pts(struct camu_clock *clock, f64 offset) { u64 start = al_atomic_u64_load(&clock->start, AL_ATOMIC_RELAXED); if (start != 0L) return -1.0; f64 base = al_atomic_f64_load(&clock->base, AL_ATOMIC_RELAXED); - f64 pts = get_pts_internal(clock); + f64 pts = get_pts_internal(clock) + offset; if (pts < base) return -1.0; return pts; } diff --git a/src/buffer/clock.h b/src/buffer/clock.h index e39a963..a7399b5 100644 --- a/src/buffer/clock.h +++ b/src/buffer/clock.h @@ -6,12 +6,13 @@ struct camu_clock { atomic_f64 base, tick; atomic_u64 start; + u64 delay; }; -void camu_clock_set(struct camu_clock *clock, u64 base, u64 start); +void camu_clock_set(struct camu_clock *clock, u64 base, u64 ts, u64 delay); +bool camu_clock_calc_tick(struct camu_clock *clock); void camu_clock_resume(struct camu_clock *clock); void camu_clock_pause(struct camu_clock *clock); -void camu_clock_seek(struct camu_clock *clock, u64 base, u64 start); bool camu_clock_is_paused(struct camu_clock *clock); f64 camu_clock_get_base_pts(struct camu_clock *clock); -f64 camu_clock_get_pts(struct camu_clock *clock); +f64 camu_clock_get_pts(struct camu_clock *clock, f64 offset); diff --git a/src/buffer/video.c b/src/buffer/video.c index 26e84b0..9a04933 100644 --- a/src/buffer/video.c +++ b/src/buffer/video.c @@ -4,17 +4,17 @@ //#define CAMU_VIDEO_BUFFER_FORCE_SCALER -#define BUFFER_WATERMARK_LOW 16 // frames. -#define BUFFER_WATERMARK_BUFFERED 12 -#define BUFFER_WATERMARK_HIGH 28 -#define BUFFER_WATERMARK_RESET BUFFER_WATERMARK_HIGH + 16. +#define BUFFER_WATERMARK_LOW 8 // frames. +#define BUFFER_WATERMARK_BUFFERED 10 +#define BUFFER_WATERMARK_HIGH 14 +#define BUFFER_WATERMARK_RESET BUFFER_WATERMARK_HIGH + 10. bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *clock, struct camu_renderer *renderer) { + buf->clock = clock; buf->queue = renderer->create_queue(renderer); buf->buffered = false; - buf->clock = clock; #ifdef CAMU_SCREEN_THREADED al_atomic_bool_store(&buf->ref, false, AL_ATOMIC_RELAXED); #endif @@ -132,11 +132,15 @@ bool camu_video_buffer_is_single_frame(struct camu_video_buffer *buf) void camu_video_buffer_flush(struct camu_video_buffer *buf) { buf->queue->flush(buf->queue); + if (!buf->buffered) { + buf->callback(buf->userdata, CAMU_BUFFER_BUFFERED); + buf->buffered = true; + } } bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out) { - f64 pts = buf->single_frame ? 0.0 : camu_clock_get_pts(buf->clock); + f64 pts = buf->single_frame ? 0.0 : camu_clock_get_pts(buf->clock, 0); if (pts < 0.0) pts = camu_clock_get_base_pts(buf->clock); u8 ret = buf->queue->read(buf->queue, pts, out); if (ret == CAMU_QUEUE_EOF || (ret == CAMU_QUEUE_OK && buf->single_frame)) { |