/* * 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.io.FileOutputStream; import java.nio.channels.WritableByteChannel; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.apache.log4j.Logger; import com.ttProject.container.IContainer; import com.ttProject.container.IWriter; 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.CodecType; import com.ttProject.frame.IAudioFrame; import com.ttProject.frame.IFrame; import com.ttProject.frame.IVideoFrame; import com.ttProject.frame.extra.AudioMultiFrame; import com.ttProject.frame.extra.VideoMultiFrame; /** * mpegts packet writer. * hold the sdt pat pmt. * these three type of packet will be written with keyFrame or (non video data) fixed time interval. * * think about 2 type. audioOnly and audio + video. * audioOnly -> 1sec for each pes. * videoOnly -> each frame is each pes. * need to setup invidually. * @author taktod */ public class MpegtsPacketWriter implements IWriter { /** logger */ private Logger logger = Logger.getLogger(MpegtsPacketWriter.class); private final WritableByteChannel outputChannel; private FileOutputStream outputStream = null; /** continuityCounterMap */ private Map<Integer, Integer> continuityCounterMap = new HashMap<Integer, Integer>(); /** sdt */ private Sdt sdt = null; /** pat */ private Pat pat = null; /** pmt */ private Pmt pmt = null; /** make pes from frame. */ private FrameToPesConverter converter = new FrameToPesConverter(); /** flag for written first metadata. */ private boolean isWriteFirstMeta = false; /** * constructor * @param fileName * @throws Exception */ public MpegtsPacketWriter(String fileName) throws Exception { outputStream = new FileOutputStream(fileName); this.outputChannel = outputStream.getChannel(); } public MpegtsPacketWriter(FileOutputStream fileOutputStream) { this(fileOutputStream.getChannel()); } public MpegtsPacketWriter(WritableByteChannel outputChannel) { this.outputChannel = outputChannel; } @Override public void addContainer(IContainer container) throws Exception { if(container instanceof Sdt) { sdt = (Sdt)container; } else if(container instanceof Pat) { pat = (Pat)container; } else if(container instanceof Pmt) { pmt = (Pmt)container; } } @Override public void addFrame(int trackId, IFrame frame) throws Exception { if(frame == null) { return; } if(frame instanceof VideoMultiFrame) { VideoMultiFrame multiFrame = (VideoMultiFrame) frame; for(IVideoFrame vFrame : multiFrame.getFrameList()) { addFrame(trackId, vFrame); } return; } if(frame instanceof AudioMultiFrame) { AudioMultiFrame multiFrame = (AudioMultiFrame) frame; for(IAudioFrame aFrame : multiFrame.getFrameList()) { addFrame(trackId, aFrame); } return; } if(!isWriteFirstMeta) { // first written, write sdt pat pmt. if(sdt == null || pat == null || pmt == null) { throw new Exception("sdt pat pmt is expected for writing."); } writeMpegtsPacket(sdt); writeMpegtsPacket(pat); writeMpegtsPacket(pmt); isWriteFirstMeta = true; } Pes pes = converter.getPeses(trackId, pmt, frame); if(pes == null) { return; } writeMpegtsPacket(pes); } private void writeMpegtsPacket(MpegtsPacket packet) throws Exception { Integer counter = continuityCounterMap.get(packet.getPid()); if(counter == null) { counter = 0; } packet.setContinuityCounter(counter); outputChannel.write(packet.getData()); continuityCounterMap.put(packet.getPid(), packet.getContinuityCounter() + 1); } @Override public void prepareHeader(CodecType ...codecs) throws Exception { /* * try to make the tracks and keep the trackId -> codecType maps * after, cause of the trackId from previous container. * pre trackId -> trackId * if no information for pre trackId, find the same codec track. * and assign the id. * ここでcodecType -> pidリストをつくっておく * フレームアクセスがあったら * すでにtrackId -> pidのマップがあるならそれを利用 * trackIdに対応したpidがわからないなら * pidを調べる * 入力フレームと同じcodecTypeのデータを探して一致するものが・・・ * この方法だと同じコーデックのトラックが複数ある場合は動作できないか・・・ * * 逆にpid -> codecTypeをいれておいて、 * あたらしいフレームをみつけたら、前からpidを確認していって * 該当のpidをみつける形にしておこう。 */ } @Override public void prepareTailer() throws Exception { Map<Integer, Pes> remainMap = converter.getPesMap(); // remained all pes will be written. for(Entry<Integer, Pes> entry : remainMap.entrySet()) { Pes pes = entry.getValue(); try { writeMpegtsPacket(pes); } catch(Exception e) { logger.error(e); } } if(outputStream != null) { try { outputStream.close(); } catch(Exception e) { } outputStream = null; } } }