/* * 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.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.log4j.Logger; import com.ttProject.container.mpegts.field.PmtElementaryField; import com.ttProject.container.mpegts.type.Pes; import com.ttProject.container.mpegts.type.Pmt; import com.ttProject.frame.AudioFrame; import com.ttProject.frame.IAudioFrame; import com.ttProject.frame.IFrame; import com.ttProject.frame.VideoFrame; import com.ttProject.frame.h264.H264Frame; import com.ttProject.frame.h264.SliceFrame; /** * make pes from frame. * audioPes will be devided by set duration. * videoPes will be devided by keyFrame. * @author taktod */ public class FrameToPesConverter { /** logger */ private Logger logger = Logger.getLogger(FrameToPesConverter.class); /** processed Pes holder map */ private final Map<Integer, Pes> pesMap = new ConcurrentHashMap<Integer, Pes>(); /** duration for audioPes. */ private final float audioPesDuration; /** * constructor */ public FrameToPesConverter() { this(0.3f); } /** * constructor * @param audioDuration */ public FrameToPesConverter(float audioDuration) { audioPesDuration = audioDuration; } /** * make pes from frame. * @param pid * @param frame * @return * @throws Exception */ public Pes getPeses(int pid, Pmt pmt, IFrame frame) throws Exception { logger.info("add frame:" + frame); if(frame instanceof VideoFrame) { return getVideoPes(pid, pmt, (VideoFrame)frame); } else if(frame instanceof AudioFrame) { return getAudioPes(pid, pmt, (AudioFrame)frame); } throw new Exception("found neither video nor audio frame." + frame.toString()); } /** * make new pes. * @param pid * @param pmt * @return * @throws Exception */ private Pes makeNewPes(int pid, Pmt pmt) throws Exception { logger.info("make pes:" + Integer.toHexString(pid)); Pes pes = new Pes(pid, pmt.getPcrPid() == pid); for(PmtElementaryField peField : pmt.getFields()) { if(pid == peField.getPid()) { if(pes.getStreamId() != 0) { throw new Exception("unexpected stream Id, need to start from non-zero."); } else { pes.setStreamId(peField.getSuggestStreamId()); } break; } } pesMap.put(pid, pes); return pes; } /** * get pes. * @param pid * @param pmt * @return * @throws Exception */ private Pes getPes(int pid, Pmt pmt) throws Exception { Pes pes = pesMap.get(pid); if(pes == null) { pes = makeNewPes(pid, pmt); } return pes; } /** * getAudioPes * @param frame * @return * @throws Exception */ private Pes getAudioPes(int pid, Pmt pmt, AudioFrame frame) throws Exception { Pes pes = getPes(pid, pmt); pes.addFrame(frame); IAudioFrame audioFrame = (IAudioFrame)pes.getFrame(); // get duration from holding audioFrame sampleNum. if(1.0f * audioFrame.getSampleNum() / audioFrame.getSampleRate() > audioPesDuration) { // make new pes and register. makeNewPes(pid, pmt); return pes; } return null; } /** * getVideoPes * @param pid * @param pmt * @param frame * @return * @throws Exception */ private Pes getVideoPes(int pid, Pmt pmt, VideoFrame frame) throws Exception { if(frame instanceof H264Frame) { return getH264Pes(pid, pmt, (H264Frame)frame); } return null; } /** * getH264Pes * @param pid * @param pmt * @param frame * @return * @throws Exception */ private Pes getH264Pes(int pid, Pmt pmt, H264Frame frame) throws Exception { // for h264, deal with sliceFrame only. if(!(frame instanceof SliceFrame)) { return null; } Pes pes = pesMap.get(pid); // first data should be sliceIDR. pes = makeNewPes(pid, pmt); pes.addFrame(frame); // complete with only one slice frame. pesMap.remove(pid); // no more reuse, remove from map. return pes; } public Map<Integer, Pes> getPesMap() { return pesMap; } }