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
15 changed files with 218 additions and 260 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

@@ -315,26 +315,26 @@ Also see [issue #14].
## Shortcuts
| Action | Shortcut |
| -------------------------------------- |:---------------------------- |
| Switch fullscreen mode | `Ctrl`+`f` |
| Resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` |
| Resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_ |
| Click on `HOME` | `Ctrl`+`h` \| _Middle-click_ |
| Click on `BACK` | `Ctrl`+`b` \| _Right-click²_ |
| Click on `APP_SWITCH` | `Ctrl`+`s` |
| Click on `MENU` | `Ctrl`+`m` |
| Click on `VOLUME_UP` | `Ctrl`+`↑` _(up)_ (`Cmd`+`↑` on macOS) |
| Click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_ (`Cmd`+`↓` on macOS) |
| Click on `POWER` | `Ctrl`+`p` |
| Power on | _Right-click²_ |
| Turn device screen off (keep mirroring)| `Ctrl`+`o` |
| Expand notification panel | `Ctrl`+`n` |
| Collapse notification panel | `Ctrl`+`Shift`+`n` |
| Copy device clipboard to computer | `Ctrl`+`c` |
| Paste computer clipboard to device | `Ctrl`+`v` |
| Copy computer clipboard to device | `Ctrl`+`Shift`+`v` |
| Enable/disable FPS counter (on stdout) | `Ctrl`+`i` |
| Action | Shortcut | Shortcut (macOS)
| -------------------------------------- |:----------------------------- |:-----------------------------
| Switch fullscreen mode | `Ctrl`+`f` | `Cmd`+`f`
| Resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` | `Cmd`+`g`
| Resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_ | `Cmd`+`x` \| _Double-click¹_
| Click on `HOME` | `Ctrl`+`h` \| _Middle-click_ | `Ctrl`+`h` \| _Middle-click_
| Click on `BACK` | `Ctrl`+`b` \| _Right-click²_ | `Cmd`+`b` \| _Right-click²_
| Click on `APP_SWITCH` | `Ctrl`+`s` | `Cmd`+`s`
| Click on `MENU` | `Ctrl`+`m` | `Ctrl`+`m`
| Click on `VOLUME_UP` | `Ctrl`+`↑` _(up)_ | `Cmd`+`↑` _(up)_
| Click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_ | `Cmd`+`↓` _(down)_
| Click on `POWER` | `Ctrl`+`p` | `Cmd`+`p`
| Power on | _Right-click²_ | _Right-click²_
| Turn device screen off (keep mirroring)| `Ctrl`+`o` | `Cmd`+`o`
| Expand notification panel | `Ctrl`+`n` | `Cmd`+`n`
| Collapse notification panel | `Ctrl`+`Shift`+`n` | `Cmd`+`Shift`+`n`
| Copy device clipboard to computer | `Ctrl`+`c` | `Cmd`+`c`
| Paste computer clipboard to device | `Ctrl`+`v` | `Cmd`+`v`
| Copy computer clipboard to device | `Ctrl`+`Shift`+`v` | `Cmd`+`Shift`+`v`
| Enable/disable FPS counter (on stdout) | `Ctrl`+`i` | `Cmd`+`i`
_¹Double-click on black borders to remove them._
_²Right-click turns the screen on if it was off, presses BACK otherwise._

View File

@@ -1,6 +1,5 @@
src = [
'src/main.c',
'src/buffered_reader.c',
'src/command.c',
'src/control_msg.c',
'src/controller.c',
@@ -151,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

@@ -1,72 +0,0 @@
#include "buffered_reader.h"
#include <SDL2/SDL_assert.h>
#include "log.h"
bool
buffered_reader_init(struct buffered_reader *reader, socket_t socket,
size_t bufsize) {
reader->buf = SDL_malloc(bufsize);
if (!reader->buf) {
LOGC("Could not allocate buffer");
return false;
}
reader->socket = socket;
reader->bufsize = bufsize;
reader->offset = 0;
reader->len = 0;
return true;
}
void
buffered_reader_destroy(struct buffered_reader *reader) {
SDL_free(reader->buf);
}
static ssize_t
buffered_reader_fill(struct buffered_reader *reader) {
SDL_assert(!reader->len);
ssize_t r = net_recv(reader->socket, reader->buf, reader->bufsize);
if (r > 0) {
reader->offset = 0;
reader->len = r;
}
return r;
}
ssize_t
buffered_reader_recv(struct buffered_reader *reader, void *buf, size_t count) {
if (!reader->len) {
// read from the socket
ssize_t r = buffered_reader_fill(reader);
if (r <= 0) {
return r;
}
}
size_t r = count < reader->len ? count : reader->len;
memcpy(buf, reader->buf + reader->offset, r);
reader->offset += r;
reader->len -= r;
return r;
}
ssize_t
buffered_reader_recv_all(struct buffered_reader *reader, void *buf,
size_t count) {
size_t done = 0;
while (done < count) {
ssize_t r = buffered_reader_recv(reader, buf, count - done);
if (r <= 0) {
// if there was some data, return them immediately
return done ? done : r;
}
done += r;
buf += r;
}
return done;
}

View File

@@ -1,29 +0,0 @@
#ifndef BUFFERED_READER_H
#define BUFFERED_READER_H
#include "common.h"
#include "net.h"
struct buffered_reader {
socket_t socket;
void *buf;
size_t bufsize;
size_t offset;
size_t len;
};
bool
buffered_reader_init(struct buffered_reader *reader, socket_t socket,
size_t bufsize);
void
buffered_reader_destroy(struct buffered_reader *reader);
ssize_t
buffered_reader_recv(struct buffered_reader *reader, void *buf, size_t count);
ssize_t
buffered_reader_recv_all(struct buffered_reader *reader, void *buf,
size_t count);
#endif

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) \
({ \

View File

@@ -242,16 +242,27 @@ input_manager_process_key(struct input_manager *input_manager,
bool alt = event->keysym.mod & (KMOD_LALT | KMOD_RALT);
bool meta = event->keysym.mod & (KMOD_LGUI | KMOD_RGUI);
// use Cmd on macOS, Ctrl on other platforms
#ifdef __APPLE__
bool cmd = !ctrl && meta;
#else
if (meta) {
// no shortcuts involve Meta on platforms other than macOS, and it must
// not be forwarded to the device
return;
}
bool cmd = ctrl; // && !meta, already guaranteed
#endif
if (alt) {
// no shortcut involves Alt or Meta, and they should not be forwarded
// to the device
// no shortcuts involve Alt, and it must not be forwarded to the device
return;
}
struct controller *controller = input_manager->controller;
// capture all Ctrl events
if (ctrl | meta) {
if (ctrl || cmd) {
SDL_Keycode keycode = event->keysym.sym;
bool down = event->type == SDL_KEYDOWN;
int action = down ? ACTION_DOWN : ACTION_UP;
@@ -259,63 +270,59 @@ input_manager_process_key(struct input_manager *input_manager,
bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT);
switch (keycode) {
case SDLK_h:
// Ctrl+h on all platform, since Cmd+h is already captured by
// the system on macOS to hide the window
if (control && ctrl && !meta && !shift && !repeat) {
action_home(controller, action);
}
return;
case SDLK_b: // fall-through
case SDLK_BACKSPACE:
if (control && ctrl && !meta && !shift && !repeat) {
if (control && cmd && !shift && !repeat) {
action_back(controller, action);
}
return;
case SDLK_s:
if (control && ctrl && !meta && !shift && !repeat) {
if (control && cmd && !shift && !repeat) {
action_app_switch(controller, action);
}
return;
case SDLK_m:
// Ctrl+m on all platform, since Cmd+m is already captured by
// the system on macOS to minimize the window
if (control && ctrl && !meta && !shift && !repeat) {
action_menu(controller, action);
}
return;
case SDLK_p:
if (control && ctrl && !meta && !shift && !repeat) {
if (control && cmd && !shift && !repeat) {
action_power(controller, action);
}
return;
case SDLK_o:
if (control && ctrl && !shift && !meta && down) {
if (control && cmd && !shift && down) {
set_screen_power_mode(controller, SCREEN_POWER_MODE_OFF);
}
return;
case SDLK_DOWN:
#ifdef __APPLE__
if (control && !ctrl && meta && !shift) {
#else
if (control && ctrl && !meta && !shift) {
#endif
if (control && cmd && !shift) {
// forward repeated events
action_volume_down(controller, action);
}
return;
case SDLK_UP:
#ifdef __APPLE__
if (control && !ctrl && meta && !shift) {
#else
if (control && ctrl && !meta && !shift) {
#endif
if (control && cmd && !shift) {
// forward repeated events
action_volume_up(controller, action);
}
return;
case SDLK_c:
if (control && ctrl && !meta && !shift && !repeat && down) {
if (control && cmd && !shift && !repeat && down) {
request_device_clipboard(controller);
}
return;
case SDLK_v:
if (control && ctrl && !meta && !repeat && down) {
if (control && cmd && !repeat && down) {
if (shift) {
// store the text in the device clipboard
set_device_clipboard(controller);
@@ -326,29 +333,29 @@ input_manager_process_key(struct input_manager *input_manager,
}
return;
case SDLK_f:
if (ctrl && !meta && !shift && !repeat && down) {
if (!shift && cmd && !repeat && down) {
screen_switch_fullscreen(input_manager->screen);
}
return;
case SDLK_x:
if (ctrl && !meta && !shift && !repeat && down) {
if (!shift && cmd && !repeat && down) {
screen_resize_to_fit(input_manager->screen);
}
return;
case SDLK_g:
if (ctrl && !meta && !shift && !repeat && down) {
if (!shift && cmd && !repeat && down) {
screen_resize_to_pixel_perfect(input_manager->screen);
}
return;
case SDLK_i:
if (ctrl && !meta && !shift && !repeat && down) {
if (!shift && cmd && !repeat && down) {
struct fps_counter *fps_counter =
input_manager->video_buffer->fps_counter;
switch_fps_counter_state(fps_counter);
}
return;
case SDLK_n:
if (control && ctrl && !meta && !repeat && down) {
if (control && cmd && !repeat && down) {
if (shift) {
collapse_notification_panel(controller);
} else {

View File

@@ -35,6 +35,11 @@ struct args {
};
static void usage(const char *arg0) {
#ifdef __APPLE__
# define CTRL_OR_CMD "Cmd"
#else
# define CTRL_OR_CMD "Ctrl"
#endif
fprintf(stderr,
"Usage: %s [options]\n"
"\n"
@@ -115,13 +120,13 @@ static void usage(const char *arg0) {
"\n"
"Shortcuts:\n"
"\n"
" Ctrl+f\n"
" " CTRL_OR_CMD "+f\n"
" switch fullscreen mode\n"
"\n"
" Ctrl+g\n"
" " CTRL_OR_CMD "+g\n"
" resize window to 1:1 (pixel-perfect)\n"
"\n"
" Ctrl+x\n"
" " CTRL_OR_CMD "+x\n"
" Double-click on black borders\n"
" resize window to remove black borders\n"
"\n"
@@ -129,48 +134,48 @@ static void usage(const char *arg0) {
" Middle-click\n"
" click on HOME\n"
"\n"
" Ctrl+b\n"
" Ctrl+Backspace\n"
" " CTRL_OR_CMD "+b\n"
" " CTRL_OR_CMD "+Backspace\n"
" Right-click (when screen is on)\n"
" click on BACK\n"
"\n"
" Ctrl+s\n"
" " CTRL_OR_CMD "+s\n"
" click on APP_SWITCH\n"
"\n"
" Ctrl+m\n"
" click on MENU\n"
"\n"
" Ctrl+Up\n"
" " CTRL_OR_CMD "+Up\n"
" click on VOLUME_UP\n"
"\n"
" Ctrl+Down\n"
" " CTRL_OR_CMD "+Down\n"
" click on VOLUME_DOWN\n"
"\n"
" Ctrl+p\n"
" " CTRL_OR_CMD "+p\n"
" click on POWER (turn screen on/off)\n"
"\n"
" Right-click (when screen is off)\n"
" power on\n"
"\n"
" Ctrl+o\n"
" " CTRL_OR_CMD "+o\n"
" turn device screen off (keep mirroring)\n"
"\n"
" Ctrl+n\n"
" " CTRL_OR_CMD "+n\n"
" expand notification panel\n"
"\n"
" Ctrl+Shift+n\n"
" " CTRL_OR_CMD "+Shift+n\n"
" collapse notification panel\n"
"\n"
" Ctrl+c\n"
" " CTRL_OR_CMD "+c\n"
" copy device clipboard to computer\n"
"\n"
" Ctrl+v\n"
" " CTRL_OR_CMD "+v\n"
" paste computer clipboard to device\n"
"\n"
" Ctrl+Shift+v\n"
" " CTRL_OR_CMD "+Shift+v\n"
" copy computer clipboard to device\n"
"\n"
" Ctrl+i\n"
" " CTRL_OR_CMD "+i\n"
" enable/disable FPS counter (print frames/second in logs)\n"
"\n"
" Drag & drop APK file\n"

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;

View File

@@ -297,7 +297,6 @@ scrcpy(const struct scrcpy_options *options) {
bool video_buffer_initialized = false;
bool file_handler_initialized = false;
bool recorder_initialized = false;
bool stream_initialized = false;
bool stream_started = false;
bool controller_initialized = false;
bool controller_started = false;
@@ -359,10 +358,7 @@ scrcpy(const struct scrcpy_options *options) {
av_log_set_callback(av_log_callback);
if (!stream_init(&stream, server.video_socket, dec, rec)) {
goto end;
}
stream_initialized = true;
stream_init(&stream, server.video_socket, dec, rec);
// now we consumed the header values, the socket receives the video stream
// start the stream
@@ -441,9 +437,6 @@ end:
if (stream_started) {
stream_join(&stream);
}
if (stream_initialized) {
stream_destroy(&stream);
}
if (controller_started) {
controller_join(&controller);
}

View File

@@ -17,7 +17,7 @@
#include "log.h"
#include "recorder.h"
#define STREAM_BUFSIZE 0x10000
#define BUFSIZE 0x10000
#define HEADER_SIZE 12
#define NO_PTS UINT64_C(-1)
@@ -37,8 +37,7 @@ stream_recv_packet(struct stream *stream, AVPacket *packet) {
// It is followed by <packet_size> bytes containing the packet/frame.
uint8_t header[HEADER_SIZE];
ssize_t r =
buffered_reader_recv_all(&stream->buffered_reader, header, HEADER_SIZE);
ssize_t r = net_recv_all(stream->socket, header, HEADER_SIZE);
if (r < HEADER_SIZE) {
return false;
}
@@ -52,7 +51,7 @@ stream_recv_packet(struct stream *stream, AVPacket *packet) {
return false;
}
r = buffered_reader_recv_all(&stream->buffered_reader, packet->data, len);
r = net_recv_all(stream->socket, packet->data, len);
if (r < len) {
av_packet_unref(packet);
return false;
@@ -268,23 +267,13 @@ end:
return 0;
}
bool
void
stream_init(struct stream *stream, socket_t socket,
struct decoder *decoder, struct recorder *recorder) {
if (!buffered_reader_init(&stream->buffered_reader, socket,
STREAM_BUFSIZE)) {
return false;
}
stream->socket = socket;
stream->decoder = decoder,
stream->recorder = recorder;
stream->has_pending = false;
return true;
}
void
stream_destroy(struct stream *stream) {
buffered_reader_destroy(&stream->buffered_reader);
}
bool

View File

@@ -7,14 +7,12 @@
#include <SDL2/SDL_atomic.h>
#include <SDL2/SDL_thread.h>
#include "buffered_reader.h"
#include "net.h"
struct video_buffer;
struct stream {
socket_t socket;
struct buffered_reader buffered_reader;
struct video_buffer *video_buffer;
SDL_Thread *thread;
struct decoder *decoder;
@@ -27,13 +25,10 @@ struct stream {
AVPacket pending;
};
bool
void
stream_init(struct stream *stream, socket_t socket,
struct decoder *decoder, struct recorder *recorder);
void
stream_destroy(struct stream *stream);
bool
stream_start(struct stream *stream);

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;
}