www in docker support

This commit is contained in:
MaddoScientisto 2026-04-22 18:41:37 +02:00
commit c227fce036
2145 changed files with 399596 additions and 58 deletions

View file

@ -0,0 +1,29 @@
package org.jcodec.containers.mps;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import org.jcodec.common.Demuxer;
import org.jcodec.common.DemuxerTrack;
import org.jcodec.common.DemuxerTrackMeta;
import org.jcodec.common.model.Packet;
public interface MPEGDemuxer extends Demuxer {
List<? extends MPEGDemuxerTrack> getTracks();
List<? extends MPEGDemuxerTrack> getVideoTracks();
List<? extends MPEGDemuxerTrack> getAudioTracks();
public static interface MPEGDemuxerTrack extends DemuxerTrack {
Packet nextFrameWithBuffer(ByteBuffer param1ByteBuffer) throws IOException;
DemuxerTrackMeta getMeta();
int getSid();
List<PESPacket> getPending();
void ignore();
}
}

View file

@ -0,0 +1,35 @@
package org.jcodec.containers.mps;
import java.nio.ByteBuffer;
import org.jcodec.common.model.Packet;
import org.jcodec.common.model.TapeTimecode;
public class MPEGPacket extends Packet {
private long offset;
private ByteBuffer seq;
private int gop;
private int timecode;
public MPEGPacket(ByteBuffer data, long pts, int timescale, long duration, long frameNo, Packet.FrameType keyFrame, TapeTimecode tapeTimecode) {
super(data, pts, timescale, duration, frameNo, keyFrame, tapeTimecode, 0);
}
public long getOffset() {
return this.offset;
}
public ByteBuffer getSeq() {
return this.seq;
}
public int getGOP() {
return this.gop;
}
public int getTimecode() {
return this.timecode;
}
}

View file

@ -0,0 +1,497 @@
package org.jcodec.containers.mps;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jcodec.codecs.aac.AACConts;
import org.jcodec.codecs.aac.ADTSParser;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.mpeg12.MPEGDecoder;
import org.jcodec.codecs.mpeg12.MPEGES;
import org.jcodec.codecs.mpeg12.SegmentReader;
import org.jcodec.common.DemuxerTrackMeta;
import org.jcodec.common.IntIntHistogram;
import org.jcodec.common.LongArrayList;
import org.jcodec.common.TrackType;
import org.jcodec.common.UsedViaReflection;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.model.Packet;
public class MPSDemuxer extends SegmentReader implements MPEGDemuxer {
private static final int BUFFER_SIZE = 1048576;
private Map<Integer, BaseTrack> streams;
private ReadableByteChannel channel;
private List<ByteBuffer> bufPool;
public MPSDemuxer(ReadableByteChannel channel) throws IOException {
super(channel, 4096);
this.streams = new HashMap<>();
this.channel = channel;
this.bufPool = new ArrayList<>();
findStreams();
}
protected void findStreams() throws IOException {
for (int i = 0; i == 0 || (i < 5 * this.streams.size() && this.streams.size() < 2); i++) {
PESPacket nextPacket = nextPacket(getBuffer());
if (nextPacket == null)
break;
addToStream(nextPacket);
}
}
public ByteBuffer getBuffer() {
synchronized (this.bufPool) {
if (this.bufPool.size() > 0)
return (ByteBuffer)this.bufPool.remove(0);
}
return ByteBuffer.allocate(1048576);
}
public void putBack(ByteBuffer buffer) {
buffer.clear();
synchronized (this.bufPool) {
this.bufPool.add(buffer);
}
}
public static abstract class BaseTrack implements MPEGDemuxer.MPEGDemuxerTrack {
protected int streamId;
protected List<PESPacket> _pending;
protected MPSDemuxer demuxer;
public BaseTrack(MPSDemuxer demuxer, int streamId, PESPacket pkt) throws IOException {
this._pending = new ArrayList<>();
this.demuxer = demuxer;
this.streamId = streamId;
this._pending.add(pkt);
}
public int getSid() {
return this.streamId;
}
public void pending(PESPacket pkt) {
if (this._pending != null) {
this._pending.add(pkt);
} else {
this.demuxer.putBack(pkt.data);
}
}
public List<PESPacket> getPending() {
return this._pending;
}
public void ignore() {
if (this._pending == null)
return;
for (PESPacket pesPacket : this._pending)
this.demuxer.putBack(pesPacket.data);
this._pending = null;
}
}
public static class MPEGTrack extends BaseTrack implements ReadableByteChannel {
private MPEGES es;
private LongArrayList ptsSeen;
private long lastPts;
private int lastSeq;
private int lastSeqSeen;
private int seqWrap;
private IntIntHistogram durationHistogram;
public MPEGTrack(MPSDemuxer demuxer, int streamId, PESPacket pkt) throws IOException {
super(demuxer, streamId, pkt);
this.es = new MPEGES(this, 4096);
this.ptsSeen = new LongArrayList(32);
this.lastSeq = Integer.MIN_VALUE;
this.lastSeqSeen = 2147482647;
this.seqWrap = 2147482647;
this.durationHistogram = new IntIntHistogram();
}
public boolean isOpen() {
return true;
}
public MPEGES getES() {
return this.es;
}
public void close() throws IOException {}
public int read(ByteBuffer arg0) throws IOException {
PESPacket pes = (this._pending.size() > 0) ? (PESPacket)this._pending.remove(0) : getPacket();
if (pes == null || !pes.data.hasRemaining())
return -1;
int toRead = Math.min(arg0.remaining(), pes.data.remaining());
arg0.put(NIOUtils.read(pes.data, toRead));
if (pes.data.hasRemaining()) {
this._pending.add(0, pes);
} else {
this.demuxer.putBack(pes.data);
}
return toRead;
}
private PESPacket getPacket() throws IOException {
if (this._pending.size() > 0)
return (PESPacket)this._pending.remove(0);
PESPacket pkt;
while ((pkt = this.demuxer.nextPacket(this.demuxer.getBuffer())) != null) {
if (pkt.streamId == this.streamId) {
if (pkt.pts != -1L)
this.ptsSeen.add(pkt.pts);
return pkt;
}
this.demuxer.addToStream(pkt);
}
return null;
}
public Packet nextFrameWithBuffer(ByteBuffer buf) throws IOException {
return this.es.frame(buf);
}
public Packet nextFrame() throws IOException {
MPEGPacket pkt = this.es.getFrame();
if (pkt == null)
return null;
int seq = MPEGDecoder.getSequenceNumber(pkt.getData());
if (seq == 0)
this.seqWrap = this.lastSeqSeen + 1;
this.lastSeqSeen = seq;
if (this.ptsSeen.size() <= 0) {
pkt.setPts((long)(Math.min(seq - this.lastSeq, seq - this.lastSeq + this.seqWrap) * this.durationHistogram.max()) + this.lastPts);
} else {
pkt.setPts(this.ptsSeen.shift());
if (this.lastSeq >= 0 && seq > this.lastSeq)
this.durationHistogram.increment((int)(pkt.getPts() - this.lastPts) /
Math.min(seq - this.lastSeq, seq - this.lastSeq + this.seqWrap));
this.lastPts = pkt.getPts();
this.lastSeq = seq;
}
pkt.setDuration((long)this.durationHistogram.max());
System.out.println(seq);
return pkt;
}
public DemuxerTrackMeta getMeta() {
return null;
}
}
public static class PlainTrack extends BaseTrack {
private int frameNo;
private Packet lastFrame;
private long lastKnownDuration = 3003L;
public PlainTrack(MPSDemuxer demuxer, int streamId, PESPacket pkt) throws IOException {
super(demuxer, streamId, pkt);
}
public boolean isOpen() {
return true;
}
public void close() throws IOException {}
public Packet nextFrameWithBuffer(ByteBuffer buf) throws IOException {
PESPacket pkt;
if (this._pending.size() > 0) {
pkt = (PESPacket)this._pending.remove(0);
} else {
while ((pkt = this.demuxer.nextPacket(this.demuxer.getBuffer())) != null && pkt.streamId != this.streamId)
this.demuxer.addToStream(pkt);
}
return (pkt == null) ? null : Packet.createPacket(pkt.data, pkt.pts, 90000, 0L, (long)this.frameNo++, Packet.FrameType.UNKNOWN, null);
}
public Packet nextFrame() throws IOException {
if (this.lastFrame == null)
this.lastFrame = nextFrameWithBuffer(null);
if (this.lastFrame == null)
return null;
Packet toReturn = this.lastFrame;
this.lastFrame = nextFrameWithBuffer(null);
if (this.lastFrame != null)
this.lastKnownDuration = this.lastFrame.getPts() - toReturn.getPts();
toReturn.setDuration(this.lastKnownDuration);
return toReturn;
}
public DemuxerTrackMeta getMeta() {
TrackType t = MPSUtils.videoStream(this.streamId) ? TrackType.VIDEO : (MPSUtils.audioStream(this.streamId) ? TrackType.AUDIO : TrackType.OTHER);
return null;
}
}
public static class AACTrack extends PlainTrack {
private List<Packet> audioStash;
public AACTrack(MPSDemuxer demuxer, int streamId, PESPacket pkt) throws IOException {
super(demuxer, streamId, pkt);
this.audioStash = new ArrayList<>();
}
public Packet nextFrame() throws IOException {
if (this.audioStash.size() == 0) {
Packet nextFrame = nextFrameWithBuffer(null);
if (nextFrame != null) {
ByteBuffer data = nextFrame.getData();
ADTSParser.Header adts = ADTSParser.read(data.duplicate());
long nextPts = nextFrame.getPts();
while (data.hasRemaining()) {
ByteBuffer data2 = NIOUtils.read(data, adts.getSize());
Packet pkt = Packet.createPacketWithData(nextFrame, data2);
pkt.setDuration(
(long)(pkt.getTimescale() * 1024 / AACConts.AAC_SAMPLE_RATES[adts.getSamplingIndex()]));
pkt.setPts(nextPts);
nextPts += pkt.getDuration();
this.audioStash.add(pkt);
if (data.hasRemaining())
adts = ADTSParser.read(data.duplicate());
}
}
}
if (this.audioStash.size() == 0)
return null;
return (Packet)this.audioStash.remove(0);
}
}
public void reset() {
for (BaseTrack track : this.streams.values())
track._pending.clear();
}
private void addToStream(PESPacket pkt) throws IOException {
BaseTrack pes = this.streams.get(Integer.valueOf(pkt.streamId));
if (pes == null) {
if (isMPEG(pkt.data)) {
pes = new MPEGTrack(this, pkt.streamId, pkt);
} else if (isAAC(pkt.data)) {
pes = new AACTrack(this, pkt.streamId, pkt);
} else {
pes = new PlainTrack(this, pkt.streamId, pkt);
}
this.streams.put(Integer.valueOf(pkt.streamId), pes);
} else {
pes.pending(pkt);
}
}
public PESPacket nextPacket(ByteBuffer out) throws IOException {
ByteBuffer dup = out.duplicate();
while (!MPSUtils.psMarker(this.curMarker)) {
if (!skipToMarker())
return null;
}
ByteBuffer fork = dup.duplicate();
readToNextMarker(dup);
PESPacket pkt = MPSUtils.readPESHeader(fork, curPos());
if (pkt.length == 0) {
while (!MPSUtils.psMarker(this.curMarker) && readToNextMarker(dup));
} else {
read(dup, pkt.length - dup.position() + 6);
}
fork.limit(dup.position());
pkt.data = fork;
return pkt;
}
public List<MPEGDemuxer.MPEGDemuxerTrack> getTracks() {
return new ArrayList<>(this.streams.values());
}
public List<MPEGDemuxer.MPEGDemuxerTrack> getVideoTracks() {
List<MPEGDemuxer.MPEGDemuxerTrack> result = new ArrayList<>();
for (BaseTrack p : this.streams.values()) {
if (MPSUtils.videoStream(p.streamId))
result.add(p);
}
return result;
}
public List<MPEGDemuxer.MPEGDemuxerTrack> getAudioTracks() {
List<MPEGDemuxer.MPEGDemuxerTrack> result = new ArrayList<>();
for (BaseTrack p : this.streams.values()) {
if (MPSUtils.audioStream(p.streamId))
result.add(p);
}
return result;
}
private boolean isAAC(ByteBuffer _data) {
ADTSParser.Header read = ADTSParser.read(_data.duplicate());
return (read != null);
}
private boolean isMPEG(ByteBuffer _data) {
ByteBuffer b = _data.duplicate();
int marker = -1;
int score = 0;
boolean hasHeader = false, slicesStarted = false;
while (b.hasRemaining()) {
int code = b.get() & 0xFF;
marker = marker << 8 | code;
if (marker < 256 || marker > 440)
continue;
if (marker >= 432 && marker <= 440) {
if ((hasHeader && marker != 437 && marker != 434) || slicesStarted)
break;
score += 5;
continue;
}
if (marker == 256) {
if (slicesStarted)
break;
hasHeader = true;
continue;
}
if (marker > 256 && marker < 432) {
if (!hasHeader)
break;
if (!slicesStarted) {
score += 50;
slicesStarted = true;
}
score++;
}
}
return (score > 50);
}
@UsedViaReflection
public static int probe(ByteBuffer b_) {
ByteBuffer b = b_.duplicate();
int marker = -1;
int sliceSize = 0;
boolean videoPes = false;
int state = 0;
int errors = 0;
boolean inNALUnit = false;
List<NALUnit> nuSeq = new ArrayList<>();
while (b.hasRemaining()) {
int code = b.get() & 0xFF;
if (state >= 3)
sliceSize++;
marker = marker << 8 | code;
if (inNALUnit) {
NALUnit nu = NALUnit.read(NIOUtils.asByteBufferInt(new int[] { code }));
if (nu.type != null)
nuSeq.add(nu);
inNALUnit = false;
}
if (videoPes && marker == 1) {
inNALUnit = true;
continue;
}
if (marker < 256 || marker > 511)
continue;
if (marker >= 442) {
videoPes = MPSUtils.videoMarker(marker);
continue;
}
if (!videoPes)
continue;
boolean stop = false;
switch (state) {
case 0:
if (marker >= 432 && marker <= 440) {
state = 1;
} else if (marker == 256) {
state = 2;
} else {
state = 0;
}
break;
case 1:
if (marker == 256) {
state = 2;
} else if (marker >= 432 && marker <= 440) {
state = 1;
} else {
errors++;
}
break;
case 2:
if (marker == 257) {
state = 3;
} else if (marker == 437 || marker == 434) {
state = 2;
} else {
errors++;
}
break;
default:
if (state > 3 && sliceSize < 1)
errors++;
sliceSize = 0;
if (state - 1 == marker - 256) {
state = marker - 256 + 2;
break;
}
if (marker == 256 || marker >= 432)
stop = true;
break;
}
if (stop)
break;
}
return Math.max(rateSeq(nuSeq), (state >= 3) ? (100 / (1 + errors)) : 0);
}
private static int rateSeq(List<NALUnit> nuSeq) {
int score = 0;
boolean hasSps = false, hasPps = false, hasSlice = false;
for (NALUnit nalUnit : nuSeq) {
if (NALUnitType.SPS == nalUnit.type) {
if (hasSps && !hasSlice) {
score -= 30;
} else {
score += 30;
}
hasSps = true;
continue;
}
if (NALUnitType.PPS == nalUnit.type) {
if (hasPps && !hasSlice)
score -= 30;
if (hasSps)
score += 20;
hasPps = true;
continue;
}
if (NALUnitType.IDR_SLICE == nalUnit.type || NALUnitType.NON_IDR_SLICE == nalUnit.type) {
if (!hasSlice)
score += 20;
hasSlice = true;
}
}
return score;
}
public void close() throws IOException {
this.channel.close();
}
}

