mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-08-04 23:29:22 +00:00
buffering_thread
This commit is contained in:
parent
05c8ed1640
commit
40721126e2
4 changed files with 218 additions and 22 deletions
|
@ -308,15 +308,21 @@ screen_init(struct screen *screen, const struct screen_params *params) {
|
||||||
.on_new_frame = sc_video_buffer_on_new_frame,
|
.on_new_frame = sc_video_buffer_on_new_frame,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ok = sc_video_buffer_init(&screen->vb, &cbs, screen);
|
bool ok = sc_video_buffer_init(&screen->vb, 0, &cbs, screen);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGE("Could not initialize video buffer");
|
LOGE("Could not initialize video buffer");
|
||||||
return false;
|
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)) {
|
if (!fps_counter_init(&screen->fps_counter)) {
|
||||||
LOGE("Could not initialize FPS counter");
|
LOGE("Could not initialize FPS counter");
|
||||||
goto error_destroy_video_buffer;
|
goto error_stop_and_join_video_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
screen->frame_size = params->frame_size;
|
screen->frame_size = params->frame_size;
|
||||||
|
@ -457,6 +463,9 @@ error_destroy_window:
|
||||||
SDL_DestroyWindow(screen->window);
|
SDL_DestroyWindow(screen->window);
|
||||||
error_destroy_fps_counter:
|
error_destroy_fps_counter:
|
||||||
fps_counter_destroy(&screen->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:
|
error_destroy_video_buffer:
|
||||||
sc_video_buffer_destroy(&screen->vb);
|
sc_video_buffer_destroy(&screen->vb);
|
||||||
|
|
||||||
|
@ -475,11 +484,13 @@ screen_hide_window(struct screen *screen) {
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_interrupt(struct screen *screen) {
|
screen_interrupt(struct screen *screen) {
|
||||||
|
sc_video_buffer_stop(&screen->vb);
|
||||||
fps_counter_interrupt(&screen->fps_counter);
|
fps_counter_interrupt(&screen->fps_counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
screen_join(struct screen *screen) {
|
screen_join(struct screen *screen) {
|
||||||
|
sc_video_buffer_join(&screen->vb);
|
||||||
fps_counter_join(&screen->fps_counter);
|
fps_counter_join(&screen->fps_counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -159,16 +159,22 @@ sc_v4l2_sink_open(struct sc_v4l2_sink *vs) {
|
||||||
.on_new_frame = sc_video_buffer_on_new_frame,
|
.on_new_frame = sc_video_buffer_on_new_frame,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ok = sc_video_buffer_init(&vs->vb, &cbs, vs);
|
bool ok = sc_video_buffer_init(&vs->vb, 0, &cbs, vs);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGE("Could not initialize video buffer");
|
LOGE("Could not initialize video buffer");
|
||||||
return false;
|
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);
|
ok = sc_mutex_init(&vs->mutex);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGC("Could not create mutex");
|
LOGC("Could not create mutex");
|
||||||
goto error_video_buffer_destroy;
|
goto error_video_buffer_stop_and_join;
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = sc_cond_init(&vs->cond);
|
ok = sc_cond_init(&vs->cond);
|
||||||
|
@ -293,6 +299,9 @@ error_cond_destroy:
|
||||||
sc_cond_destroy(&vs->cond);
|
sc_cond_destroy(&vs->cond);
|
||||||
error_mutex_destroy:
|
error_mutex_destroy:
|
||||||
sc_mutex_destroy(&vs->mutex);
|
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:
|
error_video_buffer_destroy:
|
||||||
sc_video_buffer_destroy(&vs->vb);
|
sc_video_buffer_destroy(&vs->vb);
|
||||||
|
|
||||||
|
@ -306,7 +315,10 @@ sc_v4l2_sink_close(struct sc_v4l2_sink *vs) {
|
||||||
sc_cond_signal(&vs->cond);
|
sc_cond_signal(&vs->cond);
|
||||||
sc_mutex_unlock(&vs->mutex);
|
sc_mutex_unlock(&vs->mutex);
|
||||||
|
|
||||||
|
sc_video_buffer_stop(&vs->vb);
|
||||||
|
|
||||||
sc_thread_join(&vs->thread, NULL);
|
sc_thread_join(&vs->thread, NULL);
|
||||||
|
sc_video_buffer_join(&vs->vb);
|
||||||
|
|
||||||
av_packet_free(&vs->packet);
|
av_packet_free(&vs->packet);
|
||||||
av_frame_free(&vs->frame);
|
av_frame_free(&vs->frame);
|
||||||
|
|
|
@ -6,30 +6,37 @@
|
||||||
|
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
bool
|
static struct sc_video_buffer_frame *
|
||||||
sc_video_buffer_init(struct sc_video_buffer *vb,
|
sc_video_buffer_frame_new(const AVFrame *frame) {
|
||||||
const struct sc_video_buffer_callbacks *cbs,
|
struct sc_video_buffer_frame *vb_frame = malloc(sizeof(*vb_frame));
|
||||||
void *cbs_userdata) {
|
if (!vb_frame) {
|
||||||
bool ok = sc_frame_buffer_init(&vb->fb);
|
return NULL;
|
||||||
if (!ok) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(cbs);
|
vb_frame->frame = av_frame_alloc();
|
||||||
assert(cbs->on_new_frame);
|
if (!vb_frame->frame) {
|
||||||
|
free(vb_frame);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
vb->cbs = cbs;
|
if (av_frame_ref(vb_frame->frame, frame)) {
|
||||||
vb->cbs_userdata = cbs_userdata;
|
av_frame_free(&vb_frame->frame);
|
||||||
return true;
|
free(vb_frame);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vb_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
sc_video_buffer_destroy(struct sc_video_buffer *vb) {
|
sc_video_buffer_frame_delete(struct sc_video_buffer_frame *vb_frame) {
|
||||||
sc_frame_buffer_destroy(&vb->fb);
|
av_frame_unref(vb_frame->frame);
|
||||||
|
av_frame_free(&vb_frame->frame);
|
||||||
|
free(vb_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
static bool
|
||||||
sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame) {
|
sc_video_buffer_offer(struct sc_video_buffer *vb, const AVFrame *frame) {
|
||||||
bool previous_skipped;
|
bool previous_skipped;
|
||||||
bool ok = sc_frame_buffer_push(&vb->fb, frame, &previous_skipped);
|
bool ok = sc_frame_buffer_push(&vb->fb, frame, &previous_skipped);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
|
@ -40,6 +47,143 @@ sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame) {
|
||||||
return true;
|
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) {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sc_video_buffer_frame *vb_frame;
|
||||||
|
sc_queue_take(&vb->b.queue, next, &vb_frame);
|
||||||
|
|
||||||
|
sc_mutex_unlock(&vb->b.mutex);
|
||||||
|
|
||||||
|
usleep(vb->buffering_ms * 1000);
|
||||||
|
|
||||||
|
sc_video_buffer_offer(vb, vb_frame->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.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_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;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
sc_video_buffer_consume(struct sc_video_buffer *vb, AVFrame *dst) {
|
sc_video_buffer_consume(struct sc_video_buffer *vb, AVFrame *dst) {
|
||||||
sc_frame_buffer_consume(&vb->fb, dst);
|
sc_frame_buffer_consume(&vb->fb, dst);
|
||||||
|
|
|
@ -6,13 +6,33 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "frame_buffer.h"
|
#include "frame_buffer.h"
|
||||||
|
#include "util/queue.h"
|
||||||
|
#include "util/thread.h"
|
||||||
|
|
||||||
// forward declarations
|
// forward declarations
|
||||||
typedef struct AVFrame AVFrame;
|
typedef struct AVFrame AVFrame;
|
||||||
|
|
||||||
|
struct sc_video_buffer_frame {
|
||||||
|
AVFrame *frame;
|
||||||
|
struct sc_video_buffer_frame *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sc_video_buffer_frame_queue SC_QUEUE(struct sc_video_buffer_frame);
|
||||||
|
|
||||||
struct sc_video_buffer {
|
struct sc_video_buffer {
|
||||||
struct sc_frame_buffer fb;
|
struct sc_frame_buffer fb;
|
||||||
|
|
||||||
|
unsigned buffering_ms;
|
||||||
|
|
||||||
|
// only if buffering_ms > 0
|
||||||
|
struct {
|
||||||
|
sc_thread thread;
|
||||||
|
sc_mutex mutex;
|
||||||
|
sc_cond queue_cond;
|
||||||
|
struct sc_video_buffer_frame_queue queue;
|
||||||
|
bool stopped;
|
||||||
|
} b; // buffering
|
||||||
|
|
||||||
const struct sc_video_buffer_callbacks *cbs;
|
const struct sc_video_buffer_callbacks *cbs;
|
||||||
void *cbs_userdata;
|
void *cbs_userdata;
|
||||||
};
|
};
|
||||||
|
@ -23,10 +43,19 @@ struct sc_video_buffer_callbacks {
|
||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_video_buffer_init(struct sc_video_buffer *vb,
|
sc_video_buffer_init(struct sc_video_buffer *vb, unsigned buffering_ms,
|
||||||
const struct sc_video_buffer_callbacks *cbs,
|
const struct sc_video_buffer_callbacks *cbs,
|
||||||
void *cbs_userdata);
|
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);
|
||||||
|
|
||||||
void
|
void
|
||||||
sc_video_buffer_destroy(struct sc_video_buffer *vb);
|
sc_video_buffer_destroy(struct sc_video_buffer *vb);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue