@@ -55,6 +55,9 @@ private static class OutputTask {
55
55
private final List <CodecOption > codecOptions ;
56
56
private final String encoderName ;
57
57
58
+ private final boolean recreatePts ;
59
+ private long previousPts ;
60
+
58
61
// Capacity of 64 is in practice "infinite" (it is limited by the number of available MediaCodec buffers, typically 4).
59
62
// So many pending tasks would lead to an unacceptable delay anyway.
60
63
private final BlockingQueue <InputTask > inputTasks = new ArrayBlockingQueue <>(64 );
@@ -74,6 +77,10 @@ public AudioEncoder(AudioCapture capture, Streamer streamer, Options options) {
74
77
this .bitRate = options .getAudioBitRate ();
75
78
this .codecOptions = options .getAudioCodecOptions ();
76
79
this .encoderName = options .getAudioEncoder ();
80
+
81
+ // The OPUS encoder generates its own input PTS which matches the number of samples. This is not the behavior we want: it ignores any clock
82
+ // drift and hard silences (packets not produced on silence). To fix this behavior, regenerate PTS based on the current time.
83
+ recreatePts = streamer .getCodec () == AudioCodec .OPUS ;
77
84
}
78
85
79
86
private static MediaFormat createFormat (String mimeType , int bitRate , List <CodecOption > codecOptions ) {
@@ -118,13 +125,34 @@ private void outputThread(MediaCodec mediaCodec) throws IOException, Interrupted
118
125
OutputTask task = outputTasks .take ();
119
126
ByteBuffer buffer = mediaCodec .getOutputBuffer (task .index );
120
127
try {
128
+ if (recreatePts ) {
129
+ fixTimestamp (task .bufferInfo );
130
+ }
121
131
streamer .writePacket (buffer , task .bufferInfo );
122
132
} finally {
123
133
mediaCodec .releaseOutputBuffer (task .index , false );
124
134
}
125
135
}
126
136
}
127
137
138
+ private void fixTimestamp (MediaCodec .BufferInfo bufferInfo ) {
139
+ assert recreatePts ;
140
+
141
+ if ((bufferInfo .flags & MediaCodec .BUFFER_FLAG_CODEC_CONFIG ) != 0 ) {
142
+ // Config packet, nothing to fix
143
+ return ;
144
+ }
145
+
146
+ long pts = bufferInfo .presentationTimeUs ;
147
+ if (previousPts != 0 ) {
148
+ long now = System .nanoTime () / 1000 ;
149
+ long duration = pts - previousPts ;
150
+ bufferInfo .presentationTimeUs = now - duration ;
151
+ }
152
+
153
+ previousPts = pts ;
154
+ }
155
+
128
156
@ Override
129
157
public void start (TerminationListener listener ) {
130
158
thread = new Thread (() -> {
0 commit comments