summaryrefslogtreecommitdiff
path: root/src/buffer
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2024-11-17 15:26:17 -0500
committerAndrew Opalach <andrew@akon.city> 2024-11-17 15:26:17 -0500
commit588b6d8bfa1b2efdebf8c424c86f6069cceafa41 (patch)
tree545308813e9ae19d3293b78c7cb39555a64c0abe /src/buffer
parent58d38d767442c83e31ae6dbaea7b1fc8ccb00633 (diff)
downloadcamu-588b6d8bfa1b2efdebf8c424c86f6069cceafa41.tar.gz
camu-588b6d8bfa1b2efdebf8c424c86f6069cceafa41.tar.bz2
camu-588b6d8bfa1b2efdebf8c424c86f6069cceafa41.zip
Rough subtitles implementation, improve buffers
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/buffer')
-rw-r--r--src/buffer/audio.c84
-rw-r--r--src/buffer/audio.h10
-rw-r--r--src/buffer/frame_queue.h4
-rw-r--r--src/buffer/video.c88
-rw-r--r--src/buffer/video.h14
5 files changed, 121 insertions, 79 deletions
diff --git a/src/buffer/audio.c b/src/buffer/audio.c
index 3abae42..5058154 100644
--- a/src/buffer/audio.c
+++ b/src/buffer/audio.c
@@ -9,9 +9,9 @@
#include "common_internal.h"
#include "volume.h"
-#define BUFFER_SIZE (9 * 1000000L)
-#define BUFFER_MARK_MIN (4.3 * 1000000L) // Must be a most half of the buffer size.
-#define BUFFER_MARK_BUFFERED (4.0 * 1000000L)
+#define BUFFER_SIZE 9.0
+#define BUFFER_MARK_MIN 4.3 // Must be a most half of the buffer size.
+#define BUFFER_MARK_BUFFERED 4.0
#ifdef CAMU_AUDIO_BUFFER_FADE
#define FADE_STEP(fmt) (1.f / (fmt)->sample_rate)
@@ -27,7 +27,7 @@ enum {
static void reset_buffer_state(struct camu_audio_buffer *buf)
{
- buf->pts = -1.0;
+ al_atomic_store(f64)(&buf->pts, -1.0, AL_ATOMIC_RELAXED);
buf->pause = PAUSE_PAUSED;
al_atomic_store(s32)(&buf->unpause, 0, AL_ATOMIC_RELAXED);
al_atomic_store(s32)(&buf->volume.set, 0, AL_ATOMIC_RELAXED);
@@ -40,11 +40,9 @@ static void reset_buffer_state(struct camu_audio_buffer *buf)
al_atomic_store(size_t)(&buf->uncork_at, 0, AL_ATOMIC_RELAXED);
}
-bool camu_audio_buffer_init(struct camu_audio_buffer *buf, struct camu_clock *clock,
- struct camu_mixer *mixer)
+bool camu_audio_buffer_init(struct camu_audio_buffer *buf, struct camu_clock *clock)
{
buf->clock = clock;
- buf->mixer = mixer;
reset_buffer_state(buf);
buf->ignore_desync = false;
buf->latency = 0.0;
@@ -54,10 +52,11 @@ bool camu_audio_buffer_init(struct camu_audio_buffer *buf, struct camu_clock *cl
return true;
}
-bool camu_audio_buffer_configure(struct camu_audio_buffer *buf, struct camu_codec_stream *stream)
+bool camu_audio_buffer_configure(struct camu_audio_buffer *buf, struct camu_codec_stream *stream,
+ struct camu_mixer *mixer)
{
camu_audio_format_copy(&buf->fmt.in, &stream->audio.fmt);
- camu_mixer_pick_format(buf->mixer, &buf->fmt);
+ camu_mixer_pick_format(mixer, &buf->fmt);
buf->fmt.resampler_needed = !camu_resampler_format_matches(&buf->fmt);
if (buf->fmt.resampler_needed) {
#ifdef CAMU_HAVE_FFMPEG
@@ -76,12 +75,12 @@ bool camu_audio_buffer_configure(struct camu_audio_buffer *buf, struct camu_code
in_format_name, buf->fmt.in.channel_count, buf->fmt.in.sample_rate,
req_format_name, buf->fmt.req.channel_count, buf->fmt.req.sample_rate);
- buf->size = camu_audio_format_usec_to_bytes(&buf->fmt.req, BUFFER_SIZE);
+ buf->size = camu_audio_format_sec_to_bytes(&buf->fmt.req, BUFFER_SIZE);
buf->data = (u8 *)al_malloc(buf->size);
al_ring_buffer_init(&buf->rb, buf->data, buf->size);
- buf->mark.min = camu_audio_format_usec_to_bytes(&buf->fmt.req, BUFFER_MARK_MIN);
- buf->mark.buffered = camu_audio_format_usec_to_bytes(&buf->fmt.req, BUFFER_MARK_BUFFERED);
+ buf->mark.min = camu_audio_format_sec_to_bytes(&buf->fmt.req, BUFFER_MARK_MIN);
+ buf->mark.buffered = camu_audio_format_sec_to_bytes(&buf->fmt.req, BUFFER_MARK_BUFFERED);
camu_peak_buffer_init(&buf->peak, 1024 * 16);
buf->stream = stream;
@@ -144,6 +143,16 @@ static bool push_internal(struct camu_audio_buffer *buf, u8 *data, s32 sample_co
return true;
}
+static bool frame_is_late(struct camu_audio_buffer *buf, f64 pts, f64 duration)
+{
+ f64 base = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
+ if (base == -1.0) {
+ base = camu_clock_get_base_pts(buf->clock);
+ al_atomic_store(f64)(&buf->pts, base, AL_ATOMIC_RELEASE);
+ }
+ return pts + duration < base;
+}
+
#ifdef CAMU_HAVE_FFMPEG
static void push_av_frame_internal(struct camu_audio_buffer *buf, AVFrame *frame)
{
@@ -151,8 +160,7 @@ static void push_av_frame_internal(struct camu_audio_buffer *buf, AVFrame *frame
AVStream *stream = buf->stream->av.stream;
f64 pts = frame->best_effort_timestamp * av_q2d(stream->time_base);
f64 duration = camu_audio_format_samples_to_sec(&buf->fmt.in, sample_count);
- if (pts + duration >= camu_clock_get_base_pts(buf->clock)) {
- if (buf->pts == -1.0) buf->pts = pts;
+ if (!frame_is_late(buf, pts, duration)) {
u8 **data = frame->data;
if (buf->fmt.resampler_needed) {
sample_count = buf->resamp->convert(buf->resamp, (const u8 **)data, sample_count);
@@ -173,9 +181,8 @@ void camu_audio_buffer_push(struct camu_audio_buffer *buf, struct camu_codec_fra
s32 sample_count = frame->audio.sample_count;
f64 pts = frame->pts;
f64 duration = camu_audio_format_samples_to_sec(&buf->fmt.in, sample_count);
- if (pts + duration >= camu_clock_get_base_pts(buf->clock)) {
- if (buf->pts == -1.0) buf->pts = pts;
- u8 *store[AV_NUM_DATA_POINTERS] = { 0 };
+ if (!frame_is_late(buf, pts, duration)) {
+ u8 *store[CAMU_NUM_DATA_POINTERS] = { 0 };
store[0] = frame->data;
u8 **data = store;
if (buf->fmt.resampler_needed) {
@@ -201,17 +208,6 @@ void camu_audio_buffer_unpause(struct camu_audio_buffer *buf)
al_atomic_add(s32)(&buf->unpause, 1, AL_ATOMIC_RELAXED);
}
-// Not thread-safe, must be called while the buffer is not being read from or written to.
-void camu_audio_buffer_reset(struct camu_audio_buffer *buf)
-{
- reset_buffer_state(buf);
- al_ring_buffer_reset(&buf->rb);
- camu_peak_buffer_flush(&buf->peak);
- if (buf->fmt.resampler_needed) {
- buf->resamp->flush(buf->resamp);
- }
-}
-
// flush() always comes from the same thread as push().
void camu_audio_buffer_flush(struct camu_audio_buffer *buf)
{
@@ -233,18 +229,29 @@ void camu_audio_buffer_flush(struct camu_audio_buffer *buf)
al_log_debug("audio_buffer", "Flush requested.");
}
+// Not thread-safe, must be called while the buffer is not being read from or written to.
+void camu_audio_buffer_reset(struct camu_audio_buffer *buf)
+{
+ reset_buffer_state(buf);
+ al_ring_buffer_reset(&buf->rb);
+ camu_peak_buffer_flush(&buf->peak);
+ if (buf->fmt.resampler_needed) {
+ buf->resamp->flush(buf->resamp);
+ }
+}
+
+static void increment_pts(struct camu_audio_buffer *buf, f64 amount, f64 *base)
+{
+ *base += amount;
+ al_atomic_store(f64)(&buf->pts, *base, AL_ATOMIC_RELEASE);
+}
+
#define NOT_PAUSED(pause) (pause != PAUSE_PAUSED)
// PAUSE_PAUSED signifies that the last read was silence. Meaning we can skip
// around in the buffer without worrying about pops.
size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t req)
{
- u8 flow = al_atomic_load(u8)(&buf->flow, AL_ATOMIC_ACQUIRE);
- if (flow == SIGNALED || camu_clock_is_ended(buf->clock)) {
- buf->callback(buf->userdata, CAMU_BUFFER_EOF);
- return 0;
- }
-
if (al_atomic_load(s32)(&buf->volume.set, AL_ATOMIC_ACQUIRE) > 0) {
buf->volume.user = al_atomic_load(f32)(&buf->volume.queued, AL_ATOMIC_RELAXED);
#ifdef CAMU_AUDIO_BUFFER_FADE
@@ -287,6 +294,7 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re
al_atomic_sub(s32)(&buf->unpause, 1, AL_ATOMIC_RELEASE);
}
+ f64 base = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
f64 pts = camu_clock_get_pts(buf->clock, buf->latency);
size_t ret, signal = req;
size_t have = al_ring_buffer_occupied(&buf->rb);
@@ -296,7 +304,7 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re
else have -= buf->fade.offset;
if (buf->pause != PAUSE_FADING && buf->pause != PAUSE_PLAYING) {
#endif
- pts -= buf->pts;
+ pts -= base;
// 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_PAUSED)) {
@@ -306,7 +314,7 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re
al_log_info("audio_buffer", "Skipping %fs of audio (%zu bytes).", pts, ret);
ret = al_ring_buffer_discard(&buf->rb, ret);
have -= ret;
- buf->pts += camu_audio_format_bytes_to_sec(&buf->fmt.req, ret);
+ increment_pts(buf, camu_audio_format_bytes_to_sec(&buf->fmt.req, ret), &base);
// Could go on to underrun.
} else if (pts < 0.0) { // Delay.
pts = -pts;
@@ -325,6 +333,7 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re
}
#endif
+ u8 flow = al_atomic_load(u8)(&buf->flow, AL_ATOMIC_ACQUIRE);
if (have < req) { // We don't have enough data to fulfill our request.
if (flow == FLUSHED) { // Stream is flushed.
// Check peak buffer for any remaining data.
@@ -375,7 +384,7 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re
// Read as much as we determined we can.
ret = al_ring_buffer_read(&buf->rb, data, req);
al_assert(ret == req);
- buf->pts += camu_audio_format_bytes_to_sec(&buf->fmt.req, ret);
+ increment_pts(buf, camu_audio_format_bytes_to_sec(&buf->fmt.req, ret), &base);
#ifdef CAMU_AUDIO_BUFFER_FADE
}
f32 step = 0.f;
@@ -386,6 +395,7 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re
}
if (step != 0.f || buf->fade.volume != 1.f) {
if (buf->volume.user > 1.f) {
+ // Only adjust step if it's an increase.
step *= buf->volume.user;
}
buf->fade.volume = apply_volume(data, req, &buf->fmt.req, buf->fade.volume, buf->volume.user, step);
diff --git a/src/buffer/audio.h b/src/buffer/audio.h
index 1d49448..b4d7594 100644
--- a/src/buffer/audio.h
+++ b/src/buffer/audio.h
@@ -13,9 +13,8 @@
struct camu_audio_buffer {
struct camu_codec_stream *stream;
- struct camu_mixer *mixer;
- f64 pts;
+ atomic(f64) pts;
u8 pause;
atomic(s32) unpause;
struct camu_clock *clock;
@@ -57,13 +56,14 @@ struct camu_audio_buffer {
void *userdata;
};
-bool camu_audio_buffer_init(struct camu_audio_buffer *buf, struct camu_clock *clock, struct camu_mixer *mixer);
-bool camu_audio_buffer_configure(struct camu_audio_buffer *buf, struct camu_codec_stream *stream);
+bool camu_audio_buffer_init(struct camu_audio_buffer *buf, struct camu_clock *clock);
+bool camu_audio_buffer_configure(struct camu_audio_buffer *buf, struct camu_codec_stream *stream,
+ struct camu_mixer *mixer);
void camu_audio_buffer_set_volume(struct camu_audio_buffer *buf, f32 volume);
void camu_audio_buffer_set_latency(struct camu_audio_buffer *buf, f64 latency);
void camu_audio_buffer_push(struct camu_audio_buffer *buf, struct camu_codec_frame *frame);
void camu_audio_buffer_unpause(struct camu_audio_buffer *buf);
-void camu_audio_buffer_reset(struct camu_audio_buffer *buf);
void camu_audio_buffer_flush(struct camu_audio_buffer *buf);
+void camu_audio_buffer_reset(struct camu_audio_buffer *buf);
size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t req);
void camu_audio_buffer_free(struct camu_audio_buffer *buf);
diff --git a/src/buffer/frame_queue.h b/src/buffer/frame_queue.h
index 0175668..f670ded 100644
--- a/src/buffer/frame_queue.h
+++ b/src/buffer/frame_queue.h
@@ -5,6 +5,7 @@
#endif
#include "../codec/codec.h"
+#include "../buffer/video.h"
enum {
CAMU_QUEUE_OK = 0,
@@ -14,9 +15,12 @@ enum {
};
struct camu_frame_queue {
+ struct camu_video_buffer *buf;
void (*push)(struct camu_frame_queue *, struct camu_codec_frame *, f64);
#ifdef CAMU_HAVE_FFMPEG
void (*push_av_frame)(struct camu_frame_queue *, AVFrame *, f64);
+ void (*configure_subtitles)(struct camu_frame_queue *, s32, s32, AVCodecParameters *);
+ void (*push_subtitle)(struct camu_frame_queue *, AVPacket *);
#endif
void (*flush)(struct camu_frame_queue *);
s32 (*count)(struct camu_frame_queue *);
diff --git a/src/buffer/video.c b/src/buffer/video.c
index a8b35c6..b528141 100644
--- a/src/buffer/video.c
+++ b/src/buffer/video.c
@@ -15,14 +15,15 @@
#define BUFFER_MARK_HIGH ((1.0 / 30.0) * 12)
#define BUFFER_MARK_RESET (BUFFER_MARK_HIGH * 2.0)
-bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *clock, struct camu_renderer *renderer)
+bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *clock)
{
buf->clock = clock;
buf->latency = 0.0;
- buf->pts = -1.0;
+ al_atomic_store(f64)(&buf->pts, -1.0, AL_ATOMIC_RELAXED);
// Defaulting single_frame to true can simplify non-configured buffers in sink.
buf->single_frame = true;
- buf->queue = renderer->create_queue(renderer);
+ buf->avg_frame_duration = 0.0;
+ buf->queue = NULL;
buf->buffered = false;
al_atomic_store(u8)(&buf->flow, FLOWING, AL_ATOMIC_RELAXED);
#ifdef CAMU_SCREEN_THREADED
@@ -32,27 +33,23 @@ bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *cl
return true;
}
-bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_codec_stream *stream)
+bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_codec_stream *stream,
+ struct camu_renderer *renderer)
{
buf->stream = stream;
+ buf->queue = renderer->create_queue(renderer);
+ buf->queue->buf = buf;
struct camu_video_format *fmt = &buf->stream->video.fmt;
switch (stream->mode) {
case CAMU_NORMAL: {
camu_video_format_copy(&buf->fmt.in, fmt);
-
- buf->single_frame = true;
- buf->avg_frame_duration = 0.0;
-
const char *format_name = camu_pixel_format_name(fmt->format);
- al_log_info("video_buffer", "Stream: %s (%dx%d) %s.",
- format_name, fmt->width, fmt->height, "IMAGE");
-
+ al_log_info("video_buffer", "Stream: %s (%dx%d) %s.", format_name, fmt->width, fmt->height, "IMAGE");
break;
}
#ifdef CAMU_HAVE_FFMPEG
case CAMU_FFMPEG_COMPAT: {
AVCodecParameters *codecpar = stream->av.stream->codecpar;
- s64 duration = stream->av.stream->duration;
AVRational frame_rate = stream->av.stream->avg_frame_rate;
fmt->width = codecpar->width;
@@ -60,7 +57,7 @@ bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_code
fmt->format = codecpar->format;
camu_video_format_copy(&buf->fmt.in, fmt);
- buf->single_frame = duration == 0 || frame_rate.den == 0;
+ buf->single_frame = stream->duration == 0 || frame_rate.den == 0;
if (buf->single_frame) frame_rate = (AVRational){ 0, 1 };
buf->avg_frame_duration = buf->single_frame ? 0.0 : av_q2d(av_inv_q(frame_rate));
@@ -129,25 +126,33 @@ static void after_push_internal(struct camu_video_buffer *buf)
static bool push_av_frame_internal(struct camu_video_buffer *buf, AVFrame *frame)
{
f64 pts = 0.0;
+ f64 base = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
if (!buf->single_frame) {
AVStream *stream = buf->stream->av.stream;
pts = frame->best_effort_timestamp * av_q2d(stream->time_base);
- if (pts + buf->avg_frame_duration < camu_clock_get_base_pts(buf->clock)) {
+ // If buf->pts = -1.0, this is the first frame pushed either on
+ // a new stream or after a reset(). Meaning, we can safely set buf->pts
+ // from this thread.
+ if (base == -1.0) {
+ base = camu_clock_get_base_pts(buf->clock);
+ al_atomic_store(f64)(&buf->pts, base, AL_ATOMIC_RELEASE);
+ }
+ if (pts + buf->avg_frame_duration < base) {
av_frame_free(&frame);
return false;
}
+ } else if (base == -1.0) {
+ al_atomic_store(f64)(&buf->pts, 0.0, AL_ATOMIC_RELEASE);
}
- if (buf->pts == -1.0) buf->pts = pts;
#ifdef CAMU_VIDEO_BUFFER_FORCE_SCALER
if (buf->fmt.scaler_needed) {
if (!buf->scale->scale(buf->scale, (const u8 **)frame->data, frame->linesize)) {
return false;
}
av_frame_free(&frame);
- frame = av_frame_clone(buf->scale->get_frame(buf->scale));
+ frame = av_frame_clone(buf->scale->get_frame(buf->scale)->av.frame);
}
#endif
- if (frame) frame->opaque = buf;
buf->queue->push_av_frame(buf->queue, frame, pts);
return true;
}
@@ -161,9 +166,14 @@ void camu_video_buffer_push(struct camu_video_buffer *buf, struct camu_codec_fra
return;
}
switch (frame->mode) {
- case CAMU_NORMAL:
+ case CAMU_NORMAL: {
+ f64 base = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
+ if (base == -1.0) {
+ al_atomic_store(f64)(&buf->pts, 0.0, AL_ATOMIC_RELEASE);
+ }
buf->queue->push(buf->queue, frame, 0.0);
break;
+ }
#ifdef CAMU_HAVE_FFMPEG
case CAMU_FFMPEG_COMPAT: {
bool pushed = push_av_frame_internal(buf, frame->av.frame);
@@ -176,15 +186,19 @@ void camu_video_buffer_push(struct camu_video_buffer *buf, struct camu_codec_fra
after_push_internal(buf);
}
-// Not thread-safe, must be called while the buffer is not being read from or written to.
-void camu_video_buffer_reset(struct camu_video_buffer *buf)
+#ifdef CAMU_HAVE_FFMPEG
+void camu_video_buffer_configure_subtitles(struct camu_video_buffer *buf, AVCodecParameters *codecpar)
{
- buf->pts = -1.0;
- buf->queue->reset(buf->queue);
- buf->buffered = false;
- al_atomic_store(u8)(&buf->flow, FLOWING, AL_ATOMIC_RELAXED);
+ struct camu_video_format *fmt = &buf->stream->video.fmt;
+ buf->queue->configure_subtitles(buf->queue, fmt->width, fmt->height, codecpar);
}
+void camu_video_buffer_push_subtitle(struct camu_video_buffer *buf, AVPacket *pkt)
+{
+ buf->queue->push_subtitle(buf->queue, pkt);
+}
+#endif
+
// flush() always comes from the same thread as push().
void camu_video_buffer_flush(struct camu_video_buffer *buf)
{
@@ -198,18 +212,28 @@ void camu_video_buffer_flush(struct camu_video_buffer *buf)
al_atomic_store(u8)(&buf->flow, FLUSHED, AL_ATOMIC_RELAXED);
}
+// Not thread-safe, must be called while the buffer is not being read from or written to.
+void camu_video_buffer_reset(struct camu_video_buffer *buf)
+{
+ al_atomic_store(f64)(&buf->pts, -1.0, AL_ATOMIC_RELAXED);
+ if (buf->queue) buf->queue->reset(buf->queue);
+ buf->buffered = false;
+ al_atomic_store(u8)(&buf->flow, FLOWING, AL_ATOMIC_RELAXED);
+}
+
bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out)
{
- u8 flow = al_atomic_load(u8)(&buf->flow, AL_ATOMIC_ACQUIRE);
+ f64 base = 0.0;
if (!buf->single_frame) {
- if (flow == SIGNALED || camu_clock_is_ended(buf->clock)) {
- buf->callback(buf->userdata, CAMU_BUFFER_EOF);
- return false;
- }
+ base = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
f64 pts = camu_clock_get_pts(buf->clock, buf->latency);
- if (pts > buf->pts) buf->pts = pts;
+ if (pts > base) {
+ base = pts;
+ al_atomic_store(f64)(&buf->pts, base, AL_ATOMIC_RELEASE);
+ }
}
- u8 ret = buf->queue->read(buf->queue, buf->pts, out);
+ u8 ret = buf->queue->read(buf->queue, base, out);
+ u8 flow = al_atomic_load(u8)(&buf->flow, AL_ATOMIC_ACQUIRE);
if (flow == FLUSHED && (ret == CAMU_QUEUE_EOF || (buf->single_frame && ret == CAMU_QUEUE_OK))) {
buf->callback(buf->userdata, CAMU_BUFFER_EOF);
al_atomic_store(u8)(&buf->flow, SIGNALED, AL_ATOMIC_RELEASE);
@@ -228,5 +252,5 @@ void camu_video_buffer_free(struct camu_video_buffer *buf)
#ifdef CAMU_VIDEO_BUFFER_FORCE_SCALER
if (buf->fmt.scaler_needed) buf->scale->free(&buf->scale);
#endif
- buf->queue->free(&buf->queue);
+ if (buf->queue) buf->queue->free(&buf->queue);
}
diff --git a/src/buffer/video.h b/src/buffer/video.h
index 0b48361..fd7ec9f 100644
--- a/src/buffer/video.h
+++ b/src/buffer/video.h
@@ -6,7 +6,6 @@
#include "../codec/codec.h"
#include "../render/renderer.h"
-#include "../screen/screen.h"
#include "../screen/view.h"
#include "clock.h"
@@ -17,7 +16,7 @@ struct camu_video_buffer {
struct camu_clock *clock;
f64 latency;
- f64 pts;
+ atomic(f64) pts;
bool single_frame;
f64 avg_frame_duration;
@@ -42,12 +41,17 @@ struct camu_video_buffer {
void *userdata;
};
-bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *clock, struct camu_renderer *renderer);
-bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_codec_stream *stream);
+bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *clock);
+bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_codec_stream *stream,
+ struct camu_renderer *renderer);
void camu_video_buffer_set_latency(struct camu_video_buffer *buf, s32 frames);
bool camu_video_buffer_is_single_frame(struct camu_video_buffer *buf);
void camu_video_buffer_push(struct camu_video_buffer *buf, struct camu_codec_frame *frame);
-void camu_video_buffer_reset(struct camu_video_buffer *buf);
+#ifdef CAMU_HAVE_FFMPEG
+void camu_video_buffer_configure_subtitles(struct camu_video_buffer *buf, AVCodecParameters *codecpar);
+void camu_video_buffer_push_subtitle(struct camu_video_buffer *buf, AVPacket *pkt);
+#endif
void camu_video_buffer_flush(struct camu_video_buffer *buf);
+void camu_video_buffer_reset(struct camu_video_buffer *buf);
bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out);
void camu_video_buffer_free(struct camu_video_buffer *buf);