Compare commits

..

19 Commits

Author SHA1 Message Date
Romain Vimont
6093235a96 Assert screen closed on destroy
The destruction order is important, but tricky, because the screen is
open/close by the decoder, but destroyed by scrcpy.c on the main thread.

Add assertions to guarantee that the screen is not destroyed before
being closed.
2021-04-12 15:20:54 +02:00
Romain Vimont
9307cb3c96 Remove video_buffer callbacks
Now that screen is both the owner and the listener of the video buffer,
execute the code directly without callbacks.
2021-04-12 15:20:54 +02:00
Romain Vimont
350d9bfbfb Move video_buffer to screen
The video buffer is now an internal detail of the screen component.

Since the screen is plugged to the decoder via the frame sink trait, the
decoder does not access to the video buffer anymore.
2021-04-12 15:20:54 +02:00
Romain Vimont
082083b83e Make decoder push frames to sinks
Now that screen implements the packet sink trait, make decoder push
packets to the sinks without depending on the concrete sink types.
2021-04-12 15:20:54 +02:00
Romain Vimont
58c2619a6d Expose screen as frame sink
Make screen implement the frame sink trait.

This will allow the decoder to push frames without depending on the
concrete sink type.
2021-04-12 15:20:54 +02:00
Romain Vimont
6ba6ec40e3 Add frame sink trait
This trait will allow to abstract the concrete sink types from the frame
producer (decoder.c).
2021-04-12 15:20:54 +02:00
Romain Vimont
070525de06 Make stream push packets to sinks
Now that decoder and recorder implement the packet sink trait, make
stream push packets to the sinks without depending on the concrete sink
types.
2021-04-12 15:20:54 +02:00
Romain Vimont
3fb0594fe8 Expose decoder as packet sink
Make decoder implement the packet sink trait.

This will allow the stream to push packets without depending on the
concrete sink type.
2021-04-12 15:20:54 +02:00
Romain Vimont
d470943505 Reorder decoder functions
This will make further commits more readable.
2021-04-12 15:20:54 +02:00
Romain Vimont
1be76c5113 Expose recorder as packet sink
Make recorder implement the packet sink trait.

This will allow the stream to push packets without depending on the
concrete sink type.
2021-04-12 15:20:54 +02:00
Romain Vimont
6ff4e5c926 Privatize recorder threading
The fact that the recorder uses a separate thread is an internal detail,
so the functions _start(), _stop() and _join() should not be exposed.

Instead, start the thread on _open() and _stop()+_join() on close().

This paves the way to expose the recorder as a packet sink trait.
2021-04-12 15:20:54 +02:00
Romain Vimont
b2e9270e53 Reorder recorder functions
This will make further commits more readable.
2021-04-12 15:20:54 +02:00
Romain Vimont
f3879fc92d Add packet sink trait
This trait will allow to abstract the concrete sink types from the
packet producer (stream.c).
2021-04-12 15:20:54 +02:00
Romain Vimont
042ace4b4e Add container_of() macro
This will allow to get the parent of an embedded struct.
2021-04-11 15:01:05 +02:00
Romain Vimont
f43d66d5d6 Make video_buffer more generic
The video buffer took ownership of the producer frame (so that it could
swap frames quickly).

In order to support multiple sinks plugged to the decoder, the decoded
frame must not be consumed by the display video buffer.

Therefore, move the producer and consumer frames out of the video
buffer, and use FFmpeg AVFrame refcounting to share ownership while
avoiding copies.
2021-04-11 15:01:05 +02:00
Romain Vimont
e1b2824730 Remove compat with old FFmpeg codec params API
The new API has been introduced in 2016 in libavformat 57.xx, it's very
old.

This will avoid to maintain two code paths for codec parameters.
2021-04-11 15:01:05 +02:00
Romain Vimont
19b518bf24 Remove compat with old FFmpeg decoding API
The new API has been introduced in 2016 in libavcodec 57.xx, it's very
old.

This will avoid to maintain two code paths for decoding.
2021-04-11 15:01:05 +02:00
Romain Vimont
3ffea72fa6 Remove option --render-expired-frames
This flag forced the decoder to wait for the previous frame to be
consumed by the display.

It was initially implemented as a compilation flag for testing, not
intended to be exposed at runtime. But to remove ifdefs and to allow
users to test this flag easily, it had finally been exposed by commit
ebccb9f6cc.

In practice, it turned out to be useless: it had no practical impact,
and it did not solve or mitigate any performance issues causing frame
skipping.

But that added some complexity to the codebase: it required an
additional condition variable, and made video buffer calls possibly
blocking, which in turn required code to interrupt it on exit.

To prepare support for multiple sinks plugged to the decoder (display
and v4l2 for example), the blocking call used for pacing the decoder
output becomes unacceptable, so just remove this useless "feature".
2021-04-11 15:01:05 +02:00
Romain Vimont
a34bd5c43b Write trailer from recorder thread
The recorder thread wrote the whole content except the trailer, which
was odd.
2021-04-11 15:01:05 +02:00
3 changed files with 39 additions and 52 deletions

View File

