Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Record screen to file #292

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 55 additions & 3 deletions app/src/decoder.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "decoder.h"

#include <libavformat/avformat.h>
#include <libavutil/time.h>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_thread.h>
Expand All @@ -14,6 +15,8 @@

#define BUFSIZE 0x10000

static AVRational us = {1, 1000000};

static int read_packet(void *opaque, uint8_t *buf, int buf_size) {
struct decoder *decoder = opaque;
return net_recv(decoder->video_socket, buf, buf_size);
Expand All @@ -40,6 +43,7 @@ static void notify_stopped(void) {

static int run_decoder(void *data) {
struct decoder *decoder = data;
int ret;

AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!codec) {
Expand Down Expand Up @@ -86,16 +90,55 @@ static int run_decoder(void *data) {
goto run_finally_free_avio_ctx;
}

AVStream *outstream = NULL;
AVFormatContext *output_ctx = NULL;
if (decoder->outfilename) {
avformat_alloc_output_context2(&output_ctx, NULL, NULL, decoder->outfilename);
if (!output_ctx) {
LOGE("Could not allocate output format context");
goto run_finally_free_avio_ctx;
} else {
outstream = avformat_new_stream(output_ctx, codec);
if (!outstream) {
LOGE("Could not allocate output stream");
goto run_finally_free_output_ctx;
}
outstream->codec = avcodec_alloc_context3(codec);
outstream->codec->pix_fmt = AV_PIX_FMT_YUV420P;
outstream->codec->width = decoder->frame_size.width;
outstream->codec->height = decoder->frame_size.height;
outstream->time_base = (AVRational) {1, 60};
outstream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
ret = avio_open(&output_ctx->pb, decoder->outfilename, AVIO_FLAG_WRITE);
if (ret < 0) {
LOGE("Failed to open output file");
goto run_finally_free_output_ctx;
}
ret = avformat_write_header(output_ctx, NULL);
if (ret < 0) {
LOGE("Error writing output header");
avio_closep(&output_ctx->pb);
goto run_finally_free_output_ctx;
}
}
}

AVPacket packet;
av_init_packet(&packet);
packet.data = NULL;
packet.size = 0;

while (!av_read_frame(format_ctx, &packet)) {

if (output_ctx) {
packet.pts = av_gettime();
av_packet_rescale_ts(&packet, us, outstream->time_base);
ret = av_write_frame(output_ctx, &packet);
}

// the new decoding/encoding API has been introduced by:
// <http://git.videolan.org/?p=ffmpeg.git;a=commitdiff;h=7fc329e2dd6226dfecaa4a1d7adf353bf2773726>
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 37, 0)
int ret;
if ((ret = avcodec_send_packet(codec_ctx, &packet)) < 0) {
LOGE("Could not send video packet: %d", ret);
goto run_quit;
Expand Down Expand Up @@ -134,7 +177,14 @@ static int run_decoder(void *data) {
LOGD("End of frames");

run_quit:
if (output_ctx) {
ret = av_write_trailer(output_ctx);
avio_closep(&output_ctx->pb);
}
avformat_close_input(&format_ctx);
run_finally_free_output_ctx:
if (output_ctx)
avformat_free_context(output_ctx);
run_finally_free_avio_ctx:
av_freep(&avio_ctx);
run_finally_free_format_ctx:
Expand All @@ -148,14 +198,16 @@ static int run_decoder(void *data) {
return 0;
}

void decoder_init(struct decoder *decoder, struct frames *frames, socket_t video_socket) {
void decoder_init(struct decoder *decoder, struct frames *frames, socket_t video_socket, struct size frame_size) {
decoder->frames = frames;
decoder->video_socket = video_socket;
decoder->frame_size = frame_size;
}

SDL_bool decoder_start(struct decoder *decoder) {
SDL_bool decoder_start(struct decoder *decoder, const char *outfilename) {
LOGD("Starting decoder thread");

decoder->outfilename = outfilename;
decoder->thread = SDL_CreateThread(run_decoder, "video_decoder", decoder);
if (!decoder->thread) {
LOGC("Could not start decoder thread");
Expand Down
7 changes: 5 additions & 2 deletions app/src/decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <SDL2/SDL_stdinc.h>
#include <SDL2/SDL_thread.h>

#include "common.h"
#include "net.h"

struct frames;
Expand All @@ -13,10 +14,12 @@ struct decoder {
socket_t video_socket;
SDL_Thread *thread;
SDL_mutex *mutex;
const char *outfilename;
struct size frame_size;
};

void decoder_init(struct decoder *decoder, struct frames *frames, socket_t video_socket);
SDL_bool decoder_start(struct decoder *decoder);
void decoder_init(struct decoder *decoder, struct frames *frames, socket_t video_socket, struct size frame_size);
SDL_bool decoder_start(struct decoder *decoder, const char *outfilename);
void decoder_stop(struct decoder *decoder);
void decoder_join(struct decoder *decoder);

Expand Down
11 changes: 10 additions & 1 deletion app/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
struct args {
const char *serial;
const char *crop;
const char *outfilename;
SDL_bool fullscreen;
SDL_bool help;
SDL_bool version;
Expand Down Expand Up @@ -49,6 +50,9 @@ static void usage(const char *arg0) {
" is preserved.\n"
" Default is %d%s.\n"
"\n"
" -o, --output\n"
" Write video output to file.\n"
"\n"
" -p, --port port\n"
" Set the TCP port the client listens on.\n"
" Default is %d.\n"
Expand Down Expand Up @@ -207,14 +211,15 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
{"fullscreen", no_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{"max-size", required_argument, NULL, 'm'},
{"output", required_argument, NULL, 'o'},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, the output is too general. In the future, we may add some feature like output format. It make a bit confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--output-file?

{"port", required_argument, NULL, 'p'},
{"serial", required_argument, NULL, 's'},
{"show-touches", no_argument, NULL, 't'},
{"version", no_argument, NULL, 'v'},
{NULL, 0, NULL, 0 },
};
int c;
while ((c = getopt_long(argc, argv, "b:c:fhm:p:s:tv", long_options, NULL)) != -1) {
while ((c = getopt_long(argc, argv, "b:c:fhm:o:p:s:tv", long_options, NULL)) != -1) {
switch (c) {
case 'b':
if (!parse_bit_rate(optarg, &args->bit_rate)) {
Expand All @@ -235,6 +240,9 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
return SDL_FALSE;
}
break;
case 'o':
args->outfilename = optarg;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To make consistent, use underscore as delimiter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

break;
case 'p':
if (!parse_port(optarg, &args->port)) {
return SDL_FALSE;
Expand Down Expand Up @@ -310,6 +318,7 @@ int main(int argc, char *argv[]) {
.serial = args.serial,
.crop = args.crop,
.port = args.port,
.outfilename = args.outfilename,
.max_size = args.max_size,
.bit_rate = args.bit_rate,
.show_touches = args.show_touches,
Expand Down
6 changes: 3 additions & 3 deletions app/src/scrcpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,11 @@ SDL_bool scrcpy(const struct scrcpy_options *options) {
goto finally_destroy_frames;
}

decoder_init(&decoder, &frames, device_socket);
decoder_init(&decoder, &frames, device_socket, frame_size);

// now we consumed the header values, the socket receives the video stream
// start the decoder
if (!decoder_start(&decoder)) {
if (!decoder_start(&decoder, options->outfilename)) {
ret = SDL_FALSE;
server_stop(&server);
goto finally_destroy_file_handler;
Expand Down Expand Up @@ -228,7 +228,7 @@ SDL_bool scrcpy(const struct scrcpy_options *options) {
}

ret = event_loop();
LOGD("quit...");
LOGI("quit...");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, LOGD is good enough.


screen_destroy(&screen);

Expand Down
1 change: 1 addition & 0 deletions app/src/scrcpy.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
struct scrcpy_options {
const char *serial;
const char *crop;
const char *outfilename;
Uint16 port;
Uint16 max_size;
Uint32 bit_rate;
Expand Down