From b4dc78af0c86f442748ea70b17c25475392d9352 Mon Sep 17 00:00:00 2001 From: Killian Richard Date: Mon, 24 Feb 2020 10:13:29 +0100 Subject: [PATCH] Adding --serve option : - Add current working files --- README.md | 4 +- app/.project | 11 ++ app/meson.build | 1 + app/src/cli.c | 173 +++++++++++++++++- app/src/scrcpy.c | 16 +- app/src/scrcpy.h | 10 +- app/src/serve.c | 101 +++++++++++ app/src/serve.h | 28 +++ app/src/stream.c | 450 ++++++++++++++++++++++++----------------------- app/src/stream.h | 3 +- 10 files changed, 574 insertions(+), 223 deletions(-) create mode 100644 app/.project create mode 100644 app/src/serve.c create mode 100644 app/src/serve.h diff --git a/README.md b/README.md index 7397cc59..6684d8ca 100644 --- a/README.md +++ b/README.md @@ -213,8 +213,8 @@ To disable mirroring while forwarding the stream: ```bash scrcpy --no-display --serve tcp:localhost:1234 -scrcpy -Nr --serve tcp:localhost:1234 -# interrupt recording with Ctrl+C +scrcpy -N --serve tcp:localhost:1234 +# interrupt serve with Ctrl+C # Ctrl+C does not terminate properly on Windows, so disconnect the device ``` diff --git a/app/.project b/app/.project new file mode 100644 index 00000000..3256dae8 --- /dev/null +++ b/app/.project @@ -0,0 +1,11 @@ + + + app + + + + + + + + diff --git a/app/meson.build b/app/meson.build index 3bcb9bc1..fbda1340 100644 --- a/app/meson.build +++ b/app/meson.build @@ -15,6 +15,7 @@ src = [ 'src/recorder.c', 'src/scrcpy.c', 'src/screen.c', + 'src/serve.c', 'src/server.c', 'src/stream.c', 'src/tiny_xpm.c', diff --git a/app/src/cli.c b/app/src/cli.c index d9e1013a..a449f912 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -6,9 +6,12 @@ #include "config.h" #include "recorder.h" +#include "serve.h" #include "util/log.h" #include "util/str_util.h" +#define IPV4_LOCALHOST 0x7F000001 + void scrcpy_print_usage(const char *arg0) { #ifdef __APPLE__ @@ -79,6 +82,11 @@ scrcpy_print_usage(const char *arg0) { " The format is determined by the --record-format option if\n" " set, or by the file extension (.mp4 or .mkv).\n" "\n" + " --serve tcp:localhost:1234\n" + " Open a socket to redirect video stream.\n" + " It will Wait for a client to connect before starting the mirroring,\n" + " then it would forward the video stream.\n" + "\n" " --record-format format\n" " Force recording format (either mp4 or mkv).\n" "\n" @@ -297,6 +305,7 @@ parse_port(const char *s, uint16_t *port) { return true; } + static bool parse_record_format(const char *optarg, enum recorder_format *format) { if (!strcmp(optarg, "mp4")) { @@ -311,6 +320,154 @@ parse_record_format(const char *optarg, enum recorder_format *format) { return false; } +char** +str_split(const char* a_str, const char a_delim) +{ + char** result = 0; + size_t count = 0; + char* tmp = (char*)a_str; + char str[100]; + strncpy(str, a_str, sizeof(str)); + char* last_comma = 0; + char delim[2]; + delim[0] = a_delim; + delim[1] = 0; + + /* Count how many elements will be extracted. */ + while (*tmp) + { + if (a_delim == *tmp) + { + count++; + last_comma = tmp; + } + tmp++; + } + + /* Add space for trailing token. */ + count += last_comma < (str + strlen(str) - 1); + + /* Add space for terminating null string so caller + knows where the list of returned strings ends. */ + count++; + + result = malloc(sizeof(char*) * count); + + if (result) + { + size_t idx = 0; + char* token = strtok(str, delim); + + while (token) + { + assert(idx < count); + *(result + idx++) = strdup(token); + token = strtok(0, delim); + } + assert(idx == count - 1); + *(result + idx) = 0; + } + + return result; +} + +int +validate_ip(char* ip) { //check whether the IP is valid or not + int num, dots = 0; + char* ptr; + if (ip == NULL) + return 0; + ptr = strtok(ip, "."); //cut the string using dor delimiter + if (ptr == NULL) + return 0; + while (ptr) { + long value; + if (!parse_integer(ptr, &value)) //check whether the sub string is holding only number or not + return 0; + num = atoi(ptr); //convert substring to number + if (num >= 0 && num <= 255) { + ptr = strtok(NULL, "."); //cut the next part of the string + if (ptr != NULL) + dots++; //increase the dot count + } + else + return 0; + } + if (dots != 3) //if the number of dots are not 3, return false + return 0; + return 1; +} + +static bool +parse_serve_args(const char *optarg, char **s_protocol, uint32_t *s_ip, uint16_t *s_port) { + bool protocol_valid = false; + bool ip_valid = false; + bool port_valid = false; + + char* protocol = NULL; + char* ip = NULL; + uint32_t ip_value; + char* port = NULL; + char** values; + + values = str_split(optarg, ':'); + + if (values) + { + protocol = *values; + ip = *(values + 1); + port = *(values + 2); + } + + free(values); + + if (!strcmp(protocol, "tcp")) + { + //protocol = "tcp"; + protocol_valid = true; + } else if (!strcmp(protocol, "udp")) + { + //protocol = "udp"; + protocol_valid = true; + } + else { + LOGE("Unexpected protocol: %s (expected tcp or udp)", protocol); + return false; + } + + //Allowing to write localhost or the IP address + if (!strcmp(ip, "localhost")) + { + ip_value = IPV4_LOCALHOST; + ip_valid = true; + } else if (validate_ip(ip)) { + ip_valid = true; + } + else { + LOGE("Unexpected ip address (expected \"localhost\" or 255.255.255.255)"); + return false; + } + + long port_value = 0; + port_valid = parse_integer_arg(port, &port_value, false, 0, 0xFFFF, "port"); + + //Check if everything is valid + if (!protocol_valid || !ip_valid || !port_valid) { + LOGE("Unexpected argument format: %s (expected [tcp/udp]:[ip or \"localhost\"]:[port])", optarg); + return false; + } + + /*LOGI("%s", protocol); + LOGI("%d", ip_value); + LOGI("%ld", port_value);*/ + + *s_protocol = protocol; + *s_ip = (uint32_t)ip_value; + *s_port = (uint16_t)port_value; + + return true; +} + static enum recorder_format guess_record_format(const char *filename) { size_t len = strlen(filename); @@ -340,6 +497,7 @@ guess_record_format(const char *filename) { #define OPT_WINDOW_HEIGHT 1010 #define OPT_WINDOW_BORDERLESS 1011 #define OPT_MAX_FPS 1012 +#define OPT_SERVE 1013 bool scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { @@ -360,6 +518,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { {"render-expired-frames", no_argument, NULL, OPT_RENDER_EXPIRED_FRAMES}, {"serial", required_argument, NULL, 's'}, + {"serve", required_argument, NULL, OPT_SERVE}, {"show-touches", no_argument, NULL, 't'}, {"turn-screen-off", no_argument, NULL, 'S'}, {"prefer-text", no_argument, NULL, OPT_PREFER_TEXT}, @@ -434,6 +593,16 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { case 's': opts->serial = optarg; break; + case OPT_SERVE: + if (!parse_serve_args(optarg, &opts->serve_protocol, &opts->serve_ip, &opts->serve_port)) { + return false; + } else { + opts->serve = true; + } + /*LOGI("protocol is %s", opts->serve_protocol); + LOGI("ip value is %d", opts->serve_ip); + LOGI("port is %d", opts->serve_port);*/ + break; case 'S': opts->turn_screen_off = true; break; @@ -490,8 +659,8 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { } } - if (!opts->display && !opts->record_filename) { - LOGE("-N/--no-display requires screen recording (-r/--record)"); + if (!opts->display && !opts->record_filename && !opts->serve) { + LOGE("-N/--no-display requires screen recording (-r/--record) or to serve to another client (--serve)"); return false; } diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 17be1ed4..b7138fa5 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -21,6 +21,7 @@ #include "recorder.h" #include "screen.h" #include "server.h" +#include "serve.h" #include "stream.h" #include "tiny_xpm.h" #include "video_buffer.h" @@ -35,6 +36,7 @@ static struct video_buffer video_buffer; static struct stream stream; static struct decoder decoder; static struct recorder recorder; +static struct serve serve; static struct controller controller; static struct file_handler file_handler; @@ -307,6 +309,7 @@ scrcpy(const struct scrcpy_options *options) { bool stream_started = false; bool controller_initialized = false; bool controller_started = false; + //bool serve_initialized = false; if (!sdl_init_and_configure(options->display)) { goto end; @@ -365,7 +368,18 @@ scrcpy(const struct scrcpy_options *options) { av_log_set_callback(av_log_callback); - stream_init(&stream, server.video_socket, dec, rec); + struct serve *serv = NULL; + if (options->serve) + { + serve_init(&serve, options->serve_protocol, options->serve_ip, options->serve_port); + + if (!serve_start(&serve)) { + goto end; + } + serv = &serve; + } + + stream_init(&stream, server.video_socket, dec, rec, serv); // now we consumed the header values, the socket receives the video stream // start the stream diff --git a/app/src/scrcpy.h b/app/src/scrcpy.h index 75de8717..6908cdf8 100644 --- a/app/src/scrcpy.h +++ b/app/src/scrcpy.h @@ -13,7 +13,7 @@ struct scrcpy_options { const char *crop; const char *record_filename; const char *window_title; - const char *push_target; + const char *push_target; enum recorder_format record_format; uint16_t port; uint16_t max_size; @@ -32,6 +32,10 @@ struct scrcpy_options { bool render_expired_frames; bool prefer_text; bool window_borderless; + char *serve_protocol; + uint32_t serve_ip; + uint16_t serve_port; + bool serve; }; #define SCRCPY_OPTIONS_DEFAULT { \ @@ -58,6 +62,10 @@ struct scrcpy_options { .render_expired_frames = false, \ .prefer_text = false, \ .window_borderless = false, \ + .serve_protocol = NULL, \ + .serve_ip = 0, \ + .serve_port = 0, \ + .serve = false, \ } bool diff --git a/app/src/serve.c b/app/src/serve.c new file mode 100644 index 00000000..8d40290e --- /dev/null +++ b/app/src/serve.c @@ -0,0 +1,101 @@ +#include "serve.h" + +#include +#include +#include + +#include "config.h" +#include "events.h" +#include "util/log.h" +#include "util/net.h" + +# define SOCKET_ERROR -1 + +void +serve_init(struct serve* serve, char *protocol, uint32_t ip, uint16_t port) { + serve->protocol = protocol; + serve->ip = ip; + serve->port = port; +} + +//static int +//run_serve(void *data) { +// struct serve* serve = data; +// +// socket_t Listensocket; +// socket_t ClientSocket; +// +// Listensocket = net_listen(serve->ip, serve->port, 1); +// if (Listensocket == INVALID_SOCKET) { +// LOGI("Listen Error"); +// net_close(Listensocket); +// return 0; +// } +// +// for (;;) { +// ClientSocket = net_accept(Listensocket); +// if (ClientSocket == INVALID_SOCKET) { +// LOGI("Client Error"); +// net_close(Listensocket); +// return 0; +// } +// LOGI("Client found"); +// +// net_close(Listensocket); +// +// serve->socket = ClientSocket; +// +// if (serve->stopped) +// { +// break; +// } +// } +// +// LOGD("Serve thread ended"); +// return 0; +//} + +bool +serve_start(struct serve* serve) { + LOGD("Starting serve thread"); + + socket_t Listensocket; + socket_t ClientSocket; + + Listensocket = net_listen(serve->ip, serve->port, 1); + if (Listensocket == INVALID_SOCKET) { + LOGI("Listen Error"); + net_close(Listensocket); + return 0; + } + + ClientSocket = net_accept(Listensocket); + if (ClientSocket == INVALID_SOCKET) { + LOGI("Client Error"); + net_close(Listensocket); + return 0; + } + LOGI("Client found"); + + net_close(Listensocket); + + serve->socket = ClientSocket; + + /*serve->thread = SDL_CreateThread(run_serve, "serve", serve); + if (!serve->thread) { + LOGC("Could not start stream thread"); + return false; + }*/ + return true; +} + +bool +serve_push(struct serve* serve, const AVPacket packet) { + if (net_send(serve->socket, packet.data, packet.size) == SOCKET_ERROR) + { + LOGI("Client lost"); + net_close(serve->socket); + return false; + } + return true; +} diff --git a/app/src/serve.h b/app/src/serve.h new file mode 100644 index 00000000..44ee7583 --- /dev/null +++ b/app/src/serve.h @@ -0,0 +1,28 @@ +#ifndef SERVE_H +#define SERVE_H + +#include +#include +#include +#include +#include + +#include "config.h" +#include "util/net.h" + +struct serve { + socket_t socket; + char *protocol; + uint32_t ip; + uint16_t port; +}; + +void +serve_init(struct serve* serve, char* protocol, uint32_t ip, uint16_t port); + +bool +serve_start(struct serve* serve); + +bool +serve_push(struct serve* serve, const AVPacket packet); +#endif \ No newline at end of file diff --git a/app/src/stream.c b/app/src/stream.c index dd2dbd76..b91fb2fe 100644 --- a/app/src/stream.c +++ b/app/src/stream.c @@ -7,14 +7,18 @@ #include #include #include +#include #include "config.h" #include "compat.h" #include "decoder.h" #include "events.h" #include "recorder.h" +#include "serve.h" +#include "stream.h" #include "util/buffer_util.h" #include "util/log.h" +#include "util/net.h" #define BUFSIZE 0x10000 @@ -22,281 +26,295 @@ #define NO_PTS UINT64_C(-1) static bool -stream_recv_packet(struct stream *stream, AVPacket *packet) { - // The video stream contains raw packets, without time information. When we - // record, we retrieve the timestamps separately, from a "meta" header - // added by the server before each raw packet. - // - // The "meta" header length is 12 bytes: - // [. . . . . . . .|. . . .]. . . . . . . . . . . . . . . ... - // <-------------> <-----> <-----------------------------... - // PTS packet raw packet - // size - // - // It is followed by bytes containing the packet/frame. +stream_recv_packet(struct stream* stream, AVPacket* packet) { + // The video stream contains raw packets, without time information. When we + // record, we retrieve the timestamps separately, from a "meta" header + // added by the server before each raw packet. + // + // The "meta" header length is 12 bytes: + // [. . . . . . . .|. . . .]. . . . . . . . . . . . . . . ... + // <-------------> <-----> <-----------------------------... + // PTS packet raw packet + // size + // + // It is followed by bytes containing the packet/frame. - uint8_t header[HEADER_SIZE]; - ssize_t r = net_recv_all(stream->socket, header, HEADER_SIZE); - if (r < HEADER_SIZE) { - return false; - } + uint8_t header[HEADER_SIZE]; + ssize_t r = net_recv_all(stream->socket, header, HEADER_SIZE); + if (r < HEADER_SIZE) { + return false; + } - uint64_t pts = buffer_read64be(header); - uint32_t len = buffer_read32be(&header[8]); - assert(pts == NO_PTS || (pts & 0x8000000000000000) == 0); - assert(len); + uint64_t pts = buffer_read64be(header); + uint32_t len = buffer_read32be(&header[8]); + assert(pts == NO_PTS || (pts & 0x8000000000000000) == 0); + assert(len); - if (av_new_packet(packet, len)) { - LOGE("Could not allocate packet"); - return false; - } + if (av_new_packet(packet, len)) { + LOGE("Could not allocate packet"); + return false; + } - r = net_recv_all(stream->socket, packet->data, len); - if (r < 0 || ((uint32_t) r) < len) { - av_packet_unref(packet); - return false; - } + r = net_recv_all(stream->socket, packet->data, len); + if (r < 0 || ((uint32_t)r) < len) { + av_packet_unref(packet); + return false; + } - packet->pts = pts != NO_PTS ? (int64_t) pts : AV_NOPTS_VALUE; + packet->pts = pts != NO_PTS ? (int64_t)pts : AV_NOPTS_VALUE; - return true; + return true; } static void notify_stopped(void) { - SDL_Event stop_event; - stop_event.type = EVENT_STREAM_STOPPED; - SDL_PushEvent(&stop_event); + SDL_Event stop_event; + stop_event.type = EVENT_STREAM_STOPPED; + SDL_PushEvent(&stop_event); } static bool -process_config_packet(struct stream *stream, AVPacket *packet) { - if (stream->recorder && !recorder_push(stream->recorder, packet)) { - LOGE("Could not send config packet to recorder"); - return false; - } - return true; +process_config_packet(struct stream* stream, AVPacket* packet) { + if (stream->recorder && !recorder_push(stream->recorder, packet)) { + LOGE("Could not send config packet to recorder"); + return false; + } + return true; +} + + + +static bool +process_frame(struct stream* stream, AVPacket* packet) { + if (stream->decoder && !decoder_push(stream->decoder, packet)) { + return false; + } + + if (stream->recorder) { + packet->dts = packet->pts; + + if (!recorder_push(stream->recorder, packet)) { + LOGE("Could not send packet to recorder"); + return false; + } + } + + if (stream->serve && !serve_push(stream->serve, *packet)) { + LOGE("Could not serve packet"); + return false; + } + + return true; } static bool -process_frame(struct stream *stream, AVPacket *packet) { - if (stream->decoder && !decoder_push(stream->decoder, packet)) { - return false; - } +stream_parse(struct stream* stream, AVPacket* packet) { + uint8_t* in_data = packet->data; + int in_len = packet->size; + uint8_t* out_data = NULL; + int out_len = 0; + int r = av_parser_parse2(stream->parser, stream->codec_ctx, + &out_data, &out_len, in_data, in_len, + AV_NOPTS_VALUE, AV_NOPTS_VALUE, -1); - if (stream->recorder) { - packet->dts = packet->pts; + // PARSER_FLAG_COMPLETE_FRAMES is set + assert(r == in_len); + (void)r; + assert(out_len == in_len); - if (!recorder_push(stream->recorder, packet)) { - LOGE("Could not send packet to recorder"); - return false; - } - } + if (stream->parser->key_frame == 1) { + packet->flags |= AV_PKT_FLAG_KEY; + } - return true; + bool ok = process_frame(stream, packet); + if (!ok) { + LOGE("Could not process frame"); + return false; + } + + return true; } static bool -stream_parse(struct stream *stream, AVPacket *packet) { - uint8_t *in_data = packet->data; - int in_len = packet->size; - uint8_t *out_data = NULL; - int out_len = 0; - int r = av_parser_parse2(stream->parser, stream->codec_ctx, - &out_data, &out_len, in_data, in_len, - AV_NOPTS_VALUE, AV_NOPTS_VALUE, -1); +stream_push_packet(struct stream* stream, AVPacket* packet) { + bool is_config = packet->pts == AV_NOPTS_VALUE; - // PARSER_FLAG_COMPLETE_FRAMES is set - assert(r == in_len); - (void) r; - assert(out_len == in_len); + // A config packet must not be decoded immetiately (it contains no + // frame); instead, it must be concatenated with the future data packet. + if (stream->has_pending || is_config) { + size_t offset; + if (stream->has_pending) { + offset = stream->pending.size; + if (av_grow_packet(&stream->pending, packet->size)) { + LOGE("Could not grow packet"); + return false; + } + } + else { + offset = 0; + if (av_new_packet(&stream->pending, packet->size)) { + LOGE("Could not create packet"); + return false; + } + stream->has_pending = true; + } - if (stream->parser->key_frame == 1) { - packet->flags |= AV_PKT_FLAG_KEY; - } + memcpy(stream->pending.data + offset, packet->data, packet->size); - bool ok = process_frame(stream, packet); - if (!ok) { - LOGE("Could not process frame"); - return false; - } + if (!is_config) { + // prepare the concat packet to send to the decoder + stream->pending.pts = packet->pts; + stream->pending.dts = packet->dts; + stream->pending.flags = packet->flags; + packet = &stream->pending; + } + } - return true; -} + if (is_config) { + // config packet + bool ok = process_config_packet(stream, packet); + if (!ok) { + return false; + } + } + else { + // data packet + bool ok = stream_parse(stream, packet); -static bool -stream_push_packet(struct stream *stream, AVPacket *packet) { - bool is_config = packet->pts == AV_NOPTS_VALUE; + if (stream->has_pending) { + // the pending packet must be discarded (consumed or error) + stream->has_pending = false; + av_packet_unref(&stream->pending); + } - // A config packet must not be decoded immetiately (it contains no - // frame); instead, it must be concatenated with the future data packet. - if (stream->has_pending || is_config) { - size_t offset; - if (stream->has_pending) { - offset = stream->pending.size; - if (av_grow_packet(&stream->pending, packet->size)) { - LOGE("Could not grow packet"); - return false; - } - } else { - offset = 0; - if (av_new_packet(&stream->pending, packet->size)) { - LOGE("Could not create packet"); - return false; - } - stream->has_pending = true; - } - - memcpy(stream->pending.data + offset, packet->data, packet->size); - - if (!is_config) { - // prepare the concat packet to send to the decoder - stream->pending.pts = packet->pts; - stream->pending.dts = packet->dts; - stream->pending.flags = packet->flags; - packet = &stream->pending; - } - } - - if (is_config) { - // config packet - bool ok = process_config_packet(stream, packet); - if (!ok) { - return false; - } - } else { - // data packet - bool ok = stream_parse(stream, packet); - - if (stream->has_pending) { - // the pending packet must be discarded (consumed or error) - stream->has_pending = false; - av_packet_unref(&stream->pending); - } - - if (!ok) { - return false; - } - } - return true; + if (!ok) { + return false; + } + } + return true; } static int -run_stream(void *data) { - struct stream *stream = data; +run_stream(void* data) { + struct stream* stream = data; - AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264); - if (!codec) { - LOGE("H.264 decoder not found"); - goto end; - } + AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264); + if (!codec) { + LOGE("H.264 decoder not found"); + goto end; + } - stream->codec_ctx = avcodec_alloc_context3(codec); - if (!stream->codec_ctx) { - LOGC("Could not allocate codec context"); - goto end; - } + stream->codec_ctx = avcodec_alloc_context3(codec); + if (!stream->codec_ctx) { + LOGC("Could not allocate codec context"); + goto end; + } - if (stream->decoder && !decoder_open(stream->decoder, codec)) { - LOGE("Could not open decoder"); - goto finally_free_codec_ctx; - } + if (stream->decoder && !decoder_open(stream->decoder, codec)) { + LOGE("Could not open decoder"); + goto finally_free_codec_ctx; + } - if (stream->recorder) { - if (!recorder_open(stream->recorder, codec)) { - LOGE("Could not open recorder"); - goto finally_close_decoder; - } + if (stream->recorder) { + if (!recorder_open(stream->recorder, codec)) { + LOGE("Could not open recorder"); + goto finally_close_decoder; + } - if (!recorder_start(stream->recorder)) { - LOGE("Could not start recorder"); - goto finally_close_recorder; - } - } + if (!recorder_start(stream->recorder)) { + LOGE("Could not start recorder"); + goto finally_close_recorder; + } + } - stream->parser = av_parser_init(AV_CODEC_ID_H264); - if (!stream->parser) { - LOGE("Could not initialize parser"); - goto finally_stop_and_join_recorder; - } + stream->parser = av_parser_init(AV_CODEC_ID_H264); + if (!stream->parser) { + LOGE("Could not initialize parser"); + goto finally_stop_and_join_recorder; + } - // We must only pass complete frames to av_parser_parse2()! - // It's more complicated, but this allows to reduce the latency by 1 frame! - stream->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; + // We must only pass complete frames to av_parser_parse2()! + // It's more complicated, but this allows to reduce the latency by 1 frame! + stream->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; - for (;;) { - AVPacket packet; - bool ok = stream_recv_packet(stream, &packet); - if (!ok) { - // end of stream - break; - } + for (;;) { + AVPacket packet; + bool ok = stream_recv_packet(stream, &packet); - ok = stream_push_packet(stream, &packet); - av_packet_unref(&packet); - if (!ok) { - // cannot process packet (error already logged) - break; - } - } + if (!ok) { + // end of stream + break; + } - LOGD("End of frames"); + ok = stream_push_packet(stream, &packet); - if (stream->has_pending) { - av_packet_unref(&stream->pending); - } + av_packet_unref(&packet); + + if (!ok) { + // cannot process packet (error already logged) + break; + } + } - av_parser_close(stream->parser); + LOGD("End of frames"); + + if (stream->has_pending) { + av_packet_unref(&stream->pending); + } + + av_parser_close(stream->parser); finally_stop_and_join_recorder: - if (stream->recorder) { - recorder_stop(stream->recorder); - LOGI("Finishing recording..."); - recorder_join(stream->recorder); - } + if (stream->recorder) { + recorder_stop(stream->recorder); + LOGI("Finishing recording..."); + recorder_join(stream->recorder); + } finally_close_recorder: - if (stream->recorder) { - recorder_close(stream->recorder); - } + if (stream->recorder) { + recorder_close(stream->recorder); + } finally_close_decoder: - if (stream->decoder) { - decoder_close(stream->decoder); - } + if (stream->decoder) { + decoder_close(stream->decoder); + } finally_free_codec_ctx: - avcodec_free_context(&stream->codec_ctx); + avcodec_free_context(&stream->codec_ctx); end: - notify_stopped(); - return 0; + notify_stopped(); + return 0; } void -stream_init(struct stream *stream, socket_t socket, - struct decoder *decoder, struct recorder *recorder) { - stream->socket = socket; - stream->decoder = decoder, - stream->recorder = recorder; - stream->has_pending = false; +stream_init(struct stream* stream, socket_t socket, + struct decoder* decoder, struct recorder* recorder, struct serve* serve) { + stream->socket = socket; + stream->decoder = decoder; + stream->recorder = recorder; + stream->serve = serve; + stream->has_pending = false; } bool -stream_start(struct stream *stream) { - LOGD("Starting stream thread"); +stream_start(struct stream* stream) { + LOGD("Starting stream thread"); - stream->thread = SDL_CreateThread(run_stream, "stream", stream); - if (!stream->thread) { - LOGC("Could not start stream thread"); - return false; - } - return true; + stream->thread = SDL_CreateThread(run_stream, "stream", stream); + if (!stream->thread) { + LOGC("Could not start stream thread"); + return false; + } + return true; } void -stream_stop(struct stream *stream) { - if (stream->decoder) { - decoder_interrupt(stream->decoder); - } +stream_stop(struct stream* stream) { + if (stream->decoder) { + decoder_interrupt(stream->decoder); + } } void -stream_join(struct stream *stream) { - SDL_WaitThread(stream->thread, NULL); +stream_join(struct stream* stream) { + SDL_WaitThread(stream->thread, NULL); } + diff --git a/app/src/stream.h b/app/src/stream.h index f7c5e475..fb82f802 100644 --- a/app/src/stream.h +++ b/app/src/stream.h @@ -18,6 +18,7 @@ struct stream { SDL_Thread *thread; struct decoder *decoder; struct recorder *recorder; + struct serve *serve; AVCodecContext *codec_ctx; AVCodecParserContext *parser; // successive packets may need to be concatenated, until a non-config @@ -28,7 +29,7 @@ struct stream { void stream_init(struct stream *stream, socket_t socket, - struct decoder *decoder, struct recorder *recorder); + struct decoder *decoder, struct recorder *recorder, struct serve *serve); bool stream_start(struct stream *stream);