@@ -126,6 +126,30 @@ sdl_init_and_configure(bool display, const char *render_driver,
return true;
}
#if defined(__APPLE__) || defined(__WINDOWS__)
# define CONTINUOUS_RESIZING_WORKAROUND
#endif
#ifdef CONTINUOUS_RESIZING_WORKAROUND
// On Windows and MacOS, resizing blocks the event loop, so resizing events are
// not triggered. As a workaround, handle them in an event handler.
//
// <https://bugzilla.libsdl.org/show_bug.cgi?id=2077>
// <https://stackoverflow.com/a/40693139/1987178>
static int
event_watcher(void *data, SDL_Event *event) {
(void) data;
if (event->type == SDL_WINDOWEVENT
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
// In practice, it seems to always be called from the same thread in
// that specific case. Anyway, it's just a workaround.
screen_render(&screen, true);
}
return 0;
}
#endif
static bool
is_apk(const char *file) {
const char *ext = strrchr(file, '.');
@@ -165,7 +189,7 @@ handle_event(SDL_Event *event, const struct scrcpy_options *options) {
action = ACTION_PUSH_FILE;
}
file_handler_request(&file_handler, action, file);
goto end;
break;
}
}
@@ -183,6 +207,11 @@ end:
static bool
event_loop(const struct scrcpy_options *options) {
#ifdef CONTINUOUS_RESIZING_WORKAROUND
if (options->display) {
SDL_AddEventWatch(event_watcher, NULL);
}
#endif
SDL_Event event;
while (SDL_WaitEvent(&event)) {
enum event_result result = handle_event(&event, options);
@@ -365,7 +394,6 @@ scrcpy(const struct scrcpy_options *options) {
.window_borderless = options->window_borderless,
.rotation = options->rotation,
.mipmaps = options->mipmaps,
.fullscreen = options->fullscreen,
};
if (!screen_init(&screen, &fps_counter, &screen_params)) {
@@ -384,6 +412,10 @@ scrcpy(const struct scrcpy_options *options) {
LOGW("Could not request 'set screen power mode'");
}
}
if (options->fullscreen) {
screen_switch_fullscreen(&screen);
}
}
// now we consumed the header values, the socket receives the video stream
@@ -398,10 +430,6 @@ scrcpy(const struct scrcpy_options *options) {
ret = event_loop(options);
LOGD("quit...");
// Close the window immediately on closing, because screen_destroy() may
// only be called once the stream thread is joined (it may take time)
screen_hide_window(&screen);
end:
// The stream is not stopped explicitly, because it will stop by itself on
// end-of-stream

View File

@@ -220,29 +220,6 @@ create_texture(struct screen *screen) {
return texture;
}
#if defined(__APPLE__) || defined(__WINDOWS__)
# define CONTINUOUS_RESIZING_WORKAROUND
#endif
#ifdef CONTINUOUS_RESIZING_WORKAROUND
// On Windows and MacOS, resizing blocks the event loop, so resizing events are
// not triggered. As a workaround, handle them in an event handler.
//
// <https://bugzilla.libsdl.org/show_bug.cgi?id=2077>
// <https://stackoverflow.com/a/40693139/1987178>
static int
event_watcher(void *data, SDL_Event *event) {
struct screen *screen = data;
if (event->type == SDL_WINDOWEVENT
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
// In practice, it seems to always be called from the same thread in
// that specific case. Anyway, it's just a workaround.
screen_render(screen, true);
}
return 0;
}
#endif
static bool
screen_frame_sink_open(struct sc_frame_sink *sink) {
struct screen *screen = DOWNCAST(sink);
@@ -425,14 +402,6 @@ screen_init(struct screen *screen, struct fps_counter *fps_counter,
screen_update_content_rect(screen);
if (params->fullscreen) {
screen_switch_fullscreen(screen);
}
#ifdef CONTINUOUS_RESIZING_WORKAROUND
SDL_AddEventWatch(event_watcher, screen);
#endif
static const struct sc_frame_sink_ops ops = {
.open = screen_frame_sink_open,
.close = screen_frame_sink_close,
@@ -448,16 +417,11 @@ screen_init(struct screen *screen, struct fps_counter *fps_counter,
return true;
}
static void
void
screen_show_window(struct screen *screen) {
SDL_ShowWindow(screen->window);
}
void
screen_hide_window(struct screen *screen) {
SDL_HideWindow(screen->window);
}
void
screen_destroy(struct screen *screen) {
#ifndef NDEBUG

View File

@@ -62,8 +62,6 @@ struct screen_params {
uint8_t rotation;
bool mipmaps;
bool fullscreen;
};
// initialize screen, create window, renderer and texture (window is hidden)
@@ -71,17 +69,14 @@ bool
screen_init(struct screen *screen, struct fps_counter *fps_counter,
const struct screen_params *params);
// show the window
void
screen_show_window(struct screen *screen);
// destroy window, renderer and texture (if any)
void
screen_destroy(struct screen *screen);
// hide the window
//
// It is used to hide the window immediately on closing without waiting for
// screen_destroy()
void
screen_hide_window(struct screen *screen);
// render the texture to the renderer
//
// Set the update_content_rect flag if the window or content size may have