Compare commits

..

6 Commits

Author SHA1 Message Date
Romain Vimont
e2ac996183 Use Cmd instead of Ctrl on macOS when possible
Fixes <https://github.com/Genymobile/scrcpy/issues/642>
2019-08-03 23:13:44 +02:00
Romain Vimont
5e4ccfd832 Use generic FIFO queue for recording
Replace the specific recording queue by the new generic FIFO queue
implementation.
2019-08-01 23:15:47 +02:00
Romain Vimont
53b6ee2cf4 Add generic intrusive FIFO queue
We need several FIFO queues (a queue of packets, a queue of messages,
etc.).

Some of them are implemented using cbuf, a generic circular buffer. But
for recording, we need to store the packets in an unbounded queue until
they are written, so the queue was implemented manually.

Create a generic implementation (using macros) to avoid reimplementing
it every time.
2019-08-01 23:14:50 +02:00
Romain Vimont
26213f1031 Fix cbuf documentation 2019-08-01 22:50:03 +02:00
Romain Vimont
96b5067cbf Remove unnecessary backslash in cbuf 2019-08-01 22:08:34 +02:00
Romain Vimont
421e4be399 Remove root directory from Windows zip releases
Put the scrcpy files at the root of the zip archive. This avoids an
unnecessary level of directories when extracting.
2019-07-31 16:35:14 +02:00
7 changed files with 140 additions and 69 deletions

View File

@@ -125,12 +125,12 @@ dist-win64: build-server build-win64 build-win64-noconsole
cp prebuilt-deps/SDL2-2.0.8/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
zip-win32: dist-win32
cd "$(DIST)"; \
zip -r "$(WIN32_TARGET)" "$(WIN32_TARGET_DIR)"
cd "$(DIST)/$(WIN32_TARGET_DIR)"; \
zip -r "../$(WIN32_TARGET)" .
zip-win64: dist-win64
cd "$(DIST)"; \
zip -r "$(WIN64_TARGET)" "$(WIN64_TARGET_DIR)"
cd "$(DIST)/$(WIN64_TARGET_DIR)"; \
zip -r "../$(WIN64_TARGET)" .
sums:
cd "$(DIST)"; \

View File

