Compare commits

..

4 Commits

Author SHA1 Message Date
Romain Vimont
86c4cbcf9e Extract current video_buffer to frame_buffer
The current video buffer only stores one pending frame.

In order to add a new buffering feature, move this part to a separate
"frame buffer". Keep the video_buffer, which currently delegates all its
calls to the frame_buffer.
2021-06-27 20:29:07 +02:00
Romain Vimont
7917dc313e Rename video_buffer to sc_video_buffer
Add a scrcpy-specific prefix.
2021-06-26 16:05:56 +02:00
Romain Vimont
d743e03f44 Move include fps_counter
The fps_counter is not used from video_buffer.
2021-06-26 16:04:52 +02:00
Romain Vimont
9217cfa079 Remove obsolete comment
Commit 2a94a2b119 removed video_buffer
callbacks, the comment is now meaningless.
2021-06-26 16:04:52 +02:00
14 changed files with 87 additions and 449 deletions

View File

@@ -1,6 +1,7 @@
#include "fps_counter.h"
#include <assert.h>
#include <SDL2/SDL_timer.h>
#include "util/log.h"
@@ -81,12 +82,14 @@ run_fps_counter(void *data) {
sc_cond_wait(&counter->state_cond, &counter->mutex);
}
while (!counter->interrupted && is_started(counter)) {
sc_tick now = sc_tick_now();
uint32_t now = SDL_GetTicks();
check_interval_expired(counter, now);
assert(counter->next_timestamp > now);
uint32_t remaining = counter->next_timestamp - now;
// ignore the reason (timeout or signaled), we just loop anyway
sc_cond_timedwait(&counter->state_cond, &counter->mutex,
counter->next_timestamp);
sc_cond_timedwait(&counter->state_cond, &counter->mutex, remaining);
}
}
sc_mutex_unlock(&counter->mutex);
@@ -96,7 +99,7 @@ run_fps_counter(void *data) {
bool
fps_counter_start(struct fps_counter *counter) {
sc_mutex_lock(&counter->mutex);
counter->next_timestamp = sc_tick_now() + FPS_COUNTER_INTERVAL_MS;
counter->next_timestamp = SDL_GetTicks() + FPS_COUNTER_INTERVAL_MS;
counter->nr_rendered = 0;
counter->nr_skipped = 0;
sc_mutex_unlock(&counter->mutex);
@@ -162,7 +165,7 @@ fps_counter_add_rendered_frame(struct fps_counter *counter) {
}
sc_mutex_lock(&counter->mutex);
sc_tick now = sc_tick_now();
uint32_t now = SDL_GetTicks();
check_interval_expired(counter, now);
++counter->nr_rendered;
sc_mutex_unlock(&counter->mutex);
@@ -175,7 +178,7 @@ fps_counter_add_skipped_frame(struct fps_counter *counter) {
}
sc_mutex_lock(&counter->mutex);
sc_tick now = sc_tick_now();
uint32_t now = SDL_GetTicks();
check_interval_expired(counter, now);
++counter->nr_skipped;
sc_mutex_unlock(&counter->mutex);

View File

@@ -24,7 +24,7 @@ struct fps_counter {
bool interrupted;
unsigned nr_rendered;
unsigned nr_skipped;
sc_tick next_timestamp;
uint32_t next_timestamp;
};
bool

View File

@@ -51,15 +51,16 @@ record_packet_new(const AVPacket *packet) {
static void
record_packet_delete(struct record_packet *rec) {
av_packet_unref(rec->packet);
av_packet_free(&rec->packet);
free(rec);
}
static void
recorder_queue_clear(struct recorder_queue *queue) {
while (!sc_queue_is_empty(queue)) {
while (!queue_is_empty(queue)) {
struct record_packet *rec;
sc_queue_take(queue, next, &rec);
queue_take(queue, next, &rec);
record_packet_delete(rec);
}
}
@@ -135,14 +136,14 @@ run_recorder(void *data) {
for (;;) {
sc_mutex_lock(&recorder->mutex);
while (!recorder->stopped && sc_queue_is_empty(&recorder->queue)) {
while (!recorder->stopped && queue_is_empty(&recorder->queue)) {
sc_cond_wait(&recorder->queue_cond, &recorder->mutex);
}
// if stopped is set, continue to process the remaining events (to
// finish the recording) before actually stopping
if (recorder->stopped && sc_queue_is_empty(&recorder->queue)) {
if (recorder->stopped && queue_is_empty(&recorder->queue)) {
sc_mutex_unlock(&recorder->mutex);
struct record_packet *last = recorder->previous;
if (last) {
@@ -161,7 +162,7 @@ run_recorder(void *data) {
}
struct record_packet *rec;
sc_queue_take(&recorder->queue, next, &rec);
queue_take(&recorder->queue, next, &rec);
sc_mutex_unlock(&recorder->mutex);
@@ -235,7 +236,7 @@ recorder_open(struct recorder *recorder, const AVCodec *input_codec) {
goto error_mutex_destroy;
}
sc_queue_init(&recorder->queue);
queue_init(&recorder->queue);
recorder->stopped = false;
recorder->failed = false;
recorder->header_written = false;
@@ -340,7 +341,7 @@ recorder_push(struct recorder *recorder, const AVPacket *packet) {
return false;
}
sc_queue_push(&recorder->queue, next, rec);
queue_push(&recorder->queue, next, rec);
sc_cond_signal(&recorder->queue_cond);
sc_mutex_unlock(&recorder->mutex);

View File

@@ -17,7 +17,7 @@ struct record_packet {
struct record_packet *next;
};
struct recorder_queue SC_QUEUE(struct record_packet);
struct recorder_queue QUEUE(struct record_packet);
struct recorder {
struct sc_packet_sink packet_sink; // packet sink trait

View File

@@ -274,16 +274,14 @@ screen_frame_sink_close(struct sc_frame_sink *sink) {
static bool
screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) {
struct screen *screen = DOWNCAST(sink);
return sc_video_buffer_push(&screen->vb, frame);
}
static void
sc_video_buffer_on_new_frame(struct sc_video_buffer *vb, bool previous_skipped,
void *userdata) {
(void) vb;
struct screen *screen = userdata;
bool previous_frame_skipped;
bool ok = sc_video_buffer_push(&screen->vb, frame, &previous_frame_skipped);
if (!ok) {
return false;
}
if (previous_skipped) {
if (previous_frame_skipped) {
fps_counter_add_skipped_frame(&screen->fps_counter);
// The EVENT_NEW_FRAME triggered for the previous frame will consume
// this new frame instead
@@ -295,6 +293,8 @@ sc_video_buffer_on_new_frame(struct sc_video_buffer *vb, bool previous_skipped,
// Post the event on the UI thread
SDL_PushEvent(&new_frame_event);
}
return true;
}
bool
@@ -304,25 +304,15 @@ screen_init(struct screen *screen, const struct screen_params *params) {
screen->fullscreen = false;
screen->maximized = false;
static const struct sc_video_buffer_callbacks cbs = {
.on_new_frame = sc_video_buffer_on_new_frame,
};
bool ok = sc_video_buffer_init(&screen->vb, 2000, &cbs, screen);
bool ok = sc_video_buffer_init(&screen->vb);
if (!ok) {
LOGE("Could not initialize video buffer");
return false;
}
ok = sc_video_buffer_start(&screen->vb);
if (!ok) {
LOGE("Could not start video_buffer");
goto error_destroy_video_buffer;
}
if (!fps_counter_init(&screen->fps_counter)) {
LOGE("Could not initialize FPS counter");
goto error_stop_and_join_video_buffer;
goto error_destroy_video_buffer;
}
screen->frame_size = params->frame_size;
@@ -463,9 +453,6 @@ error_destroy_window:
SDL_DestroyWindow(screen->window);
error_destroy_fps_counter:
fps_counter_destroy(&screen->fps_counter);
error_stop_and_join_video_buffer:
sc_video_buffer_stop(&screen->vb);
sc_video_buffer_join(&screen->vb);
error_destroy_video_buffer:
sc_video_buffer_destroy(&screen->vb);
@@ -484,13 +471,11 @@ screen_hide_window(struct screen *screen) {
void
screen_interrupt(struct screen *screen) {
sc_video_buffer_stop(&screen->vb);
fps_counter_interrupt(&screen->fps_counter);
}
void
screen_join(struct screen *screen) {
sc_video_buffer_join(&screen->vb);
fps_counter_join(&screen->fps_counter);
}

View File

@@ -557,7 +557,7 @@ server_stop(struct server *server) {
#define WATCHDOG_DELAY_MS 1000
signaled = sc_cond_timedwait(&server->process_terminated_cond,
&server->mutex,
sc_tick_now() + WATCHDOG_DELAY_MS);
WATCHDOG_DELAY_MS);
}
sc_mutex_unlock(&server->mutex);

View File

@@ -151,6 +151,7 @@ stream_push_packet(struct stream *stream, AVPacket *packet) {
if (stream->pending) {
// the pending packet must be discarded (consumed or error)
av_packet_unref(stream->pending);
av_packet_free(&stream->pending);
}
@@ -243,6 +244,7 @@ run_stream(void *data) {
LOGD("End of frames");
if (stream->pending) {
av_packet_unref(stream->pending);
av_packet_free(&stream->pending);
}

View File

@@ -1,6 +1,6 @@
// generic intrusive FIFO queue
#ifndef SC_QUEUE_H
#define SC_QUEUE_H
#ifndef QUEUE_H
#define QUEUE_H
#include "common.h"
@@ -10,15 +10,15 @@
// To define a queue type of "struct foo":
// struct queue_foo QUEUE(struct foo);
#define SC_QUEUE(TYPE) { \
#define QUEUE(TYPE) { \
TYPE *first; \
TYPE *last; \
}
#define sc_queue_init(PQ) \
#define queue_init(PQ) \
(void) ((PQ)->first = (PQ)->last = NULL)
#define sc_queue_is_empty(PQ) \
#define queue_is_empty(PQ) \
!(PQ)->first
// NEXTFIELD is the field in the ITEM type used for intrusive linked-list
@@ -30,30 +30,30 @@
// };
//
// // define the type "struct my_queue"
// struct my_queue SC_QUEUE(struct foo);
// struct my_queue QUEUE(struct foo);
//
// struct my_queue queue;
// sc_queue_init(&queue);
// queue_init(&queue);
//
// struct foo v1 = { .value = 42 };
// struct foo v2 = { .value = 27 };
//
// sc_queue_push(&queue, next, v1);
// sc_queue_push(&queue, next, v2);
// queue_push(&queue, next, v1);
// queue_push(&queue, next, v2);
//
// struct foo *foo;
// sc_queue_take(&queue, next, &foo);
// queue_take(&queue, next, &foo);
// assert(foo->value == 42);
// sc_queue_take(&queue, next, &foo);
// queue_take(&queue, next, &foo);
// assert(foo->value == 27);
// assert(sc_queue_is_empty(&queue));
// assert(queue_is_empty(&queue));
//
// push a new item into the queue
#define sc_queue_push(PQ, NEXTFIELD, ITEM) \
#define queue_push(PQ, NEXTFIELD, ITEM) \
(void) ({ \
(ITEM)->NEXTFIELD = NULL; \
if (sc_queue_is_empty(PQ)) { \
if (queue_is_empty(PQ)) { \
(PQ)->first = (PQ)->last = (ITEM); \
} else { \
(PQ)->last->NEXTFIELD = (ITEM); \
@@ -65,9 +65,9 @@
// the result is stored in *(PITEM)
// (without typeof(), we could not store a local variable having the correct
// type so that we can "return" it)
#define sc_queue_take(PQ, NEXTFIELD, PITEM) \
#define queue_take(PQ, NEXTFIELD, PITEM) \
(void) ({ \
assert(!sc_queue_is_empty(PQ)); \
assert(!queue_is_empty(PQ)); \
*(PITEM) = (PQ)->first; \
(PQ)->first = (PQ)->first->NEXTFIELD; \
})

View File

@@ -2,7 +2,6 @@
#include <assert.h>
#include <SDL2/SDL_thread.h>
#include <SDL2/SDL_timer.h>
#include "log.h"
@@ -124,14 +123,8 @@ sc_cond_wait(sc_cond *cond, sc_mutex *mutex) {
}
bool
sc_cond_timedwait(sc_cond *cond, sc_mutex *mutex, sc_tick deadline) {
sc_tick now = sc_tick_now();
if (deadline <= now) {
return false; // timeout
}
sc_tick delay = deadline - now;
int r = SDL_CondWaitTimeout(cond->cond, mutex->mutex, delay);
sc_cond_timedwait(sc_cond *cond, sc_mutex *mutex, uint32_t ms) {
int r = SDL_CondWaitTimeout(cond->cond, mutex->mutex, ms);
#ifndef NDEBUG
if (r < 0) {
LOGC("Could not wait on condition with timeout: %s", SDL_GetError());
@@ -170,11 +163,3 @@ sc_cond_broadcast(sc_cond *cond) {
(void) r;
#endif
}
sc_tick
sc_tick_now(void) {
// SDL ticks is an unsigned 32 bits, but this is an implementation detail.
// It wraps if the program runs for more than ~49 days, but in practice we
// can assume it does not.
return (sc_tick) SDL_GetTicks();
}

View File

@@ -16,8 +16,6 @@ typedef int sc_thread_fn(void *);
typedef unsigned sc_thread_id;
typedef atomic_uint sc_atomic_thread_id;
typedef int64_t sc_tick;
typedef struct sc_thread {
SDL_Thread *thread;
} sc_thread;
@@ -74,7 +72,7 @@ sc_cond_wait(sc_cond *cond, sc_mutex *mutex);
// return true on signaled, false on timeout
bool
sc_cond_timedwait(sc_cond *cond, sc_mutex *mutex, sc_tick deadline);
sc_cond_timedwait(sc_cond *cond, sc_mutex *mutex, uint32_t ms);
void
sc_cond_signal(sc_cond *cond);
@@ -82,7 +80,4 @@ sc_cond_signal(sc_cond *cond);
void
sc_cond_broadcast(sc_cond *cond);
sc_tick
sc_tick_now(void);
#endif

View File

@@ -121,10 +121,10 @@ run_v4l2_sink(void *data) {
break;
}
vs->has_frame = false;
sc_mutex_unlock(&vs->mutex);
sc_video_buffer_consume(&vs->vb, vs->frame);
vs->has_frame = false;
sc_mutex_unlock(&vs->mutex);
bool ok = encode_and_write_frame(vs, vs->frame);
av_frame_unref(vs->frame);
@@ -139,42 +139,17 @@ run_v4l2_sink(void *data) {
return 0;
}
static void
sc_video_buffer_on_new_frame(struct sc_video_buffer *vb, bool previous_skipped,
void *userdata) {
(void) vb;
struct sc_v4l2_sink *vs = userdata;
if (!previous_skipped) {
sc_mutex_lock(&vs->mutex);
vs->has_frame = true;
sc_cond_signal(&vs->cond);
sc_mutex_unlock(&vs->mutex);
}
}
static bool
sc_v4l2_sink_open(struct sc_v4l2_sink *vs) {
static const struct sc_video_buffer_callbacks cbs = {
.on_new_frame = sc_video_buffer_on_new_frame,
};
bool ok = sc_video_buffer_init(&vs->vb, 1, &cbs, vs);
bool ok = sc_video_buffer_init(&vs->vb);
if (!ok) {
LOGE("Could not initialize video buffer");
return false;
}
ok = sc_video_buffer_start(&vs->vb);
if (!ok) {
LOGE("Could not start video buffer");
goto error_video_buffer_destroy;
}
ok = sc_mutex_init(&vs->mutex);
if (!ok) {
LOGC("Could not create mutex");
goto error_video_buffer_stop_and_join;
goto error_video_buffer_destroy;
}
ok = sc_cond_init(&vs->cond);
@@ -299,9 +274,6 @@ error_cond_destroy:
sc_cond_destroy(&vs->cond);
error_mutex_destroy:
sc_mutex_destroy(&vs->mutex);
error_video_buffer_stop_and_join:
sc_video_buffer_stop(&vs->vb);
sc_video_buffer_join(&vs->vb);
error_video_buffer_destroy:
sc_video_buffer_destroy(&vs->vb);
@@ -315,10 +287,7 @@ sc_v4l2_sink_close(struct sc_v4l2_sink *vs) {
sc_cond_signal(&vs->cond);
sc_mutex_unlock(&vs->mutex);
sc_video_buffer_stop(&vs->vb);
sc_thread_join(&vs->thread, NULL);
sc_video_buffer_join(&vs->vb);
av_packet_free(&vs->packet);
av_frame_free(&vs->frame);
@@ -333,7 +302,19 @@ sc_v4l2_sink_close(struct sc_v4l2_sink *vs) {
static bool
sc_v4l2_sink_push(struct sc_v4l2_sink *vs, const AVFrame *frame) {
return sc_video_buffer_push(&vs->vb, frame);
sc_mutex_lock(&vs->mutex);
bool ok = sc_video_buffer_push(&vs->vb, frame, NULL);
if (!ok) {
return false;
}
vs->has_frame = true;
sc_cond_signal(&vs->cond);
sc_mutex_unlock(&vs->mutex);
return true;
}
static bool

View File

@@ -1,284 +1,25 @@
#include "video_buffer.h"
#include <assert.h>
#include <stdlib.h>
#include <libavutil/avutil.h>
#include <libavformat/avformat.h>
#include "util/log.h"
#define SC_CLOCK_AVERAGE_RANGE 32
static void
sc_clock_init(struct sc_clock *clock) {
clock->coeff = 1;
clock->offset = 0;
clock->weight = 0;
clock->last.system = 0;
clock->last.stream = 0;
}
static sc_tick
sc_clock_to_system_ts(struct sc_clock *clock, sc_tick stream_ts) {
assert(clock->weight); // sc_clock_update() must have been called
return (sc_tick) (stream_ts * clock->coeff) + clock->offset;
}
static void
sc_clock_update(struct sc_clock *clock, sc_tick now, sc_tick stream_ts) {
double instant_coeff;
sc_tick mad;
if (clock->weight) {
sc_tick system_delta = now - clock->last.system;
sc_tick stream_delta = stream_ts - clock->last.stream;
instant_coeff = (double) system_delta / stream_delta;
sc_tick system_pts = sc_clock_to_system_ts(clock, stream_ts);
mad = llabs(now - system_pts);
} else {
// This is the first update, we cannot compute delta
instant_coeff = 1;
mad = 0;
}
if (clock->weight < SC_CLOCK_AVERAGE_RANGE) {
++clock->weight;
}
// (1-t) * avg + t * new
clock->coeff = ((clock->weight - 1) * clock->coeff + instant_coeff)
/ clock->weight;
// FIXME it cannot change at every frame!
clock->offset = now - (sc_tick) (stream_ts * clock->coeff);
LOGD("%g x + %ld", clock->coeff, clock->offset);
clock->mad = ((clock->weight - 1) * clock->mad + mad) / clock->weight;
clock->last.system = now;
clock->last.stream = stream_ts;
}
static struct sc_video_buffer_frame *
sc_video_buffer_frame_new(const AVFrame *frame) {
struct sc_video_buffer_frame *vb_frame = malloc(sizeof(*vb_frame));
if (!vb_frame) {
return NULL;
}
vb_frame->frame = av_frame_alloc();
if (!vb_frame->frame) {
free(vb_frame);
return NULL;
}
if (av_frame_ref(vb_frame->frame, frame)) {
av_frame_free(&vb_frame->frame);
free(vb_frame);
return NULL;
}
return vb_frame;
}
static void
sc_video_buffer_frame_delete(struct sc_video_buffer_frame *vb_frame) {
av_frame_unref(vb_frame->frame);
av_frame_free(&vb_frame->frame);
free(vb_frame);
}
static bool
sc_video_buffer_offer(struct sc_video_buffer *vb, const AVFrame *frame) {
bool previous_skipped;
bool ok = sc_frame_buffer_push(&vb->fb, frame, &previous_skipped);
if (!ok) {
return false;
}
vb->cbs->on_new_frame(vb, previous_skipped, vb->cbs_userdata);
return true;
}
static int
run_buffering(void *data) {
struct sc_video_buffer *vb = data;
assert(vb->buffering_ms);
for (;;) {
sc_mutex_lock(&vb->b.mutex);
while (!vb->b.stopped && sc_queue_is_empty(&vb->b.queue)) {
sc_cond_wait(&vb->b.queue_cond, &vb->b.mutex);
}
if (vb->b.stopped) {
sc_mutex_unlock(&vb->b.mutex);
goto stopped;
}
struct sc_video_buffer_frame *vb_frame;
sc_queue_take(&vb->b.queue, next, &vb_frame);
sc_tick now = sc_tick_now();
// FIXME time_base units
int64_t pts = vb_frame->frame->pts; // micros to millis
LOGD("==== pts = %ld", pts);
sc_tick system_pts = sc_clock_to_system_ts(&vb->b.clock, pts);
if (now + vb->buffering_ms < system_pts) {
system_pts = now + vb->buffering_ms;
}
sc_tick deadline = system_pts + vb->buffering_ms;
LOGD("==== %ld %ld %ld\n", now, system_pts, deadline);
LOGD("[WAITING FOR] %ld ", deadline-now);
bool timed_out = false;
while (!vb->b.stopped && !timed_out) {
timed_out =
!sc_cond_timedwait(&vb->b.wait_cond, &vb->b.mutex, deadline);
}
if (vb->b.stopped) {
sc_video_buffer_frame_delete(vb_frame);
sc_mutex_unlock(&vb->b.mutex);
goto stopped;
}
sc_mutex_unlock(&vb->b.mutex);
sc_video_buffer_offer(vb, vb_frame->frame);
sc_video_buffer_frame_delete(vb_frame);
}
stopped:
// Flush queue
while (!sc_queue_is_empty(&vb->b.queue)) {
struct sc_video_buffer_frame *vb_frame;
sc_queue_take(&vb->b.queue, next, &vb_frame);
sc_video_buffer_frame_delete(vb_frame);
}
LOGD("Buffering thread ended");
return 0;
}
bool
sc_video_buffer_init(struct sc_video_buffer *vb, unsigned buffering_ms,
const struct sc_video_buffer_callbacks *cbs,
void *cbs_userdata) {
bool ok = sc_frame_buffer_init(&vb->fb);
if (!ok) {
return false;
}
if (buffering_ms) {
ok = sc_mutex_init(&vb->b.mutex);
if (!ok) {
LOGC("Could not create mutex");
sc_frame_buffer_destroy(&vb->fb);
return false;
}
ok = sc_cond_init(&vb->b.queue_cond);
if (!ok) {
LOGC("Could not create cond");
sc_mutex_destroy(&vb->b.mutex);
sc_frame_buffer_destroy(&vb->fb);
return false;
}
ok = sc_cond_init(&vb->b.wait_cond);
if (!ok) {
LOGC("Could not create wait cond");
sc_cond_destroy(&vb->b.queue_cond);
sc_mutex_destroy(&vb->b.mutex);
sc_frame_buffer_destroy(&vb->fb);
return false;
}
sc_clock_init(&vb->b.clock);
sc_queue_init(&vb->b.queue);
}
assert(cbs);
assert(cbs->on_new_frame);
vb->buffering_ms = buffering_ms;
vb->cbs = cbs;
vb->cbs_userdata = cbs_userdata;
return true;
}
bool
sc_video_buffer_start(struct sc_video_buffer *vb) {
if (vb->buffering_ms) {
bool ok =
sc_thread_create(&vb->b.thread, run_buffering, "buffering", vb);
if (!ok) {
LOGE("Could not start buffering thread");
return false;
}
}
return true;
}
void
sc_video_buffer_stop(struct sc_video_buffer *vb) {
if (vb->buffering_ms) {
sc_mutex_lock(&vb->b.mutex);
vb->b.stopped = true;
sc_cond_signal(&vb->b.queue_cond);
sc_cond_signal(&vb->b.wait_cond);
sc_mutex_unlock(&vb->b.mutex);
}
}
void
sc_video_buffer_join(struct sc_video_buffer *vb) {
if (vb->buffering_ms) {
sc_thread_join(&vb->b.thread, NULL);
}
sc_video_buffer_init(struct sc_video_buffer *vb) {
return sc_frame_buffer_init(&vb->fb);
}
void
sc_video_buffer_destroy(struct sc_video_buffer *vb) {
sc_frame_buffer_destroy(&vb->fb);
if (vb->buffering_ms) {
sc_cond_destroy(&vb->b.wait_cond);
sc_cond_destroy(&vb->b.queue_cond);
sc_mutex_destroy(&vb->b.mutex);
}
}
bool
sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame) {
if (!vb->buffering_ms) {
// no buffering
return sc_video_buffer_offer(vb, frame);
}
struct sc_video_buffer_frame *vb_frame = sc_video_buffer_frame_new(frame);
if (!vb_frame) {
LOGE("Could not allocate frame");
return false;
}
sc_clock_update(&vb->b.clock, sc_tick_now(), vb_frame->frame->pts);
sc_mutex_lock(&vb->b.mutex);
sc_queue_push(&vb->b.queue, next, vb_frame);
sc_cond_signal(&vb->b.queue_cond);
sc_mutex_unlock(&vb->b.mutex);
return true;
sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame,
bool *previous_frame_skipped) {
return sc_frame_buffer_push(&vb->fb, frame, previous_frame_skipped);
}
void

View File

@@ -6,78 +6,23 @@
#include <stdbool.h>
#include "frame_buffer.h"
#include "util/queue.h"
#include "util/thread.h"
// forward declarations
typedef struct AVFrame AVFrame;
struct sc_video_buffer_frame {
AVFrame *frame;
sc_tick system_pts;
struct sc_video_buffer_frame *next;
};
struct sc_video_buffer_frame_queue SC_QUEUE(struct sc_video_buffer_frame);
struct sc_clock {
double coeff;
sc_tick offset;
unsigned weight;
struct {
sc_tick system;
sc_tick stream;
} last;
sc_tick mad; // mean absolute difference
};
struct sc_video_buffer {
struct sc_frame_buffer fb;
unsigned buffering_ms;
// only if buffering_ms > 0
struct {
sc_thread thread;
sc_mutex mutex;
sc_cond queue_cond;
sc_cond wait_cond;
struct sc_clock clock;
struct sc_video_buffer_frame_queue queue;
bool stopped;
} b; // buffering
const struct sc_video_buffer_callbacks *cbs;
void *cbs_userdata;
};
struct sc_video_buffer_callbacks {
void (*on_new_frame)(struct sc_video_buffer *vb, bool previous_skipped,
void *userdata);
};
bool
sc_video_buffer_init(struct sc_video_buffer *vb, unsigned buffering_ms,
const struct sc_video_buffer_callbacks *cbs,
void *cbs_userdata);
bool
sc_video_buffer_start(struct sc_video_buffer *vb);
void
sc_video_buffer_stop(struct sc_video_buffer *vb);
void
sc_video_buffer_join(struct sc_video_buffer *vb);
sc_video_buffer_init(struct sc_video_buffer *vb);
void
sc_video_buffer_destroy(struct sc_video_buffer *vb);
bool
sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame);
sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame,
bool *skipped);
void
sc_video_buffer_consume(struct sc_video_buffer *vb, AVFrame *dst);

View File

@@ -10,28 +10,28 @@ struct foo {
};
static void test_queue(void) {
struct my_queue SC_QUEUE(struct foo) queue;
sc_queue_init(&queue);
struct my_queue QUEUE(struct foo) queue;
queue_init(&queue);
assert(sc_queue_is_empty(&queue));
assert(queue_is_empty(&queue));
struct foo v1 = { .value = 42 };
struct foo v2 = { .value = 27 };
sc_queue_push(&queue, next, &v1);
sc_queue_push(&queue, next, &v2);
queue_push(&queue, next, &v1);
queue_push(&queue, next, &v2);
struct foo *foo;
assert(!sc_queue_is_empty(&queue));
sc_queue_take(&queue, next, &foo);
assert(!queue_is_empty(&queue));
queue_take(&queue, next, &foo);
assert(foo->value == 42);
assert(!sc_queue_is_empty(&queue));
sc_queue_take(&queue, next, &foo);
assert(!queue_is_empty(&queue));
queue_take(&queue, next, &foo);
assert(foo->value == 27);
assert(sc_queue_is_empty(&queue));
assert(queue_is_empty(&queue));
}
int main(int argc, char *argv[]) {