/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under The MIT license.
*/
package com.ttProject.media.mpegts;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import com.ttProject.media.Manager;
import com.ttProject.media.mpegts.field.PmtElementaryField;
import com.ttProject.media.mpegts.packet.Pes;
import com.ttProject.media.mpegts.packet.Pat;
import com.ttProject.media.mpegts.packet.Pmt;
import com.ttProject.media.mpegts.packet.Sdt;
import com.ttProject.nio.channels.ByteReadChannel;
import com.ttProject.nio.channels.IReadChannel;
import com.ttProject.util.BufferUtil;
/**
* mpegtsの内部データの解析を実行します。
* patからpmtがわかる。
* pmtからesがわかる。(コーデック情報もわかる)
* といった感じになっている
* @author taktod
*/
public class MpegtsManager extends Manager<Packet> {
private Logger logger = Logger.getLogger(MpegtsManager.class);
/** PatID(テーブルの基本情報) */
private final int patId = 0x0000;
/** SdtID(情報) */
private final int sdtId = 0x0011;
/** PmtID(Patによる) */
private short pmtId;
/** pcrとして、指定されているPID */
private int pcrPid;
/** elementStreamのデータ pic -> CodecTypeとしてある */
private Map<Integer, CodecType> esMap = new HashMap<Integer, CodecType>();
/**
* {@inheritDoc}
*/
@Override
public List<Packet> getUnits(ByteBuffer data) throws Exception {
ByteBuffer buffer = appendBuffer(data);
if(buffer == null) {
return null;
}
IReadChannel bufferChannel = new ByteReadChannel(buffer);
List<Packet> result = new ArrayList<Packet>();
while(true) {
int position = bufferChannel.position();
Packet packet = getUnit(bufferChannel);
if(packet == null) {
buffer.position(position);
break;
}
result.add(packet);
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public Packet getUnit(IReadChannel source) throws Exception {
// 188バイトデータがあるか確認する。
if(source.size() - source.position() < 188) {
// 188バイト未満なのでmpegtsのpacketとして成立しない。
return null;
}
int position = source.position();
ByteBuffer buffer = BufferUtil.safeRead(source, 188);
// 先頭が0x47になっているか確認する。
if(buffer.get() != 0x47) {
throw new Exception("先頭が0x47になっていませんでした。mpegtsとして成立していません。");
}
int pid = getPid(buffer); // 2バイト読み込んである
// pidによってなんのデータであるか分岐させておく必要がある
buffer.position(0);
if(pid == patId) {
Pat pat = new Pat(position, buffer);
// patを解析して、pmtを知る必要あり
// pat.analyze(source);
pmtId = pat.getProgramPid();
return pat;
}
else if(pid == sdtId) {
Sdt sdt = new Sdt(position, buffer);
// 外でほしかったら勝手にanalyzeすればいいと思う
// sdt.analyze(source);
return sdt;
}
else if(pmtId == pid) {
Pmt pmt = new Pmt(position, buffer);
// pmtを解析して、esを知る必要あり。
// pmt.analyze(source);
// pcr(時間情報を保持しているesのpid)
pcrPid = pmt.getPcrPid();
for(PmtElementaryField field : pmt.getFields()) {
esMap.put((int)field.getPid(), field.getCodecType());
}
return pmt;
}
else if(esMap.containsKey(pid)) {
// メディアデータ
Pes pes = new Pes(position, buffer, esMap.get(pid), pid == pcrPid);
// pes.analyze(source);
return pes;
}
else {
// しらないデータ、これがきた場合は、なんとかしておかないとだめ
logger.warn("unknownデータ");
logger.warn(pid);
return null;
}
}
/**
* pidを計算して出す
* @param buffer
* @return
*/
private int getPid(ByteBuffer buffer) {
return buffer.getShort() & 0x1FFF;
}
}