From 653387acdb5aafe54ae08215ac0739e3af526d8b 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 by 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 | 17 +++++++++++++++++ app/src/recorder.h | 2 ++ .../com/genymobile/scrcpy/ScreenEncoder.java | 6 +----- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/app/src/recorder.c b/app/src/recorder.c index b14b6050..18923527 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 * @@ -128,6 +130,8 @@ sc_recorder_write(struct sc_recorder *recorder, AVPacket *packet) { return true; } + LOGI("==== %" PRIu64, packet->pts); + sc_recorder_rescale_packet(recorder, packet); return av_write_frame(recorder->ctx, packet) >= 0; } @@ -169,6 +173,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 +259,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 f0384e2c..1d30fe96 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; @@ -218,10 +217,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; }