Skip to content

Commit d706c5d

Browse files
igorinovrom1v
authored andcommitted
Enable video output file, with pts set by server
1 parent b5c64c0 commit d706c5d

File tree

7 files changed

+126
-10
lines changed

7 files changed

+126
-10
lines changed

app/src/decoder.c

+96-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "decoder.h"
22

33
#include <libavformat/avformat.h>
4+
#include <libavutil/time.h>
45
#include <SDL2/SDL_events.h>
56
#include <SDL2/SDL_mutex.h>
67
#include <SDL2/SDL_thread.h>
@@ -14,9 +15,50 @@
1415

1516
#define BUFSIZE 0x10000
1617

18+
static AVRational us = {1, 1000000};
19+
20+
static inline uint64_t from_be(uint8_t *b, int size)
21+
{
22+
uint64_t x = 0;
23+
int i;
24+
25+
for (i = 0; i < size; i += 1) {
26+
x <<= 8;
27+
x |= b[i];
28+
}
29+
30+
return x;
31+
}
32+
33+
#define HEADER_SIZE 16
34+
1735
static int read_packet(void *opaque, uint8_t *buf, int buf_size) {
1836
struct decoder *decoder = opaque;
19-
return net_recv(decoder->video_socket, buf, buf_size);
37+
uint8_t header[HEADER_SIZE];
38+
int remaining;
39+
int ret;
40+
41+
remaining = decoder->remaining;
42+
if (remaining == 0) {
43+
ret = net_recv(decoder->video_socket, header, HEADER_SIZE);
44+
if (ret <= 0)
45+
return ret;
46+
47+
decoder->pts = from_be(header, 8);
48+
remaining = from_be(header + 12, 4);
49+
}
50+
51+
if (buf_size > remaining)
52+
buf_size = remaining;
53+
54+
ret = net_recv(decoder->video_socket, buf, buf_size);
55+
if (ret <= 0)
56+
return ret;
57+
58+
remaining -= ret;
59+
decoder->remaining = remaining;
60+
61+
return ret;
2062
}
2163

