Mp4FromDashWriter fixes

* correct calculation of "co64" box and usage of 64bits offsets
* generate one chunk for audio streams like ffmpeg does, attempt to fix cut-off audio
* misc. cleanup
This commit is contained in:
kapodamy 2019-09-29 01:44:13 -03:00
parent 86dafdd92b
commit 570738190d
1 changed files with 50 additions and 29 deletions

View File

@ -6,6 +6,7 @@ import org.schabi.newpipe.streams.Mp4DashReader.Mp4DashChunk;
import org.schabi.newpipe.streams.Mp4DashReader.Mp4DashSample; import org.schabi.newpipe.streams.Mp4DashReader.Mp4DashSample;
import org.schabi.newpipe.streams.Mp4DashReader.Mp4Track; import org.schabi.newpipe.streams.Mp4DashReader.Mp4Track;
import org.schabi.newpipe.streams.Mp4DashReader.TrunEntry; import org.schabi.newpipe.streams.Mp4DashReader.TrunEntry;
import org.schabi.newpipe.streams.Mp4DashReader.TrackKind;
import org.schabi.newpipe.streams.io.SharpStream; import org.schabi.newpipe.streams.io.SharpStream;
import java.io.IOException; import java.io.IOException;
@ -22,6 +23,7 @@ public class Mp4FromDashWriter {
private final static byte SAMPLES_PER_CHUNK = 6;// ffmpeg uses 2, basic uses 1 (with 60fps uses 21 or 22). NewPipe will use 6 private final static byte SAMPLES_PER_CHUNK = 6;// ffmpeg uses 2, basic uses 1 (with 60fps uses 21 or 22). NewPipe will use 6
private final static long THRESHOLD_FOR_CO64 = 0xFFFEFFFFL;// near 3.999 GiB private final static long THRESHOLD_FOR_CO64 = 0xFFFEFFFFL;// near 3.999 GiB
private final static int THRESHOLD_MOOV_LENGTH = (256 * 1024) + (2048 * 1024); // 2.2 MiB enough for: 1080p 60fps 00h35m00s private final static int THRESHOLD_MOOV_LENGTH = (256 * 1024) + (2048 * 1024); // 2.2 MiB enough for: 1080p 60fps 00h35m00s
private final static short SINGLE_CHUNK_SAMPLE_BUFFER = 256;
private final long time; private final long time;
@ -145,7 +147,7 @@ public class Mp4FromDashWriter {
// not allowed for very short tracks (less than 0.5 seconds) // not allowed for very short tracks (less than 0.5 seconds)
// //
outStream = output; outStream = output;
int read = 8;// mdat box header size long read = 8;// mdat box header size
long totalSampleSize = 0; long totalSampleSize = 0;
int[] sampleExtra = new int[readers.length]; int[] sampleExtra = new int[readers.length];
int[] defaultMediaTime = new int[readers.length]; int[] defaultMediaTime = new int[readers.length];
@ -157,6 +159,8 @@ public class Mp4FromDashWriter {
tablesInfo[i] = new TablesInfo(); tablesInfo[i] = new TablesInfo();
} }
boolean singleChunk = tracks.length == 1 && tracks[0].kind == TrackKind.Audio;
//<editor-fold defaultstate="expanded" desc="calculate stbl sample tables size and required moov values"> //<editor-fold defaultstate="expanded" desc="calculate stbl sample tables size and required moov values">
for (int i = 0; i < readers.length; i++) { for (int i = 0; i < readers.length; i++) {
int samplesSize = 0; int samplesSize = 0;
@ -210,14 +214,21 @@ public class Mp4FromDashWriter {
tablesInfo[i].stco = (tmp / SAMPLES_PER_CHUNK) + 1;// +1 for samples in first chunk tablesInfo[i].stco = (tmp / SAMPLES_PER_CHUNK) + 1;// +1 for samples in first chunk
tmp = tmp % SAMPLES_PER_CHUNK; tmp = tmp % SAMPLES_PER_CHUNK;
if (tmp == 0) { if (singleChunk) {
// avoid split audio streams in chunks
tablesInfo[i].stsc = 1;
tablesInfo[i].stsc_bEntries = new int[]{
1, tablesInfo[i].stsz, 1
};
tablesInfo[i].stco = 1;
} else if (tmp == 0) {
tablesInfo[i].stsc = 2;// first chunk (init) and succesive chunks tablesInfo[i].stsc = 2;// first chunk (init) and succesive chunks
tablesInfo[i].stsc_bEntries = new int[]{ tablesInfo[i].stsc_bEntries = new int[]{
1, SAMPLES_PER_CHUNK_INIT, 1, 1, SAMPLES_PER_CHUNK_INIT, 1,
2, SAMPLES_PER_CHUNK, 1 2, SAMPLES_PER_CHUNK, 1
}; };
} else { } else {
tablesInfo[i].stsc = 3;// first chunk (init) and succesive chunks and remain chunk tablesInfo[i].stsc = 3;// first chunk (init) and successive chunks and remain chunk
tablesInfo[i].stsc_bEntries = new int[]{ tablesInfo[i].stsc_bEntries = new int[]{
1, SAMPLES_PER_CHUNK_INIT, 1, 1, SAMPLES_PER_CHUNK_INIT, 1,
2, SAMPLES_PER_CHUNK, 1, 2, SAMPLES_PER_CHUNK, 1,
@ -268,10 +279,10 @@ public class Mp4FromDashWriter {
} else {*/ } else {*/
if (auxSize > 0) { if (auxSize > 0) {
int length = auxSize; int length = auxSize;
byte[] buffer = new byte[8 * 1024];// 8 KiB byte[] buffer = new byte[64 * 1024];// 64 KiB
while (length > 0) { while (length > 0) {
int count = Math.min(length, buffer.length); int count = Math.min(length, buffer.length);
outWrite(buffer, 0, count); outWrite(buffer, count);
length -= count; length -= count;
} }
} }
@ -280,7 +291,7 @@ public class Mp4FromDashWriter {
outSeek(ftyp_size); outSeek(ftyp_size);
} }
// tablesInfo contais row counts // tablesInfo contains row counts
// and after returning from make_moov() will contain table offsets // and after returning from make_moov() will contain table offsets
make_moov(defaultMediaTime, tablesInfo, is64); make_moov(defaultMediaTime, tablesInfo, is64);
@ -291,7 +302,7 @@ public class Mp4FromDashWriter {
writeEntryArray(tablesInfo[i].stsc, tablesInfo[i].stsc_bEntries.length, tablesInfo[i].stsc_bEntries); writeEntryArray(tablesInfo[i].stsc, tablesInfo[i].stsc_bEntries.length, tablesInfo[i].stsc_bEntries);
tablesInfo[i].stsc_bEntries = null; tablesInfo[i].stsc_bEntries = null;
if (tablesInfo[i].ctts > 0) { if (tablesInfo[i].ctts > 0) {
sampleCount[i] = 1;// index is not base zero sampleCount[i] = 1;// the index is not base zero
sampleExtra[i] = -1; sampleExtra[i] = -1;
} }
} }
@ -303,8 +314,8 @@ public class Mp4FromDashWriter {
outWrite(make_mdat(totalSampleSize, is64)); outWrite(make_mdat(totalSampleSize, is64));
int[] sampleIndex = new int[readers.length]; int[] sampleIndex = new int[readers.length];
int[] sizes = new int[SAMPLES_PER_CHUNK]; int[] sizes = new int[singleChunk ? SINGLE_CHUNK_SAMPLE_BUFFER : SAMPLES_PER_CHUNK];
int[] sync = new int[SAMPLES_PER_CHUNK]; int[] sync = new int[singleChunk ? SINGLE_CHUNK_SAMPLE_BUFFER : SAMPLES_PER_CHUNK];
int written = readers.length; int written = readers.length;
while (written > 0) { while (written > 0) {
@ -317,7 +328,12 @@ public class Mp4FromDashWriter {
long chunkOffset = writeOffset; long chunkOffset = writeOffset;
int syncCount = 0; int syncCount = 0;
int limit = sampleIndex[i] == 0 ? SAMPLES_PER_CHUNK_INIT : SAMPLES_PER_CHUNK; int limit;
if (singleChunk) {
limit = SINGLE_CHUNK_SAMPLE_BUFFER;
} else {
limit = sampleIndex[i] == 0 ? SAMPLES_PER_CHUNK_INIT : SAMPLES_PER_CHUNK;
}
int j = 0; int j = 0;
for (; j < limit; j++) { for (; j < limit; j++) {
@ -354,7 +370,7 @@ public class Mp4FromDashWriter {
sizes[j] = sample.data.length; sizes[j] = sample.data.length;
} }
outWrite(sample.data, 0, sample.data.length); outWrite(sample.data, sample.data.length);
} }
if (j > 0) { if (j > 0) {
@ -368,12 +384,18 @@ public class Mp4FromDashWriter {
tablesInfo[i].stss = writeEntryArray(tablesInfo[i].stss, syncCount, sync); tablesInfo[i].stss = writeEntryArray(tablesInfo[i].stss, syncCount, sync);
} }
if (tablesInfo[i].stco > 0) {
if (is64) { if (is64) {
tablesInfo[i].stco = writeEntry64(tablesInfo[i].stco, chunkOffset); tablesInfo[i].stco = writeEntry64(tablesInfo[i].stco, chunkOffset);
} else { } else {
tablesInfo[i].stco = writeEntryArray(tablesInfo[i].stco, 1, (int) chunkOffset); tablesInfo[i].stco = writeEntryArray(tablesInfo[i].stco, 1, (int) chunkOffset);
} }
if (singleChunk) {
tablesInfo[i].stco = -1;
}
}
outRestore(); outRestore();
} }
} }
@ -451,12 +473,12 @@ public class Mp4FromDashWriter {
// <editor-fold defaultstate="expanded" desc="Utils"> // <editor-fold defaultstate="expanded" desc="Utils">
private void outWrite(byte[] buffer) throws IOException { private void outWrite(byte[] buffer) throws IOException {
outWrite(buffer, 0, buffer.length); outWrite(buffer, buffer.length);
} }
private void outWrite(byte[] buffer, int offset, int count) throws IOException { private void outWrite(byte[] buffer, int count) throws IOException {
writeOffset += count; writeOffset += count;
outStream.write(buffer, offset, count); outStream.write(buffer, 0, count);
} }
private void outSeek(long offset) throws IOException { private void outSeek(long offset) throws IOException {
@ -509,7 +531,6 @@ public class Mp4FromDashWriter {
); );
if (extra >= 0) { if (extra >= 0) {
//size += 4;// commented for auxiliar buffer !!!
offset += 4; offset += 4;
auxWrite(extra); auxWrite(extra);
} }
@ -531,7 +552,7 @@ public class Mp4FromDashWriter {
if (moovSimulation) { if (moovSimulation) {
writeOffset += buffer.length; writeOffset += buffer.length;
} else if (auxBuffer == null) { } else if (auxBuffer == null) {
outWrite(buffer, 0, buffer.length); outWrite(buffer, buffer.length);
} else { } else {
auxBuffer.put(buffer); auxBuffer.put(buffer);
} }
@ -703,7 +724,7 @@ public class Mp4FromDashWriter {
int mediaTime; int mediaTime;
if (tracks[index].trak.edst_elst == null) { if (tracks[index].trak.edst_elst == null) {
// is a audio track ¿is edst/elst opcional for audio tracks? // is a audio track ¿is edst/elst optional for audio tracks?
mediaTime = 0x00;// ffmpeg set this value as zero, instead of defaultMediaTime mediaTime = 0x00;// ffmpeg set this value as zero, instead of defaultMediaTime
bMediaRate = 0x00010000; bMediaRate = 0x00010000;
} else { } else {
@ -798,13 +819,13 @@ public class Mp4FromDashWriter {
class TablesInfo { class TablesInfo {
public int stts; int stts;
public int stsc; int stsc;
public int[] stsc_bEntries; int[] stsc_bEntries;
public int ctts; int ctts;
public int stsz; int stsz;
public int stsz_default; int stsz_default;
public int stss; int stss;
public int stco; int stco;
} }
} }