View file

@ -0,0 +1,365 @@
package org.jcodec.containers.mps;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.Arrays;
import org.jcodec.codecs.mpeg12.MPEGUtil;
import org.jcodec.codecs.mpeg12.bitstream.CopyrightExtension;
import org.jcodec.codecs.mpeg12.bitstream.GOPHeader;
import org.jcodec.codecs.mpeg12.bitstream.PictureCodingExtension;
import org.jcodec.codecs.mpeg12.bitstream.PictureDisplayExtension;
import org.jcodec.codecs.mpeg12.bitstream.PictureHeader;
import org.jcodec.codecs.mpeg12.bitstream.PictureSpatialScalableExtension;
import org.jcodec.codecs.mpeg12.bitstream.PictureTemporalScalableExtension;
import org.jcodec.codecs.mpeg12.bitstream.QuantMatrixExtension;
import org.jcodec.codecs.mpeg12.bitstream.SequenceDisplayExtension;
import org.jcodec.codecs.mpeg12.bitstream.SequenceExtension;
import org.jcodec.codecs.mpeg12.bitstream.SequenceHeader;
import org.jcodec.codecs.mpeg12.bitstream.SequenceScalableExtension;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.FileChannelWrapper;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.tools.MainUtils;
import org.jcodec.platform.Platform;
public class MPSDump {
private static final MainUtils.Flag DUMP_FROM = MainUtils.Flag.flag("dump-from", null, "Stop reading at timestamp");
private static final MainUtils.Flag STOP_AT = MainUtils.Flag.flag("stop-at", null, "Start dumping from timestamp");
private static final MainUtils.Flag[] ALL_FLAGS = new MainUtils.Flag[] { DUMP_FROM, STOP_AT };
protected ReadableByteChannel ch;
public MPSDump(ReadableByteChannel ch) {
this.ch = ch;
}
public static void main1(String[] args) throws IOException {
FileChannelWrapper ch = null;
try {
MainUtils.Cmd cmd = MainUtils.parseArguments(args, ALL_FLAGS);
if (cmd.args.length < 1) {
MainUtils.printHelp(ALL_FLAGS, Arrays.asList("file name"));
return;
}
ch = NIOUtils.readableChannel(new File(cmd.args[0]));
Long dumpAfterPts = cmd.getLongFlag(DUMP_FROM);
Long stopPts = cmd.getLongFlag(STOP_AT);
new MPSDump(ch).dump(dumpAfterPts, stopPts);
} finally {
NIOUtils.closeQuietly(ch);
}
}
public void dump(Long dumpAfterPts, Long stopPts) throws IOException {
MPEGVideoAnalyzer analyzer = null;
ByteBuffer buffer = ByteBuffer.allocate(1048576);
PESPacket pkt = null;
int hdrSize = 0;
long position = 0L;
while (true) {
position -= (long)buffer.position();
if (fillBuffer(buffer) == -1)
break;
buffer.flip();
if (buffer.remaining() < 4)
break;
position += (long)buffer.remaining();
while (true) {
ByteBuffer payload = null;
if (pkt != null && pkt.length > 0) {
int pesLen = pkt.length - hdrSize + 6;
if (pesLen <= buffer.remaining())
payload = NIOUtils.read(buffer, pesLen);
} else {
payload = getPesPayload(buffer);
}
if (payload == null)
break;
if (pkt != null)
logPes(pkt, hdrSize, payload);
if (analyzer != null && pkt != null && pkt.streamId >= 224 && pkt.streamId <= 239)
analyzer.analyzeMpegVideoPacket(payload);
if (buffer.remaining() < 32) {
pkt = null;
break;
}
skipToNextPES(buffer);
if (buffer.remaining() < 32) {
pkt = null;
break;
}
hdrSize = buffer.position();
pkt = MPSUtils.readPESHeader(buffer, position - (long)buffer.remaining());
hdrSize = buffer.position() - hdrSize;
if (dumpAfterPts != null && pkt.pts >= dumpAfterPts)
analyzer = new MPEGVideoAnalyzer();
if (stopPts != null && pkt.pts >= stopPts)
return;
}
buffer = transferRemainder(buffer);
}
}
protected int fillBuffer(ByteBuffer buffer) throws IOException {
return this.ch.read(buffer);
}
protected void logPes(PESPacket pkt, int hdrSize, ByteBuffer payload) {
System.out.println("" + pkt.streamId + "(" + pkt.streamId + ") [" + ((pkt.streamId >= 224) ? "video" : "audio") + ", " + pkt.pos + "], pts: " +
payload.remaining() + hdrSize + ", dts: " + pkt.pts);
}
private ByteBuffer transferRemainder(ByteBuffer buffer) {
ByteBuffer dup = buffer.duplicate();
dup.clear();
while (buffer.hasRemaining())
dup.put(buffer.get());
return dup;
}
private static void skipToNextPES(ByteBuffer buffer) {
while (buffer.hasRemaining()) {
int marker = buffer.duplicate().getInt();
if (marker >= 445 && marker <= 511 && marker != 446)
break;
buffer.getInt();
MPEGUtil.gotoNextMarker(buffer);
}
}
private static ByteBuffer getPesPayload(ByteBuffer buffer) {
ByteBuffer copy = buffer.duplicate();
ByteBuffer result = buffer.duplicate();
while (copy.hasRemaining()) {
int marker = copy.duplicate().getInt();
if (marker >= 441) {
result.limit(copy.position());
buffer.position(copy.position());
return result;
}
copy.getInt();
MPEGUtil.gotoNextMarker(copy);
}
return null;
}
private static class MPEGVideoAnalyzer {
private int nextStartCode = -1;
private ByteBuffer bselPayload;
private int bselStartCode;
private int bselOffset;
private int bselBufInd;
private int prevBufSize;
private int curBufInd;
private PictureHeader picHeader;
private SequenceHeader sequenceHeader;
private PictureCodingExtension pictureCodingExtension;
private SequenceExtension sequenceExtension;
public MPEGVideoAnalyzer() {
this.bselPayload = ByteBuffer.allocate(1048576);
}
private void analyzeMpegVideoPacket(ByteBuffer buffer) {
int pos = buffer.position();
int bufSize = buffer.remaining();
while (buffer.hasRemaining()) {
this.bselPayload.put((byte)(this.nextStartCode >> 24));
this.nextStartCode = this.nextStartCode << 8 | buffer.get() & 0xFF;
if (this.nextStartCode >= 256 && this.nextStartCode <= 440) {
this.bselPayload.flip();
this.bselPayload.getInt();
if (this.bselStartCode != 0) {
if (this.bselBufInd != this.curBufInd)
this.bselOffset -= this.prevBufSize;
dumpBSEl(this.bselStartCode, this.bselOffset, this.bselPayload);
}
this.bselPayload.clear();
this.bselStartCode = this.nextStartCode;
this.bselOffset = buffer.position() - 4 - pos;
this.bselBufInd = this.curBufInd;
}
}
this.curBufInd++;
this.prevBufSize = bufSize;
}
private void dumpBSEl(int mark, int offset, ByteBuffer b) {
System.out.print(String.format("marker: 0x%02x [@%d] ( ", mark, offset));
if (mark == 256) {
dumpPictureHeader(b);
} else if (mark <= 431) {
System.out.print(MainUtils.colorBright(String.format("slice @0x%02x", mark - 257), MainUtils.ANSIColor.BLACK, true));
} else if (mark == 435) {
dumpSequenceHeader(b);
} else if (mark == 437) {
dumpExtension(b);
} else if (mark == 440) {
dumpGroupHeader(b);
} else {
System.out.print("--");
}
System.out.println(" )");
}
private void dumpExtension(ByteBuffer b) {
BitReader _in = BitReader.createBitReader(b);
int extType = _in.readNBit(4);
if (this.picHeader == null) {
if (this.sequenceHeader != null) {
switch (extType) {
case 1:
this.sequenceExtension = SequenceExtension.read(_in);
dumpSequenceExtension(this.sequenceExtension);
break;
case 5:
dumpSequenceScalableExtension(SequenceScalableExtension.read(_in));
break;
case 2:
dumpSequenceDisplayExtension(SequenceDisplayExtension.read(_in));
break;
default:
System.out.print(MainUtils.colorBright("extension " + extType, MainUtils.ANSIColor.GREEN, true));
break;
}
} else {
System.out.print(MainUtils.colorBright("dangling extension " + extType, MainUtils.ANSIColor.GREEN, true));
}
} else {
switch (extType) {
case 3:
dumpQuantMatrixExtension(QuantMatrixExtension.read(_in));
break;
case 4:
dumpCopyrightExtension(CopyrightExtension.read(_in));
break;
case 7:
if (this.sequenceHeader != null && this.pictureCodingExtension != null)
dumpPictureDisplayExtension(PictureDisplayExtension.read(_in, this.sequenceExtension, this.pictureCodingExtension));
break;
case 8:
this.pictureCodingExtension = PictureCodingExtension.read(_in);
dumpPictureCodingExtension(this.pictureCodingExtension);
break;
case 9:
dumpPictureSpatialScalableExtension(PictureSpatialScalableExtension.read(_in));
break;
case 16:
dumpPictureTemporalScalableExtension(PictureTemporalScalableExtension.read(_in));
break;
default:
System.out.print(MainUtils.colorBright("extension " + extType, MainUtils.ANSIColor.GREEN, true));
break;
}
}
}
private void dumpSequenceDisplayExtension(SequenceDisplayExtension read) {
System.out.print(MainUtils.colorBright("sequence display extension " + dumpBin(read), MainUtils.ANSIColor.GREEN, true));
}
private void dumpSequenceScalableExtension(SequenceScalableExtension read) {
System.out.print(MainUtils.colorBright("sequence scalable extension " + dumpBin(read), MainUtils.ANSIColor.GREEN, true));
}
private void dumpSequenceExtension(SequenceExtension read) {
System.out.print(MainUtils.colorBright("sequence extension " + dumpBin(read), MainUtils.ANSIColor.GREEN, true));
}
private void dumpPictureTemporalScalableExtension(PictureTemporalScalableExtension read) {
System.out.print(MainUtils.colorBright("picture temporal scalable extension " + dumpBin(read), MainUtils.ANSIColor.GREEN, true));
}
private void dumpPictureSpatialScalableExtension(PictureSpatialScalableExtension read) {
System.out.print(MainUtils.colorBright("picture spatial scalable extension " + dumpBin(read), MainUtils.ANSIColor.GREEN, true));
}
private void dumpPictureCodingExtension(PictureCodingExtension read) {
System.out.print(MainUtils.colorBright("picture coding extension " + dumpBin(read), MainUtils.ANSIColor.GREEN, true));
}
private void dumpPictureDisplayExtension(PictureDisplayExtension read) {
System.out.print(MainUtils.colorBright("picture display extension " + dumpBin(read), MainUtils.ANSIColor.GREEN, true));
}
private void dumpCopyrightExtension(CopyrightExtension read) {
System.out.print(MainUtils.colorBright("copyright extension " + dumpBin(read), MainUtils.ANSIColor.GREEN, true));
}
private void dumpQuantMatrixExtension(QuantMatrixExtension read) {
System.out.print(
MainUtils.colorBright("quant matrix extension " + dumpBin(read), MainUtils.ANSIColor.GREEN, true));
}
private String dumpBin(Object read) {
StringBuilder bldr = new StringBuilder();
bldr.append("<");
Field[] fields = Platform.getFields(read.getClass());
for (int i = 0; i < fields.length; i++) {
if (Modifier.isPublic(fields[i].getModifiers()) && !Modifier.isStatic(fields[i].getModifiers())) {
bldr.append(convertName(fields[i].getName()) + ": ");
if (fields[i].getType().isPrimitive()) {
try {
bldr.append(fields[i].get(read));
} catch (Exception e) {}
} else {
try {
Object val = fields[i].get(read);
if (val != null) {
bldr.append(dumpBin(val));
} else {
bldr.append("N/A");
}
} catch (Exception e) {}
}
if (i < fields.length - 1)
bldr.append(",");
}
}
bldr.append(">");
return bldr.toString();
}
private String convertName(String name) {
return name.replaceAll("([A-Z])", " $1").replaceFirst("^ ", "").toLowerCase();
}
private void dumpGroupHeader(ByteBuffer b) {
GOPHeader gopHeader = GOPHeader.read(b);
System.out.print(MainUtils.colorBright("group header <closed:" + gopHeader.isClosedGop() + ",broken link:" +
gopHeader.isBrokenLink() + (
(gopHeader.getTimeCode() != null) ? (",timecode:" + gopHeader.getTimeCode().toString()) : "") + ">", MainUtils.ANSIColor.MAGENTA, true));
}
private void dumpSequenceHeader(ByteBuffer b) {
this.picHeader = null;
this.pictureCodingExtension = null;
this.sequenceExtension = null;
this.sequenceHeader = SequenceHeader.read(b);
System.out.print(MainUtils.colorBright("sequence header", MainUtils.ANSIColor.BLUE, true));
}
private void dumpPictureHeader(ByteBuffer b) {
this.picHeader = PictureHeader.read(b);
this.pictureCodingExtension = null;
System.out.print(MainUtils.colorBright("picture header <type:" + (
(this.picHeader.picture_coding_type == 1) ? "I" : ((this.picHeader.picture_coding_type == 2) ? "P" : "B")) + ", temp_ref:" + this.picHeader.temporal_reference + ">", MainUtils.ANSIColor.BROWN, true));
}
}
}

