mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-08-04 23:29:22 +00:00
Fix PTS produced by the default opus/flac encoders
The default OPUS and FLAC encoders on Android rewrite the input PTS so that they exactly match the number of samples. As a consequence: - audio clock drift is not compensated - implicit silences (without packets) are ignored To work around this behavior, generate new PTS based on the current time (after encoding) and the packet duration. PR #5870 <https://github.com/Genymobile/scrcpy/pull/5870>
This commit is contained in:
parent
1d25338119
commit
245981281e
1 changed files with 31 additions and 0 deletions
|
@ -55,6 +55,9 @@ public final class AudioEncoder implements AsyncProcessor {
|
||||||
private final List<CodecOption> codecOptions;
|
private final List<CodecOption> codecOptions;
|
||||||
private final String encoderName;
|
private final String encoderName;
|
||||||
|
|
||||||
|
private boolean recreatePts;
|
||||||
|
private long previousPts;
|
||||||
|
|
||||||
// Capacity of 64 is in practice "infinite" (it is limited by the number of available MediaCodec buffers, typically 4).
|
// Capacity of 64 is in practice "infinite" (it is limited by the number of available MediaCodec buffers, typically 4).
|
||||||
// So many pending tasks would lead to an unacceptable delay anyway.
|
// So many pending tasks would lead to an unacceptable delay anyway.
|
||||||
private final BlockingQueue<InputTask> inputTasks = new ArrayBlockingQueue<>(64);
|
private final BlockingQueue<InputTask> inputTasks = new ArrayBlockingQueue<>(64);
|
||||||
|
@ -118,6 +121,9 @@ public final class AudioEncoder implements AsyncProcessor {
|
||||||
OutputTask task = outputTasks.take();
|
OutputTask task = outputTasks.take();
|
||||||
ByteBuffer buffer = mediaCodec.getOutputBuffer(task.index);
|
ByteBuffer buffer = mediaCodec.getOutputBuffer(task.index);
|
||||||
try {
|
try {
|
||||||
|
if (recreatePts) {
|
||||||
|
fixTimestamp(task.bufferInfo);
|
||||||
|
}
|
||||||
streamer.writePacket(buffer, task.bufferInfo);
|
streamer.writePacket(buffer, task.bufferInfo);
|
||||||
} finally {
|
} finally {
|
||||||
mediaCodec.releaseOutputBuffer(task.index, false);
|
mediaCodec.releaseOutputBuffer(task.index, false);
|
||||||
|
@ -125,6 +131,25 @@ public final class AudioEncoder implements AsyncProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fixTimestamp(MediaCodec.BufferInfo bufferInfo) {
|
||||||
|
assert recreatePts;
|
||||||
|
|
||||||
|
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
|
||||||
|
// Config packet, nothing to fix
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long pts = bufferInfo.presentationTimeUs;
|
||||||
|
if (previousPts != 0) {
|
||||||
|
long now = System.nanoTime() / 1000;
|
||||||
|
// This specific encoder produces PTS matching the exact number of samples
|
||||||
|
long duration = pts - previousPts;
|
||||||
|
bufferInfo.presentationTimeUs = now - duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
previousPts = pts;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(TerminationListener listener) {
|
public void start(TerminationListener listener) {
|
||||||
thread = new Thread(() -> {
|
thread = new Thread(() -> {
|
||||||
|
@ -194,6 +219,12 @@ public final class AudioEncoder implements AsyncProcessor {
|
||||||
Codec codec = streamer.getCodec();
|
Codec codec = streamer.getCodec();
|
||||||
mediaCodec = createMediaCodec(codec, encoderName);
|
mediaCodec = createMediaCodec(codec, encoderName);
|
||||||
|
|
||||||
|
// The default OPUS and FLAC encoders overwrite the input PTS with a value that matches the number of samples. This is not the behavior
|
||||||
|
// we want: it ignores any audio clock drift and hard silences (packets not produced on silence). To work around this behavior,
|
||||||
|
// regenerate PTS based on the current time and the packet duration.
|
||||||
|
String codecName = mediaCodec.getCanonicalName();
|
||||||
|
recreatePts = "c2.android.opus.encoder".equals(codecName) || "c2.android.flac.encoder".equals(codecName);
|
||||||
|
|
||||||
mediaCodecThread = new HandlerThread("media-codec");
|
mediaCodecThread = new HandlerThread("media-codec");
|
||||||
mediaCodecThread.start();
|
mediaCodecThread.start();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue