Skip to content

Commit 23b3c42

Browse files
committed
Fix audio-video sync issue in DASH MP4 muxer by normalizing composition timestamps
1 parent 515bb6e commit 23b3c42

1 file changed

Lines changed: 48 additions & 6 deletions

File tree

app/src/main/java/org/schabi/newpipe/streams/Mp4FromDashWriter.java

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ public void build(final SharpStream output) throws IOException {
162162
final int[] defaultMediaTime = new int[readers.length];
163163
final int[] defaultSampleDuration = new int[readers.length];
164164
final int[] sampleCount = new int[readers.length];
165+
long[] firstPts = new long[readers.length];
166+
boolean[] firstPtsSet = new boolean[readers.length];
167+
boolean minPtsComputed = false;
168+
long minPts = 0;
169+
long[] basePts = new long[readers.length];
170+
boolean[] basePtsSet = new boolean[readers.length];
165171

166172
final TablesInfo[] tablesInfo = new TablesInfo[tracks.length];
167173
for (int i = 0; i < tablesInfo.length; i++) {
@@ -317,7 +323,7 @@ public void build(final SharpStream output) throws IOException {
317323

318324
for (int i = 0; i < readers.length; i++) {
319325
if (sampleIndex[i] < 0) {
320-
continue; // track is done
326+
continue;
321327
}
322328

323329
final long chunkOffset = writeOffset;
@@ -336,26 +342,62 @@ public void build(final SharpStream output) throws IOException {
336342
if (sample == null) {
337343
if (tablesInfo[i].ctts > 0 && sampleExtra[i] >= 0) {
338344
writeEntryArray(tablesInfo[i].ctts, 1, sampleCount[i],
339-
sampleExtra[i]); // flush last entries
345+
sampleExtra[i]);
340346
outRestore();
341347
}
342348
sampleIndex[i] = -1;
343349
break;
344350
}
345351

352+
// Normalize composition timestamps to align audio and video timelines
353+
long pts = sample.info.sampleCompositionTimeOffset;
354+
if (!firstPtsSet[i]) {
355+
firstPts[i] = pts;
356+
firstPtsSet[i] = true;
357+
}
358+
359+
if (!minPtsComputed) {
360+
boolean allSet = true;
361+
for (int k = 0; k < readers.length; k++) {
362+
if (!firstPtsSet[k]) {
363+
allSet = false;
364+
break;
365+
}
366+
}
367+
368+
if (allSet) {
369+
minPts = firstPts[0];
370+
for (int k = 1; k < readers.length; k++) {
371+
minPts = Math.min(minPts, firstPts[k]);
372+
}
373+
minPtsComputed = true;
374+
}
375+
}
376+
346377
sampleIndex[i]++;
347378

379+
if (!basePtsSet[i]) {
380+
if (minPtsComputed) {
381+
basePts[i] = minPts;
382+
} else {
383+
basePts[i] = firstPts[i];
384+
}
385+
basePtsSet[i] = true;
386+
}
387+
388+
int correctedOffset = (int) (pts - basePts[i]);
348389
if (tablesInfo[i].ctts > 0) {
349-
if (sample.info.sampleCompositionTimeOffset == sampleExtra[i]) {
390+
if (correctedOffset == sampleExtra[i]) {
350391
sampleCount[i]++;
351392
} else {
352393
if (sampleExtra[i] >= 0) {
353-
tablesInfo[i].ctts = writeEntryArray(tablesInfo[i].ctts, 2,
354-
sampleCount[i], sampleExtra[i]);
394+
tablesInfo[i].ctts = writeEntryArray(
395+
tablesInfo[i].ctts, 2, sampleCount[i], sampleExtra[i]
396+
);
355397
outRestore();
356398
}
357399
sampleCount[i] = 1;
358-
sampleExtra[i] = sample.info.sampleCompositionTimeOffset;
400+
sampleExtra[i] = correctedOffset;
359401
}
360402
}
361403

0 commit comments

Comments
 (0)