summaryrefslogtreecommitdiff
path: root/src/buffer
diff options
context:
space:
mode:
Diffstat (limited to 'src/buffer')
-rw-r--r--src/buffer/audio.c79
-rw-r--r--src/buffer/audio.h4
-rw-r--r--src/buffer/clock.c43
-rw-r--r--src/buffer/clock.h7
-rw-r--r--src/buffer/video.c16
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)) {