@@ -150,6 +150,9 @@ tests = [
'tests/test_device_msg_deserialize.c',
'src/device_msg.c'
]],
['test_queue', [
'tests/test_queue.c',
]],
['test_strutil', [
'tests/test_strutil.c',
'src/str_util.c'

View File

@@ -6,7 +6,7 @@
#include <unistd.h>
// To define a circular buffer type of 20 ints:
// typedef CBUF(int, 20) my_cbuf_t;
// struct cbuf_int CBUF(int, 20);
//
// data has length CAP + 1 to distinguish empty vs full.
#define CBUF(TYPE, CAP) { \
@@ -35,7 +35,7 @@
(PCBUF)->head = ((PCBUF)->head + 1) % cbuf_size_(PCBUF); \
} \
ok; \
}) \
})
#define cbuf_take(PCBUF, PITEM) \
({ \

74
app/src/queue.h Normal file
View File

@@ -0,0 +1,74 @@
// generic intrusive FIFO queue
#ifndef QUEUE_H
#define QUEUE_H
#include <stdbool.h>
#include <SDL2/SDL_assert.h>
// To define a queue type of "struct foo":
// struct queue_foo QUEUE(struct foo);
#define QUEUE(TYPE) { \
TYPE *first; \
TYPE *last; \
}
#define queue_init(PQ) \
(void) ((PQ)->first = NULL)
#define queue_is_empty(PQ) \
!(PQ)->first
// NEXTFIELD is the field in the ITEM type used for intrusive linked-list
//
// For example:
// struct foo {
// int value;
// struct foo *next;
// };
//
// // define the type "struct my_queue"
// struct my_queue QUEUE(struct foo);
//
// struct my_queue queue;
// queue_init(&queue);
//
// struct foo v1 = { .value = 42 };
// struct foo v2 = { .value = 27 };
//
// queue_push(&queue, next, v1);
// queue_push(&queue, next, v2);
//
// struct foo *foo;
// queue_take(&queue, next, &foo);
// assert(foo->value == 42);
// queue_take(&queue, next, &foo);
// assert(foo->value == 27);
// assert(queue_is_empty(&queue));
//
// push a new item into the queue
#define queue_push(PQ, NEXTFIELD, ITEM) \
(void) ({ \
(ITEM)->NEXTFIELD = NULL; \
if (queue_is_empty(PQ)) { \
(PQ)->first = (PQ)->last = (ITEM); \
} else { \
(PQ)->last->NEXTFIELD = (ITEM); \
(PQ)->last = (ITEM); \
} \
})
// take the next item and remove it from the queue (the queue must not be empty)
// 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 queue_take(PQ, NEXTFIELD, PITEM) \
(void) ({ \
SDL_assert(!queue_is_empty(PQ)); \
*(PITEM) = (PQ)->first; \
(PQ)->first = (PQ)->first->NEXTFIELD; \
})
// no need to update (PQ)->last if the queue is left empty:
// (PQ)->last is undefined if !(PQ)->first anyway
#endif

View File

@@ -37,7 +37,6 @@ record_packet_new(const AVPacket *packet) {
SDL_free(rec);
return NULL;
}
rec->next = NULL;
return rec;
}
@@ -47,60 +46,13 @@ record_packet_delete(struct record_packet *rec) {
SDL_free(rec);
}
static void
recorder_queue_init(struct recorder_queue *queue) {
queue->first = NULL;
// queue->last is undefined if queue->first == NULL
}
static inline bool
recorder_queue_is_empty(struct recorder_queue *queue) {
return !queue->first;
}
static bool
recorder_queue_push(struct recorder_queue *queue, const AVPacket *packet) {
struct record_packet *rec = record_packet_new(packet);
if (!rec) {
LOGC("Could not allocate record packet");
return false;
}
rec->next = NULL;
if (recorder_queue_is_empty(queue)) {
queue->first = queue->last = rec;
} else {
// chain rec after the (current) last packet
queue->last->next = rec;
// the last packet is now rec
queue->last = rec;
}
return true;
}
static inline struct record_packet *
recorder_queue_take(struct recorder_queue *queue) {
SDL_assert(!recorder_queue_is_empty(queue));
struct record_packet *rec = queue->first;
SDL_assert(rec);
queue->first = rec->next;
// no need to update queue->last if the queue is left empty:
// queue->last is undefined if queue->first == NULL
return rec;
}
static void
recorder_queue_clear(struct recorder_queue *queue) {
struct record_packet *rec = queue->first;
while (rec) {
struct record_packet *current = rec;
rec = rec->next;
record_packet_delete(current);
while (!queue_is_empty(queue)) {
struct record_packet *rec;
queue_take(queue, next, &rec);
record_packet_delete(rec);
}
queue->first = NULL;
}
bool
@@ -129,7 +81,7 @@ recorder_init(struct recorder *recorder,
return false;
}
recorder_queue_init(&recorder->queue);
queue_init(&recorder->queue);
recorder->stopped = false;
recorder->failed = false;
recorder->format = format;
@@ -296,20 +248,20 @@ run_recorder(void *data) {
for (;;) {
mutex_lock(recorder->mutex);
while (!recorder->stopped &&
recorder_queue_is_empty(&recorder->queue)) {
while (!recorder->stopped && queue_is_empty(&recorder->queue)) {
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 && recorder_queue_is_empty(&recorder->queue)) {
if (recorder->stopped && queue_is_empty(&recorder->queue)) {
mutex_unlock(recorder->mutex);
break;
}
struct record_packet *rec = recorder_queue_take(&recorder->queue);
struct record_packet *rec;
queue_take(&recorder->queue, next, &rec);
mutex_unlock(recorder->mutex);
@@ -369,9 +321,15 @@ recorder_push(struct recorder *recorder, const AVPacket *packet) {
return false;
}
bool ok = recorder_queue_push(&recorder->queue, packet);
struct record_packet *rec = record_packet_new(packet);
if (!rec) {
LOGC("Could not allocate record packet");
return false;
}
queue_push(&recorder->queue, next, rec);
cond_signal(recorder->queue_cond);
mutex_unlock(recorder->mutex);
return ok;
return true;
}

View File

@@ -7,6 +7,7 @@
#include <SDL2/SDL_thread.h>
#include "common.h"
#include "queue.h"
enum recorder_format {
RECORDER_FORMAT_MP4 = 1,
@@ -18,10 +19,7 @@ struct record_packet {
struct record_packet *next;
};
struct recorder_queue {
struct record_packet *first;
struct record_packet *last; // undefined if first is NULL
};
struct recorder_queue QUEUE(struct record_packet);
struct recorder {
char *filename;

38
app/tests/test_queue.c Normal file
View File

@@ -0,0 +1,38 @@
#include <assert.h>
#include <queue.h>
struct foo {
int value;
struct foo *next;
};
static void test_queue(void) {
struct my_queue QUEUE(struct foo) queue;
queue_init(&queue);
assert(queue_is_empty(&queue));
struct foo v1 = { .value = 42 };
struct foo v2 = { .value = 27 };
queue_push(&queue, next, &v1);
queue_push(&queue, next, &v2);
struct foo *foo;
assert(!queue_is_empty(&queue));
queue_take(&queue, next, &foo);
assert(foo->value == 42);
assert(!queue_is_empty(&queue));
queue_take(&queue, next, &foo);
assert(foo->value == 27);
assert(queue_is_empty(&queue));
}
int main(void) {
test_queue();
return 0;
}