package org.jcodec.containers.mps; import static org.jcodec.common.io.NIOUtils.getRel; import org.jcodec.common.Assert; import org.jcodec.common.IntArrayList; 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.PMTSection.PMTStream; import org.jcodec.containers.mps.psi.PSISection; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * @author The JCodec project * */ public class MTSUtils { /** * Parses PAT ( Program Association Table ) * * @param data * @deprecated Use org.jcodec.containers.mps.psi.PAT.parse method instead, * this method will not work correctly for streams with multiple * programs * @return Pid of the first PMT found in the PAT */ @Deprecated public static int parsePAT(ByteBuffer data) { PATSection pat = PATSection.parsePAT(data); if (pat.getPrograms().size() > 0) return pat.getPrograms().values()[0]; else 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 PMTStream[] getProgramGuids(File src) throws IOException { SeekableByteChannel ch = null; try { ch = NIOUtils.readableChannel(src); return getProgramGuidsFromChannel(ch); } finally { NIOUtils.closeQuietly(ch); } } public static 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; @Override public boolean onPkt(int guid, boolean payloadStart, ByteBuffer tsBuf, long filePos, boolean sectionSyntax, ByteBuffer fullPkt) { if (guid == 0) { pmtGuid = parsePAT(tsBuf); } else if (pmtGuid != -1 && guid == pmtGuid) { pmt = parsePMT(tsBuf); return false; } return true; } public PMTSection getPmt() { return pmt; } }; public static abstract class TSReader { private static final int TS_SYNC_MARKER = 0x47; private static final int TS_PKT_SIZE = 188; // Buffer must have an integral number of MPEG TS packets public static final int BUFFER_SIZE = TS_PKT_SIZE << 9; private boolean flush; public TSReader(boolean flush) { this.flush = flush; } public void readTsFile(SeekableByteChannel ch) throws IOException { ch.setPosition(0); ByteBuffer buf = ByteBuffer.allocate(BUFFER_SIZE); for (long pos = ch.position(); ch.read(buf) >= TS_PKT_SIZE; pos = ch.position()) { long posRem = pos; buf.flip(); while (buf.remaining() >= TS_PKT_SIZE) { ByteBuffer tsBuf = NIOUtils.read(buf, TS_PKT_SIZE); ByteBuffer fullPkt = tsBuf.duplicate(); pos += TS_PKT_SIZE; Assert.assertEquals(TS_SYNC_MARKER, tsBuf.get() & 0xff); int guidFlags = ((tsBuf.get() & 0xff) << 8) | (tsBuf.get() & 0xff); int guid = (int) 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 && (getRel(tsBuf, getRel(tsBuf, 0) + 2) & 0x80) == 0x80; if (sectionSyntax) { // Adaptation field NIOUtils.skip(tsBuf, tsBuf.get() & 0xff); } if (!onPkt(guid, payloadStart == 1, tsBuf, pos - tsBuf.remaining(), sectionSyntax, fullPkt)) return; } if (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) { // DO NOTHING return true; } } public static int getVideoPid(File src) throws IOException { for (PMTStream stream : MTSUtils.getProgramGuids(src)) { if (stream.getStreamType().isVideo()) return stream.getPid(); } throw new RuntimeException("No video stream"); } public static int getAudioPid(File src) throws IOException { for (PMTStream stream : MTSUtils.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(MTSUtils.getProgramGuidsFromChannel(src)); } public static int[] getMediaPids(File src) throws IOException { return filterMediaPids(MTSUtils.getProgramGuids(src)); } private static int[] filterMediaPids(PMTStream[] programs) { IntArrayList result = IntArrayList.createIntArrayList(); for (PMTStream stream : programs) { if (stream.getStreamType().isVideo() || stream.getStreamType().isAudio()) result.add(stream.getPid()); } return result.toArray(); } }