diff options
Diffstat (limited to 'src/buffer')
| -rw-r--r-- | src/buffer/audio.c | 59 | ||||
| -rw-r--r-- | src/buffer/audio.h | 1 | ||||
| -rw-r--r-- | src/buffer/clock.c | 10 | ||||
| -rw-r--r-- | src/buffer/clock.h | 2 | ||||
| -rw-r--r-- | src/buffer/common.h | 4 | ||||
| -rw-r--r-- | src/buffer/video.c | 65 | ||||
| -rw-r--r-- | src/buffer/video.h | 5 |
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; |