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 '**/R.java' | ||||||
|     exclude '**/BuildConfig.java' |     exclude '**/BuildConfig.java' | ||||||
|     exclude 'main/java/us/shandian/giga/**' |     exclude 'main/java/us/shandian/giga/**' | ||||||
|     exclude 'main/java/org/schabi/newpipe/streams/**' |  | ||||||
|  |  | ||||||
|     // empty classpath |     // empty classpath | ||||||
|     classpath = files() |     classpath = files() | ||||||
|   | |||||||
| @@ -10,13 +10,12 @@ import java.io.InputStream; | |||||||
|  * @author kapodamy |  * @author kapodamy | ||||||
|  */ |  */ | ||||||
| public class DataReader { | 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; |     private static final int BUFFER_SIZE = 128 * 1024; // 128 KiB | ||||||
|     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 long position = 0; |     private long position = 0; | ||||||
|     private final SharpStream stream; |     private final SharpStream stream; | ||||||
| @@ -24,7 +23,7 @@ public class DataReader { | |||||||
|     private InputStream view; |     private InputStream view; | ||||||
|     private int viewSize; |     private int viewSize; | ||||||
|  |  | ||||||
|     public DataReader(SharpStream stream) { |     public DataReader(final SharpStream stream) { | ||||||
|         this.stream = stream; |         this.stream = stream; | ||||||
|         this.readOffset = this.readBuffer.length; |         this.readOffset = this.readBuffer.length; | ||||||
|     } |     } | ||||||
| @@ -74,6 +73,7 @@ public class DataReader { | |||||||
|         return value & 0xffffffffL; |         return value & 0xffffffffL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     public short readShort() throws IOException { |     public short readShort() throws IOException { | ||||||
|         primitiveRead(SHORT_SIZE); |         primitiveRead(SHORT_SIZE); | ||||||
|         return (short) (primitive[0] << 8 | primitive[1]); |         return (short) (primitive[0] << 8 | primitive[1]); | ||||||
| @@ -86,11 +86,11 @@ public class DataReader { | |||||||
|         return high << 32 | low; |         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); |         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) { |         if (readCount < 0) { | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
| @@ -157,7 +157,7 @@ public class DataReader { | |||||||
|      * @param size the size of the view |      * @param size the size of the view | ||||||
|      * @return the view |      * @return the view | ||||||
|      */ |      */ | ||||||
|     public InputStream getView(int size) { |     public InputStream getView(final int size) { | ||||||
|         if (view == null) { |         if (view == null) { | ||||||
|             view = new InputStream() { |             view = new InputStream() { | ||||||
|                 @Override |                 @Override | ||||||
| @@ -173,12 +173,13 @@ public class DataReader { | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 @Override |                 @Override | ||||||
|                 public int read(byte[] buffer) throws IOException { |                 public int read(final byte[] buffer) throws IOException { | ||||||
|                     return read(buffer, 0, buffer.length); |                     return read(buffer, 0, buffer.length); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 @Override |                 @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) { |                     if (viewSize < 1) { | ||||||
|                         return -1; |                         return -1; | ||||||
|                     } |                     } | ||||||
| @@ -190,7 +191,7 @@ public class DataReader { | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 @Override |                 @Override | ||||||
|                 public long skip(long amount) throws IOException { |                 public long skip(final long amount) throws IOException { | ||||||
|                     if (viewSize < 1) { |                     if (viewSize < 1) { | ||||||
|                         return 0; |                         return 0; | ||||||
|                     } |                     } | ||||||
| @@ -224,16 +225,18 @@ public class DataReader { | |||||||
|  |  | ||||||
|     private final short[] primitive = new short[LONG_SIZE]; |     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]; |         byte[] buffer = new byte[amount]; | ||||||
|         int read = read(buffer, 0, amount); |         int read = read(buffer, 0, amount); | ||||||
|  |  | ||||||
|         if (read != 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++) { |         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; |         return readCount < 1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -14,7 +14,6 @@ import java.util.NoSuchElementException; | |||||||
|  * @author kapodamy |  * @author kapodamy | ||||||
|  */ |  */ | ||||||
| public class Mp4DashReader { | public class Mp4DashReader { | ||||||
|  |  | ||||||
|     private static final int ATOM_MOOF = 0x6D6F6F66; |     private static final int ATOM_MOOF = 0x6D6F6F66; | ||||||
|     private static final int ATOM_MFHD = 0x6D666864; |     private static final int ATOM_MFHD = 0x6D666864; | ||||||
|     private static final int ATOM_TRAF = 0x74726166; |     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_SOUN = 0x736F756E; | ||||||
|     private static final int HANDLER_SUBT = 0x73756274; |     private static final int HANDLER_SUBT = 0x73756274; | ||||||
|  |  | ||||||
|  |  | ||||||
|     private final DataReader stream; |     private final DataReader stream; | ||||||
|  |  | ||||||
|     private Mp4Track[] tracks = null; |     private Mp4Track[] tracks = null; | ||||||
| @@ -68,7 +66,7 @@ public class Mp4DashReader { | |||||||
|         Audio, Video, Subtitles, Other |         Audio, Video, Subtitles, Other | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public Mp4DashReader(SharpStream source) { |     public Mp4DashReader(final SharpStream source) { | ||||||
|         this.stream = new DataReader(source); |         this.stream = new DataReader(source); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -78,14 +76,15 @@ public class Mp4DashReader { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         box = readBox(ATOM_FTYP); |         box = readBox(ATOM_FTYP); | ||||||
|         brands = parse_ftyp(box); |         brands = parseFtyp(box); | ||||||
|         switch (brands[0]) { |         switch (brands[0]) { | ||||||
|             case BRAND_DASH: |             case BRAND_DASH: | ||||||
|             case BRAND_ISO5:// ¿why not? |             case BRAND_ISO5:// ¿why not? | ||||||
|                 break; |                 break; | ||||||
|             default: |             default: | ||||||
|                 throw new NoSuchElementException( |                 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) { |             switch (box.type) { | ||||||
|                 case ATOM_MOOV: |                 case ATOM_MOOV: | ||||||
|                     moov = parse_moov(box); |                     moov = parseMoov(box); | ||||||
|                     break; |                     break; | ||||||
|                 case ATOM_SIDX: |                 case ATOM_SIDX: | ||||||
|                     break; |                     break; | ||||||
| @@ -117,10 +116,10 @@ public class Mp4DashReader { | |||||||
|             tracks[i] = new Mp4Track(); |             tracks[i] = new Mp4Track(); | ||||||
|             tracks[i].trak = moov.trak[i]; |             tracks[i].trak = moov.trak[i]; | ||||||
|  |  | ||||||
|             if (moov.mvex_trex != null) { |             if (moov.mvexTrex != null) { | ||||||
|                 for (Trex mvex_trex : moov.mvex_trex) { |                 for (Trex mvexTrex : moov.mvexTrex) { | ||||||
|                     if (tracks[i].trak.tkhd.trackId == mvex_trex.trackId) { |                     if (tracks[i].trak.tkhd.trackId == mvexTrex.trackId) { | ||||||
|                         tracks[i].trex = mvex_trex; |                         tracks[i].trex = mvexTrex; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @@ -144,7 +143,7 @@ public class Mp4DashReader { | |||||||
|         backupBox = box; |         backupBox = box; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     Mp4Track selectTrack(int index) { |     Mp4Track selectTrack(final int index) { | ||||||
|         selectedTrack = index; |         selectedTrack = index; | ||||||
|         return tracks[index]; |         return tracks[index]; | ||||||
|     } |     } | ||||||
| @@ -179,7 +178,7 @@ public class Mp4DashReader { | |||||||
|                 Box traf; |                 Box traf; | ||||||
|                 while ((traf = untilBox(tmp, ATOM_TRAF)) != null) { |                 while ((traf = untilBox(tmp, ATOM_TRAF)) != null) { | ||||||
|                     Box tfhd = readBox(ATOM_TFHD); |                     Box tfhd = readBox(ATOM_TFHD); | ||||||
|                     if (parse_tfhd(tracks[selectedTrack].trak.tkhd.trackId) != null) { |                     if (parseTfhd(tracks[selectedTrack].trak.tkhd.trackId) != null) { | ||||||
|                         count++; |                         count++; | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
| @@ -196,7 +195,9 @@ public class Mp4DashReader { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public int[] getBrands() { |     public int[] getBrands() { | ||||||
|         if (brands == null) throw new IllegalStateException("Not parsed"); |         if (brands == null) { | ||||||
|  |             throw new IllegalStateException("Not parsed"); | ||||||
|  |         } | ||||||
|         return brands; |         return brands; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -219,7 +220,7 @@ public class Mp4DashReader { | |||||||
|         return tracks; |         return tracks; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public Mp4DashChunk getNextChunk(boolean infoOnly) throws IOException { |     public Mp4DashChunk getNextChunk(final boolean infoOnly) throws IOException { | ||||||
|         Mp4Track track = tracks[selectedTrack]; |         Mp4Track track = tracks[selectedTrack]; | ||||||
|  |  | ||||||
|         while (stream.available()) { |         while (stream.available()) { | ||||||
| @@ -240,27 +241,31 @@ public class Mp4DashReader { | |||||||
|                         throw new IOException("moof found without mdat"); |                         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 (moof.traf != null) { | ||||||
|  |  | ||||||
|                         if (hasFlag(moof.traf.trun.bFlags, 0x0001)) { |                         if (hasFlag(moof.traf.trun.bFlags, 0x0001)) { | ||||||
|                             moof.traf.trun.dataOffset -= box.size + 8; |                             moof.traf.trun.dataOffset -= box.size + 8; | ||||||
|                             if (moof.traf.trun.dataOffset < 0) { |                             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 (moof.traf.trun.chunkSize < 1) { | ||||||
|                             if (hasFlag(moof.traf.tfhd.bFlags, 0x10)) { |                             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 { |                             } else { | ||||||
|                                 moof.traf.trun.chunkSize = (int) (box.size - 8); |                                 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)) { |                             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; | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
| @@ -292,17 +297,15 @@ public class Mp4DashReader { | |||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public static boolean hasFlag(final int flags, final int mask) { | ||||||
|  |  | ||||||
|     public static boolean hasFlag(int flags, int mask) { |  | ||||||
|         return (flags & mask) == mask; |         return (flags & mask) == mask; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private String boxName(Box ref) { |     private String boxName(final Box ref) { | ||||||
|         return boxName(ref.type); |         return boxName(ref.type); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private String boxName(int type) { |     private String boxName(final int type) { | ||||||
|         try { |         try { | ||||||
|             return new String(ByteBuffer.allocate(4).putInt(type).array(), "UTF-8"); |             return new String(ByteBuffer.allocate(4).putInt(type).array(), "UTF-8"); | ||||||
|         } catch (UnsupportedEncodingException e) { |         } catch (UnsupportedEncodingException e) { | ||||||
| @@ -323,15 +326,16 @@ public class Mp4DashReader { | |||||||
|         return b; |         return b; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Box readBox(int expected) throws IOException { |     private Box readBox(final int expected) throws IOException { | ||||||
|         Box b = readBox(); |         Box b = readBox(); | ||||||
|         if (b.type != expected) { |         if (b.type != expected) { | ||||||
|             throw new NoSuchElementException("expected " + boxName(expected) + " found " + boxName(b)); |             throw new NoSuchElementException("expected " + boxName(expected) | ||||||
|  |                     + " found " + boxName(b)); | ||||||
|         } |         } | ||||||
|         return 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 |         // full box reading is limited to 2 GiB, and should be enough | ||||||
|         int size = (int) ref.size; |         int size = (int) ref.size; | ||||||
|  |  | ||||||
| @@ -342,15 +346,14 @@ public class Mp4DashReader { | |||||||
|         int read = size - 8; |         int read = size - 8; | ||||||
|  |  | ||||||
|         if (stream.read(buffer.array(), 8, read) != read) { |         if (stream.read(buffer.array(), 8, read) != read) { | ||||||
|             throw new EOFException( |             throw new EOFException(String.format("EOF reached in box: type=%s offset=%s size=%s", | ||||||
|                     String.format("EOF reached in box: type=%s offset=%s size=%s", boxName(ref.type), ref.offset, ref.size) |                     boxName(ref.type), ref.offset, ref.size)); | ||||||
|             ); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return buffer.array(); |         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(); |         long skip = ref.offset + ref.size - stream.position(); | ||||||
|  |  | ||||||
|         if (skip == 0) { |         if (skip == 0) { | ||||||
| @@ -365,7 +368,7 @@ public class Mp4DashReader { | |||||||
|         stream.skipBytes((int) skip); |         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; |         Box b; | ||||||
|         while (stream.position() < (ref.offset + ref.size)) { |         while (stream.position() < (ref.offset + ref.size)) { | ||||||
|             b = readBox(); |             b = readBox(); | ||||||
| @@ -380,7 +383,7 @@ public class Mp4DashReader { | |||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Box untilAnyBox(Box ref) throws IOException { |     private Box untilAnyBox(final Box ref) throws IOException { | ||||||
|         if (stream.position() >= (ref.offset + ref.size)) { |         if (stream.position() >= (ref.offset + ref.size)) { | ||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
| @@ -388,17 +391,15 @@ public class Mp4DashReader { | |||||||
|         return readBox(); |         return readBox(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private Moof parseMoof(final Box ref, final int trackId) throws IOException { | ||||||
|  |  | ||||||
|     private Moof parse_moof(Box ref, int trackId) throws IOException { |  | ||||||
|         Moof obj = new Moof(); |         Moof obj = new Moof(); | ||||||
|  |  | ||||||
|         Box b = readBox(ATOM_MFHD); |         Box b = readBox(ATOM_MFHD); | ||||||
|         obj.mfhd_SequenceNumber = parse_mfhd(); |         obj.mfhdSequenceNumber = parseMfhd(); | ||||||
|         ensure(b); |         ensure(b); | ||||||
|  |  | ||||||
|         while ((b = untilBox(ref, ATOM_TRAF)) != null) { |         while ((b = untilBox(ref, ATOM_TRAF)) != null) { | ||||||
|             obj.traf = parse_traf(b, trackId); |             obj.traf = parseTraf(b, trackId); | ||||||
|             ensure(b); |             ensure(b); | ||||||
|  |  | ||||||
|             if (obj.traf != null) { |             if (obj.traf != null) { | ||||||
| @@ -409,7 +410,7 @@ public class Mp4DashReader { | |||||||
|         return obj; |         return obj; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private int parse_mfhd() throws IOException { |     private int parseMfhd() throws IOException { | ||||||
|         // version |         // version | ||||||
|         // flags |         // flags | ||||||
|         stream.skipBytes(4); |         stream.skipBytes(4); | ||||||
| @@ -417,11 +418,11 @@ public class Mp4DashReader { | |||||||
|         return stream.readInt(); |         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(); |         Traf traf = new Traf(); | ||||||
|  |  | ||||||
|         Box b = readBox(ATOM_TFHD); |         Box b = readBox(ATOM_TFHD); | ||||||
|         traf.tfhd = parse_tfhd(trackId); |         traf.tfhd = parseTfhd(trackId); | ||||||
|         ensure(b); |         ensure(b); | ||||||
|  |  | ||||||
|         if (traf.tfhd == null) { |         if (traf.tfhd == null) { | ||||||
| @@ -431,18 +432,18 @@ public class Mp4DashReader { | |||||||
|         b = untilBox(ref, ATOM_TRUN, ATOM_TFDT); |         b = untilBox(ref, ATOM_TRUN, ATOM_TFDT); | ||||||
|  |  | ||||||
|         if (b.type == ATOM_TFDT) { |         if (b.type == ATOM_TFDT) { | ||||||
|             traf.tfdt = parse_tfdt(); |             traf.tfdt = parseTfdt(); | ||||||
|             ensure(b); |             ensure(b); | ||||||
|             b = readBox(ATOM_TRUN); |             b = readBox(ATOM_TRUN); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         traf.trun = parse_trun(); |         traf.trun = parseTrun(); | ||||||
|         ensure(b); |         ensure(b); | ||||||
|  |  | ||||||
|         return traf; |         return traf; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Tfhd parse_tfhd(int trackId) throws IOException { |     private Tfhd parseTfhd(final int trackId) throws IOException { | ||||||
|         Tfhd obj = new Tfhd(); |         Tfhd obj = new Tfhd(); | ||||||
|  |  | ||||||
|         obj.bFlags = stream.readInt(); |         obj.bFlags = stream.readInt(); | ||||||
| @@ -471,31 +472,31 @@ public class Mp4DashReader { | |||||||
|         return obj; |         return obj; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private long parse_tfdt() throws IOException { |     private long parseTfdt() throws IOException { | ||||||
|         int version = stream.read(); |         int version = stream.read(); | ||||||
|         stream.skipBytes(3); // flags |         stream.skipBytes(3); // flags | ||||||
|         return version == 0 ? stream.readUnsignedInt() : stream.readLong(); |         return version == 0 ? stream.readUnsignedInt() : stream.readLong(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Trun parse_trun() throws IOException { |     private Trun parseTrun() throws IOException { | ||||||
|         Trun obj = new Trun(); |         Trun obj = new Trun(); | ||||||
|         obj.bFlags = stream.readInt(); |         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)) { |         if (hasFlag(obj.bFlags, 0x0100)) { | ||||||
|             obj.entries_rowSize += 4; |             obj.entriesRowSize += 4; | ||||||
|         } |         } | ||||||
|         if (hasFlag(obj.bFlags, 0x0200)) { |         if (hasFlag(obj.bFlags, 0x0200)) { | ||||||
|             obj.entries_rowSize += 4; |             obj.entriesRowSize += 4; | ||||||
|         } |         } | ||||||
|         if (hasFlag(obj.bFlags, 0x0400)) { |         if (hasFlag(obj.bFlags, 0x0400)) { | ||||||
|             obj.entries_rowSize += 4; |             obj.entriesRowSize += 4; | ||||||
|         } |         } | ||||||
|         if (hasFlag(obj.bFlags, 0x0800)) { |         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)) { |         if (hasFlag(obj.bFlags, 0x0001)) { | ||||||
|             obj.dataOffset = stream.readInt(); |             obj.dataOffset = stream.readInt(); | ||||||
| @@ -524,7 +525,7 @@ public class Mp4DashReader { | |||||||
|         return obj; |         return obj; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private int[] parse_ftyp(Box ref) throws IOException { |     private int[] parseFtyp(final Box ref) throws IOException { | ||||||
|         int i = 0; |         int i = 0; | ||||||
|         int[] list = new int[(int) ((ref.offset + ref.size - stream.position() - 4) / 4)]; |         int[] list = new int[(int) ((ref.offset + ref.size - stream.position() - 4) / 4)]; | ||||||
|  |  | ||||||
| @@ -532,13 +533,14 @@ public class Mp4DashReader { | |||||||
|  |  | ||||||
|         stream.skipBytes(4); // minor version |         stream.skipBytes(4); // minor version | ||||||
|  |  | ||||||
|         for (; i < list.length; i++) |         for (; i < list.length; i++) { | ||||||
|             list[i] = stream.readInt(); // compatible brands |             list[i] = stream.readInt(); // compatible brands | ||||||
|  |         } | ||||||
|  |  | ||||||
|         return list; |         return list; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Mvhd parse_mvhd() throws IOException { |     private Mvhd parseMvhd() throws IOException { | ||||||
|         int version = stream.read(); |         int version = stream.read(); | ||||||
|         stream.skipBytes(3); // flags |         stream.skipBytes(3); // flags | ||||||
|  |  | ||||||
| @@ -564,7 +566,7 @@ public class Mp4DashReader { | |||||||
|         return obj; |         return obj; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Tkhd parse_tkhd() throws IOException { |     private Tkhd parseTkhd() throws IOException { | ||||||
|         int version = stream.read(); |         int version = stream.read(); | ||||||
|  |  | ||||||
|         Tkhd obj = new Tkhd(); |         Tkhd obj = new Tkhd(); | ||||||
| @@ -597,20 +599,20 @@ public class Mp4DashReader { | |||||||
|         return obj; |         return obj; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Trak parse_trak(Box ref) throws IOException { |     private Trak parseTrak(final Box ref) throws IOException { | ||||||
|         Trak trak = new Trak(); |         Trak trak = new Trak(); | ||||||
|  |  | ||||||
|         Box b = readBox(ATOM_TKHD); |         Box b = readBox(ATOM_TKHD); | ||||||
|         trak.tkhd = parse_tkhd(); |         trak.tkhd = parseTkhd(); | ||||||
|         ensure(b); |         ensure(b); | ||||||
|  |  | ||||||
|         while ((b = untilBox(ref, ATOM_MDIA, ATOM_EDTS)) != null) { |         while ((b = untilBox(ref, ATOM_MDIA, ATOM_EDTS)) != null) { | ||||||
|             switch (b.type) { |             switch (b.type) { | ||||||
|                 case ATOM_MDIA: |                 case ATOM_MDIA: | ||||||
|                     trak.mdia = parse_mdia(b); |                     trak.mdia = parseMdia(b); | ||||||
|                     break; |                     break; | ||||||
|                 case ATOM_EDTS: |                 case ATOM_EDTS: | ||||||
|                     trak.edst_elst = parse_edts(b); |                     trak.edstElst = parseEdts(b); | ||||||
|                     break; |                     break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -620,7 +622,7 @@ public class Mp4DashReader { | |||||||
|         return trak; |         return trak; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Mdia parse_mdia(Box ref) throws IOException { |     private Mdia parseMdia(final Box ref) throws IOException { | ||||||
|         Mdia obj = new Mdia(); |         Mdia obj = new Mdia(); | ||||||
|  |  | ||||||
|         Box b; |         Box b; | ||||||
| @@ -633,13 +635,13 @@ public class Mp4DashReader { | |||||||
|                     ByteBuffer buffer = ByteBuffer.wrap(obj.mdhd); |                     ByteBuffer buffer = ByteBuffer.wrap(obj.mdhd); | ||||||
|                     byte version = buffer.get(8); |                     byte version = buffer.get(8); | ||||||
|                     buffer.position(12 + ((version == 0 ? 4 : 8) * 2)); |                     buffer.position(12 + ((version == 0 ? 4 : 8) * 2)); | ||||||
|                     obj.mdhd_timeScale = buffer.getInt(); |                     obj.mdhdTimeScale = buffer.getInt(); | ||||||
|                     break; |                     break; | ||||||
|                 case ATOM_HDLR: |                 case ATOM_HDLR: | ||||||
|                     obj.hdlr = parse_hdlr(b); |                     obj.hdlr = parseHdlr(b); | ||||||
|                     break; |                     break; | ||||||
|                 case ATOM_MINF: |                 case ATOM_MINF: | ||||||
|                     obj.minf = parse_minf(b); |                     obj.minf = parseMinf(b); | ||||||
|                     break; |                     break; | ||||||
|             } |             } | ||||||
|             ensure(b); |             ensure(b); | ||||||
| @@ -648,7 +650,7 @@ public class Mp4DashReader { | |||||||
|         return obj; |         return obj; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Hdlr parse_hdlr(Box ref) throws IOException { |     private Hdlr parseHdlr(final Box ref) throws IOException { | ||||||
|         // version |         // version | ||||||
|         // flags |         // flags | ||||||
|         stream.skipBytes(4); |         stream.skipBytes(4); | ||||||
| @@ -666,10 +668,10 @@ public class Mp4DashReader { | |||||||
|         return obj; |         return obj; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Moov parse_moov(Box ref) throws IOException { |     private Moov parseMoov(final Box ref) throws IOException { | ||||||
|         Box b = readBox(ATOM_MVHD); |         Box b = readBox(ATOM_MVHD); | ||||||
|         Moov moov = new Moov(); |         Moov moov = new Moov(); | ||||||
|         moov.mvhd = parse_mvhd(); |         moov.mvhd = parseMvhd(); | ||||||
|         ensure(b); |         ensure(b); | ||||||
|  |  | ||||||
|         ArrayList<Trak> tmp = new ArrayList<>((int) moov.mvhd.nextTrackId); |         ArrayList<Trak> tmp = new ArrayList<>((int) moov.mvhd.nextTrackId); | ||||||
| @@ -677,10 +679,10 @@ public class Mp4DashReader { | |||||||
|  |  | ||||||
|             switch (b.type) { |             switch (b.type) { | ||||||
|                 case ATOM_TRAK: |                 case ATOM_TRAK: | ||||||
|                     tmp.add(parse_trak(b)); |                     tmp.add(parseTrak(b)); | ||||||
|                     break; |                     break; | ||||||
|                 case ATOM_MVEX: |                 case ATOM_MVEX: | ||||||
|                     moov.mvex_trex = parse_mvex(b, (int) moov.mvhd.nextTrackId); |                     moov.mvexTrex = parseMvex(b, (int) moov.mvhd.nextTrackId); | ||||||
|                     break; |                     break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -692,19 +694,19 @@ public class Mp4DashReader { | |||||||
|         return moov; |         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); |         ArrayList<Trex> tmp = new ArrayList<>(possibleTrackCount); | ||||||
|  |  | ||||||
|         Box b; |         Box b; | ||||||
|         while ((b = untilBox(ref, ATOM_TREX)) != null) { |         while ((b = untilBox(ref, ATOM_TREX)) != null) { | ||||||
|             tmp.add(parse_trex()); |             tmp.add(parseTrex()); | ||||||
|             ensure(b); |             ensure(b); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return tmp.toArray(new Trex[0]); |         return tmp.toArray(new Trex[0]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Trex parse_trex() throws IOException { |     private Trex parseTrex() throws IOException { | ||||||
|         // version |         // version | ||||||
|         // flags |         // flags | ||||||
|         stream.skipBytes(4); |         stream.skipBytes(4); | ||||||
| @@ -719,7 +721,7 @@ public class Mp4DashReader { | |||||||
|         return obj; |         return obj; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Elst parse_edts(Box ref) throws IOException { |     private Elst parseEdts(final Box ref) throws IOException { | ||||||
|         Box b = untilBox(ref, ATOM_ELST); |         Box b = untilBox(ref, ATOM_ELST); | ||||||
|         if (b == null) { |         if (b == null) { | ||||||
|             return null; |             return null; | ||||||
| @@ -738,12 +740,12 @@ public class Mp4DashReader { | |||||||
|  |  | ||||||
|         if (v1) { |         if (v1) { | ||||||
|             stream.skipBytes(DataReader.LONG_SIZE); // segment duration |             stream.skipBytes(DataReader.LONG_SIZE); // segment duration | ||||||
|             obj.MediaTime = stream.readLong(); |             obj.mediaTime = stream.readLong(); | ||||||
|             // ignore all remain entries |             // ignore all remain entries | ||||||
|             stream.skipBytes((entryCount - 1) * (DataReader.LONG_SIZE * 2)); |             stream.skipBytes((entryCount - 1) * (DataReader.LONG_SIZE * 2)); | ||||||
|         } else { |         } else { | ||||||
|             stream.skipBytes(DataReader.INTEGER_SIZE); // segment duration |             stream.skipBytes(DataReader.INTEGER_SIZE); // segment duration | ||||||
|             obj.MediaTime = stream.readInt(); |             obj.mediaTime = stream.readInt(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         obj.bMediaRate = stream.readInt(); |         obj.bMediaRate = stream.readInt(); | ||||||
| @@ -751,7 +753,7 @@ public class Mp4DashReader { | |||||||
|         return obj; |         return obj; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Minf parse_minf(Box ref) throws IOException { |     private Minf parseMinf(final Box ref) throws IOException { | ||||||
|         Minf obj = new Minf(); |         Minf obj = new Minf(); | ||||||
|  |  | ||||||
|         Box b; |         Box b; | ||||||
| @@ -762,11 +764,11 @@ public class Mp4DashReader { | |||||||
|                     obj.dinf = readFullBox(b); |                     obj.dinf = readFullBox(b); | ||||||
|                     break; |                     break; | ||||||
|                 case ATOM_STBL: |                 case ATOM_STBL: | ||||||
|                     obj.stbl_stsd = parse_stbl(b); |                     obj.stblStsd = parseStbl(b); | ||||||
|                     break; |                     break; | ||||||
|                 case ATOM_VMHD: |                 case ATOM_VMHD: | ||||||
|                 case ATOM_SMHD: |                 case ATOM_SMHD: | ||||||
|                     obj.$mhd = readFullBox(b); |                     obj.mhd = readFullBox(b); | ||||||
|                     break; |                     break; | ||||||
|  |  | ||||||
|             } |             } | ||||||
| @@ -777,9 +779,12 @@ 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); |         Box b = untilBox(ref, ATOM_STSD); | ||||||
|  |  | ||||||
|         if (b == null) { |         if (b == null) { | ||||||
| @@ -789,30 +794,24 @@ public class Mp4DashReader { | |||||||
|         return readFullBox(b); |         return readFullBox(b); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     class Box { |     class Box { | ||||||
|  |  | ||||||
|         int type; |         int type; | ||||||
|         long offset; |         long offset; | ||||||
|         long size; |         long size; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Moof { |     public class Moof { | ||||||
|  |         int mfhdSequenceNumber; | ||||||
|         int mfhd_SequenceNumber; |  | ||||||
|         public Traf traf; |         public Traf traf; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Traf { |     public class Traf { | ||||||
|  |  | ||||||
|         public Tfhd tfhd; |         public Tfhd tfhd; | ||||||
|         long tfdt; |         long tfdt; | ||||||
|         public Trun trun; |         public Trun trun; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Tfhd { |     public class Tfhd { | ||||||
|  |  | ||||||
|         int bFlags; |         int bFlags; | ||||||
|         public int trackId; |         public int trackId; | ||||||
|         int defaultSampleDuration; |         int defaultSampleDuration; | ||||||
| @@ -821,7 +820,6 @@ public class Mp4DashReader { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     class TrunEntry { |     class TrunEntry { | ||||||
|  |  | ||||||
|         int sampleDuration; |         int sampleDuration; | ||||||
|         int sampleSize; |         int sampleSize; | ||||||
|         int sampleFlags; |         int sampleFlags; | ||||||
| @@ -833,7 +831,6 @@ public class Mp4DashReader { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Trun { |     public class Trun { | ||||||
|  |  | ||||||
|         public int chunkDuration; |         public int chunkDuration; | ||||||
|         public int chunkSize; |         public int chunkSize; | ||||||
|  |  | ||||||
| @@ -843,10 +840,10 @@ public class Mp4DashReader { | |||||||
|  |  | ||||||
|         public int entryCount; |         public int entryCount; | ||||||
|         byte[] bEntries; |         byte[] bEntries; | ||||||
|         int entries_rowSize; |         int entriesRowSize; | ||||||
|  |  | ||||||
|         public TrunEntry getEntry(int i) { |         public TrunEntry getEntry(final int i) { | ||||||
|             ByteBuffer buffer = ByteBuffer.wrap(bEntries, i * entries_rowSize, entries_rowSize); |             ByteBuffer buffer = ByteBuffer.wrap(bEntries, i * entriesRowSize, entriesRowSize); | ||||||
|             TrunEntry entry = new TrunEntry(); |             TrunEntry entry = new TrunEntry(); | ||||||
|  |  | ||||||
|             if (hasFlag(bFlags, 0x0100)) { |             if (hasFlag(bFlags, 0x0100)) { | ||||||
| @@ -868,7 +865,7 @@ public class Mp4DashReader { | |||||||
|             return entry; |             return entry; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public TrunEntry getAbsoluteEntry(int i, Tfhd header) { |         public TrunEntry getAbsoluteEntry(final int i, final Tfhd header) { | ||||||
|             TrunEntry entry = getEntry(i); |             TrunEntry entry = getEntry(i); | ||||||
|  |  | ||||||
|             if (!hasFlag(bFlags, 0x0100) && hasFlag(header.bFlags, 0x20)) { |             if (!hasFlag(bFlags, 0x0100) && hasFlag(header.bFlags, 0x20)) { | ||||||
| @@ -892,7 +889,6 @@ public class Mp4DashReader { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Tkhd { |     public class Tkhd { | ||||||
|  |  | ||||||
|         int trackId; |         int trackId; | ||||||
|         long duration; |         long duration; | ||||||
|         short bVolume; |         short bVolume; | ||||||
| @@ -904,28 +900,24 @@ public class Mp4DashReader { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Trak { |     public class Trak { | ||||||
|  |  | ||||||
|         public Tkhd tkhd; |         public Tkhd tkhd; | ||||||
|         public Elst edst_elst; |         public Elst edstElst; | ||||||
|         public Mdia mdia; |         public Mdia mdia; | ||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     class Mvhd { |     class Mvhd { | ||||||
|  |  | ||||||
|         long timeScale; |         long timeScale; | ||||||
|         long nextTrackId; |         long nextTrackId; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     class Moov { |     class Moov { | ||||||
|  |  | ||||||
|         Mvhd mvhd; |         Mvhd mvhd; | ||||||
|         Trak[] trak; |         Trak[] trak; | ||||||
|         Trex[] mvex_trex; |         Trex[] mvexTrex; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Trex { |     public class Trex { | ||||||
|  |  | ||||||
|         private int trackId; |         private int trackId; | ||||||
|         int defaultSampleDescriptionIndex; |         int defaultSampleDescriptionIndex; | ||||||
|         int defaultSampleDuration; |         int defaultSampleDuration; | ||||||
| @@ -934,42 +926,36 @@ public class Mp4DashReader { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Elst { |     public class Elst { | ||||||
|  |         public long mediaTime; | ||||||
|         public long MediaTime; |  | ||||||
|         public int bMediaRate; |         public int bMediaRate; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Mdia { |     public class Mdia { | ||||||
|  |         public int mdhdTimeScale; | ||||||
|         public int mdhd_timeScale; |  | ||||||
|         public byte[] mdhd; |         public byte[] mdhd; | ||||||
|         public Hdlr hdlr; |         public Hdlr hdlr; | ||||||
|         public Minf minf; |         public Minf minf; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Hdlr { |     public class Hdlr { | ||||||
|  |  | ||||||
|         public int type; |         public int type; | ||||||
|         public int subType; |         public int subType; | ||||||
|         public byte[] bReserved; |         public byte[] bReserved; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Minf { |     public class Minf { | ||||||
|  |  | ||||||
|         public byte[] dinf; |         public byte[] dinf; | ||||||
|         public byte[] stbl_stsd; |         public byte[] stblStsd; | ||||||
|         public byte[] $mhd; |         public byte[] mhd; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Mp4Track { |     public class Mp4Track { | ||||||
|  |  | ||||||
|         public TrackKind kind; |         public TrackKind kind; | ||||||
|         public Trak trak; |         public Trak trak; | ||||||
|         public Trex trex; |         public Trex trex; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Mp4DashChunk { |     public class Mp4DashChunk { | ||||||
|  |  | ||||||
|         public InputStream data; |         public InputStream data; | ||||||
|         public Moof moof; |         public Moof moof; | ||||||
|         private int i = 0; |         private int i = 0; | ||||||
| @@ -1002,9 +988,7 @@ public class Mp4DashReader { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Mp4DashSample { |     public class Mp4DashSample { | ||||||
|  |  | ||||||
|         public TrunEntry info; |         public TrunEntry info; | ||||||
|         public byte[] data; |         public byte[] data; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -17,13 +17,15 @@ import java.util.ArrayList; | |||||||
|  * @author kapodamy |  * @author kapodamy | ||||||
|  */ |  */ | ||||||
| public class Mp4FromDashWriter { | public class Mp4FromDashWriter { | ||||||
|  |     private static final int EPOCH_OFFSET = 2082844800; | ||||||
|     private final static int EPOCH_OFFSET = 2082844800; |     private static final short DEFAULT_TIMESCALE = 1000; | ||||||
|     private final static short DEFAULT_TIMESCALE = 1000; |     private static final byte SAMPLES_PER_CHUNK_INIT = 2; | ||||||
|     private final static byte SAMPLES_PER_CHUNK_INIT = 2; |     // ffmpeg uses 2, basic uses 1 (with 60fps uses 21 or 22). NewPipe will use 6 | ||||||
|     private final static byte SAMPLES_PER_CHUNK = 6;// ffmpeg uses 2, basic uses 1 (with 60fps uses 21 or 22). NewPipe will use 6 |     private static final byte SAMPLES_PER_CHUNK = 6; | ||||||
|     private final static long THRESHOLD_FOR_CO64 = 0xFFFEFFFFL;// near 3.999 GiB |     // 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 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; |     private final long time; | ||||||
|  |  | ||||||
| @@ -48,7 +50,7 @@ public class Mp4FromDashWriter { | |||||||
|  |  | ||||||
|     private final ArrayList<Integer> compatibleBrands = new ArrayList<>(5); |     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) { |         for (SharpStream src : sources) { | ||||||
|             if (!src.canRewind() && !src.canRead()) { |             if (!src.canRewind() && !src.canRead()) { | ||||||
|                 throw new IOException("All sources must be readable and allow rewind"); |                 throw new IOException("All sources must be readable and allow rewind"); | ||||||
| @@ -65,7 +67,7 @@ public class Mp4FromDashWriter { | |||||||
|         compatibleBrands.add(0x69736F32); // iso2 |         compatibleBrands.add(0x69736F32); // iso2 | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public Mp4Track[] getTracksFromSource(int sourceIndex) throws IllegalStateException { |     public Mp4Track[] getTracksFromSource(final int sourceIndex) throws IllegalStateException { | ||||||
|         if (!parsed) { |         if (!parsed) { | ||||||
|             throw new IllegalStateException("All sources must be parsed first"); |             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) { |         if (done) { | ||||||
|             throw new IOException("already 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; |         overrideMainBrand = brand; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -140,7 +142,7 @@ public class Mp4FromDashWriter { | |||||||
|         outStream = null; |         outStream = null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void build(SharpStream output) throws IOException { |     public void build(final SharpStream output) throws IOException { | ||||||
|         if (done) { |         if (done) { | ||||||
|             throw new RuntimeException("already done"); |             throw new RuntimeException("already done"); | ||||||
|         } |         } | ||||||
| @@ -165,12 +167,12 @@ public class Mp4FromDashWriter { | |||||||
|             tablesInfo[i] = new TablesInfo(); |             tablesInfo[i] = new TablesInfo(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         int single_sample_buffer; |         int singleSampleBuffer; | ||||||
|         if (tracks.length == 1 && tracks[0].kind == TrackKind.Audio) { |         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 |             // 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 { |         } else { | ||||||
|             single_sample_buffer = -1; |             singleSampleBuffer = -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -222,8 +224,8 @@ public class Mp4FromDashWriter { | |||||||
|  |  | ||||||
|             readers[i].rewind(); |             readers[i].rewind(); | ||||||
|  |  | ||||||
|             if (single_sample_buffer > 0) { |             if (singleSampleBuffer > 0) { | ||||||
|                 initChunkTables(tablesInfo[i], single_sample_buffer, single_sample_buffer); |                 initChunkTables(tablesInfo[i], singleSampleBuffer, singleSampleBuffer); | ||||||
|             } else { |             } else { | ||||||
|                 initChunkTables(tablesInfo[i], SAMPLES_PER_CHUNK_INIT, SAMPLES_PER_CHUNK); |                 initChunkTables(tablesInfo[i], SAMPLES_PER_CHUNK_INIT, SAMPLES_PER_CHUNK); | ||||||
|             } |             } | ||||||
| @@ -232,9 +234,9 @@ public class Mp4FromDashWriter { | |||||||
|  |  | ||||||
|             if (sampleSizeChanges == 1) { |             if (sampleSizeChanges == 1) { | ||||||
|                 tablesInfo[i].stsz = 0; |                 tablesInfo[i].stsz = 0; | ||||||
|                 tablesInfo[i].stsz_default = samplesSize; |                 tablesInfo[i].stszDefault = samplesSize; | ||||||
|             } else { |             } else { | ||||||
|                 tablesInfo[i].stsz_default = 0; |                 tablesInfo[i].stszDefault = 0; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (tablesInfo[i].stss == tablesInfo[i].stsz) { |             if (tablesInfo[i].stss == tablesInfo[i].stsz) { | ||||||
| @@ -251,7 +253,7 @@ public class Mp4FromDashWriter { | |||||||
|         boolean is64 = read > THRESHOLD_FOR_CO64; |         boolean is64 = read > THRESHOLD_FOR_CO64; | ||||||
|  |  | ||||||
|         // calculate the moov size |         // calculate the moov size | ||||||
|         int auxSize = make_moov(defaultMediaTime, tablesInfo, is64); |         int auxSize = makeMoov(defaultMediaTime, tablesInfo, is64); | ||||||
|  |  | ||||||
|         if (auxSize < THRESHOLD_MOOV_LENGTH) { |         if (auxSize < THRESHOLD_MOOV_LENGTH) { | ||||||
|             auxBuffer = ByteBuffer.allocate(auxSize); // cache moov in the memory |             auxBuffer = ByteBuffer.allocate(auxSize); // cache moov in the memory | ||||||
| @@ -260,7 +262,7 @@ public class Mp4FromDashWriter { | |||||||
|         moovSimulation = false; |         moovSimulation = false; | ||||||
|         writeOffset = 0; |         writeOffset = 0; | ||||||
|  |  | ||||||
|         final int ftyp_size = make_ftyp(); |         final int ftypSize = makeFtyp(); | ||||||
|  |  | ||||||
|         // reserve moov space in the output stream |         // reserve moov space in the output stream | ||||||
|         if (auxSize > 0) { |         if (auxSize > 0) { | ||||||
| @@ -274,19 +276,20 @@ public class Mp4FromDashWriter { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (auxBuffer == null) { |         if (auxBuffer == null) { | ||||||
|             outSeek(ftyp_size); |             outSeek(ftypSize); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // tablesInfo contains row counts |         // tablesInfo contains row counts | ||||||
|         // and after returning from make_moov() will contain those table offsets |         // and after returning from makeMoov() will contain those table offsets | ||||||
|         make_moov(defaultMediaTime, tablesInfo, is64); |         makeMoov(defaultMediaTime, tablesInfo, is64); | ||||||
|  |  | ||||||
|         // write tables: stts stsc sbgp |         // write tables: stts stsc sbgp | ||||||
|         // reset for ctts table: sampleCount sampleExtra |         // reset for ctts table: sampleCount sampleExtra | ||||||
|         for (int i = 0; i < readers.length; i++) { |         for (int i = 0; i < readers.length; i++) { | ||||||
|             writeEntryArray(tablesInfo[i].stts, 2, sampleCount[i], defaultSampleDuration[i]); |             writeEntryArray(tablesInfo[i].stts, 2, sampleCount[i], defaultSampleDuration[i]); | ||||||
|             writeEntryArray(tablesInfo[i].stsc, tablesInfo[i].stsc_bEntries.length, tablesInfo[i].stsc_bEntries); |             writeEntryArray(tablesInfo[i].stsc, tablesInfo[i].stscBEntries.length, | ||||||
|             tablesInfo[i].stsc_bEntries = null; |                     tablesInfo[i].stscBEntries); | ||||||
|  |             tablesInfo[i].stscBEntries = null; | ||||||
|             if (tablesInfo[i].ctts > 0) { |             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; |                 sampleExtra[i] = -1; | ||||||
| @@ -300,11 +303,11 @@ public class Mp4FromDashWriter { | |||||||
|             outRestore(); |             outRestore(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         outWrite(make_mdat(totalSampleSize, is64)); |         outWrite(makeMdat(totalSampleSize, is64)); | ||||||
|  |  | ||||||
|         int[] sampleIndex = new int[readers.length]; |         int[] sampleIndex = new int[readers.length]; | ||||||
|         int[] sizes = 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[single_sample_buffer > 0 ? single_sample_buffer : SAMPLES_PER_CHUNK]; |         int[] sync = new int[singleSampleBuffer > 0 ? singleSampleBuffer : SAMPLES_PER_CHUNK]; | ||||||
|  |  | ||||||
|         int written = readers.length; |         int written = readers.length; | ||||||
|         while (written > 0) { |         while (written > 0) { | ||||||
| @@ -318,8 +321,8 @@ public class Mp4FromDashWriter { | |||||||
|                 long chunkOffset = writeOffset; |                 long chunkOffset = writeOffset; | ||||||
|                 int syncCount = 0; |                 int syncCount = 0; | ||||||
|                 int limit; |                 int limit; | ||||||
|                 if (single_sample_buffer > 0) { |                 if (singleSampleBuffer > 0) { | ||||||
|                     limit = single_sample_buffer; |                     limit = singleSampleBuffer; | ||||||
|                 } else { |                 } else { | ||||||
|                     limit = sampleIndex[i] == 0 ? SAMPLES_PER_CHUNK_INIT : SAMPLES_PER_CHUNK; |                     limit = sampleIndex[i] == 0 ? SAMPLES_PER_CHUNK_INIT : SAMPLES_PER_CHUNK; | ||||||
|                 } |                 } | ||||||
| @@ -330,7 +333,8 @@ public class Mp4FromDashWriter { | |||||||
|  |  | ||||||
|                     if (sample == null) { |                     if (sample == null) { | ||||||
|                         if (tablesInfo[i].ctts > 0 && sampleExtra[i] >= 0) { |                         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(); |                             outRestore(); | ||||||
|                         } |                         } | ||||||
|                         sampleIndex[i] = -1; |                         sampleIndex[i] = -1; | ||||||
| @@ -344,7 +348,8 @@ public class Mp4FromDashWriter { | |||||||
|                             sampleCount[i]++; |                             sampleCount[i]++; | ||||||
|                         } else { |                         } else { | ||||||
|                             if (sampleExtra[i] >= 0) { |                             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(); |                                 outRestore(); | ||||||
|                             } |                             } | ||||||
|                             sampleCount[i] = 1; |                             sampleCount[i] = 1; | ||||||
| @@ -378,7 +383,8 @@ public class Mp4FromDashWriter { | |||||||
|                         if (is64) { |                         if (is64) { | ||||||
|                             tablesInfo[i].stco = writeEntry64(tablesInfo[i].stco, chunkOffset); |                             tablesInfo[i].stco = writeEntry64(tablesInfo[i].stco, chunkOffset); | ||||||
|                         } else { |                         } else { | ||||||
|                             tablesInfo[i].stco = writeEntryArray(tablesInfo[i].stco, 1, (int) chunkOffset); |                             tablesInfo[i].stco = writeEntryArray(tablesInfo[i].stco, 1, | ||||||
|  |                                     (int) chunkOffset); | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
| @@ -389,13 +395,13 @@ public class Mp4FromDashWriter { | |||||||
|  |  | ||||||
|         if (auxBuffer != null) { |         if (auxBuffer != null) { | ||||||
|             // dump moov |             // dump moov | ||||||
|             outSeek(ftyp_size); |             outSeek(ftypSize); | ||||||
|             outStream.write(auxBuffer.array(), 0, auxBuffer.capacity()); |             outStream.write(auxBuffer.array(), 0, auxBuffer.capacity()); | ||||||
|             auxBuffer = null; |             auxBuffer = null; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Mp4DashSample getNextSample(int track) throws IOException { |     private Mp4DashSample getNextSample(final int track) throws IOException { | ||||||
|         if (readersChunks[track] == null) { |         if (readersChunks[track] == null) { | ||||||
|             readersChunks[track] = readers[track].getNextChunk(false); |             readersChunks[track] = readers[track].getNextChunk(false); | ||||||
|             if (readersChunks[track] == null) { |             if (readersChunks[track] == null) { | ||||||
| @@ -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(); |         outBackup(); | ||||||
|  |  | ||||||
|         auxSeek(offset); |         auxSeek(offset); | ||||||
| @@ -422,7 +428,8 @@ public class Mp4FromDashWriter { | |||||||
|         return offset + 8; |         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(); |         outBackup(); | ||||||
|  |  | ||||||
|         auxSeek(offset); |         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) |         // tables.stsz holds amount of samples of the track (total) | ||||||
|         int totalSamples = (tables.stsz - firstCount); |         int totalSamples = (tables.stsz - firstCount); | ||||||
|         float chunkAmount = totalSamples / (float) succesiveCount; |         float chunkAmount = totalSamples / (float) succesiveCount; | ||||||
| @@ -473,36 +481,36 @@ public class Mp4FromDashWriter { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // stsc_table_entry = [first_chunk, samples_per_chunk, sample_description_index] |         // stsc_table_entry = [first_chunk, samples_per_chunk, sample_description_index] | ||||||
|         tables.stsc_bEntries = new int[tables.stsc * 3]; |         tables.stscBEntries = new int[tables.stsc * 3]; | ||||||
|         tables.stco = remainChunkOffset + 1; // total entrys in chunk offset box |         tables.stco = remainChunkOffset + 1; // total entrys in chunk offset box | ||||||
|  |  | ||||||
|         tables.stsc_bEntries[index++] = 1; |         tables.stscBEntries[index++] = 1; | ||||||
|         tables.stsc_bEntries[index++] = firstCount; |         tables.stscBEntries[index++] = firstCount; | ||||||
|         tables.stsc_bEntries[index++] = 1; |         tables.stscBEntries[index++] = 1; | ||||||
|  |  | ||||||
|         if (firstCount != succesiveCount) { |         if (firstCount != succesiveCount) { | ||||||
|             tables.stsc_bEntries[index++] = 2; |             tables.stscBEntries[index++] = 2; | ||||||
|             tables.stsc_bEntries[index++] = succesiveCount; |             tables.stscBEntries[index++] = succesiveCount; | ||||||
|             tables.stsc_bEntries[index++] = 1; |             tables.stscBEntries[index++] = 1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (remain) { |         if (remain) { | ||||||
|             tables.stsc_bEntries[index++] = remainChunkOffset + 1; |             tables.stscBEntries[index++] = remainChunkOffset + 1; | ||||||
|             tables.stsc_bEntries[index++] = totalSamples % succesiveCount; |             tables.stscBEntries[index++] = totalSamples % succesiveCount; | ||||||
|             tables.stsc_bEntries[index] = 1; |             tables.stscBEntries[index] = 1; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void outWrite(byte[] buffer) throws IOException { |     private void outWrite(final byte[] buffer) throws IOException { | ||||||
|         outWrite(buffer, buffer.length); |         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; |         writeOffset += count; | ||||||
|         outStream.write(buffer, 0, count); |         outStream.write(buffer, 0, count); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void outSeek(long offset) throws IOException { |     private void outSeek(final long offset) throws IOException { | ||||||
|         if (outStream.canSeek()) { |         if (outStream.canSeek()) { | ||||||
|             outStream.seek(offset); |             outStream.seek(offset); | ||||||
|             writeOffset = 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); |         outStream.skip(amount); | ||||||
|         writeOffset += amount; |         writeOffset += amount; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private int lengthFor(int offset) throws IOException { |     private int lengthFor(final int offset) throws IOException { | ||||||
|         int size = auxOffset() - offset; |         int size = auxOffset() - offset; | ||||||
|  |  | ||||||
|         if (moovSimulation) { |         if (moovSimulation) { | ||||||
| @@ -534,7 +542,8 @@ public class Mp4FromDashWriter { | |||||||
|         return size; |         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; |         final byte base = 16; | ||||||
|         int size = columns * rows * 4; |         int size = columns * rows * 4; | ||||||
|         int total = size + base; |         int total = size + base; | ||||||
| @@ -562,14 +571,14 @@ public class Mp4FromDashWriter { | |||||||
|         return offset + base; |         return offset + base; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void auxWrite(int value) throws IOException { |     private void auxWrite(final int value) throws IOException { | ||||||
|         auxWrite(ByteBuffer.allocate(4) |         auxWrite(ByteBuffer.allocate(4) | ||||||
|                 .putInt(value) |                 .putInt(value) | ||||||
|                 .array() |                 .array() | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void auxWrite(byte[] buffer) throws IOException { |     private void auxWrite(final byte[] buffer) throws IOException { | ||||||
|         if (moovSimulation) { |         if (moovSimulation) { | ||||||
|             writeOffset += buffer.length; |             writeOffset += buffer.length; | ||||||
|         } else if (auxBuffer == null) { |         } 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) { |         if (moovSimulation) { | ||||||
|             writeOffset = offset; |             writeOffset = offset; | ||||||
|         } else if (auxBuffer == null) { |         } 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) { |         if (moovSimulation) { | ||||||
|             writeOffset += amount; |             writeOffset += amount; | ||||||
|         } else if (auxBuffer == null) { |         } else if (auxBuffer == null) { | ||||||
| @@ -603,11 +612,11 @@ public class Mp4FromDashWriter { | |||||||
|         return auxBuffer == null ? (int) writeOffset : auxBuffer.position(); |         return auxBuffer == null ? (int) writeOffset : auxBuffer.position(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private int makeFtyp() throws IOException { | ||||||
|  |  | ||||||
|     private int make_ftyp() throws IOException { |  | ||||||
|         int size = 16 + (compatibleBrands.size() * 4); |         int size = 16 + (compatibleBrands.size() * 4); | ||||||
|         if (overrideMainBrand != 0) size += 4; |         if (overrideMainBrand != 0) { | ||||||
|  |             size += 4; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         ByteBuffer buffer = ByteBuffer.allocate(size); |         ByteBuffer buffer = ByteBuffer.allocate(size); | ||||||
|         buffer.putInt(size); |         buffer.putInt(size); | ||||||
| @@ -631,7 +640,7 @@ public class Mp4FromDashWriter { | |||||||
|         return size; |         return size; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private byte[] make_mdat(long refSize, boolean is64) { |     private byte[] makeMdat(long refSize, final boolean is64) { | ||||||
|         if (is64) { |         if (is64) { | ||||||
|             refSize += 16; |             refSize += 16; | ||||||
|         } else { |         } else { | ||||||
| @@ -649,7 +658,7 @@ public class Mp4FromDashWriter { | |||||||
|         return buffer.array(); |         return buffer.array(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void make_mvhd(long longestTrack) throws IOException { |     private void makeMvhd(final long longestTrack) throws IOException { | ||||||
|         auxWrite(new byte[]{ |         auxWrite(new byte[]{ | ||||||
|                 0x00, 0x00, 0x00, 0x78, 0x6D, 0x76, 0x68, 0x64, 0x01, 0x00, 0x00, 0x00 |                 0x00, 0x00, 0x00, 0x78, 0x6D, 0x76, 0x68, 0x64, 0x01, 0x00, 0x00, 0x00 | ||||||
|         }); |         }); | ||||||
| @@ -676,7 +685,8 @@ public class Mp4FromDashWriter { | |||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     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(); |         int start = auxOffset(); | ||||||
|  |  | ||||||
|         auxWrite(new byte[]{ |         auxWrite(new byte[]{ | ||||||
| @@ -688,21 +698,21 @@ public class Mp4FromDashWriter { | |||||||
|  |  | ||||||
|         for (int i = 0; i < durations.length; i++) { |         for (int i = 0; i < durations.length; i++) { | ||||||
|             durations[i] = (long) Math.ceil( |             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) { |             if (durations[i] > longestTrack) { | ||||||
|                 longestTrack = durations[i]; |                 longestTrack = durations[i]; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         make_mvhd(longestTrack); |         makeMvhd(longestTrack); | ||||||
|  |  | ||||||
|         for (int i = 0; i < tracks.length; i++) { |         for (int i = 0; i < tracks.length; i++) { | ||||||
|             if (tracks[i].trak.tkhd.matrix.length != 36) { |             if (tracks[i].trak.tkhd.matrix.length != 36) { | ||||||
|                 throw new RuntimeException("bad track matrix length (expected 36) in track n°" + i); |                 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 |         // udta/meta/ilst/©too | ||||||
| @@ -719,7 +729,8 @@ public class Mp4FromDashWriter { | |||||||
|         return lengthFor(start); |         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(); |         int start = auxOffset(); | ||||||
|  |  | ||||||
|         auxWrite(new byte[]{ |         auxWrite(new byte[]{ | ||||||
| @@ -754,13 +765,13 @@ public class Mp4FromDashWriter { | |||||||
|         int bMediaRate; |         int bMediaRate; | ||||||
|         int mediaTime; |         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? |             // is a audio track ¿is edst/elst optional for audio tracks? | ||||||
|             mediaTime = 0x00; // ffmpeg set this value as zero, instead of defaultMediaTime |             mediaTime = 0x00; // ffmpeg set this value as zero, instead of defaultMediaTime | ||||||
|             bMediaRate = 0x00010000; |             bMediaRate = 0x00010000; | ||||||
|         } else { |         } else { | ||||||
|             mediaTime = (int) tracks[index].trak.edst_elst.MediaTime; |             mediaTime = (int) tracks[index].trak.edstElst.mediaTime; | ||||||
|             bMediaRate = tracks[index].trak.edst_elst.bMediaRate; |             bMediaRate = tracks[index].trak.edstElst.bMediaRate; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         auxWrite(ByteBuffer |         auxWrite(ByteBuffer | ||||||
| @@ -771,25 +782,26 @@ public class Mp4FromDashWriter { | |||||||
|                 .array() |                 .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); |         lengthFor(start); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void make_mdia(Mdia mdia, TablesInfo tablesInfo, boolean is64, boolean isAudio) throws IOException { |     private void makeMdia(final Mdia mdia, final TablesInfo tablesInfo, final boolean is64, | ||||||
|         int start_mdia = auxOffset(); |                           final boolean isAudio) throws IOException { | ||||||
|  |         int startMdia = auxOffset(); | ||||||
|         auxWrite(new byte[]{0x00, 0x00, 0x00, 0x00, 0x6D, 0x64, 0x69, 0x61}); // mdia |         auxWrite(new byte[]{0x00, 0x00, 0x00, 0x00, 0x6D, 0x64, 0x69, 0x61}); // mdia | ||||||
|         auxWrite(mdia.mdhd); |         auxWrite(mdia.mdhd); | ||||||
|         auxWrite(make_hdlr(mdia.hdlr)); |         auxWrite(makeHdlr(mdia.hdlr)); | ||||||
|  |  | ||||||
|         int start_minf = auxOffset(); |         int startMinf = auxOffset(); | ||||||
|         auxWrite(new byte[]{0x00, 0x00, 0x00, 0x00, 0x6D, 0x69, 0x6E, 0x66}); // minf |         auxWrite(new byte[]{0x00, 0x00, 0x00, 0x00, 0x6D, 0x69, 0x6E, 0x66}); // minf | ||||||
|         auxWrite(mdia.minf.$mhd); |         auxWrite(mdia.minf.mhd); | ||||||
|         auxWrite(mdia.minf.dinf); |         auxWrite(mdia.minf.dinf); | ||||||
|  |  | ||||||
|         int start_stbl = auxOffset(); |         int startStbl = auxOffset(); | ||||||
|         auxWrite(new byte[]{0x00, 0x00, 0x00, 0x00, 0x73, 0x74, 0x62, 0x6C}); // stbl |         auxWrite(new byte[]{0x00, 0x00, 0x00, 0x00, 0x73, 0x74, 0x62, 0x6C}); // stbl | ||||||
|         auxWrite(mdia.minf.stbl_stsd); |         auxWrite(mdia.minf.stblStsd); | ||||||
|  |  | ||||||
|         // |         // | ||||||
|         // In audio tracks the following tables is not required: ssts ctts |         // In audio tracks the following tables is not required: ssts ctts | ||||||
| @@ -804,7 +816,7 @@ public class Mp4FromDashWriter { | |||||||
|                 make(0x63747473, -1, 2, tablesInfo.ctts); |                 make(0x63747473, -1, 2, tablesInfo.ctts); | ||||||
|             } |             } | ||||||
|             make(0x73747363, -1, 3, tablesInfo.stsc); |             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); |             make(is64 ? 0x636F3634 : 0x7374636F, -1, is64 ? 2 : 1, tablesInfo.stco); | ||||||
|         } else { |         } else { | ||||||
|             tablesInfo.stts = make(0x73747473, -1, 2, 1); |             tablesInfo.stts = make(0x73747473, -1, 2, 1); | ||||||
| @@ -815,21 +827,22 @@ public class Mp4FromDashWriter { | |||||||
|                 tablesInfo.ctts = make(0x63747473, -1, 2, tablesInfo.ctts); |                 tablesInfo.ctts = make(0x63747473, -1, 2, tablesInfo.ctts); | ||||||
|             } |             } | ||||||
|             tablesInfo.stsc = make(0x73747363, -1, 3, tablesInfo.stsc); |             tablesInfo.stsc = make(0x73747363, -1, 3, tablesInfo.stsc); | ||||||
|             tablesInfo.stsz = make(0x7374737A, tablesInfo.stsz_default, 1, tablesInfo.stsz); |             tablesInfo.stsz = make(0x7374737A, tablesInfo.stszDefault, 1, tablesInfo.stsz); | ||||||
|             tablesInfo.stco = make(is64 ? 0x636F3634 : 0x7374636F, -1, is64 ? 2 : 1, tablesInfo.stco); |             tablesInfo.stco = make(is64 ? 0x636F3634 : 0x7374636F, -1, is64 ? 2 : 1, | ||||||
|  |                     tablesInfo.stco); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (isAudio) { |         if (isAudio) { | ||||||
|             auxWrite(make_sgpd()); |             auxWrite(makeSgpd()); | ||||||
|             tablesInfo.sbgp = make_sbgp();// during simulation the returned offset is ignored |             tablesInfo.sbgp = makeSbgp(); // during simulation the returned offset is ignored | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         lengthFor(start_stbl); |         lengthFor(startStbl); | ||||||
|         lengthFor(start_minf); |         lengthFor(startMinf); | ||||||
|         lengthFor(start_mdia); |         lengthFor(startMdia); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private byte[] make_hdlr(Hdlr hdlr) { |     private byte[] makeHdlr(final Hdlr hdlr) { | ||||||
|         ByteBuffer buffer = ByteBuffer.wrap(new byte[]{ |         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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
| @@ -851,7 +864,7 @@ public class Mp4FromDashWriter { | |||||||
|         return buffer.array(); |         return buffer.array(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private int make_sbgp() throws IOException { |     private int makeSbgp() throws IOException { | ||||||
|         int offset = auxOffset(); |         int offset = auxOffset(); | ||||||
|  |  | ||||||
|         auxWrite(new byte[] { |         auxWrite(new byte[] { | ||||||
| @@ -867,7 +880,7 @@ public class Mp4FromDashWriter { | |||||||
|         return offset + 0x14; |         return offset + 0x14; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private byte[] make_sgpd() { |     private byte[] makeSgpd() { | ||||||
|         /* |         /* | ||||||
|          * Sample Group Description Box |          * Sample Group Description Box | ||||||
|          * |          * | ||||||
| @@ -895,13 +908,12 @@ public class Mp4FromDashWriter { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     class TablesInfo { |     class TablesInfo { | ||||||
|  |  | ||||||
|         int stts; |         int stts; | ||||||
|         int stsc; |         int stsc; | ||||||
|         int[] stsc_bEntries; |         int[] stscBEntries; | ||||||
|         int ctts; |         int ctts; | ||||||
|         int stsz; |         int stsz; | ||||||
|         int stsz_default; |         int stszDefault; | ||||||
|         int stss; |         int stss; | ||||||
|         int stco; |         int stco; | ||||||
|         int sbgp; |         int sbgp; | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package org.schabi.newpipe.streams; | package org.schabi.newpipe.streams; | ||||||
|  |  | ||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
|  | import androidx.annotation.Nullable; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.streams.WebMReader.Cluster; | import org.schabi.newpipe.streams.WebMReader.Cluster; | ||||||
| import org.schabi.newpipe.streams.WebMReader.Segment; | import org.schabi.newpipe.streams.WebMReader.Segment; | ||||||
| @@ -13,22 +14,19 @@ import java.io.IOException; | |||||||
| import java.nio.ByteBuffer; | import java.nio.ByteBuffer; | ||||||
| import java.nio.ByteOrder; | import java.nio.ByteOrder; | ||||||
|  |  | ||||||
| import androidx.annotation.Nullable; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @author kapodamy |  * @author kapodamy | ||||||
|  */ |  */ | ||||||
| public class OggFromWebMWriter implements Closeable { | public class OggFromWebMWriter implements Closeable { | ||||||
|  |  | ||||||
|     private static final byte FLAG_UNSET = 0x00; |     private static final byte FLAG_UNSET = 0x00; | ||||||
|     //private static final byte FLAG_CONTINUED = 0x01; |     //private static final byte FLAG_CONTINUED = 0x01; | ||||||
|     private static final byte FLAG_FIRST = 0x02; |     private static final byte FLAG_FIRST = 0x02; | ||||||
|     private static final byte FLAG_LAST = 0x04; |     private static final byte FLAG_LAST = 0x04; | ||||||
|  |  | ||||||
|     private final static byte HEADER_CHECKSUM_OFFSET = 22; |     private static final byte HEADER_CHECKSUM_OFFSET = 22; | ||||||
|     private final static byte HEADER_SIZE = 27; |     private static final byte HEADER_SIZE = 27; | ||||||
|  |  | ||||||
|     private final static int TIME_SCALE_NS = 1000000000; |     private static final int TIME_SCALE_NS = 1000000000; | ||||||
|  |  | ||||||
|     private boolean done = false; |     private boolean done = false; | ||||||
|     private boolean parsed = false; |     private boolean parsed = false; | ||||||
| @@ -36,26 +34,26 @@ public class OggFromWebMWriter implements Closeable { | |||||||
|     private SharpStream source; |     private SharpStream source; | ||||||
|     private SharpStream output; |     private SharpStream output; | ||||||
|  |  | ||||||
|     private int sequence_count = 0; |     private int sequenceCount = 0; | ||||||
|     private final int STREAM_ID; |     private final int streamId; | ||||||
|     private byte packet_flag = FLAG_FIRST; |     private byte packetFlag = FLAG_FIRST; | ||||||
|  |  | ||||||
|     private WebMReader webm = null; |     private WebMReader webm = null; | ||||||
|     private WebMTrack webm_track = null; |     private WebMTrack webmTrack = null; | ||||||
|     private Segment webm_segment = null; |     private Segment webmSegment = null; | ||||||
|     private Cluster webm_cluster = null; |     private Cluster webmCluster = null; | ||||||
|     private SimpleBlock webm_block = null; |     private SimpleBlock webmBlock = null; | ||||||
|  |  | ||||||
|     private long webm_block_last_timecode = 0; |     private long webmBlockLastTimecode = 0; | ||||||
|     private long webm_block_near_duration = 0; |     private long webmBlockNearDuration = 0; | ||||||
|  |  | ||||||
|     private short segment_table_size = 0; |     private short segmentTableSize = 0; | ||||||
|     private final byte[] segment_table = new byte[255]; |     private final byte[] segmentTable = new byte[255]; | ||||||
|     private long segment_table_next_timestamp = TIME_SCALE_NS; |     private long segmentTableNextTimestamp = TIME_SCALE_NS; | ||||||
|  |  | ||||||
|     private final int[] crc32_table = new int[256]; |     private final int[] crc32Table = new int[256]; | ||||||
|  |  | ||||||
|     public OggFromWebMWriter(@NonNull SharpStream source, @NonNull SharpStream target) { |     public OggFromWebMWriter(@NonNull final SharpStream source, @NonNull final SharpStream target) { | ||||||
|         if (!source.canRead() || !source.canRewind()) { |         if (!source.canRead() || !source.canRewind()) { | ||||||
|             throw new IllegalArgumentException("source stream must be readable and allows seeking"); |             throw new IllegalArgumentException("source stream must be readable and allows seeking"); | ||||||
|         } |         } | ||||||
| @@ -66,9 +64,9 @@ public class OggFromWebMWriter implements Closeable { | |||||||
|         this.source = source; |         this.source = source; | ||||||
|         this.output = target; |         this.output = target; | ||||||
|  |  | ||||||
|         this.STREAM_ID = (int) System.currentTimeMillis(); |         this.streamId = (int) System.currentTimeMillis(); | ||||||
|  |  | ||||||
|         populate_crc32_table(); |         populateCrc32Table(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public boolean isDone() { |     public boolean isDone() { | ||||||
| @@ -98,20 +96,20 @@ public class OggFromWebMWriter implements Closeable { | |||||||
|         try { |         try { | ||||||
|             webm = new WebMReader(source); |             webm = new WebMReader(source); | ||||||
|             webm.parse(); |             webm.parse(); | ||||||
|             webm_segment = webm.getNextSegment(); |             webmSegment = webm.getNextSegment(); | ||||||
|         } finally { |         } finally { | ||||||
|             parsed = true; |             parsed = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void selectTrack(int trackIndex) throws IOException { |     public void selectTrack(final int trackIndex) throws IOException { | ||||||
|         if (!parsed) { |         if (!parsed) { | ||||||
|             throw new IllegalStateException("source must be parsed first"); |             throw new IllegalStateException("source must be parsed first"); | ||||||
|         } |         } | ||||||
|         if (done) { |         if (done) { | ||||||
|             throw new IOException("already done"); |             throw new IOException("already done"); | ||||||
|         } |         } | ||||||
|         if (webm_track != null) { |         if (webmTrack != null) { | ||||||
|             throw new IOException("tracks already selected"); |             throw new IOException("tracks already selected"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -124,7 +122,7 @@ public class OggFromWebMWriter implements Closeable { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         try { |         try { | ||||||
|             webm_track = webm.selectTrack(trackIndex); |             webmTrack = webm.selectTrack(trackIndex); | ||||||
|         } finally { |         } finally { | ||||||
|             parsed = true; |             parsed = true; | ||||||
|         } |         } | ||||||
| @@ -135,7 +133,7 @@ public class OggFromWebMWriter implements Closeable { | |||||||
|         done = true; |         done = true; | ||||||
|         parsed = true; |         parsed = true; | ||||||
|  |  | ||||||
|         webm_track = null; |         webmTrack = null; | ||||||
|         webm = null; |         webm = null; | ||||||
|  |  | ||||||
|         if (!output.isClosed()) { |         if (!output.isClosed()) { | ||||||
| @@ -155,43 +153,44 @@ public class OggFromWebMWriter implements Closeable { | |||||||
|         header.order(ByteOrder.LITTLE_ENDIAN); |         header.order(ByteOrder.LITTLE_ENDIAN); | ||||||
|  |  | ||||||
|         /* step 1: get the amount of frames per seconds */ |         /* step 1: get the amount of frames per seconds */ | ||||||
|         switch (webm_track.kind) { |         switch (webmTrack.kind) { | ||||||
|             case Audio: |             case Audio: | ||||||
|                 resolution = getSampleFrequencyFromTrack(webm_track.bMetadata); |                 resolution = getSampleFrequencyFromTrack(webmTrack.bMetadata); | ||||||
|                 if (resolution == 0f) { |                 if (resolution == 0f) { | ||||||
|                     throw new RuntimeException("cannot get the audio sample rate"); |                     throw new RuntimeException("cannot get the audio sample rate"); | ||||||
|                 } |                 } | ||||||
|                 break; |                 break; | ||||||
|             case Video: |             case Video: | ||||||
|                 // WARNING: untested |                 // WARNING: untested | ||||||
|                 if (webm_track.defaultDuration == 0) { |                 if (webmTrack.defaultDuration == 0) { | ||||||
|                     throw new RuntimeException("missing default frame time"); |                     throw new RuntimeException("missing default frame time"); | ||||||
|                 } |                 } | ||||||
|                 resolution = 1000f / ((float) webm_track.defaultDuration / webm_segment.info.timecodeScale); |                 resolution = 1000f / ((float) webmTrack.defaultDuration | ||||||
|  |                         / webmSegment.info.timecodeScale); | ||||||
|                 break; |                 break; | ||||||
|             default: |             default: | ||||||
|                 throw new RuntimeException("not implemented"); |                 throw new RuntimeException("not implemented"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         /* step 2: create packet with code init data */ |         /* step 2: create packet with code init data */ | ||||||
|         if (webm_track.codecPrivate != null) { |         if (webmTrack.codecPrivate != null) { | ||||||
|             addPacketSegment(webm_track.codecPrivate.length); |             addPacketSegment(webmTrack.codecPrivate.length); | ||||||
|             make_packetHeader(0x00, header, webm_track.codecPrivate); |             makePacketheader(0x00, header, webmTrack.codecPrivate); | ||||||
|             write(header); |             write(header); | ||||||
|             output.write(webm_track.codecPrivate); |             output.write(webmTrack.codecPrivate); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         /* step 3: create packet with metadata */ |         /* step 3: create packet with metadata */ | ||||||
|         byte[] buffer = make_metadata(); |         byte[] buffer = makeMetadata(); | ||||||
|         if (buffer != null) { |         if (buffer != null) { | ||||||
|             addPacketSegment(buffer.length); |             addPacketSegment(buffer.length); | ||||||
|             make_packetHeader(0x00, header, buffer); |             makePacketheader(0x00, header, buffer); | ||||||
|             write(header); |             write(header); | ||||||
|             output.write(buffer); |             output.write(buffer); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         /* step 4: calculate amount of packets */ |         /* step 4: calculate amount of packets */ | ||||||
|         while (webm_segment != null) { |         while (webmSegment != null) { | ||||||
|             bloq = getNextBlock(); |             bloq = getNextBlock(); | ||||||
|  |  | ||||||
|             if (bloq != null && addPacketSegment(bloq)) { |             if (bloq != null && addPacketSegment(bloq)) { | ||||||
| @@ -203,29 +202,29 @@ public class OggFromWebMWriter implements Closeable { | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             // calculate the current packet duration using the next block |             // calculate the current packet duration using the next block | ||||||
|             double elapsed_ns = webm_track.codecDelay; |             double elapsedNs = webmTrack.codecDelay; | ||||||
|  |  | ||||||
|             if (bloq == null) { |             if (bloq == null) { | ||||||
|                 packet_flag = FLAG_LAST;// note: if the flag is FLAG_CONTINUED, is changed |                 packetFlag = FLAG_LAST; // note: if the flag is FLAG_CONTINUED, is changed | ||||||
|                 elapsed_ns += webm_block_last_timecode; |                 elapsedNs += webmBlockLastTimecode; | ||||||
|  |  | ||||||
|                 if (webm_track.defaultDuration > 0) { |                 if (webmTrack.defaultDuration > 0) { | ||||||
|                     elapsed_ns += webm_track.defaultDuration; |                     elapsedNs += webmTrack.defaultDuration; | ||||||
|                 } else { |                 } else { | ||||||
|                     // hardcoded way, guess the sample duration |                     // hardcoded way, guess the sample duration | ||||||
|                     elapsed_ns += webm_block_near_duration; |                     elapsedNs += webmBlockNearDuration; | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 elapsed_ns += bloq.absoluteTimeCodeNs; |                 elapsedNs += bloq.absoluteTimeCodeNs; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             // get the sample count in the page |             // get the sample count in the page | ||||||
|             elapsed_ns = elapsed_ns / TIME_SCALE_NS; |             elapsedNs = elapsedNs / TIME_SCALE_NS; | ||||||
|             elapsed_ns = Math.ceil(elapsed_ns * resolution); |             elapsedNs = Math.ceil(elapsedNs * resolution); | ||||||
|  |  | ||||||
|             // create header and calculate page checksum |             // create header and calculate page checksum | ||||||
|             int checksum = make_packetHeader((long) elapsed_ns, header, null); |             int checksum = makePacketheader((long) elapsedNs, header, null); | ||||||
|             checksum = calc_crc32(checksum, page.array(), page.position()); |             checksum = calcCrc32(checksum, page.array(), page.position()); | ||||||
|  |  | ||||||
|             header.putInt(HEADER_CHECKSUM_OFFSET, checksum); |             header.putInt(HEADER_CHECKSUM_OFFSET, checksum); | ||||||
|  |  | ||||||
| @@ -233,52 +232,53 @@ public class OggFromWebMWriter implements Closeable { | |||||||
|             write(header); |             write(header); | ||||||
|             write(page); |             write(page); | ||||||
|  |  | ||||||
|             webm_block = bloq; |             webmBlock = bloq; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private int make_packetHeader(long gran_pos, @NonNull ByteBuffer buffer, byte[] immediate_page) { |     private int makePacketheader(final long granPos, @NonNull final ByteBuffer buffer, | ||||||
|  |                                  final byte[] immediatePage) { | ||||||
|         short length = HEADER_SIZE; |         short length = HEADER_SIZE; | ||||||
|  |  | ||||||
|         buffer.putInt(0x5367674f); // "OggS" binary string in little-endian |         buffer.putInt(0x5367674f); // "OggS" binary string in little-endian | ||||||
|         buffer.put((byte) 0x00); // version |         buffer.put((byte) 0x00); // version | ||||||
|         buffer.put(packet_flag);// type |         buffer.put(packetFlag); // type | ||||||
|  |  | ||||||
|         buffer.putLong(gran_pos);// granulate position |         buffer.putLong(granPos); // granulate position | ||||||
|  |  | ||||||
|         buffer.putInt(STREAM_ID);// bitstream serial number |         buffer.putInt(streamId); // bitstream serial number | ||||||
|         buffer.putInt(sequence_count++);// page sequence number |         buffer.putInt(sequenceCount++); // page sequence number | ||||||
|  |  | ||||||
|         buffer.putInt(0x00); // page checksum |         buffer.putInt(0x00); // page checksum | ||||||
|  |  | ||||||
|         buffer.put((byte) segment_table_size);// segment table |         buffer.put((byte) segmentTableSize); // segment table | ||||||
|         buffer.put(segment_table, 0, segment_table_size);// segment size |         buffer.put(segmentTable, 0, segmentTableSize); // segment size | ||||||
|  |  | ||||||
|         length += segment_table_size; |         length += segmentTableSize; | ||||||
|  |  | ||||||
|         clearSegmentTable(); // clear segment table for next header |         clearSegmentTable(); // clear segment table for next header | ||||||
|  |  | ||||||
|         int checksum_crc32 = calc_crc32(0x00, buffer.array(), length); |         int checksumCrc32 = calcCrc32(0x00, buffer.array(), length); | ||||||
|  |  | ||||||
|         if (immediate_page != null) { |         if (immediatePage != null) { | ||||||
|             checksum_crc32 = calc_crc32(checksum_crc32, immediate_page, immediate_page.length); |             checksumCrc32 = calcCrc32(checksumCrc32, immediatePage, immediatePage.length); | ||||||
|             buffer.putInt(HEADER_CHECKSUM_OFFSET, checksum_crc32); |             buffer.putInt(HEADER_CHECKSUM_OFFSET, checksumCrc32); | ||||||
|             segment_table_next_timestamp -= TIME_SCALE_NS; |             segmentTableNextTimestamp -= TIME_SCALE_NS; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return checksum_crc32; |         return checksumCrc32; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Nullable |     @Nullable | ||||||
|     private byte[] make_metadata() { |     private byte[] makeMetadata() { | ||||||
|         if ("A_OPUS".equals(webm_track.codecId)) { |         if ("A_OPUS".equals(webmTrack.codecId)) { | ||||||
|             return new byte[]{ |             return new byte[]{ | ||||||
|                     0x4F, 0x70, 0x75, 0x73, 0x54, 0x61, 0x67, 0x73, // "OpusTags" binary string |                     0x4F, 0x70, 0x75, 0x73, 0x54, 0x61, 0x67, 0x73, // "OpusTags" binary string | ||||||
|                     0x07, 0x00, 0x00, 0x00,// writting application string size |                     0x07, 0x00, 0x00, 0x00, // writing application string size | ||||||
|                     0x4E, 0x65, 0x77, 0x50, 0x69, 0x70, 0x65, // "NewPipe" binary string |                     0x4E, 0x65, 0x77, 0x50, 0x69, 0x70, 0x65, // "NewPipe" binary string | ||||||
|                     0x00, 0x00, 0x00, 0x00 // additional tags count (zero means no tags) |                     0x00, 0x00, 0x00, 0x00 // additional tags count (zero means no tags) | ||||||
|             }; |             }; | ||||||
|         } else if ("A_VORBIS".equals(webm_track.codecId)) { |         } else if ("A_VORBIS".equals(webmTrack.codecId)) { | ||||||
|             return new byte[]{ |             return new byte[]{ | ||||||
|                     0x03, // ???????? |                     0x03, // ???????? | ||||||
|                     0x76, 0x6f, 0x72, 0x62, 0x69, 0x73, // "vorbis" binary string |                     0x76, 0x6f, 0x72, 0x62, 0x69, 0x73, // "vorbis" binary string | ||||||
| @@ -303,51 +303,49 @@ public class OggFromWebMWriter implements Closeable { | |||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void write(ByteBuffer buffer) throws IOException { |     private void write(final ByteBuffer buffer) throws IOException { | ||||||
|         output.write(buffer.array(), 0, buffer.position()); |         output.write(buffer.array(), 0, buffer.position()); | ||||||
|         buffer.position(0); |         buffer.position(0); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     @Nullable |     @Nullable | ||||||
|     private SimpleBlock getNextBlock() throws IOException { |     private SimpleBlock getNextBlock() throws IOException { | ||||||
|         SimpleBlock res; |         SimpleBlock res; | ||||||
|  |  | ||||||
|         if (webm_block != null) { |         if (webmBlock != null) { | ||||||
|             res = webm_block; |             res = webmBlock; | ||||||
|             webm_block = null; |             webmBlock = null; | ||||||
|             return res; |             return res; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (webm_segment == null) { |         if (webmSegment == null) { | ||||||
|             webm_segment = webm.getNextSegment(); |             webmSegment = webm.getNextSegment(); | ||||||
|             if (webm_segment == null) { |             if (webmSegment == null) { | ||||||
|                 return null; // no more blocks in the selected track |                 return null; // no more blocks in the selected track | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (webm_cluster == null) { |         if (webmCluster == null) { | ||||||
|             webm_cluster = webm_segment.getNextCluster(); |             webmCluster = webmSegment.getNextCluster(); | ||||||
|             if (webm_cluster == null) { |             if (webmCluster == null) { | ||||||
|                 webm_segment = null; |                 webmSegment = null; | ||||||
|                 return getNextBlock(); |                 return getNextBlock(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         res = webm_cluster.getNextSimpleBlock(); |         res = webmCluster.getNextSimpleBlock(); | ||||||
|         if (res == null) { |         if (res == null) { | ||||||
|             webm_cluster = null; |             webmCluster = null; | ||||||
|             return getNextBlock(); |             return getNextBlock(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         webm_block_near_duration = res.absoluteTimeCodeNs - webm_block_last_timecode; |         webmBlockNearDuration = res.absoluteTimeCodeNs - webmBlockLastTimecode; | ||||||
|         webm_block_last_timecode = res.absoluteTimeCodeNs; |         webmBlockLastTimecode = res.absoluteTimeCodeNs; | ||||||
|  |  | ||||||
|         return res; |         return res; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private float getSampleFrequencyFromTrack(byte[] bMetadata) { |     private float getSampleFrequencyFromTrack(final byte[] bMetadata) { | ||||||
|         // hardcoded way |         // hardcoded way | ||||||
|         ByteBuffer buffer = ByteBuffer.wrap(bMetadata); |         ByteBuffer buffer = ByteBuffer.wrap(bMetadata); | ||||||
|  |  | ||||||
| @@ -362,15 +360,15 @@ public class OggFromWebMWriter implements Closeable { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void clearSegmentTable() { |     private void clearSegmentTable() { | ||||||
|         segment_table_next_timestamp += TIME_SCALE_NS; |         segmentTableNextTimestamp += TIME_SCALE_NS; | ||||||
|         packet_flag = FLAG_UNSET; |         packetFlag = FLAG_UNSET; | ||||||
|         segment_table_size = 0; |         segmentTableSize = 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private boolean addPacketSegment(SimpleBlock block) { |     private boolean addPacketSegment(final SimpleBlock block) { | ||||||
|         long timestamp = block.absoluteTimeCodeNs + webm_track.codecDelay; |         long timestamp = block.absoluteTimeCodeNs + webmTrack.codecDelay; | ||||||
|  |  | ||||||
|         if (timestamp >= segment_table_next_timestamp) { |         if (timestamp >= segmentTableNextTimestamp) { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -382,7 +380,7 @@ public class OggFromWebMWriter implements Closeable { | |||||||
|             throw new UnsupportedOperationException("page size cannot be larger than 65025"); |             throw new UnsupportedOperationException("page size cannot be larger than 65025"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         int available = (segment_table.length - segment_table_size) * 255; |         int available = (segmentTable.length - segmentTableSize) * 255; | ||||||
|         boolean extra = (size % 255) == 0; |         boolean extra = (size % 255) == 0; | ||||||
|  |  | ||||||
|         if (extra) { |         if (extra) { | ||||||
| @@ -397,17 +395,17 @@ public class OggFromWebMWriter implements Closeable { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         for (; size > 0; size -= 255) { |         for (; size > 0; size -= 255) { | ||||||
|             segment_table[segment_table_size++] = (byte) Math.min(size, 255); |             segmentTable[segmentTableSize++] = (byte) Math.min(size, 255); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (extra) { |         if (extra) { | ||||||
|             segment_table[segment_table_size++] = 0x00; |             segmentTable[segmentTableSize++] = 0x00; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void populate_crc32_table() { |     private void populateCrc32Table() { | ||||||
|         for (int i = 0; i < 0x100; i++) { |         for (int i = 0; i < 0x100; i++) { | ||||||
|             int crc = i << 24; |             int crc = i << 24; | ||||||
|             for (int j = 0; j < 8; j++) { |             for (int j = 0; j < 8; j++) { | ||||||
| @@ -415,17 +413,16 @@ public class OggFromWebMWriter implements Closeable { | |||||||
|                 crc <<= 1; |                 crc <<= 1; | ||||||
|                 crc ^= (int) (0x100000000L - b) & 0x04c11db7; |                 crc ^= (int) (0x100000000L - b) & 0x04c11db7; | ||||||
|             } |             } | ||||||
|             crc32_table[i] = crc; |             crc32Table[i] = crc; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private int calc_crc32(int initial_crc, byte[] buffer, int size) { |     private int calcCrc32(int initialCrc, final byte[] buffer, final int size) { | ||||||
|         for (int i = 0; i < size; i++) { |         for (int i = 0; i < size; i++) { | ||||||
|             int reg = (initial_crc >>> 24) & 0xff; |             int reg = (initialCrc >>> 24) & 0xff; | ||||||
|             initial_crc = (initial_crc << 8) ^ crc32_table[reg ^ (buffer[i] & 0xff)]; |             initialCrc = (initialCrc << 8) ^ crc32Table[reg ^ (buffer[i] & 0xff)]; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return initial_crc; |         return initialCrc; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -26,18 +26,19 @@ public class SrtFromTtmlWriter { | |||||||
|  |  | ||||||
|     private int frameIndex = 0; |     private int frameIndex = 0; | ||||||
|  |  | ||||||
|     public SrtFromTtmlWriter(SharpStream out, boolean ignoreEmptyFrames) { |     public SrtFromTtmlWriter(final SharpStream out, final boolean ignoreEmptyFrames) { | ||||||
|         this.out = out; |         this.out = out; | ||||||
|         this.ignoreEmptyFrames = ignoreEmptyFrames; |         this.ignoreEmptyFrames = ignoreEmptyFrames; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private static String getTimestamp(Element frame, String attr) { |     private static String getTimestamp(final Element frame, final String attr) { | ||||||
|         return frame |         return frame | ||||||
|                 .attr(attr) |                 .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(String.valueOf(frameIndex++)); | ||||||
|         writeString(NEW_LINE); |         writeString(NEW_LINE); | ||||||
|         writeString(begin); |         writeString(begin); | ||||||
| @@ -49,11 +50,11 @@ public class SrtFromTtmlWriter { | |||||||
|         writeString(NEW_LINE); |         writeString(NEW_LINE); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void writeString(String text) throws IOException { |     private void writeString(final String text) throws IOException { | ||||||
|         out.write(text.getBytes(charset)); |         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 |          * TTML parser with BASIC support | ||||||
|          * multiple CUE is not supported |          * multiple CUE is not supported | ||||||
| @@ -66,25 +67,32 @@ public class SrtFromTtmlWriter { | |||||||
|         // parse XML |         // parse XML | ||||||
|         byte[] buffer = new byte[(int) ttml.available()]; |         byte[] buffer = new byte[(int) ttml.available()]; | ||||||
|         ttml.read(buffer); |         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); |         StringBuilder text = new StringBuilder(128); | ||||||
|         Elements paragraph_list = doc.select("body > div > p"); |         Elements paragraphList = doc.select("body > div > p"); | ||||||
|  |  | ||||||
|         // check if has frames |         // 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); |             text.setLength(0); | ||||||
|  |  | ||||||
|             for (Node children : paragraph.childNodes()) { |             for (Node children : paragraph.childNodes()) { | ||||||
|                 if (children instanceof TextNode) |                 if (children instanceof TextNode) { | ||||||
|                     text.append(((TextNode) children).text()); |                     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); |                     text.append(NEW_LINE); | ||||||
|                 } |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|             if (ignoreEmptyFrames && text.length() < 1) continue; |             if (ignoreEmptyFrames && text.length() < 1) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             String begin = getTimestamp(paragraph, "begin"); |             String begin = getTimestamp(paragraph, "begin"); | ||||||
|             String end = getTimestamp(paragraph, "end"); |             String end = getTimestamp(paragraph, "end"); | ||||||
|   | |||||||
| @@ -14,36 +14,35 @@ import java.util.NoSuchElementException; | |||||||
|  * @author kapodamy |  * @author kapodamy | ||||||
|  */ |  */ | ||||||
| public class WebMReader { | public class WebMReader { | ||||||
|  |     private static final int ID_EMBL = 0x0A45DFA3; | ||||||
|  |     private static final int ID_EMBL_READ_VERSION = 0x02F7; | ||||||
|  |     private static final int ID_EMBL_DOC_TYPE = 0x0282; | ||||||
|  |     private static final int ID_EMBL_DOC_TYPE_READ_VERSION = 0x0285; | ||||||
|  |  | ||||||
|     private final static int ID_EMBL = 0x0A45DFA3; |     private static final int ID_SEGMENT = 0x08538067; | ||||||
|     private final static int ID_EMBLReadVersion = 0x02F7; |  | ||||||
|     private final static int ID_EMBLDocType = 0x0282; |  | ||||||
|     private final static int ID_EMBLDocTypeReadVersion = 0x0285; |  | ||||||
|  |  | ||||||
|     private final static int ID_Segment = 0x08538067; |     private static final int ID_INFO = 0x0549A966; | ||||||
|  |     private static final int ID_TIMECODE_SCALE = 0x0AD7B1; | ||||||
|  |     private static final int ID_DURATION = 0x489; | ||||||
|  |  | ||||||
|     private final static int ID_Info = 0x0549A966; |     private static final int ID_TRACKS = 0x0654AE6B; | ||||||
|     private final static int ID_TimecodeScale = 0x0AD7B1; |     private static final int ID_TRACK_ENTRY = 0x2E; | ||||||
|     private final static int ID_Duration = 0x489; |     private static final int ID_TRACK_NUMBER = 0x57; | ||||||
|  |     private static final int ID_TRACK_TYPE = 0x03; | ||||||
|  |     private static final int ID_CODEC_ID = 0x06; | ||||||
|  |     private static final int ID_CODEC_PRIVATE = 0x23A2; | ||||||
|  |     private static final int ID_VIDEO = 0x60; | ||||||
|  |     private static final int ID_AUDIO = 0x61; | ||||||
|  |     private static final int ID_DEFAULT_DURATION = 0x3E383; | ||||||
|  |     private static final int ID_FLAG_LACING = 0x1C; | ||||||
|  |     private static final int ID_CODEC_DELAY = 0x16AA; | ||||||
|  |     private static final int ID_SEEK_PRE_ROLL = 0x16BB; | ||||||
|  |  | ||||||
|     private final static int ID_Tracks = 0x0654AE6B; |     private static final int ID_CLUSTER = 0x0F43B675; | ||||||
|     private final static int ID_TrackEntry = 0x2E; |     private static final int ID_TIMECODE = 0x67; | ||||||
|     private final static int ID_TrackNumber = 0x57; |     private static final int ID_SIMPLE_BLOCK = 0x23; | ||||||
|     private final static int ID_TrackType = 0x03; |     private static final int ID_BLOCK = 0x21; | ||||||
|     private final static int ID_CodecID = 0x06; |     private static final int ID_GROUP_BLOCK = 0x20; | ||||||
|     private final static int ID_CodecPrivate = 0x23A2; |  | ||||||
|     private final static int ID_Video = 0x60; |  | ||||||
|     private final static int ID_Audio = 0x61; |  | ||||||
|     private final static int ID_DefaultDuration = 0x3E383; |  | ||||||
|     private final static int ID_FlagLacing = 0x1C; |  | ||||||
|     private final static int ID_CodecDelay = 0x16AA; |  | ||||||
|     private final static int ID_SeekPreRoll = 0x16BB; |  | ||||||
|  |  | ||||||
|     private final static int ID_Cluster = 0x0F43B675; |  | ||||||
|     private final static int ID_Timecode = 0x67; |  | ||||||
|     private final static int ID_SimpleBlock = 0x23; |  | ||||||
|     private final static int ID_Block = 0x21; |  | ||||||
|     private final static int ID_GroupBlock = 0x20; |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     public enum TrackKind { |     public enum TrackKind { | ||||||
| @@ -57,7 +56,7 @@ public class WebMReader { | |||||||
|     private boolean done; |     private boolean done; | ||||||
|     private boolean firstSegment; |     private boolean firstSegment; | ||||||
|  |  | ||||||
|     public WebMReader(SharpStream source) { |     public WebMReader(final SharpStream source) { | ||||||
|         this.stream = new DataReader(source); |         this.stream = new DataReader(source); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -68,7 +67,7 @@ public class WebMReader { | |||||||
|         } |         } | ||||||
|         ensure(elem); |         ensure(elem); | ||||||
|  |  | ||||||
|         elem = untilElement(null, ID_Segment); |         elem = untilElement(null, ID_SEGMENT); | ||||||
|         if (elem == null) { |         if (elem == null) { | ||||||
|             throw new IOException("Fragment element not found"); |             throw new IOException("Fragment element not found"); | ||||||
|         } |         } | ||||||
| @@ -83,7 +82,7 @@ public class WebMReader { | |||||||
|         return tracks; |         return tracks; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public WebMTrack selectTrack(int index) { |     public WebMTrack selectTrack(final int index) { | ||||||
|         selectedTrack = index; |         selectedTrack = index; | ||||||
|         return tracks[index]; |         return tracks[index]; | ||||||
|     } |     } | ||||||
| @@ -100,7 +99,7 @@ public class WebMReader { | |||||||
|  |  | ||||||
|         ensure(segment.ref); |         ensure(segment.ref); | ||||||
|         // WARNING: track cannot be the same or have different index in new segments |         // WARNING: track cannot be the same or have different index in new segments | ||||||
|         Element elem = untilElement(null, ID_Segment); |         Element elem = untilElement(null, ID_SEGMENT); | ||||||
|         if (elem == null) { |         if (elem == null) { | ||||||
|             done = true; |             done = true; | ||||||
|             return null; |             return null; | ||||||
| @@ -110,9 +109,7 @@ public class WebMReader { | |||||||
|         return segment; |         return segment; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private long readNumber(final Element parent) throws IOException { | ||||||
|  |  | ||||||
|     private long readNumber(Element parent) throws IOException { |  | ||||||
|         int length = (int) parent.contentSize; |         int length = (int) parent.contentSize; | ||||||
|         long value = 0; |         long value = 0; | ||||||
|         while (length-- > 0) { |         while (length-- > 0) { | ||||||
| @@ -125,11 +122,11 @@ public class WebMReader { | |||||||
|         return value; |         return value; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private String readString(Element parent) throws IOException { |     private String readString(final Element parent) throws IOException { | ||||||
|         return new String(readBlob(parent), StandardCharsets.UTF_8); // or use "utf-8" |         return new String(readBlob(parent), StandardCharsets.UTF_8); // or use "utf-8" | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private byte[] readBlob(Element parent) throws IOException { |     private byte[] readBlob(final Element parent) throws IOException { | ||||||
|         long length = parent.contentSize; |         long length = parent.contentSize; | ||||||
|         byte[] buffer = new byte[(int) length]; |         byte[] buffer = new byte[(int) length]; | ||||||
|         int read = stream.read(buffer); |         int read = stream.read(buffer); | ||||||
| @@ -180,16 +177,17 @@ public class WebMReader { | |||||||
|         return elem; |         return elem; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Element readElement(int expected) throws IOException { |     private Element readElement(final int expected) throws IOException { | ||||||
|         Element elem = readElement(); |         Element elem = readElement(); | ||||||
|         if (expected != 0 && elem.type != expected) { |         if (expected != 0 && elem.type != expected) { | ||||||
|             throw new NoSuchElementException("expected " + elementID(expected) + " found " + elementID(elem.type)); |             throw new NoSuchElementException("expected " + elementID(expected) | ||||||
|  |                     + " found " + elementID(elem.type)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return elem; |         return elem; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Element untilElement(Element ref, int... expected) throws IOException { |     private Element untilElement(final Element ref, final int... expected) throws IOException { | ||||||
|         Element elem; |         Element elem; | ||||||
|         while (ref == null ? stream.available() : (stream.position() < (ref.offset + ref.size))) { |         while (ref == null ? stream.available() : (stream.position() < (ref.offset + ref.size))) { | ||||||
|             elem = readElement(); |             elem = readElement(); | ||||||
| @@ -208,11 +206,11 @@ public class WebMReader { | |||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private String elementID(long type) { |     private String elementID(final long type) { | ||||||
|         return "0x".concat(Long.toHexString(type)); |         return "0x".concat(Long.toHexString(type)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void ensure(Element ref) throws IOException { |     private void ensure(final Element ref) throws IOException { | ||||||
|         long skip = (ref.offset + ref.size) - stream.position(); |         long skip = (ref.offset + ref.size) - stream.position(); | ||||||
|  |  | ||||||
|         if (skip == 0) { |         if (skip == 0) { | ||||||
| @@ -227,10 +225,9 @@ public class WebMReader { | |||||||
|         stream.skipBytes(skip); |         stream.skipBytes(skip); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private boolean readEbml(final Element ref, final int minReadVersion, | ||||||
|  |                              final int minDocTypeVersion) throws IOException { | ||||||
|     private boolean readEbml(Element ref, int minReadVersion, int minDocTypeVersion) throws IOException { |         Element elem = untilElement(ref, ID_EMBL_READ_VERSION); | ||||||
|         Element elem = untilElement(ref, ID_EMBLReadVersion); |  | ||||||
|         if (elem == null) { |         if (elem == null) { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
| @@ -238,28 +235,28 @@ public class WebMReader { | |||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         elem = untilElement(ref, ID_EMBLDocType); |         elem = untilElement(ref, ID_EMBL_DOC_TYPE); | ||||||
|         if (elem == null) { |         if (elem == null) { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|         if (!readString(elem).equals("webm")) { |         if (!readString(elem).equals("webm")) { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|         elem = untilElement(ref, ID_EMBLDocTypeReadVersion); |         elem = untilElement(ref, ID_EMBL_DOC_TYPE_READ_VERSION); | ||||||
|  |  | ||||||
|         return elem != null && readNumber(elem) <= minDocTypeVersion; |         return elem != null && readNumber(elem) <= minDocTypeVersion; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Info readInfo(Element ref) throws IOException { |     private Info readInfo(final Element ref) throws IOException { | ||||||
|         Element elem; |         Element elem; | ||||||
|         Info info = new Info(); |         Info info = new Info(); | ||||||
|  |  | ||||||
|         while ((elem = untilElement(ref, ID_TimecodeScale, ID_Duration)) != null) { |         while ((elem = untilElement(ref, ID_TIMECODE_SCALE, ID_DURATION)) != null) { | ||||||
|             switch (elem.type) { |             switch (elem.type) { | ||||||
|                 case ID_TimecodeScale: |                 case ID_TIMECODE_SCALE: | ||||||
|                     info.timecodeScale = readNumber(elem); |                     info.timecodeScale = readNumber(elem); | ||||||
|                     break; |                     break; | ||||||
|                 case ID_Duration: |                 case ID_DURATION: | ||||||
|                     info.duration = readNumber(elem); |                     info.duration = readNumber(elem); | ||||||
|                     break; |                     break; | ||||||
|             } |             } | ||||||
| @@ -273,19 +270,20 @@ public class WebMReader { | |||||||
|         return info; |         return info; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Segment readSegment(Element ref, int trackLacingExpected, boolean metadataExpected) throws IOException { |     private Segment readSegment(final Element ref, final int trackLacingExpected, | ||||||
|  |                                 final boolean metadataExpected) throws IOException { | ||||||
|         Segment obj = new Segment(ref); |         Segment obj = new Segment(ref); | ||||||
|         Element elem; |         Element elem; | ||||||
|         while ((elem = untilElement(ref, ID_Info, ID_Tracks, ID_Cluster)) != null) { |         while ((elem = untilElement(ref, ID_INFO, ID_TRACKS, ID_CLUSTER)) != null) { | ||||||
|             if (elem.type == ID_Cluster) { |             if (elem.type == ID_CLUSTER) { | ||||||
|                 obj.currentCluster = elem; |                 obj.currentCluster = elem; | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             switch (elem.type) { |             switch (elem.type) { | ||||||
|                 case ID_Info: |                 case ID_INFO: | ||||||
|                     obj.info = readInfo(elem); |                     obj.info = readInfo(elem); | ||||||
|                     break; |                     break; | ||||||
|                 case ID_Tracks: |                 case ID_TRACKS: | ||||||
|                     obj.tracks = readTracks(elem, trackLacingExpected); |                     obj.tracks = readTracks(elem, trackLacingExpected); | ||||||
|                     break; |                     break; | ||||||
|             } |             } | ||||||
| @@ -293,48 +291,50 @@ public class WebMReader { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (metadataExpected && (obj.info == null || obj.tracks == null)) { |         if (metadataExpected && (obj.info == null || obj.tracks == null)) { | ||||||
|             throw new RuntimeException("Cluster element found without Info and/or Tracks element at position " + String.valueOf(ref.offset)); |             throw new RuntimeException( | ||||||
|  |                     "Cluster element found without Info and/or Tracks element at position " | ||||||
|  |                             + String.valueOf(ref.offset)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return obj; |         return obj; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private WebMTrack[] readTracks(Element ref, int lacingExpected) throws IOException { |     private WebMTrack[] readTracks(final Element ref, final int lacingExpected) throws IOException { | ||||||
|         ArrayList<WebMTrack> trackEntries = new ArrayList<>(2); |         ArrayList<WebMTrack> trackEntries = new ArrayList<>(2); | ||||||
|         Element elem_trackEntry; |         Element elemTrackEntry; | ||||||
|  |  | ||||||
|         while ((elem_trackEntry = untilElement(ref, ID_TrackEntry)) != null) { |         while ((elemTrackEntry = untilElement(ref, ID_TRACK_ENTRY)) != null) { | ||||||
|             WebMTrack entry = new WebMTrack(); |             WebMTrack entry = new WebMTrack(); | ||||||
|             boolean drop = false; |             boolean drop = false; | ||||||
|             Element elem; |             Element elem; | ||||||
|             while ((elem = untilElement(elem_trackEntry)) != null) { |             while ((elem = untilElement(elemTrackEntry)) != null) { | ||||||
|                 switch (elem.type) { |                 switch (elem.type) { | ||||||
|                     case ID_TrackNumber: |                     case ID_TRACK_NUMBER: | ||||||
|                         entry.trackNumber = readNumber(elem); |                         entry.trackNumber = readNumber(elem); | ||||||
|                         break; |                         break; | ||||||
|                     case ID_TrackType: |                     case ID_TRACK_TYPE: | ||||||
|                         entry.trackType = (int) readNumber(elem); |                         entry.trackType = (int) readNumber(elem); | ||||||
|                         break; |                         break; | ||||||
|                     case ID_CodecID: |                     case ID_CODEC_ID: | ||||||
|                         entry.codecId = readString(elem); |                         entry.codecId = readString(elem); | ||||||
|                         break; |                         break; | ||||||
|                     case ID_CodecPrivate: |                     case ID_CODEC_PRIVATE: | ||||||
|                         entry.codecPrivate = readBlob(elem); |                         entry.codecPrivate = readBlob(elem); | ||||||
|                         break; |                         break; | ||||||
|                     case ID_Audio: |                     case ID_AUDIO: | ||||||
|                     case ID_Video: |                     case ID_VIDEO: | ||||||
|                         entry.bMetadata = readBlob(elem); |                         entry.bMetadata = readBlob(elem); | ||||||
|                         break; |                         break; | ||||||
|                     case ID_DefaultDuration: |                     case ID_DEFAULT_DURATION: | ||||||
|                         entry.defaultDuration = readNumber(elem); |                         entry.defaultDuration = readNumber(elem); | ||||||
|                         break; |                         break; | ||||||
|                     case ID_FlagLacing: |                     case ID_FLAG_LACING: | ||||||
|                         drop = readNumber(elem) != lacingExpected; |                         drop = readNumber(elem) != lacingExpected; | ||||||
|                         break; |                         break; | ||||||
|                     case ID_CodecDelay: |                     case ID_CODEC_DELAY: | ||||||
|                         entry.codecDelay = readNumber(elem); |                         entry.codecDelay = readNumber(elem); | ||||||
|                         break; |                         break; | ||||||
|                     case ID_SeekPreRoll: |                     case ID_SEEK_PRE_ROLL: | ||||||
|                         entry.seekPreRoll = readNumber(elem); |                         entry.seekPreRoll = readNumber(elem); | ||||||
|                         break; |                         break; | ||||||
|                     default: |                     default: | ||||||
| @@ -345,7 +345,7 @@ public class WebMReader { | |||||||
|             if (!drop) { |             if (!drop) { | ||||||
|                 trackEntries.add(entry); |                 trackEntries.add(entry); | ||||||
|             } |             } | ||||||
|             ensure(elem_trackEntry); |             ensure(elemTrackEntry); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         WebMTrack[] entries = new WebMTrack[trackEntries.size()]; |         WebMTrack[] entries = new WebMTrack[trackEntries.size()]; | ||||||
| @@ -368,37 +368,36 @@ public class WebMReader { | |||||||
|         return entries; |         return entries; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private SimpleBlock readSimpleBlock(Element ref) throws IOException { |     private SimpleBlock readSimpleBlock(final Element ref) throws IOException { | ||||||
|         SimpleBlock obj = new SimpleBlock(ref); |         SimpleBlock obj = new SimpleBlock(ref); | ||||||
|         obj.trackNumber = readEncodedNumber(); |         obj.trackNumber = readEncodedNumber(); | ||||||
|         obj.relativeTimeCode = stream.readShort(); |         obj.relativeTimeCode = stream.readShort(); | ||||||
|         obj.flags = (byte) stream.read(); |         obj.flags = (byte) stream.read(); | ||||||
|         obj.dataSize = (int) ((ref.offset + ref.size) - stream.position()); |         obj.dataSize = (int) ((ref.offset + ref.size) - stream.position()); | ||||||
|         obj.createdFromBlock = ref.type == ID_Block; |         obj.createdFromBlock = ref.type == ID_BLOCK; | ||||||
|  |  | ||||||
|         // NOTE: lacing is not implemented, and will be mixed with the stream data |         // NOTE: lacing is not implemented, and will be mixed with the stream data | ||||||
|         if (obj.dataSize < 0) { |         if (obj.dataSize < 0) { | ||||||
|             throw new IOException(String.format("Unexpected SimpleBlock element size, missing %s bytes", -obj.dataSize)); |             throw new IOException(String.format( | ||||||
|  |                     "Unexpected SimpleBlock element size, missing %s bytes", -obj.dataSize)); | ||||||
|         } |         } | ||||||
|         return obj; |         return obj; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Cluster readCluster(Element ref) throws IOException { |     private Cluster readCluster(final Element ref) throws IOException { | ||||||
|         Cluster obj = new Cluster(ref); |         Cluster obj = new Cluster(ref); | ||||||
|  |  | ||||||
|         Element elem = untilElement(ref, ID_Timecode); |         Element elem = untilElement(ref, ID_TIMECODE); | ||||||
|         if (elem == null) { |         if (elem == null) { | ||||||
|             throw new NoSuchElementException("Cluster at " + String.valueOf(ref.offset) + " without Timecode element"); |             throw new NoSuchElementException("Cluster at " + String.valueOf(ref.offset) | ||||||
|  |                     + " without Timecode element"); | ||||||
|         } |         } | ||||||
|         obj.timecode = readNumber(elem); |         obj.timecode = readNumber(elem); | ||||||
|  |  | ||||||
|         return obj; |         return obj; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     class Element { |     class Element { | ||||||
|  |  | ||||||
|         int type; |         int type; | ||||||
|         long offset; |         long offset; | ||||||
|         long contentSize; |         long contentSize; | ||||||
| @@ -406,13 +405,11 @@ public class WebMReader { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Info { |     public class Info { | ||||||
|  |  | ||||||
|         public long timecodeScale; |         public long timecodeScale; | ||||||
|         public long duration; |         public long duration; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class WebMTrack { |     public class WebMTrack { | ||||||
|  |  | ||||||
|         public long trackNumber; |         public long trackNumber; | ||||||
|         protected int trackType; |         protected int trackType; | ||||||
|         public String codecId; |         public String codecId; | ||||||
| @@ -425,8 +422,7 @@ public class WebMReader { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Segment { |     public class Segment { | ||||||
|  |         Segment(final Element ref) { | ||||||
|         Segment(Element ref) { |  | ||||||
|             this.ref = ref; |             this.ref = ref; | ||||||
|             this.firstClusterInSegment = true; |             this.firstClusterInSegment = true; | ||||||
|         } |         } | ||||||
| @@ -447,7 +443,7 @@ public class WebMReader { | |||||||
|             } |             } | ||||||
|             ensure(segment.currentCluster); |             ensure(segment.currentCluster); | ||||||
|  |  | ||||||
|             Element elem = untilElement(segment.ref, ID_Cluster); |             Element elem = untilElement(segment.ref, ID_CLUSTER); | ||||||
|             if (elem == null) { |             if (elem == null) { | ||||||
|                 return null; |                 return null; | ||||||
|             } |             } | ||||||
| @@ -459,11 +455,10 @@ public class WebMReader { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class SimpleBlock { |     public class SimpleBlock { | ||||||
|  |  | ||||||
|         public InputStream data; |         public InputStream data; | ||||||
|         public boolean createdFromBlock; |         public boolean createdFromBlock; | ||||||
|  |  | ||||||
|         SimpleBlock(Element ref) { |         SimpleBlock(final Element ref) { | ||||||
|             this.ref = ref; |             this.ref = ref; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -480,13 +475,12 @@ public class WebMReader { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class Cluster { |     public class Cluster { | ||||||
|  |  | ||||||
|         Element ref; |         Element ref; | ||||||
|         SimpleBlock currentSimpleBlock = null; |         SimpleBlock currentSimpleBlock = null; | ||||||
|         Element currentBlockGroup = null; |         Element currentBlockGroup = null; | ||||||
|         public long timecode; |         public long timecode; | ||||||
|  |  | ||||||
|         Cluster(Element ref) { |         Cluster(final Element ref) { | ||||||
|             this.ref = ref; |             this.ref = ref; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -508,14 +502,14 @@ public class WebMReader { | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             while (!insideClusterBounds()) { |             while (!insideClusterBounds()) { | ||||||
|                 Element elem = untilElement(ref, ID_SimpleBlock, ID_GroupBlock); |                 Element elem = untilElement(ref, ID_SIMPLE_BLOCK, ID_GROUP_BLOCK); | ||||||
|                 if (elem == null) { |                 if (elem == null) { | ||||||
|                     return null; |                     return null; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 if (elem.type == ID_GroupBlock) { |                 if (elem.type == ID_GROUP_BLOCK) { | ||||||
|                     currentBlockGroup = elem; |                     currentBlockGroup = elem; | ||||||
|                     elem = untilElement(currentBlockGroup, ID_Block); |                     elem = untilElement(currentBlockGroup, ID_BLOCK); | ||||||
|  |  | ||||||
|                     if (elem == null) { |                     if (elem == null) { | ||||||
|                         ensure(currentBlockGroup); |                         ensure(currentBlockGroup); | ||||||
| @@ -529,7 +523,8 @@ public class WebMReader { | |||||||
|                     currentSimpleBlock.data = stream.getView((int) currentSimpleBlock.dataSize); |                     currentSimpleBlock.data = stream.getView((int) currentSimpleBlock.dataSize); | ||||||
|  |  | ||||||
|                     // calculate the timestamp in nanoseconds |                     // calculate the timestamp in nanoseconds | ||||||
|                     currentSimpleBlock.absoluteTimeCodeNs = currentSimpleBlock.relativeTimeCode + this.timecode; |                     currentSimpleBlock.absoluteTimeCodeNs = currentSimpleBlock.relativeTimeCode | ||||||
|  |                             + this.timecode; | ||||||
|                     currentSimpleBlock.absoluteTimeCodeNs *= segment.info.timecodeScale; |                     currentSimpleBlock.absoluteTimeCodeNs *= segment.info.timecodeScale; | ||||||
|  |  | ||||||
|                     return currentSimpleBlock; |                     return currentSimpleBlock; | ||||||
| @@ -537,10 +532,7 @@ public class WebMReader { | |||||||
|  |  | ||||||
|                 ensure(elem); |                 ensure(elem); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -19,14 +19,13 @@ import java.util.ArrayList; | |||||||
|  * @author kapodamy |  * @author kapodamy | ||||||
|  */ |  */ | ||||||
| public class WebMWriter implements Closeable { | public class WebMWriter implements Closeable { | ||||||
|  |     private static final int BUFFER_SIZE = 8 * 1024; | ||||||
|     private final static int BUFFER_SIZE = 8 * 1024; |     private static final int DEFAULT_TIMECODE_SCALE = 1000000; | ||||||
|     private final static int DEFAULT_TIMECODE_SCALE = 1000000; |     private static final int INTERV = 100; // 100ms on 1000000us timecode scale | ||||||
|     private final static int INTERV = 100;// 100ms on 1000000us timecode scale |     private static final int DEFAULT_CUES_EACH_MS = 5000; // 5000ms on 1000000us timecode scale | ||||||
|     private final static int DEFAULT_CUES_EACH_MS = 5000;// 5000ms on 1000000us timecode scale |     private static final byte CLUSTER_HEADER_SIZE = 8; | ||||||
|     private final static byte CLUSTER_HEADER_SIZE = 8; |     private static final int CUE_RESERVE_SIZE = 65535; | ||||||
|     private final static int CUE_RESERVE_SIZE = 65535; |     private static final byte MINIMUM_EBML_VOID_SIZE = 4; | ||||||
|     private final static byte MINIMUM_EBML_VOID_SIZE = 4; |  | ||||||
|  |  | ||||||
|     private WebMReader.WebMTrack[] infoTracks; |     private WebMReader.WebMTrack[] infoTracks; | ||||||
|     private SharpStream[] sourceTracks; |     private SharpStream[] sourceTracks; | ||||||
| @@ -46,7 +45,7 @@ public class WebMWriter implements Closeable { | |||||||
|     private byte[] outBuffer; |     private byte[] outBuffer; | ||||||
|     private ByteBuffer outByteBuffer; |     private ByteBuffer outByteBuffer; | ||||||
|  |  | ||||||
|     public WebMWriter(SharpStream... source) { |     public WebMWriter(final SharpStream... source) { | ||||||
|         sourceTracks = source; |         sourceTracks = source; | ||||||
|         readers = new WebMReader[sourceTracks.length]; |         readers = new WebMReader[sourceTracks.length]; | ||||||
|         infoTracks = new WebMTrack[sourceTracks.length]; |         infoTracks = new WebMTrack[sourceTracks.length]; | ||||||
| @@ -55,7 +54,7 @@ public class WebMWriter implements Closeable { | |||||||
|         clustersOffsetsSizes = new ArrayList<>(256); |         clustersOffsetsSizes = new ArrayList<>(256); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public WebMTrack[] getTracksFromSource(int sourceIndex) throws IllegalStateException { |     public WebMTrack[] getTracksFromSource(final int sourceIndex) throws IllegalStateException { | ||||||
|         if (done) { |         if (done) { | ||||||
|             throw new IllegalStateException("already done"); |             throw new IllegalStateException("already done"); | ||||||
|         } |         } | ||||||
| @@ -85,7 +84,7 @@ public class WebMWriter implements Closeable { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void selectTracks(int... trackIndex) throws IOException { |     public void selectTracks(final int... trackIndex) throws IOException { | ||||||
|         try { |         try { | ||||||
|             readersSegment = new Segment[readers.length]; |             readersSegment = new Segment[readers.length]; | ||||||
|             readersCluster = new Cluster[readers.length]; |             readersCluster = new Cluster[readers.length]; | ||||||
| @@ -126,7 +125,7 @@ public class WebMWriter implements Closeable { | |||||||
|         clustersOffsetsSizes = null; |         clustersOffsetsSizes = null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void build(SharpStream out) throws IOException, RuntimeException { |     public void build(final SharpStream out) throws IOException, RuntimeException { | ||||||
|         if (!out.canRewind()) { |         if (!out.canRewind()) { | ||||||
|             throw new IOException("The output stream must be allow seek"); |             throw new IOException("The output stream must be allow seek"); | ||||||
|         } |         } | ||||||
| @@ -187,7 +186,7 @@ public class WebMWriter implements Closeable { | |||||||
|  |  | ||||||
|         // reserve space for Cues element |         // reserve space for Cues element | ||||||
|         long cueOffset = written; |         long cueOffset = written; | ||||||
|         make_EBML_void(out, CUE_RESERVE_SIZE, true); |         makeEbmlVoid(out, CUE_RESERVE_SIZE, true); | ||||||
|  |  | ||||||
|         int[] defaultSampleDuration = new int[infoTracks.length]; |         int[] defaultSampleDuration = new int[infoTracks.length]; | ||||||
|         long[] duration = new long[infoTracks.length]; |         long[] duration = new long[infoTracks.length]; | ||||||
| @@ -196,7 +195,8 @@ public class WebMWriter implements Closeable { | |||||||
|             if (infoTracks[i].defaultDuration < 0) { |             if (infoTracks[i].defaultDuration < 0) { | ||||||
|                 defaultSampleDuration[i] = -1; // not available |                 defaultSampleDuration[i] = -1; // not available | ||||||
|             } else { |             } else { | ||||||
|                 defaultSampleDuration[i] = (int) Math.ceil(infoTracks[i].defaultDuration / (float) DEFAULT_TIMECODE_SCALE); |                 defaultSampleDuration[i] = (int) Math.ceil(infoTracks[i].defaultDuration | ||||||
|  |                         / (float) DEFAULT_TIMECODE_SCALE); | ||||||
|             } |             } | ||||||
|             duration[i] = -1; |             duration[i] = -1; | ||||||
|         } |         } | ||||||
| @@ -239,15 +239,18 @@ public class WebMWriter implements Closeable { | |||||||
|                     newClusterByTrackId = -1; |                     newClusterByTrackId = -1; | ||||||
|                     baseTimecode = bloq.absoluteTimecode; |                     baseTimecode = bloq.absoluteTimecode; | ||||||
|                     limitTimecode = baseTimecode + INTERV; |                     limitTimecode = baseTimecode + INTERV; | ||||||
|                     currentClusterOffset = makeCluster(out, baseTimecode, currentClusterOffset, true); |                     currentClusterOffset = makeCluster(out, baseTimecode, currentClusterOffset, | ||||||
|  |                             true); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 if (cuesForTrackId == i) { |                 if (cuesForTrackId == i) { | ||||||
|                     if ((nextCueTime > -1 && bloq.absoluteTimecode >= nextCueTime) || (nextCueTime < 0 && bloq.isKeyframe())) { |                     if ((nextCueTime > -1 && bloq.absoluteTimecode >= nextCueTime) | ||||||
|  |                             || (nextCueTime < 0 && bloq.isKeyframe())) { | ||||||
|                         if (nextCueTime > -1) { |                         if (nextCueTime > -1) { | ||||||
|                             nextCueTime += DEFAULT_CUES_EACH_MS; |                             nextCueTime += DEFAULT_CUES_EACH_MS; | ||||||
|                         } |                         } | ||||||
|                         keyFrames.add(new KeyFrame(segmentOffset, currentClusterOffset, written, bloq.absoluteTimecode)); |                         keyFrames.add(new KeyFrame(segmentOffset, currentClusterOffset, written, | ||||||
|  |                                 bloq.absoluteTimecode)); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
| @@ -255,7 +258,8 @@ public class WebMWriter implements Closeable { | |||||||
|                 blockWritten++; |                 blockWritten++; | ||||||
|  |  | ||||||
|                 if (defaultSampleDuration[i] < 0 && duration[i] >= 0) { |                 if (defaultSampleDuration[i] < 0 && duration[i] >= 0) { | ||||||
|                     // if the sample duration in unknown, calculate using current_duration - previous_duration |                     // if the sample duration in unknown, | ||||||
|  |                     // calculate using current_duration - previous_duration | ||||||
|                     defaultSampleDuration[i] = (int) (bloq.absoluteTimecode - duration[i]); |                     defaultSampleDuration[i] = (int) (bloq.absoluteTimecode - duration[i]); | ||||||
|                 } |                 } | ||||||
|                 duration[i] = bloq.absoluteTimecode; |                 duration[i] = bloq.absoluteTimecode; | ||||||
| @@ -318,7 +322,7 @@ public class WebMWriter implements Closeable { | |||||||
|             dump(outBuffer, size, out); |             dump(outBuffer, size, out); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         make_EBML_void(out, CUE_RESERVE_SIZE - cueSize - 7, false); |         makeEbmlVoid(out, CUE_RESERVE_SIZE - cueSize - 7, false); | ||||||
|  |  | ||||||
|         seekTo(out, cueOffset + 5); |         seekTo(out, cueOffset + 5); | ||||||
|         outByteBuffer.putShort(0, cueSize); |         outByteBuffer.putShort(0, cueSize); | ||||||
| @@ -332,7 +336,7 @@ public class WebMWriter implements Closeable { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Block getNextBlockFrom(int internalTrackId) throws IOException { |     private Block getNextBlockFrom(final int internalTrackId) throws IOException { | ||||||
|         if (readersSegment[internalTrackId] == null) { |         if (readersSegment[internalTrackId] == null) { | ||||||
|             readersSegment[internalTrackId] = readers[internalTrackId].getNextSegment(); |             readersSegment[internalTrackId] = readers[internalTrackId].getNextSegment(); | ||||||
|             if (readersSegment[internalTrackId] == null) { |             if (readersSegment[internalTrackId] == null) { | ||||||
| @@ -364,7 +368,7 @@ public class WebMWriter implements Closeable { | |||||||
|         return bloq; |         return bloq; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void seekTo(SharpStream stream, long offset) throws IOException { |     private void seekTo(final SharpStream stream, final long offset) throws IOException { | ||||||
|         if (stream.canSeek()) { |         if (stream.canSeek()) { | ||||||
|             stream.seek(offset); |             stream.seek(offset); | ||||||
|         } else { |         } else { | ||||||
| @@ -379,13 +383,15 @@ public class WebMWriter implements Closeable { | |||||||
|         written = offset; |         written = offset; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void writeInt(SharpStream stream, long offset, int number) throws IOException { |     private void writeInt(final SharpStream stream, final long offset, final int number) | ||||||
|  |             throws IOException { | ||||||
|         seekTo(stream, offset); |         seekTo(stream, offset); | ||||||
|         outByteBuffer.putInt(0, number); |         outByteBuffer.putInt(0, number); | ||||||
|         dump(outBuffer, DataReader.INTEGER_SIZE, stream); |         dump(outBuffer, DataReader.INTEGER_SIZE, stream); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void writeBlock(SharpStream stream, Block bloq, long clusterTimecode) throws IOException { |     private void writeBlock(final SharpStream stream, final Block bloq, final long clusterTimecode) | ||||||
|  |             throws IOException { | ||||||
|         long relativeTimeCode = bloq.absoluteTimecode - clusterTimecode; |         long relativeTimeCode = bloq.absoluteTimecode - clusterTimecode; | ||||||
|  |  | ||||||
|         if (relativeTimeCode < Short.MIN_VALUE || relativeTimeCode > Short.MAX_VALUE) { |         if (relativeTimeCode < Short.MIN_VALUE || relativeTimeCode > Short.MAX_VALUE) { | ||||||
| @@ -396,7 +402,8 @@ public class WebMWriter implements Closeable { | |||||||
|         listBuffer.add(new byte[]{(byte) 0xa3}); |         listBuffer.add(new byte[]{(byte) 0xa3}); | ||||||
|         listBuffer.add(null); // block size |         listBuffer.add(null); // block size | ||||||
|         listBuffer.add(encode(bloq.trackNumber + 1, false)); |         listBuffer.add(encode(bloq.trackNumber + 1, false)); | ||||||
|         listBuffer.add(ByteBuffer.allocate(DataReader.SHORT_SIZE).putShort((short) relativeTimeCode).array()); |         listBuffer.add(ByteBuffer.allocate(DataReader.SHORT_SIZE).putShort((short) relativeTimeCode) | ||||||
|  |                 .array()); | ||||||
|         listBuffer.add(new byte[]{bloq.flags}); |         listBuffer.add(new byte[]{bloq.flags}); | ||||||
|  |  | ||||||
|         int blockSize = bloq.dataSize; |         int blockSize = bloq.dataSize; | ||||||
| @@ -413,7 +420,8 @@ public class WebMWriter implements Closeable { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private long makeCluster(SharpStream stream, long timecode, long offset, boolean create) throws IOException { |     private long makeCluster(final SharpStream stream, final long timecode, long offset, | ||||||
|  |                              final boolean create) throws IOException { | ||||||
|         ClusterInfo cluster; |         ClusterInfo cluster; | ||||||
|  |  | ||||||
|         if (offset > 0) { |         if (offset > 0) { | ||||||
| @@ -444,7 +452,7 @@ public class WebMWriter implements Closeable { | |||||||
|         return offset; |         return offset; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void makeEBML(SharpStream stream) throws IOException { |     private void makeEBML(final SharpStream stream) throws IOException { | ||||||
|         // deafult values |         // deafult values | ||||||
|         dump(new byte[]{ |         dump(new byte[]{ | ||||||
|                 0x1A, 0x45, (byte) 0xDF, (byte) 0xA3, 0x01, 0x00, 0x00, 0x00, |                 0x1A, 0x45, (byte) 0xDF, (byte) 0xA3, 0x01, 0x00, 0x00, 0x00, | ||||||
| @@ -468,7 +476,7 @@ public class WebMWriter implements Closeable { | |||||||
|         return lengthFor(buffer); |         return lengthFor(buffer); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private ArrayList<byte[]> makeTrackEntry(int internalTrackId, WebMTrack track) { |     private ArrayList<byte[]> makeTrackEntry(final int internalTrackId, final WebMTrack track) { | ||||||
|         byte[] id = encode(internalTrackId + 1, true); |         byte[] id = encode(internalTrackId + 1, true); | ||||||
|         ArrayList<byte[]> buffer = new ArrayList<>(12); |         ArrayList<byte[]> buffer = new ArrayList<>(12); | ||||||
|  |  | ||||||
| @@ -531,10 +539,10 @@ public class WebMWriter implements Closeable { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         return lengthFor(buffer); |         return lengthFor(buffer); | ||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private int makeCuePoint(int internalTrackId, KeyFrame keyFrame, byte[] buffer) { |     private int makeCuePoint(final int internalTrackId, final KeyFrame keyFrame, | ||||||
|  |                              final byte[] buffer) { | ||||||
|         ArrayList<byte[]> cue = new ArrayList<>(5); |         ArrayList<byte[]> cue = new ArrayList<>(5); | ||||||
|  |  | ||||||
|         /* CuePoint */ |         /* CuePoint */ | ||||||
| @@ -559,7 +567,8 @@ public class WebMWriter implements Closeable { | |||||||
|         return size; |         return size; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private ArrayList<byte[]> makeCueTrackPosition(int internalTrackId, KeyFrame keyFrame) { |     private ArrayList<byte[]> makeCueTrackPosition(final int internalTrackId, | ||||||
|  |                                                    final KeyFrame keyFrame) { | ||||||
|         ArrayList<byte[]> buffer = new ArrayList<>(8); |         ArrayList<byte[]> buffer = new ArrayList<>(8); | ||||||
|  |  | ||||||
|         /* CueTrackPositions */ |         /* CueTrackPositions */ | ||||||
| @@ -583,7 +592,8 @@ public class WebMWriter implements Closeable { | |||||||
|         return lengthFor(buffer); |         return lengthFor(buffer); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void make_EBML_void(SharpStream out, int size, boolean wipe) throws IOException { |     private void makeEbmlVoid(final SharpStream out, int size, final boolean wipe) | ||||||
|  |             throws IOException { | ||||||
|         /* ebml void */ |         /* ebml void */ | ||||||
|         outByteBuffer.putShort(0, (short) 0xec20); |         outByteBuffer.putShort(0, (short) 0xec20); | ||||||
|         outByteBuffer.putShort(2, (short) (size - 4)); |         outByteBuffer.putShort(2, (short) (size - 4)); | ||||||
| @@ -600,23 +610,25 @@ public class WebMWriter implements Closeable { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void dump(byte[] buffer, SharpStream stream) throws IOException { |     private void dump(final byte[] buffer, final SharpStream stream) throws IOException { | ||||||
|         dump(buffer, buffer.length, stream); |         dump(buffer, buffer.length, stream); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void dump(byte[] buffer, int count, SharpStream stream) throws IOException { |     private void dump(final byte[] buffer, final int count, final SharpStream stream) | ||||||
|  |             throws IOException { | ||||||
|         stream.write(buffer, 0, count); |         stream.write(buffer, 0, count); | ||||||
|         written += count; |         written += count; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void dump(ArrayList<byte[]> buffers, SharpStream stream) throws IOException { |     private void dump(final ArrayList<byte[]> buffers, final SharpStream stream) | ||||||
|  |             throws IOException { | ||||||
|         for (byte[] buffer : buffers) { |         for (byte[] buffer : buffers) { | ||||||
|             stream.write(buffer); |             stream.write(buffer); | ||||||
|             written += buffer.length; |             written += buffer.length; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private ArrayList<byte[]> lengthFor(ArrayList<byte[]> buffer) { |     private ArrayList<byte[]> lengthFor(final ArrayList<byte[]> buffer) { | ||||||
|         long size = 0; |         long size = 0; | ||||||
|         for (int i = 2; i < buffer.size(); i++) { |         for (int i = 2; i < buffer.size(); i++) { | ||||||
|             size += buffer.get(i).length; |             size += buffer.get(i).length; | ||||||
| @@ -625,7 +637,7 @@ public class WebMWriter implements Closeable { | |||||||
|         return buffer; |         return buffer; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private byte[] encode(long number, boolean withLength) { |     private byte[] encode(final long number, final boolean withLength) { | ||||||
|         int length = -1; |         int length = -1; | ||||||
|         for (int i = 1; i <= 7; i++) { |         for (int i = 1; i <= 7; i++) { | ||||||
|             if (number < Math.pow(2, 7 * i)) { |             if (number < Math.pow(2, 7 * i)) { | ||||||
| @@ -662,7 +674,7 @@ public class WebMWriter implements Closeable { | |||||||
|         return buffer; |         return buffer; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private ArrayList<byte[]> encode(String value) { |     private ArrayList<byte[]> encode(final String value) { | ||||||
|         byte[] str; |         byte[] str; | ||||||
|         str = value.getBytes(StandardCharsets.UTF_8); // or use "utf-8" |         str = value.getBytes(StandardCharsets.UTF_8); // or use "utf-8" | ||||||
|  |  | ||||||
| @@ -673,7 +685,7 @@ public class WebMWriter implements Closeable { | |||||||
|         return buffer; |         return buffer; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private boolean valid(byte[] buffer) { |     private boolean valid(final byte[] buffer) { | ||||||
|         return buffer != null && buffer.length > 0; |         return buffer != null && buffer.length > 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -717,8 +729,7 @@ public class WebMWriter implements Closeable { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     class KeyFrame { |     class KeyFrame { | ||||||
|  |         KeyFrame(final long segment, final long cluster, final long block, final long timecode) { | ||||||
|         KeyFrame(long segment, long cluster, long block, long timecode) { |  | ||||||
|             clusterPosition = cluster - segment; |             clusterPosition = cluster - segment; | ||||||
|             relativePosition = (int) (block - cluster - CLUSTER_HEADER_SIZE); |             relativePosition = (int) (block - cluster - CLUSTER_HEADER_SIZE); | ||||||
|             duration = timecode; |             duration = timecode; | ||||||
| @@ -730,7 +741,6 @@ public class WebMWriter implements Closeable { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     class Block { |     class Block { | ||||||
|  |  | ||||||
|         InputStream data; |         InputStream data; | ||||||
|         int trackNumber; |         int trackNumber; | ||||||
|         byte flags; |         byte flags; | ||||||
| @@ -744,14 +754,13 @@ public class WebMWriter implements Closeable { | |||||||
|         @NonNull |         @NonNull | ||||||
|         @Override |         @Override | ||||||
|         public String toString() { |         public String toString() { | ||||||
|             return String.format("trackNumber=%s  isKeyFrame=%S  absoluteTimecode=%s", trackNumber, isKeyframe(), absoluteTimecode); |             return String.format("trackNumber=%s  isKeyFrame=%S  absoluteTimecode=%s", trackNumber, | ||||||
|  |                     isKeyframe(), absoluteTimecode); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     class ClusterInfo { |     class ClusterInfo { | ||||||
|  |  | ||||||
|         long offset; |         long offset; | ||||||
|         int size; |         int size; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,15 +4,14 @@ import java.io.Closeable; | |||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * based on c# |  * Based on C#'s Stream class. | ||||||
|  */ |  */ | ||||||
| public abstract class SharpStream  implements Closeable { | public abstract class SharpStream  implements Closeable { | ||||||
|  |  | ||||||
|     public abstract int read() throws IOException; |     public abstract int read() throws IOException; | ||||||
|  |  | ||||||
|     public abstract int read(byte buffer[]) throws IOException; |     public abstract int read(byte[] buffer) throws IOException; | ||||||
|  |  | ||||||
|     public abstract int read(byte buffer[], int offset, int count) 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 skip(long amount) throws IOException; | ||||||
|  |  | ||||||
| @@ -49,11 +48,11 @@ public abstract class SharpStream  implements Closeable { | |||||||
|         // STUB |         // STUB | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void setLength(long length) throws IOException { |     public void setLength(final long length) throws IOException { | ||||||
|         throw new IOException("Not implemented"); |         throw new IOException("Not implemented"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void seek(long offset) throws IOException { |     public void seek(final long offset) throws IOException { | ||||||
|         throw new IOException("Not implemented"); |         throw new IOException("Not implemented"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,7 +6,38 @@ | |||||||
|   <suppress checks="FinalParameters" |   <suppress checks="FinalParameters" | ||||||
|     files="LocalItemListAdapter.java" |     files="LocalItemListAdapter.java" | ||||||
|     lines="220,292"/> |     lines="220,292"/> | ||||||
|  |  | ||||||
|   <suppress checks="FinalParameters" |   <suppress checks="FinalParameters" | ||||||
|     files="InfoListAdapter.java" |     files="InfoListAdapter.java" | ||||||
|     lines="253,325"/> |     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> | </suppressions> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 wb9688
					wb9688