/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.chunk.mpegts.analyzer; import java.nio.ByteBuffer; import org.apache.log4j.Logger; import com.ttProject.chunk.mpegts.AudioDataList; import com.ttProject.chunk.mpegts.VideoDataList; import com.ttProject.media.Unit; import com.ttProject.media.h264.frame.AccessUnitDelimiter; import com.ttProject.media.h264.frame.PictureParameterSet; import com.ttProject.media.h264.frame.SequenceParameterSet; import com.ttProject.media.h264.frame.Slice; import com.ttProject.media.h264.frame.SliceIDR; import com.ttProject.media.mpegts.CodecType; import com.ttProject.media.mpegts.field.PmtElementaryField; import com.ttProject.media.mpegts.packet.Pes; import com.ttProject.media.mpegts.packet.Pmt; /** * h264のframeを解析してPesを作成します。 * IAudioDataはスルー * 強制pcrとなります。 * @author taktod */ public class H264PesAnalyzer implements IPesAnalyzer { /** 動作ロガー */ private Logger logger = Logger.getLogger(H264PesAnalyzer.class); /** 動作pmt(pesをつくるときに、pcr判定やpid等が必要になります。) */ private Pmt pmt = null; /** 動作pid */ private short pid; /** 分岐用のaccessUnitDelimiter */ private AccessUnitDelimiter aud = new AccessUnitDelimiter(); /** sps */ private SequenceParameterSet sps = null; /** pps */ private PictureParameterSet pps = null; /** pesデータ投入先 */ private VideoDataList videoDataList = null; /** * データの解析を実行します。 */ @Override public void analyze(Unit unit, long timestamp) { if(unit instanceof Pmt) { if(pmt != null) { // すでにpmt解析済みなら捨てます。 return; } try { pmt = (Pmt)unit; for(PmtElementaryField field : pmt.getFields()) { if(field.getCodecType() != CodecType.VIDEO_H264) { continue; } // 問題のトラックをみつけた。 pid = field.getPid(); break; } } catch(Exception e) { logger.error("pmt解析時に例外発生", e); } } else { try { if(unit instanceof Slice) { // innerFrame Slice slice = (Slice) unit; ByteBuffer audData = aud.getData(); ByteBuffer sliceData = slice.getData(); ByteBuffer buffer = ByteBuffer.allocate( 4 + audData.remaining() + 4 + sliceData.remaining()); buffer.putInt(1); buffer.put(audData); buffer.putInt(1); buffer.put(sliceData); buffer.flip(); addByteBuffer(buffer, true, timestamp); } else if(unit instanceof SliceIDR) { // keyFrame SliceIDR sliceIDR = (SliceIDR)unit; ByteBuffer audData = aud.getData(); ByteBuffer spsData = sps.getData(); ByteBuffer ppsData = pps.getData(); ByteBuffer sliceIDRData = sliceIDR.getData(); ByteBuffer buffer = ByteBuffer.allocate( 4 + audData.remaining() + 4 + spsData.remaining() + 4 + ppsData.remaining() + 4 + sliceIDRData.remaining()); buffer.putInt(1); buffer.put(audData); buffer.putInt(1); buffer.put(spsData); buffer.putInt(1); buffer.put(ppsData); buffer.putInt(1); buffer.put(sliceIDRData); buffer.flip(); addByteBuffer(buffer, true, timestamp); } else if(unit instanceof SequenceParameterSet) { sps = (SequenceParameterSet) unit; } else if(unit instanceof PictureParameterSet) { pps = (PictureParameterSet) unit; } } catch(Exception e) { logger.error("h264 nal解析中に例外が発生", e); } } } /** * bufferをpesに分解して登録する動作 * @param buffer * @param keyFrame * @param startPts * @throws Exception */ private void addByteBuffer(ByteBuffer buffer, boolean keyFrame, long startPts) throws Exception { Pes videoPes = new Pes(CodecType.VIDEO_H264, keyFrame, keyFrame, pid, buffer, startPts); do { // 空bufferの取得を実施して、内部のbyfferデータポインターを移動させる。 videoPes.getBuffer(); videoDataList.addPes(videoPes); } while((videoPes = videoPes.nextPes()) != null); } /** * audioDataListを登録 */ @Override public void setAudioDataList(AudioDataList audioDataList) { } /** * videoDataListを登録 */ @Override public void setVideoDataList(VideoDataList videoDataList) { this.videoDataList = videoDataList; } }