summaryrefslogtreecommitdiff
path: root/src/buffer
diff options
context:
space:
mode:
Diffstat (limited to 'src/buffer')
-rw-r--r--src/buffer/audio.c59
-rw-r--r--src/buffer/audio.h1
-rw-r--r--src/buffer/clock.c10
-rw-r--r--src/buffer/clock.h2
-rw-r--r--src/buffer/common.h4
-rw-r--r--src/buffer/video.c65
-rw-r--r--src/buffer/video.h5
7 files changed, 86 insertions, 60 deletions
diff --git a/src/buffer/audio.c b/src/buffer/audio.c
index 939b98a..9799a95 100644
--- a/src/buffer/audio.c
+++ b/src/buffer/audio.c
@@ -2,16 +2,16 @@
#include "audio.h"
-#define BUFFER_USEC (10 * 1000000L)
+#define BUFFER_USEC (12 * 1000000L)
#define BUFFER_WATERMARK_LOW (3 * 1000000L) // Must be a most half of the buffer size.
-#define BUFFER_WATERMARK_HIGH (4 * 1000000L)
+#define BUFFER_WATERMARK_HIGH (0.75 * 1000000L)
#define DESYNC_PTS 0.022
#ifdef CAMU_AUDIO_BUFFER_FADE
-#define FADE 0.9999
+#define FADE 1.0
#define FADE_LENGTH 6
-#define FADE_STEP(rate) ((FADE / (rate)) / (FADE_LENGTH - 1))
+#define FADE_STEP(rate) ((FADE / ((f64)rate)) / (FADE_LENGTH - 1.0))
#endif
enum {
@@ -23,7 +23,6 @@ enum {
enum {
PAUSE_PRE = 0,
PAUSE_UNPAUSED,
- PAUSE_IGNORE_DESYNC,
#ifdef CAMU_AUDIO_BUFFER_FADE
PAUSE_FADING,
#endif
@@ -73,8 +72,8 @@ bool camu_audio_buffer_init(struct camu_audio_buffer *buf, struct camu_clock *cl
buf->clock = clock;
buf->pts = camu_mixer_get_latency(buf->mixer);
buf->buffered = false;
- //buf->pause = PAUSE_PRE;
- buf->pause = PAUSE_IGNORE_DESYNC;
+ buf->pause = PAUSE_PRE;
+ buf->ignore_desync = true;
#ifdef CAMU_AUDIO_BUFFER_FADE
buf->fade_period = 0;
buf->fade_offset = 0;
@@ -93,23 +92,29 @@ bool camu_audio_buffer_configure(struct camu_audio_buffer *buf, struct camu_stre
av_channel_layout_copy(&buf->fmt.in_channel_layout, &stream->av.stream->codecpar->ch_layout);
buf->fmt.in_channel_count = buf->fmt.in_channel_layout.nb_channels;
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;
}
+
const char *in_format_name = av_get_sample_fmt_name(buf->fmt.in_format);
const char *req_format_name = av_get_sample_fmt_name(buf->fmt.req_format);
al_log_info("audio_buffer", "Stream: %s (%dch) %dHz -> %s (%dch) %dHz.",
in_format_name, buf->fmt.in_channel_layout.nb_channels, buf->fmt.in_sample_rate,
req_format_name, buf->fmt.req_channel_layout.nb_channels, buf->fmt.req_sample_rate);
+
buf->size = camu_lav_resample_fmt_usec_to_bytes(&buf->fmt, BUFFER_USEC);
buf->data = (u8 *)al_malloc(buf->size);
al_ring_buffer_init(&buf->rb, buf->data, buf->size);
+
buf->watermark.low = camu_lav_resample_fmt_usec_to_bytes(&buf->fmt, BUFFER_WATERMARK_LOW);
buf->watermark.high = camu_lav_resample_fmt_usec_to_bytes(&buf->fmt, BUFFER_WATERMARK_HIGH);
camu_peak_buffer_init(&buf->peak);
+
buf->stream = stream;
+
return true;
}
@@ -144,9 +149,9 @@ static bool push_internal(struct camu_audio_buffer *buf, u8 **data, s32 sample_c
}
// Continuing based on an outdated continue_mark value is safe as long as the
// peak buffer is smaller than the low watermark and the low watermark is
- // less than or equal to half the buffer.
+ // less than or equal to half the buffer size.
al_atomic_size_t_store(&buf->continue_mark, buf->watermark.low + peak, AL_ATOMIC_RELAXED);
- buf->callback(buf->userdata, CAMU_BUFFER_STOP);
+ buf->callback(buf->userdata, CAMU_BUFFER_CORK);
return false;
}
@@ -195,6 +200,7 @@ void camu_audio_buffer_reset(struct camu_audio_buffer *buf)
al_atomic_u8_store(&buf->flow, FLOWING, AL_ATOMIC_RELAXED);
buf->pts = camu_clock_get_base_pts(buf->clock);
buf->buffered = false;
+ buf->pause = PAUSE_PRE;
al_ring_buffer_reset(&buf->rb);
}
@@ -212,10 +218,11 @@ void camu_audio_buffer_flush(struct camu_audio_buffer *buf)
#define TYPED_CAST(f, type) (f64)(*((type *)f))
#define TYPED_CLAMP(f, type, min, max, ret) *((type *)ret) = (type)AL_CLAMP(f, min, max)
+// This is obviously bad for optimization, should just make a function for each type.
static void handle_fade(struct camu_audio_buffer *buf, u8 *data, size_t size, bool out)
{
- u8 bits[MAX_BYTES];
- u8 *ptr = bits + (MAX_BYTES - buf->bytes_per_sample);
+ u8 bytes[MAX_BYTES];
+ u8 *ptr = bytes + (MAX_BYTES - buf->bytes_per_sample);
f64 value;
u32 sample_count = size / buf->bytes_per_sample;
u32 channel_count = 2;
@@ -248,14 +255,14 @@ static void handle_fade(struct camu_audio_buffer *buf, u8 *data, size_t size, bo
}
#endif
-#define NOT_UNPAUSED(pause) (pause != PAUSE_UNPAUSED && pause != PAUSE_PRE && pause != PAUSE_IGNORE_DESYNC)
+#define NOT_UNPAUSED(pause) (pause != PAUSE_UNPAUSED && pause != PAUSE_PRE)
size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t req)
{
- f64 pts = camu_clock_get_pts(buf->clock);
- if (pts == -1.0) {
+ if (camu_clock_is_paused(buf->clock)) {
#ifdef CAMU_AUDIO_BUFFER_FADE
if (buf->pause == PAUSE_PLAYING) {
+ // Fade out. Signified by >0 fade_period and PAUSE_FADING.
buf->volume = FADE;
buf->fade_period = FADE_LENGTH;
buf->fade_offset = 0;
@@ -269,10 +276,13 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re
return req;
}
} else if (buf->pause == PAUSE_FADING || buf->pause == PAUSE_UNPAUSED) {
+ // Fade in. Signified by >0 fade_period and PAUSE_PLAYING.
buf->volume = 1.0 - FADE;
buf->fade_period = FADE_LENGTH;
buf->fade_offset = 0;
- buf->pause = PAUSE_PLAYING;
+ // We don't want to consider sync if reversing an active
+ // fade but we do if PAUSE_UNPAUSED.
+ if (buf->pause == PAUSE_FADING) buf->pause = PAUSE_PLAYING;
#else
al_memset(data, 0, req);
if (NOT_UNPAUSED(buf->pause)) {
@@ -289,23 +299,30 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re
#ifdef CAMU_AUDIO_BUFFER_FADE
if (buf->pause != PAUSE_FADING) {
#endif
- pts -= buf->pts;
- if (UNLIKELY(buf->pause == PAUSE_PRE || buf->pause == PAUSE_UNPAUSED)) {
+ f64 pts = camu_clock_get_pts(buf->clock) - 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))) {
+ if (fabs(pts) >= 0.322) {
+ al_log_warn("audio_buffer", "Abnormally large audio desync of %.5fs", pts);
+ }
if (pts > 0.0) {
ret = camu_lav_resample_fmt_sec_to_bytes(&buf->fmt, pts);
ret = AL_MIN(ret, size);
- al_log_debug("audio_buffer", "Skipping %.5fs of audio.", pts);
+ al_log_debug("audio_buffer", "Skipping %.5fs of audio (%zu bytes).", pts, ret);
ret = al_ring_buffer_discard(&buf->rb, ret);
size -= ret;
buf->pts += camu_lav_resample_fmt_bytes_to_sec(&buf->fmt, ret);
+ // Could go on to underrun.
} else if (pts < 0.0) {
pts = -pts;
ret = camu_lav_resample_fmt_sec_to_bytes(&buf->fmt, pts);
ret = AL_MIN(ret, req);
- al_log_debug("audio_buffer", "Delaying audio by %.5fs.", pts);
+ al_log_debug("audio_buffer", "Delaying audio by %.5fs (%zu bytes).", pts, ret);
al_memset(data, 0, ret);
data += ret;
- req -= ret;
+ // We can continue to delay.
+ if ((req -= ret) == 0) return signal;
}
}
if (buf->pause != PAUSE_PLAYING) buf->pause = PAUSE_PLAYING;
@@ -339,7 +356,7 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re
if (flow == FLOWING) {
ret = al_atomic_size_t_load(&buf->continue_mark, AL_ATOMIC_RELAXED);
if (ret && ((buf->size - size) - req) >= ret) {
- buf->callback(buf->userdata, CAMU_BUFFER_CONTINUE);
+ buf->callback(buf->userdata, CAMU_BUFFER_UNCORK);
}
}
#ifdef CAMU_AUDIO_BUFFER_FADE
diff --git a/src/buffer/audio.h b/src/buffer/audio.h
index 1a51807..7bd30a2 100644
--- a/src/buffer/audio.h
+++ b/src/buffer/audio.h
@@ -20,6 +20,7 @@ struct camu_audio_buffer {
f64 pts;
u8 pause;
+ bool ignore_desync;
struct camu_clock *clock;
f64 volume;
diff --git a/src/buffer/clock.c b/src/buffer/clock.c
index 5f4133e..c9b18ce 100644
--- a/src/buffer/clock.c
+++ b/src/buffer/clock.c
@@ -4,24 +4,26 @@ void camu_clock_init(struct camu_clock *clock)
{
f64 tick = aki_get_tick();
al_atomic_f64_store(&clock->pause, tick, AL_ATOMIC_RELAXED);
+ al_atomic_f64_store(&clock->start, tick, AL_ATOMIC_RELAXED);
al_atomic_f64_store(&clock->base, 0.0, AL_ATOMIC_RELAXED);
- al_atomic_f64_store(&clock->start, 0.0, AL_ATOMIC_RELAXED);
- al_atomic_f64_store(&clock->offset, 0.0, AL_ATOMIC_RELAXED);
}
void camu_clock_pause(struct camu_clock *clock)
{
+ f64 pause = al_atomic_f64_load(&clock->pause, AL_ATOMIC_RELAXED);
+ if (pause != -1.0) return;
al_atomic_f64_store(&clock->pause, aki_get_tick(), AL_ATOMIC_RELAXED);
}
void camu_clock_resume(struct camu_clock *clock)
{
f64 pause = al_atomic_f64_load(&clock->pause, AL_ATOMIC_RELAXED);
+ if (pause == -1.0) return;
f64 start = al_atomic_f64_load(&clock->start, AL_ATOMIC_RELAXED);
f64 base = al_atomic_f64_load(&clock->base, AL_ATOMIC_RELAXED);
if (start == 0.0) start = (pause - base);
- al_atomic_f64_store(&clock->start, aki_get_tick() - (pause - start), AL_ATOMIC_RELAXED);
al_atomic_f64_store(&clock->pause, -1.0, AL_ATOMIC_RELAXED);
+ al_atomic_f64_store(&clock->start, aki_get_tick() - (pause - start), AL_ATOMIC_RELAXED);
}
void camu_clock_seek(struct camu_clock *clock, f64 pos)
@@ -47,7 +49,7 @@ f64 camu_clock_get_base_pts(struct camu_clock *clock)
f64 camu_clock_get_pts(struct camu_clock *clock)
{
f64 pause = al_atomic_f64_load(&clock->pause, AL_ATOMIC_RELAXED);
- if (pause != -1.0) return -1.0;
f64 start = al_atomic_f64_load(&clock->start, AL_ATOMIC_RELAXED);
+ if (pause != -1.f) return pause - start;
return aki_get_tick() - start;
}
diff --git a/src/buffer/clock.h b/src/buffer/clock.h
index 072f7d3..9f34807 100644
--- a/src/buffer/clock.h
+++ b/src/buffer/clock.h
@@ -4,7 +4,7 @@
#include <al/atomic.h>
struct camu_clock {
- atomic_f64 base, start, pause, offset;
+ atomic_f64 base, start, pause;
};
void camu_clock_init(struct camu_clock *clock);
diff --git a/src/buffer/common.h b/src/buffer/common.h
index 8a99c96..64168ff 100644
--- a/src/buffer/common.h
+++ b/src/buffer/common.h
@@ -2,8 +2,8 @@
enum {
CAMU_BUFFER_BUFFERED = 0,
- CAMU_BUFFER_STOP,
- CAMU_BUFFER_CONTINUE,
+ CAMU_BUFFER_CORK,
+ CAMU_BUFFER_UNCORK,
CAMU_BUFFER_PAUSED,
CAMU_BUFFER_EOF
};
diff --git a/src/buffer/video.c b/src/buffer/video.c
index d5d2c12..6c8d6e3 100644
--- a/src/buffer/video.c
+++ b/src/buffer/video.c
@@ -4,13 +4,17 @@
//#define CAMU_VIDEO_BUFFER_FORCE_SCALER
+#define BUFFER_WATERMARK_LOW 16 // frames.
+#define BUFFER_WATERMARK_BUFFERED 24
+#define BUFFER_WATERMARK_HIGH 48
+#define BUFFER_WATERMARK_RESET BUFFER_WATERMARK_HIGH + 16.
+
bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *clock,
struct camu_renderer *renderer)
{
buf->queue = renderer->create_queue(renderer);
buf->buffered = false;
buf->clock = clock;
- buf->last_pts = 0.0;
#ifdef CAMU_SCREEN_THREADED
al_atomic_bool_store(&buf->ref, false, AL_ATOMIC_RELAXED);
#endif
@@ -32,20 +36,24 @@ bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_stre
s32 height = stream->av.stream->codecpar->height;
buf->fmt.in_width = buf->stream->video.width = width;
buf->fmt.in_height = buf->stream->video.height = height;
+ buf->fmt.in_format = stream->av.stream->codecpar->format;
buf->single_frame = stream->av.stream->duration == 0 ||
stream->av.stream->avg_frame_rate.den == 0;
- buf->avg_frame_duration = buf->single_frame ? 0.0 :
- av_q2d(av_inv_q(stream->av.stream->avg_frame_rate));
- buf->fmt.in_format = stream->av.stream->codecpar->format;
+ AVRational frame_rate = buf->single_frame ? (AVRational){ 0, 1 } :
+ stream->av.stream->avg_frame_rate;
+ buf->avg_frame_duration = av_q2d(av_inv_q(frame_rate));
const char *format_name = av_get_pix_fmt_name(buf->fmt.in_format);
- al_log_info("video_buffer", "Stream: %s (%dx%d) %s %.2ffps.",
- format_name, width, height, buf->single_frame ? "IMAGE" : "VIDEO",
- buf->single_frame ? 0.0 : 1.0 / buf->avg_frame_duration);
+ al_log_info("video_buffer", "Stream: %s (%dx%d) %s %.2ffps.", format_name,
+ width, height, buf->single_frame ? "IMAGE" : "VIDEO", av_q2d(frame_rate));
#ifdef CAMU_VIDEO_BUFFER_FORCE_SCALER
buf->fmt.req_width = width;
buf->fmt.req_height = height;
buf->fmt.req_format = AV_PIX_FMT_RGBA;
- if (!camu_lav_scaler_init(&buf->scale, &buf->fmt)) {
+ if (camu_lav_scaler_init(&buf->scale, &buf->fmt) && buf->fmt.scaler_needed) {
+ format_name = av_get_pix_fmt_name(buf->fmt.req_format);
+ al_log_info("video_buffer", "Scaling stream to: %s (%dx%d)", format_name,
+ buf->fmt.req_width, buf->fmt.req_height);
+ } else {
return false;
}
#endif
@@ -59,40 +67,38 @@ bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_stre
static void after_push_internal(struct camu_video_buffer *buf)
{
s32 count = buf->queue->count(buf->queue);
- if (!buf->buffered && (buf->single_frame || count >= 15)) {
+ if (!buf->buffered && (buf->single_frame || count >= BUFFER_WATERMARK_BUFFERED)) {
buf->callback(buf->userdata, CAMU_BUFFER_BUFFERED);
buf->buffered = true;
}
- if (count >= 40) buf->queue->reset(buf->queue);
- else if (count >= 30) buf->callback(buf->userdata, CAMU_BUFFER_STOP);
+ if (count >= BUFFER_WATERMARK_RESET) buf->queue->reset(buf->queue);
+ else if (count >= BUFFER_WATERMARK_HIGH) buf->callback(buf->userdata, CAMU_BUFFER_CORK);
if (buf->single_frame) camu_video_buffer_flush(buf);
}
#ifdef HAVE_FFMPEG
static void push_av_frame_internal(struct camu_video_buffer *buf, AVFrame *frame)
{
- AVStream *stream = buf->stream->av.stream;
- f64 pts = buf->single_frame ? 0.0 :
- (frame->best_effort_timestamp - stream->start_time) * av_q2d(stream->time_base);
- if (pts + buf->avg_frame_duration < camu_clock_get_base_pts(buf->clock)) {
- av_frame_free(&frame);
- return;
+ f64 pts = 0.0;
+ if (!buf->single_frame) {
+ AVStream *stream = buf->stream->av.stream;
+ pts = (frame->best_effort_timestamp - stream->start_time) * av_q2d(stream->time_base);
+ if (pts + buf->avg_frame_duration < camu_clock_get_base_pts(buf->clock)) {
+ av_frame_free(&frame);
+ return;
+ }
}
- AVFrame *scaled_frame;
#ifdef CAMU_VIDEO_BUFFER_FORCE_SCALER
if (buf->fmt.scaler_needed) {
if (!camu_lav_scaler_scale(&buf->scale, (const u8 **)frame->data, frame->linesize)) {
return;
}
- scaled_frame = buf->scale.frame;
- } else {
- scaled_frame = frame;
+ av_frame_free(&frame);
+ frame = av_frame_clone(buf->scale.frame);
}
-#else
- scaled_frame = frame;
#endif
- if (scaled_frame) scaled_frame->opaque = buf;
- buf->queue->push_av_frame(buf->queue, scaled_frame, pts);
+ if (frame) frame->opaque = buf;
+ buf->queue->push_av_frame(buf->queue, frame, pts);
}
#endif
@@ -130,19 +136,18 @@ void camu_video_buffer_flush(struct camu_video_buffer *buf)
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);
- if (pts == -1.0) pts = buf->last_pts;
- buf->last_pts = pts;
+ f64 pts = buf->single_frame ? 0.0 : camu_clock_get_pts(buf->clock);
u8 ret = buf->queue->read(buf->queue, pts, out);
if (ret == CAMU_QUEUE_EOF || (ret == CAMU_QUEUE_OK && buf->single_frame)) {
buf->callback(buf->userdata, CAMU_BUFFER_EOF);
- } else if (buf->queue->count(buf->queue) <= 20) {
- buf->callback(buf->userdata, CAMU_BUFFER_CONTINUE);
+ } else if (buf->queue->count(buf->queue) <= BUFFER_WATERMARK_LOW) {
+ buf->callback(buf->userdata, CAMU_BUFFER_UNCORK);
}
return ret == CAMU_QUEUE_OK || ret == CAMU_QUEUE_MORE;
}
void camu_video_buffer_free(struct camu_video_buffer *buf)
{
+ if (buf->fmt.scaler_needed) camu_lav_scaler_close(&buf->scale);
buf->queue->free(&buf->queue);
}
diff --git a/src/buffer/video.h b/src/buffer/video.h
index 534f797..a6349f8 100644
--- a/src/buffer/video.h
+++ b/src/buffer/video.h
@@ -14,9 +14,10 @@ struct camu_video_buffer {
struct camu_stream *stream;
struct camu_clock *clock;
+ f32 start_time;
+ f32 avg_frame_duration;
+
bool single_frame;
- f64 last_pts;
- f64 avg_frame_duration;
struct camu_lav_scaler scale;
struct camu_lav_scale_fmt fmt;