/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under The MIT license.
*/
package com.ttProject.media.h264;
import java.nio.ByteBuffer;
import com.ttProject.media.IAnalyzer;
import com.ttProject.media.IVideoData;
import com.ttProject.media.Unit;
import com.ttProject.media.extra.Bit1;
import com.ttProject.media.extra.Bit2;
import com.ttProject.media.extra.Bit5;
import com.ttProject.media.extra.BitConnector;
import com.ttProject.media.h264.frame.SequenceParameterSet;
import com.ttProject.nio.channels.IReadChannel;
import com.ttProject.util.BufferUtil;
/**
* nalの基本構造
* @see http://r2d2n3po.tistory.com/26
* @see http://www.itu.int/rec/T-REC-H.264-201304-I/en
* 0 1 2 3
* 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F|NRI| Type |R|I| PRID |N| DID | QID | TID |U|D|O| RR|
mshの部分は次のようになっているらしい。
01:avcC version1
spsのindex 1,2,3の部分(profile, compatibilityFlg, levelがはいっている)
ff
e1:spsの数値?
xx xx spsの長さ
spsの実データ
01:ppsの数値?
xx xx ppsの長さ
ppsの実データ
その後のflvタグは次のようになっているみたい。
09 size[3byte] timestamp[4byte(転地あり)] trackId[3byte(0埋め)] コーデックタイプとフレームフラグ(1byte)
mshFlag(00:msh 01:通常フレーム) avcCompositionTimeOffset[3byte] nalの内部データサイズ[4バイト] nalの実データ
tailsize[4byte]
となっている模様
* @author taktod
*/
public abstract class Frame extends Unit implements IVideoData {
private Bit1 forbiddenZeroBit; // 0のみ?
private Bit2 nalRefIdc; // 0:ならなくてもいいやつ?数値のあるやつはdecodeに必須なnalなお0x09のadtはmpegtsには必要っぽい。
private Bit5 type; // typeで宣言している数値がはいるっぽい
private ByteBuffer buffer; // データ本体保持
private SequenceParameterSet sps = null;
public Frame(final int size, byte frameTypeData) {
super(0, size);
forbiddenZeroBit = new Bit1(frameTypeData >>> 7);
nalRefIdc = new Bit2(frameTypeData >>> 5);
type = new Bit5(frameTypeData);
}
public void setSps(SequenceParameterSet sps) {
this.sps = sps;
}
// たぶんつかわん
public int getForbiddenZeroBit() {
return forbiddenZeroBit.get();
}
public int getNalRefIdc() {
return nalRefIdc.get();
}
public int getType() {
return type.get();
}
public ByteBuffer getBuffer() {
return buffer;
}
protected void setBuffer(ByteBuffer buffer) {
this.buffer = buffer.duplicate();
}
public ByteBuffer getData() throws Exception {
ByteBuffer data = ByteBuffer.allocate(buffer.remaining() + 1);
BitConnector bitConnector = new BitConnector();
data.put(bitConnector.connect(forbiddenZeroBit, nalRefIdc, type));
data.put(buffer);
buffer.position(0);
data.flip();
return data;
}
@Override
public void analyze(IReadChannel ch, IAnalyzer<?> analyzer)
throws Exception {
if(analyzer == null){
analyze(ch, (IFrameAnalyzer)null);
}
}
public void analyze(IReadChannel ch, IFrameAnalyzer analyzer) throws Exception {
// 設定されている分だけデータを読み込む
if(getSize() <= 1) {
throw new Exception("解析実行する前にデータサイズが設定されていません。");
}
if(ch.size() - ch.position() < getSize() - 1) {
throw new Exception("読み込みに必要なデータがありません。");
}
buffer = BufferUtil.safeRead(ch, getSize() - 1);
}
// videoData用の拡張動作
private long pts = 0;
private long dts = 0;
private double timebase = 0.001;
public void setPts(long pts) {
this.pts = pts;
}
public long getPts() {
return pts;
}
public void setDts(long dts) {
this.dts = dts;
}
public long getDts() {
return dts;
}
public void setTimebase(double timebase) {
this.timebase = timebase;
}
public double getTimebase() {
return timebase;
}
public ByteBuffer getRawData() throws Exception {
return getData();
}
@Override
public int getHeight() {
if(sps != null) {
return sps.getHeight();
}
return -1;
}
@Override
public int getWidth() {
if(sps != null) {
return sps.getWidth();
}
return -1;
}
@Override
public String toString() {
StringBuilder data = new StringBuilder();
data.append(getClass().getSimpleName());
data.append(" width:").append(getWidth());
data.append(" height:").append(getHeight());
return data.toString();
}
}