2264
// set the decoded frame as ready for rendering, and notify
@@ -40,6 +82,7 @@ static void notify_stopped(void) {
4082

4183
static int run_decoder(void *data) {
4284
struct decoder *decoder = data;
85+
int ret;
4386

4487
AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);
4588
if (!codec) {
@@ -86,16 +129,55 @@ static int run_decoder(void *data) {
86129
goto run_finally_free_avio_ctx;
87130
}
88131

132+
AVStream *outstream = NULL;
133+
AVFormatContext *output_ctx = NULL;
134+
if (decoder->out_filename) {
135+
avformat_alloc_output_context2(&output_ctx, NULL, NULL, decoder->out_filename);
136+
if (!output_ctx) {
137+
LOGE("Could not allocate output format context");
138+
goto run_finally_free_avio_ctx;
139+
} else {
140+
outstream = avformat_new_stream(output_ctx, codec);
141+
if (!outstream) {
142+
LOGE("Could not allocate output stream");
143+
goto run_finally_free_output_ctx;
144+
}
145+
outstream->codec = avcodec_alloc_context3(codec);
146+
outstream->codec->pix_fmt = AV_PIX_FMT_YUV420P;
147+
outstream->codec->width = decoder->frame_size.width;
148+
outstream->codec->height = decoder->frame_size.height;
149+
outstream->time_base = (AVRational) {1, 60};
150+
outstream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
151+
ret = avio_open(&output_ctx->pb, decoder->out_filename, AVIO_FLAG_WRITE);
152+
if (ret < 0) {
153+
LOGE("Failed to open output file");
154+
goto run_finally_free_output_ctx;
155+
}
156+
ret = avformat_write_header(output_ctx, NULL);
157+
if (ret < 0) {
158+
LOGE("Error writing output header");
159+
avio_closep(&output_ctx->pb);
160+
goto run_finally_free_output_ctx;
161+
}
162+
}
163+
}
164+
89165
AVPacket packet;
90166
av_init_packet(&packet);
91167
packet.data = NULL;
92168
packet.size = 0;
93169

94170
while (!av_read_frame(format_ctx, &packet)) {
171+
172+
if (output_ctx) {
173+
packet.pts = decoder->pts;
174+
av_packet_rescale_ts(&packet, us, outstream->time_base);
175+
ret = av_write_frame(output_ctx, &packet);
176+
}
177+
95178
// the new decoding/encoding API has been introduced by:
96179
// <http://git.videolan.org/?p=ffmpeg.git;a=commitdiff;h=7fc329e2dd6226dfecaa4a1d7adf353bf2773726>
97180
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 37, 0)
98-
int ret;
99181
if ((ret = avcodec_send_packet(codec_ctx, &packet)) < 0) {
100182
LOGE("Could not send video packet: %d", ret);
101183
goto run_quit;
@@ -125,6 +207,7 @@ static int run_decoder(void *data) {
125207
packet.data += len;
126208
}
127209
#endif
210+
128211
av_packet_unref(&packet);
129212

130213
if (avio_ctx->eof_reached) {
@@ -135,7 +218,14 @@ static int run_decoder(void *data) {
135218
LOGD("End of frames");
136219

137220
run_quit:
221+
if (output_ctx) {
222+
ret = av_write_trailer(output_ctx);
223+
avio_closep(&output_ctx->pb);
224+
}
138225
avformat_close_input(&format_ctx);
226+
run_finally_free_output_ctx:
227+
if (output_ctx)
228+
avformat_free_context(output_ctx);
139229
run_finally_free_avio_ctx:
140230
av_freep(&avio_ctx);
141231
run_finally_free_format_ctx:
@@ -149,20 +239,21 @@ static int run_decoder(void *data) {
149239
return 0;
150240
}
151241

152-
void decoder_init(struct decoder *decoder, struct frames *frames, socket_t video_socket) {
242+
void decoder_init(struct decoder *decoder, struct frames *frames, socket_t video_socket, struct size frame_size) {
153243
decoder->frames = frames;
154244
decoder->video_socket = video_socket;
245+
decoder->frame_size = frame_size;
155246
}
156247

157-
SDL_bool decoder_start(struct decoder *decoder) {
248+
SDL_bool decoder_start(struct decoder *decoder, const char *out_filename) {
158249
LOGD("Starting decoder thread");
159250

251+
decoder->out_filename = out_filename;
160252
decoder->thread = SDL_CreateThread(run_decoder, "video_decoder", decoder);
161253
if (!decoder->thread) {
162254
LOGC("Could not start decoder thread");
163255
return SDL_FALSE;
164256
}
165-
166257
return SDL_TRUE;
167258
}
168259

app/src/decoder.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,24 @@
44
#include <SDL2/SDL_stdinc.h>
55
#include <SDL2/SDL_thread.h>
66

7+
#include "common.h"
78
#include "net.h"
89

910
struct frames;
1011

1112
struct decoder {
13+
uint64_t pts;
1214
struct frames *frames;
1315
socket_t video_socket;
1416
SDL_Thread *thread;
1517
SDL_mutex *mutex;
18+
const char *out_filename;
19+
struct size frame_size;
20+
int remaining;
1621
};
1722

18-
void decoder_init(struct decoder *decoder, struct frames *frames, socket_t video_socket);
19-
SDL_bool decoder_start(struct decoder *decoder);
23+
void decoder_init(struct decoder *decoder, struct frames *frames, socket_t video_socket, struct size frame_size);
24+
SDL_bool decoder_start(struct decoder *decoder, const char *out_filename);
2025
void decoder_stop(struct decoder *decoder);
2126
void decoder_join(struct decoder *decoder);
2227

app/src/main.c

+10-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
struct args {
1212
const char *serial;
1313
const char *crop;
14+
const char *out_filename;
1415
SDL_bool fullscreen;
1516
SDL_bool help;
1617
SDL_bool version;
@@ -49,6 +50,9 @@ static void usage(const char *arg0) {
4950
" is preserved.\n"
5051
" Default is %d%s.\n"
5152
"\n"
53+
" -o, --output-file\n"
54+
" Write video output to file.\n"
55+
"\n"
5256
" -p, --port port\n"
5357
" Set the TCP port the client listens on.\n"
5458
" Default is %d.\n"
@@ -207,14 +211,15 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
207211
{"fullscreen", no_argument, NULL, 'f'},
208212
{"help", no_argument, NULL, 'h'},
209213
{"max-size", required_argument, NULL, 'm'},
214+
{"output-file", required_argument, NULL, 'o'},
210215
{"port", required_argument, NULL, 'p'},
211216
{"serial", required_argument, NULL, 's'},
212217
{"show-touches", no_argument, NULL, 't'},
213218
{"version", no_argument, NULL, 'v'},
214219
{NULL, 0, NULL, 0 },
215220
};
216221
int c;
217-
while ((c = getopt_long(argc, argv, "b:c:fhm:p:s:tv", long_options, NULL)) != -1) {
222+
while ((c = getopt_long(argc, argv, "b:c:fhm:o:p:s:tv", long_options, NULL)) != -1) {
218223
switch (c) {
219224
case 'b':
220225
if (!parse_bit_rate(optarg, &args->bit_rate)) {
@@ -235,6 +240,9 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
235240
return SDL_FALSE;
236241
}
237242
break;
243+
case 'o':
244+
args->out_filename = optarg;
245+
break;
238246
case 'p':
239247
if (!parse_port(optarg, &args->port)) {
240248
return SDL_FALSE;
@@ -310,6 +318,7 @@ int main(int argc, char *argv[]) {
310318
.serial = args.serial,
311319
.crop = args.crop,
312320
.port = args.port,
321+
.out_filename = args.out_filename,
313322
.max_size = args.max_size,
314323
.bit_rate = args.bit_rate,
315324
.show_touches = args.show_touches,

app/src/scrcpy.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,11 @@ SDL_bool scrcpy(const struct scrcpy_options *options) {
193193
goto finally_destroy_frames;
194194
}
195195

196-
decoder_init(&decoder, &frames, device_socket);
196+
decoder_init(&decoder, &frames, device_socket, frame_size);
197197

198198
// now we consumed the header values, the socket receives the video stream
199199
// start the decoder
200-
if (!decoder_start(&decoder)) {
200+
if (!decoder_start(&decoder, options->out_filename)) {
201201
ret = SDL_FALSE;
202202
server_stop(&server);
203203
goto finally_destroy_file_handler;

app/src/scrcpy.h

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
struct scrcpy_options {
77
const char *serial;
88
const char *crop;
9+
const char *out_filename;
910
Uint16 port;
1011
Uint16 max_size;
1112
Uint32 bit_rate;

gradle.properties

+1
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ org.gradle.jvmargs=-Xmx1536m
1515
# This option should only be used with decoupled projects. More details, visit
1616
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
1717
# org.gradle.parallel=true
18+

server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.genymobile.scrcpy.wrappers.SurfaceControl;
44

55
import android.graphics.Rect;
6+
import android.media.MediaMuxer;
67
import android.media.MediaCodec;
78
import android.media.MediaCodecInfo;
89
import android.media.MediaFormat;
@@ -80,6 +81,8 @@ public void streamScreen(Device device, FileDescriptor fd) throws IOException {
8081
private boolean encode(MediaCodec codec, FileDescriptor fd) throws IOException {
8182
boolean eof = false;
8283
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
84+
ByteBuffer bBuffer = ByteBuffer.allocate(16);
85+
8386
while (!consumeRotationChange() && !eof) {
8487
int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, -1);
8588
eof = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
@@ -90,6 +93,12 @@ private boolean encode(MediaCodec codec, FileDescriptor fd) throws IOException {
9093
}
9194
if (outputBufferId >= 0) {
9295
ByteBuffer codecBuffer = codec.getOutputBuffer(outputBufferId);
96+
bBuffer.position(0);
97+
bBuffer.putLong(bufferInfo.presentationTimeUs);
98+
bBuffer.putInt(bufferInfo.flags);
99+
bBuffer.putInt(codecBuffer.remaining());
100+
bBuffer.position(0);
101+
IO.writeFully(fd, bBuffer);
93102
IO.writeFully(fd, codecBuffer);
94103
}
95104
} finally {

0 commit comments

Comments
 (0)