mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-30 23:03:00 +00:00 
			
		
		
		
	Use Checkstyle for org.schabi.newpipe.streams as well
This commit is contained in:
		| @@ -99,7 +99,6 @@ task runCheckstyle(type: Checkstyle) { | ||||
|     exclude '**/R.java' | ||||
|     exclude '**/BuildConfig.java' | ||||
|     exclude 'main/java/us/shandian/giga/**' | ||||
|     exclude 'main/java/org/schabi/newpipe/streams/**' | ||||
|  | ||||
|     // empty classpath | ||||
|     classpath = files() | ||||
|   | ||||
| @@ -10,13 +10,12 @@ import java.io.InputStream; | ||||
|  * @author kapodamy | ||||
|  */ | ||||
| public class DataReader { | ||||
|     public static final int SHORT_SIZE = 2; | ||||
|     public static final int LONG_SIZE = 8; | ||||
|     public static final int INTEGER_SIZE = 4; | ||||
|     public static final int FLOAT_SIZE = 4; | ||||
|  | ||||
|     public final static int SHORT_SIZE = 2; | ||||
|     public final static int LONG_SIZE = 8; | ||||
|     public final static int INTEGER_SIZE = 4; | ||||
|     public final static int FLOAT_SIZE = 4; | ||||
|  | ||||
|     private final static int BUFFER_SIZE = 128 * 1024;// 128 KiB | ||||
|     private static final int BUFFER_SIZE = 128 * 1024; // 128 KiB | ||||
|  | ||||
|     private long position = 0; | ||||
|     private final SharpStream stream; | ||||
| @@ -24,7 +23,7 @@ public class DataReader { | ||||
|     private InputStream view; | ||||
|     private int viewSize; | ||||
|  | ||||
|     public DataReader(SharpStream stream) { | ||||
|     public DataReader(final SharpStream stream) { | ||||
|         this.stream = stream; | ||||
|         this.readOffset = this.readBuffer.length; | ||||
|     } | ||||
| @@ -74,6 +73,7 @@ public class DataReader { | ||||
|         return value & 0xffffffffL; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public short readShort() throws IOException { | ||||
|         primitiveRead(SHORT_SIZE); | ||||
|         return (short) (primitive[0] << 8 | primitive[1]); | ||||
| @@ -86,11 +86,11 @@ public class DataReader { | ||||
|         return high << 32 | low; | ||||
|     } | ||||
|  | ||||
|     public int read(byte[] buffer) throws IOException { | ||||
|     public int read(final byte[] buffer) throws IOException { | ||||
|         return read(buffer, 0, buffer.length); | ||||
|     } | ||||
|  | ||||
|     public int read(byte[] buffer, int offset, int count) throws IOException { | ||||
|     public int read(final byte[] buffer, int offset, int count) throws IOException { | ||||
|         if (readCount < 0) { | ||||
|             return -1; | ||||
|         } | ||||
| @@ -135,7 +135,7 @@ public class DataReader { | ||||
|         stream.rewind(); | ||||
|  | ||||
|         if ((position - viewSize) > 0) { | ||||
|             viewSize = 0;// drop view | ||||
|             viewSize = 0; // drop view | ||||
|         } else { | ||||
|             viewSize += position; | ||||
|         } | ||||
| @@ -157,7 +157,7 @@ public class DataReader { | ||||
|      * @param size the size of the view | ||||
|      * @return the view | ||||
|      */ | ||||
|     public InputStream getView(int size) { | ||||
|     public InputStream getView(final int size) { | ||||
|         if (view == null) { | ||||
|             view = new InputStream() { | ||||
|                 @Override | ||||
| @@ -173,12 +173,13 @@ public class DataReader { | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public int read(byte[] buffer) throws IOException { | ||||
|                 public int read(final byte[] buffer) throws IOException { | ||||
|                     return read(buffer, 0, buffer.length); | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public int read(byte[] buffer, int offset, int count) throws IOException { | ||||
|                 public int read(final byte[] buffer, final int offset, final int count) | ||||
|                         throws IOException { | ||||
|                     if (viewSize < 1) { | ||||
|                         return -1; | ||||
|                     } | ||||
| @@ -190,7 +191,7 @@ public class DataReader { | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public long skip(long amount) throws IOException { | ||||
|                 public long skip(final long amount) throws IOException { | ||||
|                     if (viewSize < 1) { | ||||
|                         return 0; | ||||
|                     } | ||||
| @@ -224,16 +225,18 @@ public class DataReader { | ||||
|  | ||||
|     private final short[] primitive = new short[LONG_SIZE]; | ||||
|  | ||||
|     private void primitiveRead(int amount) throws IOException { | ||||
|     private void primitiveRead(final int amount) throws IOException { | ||||
|         byte[] buffer = new byte[amount]; | ||||
|         int read = read(buffer, 0, amount); | ||||
|  | ||||
|         if (read != amount) { | ||||
|             throw new EOFException("Truncated stream, missing " + String.valueOf(amount - read) + " bytes"); | ||||
|             throw new EOFException("Truncated stream, missing " | ||||
|                     + String.valueOf(amount - read) + " bytes"); | ||||
|         } | ||||
|  | ||||
|         for (int i = 0; i < amount; i++) { | ||||
|             primitive[i] = (short) (buffer[i] & 0xFF);// the "byte" data type in java is signed and is very annoying | ||||
|             // the "byte" data type in java is signed and is very annoying | ||||
|             primitive[i] = (short) (buffer[i] & 0xFF); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -256,5 +259,4 @@ public class DataReader { | ||||
|  | ||||
|         return readCount < 1; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -14,7 +14,6 @@ import java.util.NoSuchElementException; | ||||
|  * @author kapodamy | ||||
|  */ | ||||
| public class Mp4DashReader { | ||||
|  | ||||
|     private static final int ATOM_MOOF = 0x6D6F6F66; | ||||
|     private static final int ATOM_MFHD = 0x6D666864; | ||||
|     private static final int ATOM_TRAF = 0x74726166; | ||||
| @@ -50,7 +49,6 @@ public class Mp4DashReader { | ||||
|     private static final int HANDLER_SOUN = 0x736F756E; | ||||
|     private static final int HANDLER_SUBT = 0x73756274; | ||||
|  | ||||
|  | ||||
|     private final DataReader stream; | ||||
|  | ||||
|     private Mp4Track[] tracks = null; | ||||
| @@ -68,7 +66,7 @@ public class Mp4DashReader { | ||||
|         Audio, Video, Subtitles, Other | ||||
|     } | ||||
|  | ||||
|     public Mp4DashReader(SharpStream source) { | ||||
|     public Mp4DashReader(final SharpStream source) { | ||||
|         this.stream = new DataReader(source); | ||||
|     } | ||||
|  | ||||
| @@ -78,14 +76,15 @@ public class Mp4DashReader { | ||||
|         } | ||||
|  | ||||
|         box = readBox(ATOM_FTYP); | ||||
|         brands = parse_ftyp(box); | ||||
|         brands = parseFtyp(box); | ||||
|         switch (brands[0]) { | ||||
|             case BRAND_DASH: | ||||
|             case BRAND_ISO5:// ¿why not? | ||||
|                 break; | ||||
|             default: | ||||
|                 throw new NoSuchElementException( | ||||
|                         "Not a MPEG-4 DASH container, major brand is not 'dash' or 'iso5' is " + boxName(brands[0]) | ||||
|                         "Not a MPEG-4 DASH container, major brand is not 'dash' or 'iso5' is " | ||||
|                                 + boxName(brands[0]) | ||||
|                 ); | ||||
|         } | ||||
|  | ||||
| @@ -98,7 +97,7 @@ public class Mp4DashReader { | ||||
|  | ||||
|             switch (box.type) { | ||||
|                 case ATOM_MOOV: | ||||
|                     moov = parse_moov(box); | ||||
|                     moov = parseMoov(box); | ||||
|                     break; | ||||
|                 case ATOM_SIDX: | ||||
|                     break; | ||||
| @@ -117,10 +116,10 @@ public class Mp4DashReader { | ||||
|             tracks[i] = new Mp4Track(); | ||||
|             tracks[i].trak = moov.trak[i]; | ||||
|  | ||||
|             if (moov.mvex_trex != null) { | ||||
|                 for (Trex mvex_trex : moov.mvex_trex) { | ||||
|                     if (tracks[i].trak.tkhd.trackId == mvex_trex.trackId) { | ||||
|                         tracks[i].trex = mvex_trex; | ||||
|             if (moov.mvexTrex != null) { | ||||
|                 for (Trex mvexTrex : moov.mvexTrex) { | ||||
|                     if (tracks[i].trak.tkhd.trackId == mvexTrex.trackId) { | ||||
|                         tracks[i].trex = mvexTrex; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| @@ -144,7 +143,7 @@ public class Mp4DashReader { | ||||
|         backupBox = box; | ||||
|     } | ||||
|  | ||||
|     Mp4Track selectTrack(int index) { | ||||
|     Mp4Track selectTrack(final int index) { | ||||
|         selectedTrack = index; | ||||
|         return tracks[index]; | ||||
|     } | ||||
| @@ -179,7 +178,7 @@ public class Mp4DashReader { | ||||
|                 Box traf; | ||||
|                 while ((traf = untilBox(tmp, ATOM_TRAF)) != null) { | ||||
|                     Box tfhd = readBox(ATOM_TFHD); | ||||
|                     if (parse_tfhd(tracks[selectedTrack].trak.tkhd.trackId) != null) { | ||||
|                     if (parseTfhd(tracks[selectedTrack].trak.tkhd.trackId) != null) { | ||||
|                         count++; | ||||
|                         break; | ||||
|                     } | ||||
| @@ -196,7 +195,9 @@ public class Mp4DashReader { | ||||
|     } | ||||
|  | ||||
|     public int[] getBrands() { | ||||
|         if (brands == null) throw new IllegalStateException("Not parsed"); | ||||
|         if (brands == null) { | ||||
|             throw new IllegalStateException("Not parsed"); | ||||
|         } | ||||
|         return brands; | ||||
|     } | ||||
|  | ||||
| @@ -219,7 +220,7 @@ public class Mp4DashReader { | ||||
|         return tracks; | ||||
|     } | ||||
|  | ||||
|     public Mp4DashChunk getNextChunk(boolean infoOnly) throws IOException { | ||||
|     public Mp4DashChunk getNextChunk(final boolean infoOnly) throws IOException { | ||||
|         Mp4Track track = tracks[selectedTrack]; | ||||
|  | ||||
|         while (stream.available()) { | ||||
| @@ -240,27 +241,31 @@ public class Mp4DashReader { | ||||
|                         throw new IOException("moof found without mdat"); | ||||
|                     } | ||||
|  | ||||
|                     moof = parse_moof(box, track.trak.tkhd.trackId); | ||||
|                     moof = parseMoof(box, track.trak.tkhd.trackId); | ||||
|  | ||||
|                     if (moof.traf != null) { | ||||
|  | ||||
|                         if (hasFlag(moof.traf.trun.bFlags, 0x0001)) { | ||||
|                             moof.traf.trun.dataOffset -= box.size + 8; | ||||
|                             if (moof.traf.trun.dataOffset < 0) { | ||||
|                                 throw new IOException("trun box has wrong data offset, points outside of concurrent mdat box"); | ||||
|                                 throw new IOException("trun box has wrong data offset, " | ||||
|                                         + "points outside of concurrent mdat box"); | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                         if (moof.traf.trun.chunkSize < 1) { | ||||
|                             if (hasFlag(moof.traf.tfhd.bFlags, 0x10)) { | ||||
|                                 moof.traf.trun.chunkSize = moof.traf.tfhd.defaultSampleSize * moof.traf.trun.entryCount; | ||||
|                                 moof.traf.trun.chunkSize = moof.traf.tfhd.defaultSampleSize | ||||
|                                         * moof.traf.trun.entryCount; | ||||
|                             } else { | ||||
|                                 moof.traf.trun.chunkSize = (int) (box.size - 8); | ||||
|                             } | ||||
|                         } | ||||
|                         if (!hasFlag(moof.traf.trun.bFlags, 0x900) && moof.traf.trun.chunkDuration == 0) { | ||||
|                         if (!hasFlag(moof.traf.trun.bFlags, 0x900) | ||||
|                                 && moof.traf.trun.chunkDuration == 0) { | ||||
|                             if (hasFlag(moof.traf.tfhd.bFlags, 0x20)) { | ||||
|                                 moof.traf.trun.chunkDuration = moof.traf.tfhd.defaultSampleDuration * moof.traf.trun.entryCount; | ||||
|                                 moof.traf.trun.chunkDuration = moof.traf.tfhd.defaultSampleDuration | ||||
|                                         * moof.traf.trun.entryCount; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
| @@ -272,7 +277,7 @@ public class Mp4DashReader { | ||||
|  | ||||
|                     if (moof.traf == null) { | ||||
|                         moof = null; | ||||
|                         continue;// find another chunk | ||||
|                         continue; // find another chunk | ||||
|                     } | ||||
|  | ||||
|                     Mp4DashChunk chunk = new Mp4DashChunk(); | ||||
| @@ -292,17 +297,15 @@ public class Mp4DashReader { | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     public static boolean hasFlag(int flags, int mask) { | ||||
|     public static boolean hasFlag(final int flags, final int mask) { | ||||
|         return (flags & mask) == mask; | ||||
|     } | ||||
|  | ||||
|     private String boxName(Box ref) { | ||||
|     private String boxName(final Box ref) { | ||||
|         return boxName(ref.type); | ||||
|     } | ||||
|  | ||||
|     private String boxName(int type) { | ||||
|     private String boxName(final int type) { | ||||
|         try { | ||||
|             return new String(ByteBuffer.allocate(4).putInt(type).array(), "UTF-8"); | ||||
|         } catch (UnsupportedEncodingException e) { | ||||
| @@ -323,15 +326,16 @@ public class Mp4DashReader { | ||||
|         return b; | ||||
|     } | ||||
|  | ||||
|     private Box readBox(int expected) throws IOException { | ||||
|     private Box readBox(final int expected) throws IOException { | ||||
|         Box b = readBox(); | ||||
|         if (b.type != expected) { | ||||
|             throw new NoSuchElementException("expected " + boxName(expected) + " found " + boxName(b)); | ||||
|             throw new NoSuchElementException("expected " + boxName(expected) | ||||
|                     + " found " + boxName(b)); | ||||
|         } | ||||
|         return b; | ||||
|     } | ||||
|  | ||||
|     private byte[] readFullBox(Box ref) throws IOException { | ||||
|     private byte[] readFullBox(final Box ref) throws IOException { | ||||
|         // full box reading is limited to 2 GiB, and should be enough | ||||
|         int size = (int) ref.size; | ||||
|  | ||||
| @@ -342,15 +346,14 @@ public class Mp4DashReader { | ||||
|         int read = size - 8; | ||||
|  | ||||
|         if (stream.read(buffer.array(), 8, read) != read) { | ||||
|             throw new EOFException( | ||||
|                     String.format("EOF reached in box: type=%s offset=%s size=%s", boxName(ref.type), ref.offset, ref.size) | ||||
|             ); | ||||
|             throw new EOFException(String.format("EOF reached in box: type=%s offset=%s size=%s", | ||||
|                     boxName(ref.type), ref.offset, ref.size)); | ||||
|         } | ||||
|  | ||||
|         return buffer.array(); | ||||
|     } | ||||
|  | ||||
|     private void ensure(Box ref) throws IOException { | ||||
|     private void ensure(final Box ref) throws IOException { | ||||
|         long skip = ref.offset + ref.size - stream.position(); | ||||
|  | ||||
|         if (skip == 0) { | ||||
| @@ -365,7 +368,7 @@ public class Mp4DashReader { | ||||
|         stream.skipBytes((int) skip); | ||||
|     } | ||||
|  | ||||
|     private Box untilBox(Box ref, int... expected) throws IOException { | ||||
|     private Box untilBox(final Box ref, final int... expected) throws IOException { | ||||
|         Box b; | ||||
|         while (stream.position() < (ref.offset + ref.size)) { | ||||
|             b = readBox(); | ||||
| @@ -380,7 +383,7 @@ public class Mp4DashReader { | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     private Box untilAnyBox(Box ref) throws IOException { | ||||
|     private Box untilAnyBox(final Box ref) throws IOException { | ||||
|         if (stream.position() >= (ref.offset + ref.size)) { | ||||
|             return null; | ||||
|         } | ||||
| @@ -388,17 +391,15 @@ public class Mp4DashReader { | ||||
|         return readBox(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     private Moof parse_moof(Box ref, int trackId) throws IOException { | ||||
|     private Moof parseMoof(final Box ref, final int trackId) throws IOException { | ||||
|         Moof obj = new Moof(); | ||||
|  | ||||
|         Box b = readBox(ATOM_MFHD); | ||||
|         obj.mfhd_SequenceNumber = parse_mfhd(); | ||||
|         obj.mfhdSequenceNumber = parseMfhd(); | ||||
|         ensure(b); | ||||
|  | ||||
|         while ((b = untilBox(ref, ATOM_TRAF)) != null) { | ||||
|             obj.traf = parse_traf(b, trackId); | ||||
|             obj.traf = parseTraf(b, trackId); | ||||
|             ensure(b); | ||||
|  | ||||
|             if (obj.traf != null) { | ||||
| @@ -409,7 +410,7 @@ public class Mp4DashReader { | ||||
|         return obj; | ||||
|     } | ||||
|  | ||||
|     private int parse_mfhd() throws IOException { | ||||
|     private int parseMfhd() throws IOException { | ||||
|         // version | ||||
|         // flags | ||||
|         stream.skipBytes(4); | ||||
| @@ -417,11 +418,11 @@ public class Mp4DashReader { | ||||
|         return stream.readInt(); | ||||
|     } | ||||
|  | ||||
|     private Traf parse_traf(Box ref, int trackId) throws IOException { | ||||
|     private Traf parseTraf(final Box ref, final int trackId) throws IOException { | ||||
|         Traf traf = new Traf(); | ||||
|  | ||||
|         Box b = readBox(ATOM_TFHD); | ||||
|         traf.tfhd = parse_tfhd(trackId); | ||||
|         traf.tfhd = parseTfhd(trackId); | ||||
|         ensure(b); | ||||
|  | ||||
|         if (traf.tfhd == null) { | ||||
| @@ -431,18 +432,18 @@ public class Mp4DashReader { | ||||
|         b = untilBox(ref, ATOM_TRUN, ATOM_TFDT); | ||||
|  | ||||
|         if (b.type == ATOM_TFDT) { | ||||
|             traf.tfdt = parse_tfdt(); | ||||
|             traf.tfdt = parseTfdt(); | ||||
|             ensure(b); | ||||
|             b = readBox(ATOM_TRUN); | ||||
|         } | ||||
|  | ||||
|         traf.trun = parse_trun(); | ||||
|         traf.trun = parseTrun(); | ||||
|         ensure(b); | ||||
|  | ||||
|         return traf; | ||||
|     } | ||||
|  | ||||
|     private Tfhd parse_tfhd(int trackId) throws IOException { | ||||
|     private Tfhd parseTfhd(final int trackId) throws IOException { | ||||
|         Tfhd obj = new Tfhd(); | ||||
|  | ||||
|         obj.bFlags = stream.readInt(); | ||||
| @@ -471,31 +472,31 @@ public class Mp4DashReader { | ||||
|         return obj; | ||||
|     } | ||||
|  | ||||
|     private long parse_tfdt() throws IOException { | ||||
|     private long parseTfdt() throws IOException { | ||||
|         int version = stream.read(); | ||||
|         stream.skipBytes(3);// flags | ||||
|         stream.skipBytes(3); // flags | ||||
|         return version == 0 ? stream.readUnsignedInt() : stream.readLong(); | ||||
|     } | ||||
|  | ||||
|     private Trun parse_trun() throws IOException { | ||||
|     private Trun parseTrun() throws IOException { | ||||
|         Trun obj = new Trun(); | ||||
|         obj.bFlags = stream.readInt(); | ||||
|         obj.entryCount = stream.readInt();// unsigned int | ||||
|         obj.entryCount = stream.readInt(); // unsigned int | ||||
|  | ||||
|         obj.entries_rowSize = 0; | ||||
|         obj.entriesRowSize = 0; | ||||
|         if (hasFlag(obj.bFlags, 0x0100)) { | ||||
|             obj.entries_rowSize += 4; | ||||
|             obj.entriesRowSize += 4; | ||||
|         } | ||||
|         if (hasFlag(obj.bFlags, 0x0200)) { | ||||
|             obj.entries_rowSize += 4; | ||||
|             obj.entriesRowSize += 4; | ||||
|         } | ||||
|         if (hasFlag(obj.bFlags, 0x0400)) { | ||||
|             obj.entries_rowSize += 4; | ||||
|             obj.entriesRowSize += 4; | ||||
|         } | ||||
|         if (hasFlag(obj.bFlags, 0x0800)) { | ||||
|             obj.entries_rowSize += 4; | ||||
|             obj.entriesRowSize += 4; | ||||
|         } | ||||
|         obj.bEntries = new byte[obj.entries_rowSize * obj.entryCount]; | ||||
|         obj.bEntries = new byte[obj.entriesRowSize * obj.entryCount]; | ||||
|  | ||||
|         if (hasFlag(obj.bFlags, 0x0001)) { | ||||
|             obj.dataOffset = stream.readInt(); | ||||
| @@ -524,23 +525,24 @@ public class Mp4DashReader { | ||||
|         return obj; | ||||
|     } | ||||
|  | ||||
|     private int[] parse_ftyp(Box ref) throws IOException { | ||||
|     private int[] parseFtyp(final Box ref) throws IOException { | ||||
|         int i = 0; | ||||
|         int[] list = new int[(int) ((ref.offset + ref.size - stream.position() - 4) / 4)]; | ||||
|  | ||||
|         list[i++] = stream.readInt();// major brand | ||||
|         list[i++] = stream.readInt(); // major brand | ||||
|  | ||||
|         stream.skipBytes(4);// minor version | ||||
|         stream.skipBytes(4); // minor version | ||||
|  | ||||
|         for (; i < list.length; i++) | ||||
|             list[i] = stream.readInt();// compatible brands | ||||
|         for (; i < list.length; i++) { | ||||
|             list[i] = stream.readInt(); // compatible brands | ||||
|         } | ||||
|  | ||||
|         return list; | ||||
|     } | ||||
|  | ||||
|     private Mvhd parse_mvhd() throws IOException { | ||||
|     private Mvhd parseMvhd() throws IOException { | ||||
|         int version = stream.read(); | ||||
|         stream.skipBytes(3);// flags | ||||
|         stream.skipBytes(3); // flags | ||||
|  | ||||
|         // creation entries_time | ||||
|         // modification entries_time | ||||
| @@ -564,7 +566,7 @@ public class Mp4DashReader { | ||||
|         return obj; | ||||
|     } | ||||
|  | ||||
|     private Tkhd parse_tkhd() throws IOException { | ||||
|     private Tkhd parseTkhd() throws IOException { | ||||
|         int version = stream.read(); | ||||
|  | ||||
|         Tkhd obj = new Tkhd(); | ||||
| @@ -576,17 +578,17 @@ public class Mp4DashReader { | ||||
|  | ||||
|         obj.trackId = stream.readInt(); | ||||
|  | ||||
|         stream.skipBytes(4);// reserved | ||||
|         stream.skipBytes(4); // reserved | ||||
|  | ||||
|         obj.duration = version == 0 ? stream.readUnsignedInt() : stream.readLong(); | ||||
|  | ||||
|         stream.skipBytes(2 * 4);// reserved | ||||
|         stream.skipBytes(2 * 4); // reserved | ||||
|  | ||||
|         obj.bLayer = stream.readShort(); | ||||
|         obj.bAlternateGroup = stream.readShort(); | ||||
|         obj.bVolume = stream.readShort(); | ||||
|  | ||||
|         stream.skipBytes(2);// reserved | ||||
|         stream.skipBytes(2); // reserved | ||||
|  | ||||
|         obj.matrix = new byte[9 * 4]; | ||||
|         stream.read(obj.matrix); | ||||
| @@ -597,20 +599,20 @@ public class Mp4DashReader { | ||||
|         return obj; | ||||
|     } | ||||
|  | ||||
|     private Trak parse_trak(Box ref) throws IOException { | ||||
|     private Trak parseTrak(final Box ref) throws IOException { | ||||
|         Trak trak = new Trak(); | ||||
|  | ||||
|         Box b = readBox(ATOM_TKHD); | ||||
|         trak.tkhd = parse_tkhd(); | ||||
|         trak.tkhd = parseTkhd(); | ||||
|         ensure(b); | ||||
|  | ||||
|         while ((b = untilBox(ref, ATOM_MDIA, ATOM_EDTS)) != null) { | ||||
|             switch (b.type) { | ||||
|                 case ATOM_MDIA: | ||||
|                     trak.mdia = parse_mdia(b); | ||||
|                     trak.mdia = parseMdia(b); | ||||
|                     break; | ||||
|                 case ATOM_EDTS: | ||||
|                     trak.edst_elst = parse_edts(b); | ||||
|                     trak.edstElst = parseEdts(b); | ||||
|                     break; | ||||
|             } | ||||
|  | ||||
| @@ -620,7 +622,7 @@ public class Mp4DashReader { | ||||
|         return trak; | ||||
|     } | ||||
|  | ||||
|     private Mdia parse_mdia(Box ref) throws IOException { | ||||
|     private Mdia parseMdia(final Box ref) throws IOException { | ||||
|         Mdia obj = new Mdia(); | ||||
|  | ||||
|         Box b; | ||||
| @@ -633,13 +635,13 @@ public class Mp4DashReader { | ||||
|                     ByteBuffer buffer = ByteBuffer.wrap(obj.mdhd); | ||||
|                     byte version = buffer.get(8); | ||||
|                     buffer.position(12 + ((version == 0 ? 4 : 8) * 2)); | ||||
|                     obj.mdhd_timeScale = buffer.getInt(); | ||||
|                     obj.mdhdTimeScale = buffer.getInt(); | ||||
|                     break; | ||||
|                 case ATOM_HDLR: | ||||
|                     obj.hdlr = parse_hdlr(b); | ||||
|                     obj.hdlr = parseHdlr(b); | ||||
|                     break; | ||||
|                 case ATOM_MINF: | ||||
|                     obj.minf = parse_minf(b); | ||||
|                     obj.minf = parseMinf(b); | ||||
|                     break; | ||||
|             } | ||||
|             ensure(b); | ||||
| @@ -648,7 +650,7 @@ public class Mp4DashReader { | ||||
|         return obj; | ||||
|     } | ||||
|  | ||||
|     private Hdlr parse_hdlr(Box ref) throws IOException { | ||||
|     private Hdlr parseHdlr(final Box ref) throws IOException { | ||||
|         // version | ||||
|         // flags | ||||
|         stream.skipBytes(4); | ||||
| @@ -666,10 +668,10 @@ public class Mp4DashReader { | ||||
|         return obj; | ||||
|     } | ||||
|  | ||||
|     private Moov parse_moov(Box ref) throws IOException { | ||||
|     private Moov parseMoov(final Box ref) throws IOException { | ||||
|         Box b = readBox(ATOM_MVHD); | ||||
|         Moov moov = new Moov(); | ||||
|         moov.mvhd = parse_mvhd(); | ||||
|         moov.mvhd = parseMvhd(); | ||||
|         ensure(b); | ||||
|  | ||||
|         ArrayList<Trak> tmp = new ArrayList<>((int) moov.mvhd.nextTrackId); | ||||
| @@ -677,10 +679,10 @@ public class Mp4DashReader { | ||||
|  | ||||
|             switch (b.type) { | ||||
|                 case ATOM_TRAK: | ||||
|                     tmp.add(parse_trak(b)); | ||||
|                     tmp.add(parseTrak(b)); | ||||
|                     break; | ||||
|                 case ATOM_MVEX: | ||||
|                     moov.mvex_trex = parse_mvex(b, (int) moov.mvhd.nextTrackId); | ||||
|                     moov.mvexTrex = parseMvex(b, (int) moov.mvhd.nextTrackId); | ||||
|                     break; | ||||
|             } | ||||
|  | ||||
| @@ -692,19 +694,19 @@ public class Mp4DashReader { | ||||
|         return moov; | ||||
|     } | ||||
|  | ||||
|     private Trex[] parse_mvex(Box ref, int possibleTrackCount) throws IOException { | ||||
|     private Trex[] parseMvex(final Box ref, final int possibleTrackCount) throws IOException { | ||||
|         ArrayList<Trex> tmp = new ArrayList<>(possibleTrackCount); | ||||
|  | ||||
|         Box b; | ||||
|         while ((b = untilBox(ref, ATOM_TREX)) != null) { | ||||
|             tmp.add(parse_trex()); | ||||
|             tmp.add(parseTrex()); | ||||
|             ensure(b); | ||||
|         } | ||||
|  | ||||
|         return tmp.toArray(new Trex[0]); | ||||
|     } | ||||
|  | ||||
|     private Trex parse_trex() throws IOException { | ||||
|     private Trex parseTrex() throws IOException { | ||||
|         // version | ||||
|         // flags | ||||
|         stream.skipBytes(4); | ||||
| @@ -719,7 +721,7 @@ public class Mp4DashReader { | ||||
|         return obj; | ||||
|     } | ||||
|  | ||||
|     private Elst parse_edts(Box ref) throws IOException { | ||||
|     private Elst parseEdts(final Box ref) throws IOException { | ||||
|         Box b = untilBox(ref, ATOM_ELST); | ||||
|         if (b == null) { | ||||
|             return null; | ||||
| @@ -728,22 +730,22 @@ public class Mp4DashReader { | ||||
|         Elst obj = new Elst(); | ||||
|  | ||||
|         boolean v1 = stream.read() == 1; | ||||
|         stream.skipBytes(3);// flags | ||||
|         stream.skipBytes(3); // flags | ||||
|  | ||||
|         int entryCount = stream.readInt(); | ||||
|         if (entryCount < 1) { | ||||
|             obj.bMediaRate = 0x00010000;// default media rate (1.0) | ||||
|             obj.bMediaRate = 0x00010000; // default media rate (1.0) | ||||
|             return obj; | ||||
|         } | ||||
|  | ||||
|         if (v1) { | ||||
|             stream.skipBytes(DataReader.LONG_SIZE);// segment duration | ||||
|             obj.MediaTime = stream.readLong(); | ||||
|             stream.skipBytes(DataReader.LONG_SIZE); // segment duration | ||||
|             obj.mediaTime = stream.readLong(); | ||||
|             // ignore all remain entries | ||||
|             stream.skipBytes((entryCount - 1) * (DataReader.LONG_SIZE * 2)); | ||||
|         } else { | ||||
|             stream.skipBytes(DataReader.INTEGER_SIZE);// segment duration | ||||
|             obj.MediaTime = stream.readInt(); | ||||
|             stream.skipBytes(DataReader.INTEGER_SIZE); // segment duration | ||||
|             obj.mediaTime = stream.readInt(); | ||||
|         } | ||||
|  | ||||
|         obj.bMediaRate = stream.readInt(); | ||||
| @@ -751,7 +753,7 @@ public class Mp4DashReader { | ||||
|         return obj; | ||||
|     } | ||||
|  | ||||
|     private Minf parse_minf(Box ref) throws IOException { | ||||
|     private Minf parseMinf(final Box ref) throws IOException { | ||||
|         Minf obj = new Minf(); | ||||
|  | ||||
|         Box b; | ||||
| @@ -762,11 +764,11 @@ public class Mp4DashReader { | ||||
|                     obj.dinf = readFullBox(b); | ||||
|                     break; | ||||
|                 case ATOM_STBL: | ||||
|                     obj.stbl_stsd = parse_stbl(b); | ||||
|                     obj.stblStsd = parseStbl(b); | ||||
|                     break; | ||||
|                 case ATOM_VMHD: | ||||
|                 case ATOM_SMHD: | ||||
|                     obj.$mhd = readFullBox(b); | ||||
|                     obj.mhd = readFullBox(b); | ||||
|                     break; | ||||
|  | ||||
|             } | ||||
| @@ -777,42 +779,39 @@ public class Mp4DashReader { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * this only read the "stsd" box inside | ||||
|      * This only reads the "stsd" box inside. | ||||
|      * | ||||
|      * @param ref stbl box | ||||
|      * @return stsd box inside | ||||
|      */ | ||||
|     private byte[] parse_stbl(Box ref) throws IOException { | ||||
|     private byte[] parseStbl(final Box ref) throws IOException { | ||||
|         Box b = untilBox(ref, ATOM_STSD); | ||||
|  | ||||
|         if (b == null) { | ||||
|             return new byte[0];// this never should happens (missing codec startup data) | ||||
|             return new byte[0]; // this never should happens (missing codec startup data) | ||||
|         } | ||||
|  | ||||
|         return readFullBox(b); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     class Box { | ||||
|  | ||||
|         int type; | ||||
|         long offset; | ||||
|         long size; | ||||
|     } | ||||
|  | ||||
|     public class Moof { | ||||
|  | ||||
|         int mfhd_SequenceNumber; | ||||
|         int mfhdSequenceNumber; | ||||
|         public Traf traf; | ||||
|     } | ||||
|  | ||||
|     public class Traf { | ||||
|  | ||||
|         public Tfhd tfhd; | ||||
|         long tfdt; | ||||
|         public Trun trun; | ||||
|     } | ||||
|  | ||||
|     public class Tfhd { | ||||
|  | ||||
|         int bFlags; | ||||
|         public int trackId; | ||||
|         int defaultSampleDuration; | ||||
| @@ -821,7 +820,6 @@ public class Mp4DashReader { | ||||
|     } | ||||
|  | ||||
|     class TrunEntry { | ||||
|  | ||||
|         int sampleDuration; | ||||
|         int sampleSize; | ||||
|         int sampleFlags; | ||||
| @@ -833,7 +831,6 @@ public class Mp4DashReader { | ||||
|     } | ||||
|  | ||||
|     public class Trun { | ||||
|  | ||||
|         public int chunkDuration; | ||||
|         public int chunkSize; | ||||
|  | ||||
| @@ -843,10 +840,10 @@ public class Mp4DashReader { | ||||
|  | ||||
|         public int entryCount; | ||||
|         byte[] bEntries; | ||||
|         int entries_rowSize; | ||||
|         int entriesRowSize; | ||||
|  | ||||
|         public TrunEntry getEntry(int i) { | ||||
|             ByteBuffer buffer = ByteBuffer.wrap(bEntries, i * entries_rowSize, entries_rowSize); | ||||
|         public TrunEntry getEntry(final int i) { | ||||
|             ByteBuffer buffer = ByteBuffer.wrap(bEntries, i * entriesRowSize, entriesRowSize); | ||||
|             TrunEntry entry = new TrunEntry(); | ||||
|  | ||||
|             if (hasFlag(bFlags, 0x0100)) { | ||||
| @@ -868,7 +865,7 @@ public class Mp4DashReader { | ||||
|             return entry; | ||||
|         } | ||||
|  | ||||
|         public TrunEntry getAbsoluteEntry(int i, Tfhd header) { | ||||
|         public TrunEntry getAbsoluteEntry(final int i, final Tfhd header) { | ||||
|             TrunEntry entry = getEntry(i); | ||||
|  | ||||
|             if (!hasFlag(bFlags, 0x0100) && hasFlag(header.bFlags, 0x20)) { | ||||
| @@ -892,7 +889,6 @@ public class Mp4DashReader { | ||||
|     } | ||||
|  | ||||
|     public class Tkhd { | ||||
|  | ||||
|         int trackId; | ||||
|         long duration; | ||||
|         short bVolume; | ||||
| @@ -904,28 +900,24 @@ public class Mp4DashReader { | ||||
|     } | ||||
|  | ||||
|     public class Trak { | ||||
|  | ||||
|         public Tkhd tkhd; | ||||
|         public Elst edst_elst; | ||||
|         public Elst edstElst; | ||||
|         public Mdia mdia; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     class Mvhd { | ||||
|  | ||||
|         long timeScale; | ||||
|         long nextTrackId; | ||||
|     } | ||||
|  | ||||
|     class Moov { | ||||
|  | ||||
|         Mvhd mvhd; | ||||
|         Trak[] trak; | ||||
|         Trex[] mvex_trex; | ||||
|         Trex[] mvexTrex; | ||||
|     } | ||||
|  | ||||
|     public class Trex { | ||||
|  | ||||
|         private int trackId; | ||||
|         int defaultSampleDescriptionIndex; | ||||
|         int defaultSampleDuration; | ||||
| @@ -934,42 +926,36 @@ public class Mp4DashReader { | ||||
|     } | ||||
|  | ||||
|     public class Elst { | ||||
|  | ||||
|         public long MediaTime; | ||||
|         public long mediaTime; | ||||
|         public int bMediaRate; | ||||
|     } | ||||
|  | ||||
|     public class Mdia { | ||||
|  | ||||
|         public int mdhd_timeScale; | ||||
|         public int mdhdTimeScale; | ||||
|         public byte[] mdhd; | ||||
|         public Hdlr hdlr; | ||||
|         public Minf minf; | ||||
|     } | ||||
|  | ||||
|     public class Hdlr { | ||||
|  | ||||
|         public int type; | ||||
|         public int subType; | ||||
|         public byte[] bReserved; | ||||
|     } | ||||
|  | ||||
|     public class Minf { | ||||
|  | ||||
|         public byte[] dinf; | ||||
|         public byte[] stbl_stsd; | ||||
|         public byte[] $mhd; | ||||
|         public byte[] stblStsd; | ||||
|         public byte[] mhd; | ||||
|     } | ||||
|  | ||||
|     public class Mp4Track { | ||||
|  | ||||
|         public TrackKind kind; | ||||
|         public Trak trak; | ||||
|         public Trex trex; | ||||
|     } | ||||
|  | ||||
|     public class Mp4DashChunk { | ||||
|  | ||||
|         public InputStream data; | ||||
|         public Moof moof; | ||||
|         private int i = 0; | ||||
| @@ -1002,9 +988,7 @@ public class Mp4DashReader { | ||||
|     } | ||||
|  | ||||
|     public class Mp4DashSample { | ||||
|  | ||||
|         public TrunEntry info; | ||||
|         public byte[] data; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -17,13 +17,15 @@ import java.util.ArrayList; | ||||
|  * @author kapodamy | ||||
|  */ | ||||
| public class Mp4FromDashWriter { | ||||
|  | ||||
|     private final static int EPOCH_OFFSET = 2082844800; | ||||
|     private final static short DEFAULT_TIMESCALE = 1000; | ||||
|     private final static byte SAMPLES_PER_CHUNK_INIT = 2; | ||||
|     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 int THRESHOLD_MOOV_LENGTH = (256 * 1024) + (2048 * 1024); // 2.2 MiB enough for: 1080p 60fps 00h35m00s | ||||
|     private static final int EPOCH_OFFSET = 2082844800; | ||||
|     private static final short DEFAULT_TIMESCALE = 1000; | ||||
|     private static final byte SAMPLES_PER_CHUNK_INIT = 2; | ||||
|     // ffmpeg uses 2, basic uses 1 (with 60fps uses 21 or 22). NewPipe will use 6 | ||||
|     private static final byte SAMPLES_PER_CHUNK = 6; | ||||
|     // near 3.999 GiB | ||||
|     private static final long THRESHOLD_FOR_CO64 = 0xFFFEFFFFL; | ||||
|     // 2.2 MiB enough for: 1080p 60fps 00h35m00s | ||||
|     private static final int THRESHOLD_MOOV_LENGTH = (256 * 1024) + (2048 * 1024); | ||||
|  | ||||
|     private final long time; | ||||
|  | ||||
| @@ -48,7 +50,7 @@ public class Mp4FromDashWriter { | ||||
|  | ||||
|     private final ArrayList<Integer> compatibleBrands = new ArrayList<>(5); | ||||
|  | ||||
|     public Mp4FromDashWriter(SharpStream... sources) throws IOException { | ||||
|     public Mp4FromDashWriter(final SharpStream... sources) throws IOException { | ||||
|         for (SharpStream src : sources) { | ||||
|             if (!src.canRewind() && !src.canRead()) { | ||||
|                 throw new IOException("All sources must be readable and allow rewind"); | ||||
| @@ -60,12 +62,12 @@ public class Mp4FromDashWriter { | ||||
|         readersChunks = new Mp4DashChunk[readers.length]; | ||||
|         time = (System.currentTimeMillis() / 1000L) + EPOCH_OFFSET; | ||||
|  | ||||
|         compatibleBrands.add(0x6D703431);// mp41 | ||||
|         compatibleBrands.add(0x69736F6D);// isom | ||||
|         compatibleBrands.add(0x69736F32);// iso2 | ||||
|         compatibleBrands.add(0x6D703431); // mp41 | ||||
|         compatibleBrands.add(0x69736F6D); // isom | ||||
|         compatibleBrands.add(0x69736F32); // iso2 | ||||
|     } | ||||
|  | ||||
|     public Mp4Track[] getTracksFromSource(int sourceIndex) throws IllegalStateException { | ||||
|     public Mp4Track[] getTracksFromSource(final int sourceIndex) throws IllegalStateException { | ||||
|         if (!parsed) { | ||||
|             throw new IllegalStateException("All sources must be parsed first"); | ||||
|         } | ||||
| @@ -92,7 +94,7 @@ public class Mp4FromDashWriter { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void selectTracks(int... trackIndex) throws IOException { | ||||
|     public void selectTracks(final int... trackIndex) throws IOException { | ||||
|         if (done) { | ||||
|             throw new IOException("already done"); | ||||
|         } | ||||
| @@ -110,7 +112,7 @@ public class Mp4FromDashWriter { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void setMainBrand(int brand) { | ||||
|     public void setMainBrand(final int brand) { | ||||
|         overrideMainBrand = brand; | ||||
|     } | ||||
|  | ||||
| @@ -140,7 +142,7 @@ public class Mp4FromDashWriter { | ||||
|         outStream = null; | ||||
|     } | ||||
|  | ||||
|     public void build(SharpStream output) throws IOException { | ||||
|     public void build(final SharpStream output) throws IOException { | ||||
|         if (done) { | ||||
|             throw new RuntimeException("already done"); | ||||
|         } | ||||
| @@ -153,7 +155,7 @@ public class Mp4FromDashWriter { | ||||
|         //          not allowed for very short tracks (less than 0.5 seconds) | ||||
|         // | ||||
|         outStream = output; | ||||
|         long read = 8;// mdat box header size | ||||
|         long read = 8; // mdat box header size | ||||
|         long totalSampleSize = 0; | ||||
|         int[] sampleExtra = new int[readers.length]; | ||||
|         int[] defaultMediaTime = new int[readers.length]; | ||||
| @@ -165,12 +167,12 @@ public class Mp4FromDashWriter { | ||||
|             tablesInfo[i] = new TablesInfo(); | ||||
|         } | ||||
|  | ||||
|         int single_sample_buffer; | ||||
|         int singleSampleBuffer; | ||||
|         if (tracks.length == 1 && tracks[0].kind == TrackKind.Audio) { | ||||
|             // near 1 second of audio data per chunk, avoid split the audio stream in large chunks | ||||
|             single_sample_buffer = tracks[0].trak.mdia.mdhd_timeScale / 1000; | ||||
|             singleSampleBuffer = tracks[0].trak.mdia.mdhdTimeScale / 1000; | ||||
|         } else { | ||||
|             single_sample_buffer = -1; | ||||
|             singleSampleBuffer = -1; | ||||
|         } | ||||
|  | ||||
|  | ||||
| @@ -187,7 +189,7 @@ public class Mp4FromDashWriter { | ||||
|                 } | ||||
|  | ||||
|                 read += chunk.moof.traf.trun.chunkSize; | ||||
|                 sampleExtra[i] += chunk.moof.traf.trun.chunkDuration;// calculate track duration | ||||
|                 sampleExtra[i] += chunk.moof.traf.trun.chunkDuration; // calculate track duration | ||||
|  | ||||
|                 TrunEntry info; | ||||
|                 while ((info = chunk.getNextSampleInfo()) != null) { | ||||
| @@ -222,8 +224,8 @@ public class Mp4FromDashWriter { | ||||
|  | ||||
|             readers[i].rewind(); | ||||
|  | ||||
|             if (single_sample_buffer > 0) { | ||||
|                 initChunkTables(tablesInfo[i], single_sample_buffer, single_sample_buffer); | ||||
|             if (singleSampleBuffer > 0) { | ||||
|                 initChunkTables(tablesInfo[i], singleSampleBuffer, singleSampleBuffer); | ||||
|             } else { | ||||
|                 initChunkTables(tablesInfo[i], SAMPLES_PER_CHUNK_INIT, SAMPLES_PER_CHUNK); | ||||
|             } | ||||
| @@ -232,18 +234,18 @@ public class Mp4FromDashWriter { | ||||
|  | ||||
|             if (sampleSizeChanges == 1) { | ||||
|                 tablesInfo[i].stsz = 0; | ||||
|                 tablesInfo[i].stsz_default = samplesSize; | ||||
|                 tablesInfo[i].stszDefault = samplesSize; | ||||
|             } else { | ||||
|                 tablesInfo[i].stsz_default = 0; | ||||
|                 tablesInfo[i].stszDefault = 0; | ||||
|             } | ||||
|  | ||||
|             if (tablesInfo[i].stss == tablesInfo[i].stsz) { | ||||
|                 tablesInfo[i].stss = -1;// for audio tracks (all samples are keyframes) | ||||
|                 tablesInfo[i].stss = -1; // for audio tracks (all samples are keyframes) | ||||
|             } | ||||
|  | ||||
|             // ensure track duration | ||||
|             if (tracks[i].trak.tkhd.duration < 1) { | ||||
|                 tracks[i].trak.tkhd.duration = sampleExtra[i];// this never should happen | ||||
|                 tracks[i].trak.tkhd.duration = sampleExtra[i]; // this never should happen | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -251,21 +253,21 @@ public class Mp4FromDashWriter { | ||||
|         boolean is64 = read > THRESHOLD_FOR_CO64; | ||||
|  | ||||
|         // calculate the moov size | ||||
|         int auxSize = make_moov(defaultMediaTime, tablesInfo, is64); | ||||
|         int auxSize = makeMoov(defaultMediaTime, tablesInfo, is64); | ||||
|  | ||||
|         if (auxSize < THRESHOLD_MOOV_LENGTH) { | ||||
|             auxBuffer = ByteBuffer.allocate(auxSize);// cache moov in the memory | ||||
|             auxBuffer = ByteBuffer.allocate(auxSize); // cache moov in the memory | ||||
|         } | ||||
|  | ||||
|         moovSimulation = false; | ||||
|         writeOffset = 0; | ||||
|  | ||||
|         final int ftyp_size = make_ftyp(); | ||||
|         final int ftypSize = makeFtyp(); | ||||
|  | ||||
|         // reserve moov space in the output stream | ||||
|         if (auxSize > 0) { | ||||
|             int length = auxSize; | ||||
|             byte[] buffer = new byte[64 * 1024];// 64 KiB | ||||
|             byte[] buffer = new byte[64 * 1024]; // 64 KiB | ||||
|             while (length > 0) { | ||||
|                 int count = Math.min(length, buffer.length); | ||||
|                 outWrite(buffer, count); | ||||
| @@ -274,21 +276,22 @@ public class Mp4FromDashWriter { | ||||
|         } | ||||
|  | ||||
|         if (auxBuffer == null) { | ||||
|             outSeek(ftyp_size); | ||||
|             outSeek(ftypSize); | ||||
|         } | ||||
|  | ||||
|         // tablesInfo contains row counts | ||||
|         // and after returning from make_moov() will contain those table offsets | ||||
|         make_moov(defaultMediaTime, tablesInfo, is64); | ||||
|         // and after returning from makeMoov() will contain those table offsets | ||||
|         makeMoov(defaultMediaTime, tablesInfo, is64); | ||||
|  | ||||
|         // write tables: stts stsc sbgp | ||||
|         // reset for ctts table: sampleCount sampleExtra | ||||
|         for (int i = 0; i < readers.length; i++) { | ||||
|             writeEntryArray(tablesInfo[i].stts, 2, sampleCount[i], defaultSampleDuration[i]); | ||||
|             writeEntryArray(tablesInfo[i].stsc, tablesInfo[i].stsc_bEntries.length, tablesInfo[i].stsc_bEntries); | ||||
|             tablesInfo[i].stsc_bEntries = null; | ||||
|             writeEntryArray(tablesInfo[i].stsc, tablesInfo[i].stscBEntries.length, | ||||
|                     tablesInfo[i].stscBEntries); | ||||
|             tablesInfo[i].stscBEntries = null; | ||||
|             if (tablesInfo[i].ctts > 0) { | ||||
|                 sampleCount[i] = 1;// the index is not base zero | ||||
|                 sampleCount[i] = 1; // the index is not base zero | ||||
|                 sampleExtra[i] = -1; | ||||
|             } | ||||
|             if (tablesInfo[i].sbgp > 0) { | ||||
| @@ -300,11 +303,11 @@ public class Mp4FromDashWriter { | ||||
|             outRestore(); | ||||
|         } | ||||
|  | ||||
|         outWrite(make_mdat(totalSampleSize, is64)); | ||||
|         outWrite(makeMdat(totalSampleSize, is64)); | ||||
|  | ||||
|         int[] sampleIndex = new int[readers.length]; | ||||
|         int[] sizes = new int[single_sample_buffer > 0 ? single_sample_buffer : SAMPLES_PER_CHUNK]; | ||||
|         int[] sync = new int[single_sample_buffer > 0 ? single_sample_buffer : SAMPLES_PER_CHUNK]; | ||||
|         int[] sizes = new int[singleSampleBuffer > 0 ? singleSampleBuffer : SAMPLES_PER_CHUNK]; | ||||
|         int[] sync = new int[singleSampleBuffer > 0 ? singleSampleBuffer : SAMPLES_PER_CHUNK]; | ||||
|  | ||||
|         int written = readers.length; | ||||
|         while (written > 0) { | ||||
| @@ -312,14 +315,14 @@ public class Mp4FromDashWriter { | ||||
|  | ||||
|             for (int i = 0; i < readers.length; i++) { | ||||
|                 if (sampleIndex[i] < 0) { | ||||
|                     continue;// track is done | ||||
|                     continue; // track is done | ||||
|                 } | ||||
|  | ||||
|                 long chunkOffset = writeOffset; | ||||
|                 int syncCount = 0; | ||||
|                 int limit; | ||||
|                 if (single_sample_buffer > 0) { | ||||
|                     limit = single_sample_buffer; | ||||
|                 if (singleSampleBuffer > 0) { | ||||
|                     limit = singleSampleBuffer; | ||||
|                 } else { | ||||
|                     limit = sampleIndex[i] == 0 ? SAMPLES_PER_CHUNK_INIT : SAMPLES_PER_CHUNK; | ||||
|                 } | ||||
| @@ -330,7 +333,8 @@ public class Mp4FromDashWriter { | ||||
|  | ||||
|                     if (sample == null) { | ||||
|                         if (tablesInfo[i].ctts > 0 && sampleExtra[i] >= 0) { | ||||
|                             writeEntryArray(tablesInfo[i].ctts, 1, sampleCount[i], sampleExtra[i]);// flush last entries | ||||
|                             writeEntryArray(tablesInfo[i].ctts, 1, sampleCount[i], | ||||
|                                     sampleExtra[i]); // flush last entries | ||||
|                             outRestore(); | ||||
|                         } | ||||
|                         sampleIndex[i] = -1; | ||||
| @@ -344,7 +348,8 @@ public class Mp4FromDashWriter { | ||||
|                             sampleCount[i]++; | ||||
|                         } else { | ||||
|                             if (sampleExtra[i] >= 0) { | ||||
|                                 tablesInfo[i].ctts = writeEntryArray(tablesInfo[i].ctts, 2, sampleCount[i], sampleExtra[i]); | ||||
|                                 tablesInfo[i].ctts = writeEntryArray(tablesInfo[i].ctts, 2, | ||||
|                                         sampleCount[i], sampleExtra[i]); | ||||
|                                 outRestore(); | ||||
|                             } | ||||
|                             sampleCount[i] = 1; | ||||
| @@ -378,7 +383,8 @@ public class Mp4FromDashWriter { | ||||
|                         if (is64) { | ||||
|                             tablesInfo[i].stco = writeEntry64(tablesInfo[i].stco, chunkOffset); | ||||
|                         } else { | ||||
|                             tablesInfo[i].stco = writeEntryArray(tablesInfo[i].stco, 1, (int) chunkOffset); | ||||
|                             tablesInfo[i].stco = writeEntryArray(tablesInfo[i].stco, 1, | ||||
|                                     (int) chunkOffset); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
| @@ -389,17 +395,17 @@ public class Mp4FromDashWriter { | ||||
|  | ||||
|         if (auxBuffer != null) { | ||||
|             // dump moov | ||||
|             outSeek(ftyp_size); | ||||
|             outSeek(ftypSize); | ||||
|             outStream.write(auxBuffer.array(), 0, auxBuffer.capacity()); | ||||
|             auxBuffer = null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private Mp4DashSample getNextSample(int track) throws IOException { | ||||
|     private Mp4DashSample getNextSample(final int track) throws IOException { | ||||
|         if (readersChunks[track] == null) { | ||||
|             readersChunks[track] = readers[track].getNextChunk(false); | ||||
|             if (readersChunks[track] == null) { | ||||
|                 return null;// EOF reached | ||||
|                 return null; // EOF reached | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -413,7 +419,7 @@ public class Mp4FromDashWriter { | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private int writeEntry64(int offset, long value) throws IOException { | ||||
|     private int writeEntry64(final int offset, final long value) throws IOException { | ||||
|         outBackup(); | ||||
|  | ||||
|         auxSeek(offset); | ||||
| @@ -422,7 +428,8 @@ public class Mp4FromDashWriter { | ||||
|         return offset + 8; | ||||
|     } | ||||
|  | ||||
|     private int writeEntryArray(int offset, int count, int... values) throws IOException { | ||||
|     private int writeEntryArray(final int offset, final int count, final int... values) | ||||
|             throws IOException { | ||||
|         outBackup(); | ||||
|  | ||||
|         auxSeek(offset); | ||||
| @@ -456,7 +463,8 @@ public class Mp4FromDashWriter { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void initChunkTables(TablesInfo tables, int firstCount, int succesiveCount) { | ||||
|     private void initChunkTables(final TablesInfo tables, final int firstCount, | ||||
|                                  final int succesiveCount) { | ||||
|         // tables.stsz holds amount of samples of the track (total) | ||||
|         int totalSamples = (tables.stsz - firstCount); | ||||
|         float chunkAmount = totalSamples / (float) succesiveCount; | ||||
| @@ -473,36 +481,36 @@ public class Mp4FromDashWriter { | ||||
|         } | ||||
|  | ||||
|         // stsc_table_entry = [first_chunk, samples_per_chunk, sample_description_index] | ||||
|         tables.stsc_bEntries = new int[tables.stsc * 3]; | ||||
|         tables.stco = remainChunkOffset + 1;// total entrys in chunk offset box | ||||
|         tables.stscBEntries = new int[tables.stsc * 3]; | ||||
|         tables.stco = remainChunkOffset + 1; // total entrys in chunk offset box | ||||
|  | ||||
|         tables.stsc_bEntries[index++] = 1; | ||||
|         tables.stsc_bEntries[index++] = firstCount; | ||||
|         tables.stsc_bEntries[index++] = 1; | ||||
|         tables.stscBEntries[index++] = 1; | ||||
|         tables.stscBEntries[index++] = firstCount; | ||||
|         tables.stscBEntries[index++] = 1; | ||||
|  | ||||
|         if (firstCount != succesiveCount) { | ||||
|             tables.stsc_bEntries[index++] = 2; | ||||
|             tables.stsc_bEntries[index++] = succesiveCount; | ||||
|             tables.stsc_bEntries[index++] = 1; | ||||
|             tables.stscBEntries[index++] = 2; | ||||
|             tables.stscBEntries[index++] = succesiveCount; | ||||
|             tables.stscBEntries[index++] = 1; | ||||
|         } | ||||
|  | ||||
|         if (remain) { | ||||
|             tables.stsc_bEntries[index++] = remainChunkOffset + 1; | ||||
|             tables.stsc_bEntries[index++] = totalSamples % succesiveCount; | ||||
|             tables.stsc_bEntries[index] = 1; | ||||
|             tables.stscBEntries[index++] = remainChunkOffset + 1; | ||||
|             tables.stscBEntries[index++] = totalSamples % succesiveCount; | ||||
|             tables.stscBEntries[index] = 1; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void outWrite(byte[] buffer) throws IOException { | ||||
|     private void outWrite(final byte[] buffer) throws IOException { | ||||
|         outWrite(buffer, buffer.length); | ||||
|     } | ||||
|  | ||||
|     private void outWrite(byte[] buffer, int count) throws IOException { | ||||
|     private void outWrite(final byte[] buffer, final int count) throws IOException { | ||||
|         writeOffset += count; | ||||
|         outStream.write(buffer, 0, count); | ||||
|     } | ||||
|  | ||||
|     private void outSeek(long offset) throws IOException { | ||||
|     private void outSeek(final long offset) throws IOException { | ||||
|         if (outStream.canSeek()) { | ||||
|             outStream.seek(offset); | ||||
|             writeOffset = offset; | ||||
| @@ -515,12 +523,12 @@ public class Mp4FromDashWriter { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void outSkip(long amount) throws IOException { | ||||
|     private void outSkip(final long amount) throws IOException { | ||||
|         outStream.skip(amount); | ||||
|         writeOffset += amount; | ||||
|     } | ||||
|  | ||||
|     private int lengthFor(int offset) throws IOException { | ||||
|     private int lengthFor(final int offset) throws IOException { | ||||
|         int size = auxOffset() - offset; | ||||
|  | ||||
|         if (moovSimulation) { | ||||
| @@ -534,7 +542,8 @@ public class Mp4FromDashWriter { | ||||
|         return size; | ||||
|     } | ||||
|  | ||||
|     private int make(int type, int extra, int columns, int rows) throws IOException { | ||||
|     private int make(final int type, final int extra, final int columns, final int rows) | ||||
|             throws IOException { | ||||
|         final byte base = 16; | ||||
|         int size = columns * rows * 4; | ||||
|         int total = size + base; | ||||
| @@ -562,14 +571,14 @@ public class Mp4FromDashWriter { | ||||
|         return offset + base; | ||||
|     } | ||||
|  | ||||
|     private void auxWrite(int value) throws IOException { | ||||
|     private void auxWrite(final int value) throws IOException { | ||||
|         auxWrite(ByteBuffer.allocate(4) | ||||
|                 .putInt(value) | ||||
|                 .array() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     private void auxWrite(byte[] buffer) throws IOException { | ||||
|     private void auxWrite(final byte[] buffer) throws IOException { | ||||
|         if (moovSimulation) { | ||||
|             writeOffset += buffer.length; | ||||
|         } else if (auxBuffer == null) { | ||||
| @@ -579,7 +588,7 @@ public class Mp4FromDashWriter { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void auxSeek(int offset) throws IOException { | ||||
|     private void auxSeek(final int offset) throws IOException { | ||||
|         if (moovSimulation) { | ||||
|             writeOffset = offset; | ||||
|         } else if (auxBuffer == null) { | ||||
| @@ -589,7 +598,7 @@ public class Mp4FromDashWriter { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void auxSkip(int amount) throws IOException { | ||||
|     private void auxSkip(final int amount) throws IOException { | ||||
|         if (moovSimulation) { | ||||
|             writeOffset += amount; | ||||
|         } else if (auxBuffer == null) { | ||||
| @@ -603,27 +612,27 @@ public class Mp4FromDashWriter { | ||||
|         return auxBuffer == null ? (int) writeOffset : auxBuffer.position(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     private int make_ftyp() throws IOException { | ||||
|     private int makeFtyp() throws IOException { | ||||
|         int size = 16 + (compatibleBrands.size() * 4); | ||||
|         if (overrideMainBrand != 0) size += 4; | ||||
|         if (overrideMainBrand != 0) { | ||||
|             size += 4; | ||||
|         } | ||||
|  | ||||
|         ByteBuffer buffer = ByteBuffer.allocate(size); | ||||
|         buffer.putInt(size); | ||||
|         buffer.putInt(0x66747970);// "ftyp" | ||||
|         buffer.putInt(0x66747970); // "ftyp" | ||||
|  | ||||
|         if (overrideMainBrand == 0) { | ||||
|             buffer.putInt(0x6D703432);// mayor brand "mp42" | ||||
|             buffer.putInt(512);// default minor version | ||||
|             buffer.putInt(0x6D703432); // mayor brand "mp42" | ||||
|             buffer.putInt(512); // default minor version | ||||
|         } else { | ||||
|             buffer.putInt(overrideMainBrand); | ||||
|             buffer.putInt(0); | ||||
|             buffer.putInt(0x6D703432);// "mp42" compatible brand | ||||
|             buffer.putInt(0x6D703432); // "mp42" compatible brand | ||||
|         } | ||||
|  | ||||
|         for (Integer brand : compatibleBrands) { | ||||
|             buffer.putInt(brand);// compatible brand | ||||
|             buffer.putInt(brand); // compatible brand | ||||
|         } | ||||
|  | ||||
|         outWrite(buffer.array()); | ||||
| @@ -631,7 +640,7 @@ public class Mp4FromDashWriter { | ||||
|         return size; | ||||
|     } | ||||
|  | ||||
|     private byte[] make_mdat(long refSize, boolean is64) { | ||||
|     private byte[] makeMdat(long refSize, final boolean is64) { | ||||
|         if (is64) { | ||||
|             refSize += 16; | ||||
|         } else { | ||||
| @@ -640,7 +649,7 @@ public class Mp4FromDashWriter { | ||||
|  | ||||
|         ByteBuffer buffer = ByteBuffer.allocate(is64 ? 16 : 8) | ||||
|                 .putInt(is64 ? 0x01 : (int) refSize) | ||||
|                 .putInt(0x6D646174);// mdat | ||||
|                 .putInt(0x6D646174); // mdat | ||||
|  | ||||
|         if (is64) { | ||||
|             buffer.putLong(refSize); | ||||
| @@ -649,7 +658,7 @@ public class Mp4FromDashWriter { | ||||
|         return buffer.array(); | ||||
|     } | ||||
|  | ||||
|     private void make_mvhd(long longestTrack) throws IOException { | ||||
|     private void makeMvhd(final long longestTrack) throws IOException { | ||||
|         auxWrite(new byte[]{ | ||||
|                 0x00, 0x00, 0x00, 0x78, 0x6D, 0x76, 0x68, 0x64, 0x01, 0x00, 0x00, 0x00 | ||||
|         }); | ||||
| @@ -662,21 +671,22 @@ public class Mp4FromDashWriter { | ||||
|         ); | ||||
|  | ||||
|         auxWrite(new byte[]{ | ||||
|                 0x00, 0x01, 0x00, 0x00, 0x01, 0x00,// default volume and rate | ||||
|                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// reserved values | ||||
|                 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, // default volume and rate | ||||
|                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved values | ||||
|                 // default matrix | ||||
|                 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
|                 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
|                 0x40, 0x00, 0x00, 0x00 | ||||
|         }); | ||||
|         auxWrite(new byte[24]);// predefined | ||||
|         auxWrite(new byte[24]); // predefined | ||||
|         auxWrite(ByteBuffer.allocate(4) | ||||
|                 .putInt(tracks.length + 1) | ||||
|                 .array() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     private int make_moov(int[] defaultMediaTime, TablesInfo[] tablesInfo, boolean is64) throws RuntimeException, IOException { | ||||
|     private int makeMoov(final int[] defaultMediaTime, final TablesInfo[] tablesInfo, | ||||
|                          final boolean is64) throws RuntimeException, IOException { | ||||
|         int start = auxOffset(); | ||||
|  | ||||
|         auxWrite(new byte[]{ | ||||
| @@ -688,21 +698,21 @@ public class Mp4FromDashWriter { | ||||
|  | ||||
|         for (int i = 0; i < durations.length; i++) { | ||||
|             durations[i] = (long) Math.ceil( | ||||
|                     ((double) tracks[i].trak.tkhd.duration / tracks[i].trak.mdia.mdhd_timeScale) * DEFAULT_TIMESCALE | ||||
|             ); | ||||
|                     ((double) tracks[i].trak.tkhd.duration / tracks[i].trak.mdia.mdhdTimeScale) | ||||
|                             * DEFAULT_TIMESCALE); | ||||
|  | ||||
|             if (durations[i] > longestTrack) { | ||||
|                 longestTrack = durations[i]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         make_mvhd(longestTrack); | ||||
|         makeMvhd(longestTrack); | ||||
|  | ||||
|         for (int i = 0; i < tracks.length; i++) { | ||||
|             if (tracks[i].trak.tkhd.matrix.length != 36) { | ||||
|                 throw new RuntimeException("bad track matrix length (expected 36) in track n°" + i); | ||||
|             } | ||||
|             make_trak(i, durations[i], defaultMediaTime[i], tablesInfo[i], is64); | ||||
|             makeTrak(i, durations[i], defaultMediaTime[i], tablesInfo[i], is64); | ||||
|         } | ||||
|  | ||||
|         // udta/meta/ilst/©too | ||||
| @@ -713,17 +723,18 @@ public class Mp4FromDashWriter { | ||||
|                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x69, 0x6C, 0x73, 0x74, 0x00, 0x00, 0x00, | ||||
|                 0x1F, (byte) 0xA9, 0x74, 0x6F, 0x6F, 0x00, 0x00, 0x00, 0x17, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, | ||||
|                 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, | ||||
|                 0x4E, 0x65, 0x77, 0x50, 0x69, 0x70, 0x65// "NewPipe" binary string | ||||
|                 0x4E, 0x65, 0x77, 0x50, 0x69, 0x70, 0x65 // "NewPipe" binary string | ||||
|         }); | ||||
|  | ||||
|         return lengthFor(start); | ||||
|     } | ||||
|  | ||||
|     private void make_trak(int index, long duration, int defaultMediaTime, TablesInfo tables, boolean is64) throws IOException { | ||||
|     private void makeTrak(final int index, final long duration, final int defaultMediaTime, | ||||
|                           final TablesInfo tables, final boolean is64) throws IOException { | ||||
|         int start = auxOffset(); | ||||
|  | ||||
|         auxWrite(new byte[]{ | ||||
|                 0x00, 0x00, 0x00, 0x00, 0x74, 0x72, 0x61, 0x6B,// trak header | ||||
|                 0x00, 0x00, 0x00, 0x00, 0x74, 0x72, 0x61, 0x6B, // trak header | ||||
|                 0x00, 0x00, 0x00, 0x68, 0x74, 0x6B, 0x68, 0x64, 0x01, 0x00, 0x00, 0x03 // tkhd header | ||||
|         }); | ||||
|  | ||||
| @@ -747,20 +758,20 @@ public class Mp4FromDashWriter { | ||||
|         ); | ||||
|  | ||||
|         auxWrite(new byte[]{ | ||||
|                 0x00, 0x00, 0x00, 0x24, 0x65, 0x64, 0x74, 0x73,// edts header | ||||
|                 0x00, 0x00, 0x00, 0x1C, 0x65, 0x6C, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01// elst header | ||||
|                 0x00, 0x00, 0x00, 0x24, 0x65, 0x64, 0x74, 0x73, // edts header | ||||
|                 0x00, 0x00, 0x00, 0x1C, 0x65, 0x6C, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 // elst header | ||||
|         }); | ||||
|  | ||||
|         int bMediaRate; | ||||
|         int mediaTime; | ||||
|  | ||||
|         if (tracks[index].trak.edst_elst == null) { | ||||
|         if (tracks[index].trak.edstElst == null) { | ||||
|             // 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; | ||||
|         } else { | ||||
|             mediaTime = (int) tracks[index].trak.edst_elst.MediaTime; | ||||
|             bMediaRate = tracks[index].trak.edst_elst.bMediaRate; | ||||
|             mediaTime = (int) tracks[index].trak.edstElst.mediaTime; | ||||
|             bMediaRate = tracks[index].trak.edstElst.bMediaRate; | ||||
|         } | ||||
|  | ||||
|         auxWrite(ByteBuffer | ||||
| @@ -771,32 +782,33 @@ public class Mp4FromDashWriter { | ||||
|                 .array() | ||||
|         ); | ||||
|  | ||||
|         make_mdia(tracks[index].trak.mdia, tables, is64, tracks[index].kind == TrackKind.Audio); | ||||
|         makeMdia(tracks[index].trak.mdia, tables, is64, tracks[index].kind == TrackKind.Audio); | ||||
|  | ||||
|         lengthFor(start); | ||||
|     } | ||||
|  | ||||
|     private void make_mdia(Mdia mdia, TablesInfo tablesInfo, boolean is64, boolean isAudio) throws IOException { | ||||
|         int start_mdia = auxOffset(); | ||||
|         auxWrite(new byte[]{0x00, 0x00, 0x00, 0x00, 0x6D, 0x64, 0x69, 0x61});// mdia | ||||
|     private void makeMdia(final Mdia mdia, final TablesInfo tablesInfo, final boolean is64, | ||||
|                           final boolean isAudio) throws IOException { | ||||
|         int startMdia = auxOffset(); | ||||
|         auxWrite(new byte[]{0x00, 0x00, 0x00, 0x00, 0x6D, 0x64, 0x69, 0x61}); // mdia | ||||
|         auxWrite(mdia.mdhd); | ||||
|         auxWrite(make_hdlr(mdia.hdlr)); | ||||
|         auxWrite(makeHdlr(mdia.hdlr)); | ||||
|  | ||||
|         int start_minf = auxOffset(); | ||||
|         auxWrite(new byte[]{0x00, 0x00, 0x00, 0x00, 0x6D, 0x69, 0x6E, 0x66});// minf | ||||
|         auxWrite(mdia.minf.$mhd); | ||||
|         int startMinf = auxOffset(); | ||||
|         auxWrite(new byte[]{0x00, 0x00, 0x00, 0x00, 0x6D, 0x69, 0x6E, 0x66}); // minf | ||||
|         auxWrite(mdia.minf.mhd); | ||||
|         auxWrite(mdia.minf.dinf); | ||||
|  | ||||
|         int start_stbl = auxOffset(); | ||||
|         auxWrite(new byte[]{0x00, 0x00, 0x00, 0x00, 0x73, 0x74, 0x62, 0x6C});// stbl | ||||
|         auxWrite(mdia.minf.stbl_stsd); | ||||
|         int startStbl = auxOffset(); | ||||
|         auxWrite(new byte[]{0x00, 0x00, 0x00, 0x00, 0x73, 0x74, 0x62, 0x6C}); // stbl | ||||
|         auxWrite(mdia.minf.stblStsd); | ||||
|  | ||||
|         // | ||||
|         // In audio tracks the following tables is not required: ssts ctts | ||||
|         // And stsz can be empty if has a default sample size | ||||
|         // | ||||
|         if (moovSimulation) { | ||||
|             make(0x73747473, -1, 2, 1);// stts | ||||
|             make(0x73747473, -1, 2, 1); // stts | ||||
|             if (tablesInfo.stss > 0) { | ||||
|                 make(0x73747373, -1, 1, tablesInfo.stss); | ||||
|             } | ||||
| @@ -804,7 +816,7 @@ public class Mp4FromDashWriter { | ||||
|                 make(0x63747473, -1, 2, tablesInfo.ctts); | ||||
|             } | ||||
|             make(0x73747363, -1, 3, tablesInfo.stsc); | ||||
|             make(0x7374737A, tablesInfo.stsz_default, 1, tablesInfo.stsz); | ||||
|             make(0x7374737A, tablesInfo.stszDefault, 1, tablesInfo.stsz); | ||||
|             make(is64 ? 0x636F3634 : 0x7374636F, -1, is64 ? 2 : 1, tablesInfo.stco); | ||||
|         } else { | ||||
|             tablesInfo.stts = make(0x73747473, -1, 2, 1); | ||||
| @@ -815,23 +827,24 @@ public class Mp4FromDashWriter { | ||||
|                 tablesInfo.ctts = make(0x63747473, -1, 2, tablesInfo.ctts); | ||||
|             } | ||||
|             tablesInfo.stsc = make(0x73747363, -1, 3, tablesInfo.stsc); | ||||
|             tablesInfo.stsz = make(0x7374737A, tablesInfo.stsz_default, 1, tablesInfo.stsz); | ||||
|             tablesInfo.stco = make(is64 ? 0x636F3634 : 0x7374636F, -1, is64 ? 2 : 1, tablesInfo.stco); | ||||
|             tablesInfo.stsz = make(0x7374737A, tablesInfo.stszDefault, 1, tablesInfo.stsz); | ||||
|             tablesInfo.stco = make(is64 ? 0x636F3634 : 0x7374636F, -1, is64 ? 2 : 1, | ||||
|                     tablesInfo.stco); | ||||
|         } | ||||
|  | ||||
|         if (isAudio) { | ||||
|             auxWrite(make_sgpd()); | ||||
|             tablesInfo.sbgp = make_sbgp();// during simulation the returned offset is ignored | ||||
|             auxWrite(makeSgpd()); | ||||
|             tablesInfo.sbgp = makeSbgp(); // during simulation the returned offset is ignored | ||||
|         } | ||||
|  | ||||
|         lengthFor(start_stbl); | ||||
|         lengthFor(start_minf); | ||||
|         lengthFor(start_mdia); | ||||
|         lengthFor(startStbl); | ||||
|         lengthFor(startMinf); | ||||
|         lengthFor(startMdia); | ||||
|     } | ||||
|  | ||||
|     private byte[] make_hdlr(Hdlr hdlr) { | ||||
|     private byte[] makeHdlr(final Hdlr hdlr) { | ||||
|         ByteBuffer buffer = ByteBuffer.wrap(new byte[]{ | ||||
|                 0x00, 0x00, 0x00, 0x77, 0x68, 0x64, 0x6C, 0x72,// hdlr | ||||
|                 0x00, 0x00, 0x00, 0x77, 0x68, 0x64, 0x6C, 0x72, // hdlr | ||||
|                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
|                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
|                 // binary string "ISO Media file created in NewPipe (A libre lightweight streaming frontend for Android)." | ||||
| @@ -846,28 +859,28 @@ public class Mp4FromDashWriter { | ||||
|         buffer.position(12); | ||||
|         buffer.putInt(hdlr.type); | ||||
|         buffer.putInt(hdlr.subType); | ||||
|         buffer.put(hdlr.bReserved);// always is a zero array | ||||
|         buffer.put(hdlr.bReserved); // always is a zero array | ||||
|  | ||||
|         return buffer.array(); | ||||
|     } | ||||
|  | ||||
|     private int make_sbgp() throws IOException { | ||||
|     private int makeSbgp() throws IOException { | ||||
|         int offset = auxOffset(); | ||||
|  | ||||
|         auxWrite(new byte[] { | ||||
|                 0x00, 0x00, 0x00, 0x1C,// box size | ||||
|                 0x73, 0x62, 0x67, 0x70,// "sbpg" | ||||
|                 0x00, 0x00, 0x00, 0x00,// default box flags | ||||
|                 0x72, 0x6F, 0x6C, 0x6C,// group type "roll" | ||||
|                 0x00, 0x00, 0x00, 0x01,// group table size | ||||
|                 0x00, 0x00, 0x00, 0x00,// group[0] total samples (to be set later) | ||||
|                 0x00, 0x00, 0x00, 0x01// group[0] description index | ||||
|                 0x00, 0x00, 0x00, 0x1C, // box size | ||||
|                 0x73, 0x62, 0x67, 0x70, // "sbpg" | ||||
|                 0x00, 0x00, 0x00, 0x00, // default box flags | ||||
|                 0x72, 0x6F, 0x6C, 0x6C, // group type "roll" | ||||
|                 0x00, 0x00, 0x00, 0x01, // group table size | ||||
|                 0x00, 0x00, 0x00, 0x00, // group[0] total samples (to be set later) | ||||
|                 0x00, 0x00, 0x00, 0x01 // group[0] description index | ||||
|         }); | ||||
|  | ||||
|         return offset + 0x14; | ||||
|     } | ||||
|  | ||||
|     private byte[] make_sgpd() { | ||||
|     private byte[] makeSgpd() { | ||||
|         /* | ||||
|          * Sample Group Description Box | ||||
|          * | ||||
| @@ -882,26 +895,25 @@ public class Mp4FromDashWriter { | ||||
|          */ | ||||
|  | ||||
|         ByteBuffer buffer = ByteBuffer.wrap(new byte[] { | ||||
|                 0x00, 0x00, 0x00, 0x1A,// box size | ||||
|                 0x73, 0x67, 0x70, 0x64,// "sgpd" | ||||
|                 0x01, 0x00, 0x00, 0x00,// box flags (unknown flag sets) | ||||
|                 0x00, 0x00, 0x00, 0x1A, // box size | ||||
|                 0x73, 0x67, 0x70, 0x64, // "sgpd" | ||||
|                 0x01, 0x00, 0x00, 0x00, // box flags (unknown flag sets) | ||||
|                 0x72, 0x6F, 0x6C, 0x6C, // ¿¿group type?? | ||||
|                 0x00, 0x00, 0x00, 0x02,// ¿¿?? | ||||
|                 0x00, 0x00, 0x00, 0x01,// ¿¿?? | ||||
|                 (byte)0xFF, (byte)0xFF// ¿¿?? | ||||
|                 0x00, 0x00, 0x00, 0x02, // ¿¿?? | ||||
|                 0x00, 0x00, 0x00, 0x01, // ¿¿?? | ||||
|                 (byte) 0xFF, (byte) 0xFF // ¿¿?? | ||||
|         }); | ||||
|  | ||||
|         return buffer.array(); | ||||
|     } | ||||
|  | ||||
|     class TablesInfo { | ||||
|  | ||||
|         int stts; | ||||
|         int stsc; | ||||
|         int[] stsc_bEntries; | ||||
|         int[] stscBEntries; | ||||
|         int ctts; | ||||
|         int stsz; | ||||
|         int stsz_default; | ||||
|         int stszDefault; | ||||
|         int stss; | ||||
|         int stco; | ||||
|         int sbgp; | ||||
|   | ||||
| @@ -1,431 +1,428 @@ | ||||
| package org.schabi.newpipe.streams; | ||||
|  | ||||
| import androidx.annotation.NonNull; | ||||
|  | ||||
| import org.schabi.newpipe.streams.WebMReader.Cluster; | ||||
| import org.schabi.newpipe.streams.WebMReader.Segment; | ||||
| import org.schabi.newpipe.streams.WebMReader.SimpleBlock; | ||||
| import org.schabi.newpipe.streams.WebMReader.WebMTrack; | ||||
| import org.schabi.newpipe.streams.io.SharpStream; | ||||
|  | ||||
| import java.io.Closeable; | ||||
| import java.io.IOException; | ||||
| import java.nio.ByteBuffer; | ||||
| import java.nio.ByteOrder; | ||||
|  | ||||
| import androidx.annotation.Nullable; | ||||
|  | ||||
| /** | ||||
|  * @author kapodamy | ||||
|  */ | ||||
| public class OggFromWebMWriter implements Closeable { | ||||
|  | ||||
|     private static final byte FLAG_UNSET = 0x00; | ||||
|     //private static final byte FLAG_CONTINUED = 0x01; | ||||
|     private static final byte FLAG_FIRST = 0x02; | ||||
|     private static final byte FLAG_LAST = 0x04; | ||||
|  | ||||
|     private final static byte HEADER_CHECKSUM_OFFSET = 22; | ||||
|     private final static byte HEADER_SIZE = 27; | ||||
|  | ||||
|     private final static int TIME_SCALE_NS = 1000000000; | ||||
|  | ||||
|     private boolean done = false; | ||||
|     private boolean parsed = false; | ||||
|  | ||||
|     private SharpStream source; | ||||
|     private SharpStream output; | ||||
|  | ||||
|     private int sequence_count = 0; | ||||
|     private final int STREAM_ID; | ||||
|     private byte packet_flag = FLAG_FIRST; | ||||
|  | ||||
|     private WebMReader webm = null; | ||||
|     private WebMTrack webm_track = null; | ||||
|     private Segment webm_segment = null; | ||||
|     private Cluster webm_cluster = null; | ||||
|     private SimpleBlock webm_block = null; | ||||
|  | ||||
|     private long webm_block_last_timecode = 0; | ||||
|     private long webm_block_near_duration = 0; | ||||
|  | ||||
|     private short segment_table_size = 0; | ||||
|     private final byte[] segment_table = new byte[255]; | ||||
|     private long segment_table_next_timestamp = TIME_SCALE_NS; | ||||
|  | ||||
|     private final int[] crc32_table = new int[256]; | ||||
|  | ||||
|     public OggFromWebMWriter(@NonNull SharpStream source, @NonNull SharpStream target) { | ||||
|         if (!source.canRead() || !source.canRewind()) { | ||||
|             throw new IllegalArgumentException("source stream must be readable and allows seeking"); | ||||
|         } | ||||
|         if (!target.canWrite() || !target.canRewind()) { | ||||
|             throw new IllegalArgumentException("output stream must be writable and allows seeking"); | ||||
|         } | ||||
|  | ||||
|         this.source = source; | ||||
|         this.output = target; | ||||
|  | ||||
|         this.STREAM_ID = (int) System.currentTimeMillis(); | ||||
|  | ||||
|         populate_crc32_table(); | ||||
|     } | ||||
|  | ||||
|     public boolean isDone() { | ||||
|         return done; | ||||
|     } | ||||
|  | ||||
|     public boolean isParsed() { | ||||
|         return parsed; | ||||
|     } | ||||
|  | ||||
|     public WebMTrack[] getTracksFromSource() throws IllegalStateException { | ||||
|         if (!parsed) { | ||||
|             throw new IllegalStateException("source must be parsed first"); | ||||
|         } | ||||
|  | ||||
|         return webm.getAvailableTracks(); | ||||
|     } | ||||
|  | ||||
|     public void parseSource() throws IOException, IllegalStateException { | ||||
|         if (done) { | ||||
|             throw new IllegalStateException("already done"); | ||||
|         } | ||||
|         if (parsed) { | ||||
|             throw new IllegalStateException("already parsed"); | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             webm = new WebMReader(source); | ||||
|             webm.parse(); | ||||
|             webm_segment = webm.getNextSegment(); | ||||
|         } finally { | ||||
|             parsed = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void selectTrack(int trackIndex) throws IOException { | ||||
|         if (!parsed) { | ||||
|             throw new IllegalStateException("source must be parsed first"); | ||||
|         } | ||||
|         if (done) { | ||||
|             throw new IOException("already done"); | ||||
|         } | ||||
|         if (webm_track != null) { | ||||
|             throw new IOException("tracks already selected"); | ||||
|         } | ||||
|  | ||||
|         switch (webm.getAvailableTracks()[trackIndex].kind) { | ||||
|             case Audio: | ||||
|             case Video: | ||||
|                 break; | ||||
|             default: | ||||
|                 throw new UnsupportedOperationException("the track must an audio or video stream"); | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             webm_track = webm.selectTrack(trackIndex); | ||||
|         } finally { | ||||
|             parsed = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void close() throws IOException { | ||||
|         done = true; | ||||
|         parsed = true; | ||||
|  | ||||
|         webm_track = null; | ||||
|         webm = null; | ||||
|  | ||||
|         if (!output.isClosed()) { | ||||
|             output.flush(); | ||||
|         } | ||||
|  | ||||
|         source.close(); | ||||
|         output.close(); | ||||
|     } | ||||
|  | ||||
|     public void build() throws IOException { | ||||
|         float resolution; | ||||
|         SimpleBlock bloq; | ||||
|         ByteBuffer header = ByteBuffer.allocate(27 + (255 * 255)); | ||||
|         ByteBuffer page = ByteBuffer.allocate(64 * 1024); | ||||
|  | ||||
|         header.order(ByteOrder.LITTLE_ENDIAN); | ||||
|  | ||||
|         /* step 1: get the amount of frames per seconds */ | ||||
|         switch (webm_track.kind) { | ||||
|             case Audio: | ||||
|                 resolution = getSampleFrequencyFromTrack(webm_track.bMetadata); | ||||
|                 if (resolution == 0f) { | ||||
|                     throw new RuntimeException("cannot get the audio sample rate"); | ||||
|                 } | ||||
|                 break; | ||||
|             case Video: | ||||
|                 // WARNING: untested | ||||
|                 if (webm_track.defaultDuration == 0) { | ||||
|                     throw new RuntimeException("missing default frame time"); | ||||
|                 } | ||||
|                 resolution = 1000f / ((float) webm_track.defaultDuration / webm_segment.info.timecodeScale); | ||||
|                 break; | ||||
|             default: | ||||
|                 throw new RuntimeException("not implemented"); | ||||
|         } | ||||
|  | ||||
|         /* step 2: create packet with code init data */ | ||||
|         if (webm_track.codecPrivate != null) { | ||||
|             addPacketSegment(webm_track.codecPrivate.length); | ||||
|             make_packetHeader(0x00, header, webm_track.codecPrivate); | ||||
|             write(header); | ||||
|             output.write(webm_track.codecPrivate); | ||||
|         } | ||||
|  | ||||
|         /* step 3: create packet with metadata */ | ||||
|         byte[] buffer = make_metadata(); | ||||
|         if (buffer != null) { | ||||
|             addPacketSegment(buffer.length); | ||||
|             make_packetHeader(0x00, header, buffer); | ||||
|             write(header); | ||||
|             output.write(buffer); | ||||
|         } | ||||
|  | ||||
|         /* step 4: calculate amount of packets */ | ||||
|         while (webm_segment != null) { | ||||
|             bloq = getNextBlock(); | ||||
|  | ||||
|             if (bloq != null && addPacketSegment(bloq)) { | ||||
|                 int pos = page.position(); | ||||
|                 //noinspection ResultOfMethodCallIgnored | ||||
|                 bloq.data.read(page.array(), pos, bloq.dataSize); | ||||
|                 page.position(pos + bloq.dataSize); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // calculate the current packet duration using the next block | ||||
|             double elapsed_ns = webm_track.codecDelay; | ||||
|  | ||||
|             if (bloq == null) { | ||||
|                 packet_flag = FLAG_LAST;// note: if the flag is FLAG_CONTINUED, is changed | ||||
|                 elapsed_ns += webm_block_last_timecode; | ||||
|  | ||||
|                 if (webm_track.defaultDuration > 0) { | ||||
|                     elapsed_ns += webm_track.defaultDuration; | ||||
|                 } else { | ||||
|                     // hardcoded way, guess the sample duration | ||||
|                     elapsed_ns += webm_block_near_duration; | ||||
|                 } | ||||
|             } else { | ||||
|                 elapsed_ns += bloq.absoluteTimeCodeNs; | ||||
|             } | ||||
|  | ||||
|             // get the sample count in the page | ||||
|             elapsed_ns = elapsed_ns / TIME_SCALE_NS; | ||||
|             elapsed_ns = Math.ceil(elapsed_ns * resolution); | ||||
|  | ||||
|             // create header and calculate page checksum | ||||
|             int checksum = make_packetHeader((long) elapsed_ns, header, null); | ||||
|             checksum = calc_crc32(checksum, page.array(), page.position()); | ||||
|  | ||||
|             header.putInt(HEADER_CHECKSUM_OFFSET, checksum); | ||||
|  | ||||
|             // dump data | ||||
|             write(header); | ||||
|             write(page); | ||||
|  | ||||
|             webm_block = bloq; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private int make_packetHeader(long gran_pos, @NonNull ByteBuffer buffer, byte[] immediate_page) { | ||||
|         short length = HEADER_SIZE; | ||||
|  | ||||
|         buffer.putInt(0x5367674f);// "OggS" binary string in little-endian | ||||
|         buffer.put((byte) 0x00);// version | ||||
|         buffer.put(packet_flag);// type | ||||
|  | ||||
|         buffer.putLong(gran_pos);// granulate position | ||||
|  | ||||
|         buffer.putInt(STREAM_ID);// bitstream serial number | ||||
|         buffer.putInt(sequence_count++);// page sequence number | ||||
|  | ||||
|         buffer.putInt(0x00);// page checksum | ||||
|  | ||||
|         buffer.put((byte) segment_table_size);// segment table | ||||
|         buffer.put(segment_table, 0, segment_table_size);// segment size | ||||
|  | ||||
|         length += segment_table_size; | ||||
|  | ||||
|         clearSegmentTable();// clear segment table for next header | ||||
|  | ||||
|         int checksum_crc32 = calc_crc32(0x00, buffer.array(), length); | ||||
|  | ||||
|         if (immediate_page != null) { | ||||
|             checksum_crc32 = calc_crc32(checksum_crc32, immediate_page, immediate_page.length); | ||||
|             buffer.putInt(HEADER_CHECKSUM_OFFSET, checksum_crc32); | ||||
|             segment_table_next_timestamp -= TIME_SCALE_NS; | ||||
|         } | ||||
|  | ||||
|         return checksum_crc32; | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     private byte[] make_metadata() { | ||||
|         if ("A_OPUS".equals(webm_track.codecId)) { | ||||
|             return new byte[]{ | ||||
|                     0x4F, 0x70, 0x75, 0x73, 0x54, 0x61, 0x67, 0x73,// "OpusTags" binary string | ||||
|                     0x07, 0x00, 0x00, 0x00,// writting application string size | ||||
|                     0x4E, 0x65, 0x77, 0x50, 0x69, 0x70, 0x65,// "NewPipe" binary string | ||||
|                     0x00, 0x00, 0x00, 0x00// additional tags count (zero means no tags) | ||||
|             }; | ||||
|         } else if ("A_VORBIS".equals(webm_track.codecId)) { | ||||
|             return new byte[]{ | ||||
|                     0x03,// ???????? | ||||
|                     0x76, 0x6f, 0x72, 0x62, 0x69, 0x73,// "vorbis" binary string | ||||
|                     0x07, 0x00, 0x00, 0x00,// writting application string size | ||||
|                     0x4E, 0x65, 0x77, 0x50, 0x69, 0x70, 0x65,// "NewPipe" binary string | ||||
|                     0x01, 0x00, 0x00, 0x00,// additional tags count (zero means no tags) | ||||
|  | ||||
|                     /* | ||||
|                         // whole file duration (not implemented) | ||||
|                         0x44,// tag string size | ||||
|                         0x55, 0x52, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x3D, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, | ||||
|                         0x30, 0x2E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 | ||||
|                      */ | ||||
|                     0x0F,// tag string size | ||||
|                     0x00, 0x00, 0x00, 0x45, 0x4E, 0x43, 0x4F, 0x44, 0x45, 0x52, 0x3D,// "ENCODER=" binary string | ||||
|                     0x4E, 0x65, 0x77, 0x50, 0x69, 0x70, 0x65,// "NewPipe" binary string | ||||
|                     0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00// ???????? | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         // not implemented for the desired codec | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     private void write(ByteBuffer buffer) throws IOException { | ||||
|         output.write(buffer.array(), 0, buffer.position()); | ||||
|         buffer.position(0); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     @Nullable | ||||
|     private SimpleBlock getNextBlock() throws IOException { | ||||
|         SimpleBlock res; | ||||
|  | ||||
|         if (webm_block != null) { | ||||
|             res = webm_block; | ||||
|             webm_block = null; | ||||
|             return res; | ||||
|         } | ||||
|  | ||||
|         if (webm_segment == null) { | ||||
|             webm_segment = webm.getNextSegment(); | ||||
|             if (webm_segment == null) { | ||||
|                 return null;// no more blocks in the selected track | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (webm_cluster == null) { | ||||
|             webm_cluster = webm_segment.getNextCluster(); | ||||
|             if (webm_cluster == null) { | ||||
|                 webm_segment = null; | ||||
|                 return getNextBlock(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         res = webm_cluster.getNextSimpleBlock(); | ||||
|         if (res == null) { | ||||
|             webm_cluster = null; | ||||
|             return getNextBlock(); | ||||
|         } | ||||
|  | ||||
|         webm_block_near_duration = res.absoluteTimeCodeNs - webm_block_last_timecode; | ||||
|         webm_block_last_timecode = res.absoluteTimeCodeNs; | ||||
|  | ||||
|         return res; | ||||
|     } | ||||
|  | ||||
|     private float getSampleFrequencyFromTrack(byte[] bMetadata) { | ||||
|         // hardcoded way | ||||
|         ByteBuffer buffer = ByteBuffer.wrap(bMetadata); | ||||
|  | ||||
|         while (buffer.remaining() >= 6) { | ||||
|             int id = buffer.getShort() & 0xFFFF; | ||||
|             if (id == 0x0000B584) { | ||||
|                 return buffer.getFloat(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return 0f; | ||||
|     } | ||||
|  | ||||
|     private void clearSegmentTable() { | ||||
|         segment_table_next_timestamp += TIME_SCALE_NS; | ||||
|         packet_flag = FLAG_UNSET; | ||||
|         segment_table_size = 0; | ||||
|     } | ||||
|  | ||||
|     private boolean addPacketSegment(SimpleBlock block) { | ||||
|         long timestamp = block.absoluteTimeCodeNs + webm_track.codecDelay; | ||||
|  | ||||
|         if (timestamp >= segment_table_next_timestamp) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return addPacketSegment(block.dataSize); | ||||
|     } | ||||
|  | ||||
|     private boolean addPacketSegment(int size) { | ||||
|         if (size > 65025) { | ||||
|             throw new UnsupportedOperationException("page size cannot be larger than 65025"); | ||||
|         } | ||||
|  | ||||
|         int available = (segment_table.length - segment_table_size) * 255; | ||||
|         boolean extra = (size % 255) == 0; | ||||
|  | ||||
|         if (extra) { | ||||
|             // add a zero byte entry in the table | ||||
|             // required to indicate the sample size is multiple of 255 | ||||
|             available -= 255; | ||||
|         } | ||||
|  | ||||
|         // check if possible add the segment, without overflow the table | ||||
|         if (available < size) { | ||||
|             return false;// not enough space on the page | ||||
|         } | ||||
|  | ||||
|         for (; size > 0; size -= 255) { | ||||
|             segment_table[segment_table_size++] = (byte) Math.min(size, 255); | ||||
|         } | ||||
|  | ||||
|         if (extra) { | ||||
|             segment_table[segment_table_size++] = 0x00; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     private void populate_crc32_table() { | ||||
|         for (int i = 0; i < 0x100; i++) { | ||||
|             int crc = i << 24; | ||||
|             for (int j = 0; j < 8; j++) { | ||||
|                 long b = crc >>> 31; | ||||
|                 crc <<= 1; | ||||
|                 crc ^= (int) (0x100000000L - b) & 0x04c11db7; | ||||
|             } | ||||
|             crc32_table[i] = crc; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private int calc_crc32(int initial_crc, byte[] buffer, int size) { | ||||
|         for (int i = 0; i < size; i++) { | ||||
|             int reg = (initial_crc >>> 24) & 0xff; | ||||
|             initial_crc = (initial_crc << 8) ^ crc32_table[reg ^ (buffer[i] & 0xff)]; | ||||
|         } | ||||
|  | ||||
|         return initial_crc; | ||||
|     } | ||||
|  | ||||
| } | ||||
| package org.schabi.newpipe.streams; | ||||
|  | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
|  | ||||
| import org.schabi.newpipe.streams.WebMReader.Cluster; | ||||
| import org.schabi.newpipe.streams.WebMReader.Segment; | ||||
| import org.schabi.newpipe.streams.WebMReader.SimpleBlock; | ||||
| import org.schabi.newpipe.streams.WebMReader.WebMTrack; | ||||
| import org.schabi.newpipe.streams.io.SharpStream; | ||||
|  | ||||
| import java.io.Closeable; | ||||
| import java.io.IOException; | ||||
| import java.nio.ByteBuffer; | ||||
| import java.nio.ByteOrder; | ||||
|  | ||||
| /** | ||||
|  * @author kapodamy | ||||
|  */ | ||||
| public class OggFromWebMWriter implements Closeable { | ||||
|     private static final byte FLAG_UNSET = 0x00; | ||||
|     //private static final byte FLAG_CONTINUED = 0x01; | ||||
|     private static final byte FLAG_FIRST = 0x02; | ||||
|     private static final byte FLAG_LAST = 0x04; | ||||
|  | ||||
|     private static final byte HEADER_CHECKSUM_OFFSET = 22; | ||||
|     private static final byte HEADER_SIZE = 27; | ||||
|  | ||||
|     private static final int TIME_SCALE_NS = 1000000000; | ||||
|  | ||||
|     private boolean done = false; | ||||
|     private boolean parsed = false; | ||||
|  | ||||
|     private SharpStream source; | ||||
|     private SharpStream output; | ||||
|  | ||||
|     private int sequenceCount = 0; | ||||
|     private final int streamId; | ||||
|     private byte packetFlag = FLAG_FIRST; | ||||
|  | ||||
|     private WebMReader webm = null; | ||||
|     private WebMTrack webmTrack = null; | ||||
|     private Segment webmSegment = null; | ||||
|     private Cluster webmCluster = null; | ||||
|     private SimpleBlock webmBlock = null; | ||||
|  | ||||
|     private long webmBlockLastTimecode = 0; | ||||
|     private long webmBlockNearDuration = 0; | ||||
|  | ||||
|     private short segmentTableSize = 0; | ||||
|     private final byte[] segmentTable = new byte[255]; | ||||
|     private long segmentTableNextTimestamp = TIME_SCALE_NS; | ||||
|  | ||||
|     private final int[] crc32Table = new int[256]; | ||||
|  | ||||
|     public OggFromWebMWriter(@NonNull final SharpStream source, @NonNull final SharpStream target) { | ||||
|         if (!source.canRead() || !source.canRewind()) { | ||||
|             throw new IllegalArgumentException("source stream must be readable and allows seeking"); | ||||
|         } | ||||
|         if (!target.canWrite() || !target.canRewind()) { | ||||
|             throw new IllegalArgumentException("output stream must be writable and allows seeking"); | ||||
|         } | ||||
|  | ||||
|         this.source = source; | ||||
|         this.output = target; | ||||
|  | ||||
|         this.streamId = (int) System.currentTimeMillis(); | ||||
|  | ||||
|         populateCrc32Table(); | ||||
|     } | ||||
|  | ||||
|     public boolean isDone() { | ||||
|         return done; | ||||
|     } | ||||
|  | ||||
|     public boolean isParsed() { | ||||
|         return parsed; | ||||
|     } | ||||
|  | ||||
|     public WebMTrack[] getTracksFromSource() throws IllegalStateException { | ||||
|         if (!parsed) { | ||||
|             throw new IllegalStateException("source must be parsed first"); | ||||
|         } | ||||
|  | ||||
|         return webm.getAvailableTracks(); | ||||
|     } | ||||
|  | ||||
|     public void parseSource() throws IOException, IllegalStateException { | ||||
|         if (done) { | ||||
|             throw new IllegalStateException("already done"); | ||||
|         } | ||||
|         if (parsed) { | ||||
|             throw new IllegalStateException("already parsed"); | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             webm = new WebMReader(source); | ||||
|             webm.parse(); | ||||
|             webmSegment = webm.getNextSegment(); | ||||
|         } finally { | ||||
|             parsed = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void selectTrack(final int trackIndex) throws IOException { | ||||
|         if (!parsed) { | ||||
|             throw new IllegalStateException("source must be parsed first"); | ||||
|         } | ||||
|         if (done) { | ||||
|             throw new IOException("already done"); | ||||
|         } | ||||
|         if (webmTrack != null) { | ||||
|             throw new IOException("tracks already selected"); | ||||
|         } | ||||
|  | ||||
|         switch (webm.getAvailableTracks()[trackIndex].kind) { | ||||
|             case Audio: | ||||
|             case Video: | ||||
|                 break; | ||||
|             default: | ||||
|                 throw new UnsupportedOperationException("the track must an audio or video stream"); | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             webmTrack = webm.selectTrack(trackIndex); | ||||
|         } finally { | ||||
|             parsed = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void close() throws IOException { | ||||
|         done = true; | ||||
|         parsed = true; | ||||
|  | ||||
|         webmTrack = null; | ||||
|         webm = null; | ||||
|  | ||||
|         if (!output.isClosed()) { | ||||
|             output.flush(); | ||||
|         } | ||||
|  | ||||
|         source.close(); | ||||
|         output.close(); | ||||
|     } | ||||
|  | ||||
|     public void build() throws IOException { | ||||
|         float resolution; | ||||
|         SimpleBlock bloq; | ||||
|         ByteBuffer header = ByteBuffer.allocate(27 + (255 * 255)); | ||||
|         ByteBuffer page = ByteBuffer.allocate(64 * 1024); | ||||
|  | ||||
|         header.order(ByteOrder.LITTLE_ENDIAN); | ||||
|  | ||||
|         /* step 1: get the amount of frames per seconds */ | ||||
|         switch (webmTrack.kind) { | ||||
|             case Audio: | ||||
|                 resolution = getSampleFrequencyFromTrack(webmTrack.bMetadata); | ||||
|                 if (resolution == 0f) { | ||||
|                     throw new RuntimeException("cannot get the audio sample rate"); | ||||
|                 } | ||||
|                 break; | ||||
|             case Video: | ||||
|                 // WARNING: untested | ||||
|                 if (webmTrack.defaultDuration == 0) { | ||||
|                     throw new RuntimeException("missing default frame time"); | ||||
|                 } | ||||
|                 resolution = 1000f / ((float) webmTrack.defaultDuration | ||||
|                         / webmSegment.info.timecodeScale); | ||||
|                 break; | ||||
|             default: | ||||
|                 throw new RuntimeException("not implemented"); | ||||
|         } | ||||
|  | ||||
|         /* step 2: create packet with code init data */ | ||||
|         if (webmTrack.codecPrivate != null) { | ||||
|             addPacketSegment(webmTrack.codecPrivate.length); | ||||
|             makePacketheader(0x00, header, webmTrack.codecPrivate); | ||||
|             write(header); | ||||
|             output.write(webmTrack.codecPrivate); | ||||
|         } | ||||
|  | ||||
|         /* step 3: create packet with metadata */ | ||||
|         byte[] buffer = makeMetadata(); | ||||
|         if (buffer != null) { | ||||
|             addPacketSegment(buffer.length); | ||||
|             makePacketheader(0x00, header, buffer); | ||||
|             write(header); | ||||
|             output.write(buffer); | ||||
|         } | ||||
|  | ||||
|         /* step 4: calculate amount of packets */ | ||||
|         while (webmSegment != null) { | ||||
|             bloq = getNextBlock(); | ||||
|  | ||||
|             if (bloq != null && addPacketSegment(bloq)) { | ||||
|                 int pos = page.position(); | ||||
|                 //noinspection ResultOfMethodCallIgnored | ||||
|                 bloq.data.read(page.array(), pos, bloq.dataSize); | ||||
|                 page.position(pos + bloq.dataSize); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // calculate the current packet duration using the next block | ||||
|             double elapsedNs = webmTrack.codecDelay; | ||||
|  | ||||
|             if (bloq == null) { | ||||
|                 packetFlag = FLAG_LAST; // note: if the flag is FLAG_CONTINUED, is changed | ||||
|                 elapsedNs += webmBlockLastTimecode; | ||||
|  | ||||
|                 if (webmTrack.defaultDuration > 0) { | ||||
|                     elapsedNs += webmTrack.defaultDuration; | ||||
|                 } else { | ||||
|                     // hardcoded way, guess the sample duration | ||||
|                     elapsedNs += webmBlockNearDuration; | ||||
|                 } | ||||
|             } else { | ||||
|                 elapsedNs += bloq.absoluteTimeCodeNs; | ||||
|             } | ||||
|  | ||||
|             // get the sample count in the page | ||||
|             elapsedNs = elapsedNs / TIME_SCALE_NS; | ||||
|             elapsedNs = Math.ceil(elapsedNs * resolution); | ||||
|  | ||||
|             // create header and calculate page checksum | ||||
|             int checksum = makePacketheader((long) elapsedNs, header, null); | ||||
|             checksum = calcCrc32(checksum, page.array(), page.position()); | ||||
|  | ||||
|             header.putInt(HEADER_CHECKSUM_OFFSET, checksum); | ||||
|  | ||||
|             // dump data | ||||
|             write(header); | ||||
|             write(page); | ||||
|  | ||||
|             webmBlock = bloq; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private int makePacketheader(final long granPos, @NonNull final ByteBuffer buffer, | ||||
|                                  final byte[] immediatePage) { | ||||
|         short length = HEADER_SIZE; | ||||
|  | ||||
|         buffer.putInt(0x5367674f); // "OggS" binary string in little-endian | ||||
|         buffer.put((byte) 0x00); // version | ||||
|         buffer.put(packetFlag); // type | ||||
|  | ||||
|         buffer.putLong(granPos); // granulate position | ||||
|  | ||||
|         buffer.putInt(streamId); // bitstream serial number | ||||
|         buffer.putInt(sequenceCount++); // page sequence number | ||||
|  | ||||
|         buffer.putInt(0x00); // page checksum | ||||
|  | ||||
|         buffer.put((byte) segmentTableSize); // segment table | ||||
|         buffer.put(segmentTable, 0, segmentTableSize); // segment size | ||||
|  | ||||
|         length += segmentTableSize; | ||||
|  | ||||
|         clearSegmentTable(); // clear segment table for next header | ||||
|  | ||||
|         int checksumCrc32 = calcCrc32(0x00, buffer.array(), length); | ||||
|  | ||||
|         if (immediatePage != null) { | ||||
|             checksumCrc32 = calcCrc32(checksumCrc32, immediatePage, immediatePage.length); | ||||
|             buffer.putInt(HEADER_CHECKSUM_OFFSET, checksumCrc32); | ||||
|             segmentTableNextTimestamp -= TIME_SCALE_NS; | ||||
|         } | ||||
|  | ||||
|         return checksumCrc32; | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     private byte[] makeMetadata() { | ||||
|         if ("A_OPUS".equals(webmTrack.codecId)) { | ||||
|             return new byte[]{ | ||||
|                     0x4F, 0x70, 0x75, 0x73, 0x54, 0x61, 0x67, 0x73, // "OpusTags" binary string | ||||
|                     0x07, 0x00, 0x00, 0x00, // writing application string size | ||||
|                     0x4E, 0x65, 0x77, 0x50, 0x69, 0x70, 0x65, // "NewPipe" binary string | ||||
|                     0x00, 0x00, 0x00, 0x00 // additional tags count (zero means no tags) | ||||
|             }; | ||||
|         } else if ("A_VORBIS".equals(webmTrack.codecId)) { | ||||
|             return new byte[]{ | ||||
|                     0x03, // ???????? | ||||
|                     0x76, 0x6f, 0x72, 0x62, 0x69, 0x73, // "vorbis" binary string | ||||
|                     0x07, 0x00, 0x00, 0x00, // writting application string size | ||||
|                     0x4E, 0x65, 0x77, 0x50, 0x69, 0x70, 0x65, // "NewPipe" binary string | ||||
|                     0x01, 0x00, 0x00, 0x00, // additional tags count (zero means no tags) | ||||
|  | ||||
|                     /* | ||||
|                         // whole file duration (not implemented) | ||||
|                         0x44,// tag string size | ||||
|                         0x55, 0x52, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x3D, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, | ||||
|                         0x30, 0x2E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 | ||||
|                      */ | ||||
|                     0x0F, // tag string size | ||||
|                     0x00, 0x00, 0x00, 0x45, 0x4E, 0x43, 0x4F, 0x44, 0x45, 0x52, 0x3D, // "ENCODER=" binary string | ||||
|                     0x4E, 0x65, 0x77, 0x50, 0x69, 0x70, 0x65, // "NewPipe" binary string | ||||
|                     0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ???????? | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         // not implemented for the desired codec | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     private void write(final ByteBuffer buffer) throws IOException { | ||||
|         output.write(buffer.array(), 0, buffer.position()); | ||||
|         buffer.position(0); | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     private SimpleBlock getNextBlock() throws IOException { | ||||
|         SimpleBlock res; | ||||
|  | ||||
|         if (webmBlock != null) { | ||||
|             res = webmBlock; | ||||
|             webmBlock = null; | ||||
|             return res; | ||||
|         } | ||||
|  | ||||
|         if (webmSegment == null) { | ||||
|             webmSegment = webm.getNextSegment(); | ||||
|             if (webmSegment == null) { | ||||
|                 return null; // no more blocks in the selected track | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (webmCluster == null) { | ||||
|             webmCluster = webmSegment.getNextCluster(); | ||||
|             if (webmCluster == null) { | ||||
|                 webmSegment = null; | ||||
|                 return getNextBlock(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         res = webmCluster.getNextSimpleBlock(); | ||||
|         if (res == null) { | ||||
|             webmCluster = null; | ||||
|             return getNextBlock(); | ||||
|         } | ||||
|  | ||||
|         webmBlockNearDuration = res.absoluteTimeCodeNs - webmBlockLastTimecode; | ||||
|         webmBlockLastTimecode = res.absoluteTimeCodeNs; | ||||
|  | ||||
|         return res; | ||||
|     } | ||||
|  | ||||
|     private float getSampleFrequencyFromTrack(final byte[] bMetadata) { | ||||
|         // hardcoded way | ||||
|         ByteBuffer buffer = ByteBuffer.wrap(bMetadata); | ||||
|  | ||||
|         while (buffer.remaining() >= 6) { | ||||
|             int id = buffer.getShort() & 0xFFFF; | ||||
|             if (id == 0x0000B584) { | ||||
|                 return buffer.getFloat(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return 0f; | ||||
|     } | ||||
|  | ||||
|     private void clearSegmentTable() { | ||||
|         segmentTableNextTimestamp += TIME_SCALE_NS; | ||||
|         packetFlag = FLAG_UNSET; | ||||
|         segmentTableSize = 0; | ||||
|     } | ||||
|  | ||||
|     private boolean addPacketSegment(final SimpleBlock block) { | ||||
|         long timestamp = block.absoluteTimeCodeNs + webmTrack.codecDelay; | ||||
|  | ||||
|         if (timestamp >= segmentTableNextTimestamp) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return addPacketSegment(block.dataSize); | ||||
|     } | ||||
|  | ||||
|     private boolean addPacketSegment(int size) { | ||||
|         if (size > 65025) { | ||||
|             throw new UnsupportedOperationException("page size cannot be larger than 65025"); | ||||
|         } | ||||
|  | ||||
|         int available = (segmentTable.length - segmentTableSize) * 255; | ||||
|         boolean extra = (size % 255) == 0; | ||||
|  | ||||
|         if (extra) { | ||||
|             // add a zero byte entry in the table | ||||
|             // required to indicate the sample size is multiple of 255 | ||||
|             available -= 255; | ||||
|         } | ||||
|  | ||||
|         // check if possible add the segment, without overflow the table | ||||
|         if (available < size) { | ||||
|             return false; // not enough space on the page | ||||
|         } | ||||
|  | ||||
|         for (; size > 0; size -= 255) { | ||||
|             segmentTable[segmentTableSize++] = (byte) Math.min(size, 255); | ||||
|         } | ||||
|  | ||||
|         if (extra) { | ||||
|             segmentTable[segmentTableSize++] = 0x00; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     private void populateCrc32Table() { | ||||
|         for (int i = 0; i < 0x100; i++) { | ||||
|             int crc = i << 24; | ||||
|             for (int j = 0; j < 8; j++) { | ||||
|                 long b = crc >>> 31; | ||||
|                 crc <<= 1; | ||||
|                 crc ^= (int) (0x100000000L - b) & 0x04c11db7; | ||||
|             } | ||||
|             crc32Table[i] = crc; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private int calcCrc32(int initialCrc, final byte[] buffer, final int size) { | ||||
|         for (int i = 0; i < size; i++) { | ||||
|             int reg = (initialCrc >>> 24) & 0xff; | ||||
|             initialCrc = (initialCrc << 8) ^ crc32Table[reg ^ (buffer[i] & 0xff)]; | ||||
|         } | ||||
|  | ||||
|         return initialCrc; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -26,18 +26,19 @@ public class SrtFromTtmlWriter { | ||||
|  | ||||
|     private int frameIndex = 0; | ||||
|  | ||||
|     public SrtFromTtmlWriter(SharpStream out, boolean ignoreEmptyFrames) { | ||||
|     public SrtFromTtmlWriter(final SharpStream out, final boolean ignoreEmptyFrames) { | ||||
|         this.out = out; | ||||
|         this.ignoreEmptyFrames = ignoreEmptyFrames; | ||||
|     } | ||||
|  | ||||
|     private static String getTimestamp(Element frame, String attr) { | ||||
|     private static String getTimestamp(final Element frame, final String attr) { | ||||
|         return frame | ||||
|                 .attr(attr) | ||||
|                 .replace('.', ',');// SRT subtitles uses comma as decimal separator | ||||
|                 .replace('.', ','); // SRT subtitles uses comma as decimal separator | ||||
|     } | ||||
|  | ||||
|     private void writeFrame(String begin, String end, StringBuilder text) throws IOException { | ||||
|     private void writeFrame(final String begin, final String end, final StringBuilder text) | ||||
|             throws IOException { | ||||
|         writeString(String.valueOf(frameIndex++)); | ||||
|         writeString(NEW_LINE); | ||||
|         writeString(begin); | ||||
| @@ -49,11 +50,11 @@ public class SrtFromTtmlWriter { | ||||
|         writeString(NEW_LINE); | ||||
|     } | ||||
|  | ||||
|     private void writeString(String text) throws IOException { | ||||
|     private void writeString(final String text) throws IOException { | ||||
|         out.write(text.getBytes(charset)); | ||||
|     } | ||||
|  | ||||
|     public void build(SharpStream ttml) throws IOException { | ||||
|     public void build(final SharpStream ttml) throws IOException { | ||||
|         /* | ||||
|          * TTML parser with BASIC support | ||||
|          * multiple CUE is not supported | ||||
| @@ -66,25 +67,32 @@ public class SrtFromTtmlWriter { | ||||
|         // parse XML | ||||
|         byte[] buffer = new byte[(int) ttml.available()]; | ||||
|         ttml.read(buffer); | ||||
|         Document doc = Jsoup.parse(new ByteArrayInputStream(buffer), "UTF-8", "", Parser.xmlParser()); | ||||
|         Document doc = Jsoup.parse(new ByteArrayInputStream(buffer), "UTF-8", "", | ||||
|                 Parser.xmlParser()); | ||||
|  | ||||
|         StringBuilder text = new StringBuilder(128); | ||||
|         Elements paragraph_list = doc.select("body > div > p"); | ||||
|         Elements paragraphList = doc.select("body > div > p"); | ||||
|  | ||||
|         // check if has frames | ||||
|         if (paragraph_list.size() < 1) return; | ||||
|         if (paragraphList.size() < 1) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         for (Element paragraph : paragraph_list) { | ||||
|         for (Element paragraph : paragraphList) { | ||||
|             text.setLength(0); | ||||
|  | ||||
|             for (Node children : paragraph.childNodes()) { | ||||
|                 if (children instanceof TextNode) | ||||
|                 if (children instanceof TextNode) { | ||||
|                     text.append(((TextNode) children).text()); | ||||
|                 else if (children instanceof Element && ((Element) children).tagName().equalsIgnoreCase("br")) | ||||
|                 } else if (children instanceof Element | ||||
|                         && ((Element) children).tagName().equalsIgnoreCase("br")) { | ||||
|                     text.append(NEW_LINE); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (ignoreEmptyFrames && text.length() < 1) continue; | ||||
|             if (ignoreEmptyFrames && text.length() < 1) { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             String begin = getTimestamp(paragraph, "begin"); | ||||
|             String end = getTimestamp(paragraph, "end"); | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,63 +1,62 @@ | ||||
| package org.schabi.newpipe.streams.io; | ||||
|  | ||||
| import java.io.Closeable; | ||||
| import java.io.IOException; | ||||
|  | ||||
| /** | ||||
|  * based on c# | ||||
|  */ | ||||
| public abstract class SharpStream  implements Closeable { | ||||
|  | ||||
|     public abstract int read() throws IOException; | ||||
|  | ||||
|     public abstract int read(byte buffer[]) throws IOException; | ||||
|  | ||||
|     public abstract int read(byte buffer[], int offset, int count) throws IOException; | ||||
|  | ||||
|     public abstract long skip(long amount) throws IOException; | ||||
|  | ||||
|     public abstract long available(); | ||||
|  | ||||
|     public abstract void rewind() throws IOException; | ||||
|  | ||||
|     public abstract boolean isClosed(); | ||||
|  | ||||
|     @Override | ||||
|     public abstract void close(); | ||||
|  | ||||
|     public abstract boolean canRewind(); | ||||
|  | ||||
|     public abstract boolean canRead(); | ||||
|  | ||||
|     public abstract boolean canWrite(); | ||||
|  | ||||
|     public boolean canSetLength() { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public boolean canSeek() { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public abstract void write(byte value) throws IOException; | ||||
|  | ||||
|     public abstract void write(byte[] buffer) throws IOException; | ||||
|  | ||||
|     public abstract void write(byte[] buffer, int offset, int count) throws IOException; | ||||
|  | ||||
|     public void flush() throws IOException { | ||||
|         // STUB | ||||
|     } | ||||
|  | ||||
|     public void setLength(long length) throws IOException { | ||||
|         throw new IOException("Not implemented"); | ||||
|     } | ||||
|  | ||||
|     public void seek(long offset) throws IOException { | ||||
|         throw new IOException("Not implemented"); | ||||
|     } | ||||
|  | ||||
|     public long length() throws IOException { | ||||
|         throw new UnsupportedOperationException("Unsupported operation"); | ||||
|     } | ||||
| } | ||||
| package org.schabi.newpipe.streams.io; | ||||
|  | ||||
| import java.io.Closeable; | ||||
| import java.io.IOException; | ||||
|  | ||||
| /** | ||||
|  * Based on C#'s Stream class. | ||||
|  */ | ||||
| public abstract class SharpStream  implements Closeable { | ||||
|     public abstract int read() throws IOException; | ||||
|  | ||||
|     public abstract int read(byte[] buffer) throws IOException; | ||||
|  | ||||
|     public abstract int read(byte[] buffer, int offset, int count) throws IOException; | ||||
|  | ||||
|     public abstract long skip(long amount) throws IOException; | ||||
|  | ||||
|     public abstract long available(); | ||||
|  | ||||
|     public abstract void rewind() throws IOException; | ||||
|  | ||||
|     public abstract boolean isClosed(); | ||||
|  | ||||
|     @Override | ||||
|     public abstract void close(); | ||||
|  | ||||
|     public abstract boolean canRewind(); | ||||
|  | ||||
|     public abstract boolean canRead(); | ||||
|  | ||||
|     public abstract boolean canWrite(); | ||||
|  | ||||
|     public boolean canSetLength() { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public boolean canSeek() { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public abstract void write(byte value) throws IOException; | ||||
|  | ||||
|     public abstract void write(byte[] buffer) throws IOException; | ||||
|  | ||||
|     public abstract void write(byte[] buffer, int offset, int count) throws IOException; | ||||
|  | ||||
|     public void flush() throws IOException { | ||||
|         // STUB | ||||
|     } | ||||
|  | ||||
|     public void setLength(final long length) throws IOException { | ||||
|         throw new IOException("Not implemented"); | ||||
|     } | ||||
|  | ||||
|     public void seek(final long offset) throws IOException { | ||||
|         throw new IOException("Not implemented"); | ||||
|     } | ||||
|  | ||||
|     public long length() throws IOException { | ||||
|         throw new UnsupportedOperationException("Unsupported operation"); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -6,7 +6,38 @@ | ||||
|   <suppress checks="FinalParameters" | ||||
|     files="LocalItemListAdapter.java" | ||||
|     lines="220,292"/> | ||||
|  | ||||
|   <suppress checks="FinalParameters" | ||||
|     files="InfoListAdapter.java" | ||||
|     lines="253,325"/> | ||||
|  | ||||
|   <!-- org.schabi.newpipe.streams --> | ||||
|   <suppress checks="FinalParameters" | ||||
|     files="WebMWriter.java" | ||||
|     lines="423,595"/> | ||||
|   <suppress checks="LineLength" | ||||
|     files="WebMWriter.java" | ||||
|     lines="160,162"/> | ||||
|  | ||||
|   <suppress checks="FinalParameters" | ||||
|     files="OggFromWebMWriter.java" | ||||
|     lines="378,420"/> | ||||
|   <suppress checks="LineLength" | ||||
|     files="OggFromWebMWriter.java" | ||||
|     lines="292,296"/> | ||||
|  | ||||
|   <suppress checks="FinalParameters" | ||||
|     files="Mp4FromDashWriter.java" | ||||
|     lines="643"/> | ||||
|   <suppress checks="LineLength" | ||||
|     files="Mp4FromDashWriter.java" | ||||
|     lines="677,678,720-724,738,762,848,850-855"/> | ||||
|  | ||||
|   <suppress checks="InnerAssignment" | ||||
|     files="Mp4DashReader.java" | ||||
|     lines="190"/> | ||||
|  | ||||
|   <suppress checks="FinalParameters" | ||||
|     files="DataReader.java" | ||||
|     lines="46,93"/> | ||||
| </suppressions> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 wb9688
					wb9688