Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
57687bdfcd |
@ -34,7 +34,7 @@ WIN32_TARGET := $(WIN32_TARGET_DIR)-$(VERSION).zip
|
|||||||
WIN64_TARGET := $(WIN64_TARGET_DIR)-$(VERSION).zip
|
WIN64_TARGET := $(WIN64_TARGET_DIR)-$(VERSION).zip
|
||||||
|
|
||||||
release: clean zip-win32 zip-win64 sums
|
release: clean zip-win32 zip-win64 sums
|
||||||
@echo "Windows archives generated in $(DIST)/"
|
@echo "Release created in $(DIST)/."
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(GRADLE) clean
|
$(GRADLE) clean
|
||||||
|
@ -162,7 +162,7 @@ It is possible to record the screen while mirroring:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
scrcpy --record file.mp4
|
scrcpy --record file.mp4
|
||||||
scrcpy -r file.mkv
|
scrcpy -r file.mp4
|
||||||
```
|
```
|
||||||
|
|
||||||
"Skipped frames" are recorded, even if they are not displayed in real time (for
|
"Skipped frames" are recorded, even if they are not displayed in real time (for
|
||||||
|
@ -3,33 +3,33 @@
|
|||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <SDL2/SDL_stdinc.h>
|
#include <SDL2/SDL_stdinc.h>
|
||||||
|
#include <SDL2/SDL_platform.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
# include <winsock2.h> // not needed here, but must never be included AFTER windows.h
|
|
||||||
# include <windows.h>
|
|
||||||
# define PRIexitcode "lu"
|
|
||||||
// <https://stackoverflow.com/a/44383330/1987178>
|
// <https://stackoverflow.com/a/44383330/1987178>
|
||||||
|
#ifdef _WIN32
|
||||||
|
# define PRIexitcode "lu"
|
||||||
# ifdef _WIN64
|
# ifdef _WIN64
|
||||||
# define PRIsizet PRIu64
|
# define PRIsizet PRIu64
|
||||||
# else
|
# else
|
||||||
# define PRIsizet PRIu32
|
# define PRIsizet PRIu32
|
||||||
# endif
|
# endif
|
||||||
|
#else
|
||||||
|
# define PRIsizet "zu"
|
||||||
|
# define PRIexitcode "d"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __WINDOWS__
|
||||||
|
# include <winsock2.h> // not needed here, but must never be included AFTER windows.h
|
||||||
|
# include <windows.h>
|
||||||
# define PROCESS_NONE NULL
|
# define PROCESS_NONE NULL
|
||||||
typedef HANDLE process_t;
|
typedef HANDLE process_t;
|
||||||
typedef DWORD exit_code_t;
|
typedef DWORD exit_code_t;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
# include <sys/types.h>
|
# include <sys/types.h>
|
||||||
# define PRIsizet "zu"
|
|
||||||
# define PRIexitcode "d"
|
|
||||||
# define PROCESS_NONE -1
|
# define PROCESS_NONE -1
|
||||||
typedef pid_t process_t;
|
typedef pid_t process_t;
|
||||||
typedef int exit_code_t;
|
typedef int exit_code_t;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
# define NO_EXIT_CODE -1
|
# define NO_EXIT_CODE -1
|
||||||
|
|
||||||
enum process_result {
|
enum process_result {
|
||||||
|
@ -7,13 +7,11 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "recorder.h"
|
|
||||||
|
|
||||||
struct args {
|
struct args {
|
||||||
const char *serial;
|
const char *serial;
|
||||||
const char *crop;
|
const char *crop;
|
||||||
const char *record_filename;
|
const char *record_filename;
|
||||||
enum recorder_format record_format;
|
|
||||||
SDL_bool fullscreen;
|
SDL_bool fullscreen;
|
||||||
SDL_bool help;
|
SDL_bool help;
|
||||||
SDL_bool version;
|
SDL_bool version;
|
||||||
@ -44,9 +42,6 @@ static void usage(const char *arg0) {
|
|||||||
" -f, --fullscreen\n"
|
" -f, --fullscreen\n"
|
||||||
" Start in fullscreen.\n"
|
" Start in fullscreen.\n"
|
||||||
"\n"
|
"\n"
|
||||||
" -F, --record-format\n"
|
|
||||||
" Force recording format (either mp4 or mkv).\n"
|
|
||||||
"\n"
|
|
||||||
" -h, --help\n"
|
" -h, --help\n"
|
||||||
" Print this help.\n"
|
" Print this help.\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -62,8 +57,6 @@ static void usage(const char *arg0) {
|
|||||||
"\n"
|
"\n"
|
||||||
" -r, --record file.mp4\n"
|
" -r, --record file.mp4\n"
|
||||||
" Record screen to file.\n"
|
" Record screen to file.\n"
|
||||||
" The format is determined by the -F/--record-format option if\n"
|
|
||||||
" set, or by the file extension (.mp4 or .mkv).\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
" -s, --serial\n"
|
" -s, --serial\n"
|
||||||
" The device serial number. Mandatory only if several devices\n"
|
" The device serial number. Mandatory only if several devices\n"
|
||||||
@ -215,36 +208,6 @@ static SDL_bool parse_port(char *optarg, Uint16 *port) {
|
|||||||
return SDL_TRUE;
|
return SDL_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SDL_bool
|
|
||||||
parse_record_format(const char *optarg, enum recorder_format *format) {
|
|
||||||
if (!strcmp(optarg, "mp4")) {
|
|
||||||
*format = RECORDER_FORMAT_MP4;
|
|
||||||
return SDL_TRUE;
|
|
||||||
}
|
|
||||||
if (!strcmp(optarg, "mkv")) {
|
|
||||||
*format = RECORDER_FORMAT_MKV;
|
|
||||||
return SDL_TRUE;
|
|
||||||
}
|
|
||||||
LOGE("Unsupported format: %s (expected mp4 or mkv)", optarg);
|
|
||||||
return SDL_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum recorder_format
|
|
||||||
guess_record_format(const char *filename) {
|
|
||||||
size_t len = strlen(filename);
|
|
||||||
if (len < 4) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const char *ext = &filename[len - 4];
|
|
||||||
if (!strcmp(ext, ".mp4")) {
|
|
||||||
return RECORDER_FORMAT_MP4;
|
|
||||||
}
|
|
||||||
if (!strcmp(ext, ".mkv")) {
|
|
||||||
return RECORDER_FORMAT_MKV;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
|
static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
|
||||||
static const struct option long_options[] = {
|
static const struct option long_options[] = {
|
||||||
{"always-on-top", no_argument, NULL, 'T'},
|
{"always-on-top", no_argument, NULL, 'T'},
|
||||||
@ -255,14 +218,13 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
|
|||||||
{"max-size", required_argument, NULL, 'm'},
|
{"max-size", required_argument, NULL, 'm'},
|
||||||
{"port", required_argument, NULL, 'p'},
|
{"port", required_argument, NULL, 'p'},
|
||||||
{"record", required_argument, NULL, 'r'},
|
{"record", required_argument, NULL, 'r'},
|
||||||
{"record-format", required_argument, NULL, 'f'},
|
|
||||||
{"serial", required_argument, NULL, 's'},
|
{"serial", required_argument, NULL, 's'},
|
||||||
{"show-touches", no_argument, NULL, 't'},
|
{"show-touches", no_argument, NULL, 't'},
|
||||||
{"version", no_argument, NULL, 'v'},
|
{"version", no_argument, NULL, 'v'},
|
||||||
{NULL, 0, NULL, 0 },
|
{NULL, 0, NULL, 0 },
|
||||||
};
|
};
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt_long(argc, argv, "b:c:fF:hm:p:r:s:tTv", long_options, NULL)) != -1) {
|
while ((c = getopt_long(argc, argv, "b:c:fhm:p:r:s:tTv", long_options, NULL)) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'b':
|
case 'b':
|
||||||
if (!parse_bit_rate(optarg, &args->bit_rate)) {
|
if (!parse_bit_rate(optarg, &args->bit_rate)) {
|
||||||
@ -275,11 +237,6 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
|
|||||||
case 'f':
|
case 'f':
|
||||||
args->fullscreen = SDL_TRUE;
|
args->fullscreen = SDL_TRUE;
|
||||||
break;
|
break;
|
||||||
case 'F':
|
|
||||||
if (!parse_record_format(optarg, &args->record_format)) {
|
|
||||||
return SDL_FALSE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'h':
|
case 'h':
|
||||||
args->help = SDL_TRUE;
|
args->help = SDL_TRUE;
|
||||||
break;
|
break;
|
||||||
@ -319,21 +276,6 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
|
|||||||
LOGE("Unexpected additional argument: %s", argv[index]);
|
LOGE("Unexpected additional argument: %s", argv[index]);
|
||||||
return SDL_FALSE;
|
return SDL_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args->record_format && !args->record_filename) {
|
|
||||||
LOGE("Record format specified without recording");
|
|
||||||
return SDL_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args->record_filename && !args->record_format) {
|
|
||||||
args->record_format = guess_record_format(args->record_filename);
|
|
||||||
if (!args->record_format) {
|
|
||||||
LOGE("No format specified for \"%s\" (try with -F mkv)",
|
|
||||||
args->record_filename);
|
|
||||||
return SDL_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return SDL_TRUE;
|
return SDL_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,7 +290,6 @@ int main(int argc, char *argv[]) {
|
|||||||
.serial = NULL,
|
.serial = NULL,
|
||||||
.crop = NULL,
|
.crop = NULL,
|
||||||
.record_filename = NULL,
|
.record_filename = NULL,
|
||||||
.record_format = 0,
|
|
||||||
.help = SDL_FALSE,
|
.help = SDL_FALSE,
|
||||||
.version = SDL_FALSE,
|
.version = SDL_FALSE,
|
||||||
.show_touches = SDL_FALSE,
|
.show_touches = SDL_FALSE,
|
||||||
@ -388,7 +329,6 @@ int main(int argc, char *argv[]) {
|
|||||||
.crop = args.crop,
|
.crop = args.crop,
|
||||||
.port = args.port,
|
.port = args.port,
|
||||||
.record_filename = args.record_filename,
|
.record_filename = args.record_filename,
|
||||||
.record_format = args.record_format,
|
|
||||||
.max_size = args.max_size,
|
.max_size = args.max_size,
|
||||||
.bit_rate = args.bit_rate,
|
.bit_rate = args.bit_rate,
|
||||||
.show_touches = args.show_touches,
|
.show_touches = args.show_touches,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include "recorder.h"
|
#include "recorder.h"
|
||||||
|
|
||||||
#include <libavutil/time.h>
|
#include <libavutil/time.h>
|
||||||
#include <SDL2/SDL_assert.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
@ -16,9 +15,7 @@
|
|||||||
# define LAVF_NEW_CODEC_API
|
# define LAVF_NEW_CODEC_API
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const AVRational SCRCPY_TIME_BASE = {1, 1000000}; // timestamps in us
|
static const AVOutputFormat *find_mp4_muxer(void) {
|
||||||
|
|
||||||
static const AVOutputFormat *find_muxer(const char *name) {
|
|
||||||
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 9, 100)
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 9, 100)
|
||||||
void *opaque = NULL;
|
void *opaque = NULL;
|
||||||
#endif
|
#endif
|
||||||
@ -30,13 +27,11 @@ static const AVOutputFormat *find_muxer(const char *name) {
|
|||||||
oformat = av_oformat_next(oformat);
|
oformat = av_oformat_next(oformat);
|
||||||
#endif
|
#endif
|
||||||
// until null or with name "mp4"
|
// until null or with name "mp4"
|
||||||
} while (oformat && strcmp(oformat->name, name));
|
} while (oformat && strcmp(oformat->name, "mp4"));
|
||||||
return oformat;
|
return oformat;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_bool recorder_init(struct recorder *recorder,
|
SDL_bool recorder_init(struct recorder *recorder, const char *filename,
|
||||||
const char *filename,
|
|
||||||
enum recorder_format format,
|
|
||||||
struct size declared_frame_size) {
|
struct size declared_frame_size) {
|
||||||
recorder->filename = SDL_strdup(filename);
|
recorder->filename = SDL_strdup(filename);
|
||||||
if (!recorder->filename) {
|
if (!recorder->filename) {
|
||||||
@ -44,7 +39,6 @@ SDL_bool recorder_init(struct recorder *recorder,
|
|||||||
return SDL_FALSE;
|
return SDL_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
recorder->format = format;
|
|
||||||
recorder->declared_frame_size = declared_frame_size;
|
recorder->declared_frame_size = declared_frame_size;
|
||||||
recorder->header_written = SDL_FALSE;
|
recorder->header_written = SDL_FALSE;
|
||||||
|
|
||||||
@ -55,21 +49,10 @@ void recorder_destroy(struct recorder *recorder) {
|
|||||||
SDL_free(recorder->filename);
|
SDL_free(recorder->filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *
|
|
||||||
recorder_get_format_name(enum recorder_format format) {
|
|
||||||
switch (format) {
|
|
||||||
case RECORDER_FORMAT_MP4: return "mp4";
|
|
||||||
case RECORDER_FORMAT_MKV: return "matroska";
|
|
||||||
default: return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_bool recorder_open(struct recorder *recorder, AVCodec *input_codec) {
|
SDL_bool recorder_open(struct recorder *recorder, AVCodec *input_codec) {
|
||||||
const char *format_name = recorder_get_format_name(recorder->format);
|
const AVOutputFormat *mp4 = find_mp4_muxer();
|
||||||
SDL_assert(format_name);
|
if (!mp4) {
|
||||||
const AVOutputFormat *format = find_muxer(format_name);
|
LOGE("Could not find mp4 muxer");
|
||||||
if (!format) {
|
|
||||||
LOGE("Could not find muxer");
|
|
||||||
return SDL_FALSE;
|
return SDL_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +66,7 @@ SDL_bool recorder_open(struct recorder *recorder, AVCodec *input_codec) {
|
|||||||
// returns (on purpose) a pointer-to-const, but AVFormatContext.oformat
|
// returns (on purpose) a pointer-to-const, but AVFormatContext.oformat
|
||||||
// still expects a pointer-to-non-const (it has not be updated accordingly)
|
// still expects a pointer-to-non-const (it has not be updated accordingly)
|
||||||
// <https://github.com/FFmpeg/FFmpeg/commit/0694d8702421e7aff1340038559c438b61bb30dd>
|
// <https://github.com/FFmpeg/FFmpeg/commit/0694d8702421e7aff1340038559c438b61bb30dd>
|
||||||
recorder->ctx->oformat = (AVOutputFormat *) format;
|
recorder->ctx->oformat = (AVOutputFormat *) mp4;
|
||||||
|
|
||||||
AVStream *ostream = avformat_new_stream(recorder->ctx, input_codec);
|
AVStream *ostream = avformat_new_stream(recorder->ctx, input_codec);
|
||||||
if (!ostream) {
|
if (!ostream) {
|
||||||
@ -104,6 +87,7 @@ SDL_bool recorder_open(struct recorder *recorder, AVCodec *input_codec) {
|
|||||||
ostream->codec->width = recorder->declared_frame_size.width;
|
ostream->codec->width = recorder->declared_frame_size.width;
|
||||||
ostream->codec->height = recorder->declared_frame_size.height;
|
ostream->codec->height = recorder->declared_frame_size.height;
|
||||||
#endif
|
#endif
|
||||||
|
ostream->time_base = (AVRational) {1, 1000000}; // timestamps in us
|
||||||
|
|
||||||
int ret = avio_open(&recorder->ctx->pb, recorder->filename,
|
int ret = avio_open(&recorder->ctx->pb, recorder->filename,
|
||||||
AVIO_FLAG_WRITE);
|
AVIO_FLAG_WRITE);
|
||||||
@ -114,8 +98,6 @@ SDL_bool recorder_open(struct recorder *recorder, AVCodec *input_codec) {
|
|||||||
return SDL_FALSE;
|
return SDL_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGI("Recording started to %s file: %s", format_name, recorder->filename);
|
|
||||||
|
|
||||||
return SDL_TRUE;
|
return SDL_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,16 +108,12 @@ void recorder_close(struct recorder *recorder) {
|
|||||||
}
|
}
|
||||||
avio_close(recorder->ctx->pb);
|
avio_close(recorder->ctx->pb);
|
||||||
avformat_free_context(recorder->ctx);
|
avformat_free_context(recorder->ctx);
|
||||||
|
|
||||||
const char *format_name = recorder_get_format_name(recorder->format);
|
|
||||||
LOGI("Recording complete to %s file: %s", format_name, recorder->filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static SDL_bool
|
SDL_bool recorder_write_header(struct recorder *recorder, AVPacket *packet) {
|
||||||
recorder_write_header(struct recorder *recorder, AVPacket *packet) {
|
|
||||||
AVStream *ostream = recorder->ctx->streams[0];
|
AVStream *ostream = recorder->ctx->streams[0];
|
||||||
|
|
||||||
uint8_t *extradata = av_malloc(packet->size * sizeof(uint8_t));
|
uint8_t *extradata = SDL_malloc(packet->size * sizeof(uint8_t));
|
||||||
if (!extradata) {
|
if (!extradata) {
|
||||||
LOGC("Cannot allocate extradata");
|
LOGC("Cannot allocate extradata");
|
||||||
return SDL_FALSE;
|
return SDL_FALSE;
|
||||||
@ -164,12 +142,6 @@ recorder_write_header(struct recorder *recorder, AVPacket *packet) {
|
|||||||
return SDL_TRUE;
|
return SDL_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
recorder_rescale_packet(struct recorder *recorder, AVPacket *packet) {
|
|
||||||
AVStream *ostream = recorder->ctx->streams[0];
|
|
||||||
av_packet_rescale_ts(packet, SCRCPY_TIME_BASE, ostream->time_base);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_bool recorder_write(struct recorder *recorder, AVPacket *packet) {
|
SDL_bool recorder_write(struct recorder *recorder, AVPacket *packet) {
|
||||||
if (!recorder->header_written) {
|
if (!recorder->header_written) {
|
||||||
SDL_bool ok = recorder_write_header(recorder, packet);
|
SDL_bool ok = recorder_write_header(recorder, packet);
|
||||||
@ -179,6 +151,5 @@ SDL_bool recorder_write(struct recorder *recorder, AVPacket *packet) {
|
|||||||
recorder->header_written = SDL_TRUE;
|
recorder->header_written = SDL_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
recorder_rescale_packet(recorder, packet);
|
|
||||||
return av_write_frame(recorder->ctx, packet) >= 0;
|
return av_write_frame(recorder->ctx, packet) >= 0;
|
||||||
}
|
}
|
||||||
|
@ -6,24 +6,15 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
enum recorder_format {
|
|
||||||
RECORDER_FORMAT_MP4 = 1,
|
|
||||||
RECORDER_FORMAT_MKV,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct recorder {
|
struct recorder {
|
||||||
char *filename;
|
char *filename;
|
||||||
enum recorder_format format;
|
|
||||||
AVFormatContext *ctx;
|
AVFormatContext *ctx;
|
||||||
struct size declared_frame_size;
|
struct size declared_frame_size;
|
||||||
SDL_bool header_written;
|
SDL_bool header_written;
|
||||||
};
|
};
|
||||||
|
|
||||||
SDL_bool recorder_init(struct recorder *recoder,
|
SDL_bool recorder_init(struct recorder *recoder, const char *filename,
|
||||||
const char *filename,
|
|
||||||
enum recorder_format format,
|
|
||||||
struct size declared_frame_size);
|
struct size declared_frame_size);
|
||||||
|
|
||||||
void recorder_destroy(struct recorder *recorder);
|
void recorder_destroy(struct recorder *recorder);
|
||||||
|
|
||||||
SDL_bool recorder_open(struct recorder *recorder, AVCodec *input_codec);
|
SDL_bool recorder_open(struct recorder *recorder, AVCodec *input_codec);
|
||||||
|
@ -229,10 +229,7 @@ SDL_bool scrcpy(const struct scrcpy_options *options) {
|
|||||||
|
|
||||||
struct recorder *rec = NULL;
|
struct recorder *rec = NULL;
|
||||||
if (options->record_filename) {
|
if (options->record_filename) {
|
||||||
if (!recorder_init(&recorder,
|
if (!recorder_init(&recorder, options->record_filename, frame_size)) {
|
||||||
options->record_filename,
|
|
||||||
options->record_format,
|
|
||||||
frame_size)) {
|
|
||||||
ret = SDL_FALSE;
|
ret = SDL_FALSE;
|
||||||
server_stop(&server);
|
server_stop(&server);
|
||||||
goto finally_destroy_file_handler;
|
goto finally_destroy_file_handler;
|
||||||
|
@ -2,13 +2,11 @@
|
|||||||
#define SCRCPY_H
|
#define SCRCPY_H
|
||||||
|
|
||||||
#include <SDL2/SDL_stdinc.h>
|
#include <SDL2/SDL_stdinc.h>
|
||||||
#include <recorder.h>
|
|
||||||
|
|
||||||
struct scrcpy_options {
|
struct scrcpy_options {
|
||||||
const char *serial;
|
const char *serial;
|
||||||
const char *crop;
|
const char *crop;
|
||||||
const char *record_filename;
|
const char *record_filename;
|
||||||
enum recorder_format record_format;
|
|
||||||
Uint16 port;
|
Uint16 port;
|
||||||
Uint16 max_size;
|
Uint16 max_size;
|
||||||
Uint32 bit_rate;
|
Uint32 bit_rate;
|
||||||
|
@ -3,11 +3,6 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
# include <windows.h>
|
|
||||||
# include <tchar.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t xstrncpy(char *dest, const char *src, size_t n) {
|
size_t xstrncpy(char *dest, const char *src, size_t n) {
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < n - 1 && src[i] != '\0'; ++i)
|
for (i = 0; i < n - 1 && src[i] != '\0'; ++i)
|
||||||
@ -52,22 +47,3 @@ char *strquote(const char *src) {
|
|||||||
quoted[len + 2] = '\0';
|
quoted[len + 2] = '\0';
|
||||||
return quoted;
|
return quoted;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
wchar_t *utf8_to_wide_char(const char *utf8) {
|
|
||||||
int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
|
|
||||||
if (!len) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
wchar_t *wide = malloc(len * sizeof(wchar_t));
|
|
||||||
if (!wide) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wide, len);
|
|
||||||
return wide;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
@ -20,10 +20,4 @@ size_t xstrjoin(char *dst, const char *const tokens[], char sep, size_t n);
|
|||||||
// returns the new allocated string, to be freed by the caller
|
// returns the new allocated string, to be freed by the caller
|
||||||
char *strquote(const char *src);
|
char *strquote(const char *src);
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
// convert a UTF-8 string to a wchar_t string
|
|
||||||
// returns the new allocated string, to be freed by the caller
|
|
||||||
wchar_t *utf8_to_wide_char(const char *utf8);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -18,7 +18,7 @@ static int build_cmd(char *cmd, size_t len, const char *const argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum process_result cmd_execute(const char *path, const char *const argv[], HANDLE *handle) {
|
enum process_result cmd_execute(const char *path, const char *const argv[], HANDLE *handle) {
|
||||||
STARTUPINFOW si;
|
STARTUPINFO si;
|
||||||
PROCESS_INFORMATION pi;
|
PROCESS_INFORMATION pi;
|
||||||
memset(&si, 0, sizeof(si));
|
memset(&si, 0, sizeof(si));
|
||||||
si.cb = sizeof(si);
|
si.cb = sizeof(si);
|
||||||
@ -29,19 +29,12 @@ enum process_result cmd_execute(const char *path, const char *const argv[], HAND
|
|||||||
return PROCESS_ERROR_GENERIC;
|
return PROCESS_ERROR_GENERIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
wchar_t *wide = utf8_to_wide_char(cmd);
|
|
||||||
if (!wide) {
|
|
||||||
LOGC("Cannot allocate wide char string");
|
|
||||||
return PROCESS_ERROR_GENERIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef WINDOWS_NOCONSOLE
|
#ifdef WINDOWS_NOCONSOLE
|
||||||
int flags = CREATE_NO_WINDOW;
|
int flags = CREATE_NO_WINDOW;
|
||||||
#else
|
#else
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
#endif
|
#endif
|
||||||
if (!CreateProcessW(NULL, wide, NULL, NULL, FALSE, flags, NULL, NULL, &si, &pi)) {
|
if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, flags, NULL, NULL, &si, &pi)) {
|
||||||
free(wide);
|
|
||||||
*handle = NULL;
|
*handle = NULL;
|
||||||
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
|
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
|
||||||
return PROCESS_ERROR_MISSING_BINARY;
|
return PROCESS_ERROR_MISSING_BINARY;
|
||||||
@ -49,7 +42,6 @@ enum process_result cmd_execute(const char *path, const char *const argv[], HAND
|
|||||||
return PROCESS_ERROR_GENERIC;
|
return PROCESS_ERROR_GENERIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(wide);
|
|
||||||
*handle = pi.hProcess;
|
*handle = pi.hProcess;
|
||||||
return PROCESS_SUCCESS;
|
return PROCESS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -87,13 +87,13 @@ static void test_serialize_mouse_event(void) {
|
|||||||
|
|
||||||
unsigned char buf[SERIALIZED_EVENT_MAX_SIZE];
|
unsigned char buf[SERIALIZED_EVENT_MAX_SIZE];
|
||||||
int size = control_event_serialize(&event, buf);
|
int size = control_event_serialize(&event, buf);
|
||||||
assert(size == 18);
|
assert(size == 14);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
0x02, // CONTROL_EVENT_TYPE_MOUSE
|
0x02, // CONTROL_EVENT_TYPE_MOUSE
|
||||||
0x00, // AKEY_EVENT_ACTION_DOWN
|
0x00, // AKEY_EVENT_ACTION_DOWN
|
||||||
0x00, 0x00, 0x00, 0x01, // AMOTION_EVENT_BUTTON_PRIMARY
|
0x00, 0x00, 0x00, 0x01, // AMOTION_EVENT_BUTTON_PRIMARY
|
||||||
0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x04, 0x02, // 260 1026
|
0x01, 0x04, 0x04, 0x02, // 260 1026
|
||||||
0x04, 0x38, 0x07, 0x80, // 1080 1920
|
0x04, 0x38, 0x07, 0x80, // 1080 1920
|
||||||
};
|
};
|
||||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||||
@ -120,11 +120,11 @@ static void test_serialize_scroll_event(void) {
|
|||||||
|
|
||||||
unsigned char buf[SERIALIZED_EVENT_MAX_SIZE];
|
unsigned char buf[SERIALIZED_EVENT_MAX_SIZE];
|
||||||
int size = control_event_serialize(&event, buf);
|
int size = control_event_serialize(&event, buf);
|
||||||
assert(size == 21);
|
assert(size == 17);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
0x03, // CONTROL_EVENT_TYPE_SCROLL
|
0x03, // CONTROL_EVENT_TYPE_SCROLL
|
||||||
0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x04, 0x02, // 260 1026
|
0x01, 0x04, 0x04, 0x02, // 260 1026
|
||||||
0x04, 0x38, 0x07, 0x80, // 1080 1920
|
0x04, 0x38, 0x07, 0x80, // 1080 1920
|
||||||
0x00, 0x00, 0x00, 0x01, // 1
|
0x00, 0x00, 0x00, 0x01, // 1
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, // -1
|
0xFF, 0xFF, 0xFF, 0xFF, // -1
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
project('scrcpy', 'c',
|
project('scrcpy', 'c',
|
||||||
version: '1.7',
|
version: '1.6',
|
||||||
meson_version: '>= 0.37',
|
meson_version: '>= 0.37',
|
||||||
default_options: 'c_std=c11')
|
default_options: 'c_std=c11')
|
||||||
|
|
||||||
|
35
release.sh
35
release.sh
@ -1,35 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# build and test locally
|
|
||||||
BUILDDIR=build_release
|
|
||||||
rm -rf "$BUILDDIR"
|
|
||||||
meson "$BUILDDIR" --buildtype release --strip -Db_lto=true
|
|
||||||
cd "$BUILDDIR"
|
|
||||||
ninja
|
|
||||||
ninja test
|
|
||||||
cd -
|
|
||||||
|
|
||||||
# build Windows releases
|
|
||||||
make -f Makefile.CrossWindows
|
|
||||||
|
|
||||||
# the generated server must be the same everywhere
|
|
||||||
cmp "$BUILDDIR/server/scrcpy-server.jar" dist/scrcpy-win32/scrcpy-server.jar
|
|
||||||
cmp "$BUILDDIR/server/scrcpy-server.jar" dist/scrcpy-win64/scrcpy-server.jar
|
|
||||||
|
|
||||||
# get version name
|
|
||||||
TAG=$(git describe --tags --always)
|
|
||||||
|
|
||||||
# create release directory
|
|
||||||
mkdir -p "release-$TAG"
|
|
||||||
cp "$BUILDDIR/server/scrcpy-server.jar" "release-$TAG/scrcpy-server-$TAG.jar"
|
|
||||||
cp "dist/scrcpy-win32-$TAG.zip" "release-$TAG/"
|
|
||||||
cp "dist/scrcpy-win64-$TAG.zip" "release-$TAG/"
|
|
||||||
|
|
||||||
# generate checksums
|
|
||||||
cd "release-$TAG"
|
|
||||||
sha256sum "scrcpy-server-$TAG.jar" \
|
|
||||||
"scrcpy-win32-$TAG.zip" \
|
|
||||||
"scrcpy-win64-$TAG.zip" > SHA256SUMS.txt
|
|
||||||
|
|
||||||
echo "Release generated in release-$TAG/"
|
|
@ -6,8 +6,8 @@ android {
|
|||||||
applicationId "com.genymobile.scrcpy"
|
applicationId "com.genymobile.scrcpy"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 27
|
targetSdkVersion 27
|
||||||
versionCode 8
|
versionCode 7
|
||||||
versionName "1.7"
|
versionName "1.6"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
Reference in New Issue
Block a user