/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.container.mpegts; import java.util.HashMap; import java.util.Map; import org.apache.log4j.Logger; import com.ttProject.container.mpegts.field.PmtElementaryField; import com.ttProject.container.mpegts.type.Pat; import com.ttProject.container.mpegts.type.Pes; import com.ttProject.container.mpegts.type.Pmt; import com.ttProject.container.mpegts.type.Sdt; import com.ttProject.frame.IAnalyzer; import com.ttProject.frame.aac.AacFrameAnalyzer; import com.ttProject.frame.h264.NalAnalyzer; import com.ttProject.frame.mp3.Mp3FrameAnalyzer; import com.ttProject.nio.channels.IReadChannel; import com.ttProject.unit.ISelector; import com.ttProject.unit.IUnit; import com.ttProject.unit.extra.BitLoader; import com.ttProject.unit.extra.bit.Bit1; import com.ttProject.unit.extra.bit.Bit13; import com.ttProject.unit.extra.bit.Bit2; import com.ttProject.unit.extra.bit.Bit4; import com.ttProject.unit.extra.bit.Bit8; /** * selector for mpegtsPacket * @author taktod */ public class MpegtsPacketSelector implements ISelector { /** logger */ private Logger logger = Logger.getLogger(MpegtsPacketSelector.class); private final int patPid = 0x0000; private final int sdtPid = 0x0011; private Sdt sdt = null; private Pat pat = null; private Pmt pmt = null; private Map<Integer, Pes> pesMap = new HashMap<Integer, Pes>(); private Map<Integer, IAnalyzer> analyzerMap = new HashMap<Integer, IAnalyzer>(); /** * {@inheritDoc} */ @Override public IUnit select(IReadChannel channel) throws Exception { if(channel.size() == channel.position()) { // no more information. return null; } // read first 4 byte.(mpegts packet header.) Bit8 syncByte = new Bit8(); Bit1 transportErrorIndicator = new Bit1(); Bit1 payloadUnitStartIndicator = new Bit1(); Bit1 transportPriority = new Bit1(); Bit13 pid = new Bit13(); Bit2 scramblingControl = new Bit2(); Bit1 adaptationFieldExist = new Bit1(); Bit1 payloadFieldExist = new Bit1(); Bit4 continuityCounter = new Bit4(); BitLoader loader = new BitLoader(channel); loader.load(syncByte, transportErrorIndicator, payloadUnitStartIndicator, transportPriority, pid, scramblingControl, adaptationFieldExist, payloadFieldExist, continuityCounter); if(syncByte.get() != 0x47) { throw new Exception("syncBit is invalid."); } MpegtsPacket packet = null; if(pid.get() == sdtPid) { Sdt tmpSdt = new Sdt(syncByte, transportErrorIndicator, payloadUnitStartIndicator, transportPriority, pid, scramblingControl, adaptationFieldExist, payloadFieldExist, continuityCounter); tmpSdt.minimumLoad(channel); if(sdt == null || sdt.getCrc() != tmpSdt.getCrc()) { // is sdt is null or crc is different, update with new sdt. sdt = tmpSdt; } return sdt; } else if(pid.get() == patPid) { // hold pat information. Pat tmpPat = new Pat(syncByte, transportErrorIndicator, payloadUnitStartIndicator, transportPriority, pid, scramblingControl, adaptationFieldExist, payloadFieldExist, continuityCounter); tmpPat.minimumLoad(channel); if(pat == null || pat.getCrc() != tmpPat.getCrc()) { pat = tmpPat; } return pat; } else if(pat != null && pid.get() == pat.getPmtPid()){ Pmt tmpPmt = new Pmt(syncByte, transportErrorIndicator, payloadUnitStartIndicator, transportPriority, pid, scramblingControl, adaptationFieldExist, payloadFieldExist, continuityCounter); tmpPmt.minimumLoad(channel); if(pmt == null || pmt.getCrc() != tmpPmt.getCrc()) { pmt = tmpPmt; } else { return pmt; } // in order to initialize elementaryField, need to load first. pmt.load(channel); // make analyzer. for(PmtElementaryField elementaryField : pmt.getFields()) { logger.info(elementaryField.getCodecType()); switch(elementaryField.getCodecType()) { case AUDIO_AAC: analyzerMap.put((int)elementaryField.getPid(), new AacFrameAnalyzer()); break; case AUDIO_MPEG1: analyzerMap.put((int)elementaryField.getPid(), new Mp3FrameAnalyzer()); break; case VIDEO_H264: analyzerMap.put((int)elementaryField.getPid(), new NalAnalyzer()); break; default: break; } } return pmt; } else if(pmt != null && pmt.isPesPid(pid.get())) { Pes pes = new Pes(syncByte, transportErrorIndicator, payloadUnitStartIndicator, transportPriority, pid, scramblingControl, adaptationFieldExist, payloadFieldExist, continuityCounter, pmt.getPcrPid() == pid.get()); if(payloadUnitStartIndicator.get() == 1) { // if unit is start again. commit the previous one. pesMap.put(pid.get(), pes); pes.setFrameAnalyzer(analyzerMap.get(pid.get())); } else { pes.setUnitStartPes(pesMap.get(pid.get())); } packet = pes; } else { // in the case of unknown data (include pes before pmt analysis.) logger.info("other data." + Integer.toHexString(pid.get())); // channel.position(channel.position() + 184); // I wanna send NullContainer to keep analyzing, however, this cause missing of unitStartPes for some pes data. // cause, pat or pmt on mpegts can be insert in pes frame. // return NullContainer.getInstance(); return null; } packet.minimumLoad(channel); return packet; } }