From 1c82c3923d63985655686dd5884a7a9e9407619e Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 1 Feb 2023 21:56:43 +0100 Subject: [PATCH] Compute relative PTS on the client-side The PTS received from MediaCodec are expressed relative to an arbitrary clock origin. We consider the PTS of the first frame to be 0, and the PTS of every other frame is relative to this first PTS (note that the PTS is only used for recording, it is ignored for mirroring). For simplicity, this relative PTS was computed on the server-side. To prepare support for multiple stream (video and audio), send the packet with its original PTS, and handle the PTS offset on the client-side (by the recorder). Since we can't know in advance which stream will produce the first packet with the lowest PTS (a packet received later on one stream may have a PTS lower than a packet received earlier on another stream), computing the PTS on the server-side would require unnecessary waiting. --- app/src/recorder.c | 15 +++++++++++++++ app/src/recorder.h | 2 ++ .../java/com/genymobile/scrcpy/ScreenEncoder.java | 6 +----- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/app/src/recorder.c b/app/src/recorder.c index b14b6050..455e1db1 100644 --- a/app/src/recorder.c +++ b/app/src/recorder.c @@ -11,6 +11,8 @@ /** Downcast packet_sink to recorder */ #define DOWNCAST(SINK) container_of(SINK, struct sc_recorder, packet_sink) +#define SC_PTS_ORIGIN_NONE UINT64_C(-1) + static const AVRational SCRCPY_TIME_BASE = {1, 1000000}; // timestamps in us static const AVOutputFormat * @@ -169,6 +171,18 @@ run_recorder(void *data) { sc_mutex_unlock(&recorder->mutex); + if (recorder->pts_origin == SC_PTS_ORIGIN_NONE + && rec->packet->pts != AV_NOPTS_VALUE) { + // First PTS received + recorder->pts_origin = rec->packet->pts; + } + + if (rec->packet->pts != AV_NOPTS_VALUE) { + // Set PTS relatve to the origin + rec->packet->pts -= recorder->pts_origin; + rec->packet->dts = rec->packet->pts; + } + // recorder->previous is only written from this thread, no need to lock struct sc_record_packet *previous = recorder->previous; recorder->previous = rec; @@ -243,6 +257,7 @@ sc_recorder_open(struct sc_recorder *recorder, const AVCodec *input_codec) { recorder->failed = false; recorder->header_written = false; recorder->previous = NULL; + recorder->pts_origin = SC_PTS_ORIGIN_NONE; const char *format_name = sc_recorder_get_format_name(recorder->format); assert(format_name); diff --git a/app/src/recorder.h b/app/src/recorder.h index 373278e6..a03c91d7 100644 --- a/app/src/recorder.h +++ b/app/src/recorder.h @@ -28,6 +28,8 @@ struct sc_recorder { struct sc_size declared_frame_size; bool header_written; + uint64_t pts_origin; + sc_thread thread; sc_mutex mutex; sc_cond queue_cond; diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java index c7f886b4..07ea2d80 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java @@ -42,7 +42,6 @@ public class ScreenEncoder implements Device.RotationListener { private final int maxFps; private final boolean sendFrameMeta; private final boolean downsizeOnError; - private long ptsOrigin; private boolean firstFrameSent; private int consecutiveErrors; @@ -207,10 +206,7 @@ public class ScreenEncoder implements Device.RotationListener { if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { pts = PACKET_FLAG_CONFIG; // non-media data packet } else { - if (ptsOrigin == 0) { - ptsOrigin = bufferInfo.presentationTimeUs; - } - pts = bufferInfo.presentationTimeUs - ptsOrigin; + pts = bufferInfo.presentationTimeUs; if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) { pts |= PACKET_FLAG_KEY_FRAME; }