View file

@ -0,0 +1,510 @@
package org.jcodec.containers.mps;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.jcodec.common.IntArrayList;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.model.Rational;
public class MPSUtils {
public static final int VIDEO_MIN = 480;
public static final int VIDEO_MAX = 495;
public static final int AUDIO_MIN = 448;
public static final int AUDIO_MAX = 479;
public static final int PACK = 442;
public static final int SYSTEM = 443;
public static final int PSM = 444;
public static final int PRIVATE_1 = 445;
public static final int PRIVATE_2 = 447;
public static final boolean mediaStream(int streamId) {
return ((streamId >= $(448) && streamId <= $(495)) || streamId == $(445) || streamId ==
$(447));
}
public static final boolean mediaMarker(int marker) {
return ((marker >= 448 && marker <= 495) || marker == 445 || marker == 447);
}
public static final boolean psMarker(int marker) {
return (marker >= 445 && marker <= 495);
}
public static boolean videoMarker(int marker) {
return (marker >= 480 && marker <= 495);
}
public static final boolean videoStream(int streamId) {
return (streamId >= $(480) && streamId <= $(495));
}
public static boolean audioStream(int streamId) {
return ((streamId >= $(448) && streamId <= $(479)) || streamId == $(445) || streamId ==
$(447));
}
static int $(int marker) {
return marker & 0xFF;
}
public static abstract class PESReader {
private int marker = -1;
private int lenFieldLeft;
private int pesLen;
private long pesFileStart = -1L;
private int stream;
private boolean _pes;
private int pesLeft;
private ByteBuffer pesBuffer;
public PESReader() {
this.pesBuffer = ByteBuffer.allocate(2097152);
}
protected abstract void pes(ByteBuffer param1ByteBuffer, long param1Long, int param1Int1, int param1Int2);
public void analyseBuffer(ByteBuffer buf, long pos) {
int init = buf.position();
while (buf.hasRemaining()) {
if (this.pesLeft > 0) {
int toRead = Math.min(buf.remaining(), this.pesLeft);
this.pesBuffer.put(NIOUtils.read(buf, toRead));
this.pesLeft -= toRead;
if (this.pesLeft == 0) {
long filePos = pos + (long)buf.position() - (long)init;
pes1(this.pesBuffer, this.pesFileStart, (int)(filePos - this.pesFileStart), this.stream);
this.pesFileStart = -1L;
this._pes = false;
this.stream = -1;
}
continue;
}
int bt = buf.get() & 0xFF;
if (this._pes)
this.pesBuffer.put((byte)(this.marker >>> 24));
this.marker = this.marker << 8 | bt;
if (this.marker >= 443 && this.marker <= 495) {
long filePos = pos + (long)buf.position() - (long)init - 4L;
if (this._pes)
pes1(this.pesBuffer, this.pesFileStart, (int)(filePos - this.pesFileStart), this.stream);
this.pesFileStart = filePos;
this._pes = true;
this.stream = this.marker & 0xFF;
this.lenFieldLeft = 2;
this.pesLen = 0;
continue;
}
if (this.marker >= 441 && this.marker <= 511) {
if (this._pes) {
long filePos = pos + (long)buf.position() - (long)init - 4L;
pes1(this.pesBuffer, this.pesFileStart, (int)(filePos - this.pesFileStart), this.stream);
}
this.pesFileStart = -1L;
this._pes = false;
this.stream = -1;
continue;
}
if (this.lenFieldLeft > 0) {
this.pesLen = this.pesLen << 8 | bt;
this.lenFieldLeft--;
if (this.lenFieldLeft == 0) {
this.pesLeft = this.pesLen;
if (this.pesLen != 0) {
flushMarker();
this.marker = -1;
}
}
}
}
}
private void flushMarker() {
this.pesBuffer.put((byte)(this.marker >>> 24));
this.pesBuffer.put((byte)(this.marker >>> 16 & 0xFF));
this.pesBuffer.put((byte)(this.marker >>> 8 & 0xFF));
this.pesBuffer.put((byte)(this.marker & 0xFF));
}
private void pes1(ByteBuffer pesBuffer, long start, int pesLen, int stream) {
pesBuffer.flip();
pes(pesBuffer, start, pesLen, stream);
pesBuffer.clear();
}
public void finishRead() {
if (this.pesLeft <= 4) {
flushMarker();
pes1(this.pesBuffer, this.pesFileStart, this.pesBuffer.position(), this.stream);
}
}
}
public static PESPacket readPESHeader(ByteBuffer iss, long pos) {
int streamId = iss.getInt() & 0xFF;
int len = iss.getShort() & 0xFFFF;
if (streamId != 191) {
int b0 = iss.get() & 0xFF;
if ((b0 & 0xC0) == 128)
return mpeg2Pes(b0, len, streamId, iss, pos);
return mpeg1Pes(b0, len, streamId, iss, pos);
}
return new PESPacket(null, -1L, streamId, len, pos, -1L);
}
public static PESPacket mpeg1Pes(int b0, int len, int streamId, ByteBuffer is, long pos) {
int c = b0;
while (c == 255)
c = is.get() & 0xFF;
if ((c & 0xC0) == 64) {
is.get();
c = is.get() & 0xFF;
}
long pts = -1L, dts = -1L;
if ((c & 0xF0) == 32) {
pts = _readTs(is, c);
} else if ((c & 0xF0) == 48) {
pts = _readTs(is, c);
dts = readTs(is);
} else if (c != 15) {
throw new RuntimeException("Invalid data");
}
return new PESPacket(null, pts, streamId, len, pos, dts);
}
public static long _readTs(ByteBuffer is, int c) {
return ((long)c & 0xEL) << 29L | (long)((is.get() & 0xFF) << 22) | (long)((is.get() & 0xFF) >> 1 << 15) |
(long)((is.get() & 0xFF) << 7) | (long)((is.get() & 0xFF) >> 1);
}
public static PESPacket mpeg2Pes(int b0, int len, int streamId, ByteBuffer is, long pos) {
int flags1 = b0;
int flags2 = is.get() & 0xFF;
int header_len = is.get() & 0xFF;
long pts = -1L, dts = -1L;
if ((flags2 & 0xC0) == 128) {
pts = readTs(is);
NIOUtils.skip(is, header_len - 5);
} else if ((flags2 & 0xC0) == 192) {
pts = readTs(is);
dts = readTs(is);
NIOUtils.skip(is, header_len - 10);
} else {
NIOUtils.skip(is, header_len);
}
return new PESPacket(null, pts, streamId, len, pos, dts);
}
public static long readTs(ByteBuffer is) {
return ((long)is.get() & 0xEL) << 29L | (long)((is.get() & 0xFF) << 22) | (long)((is.get() & 0xFF) >> 1 << 15) |
(long)((is.get() & 0xFF) << 7) | (long)((is.get() & 0xFF) >> 1);
}
public static void writeTs(ByteBuffer is, long ts) {
is.put((byte)(int)(ts >> 29L << 1L));
is.put((byte)(int)(ts >> 22L));
is.put((byte)(int)(ts >> 15L << 1L));
is.put((byte)(int)(ts >> 7L));
is.put((byte)(int)(ts >> 1L));
}
public static class MPEGMediaDescriptor {
private int tag;
private int len;
public void parse(ByteBuffer buf) {
this.tag = buf.get() & 0xFF;
this.len = buf.get() & 0xFF;
}
public int getTag() {
return this.tag;
}
public int getLen() {
return this.len;
}
}
public static class VideoStreamDescriptor extends MPEGMediaDescriptor {
private int multipleFrameRate;
private int frameRateCode;
private boolean mpeg1Only;
private int constrainedParameter;
private int stillPicture;
private int profileAndLevel;
private int chromaFormat;
private int frameRateExtension;
Rational[] frameRates = new Rational[] {
null, new Rational(24000, 1001), new Rational(24, 1), new Rational(25, 1), new Rational(30000, 1001), new Rational(30, 1), new Rational(50, 1), new Rational(60000, 1001), new Rational(60, 1), null,
null, null, null, null, null, null };
public void parse(ByteBuffer buf) {
super.parse(buf);
int b0 = buf.get() & 0xFF;
this.multipleFrameRate = b0 >> 7 & 0x1;
this.frameRateCode = b0 >> 3 & 0xF;
this.mpeg1Only = ((b0 >> 2 & 0x1) == 0);
this.constrainedParameter = b0 >> 1 & 0x1;
this.stillPicture = b0 & 0x1;
if (!this.mpeg1Only) {
this.profileAndLevel = buf.get() & 0xFF;
int b1 = buf.get() & 0xFF;
this.chromaFormat = b1 >> 6;
this.frameRateExtension = b1 >> 5 & 0x1;
}
}
public Rational getFrameRate() {
return this.frameRates[this.frameRateCode];
}
public int getMultipleFrameRate() {
return this.multipleFrameRate;
}
public int getFrameRateCode() {
return this.frameRateCode;
}
public boolean isMpeg1Only() {
return this.mpeg1Only;
}
public int getConstrainedParameter() {
return this.constrainedParameter;
}
public int getStillPicture() {
return this.stillPicture;
}
public int getProfileAndLevel() {
return this.profileAndLevel;
}
public int getChromaFormat() {
return this.chromaFormat;
}
public int getFrameRateExtension() {
return this.frameRateExtension;
}
}
public static class AudioStreamDescriptor extends MPEGMediaDescriptor {
private int variableRateAudioIndicator;
private int freeFormatFlag;
private int id;
private int layer;
public void parse(ByteBuffer buf) {
super.parse(buf);
int b0 = buf.get() & 0xFF;
this.freeFormatFlag = b0 >> 7 & 0x1;
this.id = b0 >> 6 & 0x1;
this.layer = b0 >> 5 & 0x3;
this.variableRateAudioIndicator = b0 >> 3 & 0x1;
}
public int getVariableRateAudioIndicator() {
return this.variableRateAudioIndicator;
}
public int getFreeFormatFlag() {
return this.freeFormatFlag;
}
public int getId() {
return this.id;
}
public int getLayer() {
return this.layer;
}
}
public static class ISO639LanguageDescriptor extends MPEGMediaDescriptor {
private IntArrayList languageCodes = IntArrayList.createIntArrayList();
public void parse(ByteBuffer buf) {
super.parse(buf);
while (buf.remaining() >= 4)
this.languageCodes.add(buf.getInt());
}
public IntArrayList getLanguageCodes() {
return this.languageCodes;
}
}
public static class Mpeg4VideoDescriptor extends MPEGMediaDescriptor {
private int profileLevel;
public void parse(ByteBuffer buf) {
super.parse(buf);
this.profileLevel = buf.get() & 0xFF;
}
}
public static class Mpeg4AudioDescriptor extends MPEGMediaDescriptor {
private int profileLevel;
public void parse(ByteBuffer buf) {
super.parse(buf);
this.profileLevel = buf.get() & 0xFF;
}
public int getProfileLevel() {
return this.profileLevel;
}
}
public static class AVCVideoDescriptor extends MPEGMediaDescriptor {
private int profileIdc;
private int flags;
private int level;
public void parse(ByteBuffer buf) {
super.parse(buf);
this.profileIdc = buf.get() & 0xFF;
this.flags = buf.get() & 0xFF;
this.level = buf.get() & 0xFF;
}
public int getProfileIdc() {
return this.profileIdc;
}
public int getFlags() {
return this.flags;
}
public int getLevel() {
return this.level;
}
}
public static class AACAudioDescriptor extends MPEGMediaDescriptor {
private int profile;
private int channel;
private int flags;
public void parse(ByteBuffer buf) {
super.parse(buf);
this.profile = buf.get() & 0xFF;
this.channel = buf.get() & 0xFF;
this.flags = buf.get() & 0xFF;
}
public int getProfile() {
return this.profile;
}
public int getChannel() {
return this.channel;
}
public int getFlags() {
return this.flags;
}
}
public static class DataStreamAlignmentDescriptor extends MPEGMediaDescriptor {
private int alignmentType;
public void parse(ByteBuffer buf) {
super.parse(buf);
this.alignmentType = buf.get() & 0xFF;
}
public int getAlignmentType() {
return this.alignmentType;
}
}
public static class RegistrationDescriptor extends MPEGMediaDescriptor {
private int formatIdentifier;
private IntArrayList additionalFormatIdentifiers = IntArrayList.createIntArrayList();
public void parse(ByteBuffer buf) {
super.parse(buf);
this.formatIdentifier = buf.getInt();
while (buf.hasRemaining())
this.additionalFormatIdentifiers.add(buf.get() & 0xFF);
}
public int getFormatIdentifier() {
return this.formatIdentifier;
}
public IntArrayList getAdditionalFormatIdentifiers() {
return this.additionalFormatIdentifiers;
}
}
public static Class<? extends MPEGMediaDescriptor>[] dMapping = new Class<?>[256];
static {
dMapping[2] = VideoStreamDescriptor.class;
dMapping[3] = AudioStreamDescriptor.class;
dMapping[6] = DataStreamAlignmentDescriptor.class;
dMapping[5] = RegistrationDescriptor.class;
dMapping[10] = ISO639LanguageDescriptor.class;
dMapping[27] = Mpeg4VideoDescriptor.class;
dMapping[28] = Mpeg4AudioDescriptor.class;
dMapping[40] = AVCVideoDescriptor.class;
dMapping[43] = AACAudioDescriptor.class;
}
public static List<MPEGMediaDescriptor> parseDescriptors(ByteBuffer bb) {
List<MPEGMediaDescriptor> result = new ArrayList<>();
while (bb.remaining() >= 2) {
ByteBuffer dup = bb.duplicate();
int tag = dup.get() & 0xFF;
int len = dup.get() & 0xFF;
ByteBuffer descriptorBuffer = NIOUtils.read(bb, len + 2);
if (dMapping[tag] != null)
try {
MPEGMediaDescriptor descriptor = dMapping[tag].newInstance();
descriptor.parse(descriptorBuffer);
result.add(descriptor);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return result;
}
}

View file

@ -0,0 +1,183 @@
package org.jcodec.containers.mps;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jcodec.common.IntObjectMap;
import org.jcodec.common.Preconditions;
import org.jcodec.common.UsedViaReflection;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
public class MTSDemuxer {
private SeekableByteChannel channel;
private Map<Integer, ProgramChannel> programs;
public Set<Integer> getPrograms() {
return this.programs.keySet();
}
public Set<Integer> findPrograms(SeekableByteChannel src) throws IOException {
long rem = src.position();
Set<Integer> guids = new HashSet<>();
for (int i = 0; guids.size() == 0 || i < guids.size() * 500; i++) {
MTSPacket pkt = readPacket(src);
if (pkt == null)
break;
if (pkt.payload != null) {
ByteBuffer payload = pkt.payload;
if (!guids.contains(Integer.valueOf(pkt.pid)) && (payload.duplicate().getInt() & 0xFFFFFF00) == 256)
guids.add(Integer.valueOf(pkt.pid));
}
}
src.setPosition(rem);
return guids;
}
public MTSDemuxer(SeekableByteChannel src) throws IOException {
this.channel = src;
this.programs = new HashMap<>();
for (Iterator<Integer> iterator = findPrograms(src).iterator(); iterator.hasNext(); ) {
int pid = iterator.next();
this.programs.put(Integer.valueOf(pid), new ProgramChannel(this));
}
src.setPosition(0L);
}
public ReadableByteChannel getProgram(int pid) {
return this.programs.get(Integer.valueOf(pid));
}
private static class ProgramChannel implements ReadableByteChannel {
private final MTSDemuxer demuxer;
private List<ByteBuffer> data;
private boolean closed;
public ProgramChannel(MTSDemuxer demuxer) {
this.demuxer = demuxer;
this.data = new ArrayList<>();
}
public boolean isOpen() {
return (!this.closed && this.demuxer.channel.isOpen());
}
public void close() throws IOException {
this.closed = true;
this.data.clear();
}
public int read(ByteBuffer dst) throws IOException {
int bytesRead = 0;
while (dst.hasRemaining()) {
while (this.data.size() == 0) {
if (!this.demuxer.readAndDispatchNextTSPacket())
return (bytesRead > 0) ? bytesRead : -1;
}
ByteBuffer first = this.data.get(0);
int toRead = Math.min(dst.remaining(), first.remaining());
dst.put(NIOUtils.read(first, toRead));
if (!first.hasRemaining())
this.data.remove(0);
bytesRead += toRead;
}
return bytesRead;
}
public void storePacket(MTSDemuxer.MTSPacket pkt) {
if (this.closed)
return;
this.data.add(pkt.payload);
}
}
private boolean readAndDispatchNextTSPacket() throws IOException {
MTSPacket pkt = readPacket(this.channel);
if (pkt == null)
return false;
ProgramChannel program = this.programs.get(Integer.valueOf(pkt.pid));
if (program != null)
program.storePacket(pkt);
return true;
}
public static class MTSPacket {
public ByteBuffer payload;
public boolean payloadStart;
public int pid;
public MTSPacket(int guid, boolean payloadStart, ByteBuffer payload) {
this.pid = guid;
this.payloadStart = payloadStart;
this.payload = payload;
}
}
public static MTSPacket readPacket(ReadableByteChannel channel) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(188);
if (NIOUtils.readFromChannel(channel, buffer) != 188)
return null;
buffer.flip();
return parsePacket(buffer);
}
public static MTSPacket parsePacket(ByteBuffer buffer) {
int marker = buffer.get() & 0xFF;
Preconditions.checkState((71 == marker));
int guidFlags = buffer.getShort();
int guid = guidFlags & 0x1FFF;
int payloadStart = guidFlags >> 14 & 0x1;
int b0 = buffer.get() & 0xFF;
int counter = b0 & 0xF;
if ((b0 & 0x20) != 0) {
int taken = 0;
taken = (buffer.get() & 0xFF) + 1;
NIOUtils.skip(buffer, taken - 1);
}
return new MTSPacket(guid, (payloadStart == 1), ((b0 & 0x10) != 0) ? buffer : null);
}
@UsedViaReflection
public static int probe(ByteBuffer b_) {
ByteBuffer b = b_.duplicate();
IntObjectMap<List<ByteBuffer>> streams = new IntObjectMap<>();
try {
while (true) {
ByteBuffer sub = NIOUtils.read(b, 188);
if (sub.remaining() < 188)
break;
MTSPacket tsPkt = parsePacket(sub);
if (tsPkt == null)
break;
List<ByteBuffer> data = streams.get(tsPkt.pid);
if (data == null) {
data = new ArrayList<>();
streams.put(tsPkt.pid, data);
}
if (tsPkt.payload != null)
data.add(tsPkt.payload);
}
} catch (Throwable t) {}
int maxScore = 0;
int[] keys = streams.keys();
for (int i : keys) {
List<ByteBuffer> packets = streams.get(i);
int score = MPSDemuxer.probe(NIOUtils.combineBuffers(packets));
if (score > maxScore)
maxScore = score + ((packets.size() > 20) ? 50 : 0);
}
return maxScore;
}
}

View file

@ -0,0 +1,200 @@
package org.jcodec.containers.mps;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.jcodec.common.IntArrayList;
import org.jcodec.common.IntIntMap;
import org.jcodec.common.Preconditions;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.tools.MainUtils;
import org.jcodec.containers.mps.psi.PATSection;
import org.jcodec.containers.mps.psi.PMTSection;
import org.jcodec.platform.Platform;
public class MTSDump extends MPSDump {
private static final MainUtils.Flag DUMP_FROM = MainUtils.Flag.flag("dump-from", null, "Stop reading at timestamp");
private static final MainUtils.Flag STOP_AT = MainUtils.Flag.flag("stop-at", null, "Start dumping from timestamp");
private static final MainUtils.Flag[] ALL_FLAGS = new MainUtils.Flag[] { DUMP_FROM, STOP_AT };
private int guid;
private ByteBuffer buf;
private ByteBuffer tsBuf;
private int tsNo;
private int globalPayload;
private int[] payloads;
private int[] nums;
private int[] prevPayloads;
private int[] prevNums;
public MTSDump(ReadableByteChannel ch, int targetGuid) {
super(ch);
this.buf = ByteBuffer.allocate(192512);
this.tsBuf = ByteBuffer.allocate(188);
this.guid = targetGuid;
this.buf.position(this.buf.limit());
this.tsBuf.position(this.tsBuf.limit());
}
public static void main2(String[] args) throws IOException {
ReadableByteChannel ch = null;
try {
MainUtils.Cmd cmd = MainUtils.parseArguments(args, ALL_FLAGS);
if (cmd.args.length < 1) {
MainUtils.printHelp(ALL_FLAGS, Arrays.asList("file name", "guid"));
return;
}
if (cmd.args.length == 1) {
System.out.println("MTS programs:");
dumpProgramPids(NIOUtils.readableChannel(new File(cmd.args[0])));
return;
}
ch = NIOUtils.readableChannel(new File(cmd.args[0]));
Long dumpAfterPts = cmd.getLongFlag(DUMP_FROM);
Long stopPts = cmd.getLongFlag(STOP_AT);
new MTSDump(ch, Integer.parseInt(cmd.args[1])).dump(dumpAfterPts, stopPts);
} finally {
NIOUtils.closeQuietly(ch);
}
}
private static void dumpProgramPids(ReadableByteChannel readableFileChannel) throws IOException {
Set<Integer> pids = new HashSet<>();
ByteBuffer buf = ByteBuffer.allocate(1925120);
readableFileChannel.read(buf);
buf.flip();
buf.limit(buf.limit() - buf.limit() % 188);
int pmtPid = -1;
while (buf.hasRemaining()) {
ByteBuffer tsBuf = NIOUtils.read(buf, 188);
Preconditions.checkState((71 == (tsBuf.get() & 0xFF)));
int guidFlags = (tsBuf.get() & 0xFF) << 8 | tsBuf.get() & 0xFF;
int guid = guidFlags & 0x1FFF;
System.out.println(guid);
if (guid != 0)
pids.add(Integer.valueOf(guid));
if (guid == 0 || guid == pmtPid) {
int payloadStart = guidFlags >> 14 & 0x1;
int b0 = tsBuf.get() & 0xFF;
int counter = b0 & 0xF;
int payloadOff = 0;
if ((b0 & 0x20) != 0)
NIOUtils.skip(tsBuf, tsBuf.get() & 0xFF);
if (payloadStart == 1)
NIOUtils.skip(tsBuf, tsBuf.get() & 0xFF);
if (guid == 0) {
PATSection pat = PATSection.parsePAT(tsBuf);
IntIntMap programs = pat.getPrograms();
pmtPid = programs.values()[0];
printPat(pat);
continue;
}
if (guid == pmtPid) {
PMTSection pmt = PMTSection.parsePMT(tsBuf);
printPmt(pmt);
return;
}
}
}
for (Integer pid : pids)
System.out.println(pid);
}
private static void printPat(PATSection pat) {
IntIntMap programs = pat.getPrograms();
System.out.print("PAT: ");
int[] keys = programs.keys();
for (int i : keys)
System.out.print("" + i + ":" + i + ", ");
System.out.println();
}
private static void printPmt(PMTSection pmt) {
System.out.print("PMT: ");
for (PMTSection.PMTStream pmtStream : pmt.getStreams()) {
System.out.print("" + pmtStream.getPid() + ":" + pmtStream.getPid() + ", ");
for (MPSUtils.MPEGMediaDescriptor descriptor : pmtStream.getDesctiptors())
System.out.println(Platform.toJSON(descriptor));
}
System.out.println();
}
protected void logPes(PESPacket pkt, int hdrSize, ByteBuffer payload) {
System.out.println("" + pkt.streamId + "(" + pkt.streamId + ") [ts#" + (
(pkt.streamId >= 224) ? "video" : "audio") + ", " + mapPos(pkt.pos) + "b], pts: " +
payload.remaining() + hdrSize + ", dts: " + pkt.pts);
}
private int mapPos(long pos) {
int left = this.globalPayload;
for (int i = this.payloads.length - 1; i >= 0; i--) {
left -= this.payloads[i];
if ((long)left <= pos)
return this.nums[i];
}
if (this.prevPayloads != null)
for (int j = this.prevPayloads.length - 1; j >= 0; j--) {
left -= this.prevPayloads[j];
if ((long)left <= pos)
return this.prevNums[j];
}
return -1;
}
public int fillBuffer(ByteBuffer dst) throws IOException {
IntArrayList payloads = IntArrayList.createIntArrayList();
IntArrayList nums = IntArrayList.createIntArrayList();
int remaining = dst.remaining();
try {
dst.put(NIOUtils.read(this.tsBuf, Math.min(dst.remaining(), this.tsBuf.remaining())));
while (dst.hasRemaining()) {
if (!this.buf.hasRemaining()) {
ByteBuffer dub = this.buf.duplicate();
dub.clear();
int read = this.ch.read(dub);
if (read == -1)
return (dst.remaining() != remaining) ? (remaining - dst.remaining()) : -1;
dub.flip();
dub.limit(dub.limit() - dub.limit() % 188);
this.buf = dub;
}
this.tsBuf = NIOUtils.read(this.buf, 188);
Preconditions.checkState((71 == (this.tsBuf.get() & 0xFF)));
this.tsNo++;
int guidFlags = (this.tsBuf.get() & 0xFF) << 8 | this.tsBuf.get() & 0xFF;
int guid = guidFlags & 0x1FFF;
if (guid != this.guid)
continue;
int payloadStart = guidFlags >> 14 & 0x1;
int b0 = this.tsBuf.get() & 0xFF;
int counter = b0 & 0xF;
if ((b0 & 0x20) != 0)
NIOUtils.skip(this.tsBuf, this.tsBuf.get() & 0xFF);
this.globalPayload += this.tsBuf.remaining();
payloads.add(this.tsBuf.remaining());
nums.add(this.tsNo - 1);
dst.put(NIOUtils.read(this.tsBuf, Math.min(dst.remaining(), this.tsBuf.remaining())));
}
} finally {
this.prevPayloads = this.payloads;
this.payloads = payloads.toArray();
this.prevNums = this.nums;
this.nums = nums.toArray();
}
return remaining - dst.remaining();
}
}

View file

@ -0,0 +1,83 @@
package org.jcodec.containers.mps;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import org.jcodec.common.IntIntMap;
import org.jcodec.common.Preconditions;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.tools.MainUtils;
import org.jcodec.containers.mps.psi.PATSection;
import org.jcodec.containers.mps.psi.PMTSection;
public class MTSPktDump {
public static void main1(String[] args) throws IOException {
MainUtils.Cmd cmd = MainUtils.parseArguments(args, new MainUtils.Flag[0]);
if (cmd.args.length < 1) {
MainUtils.printHelpNoFlags(new String[] { "file name" });
return;
}
ReadableByteChannel ch = null;
try {
ch = NIOUtils.readableChannel(new File(cmd.args[0]));
dumpTSPackets(ch);
} finally {
NIOUtils.closeQuietly(ch);
}
}
private static void dumpTSPackets(ReadableByteChannel _in) throws IOException {
ByteBuffer buf = ByteBuffer.allocate(192512);
while (_in.read(buf) != -1) {
buf.flip();
buf.limit(buf.limit() / 188 * 188);
int pmtPid = -1;
for (int pkt = 0; buf.hasRemaining(); pkt++) {
ByteBuffer tsBuf = NIOUtils.read(buf, 188);
Preconditions.checkState((71 == (tsBuf.get() & 0xFF)));
int guidFlags = (tsBuf.get() & 0xFF) << 8 | tsBuf.get() & 0xFF;
int guid = guidFlags & 0x1FFF;
int payloadStart = guidFlags >> 14 & 0x1;
int b0 = tsBuf.get() & 0xFF;
int counter = b0 & 0xF;
if ((b0 & 0x20) != 0)
NIOUtils.skip(tsBuf, tsBuf.get() & 0xFF);
System.out.print("#" + pkt + "[guid: " + guid + ", cnt: " + counter + ", start: " + (
(payloadStart == 1) ? "y" : "-"));
if (guid == 0 || guid == pmtPid) {
System.out.print(", PSI]: ");
if (payloadStart == 1)
NIOUtils.skip(tsBuf, tsBuf.get() & 0xFF);
if (guid == 0) {
PATSection pat = PATSection.parsePAT(tsBuf);
IntIntMap programs = pat.getPrograms();
pmtPid = programs.values()[0];
printPat(pat);
} else if (guid == pmtPid) {
PMTSection pmt = PMTSection.parsePMT(tsBuf);
printPmt(pmt);
}
} else {
System.out.print("]: " + tsBuf.remaining());
}
System.out.println();
}
buf.clear();
}
}
private static void printPat(PATSection pat) {
IntIntMap programs = pat.getPrograms();
System.out.print("PAT: ");
int[] keys = programs.keys();
for (int i : keys)
System.out.print("" + i + ":" + i + ", ");
}
private static void printPmt(PMTSection pmt) {
System.out.print("PMT: ");
for (PMTSection.PMTStream pmtStream : pmt.getStreams())
System.out.print("" + pmtStream.getPid() + ":" + pmtStream.getPid() + ", ");
}
}

View file

@ -0,0 +1,93 @@
package org.jcodec.containers.mps;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Set;
import org.jcodec.common.IntIntMap;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.tools.MainUtils;
import org.jcodec.containers.mps.psi.PATSection;
import org.jcodec.containers.mps.psi.PSISection;
public class MTSReplacePid extends MTSUtils.TSReader {
private Set<Integer> pmtPids;
private IntIntMap replaceSpec;
public MTSReplacePid(IntIntMap replaceSpec) {
super(true);
this.pmtPids = new HashSet<>();
this.replaceSpec = replaceSpec;
}
public boolean onPkt(int guid, boolean payloadStart, ByteBuffer tsBuf, long filePos, boolean sectionSyntax, ByteBuffer fullPkt) {
if (sectionSyntax) {
replaceRefs(this.replaceSpec, guid, tsBuf, this.pmtPids);
} else {
System.out.print("TS ");
ByteBuffer buf = fullPkt.duplicate();
short tsFlags = buf.getShort(buf.position() + 1);
buf.putShort(buf.position() + 1, (short)(replacePid(this.replaceSpec, tsFlags & 0x1FFF) | tsFlags & 0xFFFFE000));
}
return true;
}
private static IntIntMap parseReplaceSpec(String spec) {
IntIntMap map = new IntIntMap();
for (String pidPair : spec.split(",")) {
String[] pidPairParsed = pidPair.split(":");
map.put(Integer.parseInt(pidPairParsed[0]), Integer.parseInt(pidPairParsed[1]));
}
return map;
}
private void replaceRefs(IntIntMap replaceSpec, int guid, ByteBuffer buf, Set<Integer> pmtPids) {
if (guid == 0) {
PATSection pat = PATSection.parsePAT(buf);
for (int pids : pat.getPrograms().values())
pmtPids.add(Integer.valueOf(pids));
} else if (pmtPids.contains(Integer.valueOf(guid))) {
System.out.println(MainUtils.bold("PMT"));
PSISection.parsePSI(buf);
buf.getShort();
NIOUtils.skip(buf, buf.getShort() & 0xFFF);
while (buf.remaining() > 4) {
byte streamType = buf.get();
MTSStreamType fromTag = MTSStreamType.fromTag(streamType);
System.out.print(String.valueOf((fromTag == null) ? "UNKNOWN" : fromTag) + "(" + String.valueOf((fromTag == null) ? "UNKNOWN" : fromTag) + "):\t");
int wn = buf.getShort() & 0xFFFF;
int wasPid = wn & 0x1FFF;
int elementaryPid = replacePid(replaceSpec, wasPid);
buf.putShort(buf.position() - 2, (short)(elementaryPid & 0x1FFF | wn & 0xFFFFE000));
NIOUtils.skip(buf, buf.getShort() & 0xFFF);
}
}
}
private int replacePid(IntIntMap replaceSpec, int pid) {
int newPid = pid;
if (replaceSpec.contains(pid))
newPid = replaceSpec.get(pid);
System.out.println("[" + pid + "->" + newPid + "]");
return newPid;
}
public static void main1(String[] args) throws IOException {
MainUtils.Cmd cmd = MainUtils.parseArguments(args, new MainUtils.Flag[0]);
if (cmd.args.length < 2) {
MainUtils.printHelpNoFlags(new String[] { "pid_from:pid_to,[pid_from:pid_to...]", "file" });
return;
}
IntIntMap replaceSpec = parseReplaceSpec(cmd.getArg(0));
SeekableByteChannel ch = null;
try {
ch = NIOUtils.rwChannel(new File(cmd.getArg(1)));
new MTSReplacePid(replaceSpec).readTsFile(ch);
} finally {
NIOUtils.closeQuietly(ch);
}
}
}

View file

@ -0,0 +1,127 @@
package org.jcodec.containers.mps;
import java.util.ArrayList;
import java.util.List;
public final class MTSStreamType {
private static final List<MTSStreamType> _values = new ArrayList<>();
public static final MTSStreamType RESERVED = new MTSStreamType(0, false, false);
public static final MTSStreamType VIDEO_MPEG1 = new MTSStreamType(1, true, false);
public static final MTSStreamType VIDEO_MPEG2 = new MTSStreamType(2, true, false);
public static final MTSStreamType AUDIO_MPEG1 = new MTSStreamType(3, false, true);
public static final MTSStreamType AUDIO_MPEG2 = new MTSStreamType(4, false, true);
public static final MTSStreamType PRIVATE_SECTION = new MTSStreamType(5, false, false);
public static final MTSStreamType PRIVATE_DATA = new MTSStreamType(6, false, false);
public static final MTSStreamType MHEG = new MTSStreamType(7, false, false);
public static final MTSStreamType DSM_CC = new MTSStreamType(8, false, false);
public static final MTSStreamType ATM_SYNC = new MTSStreamType(9, false, false);
public static final MTSStreamType DSM_CC_A = new MTSStreamType(10, false, false);
public static final MTSStreamType DSM_CC_B = new MTSStreamType(11, false, false);
public static final MTSStreamType DSM_CC_C = new MTSStreamType(12, false, false);
public static final MTSStreamType DSM_CC_D = new MTSStreamType(13, false, false);
public static final MTSStreamType MPEG_AUX = new MTSStreamType(14, false, false);
public static final MTSStreamType AUDIO_AAC_ADTS = new MTSStreamType(15, false, true);
public static final MTSStreamType VIDEO_MPEG4 = new MTSStreamType(16, true, false);
public static final MTSStreamType AUDIO_AAC_LATM = new MTSStreamType(17, false, true);
public static final MTSStreamType FLEXMUX_PES = new MTSStreamType(18, false, false);
public static final MTSStreamType FLEXMUX_SEC = new MTSStreamType(19, false, false);
public static final MTSStreamType DSM_CC_SDP = new MTSStreamType(20, false, false);
public static final MTSStreamType META_PES = new MTSStreamType(21, false, false);
public static final MTSStreamType META_SEC = new MTSStreamType(22, false, false);
public static final MTSStreamType DSM_CC_DATA_CAROUSEL = new MTSStreamType(23, false, false);
public static final MTSStreamType DSM_CC_OBJ_CAROUSEL = new MTSStreamType(24, false, false);
public static final MTSStreamType DSM_CC_SDP1 = new MTSStreamType(25, false, false);
public static final MTSStreamType IPMP = new MTSStreamType(26, false, false);
public static final MTSStreamType VIDEO_H264 = new MTSStreamType(27, true, false);
public static final MTSStreamType AUDIO_AAC_RAW = new MTSStreamType(28, false, true);
public static final MTSStreamType SUBS = new MTSStreamType(29, false, false);
public static final MTSStreamType AUX_3D = new MTSStreamType(30, false, false);
public static final MTSStreamType VIDEO_AVC_SVC = new MTSStreamType(31, true, false);
public static final MTSStreamType VIDEO_AVC_MVC = new MTSStreamType(32, true, false);
public static final MTSStreamType VIDEO_J2K = new MTSStreamType(33, true, false);
public static final MTSStreamType VIDEO_MPEG2_3D = new MTSStreamType(34, true, false);
public static final MTSStreamType VIDEO_H264_3D = new MTSStreamType(35, true, false);
public static final MTSStreamType VIDEO_CAVS = new MTSStreamType(66, false, true);
public static final MTSStreamType IPMP_STREAM = new MTSStreamType(127, false, false);
public static final MTSStreamType AUDIO_AC3 = new MTSStreamType(129, false, true);
public static final MTSStreamType AUDIO_DTS = new MTSStreamType(138, false, true);
private int tag;
private boolean video;
private boolean audio;
private MTSStreamType(int tag, boolean video, boolean audio) {
this.tag = tag;
this.video = video;
this.audio = audio;
_values.add(this);
}
public static MTSStreamType[] values() {
return _values.<MTSStreamType>toArray(new MTSStreamType[0]);
}
public static MTSStreamType fromTag(int streamTypeTag) {
MTSStreamType[] values = values();
for (int i = 0; i < values.length; i++) {
MTSStreamType streamType = values[i];
if (streamType.tag == streamTypeTag)
return streamType;
}
return null;
}
public int getTag() {
return this.tag;
}
public boolean isVideo() {
return this.video;
}
public boolean isAudio() {
return this.audio;
}
}

View file

@ -0,0 +1,159 @@
package org.jcodec.containers.mps;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.jcodec.common.IntArrayList;
import org.jcodec.common.Preconditions;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.containers.mps.psi.PATSection;
import org.jcodec.containers.mps.psi.PMTSection;
import org.jcodec.containers.mps.psi.PSISection;
public class MTSUtils {
@Deprecated
public static int parsePAT(ByteBuffer data) {
PATSection pat = PATSection.parsePAT(data);
if (pat.getPrograms().size() > 0)
return pat.getPrograms().values()[0];
return -1;
}
@Deprecated
public static PMTSection parsePMT(ByteBuffer data) {
return PMTSection.parsePMT(data);
}
@Deprecated
public static PSISection parseSection(ByteBuffer data) {
return PSISection.parsePSI(data);
}
private static void parseEsInfo(ByteBuffer read) {}
public static PMTSection.PMTStream[] getProgramGuids(File src) throws IOException {
SeekableByteChannel ch = null;
try {
ch = NIOUtils.readableChannel(src);
return getProgramGuidsFromChannel(ch);
} finally {
NIOUtils.closeQuietly(ch);
}
}
public static PMTSection.PMTStream[] getProgramGuidsFromChannel(SeekableByteChannel _in) throws IOException {
PMTExtractor ex = new PMTExtractor();
ex.readTsFile(_in);
PMTSection pmt = ex.getPmt();
return pmt.getStreams();
}
private static class PMTExtractor extends TSReader {
public PMTExtractor() {
super(false);
}
private int pmtGuid = -1;
private PMTSection pmt;
public boolean onPkt(int guid, boolean payloadStart, ByteBuffer tsBuf, long filePos, boolean sectionSyntax, ByteBuffer fullPkt) {
if (guid == 0) {
this.pmtGuid = MTSUtils.parsePAT(tsBuf);
} else if (this.pmtGuid != -1 && guid == this.pmtGuid) {
this.pmt = MTSUtils.parsePMT(tsBuf);
return false;
}
return true;
}
public PMTSection getPmt() {
return this.pmt;
}
}
public static abstract class TSReader {
private static final int TS_SYNC_MARKER = 71;
private static final int TS_PKT_SIZE = 188;
public static final int BUFFER_SIZE = 96256;
private boolean flush;
public TSReader(boolean flush) {
this.flush = flush;
}
public void readTsFile(SeekableByteChannel ch) throws IOException {
ch.setPosition(0L);
ByteBuffer buf = ByteBuffer.allocate(96256);
for (long pos = ch.position(); ch.read(buf) >= 188; pos = ch.position()) {
long posRem = pos;
buf.flip();
while (buf.remaining() >= 188) {
ByteBuffer tsBuf = NIOUtils.read(buf, 188);
ByteBuffer fullPkt = tsBuf.duplicate();
pos += 188L;
Preconditions.checkState((71 == (tsBuf.get() & 0xFF)));
int guidFlags = (tsBuf.get() & 0xFF) << 8 | tsBuf.get() & 0xFF;
int guid = guidFlags & 0x1FFF;
int payloadStart = guidFlags >> 14 & 0x1;
int b0 = tsBuf.get() & 0xFF;
int counter = b0 & 0xF;
if ((b0 & 0x20) != 0)
NIOUtils.skip(tsBuf, tsBuf.get() & 0xFF);
boolean sectionSyntax = (payloadStart == 1 && (NIOUtils.getRel(tsBuf, NIOUtils.getRel(tsBuf, 0) + 2) & 0x80) == 128);
if (sectionSyntax)
NIOUtils.skip(tsBuf, tsBuf.get() & 0xFF);
if (!onPkt(guid, (payloadStart == 1), tsBuf, pos - (long)tsBuf.remaining(), sectionSyntax, fullPkt))
return;
}
if (this.flush) {
buf.flip();
ch.setPosition(posRem);
ch.write(buf);
}
buf.clear();
}
}
protected boolean onPkt(int guid, boolean payloadStart, ByteBuffer tsBuf, long filePos, boolean sectionSyntax, ByteBuffer fullPkt) {
return true;
}
}
public static int getVideoPid(File src) throws IOException {
for (PMTSection.PMTStream stream : getProgramGuids(src)) {
if (stream.getStreamType().isVideo())
return stream.getPid();
}
throw new RuntimeException("No video stream");
}
public static int getAudioPid(File src) throws IOException {
for (PMTSection.PMTStream stream : getProgramGuids(src)) {
if (stream.getStreamType().isAudio())
return stream.getPid();
}
throw new RuntimeException("No audio stream");
}
public static int[] getMediaPidsFromChannel(SeekableByteChannel src) throws IOException {
return filterMediaPids(getProgramGuidsFromChannel(src));
}
public static int[] getMediaPids(File src) throws IOException {
return filterMediaPids(getProgramGuids(src));
}
private static int[] filterMediaPids(PMTSection.PMTStream[] programs) {
IntArrayList result = IntArrayList.createIntArrayList();
for (PMTSection.PMTStream stream : programs) {
if (stream.getStreamType().isVideo() || stream.getStreamType().isAudio())
result.add(stream.getPid());
}
return result.toArray();
}
}

View file

@ -0,0 +1,26 @@
package org.jcodec.containers.mps;
import java.nio.ByteBuffer;
public class PESPacket {
public ByteBuffer data;
public long pts;
public int streamId;
public int length;
public long pos;
public long dts;
public PESPacket(ByteBuffer data, long pts, int streamId, int length, long pos, long dts) {
this.data = data;
this.pts = pts;
this.streamId = streamId;
this.length = length;
this.pos = pos;
this.dts = dts;
}
}

View file

@ -0,0 +1,252 @@
package org.jcodec.containers.mps.index;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jcodec.common.ArrayUtil;
import org.jcodec.common.IntArrayList;
import org.jcodec.common.LongArrayList;
import org.jcodec.common.RunLength;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.tools.MathUtil;
import org.jcodec.containers.mps.MPSUtils;
import org.jcodec.containers.mps.PESPacket;
public abstract class BaseIndexer extends MPSUtils.PESReader {
private Map<Integer, BaseAnalyser> analyzers = new HashMap<>();
private LongArrayList tokens = LongArrayList.createLongArrayList();
private RunLength.Integer streams = new RunLength.Integer();
public int estimateSize() {
int sizeEstimate = (this.tokens.size() << 3) + this.streams.estimateSize() + 128;
for (Integer stream : this.analyzers.keySet())
sizeEstimate += this.analyzers.get(stream).estimateSize();
return sizeEstimate;
}
protected static abstract class BaseAnalyser {
protected IntArrayList pts = new IntArrayList(250000);
protected IntArrayList dur = new IntArrayList(250000);
public abstract void pkt(ByteBuffer param1ByteBuffer, PESPacket param1PESPacket);
public abstract void finishAnalyse();
public int estimateSize() {
return (this.pts.size() << 2) + 4;
}
public abstract MPSIndex.MPSStreamIndex serialize(int param1Int);
}
private static class GenericAnalyser extends BaseAnalyser {
private IntArrayList sizes = new IntArrayList(250000);
private int knownDuration;
private long lastPts;
public void pkt(ByteBuffer pkt, PESPacket pesHeader) {
this.sizes.add(pkt.remaining());
if (pesHeader.pts == -1L) {
pesHeader.pts = this.lastPts + (long)this.knownDuration;
} else {
this.knownDuration = (int)(pesHeader.pts - this.lastPts);
this.lastPts = pesHeader.pts;
}
this.pts.add((int)pesHeader.pts);
this.dur.add(this.knownDuration);
}
public MPSIndex.MPSStreamIndex serialize(int streamId) {
return new MPSIndex.MPSStreamIndex(streamId, this.sizes.toArray(), this.pts.toArray(), this.dur.toArray(), new int[0]);
}
public int estimateSize() {
return super.estimateSize() + (this.sizes.size() << 2) + 32;
}
public void finishAnalyse() {}
}
private static class MPEGVideoAnalyser extends BaseAnalyser {
private int marker = -1;
private long position;
private IntArrayList sizes;
private IntArrayList keyFrames;
private int frameNo;
private boolean inFrameData;
private Frame lastFrame;
private List<Frame> curGop;
private long phPos = -1L;
private Frame lastFrameOfLastGop;
public MPEGVideoAnalyser() {
this.sizes = new IntArrayList(250000);
this.keyFrames = new IntArrayList(20000);
this.curGop = new ArrayList<>();
}
private static class Frame {
long offset;
int size;
int pts;
int tempRef;
}
public void pkt(ByteBuffer pkt, PESPacket pesHeader) {
while (pkt.hasRemaining()) {
int b = pkt.get() & 0xFF;
this.position++;
this.marker = this.marker << 8 | b;
if (this.phPos != -1L) {
long phOffset = this.position - this.phPos;
if (phOffset == 5L) {
this.lastFrame.tempRef = b << 2;
} else if (phOffset == 6L) {
int picCodingType = b >> 3 & 0x7;
this.lastFrame.tempRef |= b >> 6;
if (picCodingType == 1) {
this.keyFrames.add(this.frameNo - 1);
if (this.curGop.size() > 0)
outGop();
}
}
}
if ((this.marker & 0xFFFFFF00) != 256)
continue;
if (this.inFrameData && (this.marker == 256 || this.marker > 431)) {
this.lastFrame.size = (int)(this.position - 4L - this.lastFrame.offset);
this.curGop.add(this.lastFrame);
this.lastFrame = null;
this.inFrameData = false;
} else if (!this.inFrameData && this.marker > 256 && this.marker <= 431) {
this.inFrameData = true;
}
if (this.lastFrame == null && (this.marker == 435 || this.marker == 440 || this.marker == 256)) {
Frame frame = new Frame();
frame.pts = (int)pesHeader.pts;
frame.offset = this.position - 4L;
Logger.info(String.format("FRAME[%d]: %012x, %d", this.frameNo, pesHeader.pos + (long)pkt.position() - 4L, pesHeader.pts));
this.frameNo++;
this.lastFrame = frame;
}
if (this.lastFrame != null && this.lastFrame.pts == -1 && this.marker == 256)
this.lastFrame.pts = (int)pesHeader.pts;
this.phPos = (this.marker == 256) ? (this.position - 4L) : -1L;
}
}
private void outGop() {
fixPts(this.curGop);
for (Frame frame : this.curGop) {
this.sizes.add(frame.size);
this.pts.add(frame.pts);
}
this.curGop.clear();
}
private void fixPts(List<Frame> curGop) {
Frame[] frames = curGop.<Frame>toArray(new Frame[0]);
Arrays.sort(frames, new Comparator<>() {
public int compare(BaseIndexer.MPEGVideoAnalyser.Frame o1, BaseIndexer.MPEGVideoAnalyser.Frame o2) {
return (o1.tempRef > o2.tempRef) ? 1 : ((o1.tempRef == o2.tempRef) ? 0 : -1);
}
});
for (int dir = 0; dir < 3; dir++) {
for (int j = 0, lastPts = -1, secondLastPts = -1, lastTref = -1, secondLastTref = -1; j < frames.length; j++) {
if ((frames[j]).pts == -1 && lastPts != -1 && secondLastPts != -1)
(frames[j]).pts = lastPts + (lastPts - secondLastPts) / MathUtil.abs(lastTref - secondLastTref);
if ((frames[j]).pts != -1) {
secondLastPts = lastPts;
secondLastTref = lastTref;
lastPts = (frames[j]).pts;
lastTref = (frames[j]).tempRef;
}
}
ArrayUtil.reverse(frames);
}
if (this.lastFrameOfLastGop != null)
this.dur.add((frames[0]).pts - this.lastFrameOfLastGop.pts);
for (int i = 1; i < frames.length; i++)
this.dur.add((frames[i]).pts - (frames[i - 1]).pts);
this.lastFrameOfLastGop = frames[frames.length - 1];
}
public void finishAnalyse() {
if (this.lastFrame == null)
return;
this.lastFrame.size = (int)(this.position - this.lastFrame.offset);
this.curGop.add(this.lastFrame);
outGop();
}
public MPSIndex.MPSStreamIndex serialize(int streamId) {
return new MPSIndex.MPSStreamIndex(streamId, this.sizes.toArray(), this.pts.toArray(), this.dur.toArray(), this.keyFrames.toArray());
}
}
private static class Frame {
long offset;
int size;
int pts;
int tempRef;
}
class null implements Comparator<MPEGVideoAnalyser.Frame> {
public int compare(BaseIndexer.MPEGVideoAnalyser.Frame o1, BaseIndexer.MPEGVideoAnalyser.Frame o2) {
return (o1.tempRef > o2.tempRef) ? 1 : ((o1.tempRef == o2.tempRef) ? 0 : -1);
}
}
protected BaseAnalyser getAnalyser(int stream) {
BaseAnalyser analizer = this.analyzers.get(Integer.valueOf(stream));
if (analizer == null) {
analizer = (stream >= 224 && stream <= 239) ? new MPEGVideoAnalyser() : new GenericAnalyser();
this.analyzers.put(Integer.valueOf(stream), analizer);
}
return this.analyzers.get(Integer.valueOf(stream));
}
public MPSIndex serialize() {
List<MPSIndex.MPSStreamIndex> streamsIndices = new ArrayList<>();
Set<Map.Entry<Integer, BaseAnalyser>> entrySet = this.analyzers.entrySet();
for (Map.Entry<Integer, BaseAnalyser> entry : entrySet)
streamsIndices.add(entry.getValue().serialize(entry.getKey().intValue()));
return new MPSIndex(this.tokens.toArray(), this.streams, streamsIndices.<MPSIndex.MPSStreamIndex>toArray(new MPSIndex.MPSStreamIndex[0]));
}
protected void savePESMeta(int stream, long token) {
this.tokens.add(token);
this.streams.add(stream);
}
void finishAnalyse() {
finishRead();
for (BaseAnalyser baseAnalyser : this.analyzers.values())
baseAnalyser.finishAnalyse();
}
}

View file

@ -0,0 +1,157 @@
package org.jcodec.containers.mps.index;
import java.nio.ByteBuffer;
import org.jcodec.common.RunLength;
public class MPSIndex {
protected long[] pesTokens;
protected RunLength.Integer pesStreamIds;
protected MPSStreamIndex[] streams;
public static class MPSStreamIndex {
protected int streamId;
protected int[] fsizes;
protected int[] fpts;
protected int[] fdur;
protected int[] sync;
public MPSStreamIndex(int streamId, int[] fsizes, int[] fpts, int[] fdur, int[] sync) {
this.streamId = streamId;
this.fsizes = fsizes;
this.fpts = fpts;
this.fdur = fdur;
this.sync = sync;
}
public int getStreamId() {
return this.streamId;
}
public int[] getFsizes() {
return this.fsizes;
}
public int[] getFpts() {
return this.fpts;
}
public int[] getFdur() {
return this.fdur;
}
public int[] getSync() {
return this.sync;
}
public static MPSStreamIndex parseIndex(ByteBuffer index) {
int streamId = index.get() & 0xFF;
int fCnt = index.getInt();
int[] fsizes = new int[fCnt];
for (int i = 0; i < fCnt; i++)
fsizes[i] = index.getInt();
int fptsCnt = index.getInt();
int[] fpts = new int[fptsCnt];
for (int j = 0; j < fptsCnt; j++)
fpts[j] = index.getInt();
int fdurCnt = index.getInt();
int[] fdur = new int[fdurCnt];
for (int k = 0; k < fdurCnt; k++)
fdur[k] = index.getInt();
int syncCount = index.getInt();
int[] sync = new int[syncCount];
for (int m = 0; m < syncCount; m++)
sync[m] = index.getInt();
return new MPSStreamIndex(streamId, fsizes, fpts, fdur, sync);
}
public void serialize(ByteBuffer index) {
index.put((byte)this.streamId);
index.putInt(this.fsizes.length);
for (int m = 0; m < this.fsizes.length; m++)
index.putInt(this.fsizes[m]);
index.putInt(this.fpts.length);
for (int k = 0; k < this.fpts.length; k++)
index.putInt(this.fpts[k]);
index.putInt(this.fdur.length);
for (int j = 0; j < this.fdur.length; j++)
index.putInt(this.fdur[j]);
index.putInt(this.sync.length);
for (int i = 0; i < this.sync.length; i++)
index.putInt(this.sync[i]);
}
public int estimateSize() {
return (this.fpts.length << 2) + (this.fdur.length << 2) + (this.sync.length << 2) + (this.fsizes.length << 2) + 64;
}
}
public MPSIndex(long[] pesTokens, RunLength.Integer pesStreamIds, MPSStreamIndex[] streams) {
this.pesTokens = pesTokens;
this.pesStreamIds = pesStreamIds;
this.streams = streams;
}
public long[] getPesTokens() {
return this.pesTokens;
}
public RunLength.Integer getPesStreamIds() {
return this.pesStreamIds;
}
public MPSStreamIndex[] getStreams() {
return this.streams;
}
public static MPSIndex parseIndex(ByteBuffer index) {
int pesCnt = index.getInt();
long[] pesTokens = new long[pesCnt];
for (int i = 0; i < pesCnt; i++)
pesTokens[i] = index.getLong();
RunLength.Integer pesStreamId = RunLength.Integer.parse(index);
int nStreams = index.getInt();
MPSStreamIndex[] streams = new MPSStreamIndex[nStreams];
for (int j = 0; j < nStreams; j++)
streams[j] = MPSStreamIndex.parseIndex(index);
return new MPSIndex(pesTokens, pesStreamId, streams);
}
public void serializeTo(ByteBuffer index) {
index.putInt(this.pesTokens.length);
for (int i = 0; i < this.pesTokens.length; i++)
index.putLong(this.pesTokens[i]);
this.pesStreamIds.serialize(index);
index.putInt(this.streams.length);
for (MPSStreamIndex mpsStreamIndex : this.streams)
mpsStreamIndex.serialize(index);
}
public int estimateSize() {
int size = (this.pesTokens.length << 3) + this.pesStreamIds.estimateSize();
for (MPSStreamIndex mpsStreamIndex : this.streams)
size += mpsStreamIndex.estimateSize();
return size + 64;
}
public static long makePESToken(long leading, long pesLen, long payloadLen) {
return leading << 48L | pesLen << 24L | payloadLen;
}
public static int leadingSize(long token) {
return (int)(token >> 48L) & 0xFFFF;
}
public static int pesLen(long token) {
return (int)(token >> 24L) & 0xFFFFFF;
}
public static int payLoadSize(long token) {
return (int)token & 0xFFFFFF;
}
}

View file

@ -0,0 +1,58 @@
package org.jcodec.containers.mps.index;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.containers.mps.MPSUtils;
import org.jcodec.containers.mps.PESPacket;
public class MPSIndexer extends BaseIndexer {
private long predFileStart;
public void index(File source, NIOUtils.FileReaderListener listener) throws IOException {
newReader().readFile(source, 65536, listener);
}
public void indexChannel(SeekableByteChannel source, NIOUtils.FileReaderListener listener) throws IOException {
newReader().readChannel(source, 65536, listener);
}
private NIOUtils.FileReader newReader() {
final MPSIndexer self = this;
return new NIOUtils.FileReader() {
protected void data(ByteBuffer data, long filePos) {
self.analyseBuffer(data, filePos);
}
protected void done() {
self.finishAnalyse();
}
};
}
protected void pes(ByteBuffer pesBuffer, long start, int pesLen, int stream) {
if (!MPSUtils.mediaStream(stream))
return;
PESPacket pesHeader = MPSUtils.readPESHeader(pesBuffer, start);
int leading = 0;
if (this.predFileStart != start)
leading += (int)(start - this.predFileStart);
this.predFileStart = start + (long)pesLen;
savePESMeta(stream, MPSIndex.makePESToken((long)leading, (long)pesLen, (long)pesBuffer.remaining()));
getAnalyser(stream).pkt(pesBuffer, pesHeader);
}
public static void main1(String[] args) throws IOException {
MPSIndexer indexer = new MPSIndexer();
indexer.index(new File(args[0]), new NIOUtils.FileReaderListener() {
public void progress(int percentDone) {
System.out.println(percentDone);
}
});
ByteBuffer index = ByteBuffer.allocate(65536);
indexer.serialize().serializeTo(index);
NIOUtils.writeTo(index, new File(args[1]));
}
}

View file

@ -0,0 +1,174 @@
package org.jcodec.containers.mps.index;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.jcodec.api.NotSupportedException;
import org.jcodec.common.DemuxerTrackMeta;
import org.jcodec.common.SeekableDemuxerTrack;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.model.Packet;
import org.jcodec.containers.mps.MPSUtils;
import org.jcodec.platform.Platform;
public class MPSRandomAccessDemuxer {
private Stream[] streams;
private long[] pesTokens;
private int[] pesStreamIds;
public MPSRandomAccessDemuxer(SeekableByteChannel ch, MPSIndex mpsIndex) throws IOException {
this.pesTokens = mpsIndex.getPesTokens();
this.pesStreamIds = mpsIndex.getPesStreamIds().flattern();
MPSIndex.MPSStreamIndex[] streamIndices = mpsIndex.getStreams();
this.streams = new Stream[streamIndices.length];
for (int i = 0; i < streamIndices.length; i++)
this.streams[i] = newStream(ch, streamIndices[i]);
}
protected Stream newStream(SeekableByteChannel ch, MPSIndex.MPSStreamIndex streamIndex) throws IOException {
return new Stream(this, streamIndex, ch);
}
public Stream[] getStreams() {
return this.streams;
}
public static class Stream extends MPSIndex.MPSStreamIndex implements SeekableDemuxerTrack {
private static final int MPEG_TIMESCALE = 90000;
private int curPesIdx;
private int curFrame;
private ByteBuffer pesBuf;
private int _seekToFrame = -1;
protected SeekableByteChannel source;
private long[] foffs;
private MPSRandomAccessDemuxer demuxer;
public Stream(MPSRandomAccessDemuxer demuxer, MPSIndex.MPSStreamIndex streamIndex, SeekableByteChannel source) throws IOException {
super(streamIndex.streamId, streamIndex.fsizes, streamIndex.fpts, streamIndex.fdur, streamIndex.sync);
this.demuxer = demuxer;
this.source = source;
this.foffs = new long[this.fsizes.length];
long curOff = 0L;
for (int i = 0; i < this.fsizes.length; i++) {
this.foffs[i] = curOff;
curOff += (long)this.fsizes[i];
}
int[] seg = Platform.copyOfInt(streamIndex.getFpts(), 100);
Arrays.sort(seg);
this._seekToFrame = 0;
seekToFrame();
}
public Packet nextFrame() throws IOException {
seekToFrame();
if (this.curFrame >= this.fsizes.length)
return null;
int fs = this.fsizes[this.curFrame];
ByteBuffer result = ByteBuffer.allocate(fs);
return _nextFrame(result);
}
private Packet _nextFrame(ByteBuffer buf) throws IOException {
seekToFrame();
if (this.curFrame >= this.fsizes.length)
return null;
int fs = this.fsizes[this.curFrame];
ByteBuffer result = buf.duplicate();
result.limit(result.position() + fs);
while (result.hasRemaining()) {
if (this.pesBuf.hasRemaining()) {
result.put(NIOUtils.read(this.pesBuf, Math.min(this.pesBuf.remaining(), result.remaining())));
continue;
}
this.curPesIdx++;
long posShift = 0L;
while (this.demuxer.pesStreamIds[this.curPesIdx] != this.streamId) {
posShift += (long)(MPSIndex.pesLen(this.demuxer.pesTokens[this.curPesIdx]) + MPSIndex.leadingSize(this.demuxer.pesTokens[this.curPesIdx]));
this.curPesIdx++;
}
skip(posShift + (long)MPSIndex.leadingSize(this.demuxer.pesTokens[this.curPesIdx]));
int pesLen = MPSIndex.pesLen(this.demuxer.pesTokens[this.curPesIdx]);
this.pesBuf = fetch(pesLen);
MPSUtils.readPESHeader(this.pesBuf, 0L);
}
result.flip();
Packet pkt = Packet.createPacket(result, (long)this.fpts[this.curFrame], 90000, (long)this.fdur[this.curFrame], (long)this.curFrame, (
this.sync.length == 0 || Arrays.binarySearch(this.sync, this.curFrame) >= 0) ? Packet.FrameType.KEY : Packet.FrameType.INTER, null);
this.curFrame++;
return pkt;
}
protected ByteBuffer fetch(int pesLen) throws IOException {
return NIOUtils.fetchFromChannel(this.source, pesLen);
}
protected void skip(long leadingSize) throws IOException {
this.source.setPosition(this.source.position() + leadingSize);
}
protected void reset() throws IOException {
this.source.setPosition(0L);
}
public DemuxerTrackMeta getMeta() {
return null;
}
public boolean gotoFrame(long frameNo) {
this._seekToFrame = (int)frameNo;
return true;
}
public boolean gotoSyncFrame(long frameNo) {
for (int i = 0; i < this.sync.length; i++) {
if ((long)this.sync[i] > frameNo) {
this._seekToFrame = this.sync[i - 1];
return true;
}
}
this._seekToFrame = this.sync[this.sync.length - 1];
return true;
}
private void seekToFrame() throws IOException {
if (this._seekToFrame == -1)
return;
this.curFrame = this._seekToFrame;
long payloadOff = this.foffs[this.curFrame];
long posShift = 0L;
reset();
for (this.curPesIdx = 0;; this.curPesIdx++) {
if (this.demuxer.pesStreamIds[this.curPesIdx] == this.streamId) {
int payloadSize = MPSIndex.payLoadSize(this.demuxer.pesTokens[this.curPesIdx]);
if (payloadOff < (long)payloadSize)
break;
payloadOff -= (long)payloadSize;
}
posShift += (long)(MPSIndex.pesLen(this.demuxer.pesTokens[this.curPesIdx]) + MPSIndex.leadingSize(this.demuxer.pesTokens[this.curPesIdx]));
}
skip(posShift + (long)MPSIndex.leadingSize(this.demuxer.pesTokens[this.curPesIdx]));
this.pesBuf = fetch(MPSIndex.pesLen(this.demuxer.pesTokens[this.curPesIdx]));
MPSUtils.readPESHeader(this.pesBuf, 0L);
NIOUtils.skip(this.pesBuf, (int)payloadOff);
this._seekToFrame = -1;
}
public long getCurFrame() {
return (long)this.curFrame;
}
public void seek(double second) {
throw new NotSupportedException("");
}
}
}

View file

@ -0,0 +1,79 @@
package org.jcodec.containers.mps.index;
import java.nio.ByteBuffer;
import org.jcodec.common.RunLength;
import org.jcodec.common.io.NIOUtils;
public class MTSIndex {
private MTSProgram[] programs;
public static MTSProgram createMTSProgram(MPSIndex mpsIndex, int target) {
MTSProgram m = new MTSProgram(mpsIndex.pesTokens, mpsIndex.pesStreamIds, mpsIndex.streams, target);
return m;
}
public static class MTSProgram extends MPSIndex {
private int targetGuid;
public MTSProgram(long[] pesTokens, RunLength.Integer pesStreamIds, MPSIndex.MPSStreamIndex[] streams, int targetGuid) {
super(pesTokens, pesStreamIds, streams);
this.targetGuid = targetGuid;
}
public int getTargetGuid() {
return this.targetGuid;
}
public void serializeTo(ByteBuffer index) {
index.putInt(this.targetGuid);
super.serializeTo(index);
}
public static MTSProgram parse(ByteBuffer read) {
int targetGuid = read.getInt();
return MTSIndex.createMTSProgram(MPSIndex.parseIndex(read), targetGuid);
}
}
public MTSIndex(MTSProgram[] programs) {
this.programs = programs;
}
public MTSProgram[] getPrograms() {
return this.programs;
}
public static MTSIndex parse(ByteBuffer buf) {
int numPrograms = buf.getInt();
MTSProgram[] programs = new MTSProgram[numPrograms];
for (int i = 0; i < numPrograms; i++) {
int programDataSize = buf.getInt();
programs[i] = MTSProgram.parse(NIOUtils.read(buf, programDataSize));
}
return new MTSIndex(programs);
}
public int estimateSize() {
int totalSize = 64;
for (MTSProgram mtsProgram : this.programs)
totalSize += 4 + mtsProgram.estimateSize();
return totalSize;
}
public void serializeTo(ByteBuffer buf) {
buf.putInt(this.programs.length);
for (MTSProgram mtsAnalyser : this.programs) {
ByteBuffer dup = buf.duplicate();
NIOUtils.skip(buf, 4);
mtsAnalyser.serializeTo(buf);
dup.putInt(buf.position() - dup.position() - 4);
}
}
public ByteBuffer serialize() {
ByteBuffer bb = ByteBuffer.allocate(estimateSize());
serializeTo(bb);
bb.flip();
return bb;
}
}

View file

@ -0,0 +1,117 @@
package org.jcodec.containers.mps.index;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.jcodec.common.Preconditions;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.logging.Logger;
import org.jcodec.containers.mps.MPSUtils;
import org.jcodec.containers.mps.MTSUtils;
import org.jcodec.containers.mps.PESPacket;
public class MTSIndexer {
public static final int BUFFER_SIZE = 96256;
private MTSAnalyser[] indexers;
public void index(File source, NIOUtils.FileReaderListener listener) throws IOException {
indexReader(listener, MTSUtils.getMediaPids(source)).readFile(source, 96256, listener);
}
public void indexChannel(SeekableByteChannel source, NIOUtils.FileReaderListener listener) throws IOException {
indexReader(listener, MTSUtils.getMediaPidsFromChannel(source)).readChannel(source, 96256, listener);
}
public NIOUtils.FileReader indexReader(NIOUtils.FileReaderListener listener, int[] targetGuids) throws IOException {
this.indexers = new MTSAnalyser[targetGuids.length];
for (int i = 0; i < targetGuids.length; i++)
this.indexers[i] = new MTSAnalyser(targetGuids[i]);
return new MTSFileReader(this);
}
public MTSIndex serialize() {
MTSIndex.MTSProgram[] programs = new MTSIndex.MTSProgram[this.indexers.length];
for (int i = 0; i < this.indexers.length; i++)
programs[i] = this.indexers[i].serializeTo();
return new MTSIndex(programs);
}
private static final class MTSFileReader extends NIOUtils.FileReader {
private MTSIndexer indexer;
public MTSFileReader(MTSIndexer indexer) {
this.indexer = indexer;
}
protected void data(ByteBuffer data, long filePos) {
analyseBuffer(data, filePos);
}
protected void analyseBuffer(ByteBuffer buf, long pos) {
while (buf.hasRemaining()) {
ByteBuffer tsBuf = NIOUtils.read(buf, 188);
pos += 188L;
Preconditions.checkState((71 == (tsBuf.get() & 0xFF)));
int guidFlags = (tsBuf.get() & 0xFF) << 8 | tsBuf.get() & 0xFF;
int guid = guidFlags & 0x1FFF;
for (int i = 0; i < this.indexer.indexers.length; i++) {
if (guid == (this.indexer.indexers[i]).targetGuid) {
int payloadStart = guidFlags >> 14 & 0x1;
int b0 = tsBuf.get() & 0xFF;
int counter = b0 & 0xF;
if ((b0 & 0x20) != 0)
NIOUtils.skip(tsBuf, tsBuf.get() & 0xFF);
this.indexer.indexers[i].analyseBuffer(tsBuf, pos - (long)tsBuf.remaining());
}
}
}
}
protected void done() {
for (MTSIndexer.MTSAnalyser mtsAnalyser : this.indexer.indexers)
mtsAnalyser.finishAnalyse();
}
}
private static class MTSAnalyser extends BaseIndexer {
private int targetGuid;
private long predFileStartInTsPkt;
public MTSAnalyser(int targetGuid) {
this.targetGuid = targetGuid;
}
public MTSIndex.MTSProgram serializeTo() {
return MTSIndex.createMTSProgram(serialize(), this.targetGuid);
}
protected void pes(ByteBuffer pesBuffer, long start, int pesLen, int stream) {
if (!MPSUtils.mediaStream(stream))
return;
Logger.debug(String.format("PES: %08x, %d", start, pesLen));
PESPacket pesHeader = MPSUtils.readPESHeader(pesBuffer, start);
int leadingTsPkt = 0;
if (this.predFileStartInTsPkt != start)
leadingTsPkt = (int)(start / 188L - this.predFileStartInTsPkt);
this.predFileStartInTsPkt = (start + (long)pesLen) / 188L;
int tsPktInPes = (int)(this.predFileStartInTsPkt - start / 188L);
savePESMeta(stream, MPSIndex.makePESToken((long)leadingTsPkt, (long)tsPktInPes, (long)pesBuffer.remaining()));
getAnalyser(stream).pkt(pesBuffer, pesHeader);
}
}
public static void main1(String[] args) throws IOException {
File src = new File(args[0]);
MTSIndexer indexer = new MTSIndexer();
indexer.index(src, new NIOUtils.FileReaderListener() {
public void progress(int percentDone) {
System.out.println(percentDone);
}
});
MTSIndex index = indexer.serialize();
NIOUtils.writeTo(index.serialize(), new File(args[1]));
}
}

View file

@ -0,0 +1,70 @@
package org.jcodec.containers.mps.index;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.jcodec.common.Preconditions;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
public class MTSRandomAccessDemuxer {
private MTSIndex.MTSProgram[] programs;
private SeekableByteChannel ch;
public MTSRandomAccessDemuxer(SeekableByteChannel ch, MTSIndex index) {
this.programs = index.getPrograms();
this.ch = ch;
}
public int[] getGuids() {
int[] guids = new int[this.programs.length];
for (int i = 0; i < this.programs.length; i++)
guids[i] = this.programs[i].getTargetGuid();
return guids;
}
public MPSRandomAccessDemuxer getProgramDemuxer(final int tgtGuid) throws IOException {
MPSIndex index = getProgram(tgtGuid);
return new MPSRandomAccessDemuxer(this.ch, index) {
protected MPSRandomAccessDemuxer.Stream newStream(SeekableByteChannel ch, MPSIndex.MPSStreamIndex streamIndex) throws IOException {
return new MPSRandomAccessDemuxer.Stream(this, streamIndex, ch) {
protected ByteBuffer fetch(int pesLen) throws IOException {
ByteBuffer bb = ByteBuffer.allocate(pesLen * 188);
for (int i = 0; i < pesLen; i++) {
ByteBuffer tsBuf = NIOUtils.fetchFromChannel(this.source, 188);
Preconditions.checkState((71 == (tsBuf.get() & 0xFF)));
int guidFlags = (tsBuf.get() & 0xFF) << 8 | tsBuf.get() & 0xFF;
int guid = guidFlags & 0x1FFF;
if (guid == tgtGuid) {
int payloadStart = guidFlags >> 14 & 0x1;
int b0 = tsBuf.get() & 0xFF;
int counter = b0 & 0xF;
if ((b0 & 0x20) != 0)
NIOUtils.skip(tsBuf, tsBuf.get() & 0xFF);
bb.put(tsBuf);
}
}
bb.flip();
return bb;
}
protected void skip(long leadingSize) throws IOException {
this.source.setPosition(this.source.position() + leadingSize * 188L);
}
protected void reset() throws IOException {
this.source.setPosition(0L);
}
};
}
};
}
private MPSIndex getProgram(int guid) {
for (MTSIndex.MTSProgram mtsProgram : this.programs) {
if (mtsProgram.getTargetGuid() == guid)
return mtsProgram;
}
return null;
}
}

View file

@ -0,0 +1,57 @@
package org.jcodec.containers.mps.index;
import java.io.File;
import java.io.IOException;
import org.jcodec.codecs.mpeg12.MPEGDecoder;
import org.jcodec.common.Codec;
import org.jcodec.common.MuxerTrack;
import org.jcodec.common.VideoCodecMeta;
import org.jcodec.common.io.FileChannelWrapper;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.model.Packet;
import org.jcodec.containers.mp4.Brand;
import org.jcodec.containers.mp4.MP4Packet;
import org.jcodec.containers.mp4.muxer.MP4Muxer;
public class MTSRandomAccessDemuxerMain {
public static void main1(String[] args) throws IOException {
MTSIndex index;
MTSIndexer indexer = new MTSIndexer();
File source = new File(args[0]);
File indexFile = new File(source.getParentFile(), source.getName() + ".idx");
if (!indexFile.exists()) {
indexer.index(source, null);
index = indexer.serialize();
NIOUtils.writeTo(index.serialize(), indexFile);
} else {
System.out.println("Reading index from: " + indexFile.getName());
index = MTSIndex.parse(NIOUtils.fetchFromFile(indexFile));
}
MTSRandomAccessDemuxer demuxer = new MTSRandomAccessDemuxer(NIOUtils.readableChannel(source), index);
int[] guids = demuxer.getGuids();
MPSRandomAccessDemuxer.Stream video = getVideoStream(demuxer.getProgramDemuxer(guids[0]));
FileChannelWrapper ch = NIOUtils.writableChannel(new File(args[1]));
MP4Muxer mp4Muxer = MP4Muxer.createMP4Muxer(ch, Brand.MOV);
video.gotoSyncFrame(175L);
Packet pkt = video.nextFrame();
VideoCodecMeta meta = new MPEGDecoder().getCodecMeta(pkt.getData());
MuxerTrack videoTrack = mp4Muxer.addVideoTrack(Codec.MPEG2, meta);
long firstPts = pkt.getPts();
for (int i = 0; pkt != null && i < 150; i++) {
videoTrack.addFrame(MP4Packet.createMP4Packet(pkt.getData(), pkt.getPts() - firstPts, pkt.getTimescale(),
pkt.getDuration(), pkt.getFrameNo(), pkt.getFrameType(), pkt.getTapeTimecode(), 0, pkt.getPts() - firstPts, 0));
pkt = video.nextFrame();
}
mp4Muxer.finish();
NIOUtils.closeQuietly(ch);
}
private static MPSRandomAccessDemuxer.Stream getVideoStream(MPSRandomAccessDemuxer demuxer) {
MPSRandomAccessDemuxer.Stream[] streams = demuxer.getStreams();
for (MPSRandomAccessDemuxer.Stream stream : streams) {
if (stream.getStreamId() >= 224 && stream.getStreamId() <= 239)
return stream;
}
return null;
}
}

View file

@ -0,0 +1,42 @@
package org.jcodec.containers.mps.psi;
import java.nio.ByteBuffer;
import org.jcodec.common.IntArrayList;
import org.jcodec.common.IntIntMap;
public class PATSection extends PSISection {
private int[] networkPids;
private IntIntMap programs;
public PATSection(PSISection psi, int[] networkPids, IntIntMap programs) {
super(psi.tableId, psi.specificId, psi.versionNumber, psi.currentNextIndicator, psi.sectionNumber, psi.lastSectionNumber);
this.networkPids = networkPids;
this.programs = programs;
}
public int[] getNetworkPids() {
return this.networkPids;
}
public IntIntMap getPrograms() {
return this.programs;
}
public static PATSection parsePAT(ByteBuffer data) {
PSISection psi = PSISection.parsePSI(data);
IntArrayList networkPids = IntArrayList.createIntArrayList();
IntIntMap programs = new IntIntMap();
while (data.remaining() > 4) {
int programNum = data.getShort() & 0xFFFF;
int w = data.getShort();
int pid = w & 0x1FFF;
if (programNum == 0) {
networkPids.add(pid);
continue;
}
programs.put(programNum, pid);
}
return new PATSection(psi, networkPids.toArray(), programs);
}
}

View file

@ -0,0 +1,117 @@
package org.jcodec.containers.mps.psi;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.containers.mps.MPSUtils;
import org.jcodec.containers.mps.MTSStreamType;
public class PMTSection extends PSISection {
private int pcrPid;
private Tag[] tags;
private PMTStream[] streams;
public PMTSection(PSISection psi, int pcrPid, Tag[] tags, PMTStream[] streams) {
super(psi.tableId, psi.specificId, psi.versionNumber, psi.currentNextIndicator, psi.sectionNumber, psi.lastSectionNumber);
this.pcrPid = pcrPid;
this.tags = tags;
this.streams = streams;
}
public int getPcrPid() {
return this.pcrPid;
}
public Tag[] getTags() {
return this.tags;
}
public PMTStream[] getStreams() {
return this.streams;
}
public static PMTSection parsePMT(ByteBuffer data) {
PSISection psi = PSISection.parsePSI(data);
int w1 = data.getShort() & 0xFFFF;
int pcrPid = w1 & 0x1FFF;
int w2 = data.getShort() & 0xFFFF;
int programInfoLength = w2 & 0xFFF;
List<Tag> tags = parseTags(NIOUtils.read(data, programInfoLength));
List<PMTStream> streams = new ArrayList<>();
while (data.remaining() > 4) {
int streamType = data.get() & 0xFF;
int wn = data.getShort() & 0xFFFF;
int elementaryPid = wn & 0x1FFF;
int wn1 = data.getShort() & 0xFFFF;
int esInfoLength = wn1 & 0xFFF;
ByteBuffer read = NIOUtils.read(data, esInfoLength);
streams.add(new PMTStream(streamType, elementaryPid, MPSUtils.parseDescriptors(read)));
}
return new PMTSection(psi, pcrPid, tags.<Tag>toArray(new Tag[0]), streams.<PMTStream>toArray(new PMTStream[0]));
}
static List<Tag> parseTags(ByteBuffer bb) {
List<Tag> tags = new ArrayList<>();
while (bb.hasRemaining()) {
int tag = bb.get();
int tagLen = bb.get();
tags.add(new Tag(tag, NIOUtils.read(bb, tagLen)));
}
return tags;
}
public static class Tag {
private int tag;
private ByteBuffer content;
public Tag(int tag, ByteBuffer content) {
this.tag = tag;
this.content = content;
}
public int getTag() {
return this.tag;
}
public ByteBuffer getContent() {
return this.content;
}
}
public static class PMTStream {
private int streamTypeTag;
private int pid;
private List<MPSUtils.MPEGMediaDescriptor> descriptors;
private MTSStreamType streamType;
public PMTStream(int streamTypeTag, int pid, List<MPSUtils.MPEGMediaDescriptor> descriptors) {
this.streamTypeTag = streamTypeTag;
this.pid = pid;
this.descriptors = descriptors;
this.streamType = MTSStreamType.fromTag(streamTypeTag);
}
public int getStreamTypeTag() {
return this.streamTypeTag;
}
public MTSStreamType getStreamType() {
return this.streamType;
}
public int getPid() {
return this.pid;
}
public List<MPSUtils.MPEGMediaDescriptor> getDesctiptors() {
return this.descriptors;
}
}
}

View file

@ -0,0 +1,66 @@
package org.jcodec.containers.mps.psi;
import java.nio.ByteBuffer;
public class PSISection {
protected int tableId;
protected int specificId;
protected int versionNumber;
protected int currentNextIndicator;
protected int sectionNumber;
protected int lastSectionNumber;
public PSISection(int tableId, int specificId, int versionNumber, int currentNextIndicator, int sectionNumber, int lastSectionNumber) {
this.tableId = tableId;
this.specificId = specificId;
this.versionNumber = versionNumber;
this.currentNextIndicator = currentNextIndicator;
this.sectionNumber = sectionNumber;
this.lastSectionNumber = lastSectionNumber;
}
public static PSISection parsePSI(ByteBuffer data) {
int tableId = data.get() & 0xFF;
int w0 = data.getShort() & 0xFFFF;
if ((w0 & 0xC000) != 32768)
throw new RuntimeException("Invalid section data");
int sectionLength = w0 & 0xFFF;
data.limit(data.position() + sectionLength);
int specificId = data.getShort() & 0xFFFF;
int b0 = data.get() & 0xFF;
int versionNumber = b0 >> 1 & 0x1F;
int currentNextIndicator = b0 & 0x1;
int sectionNumber = data.get() & 0xFF;
int lastSectionNumber = data.get() & 0xFF;
return new PSISection(tableId, specificId, versionNumber, currentNextIndicator, sectionNumber, lastSectionNumber);
}
public int getTableId() {
return this.tableId;
}
public int getSpecificId() {
return this.specificId;
}
public int getVersionNumber() {
return this.versionNumber;
}
public int getCurrentNextIndicator() {
return this.currentNextIndicator;
}
public int getSectionNumber() {
return this.sectionNumber;
}
public int getLastSectionNumber() {
return this.lastSectionNumber;
}
}