/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under The MIT license.
*/
package com.ttProject.container.ogg.type;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;
import com.ttProject.container.ogg.Crc32;
import com.ttProject.container.ogg.OggPage;
import com.ttProject.frame.AudioFrame;
import com.ttProject.frame.IFrame;
import com.ttProject.nio.channels.ByteReadChannel;
import com.ttProject.nio.channels.IReadChannel;
import com.ttProject.unit.extra.bit.Bit1;
import com.ttProject.unit.extra.bit.Bit5;
import com.ttProject.unit.extra.bit.Bit8;
import com.ttProject.util.BufferUtil;
/**
* basic unit of ogg, "page"
* @see http://www.xiph.org/vorbis/doc/framing.html
* @see http://ja.wikipedia.org/wiki/Ogg%E3%83%9A%E3%83%BC%E3%82%B8
*
* 内容は次のような感じ
* pageの開始
* 4バイト:OggS
* 1バイト:stream_structure_version (現在は0x00のみ)
* 1バイト:bitFlag 0000 0abc c:フラグが立っていたら続きpacket b:フラグがたっていたらロジックストリームの開始のページ a:フラグがたっていたらロジックストリームの最後のページ
*
* 8バイト:absoluteGranulePosition 位置情報(含有物次第の値らしい)
* 4バイト:streamSerialNumber とりあえずなにがしの番号
* 4バイト:pageSequenceNo ページの番号 mpegtsのcounterみたいなもんかな
* 4バイト:pageChecksum headerから導くCRC値らしい
* 1バイト:ページが保持するsegmentsの数
* 以下セグメントデータ
* 1バイト:セグメントサイズ(Nとする) ←segmentsの数だけならぶ
* Nバイト:セグメント実体 ←segmentsの数だけならぶ
* みたいな感じになってる。
*
* 以下これの繰り返しっぽい。
* avconvでつくったoggデータの確認しつつやってみた結果。
* どこか正しくないことがあっても怒らないこと。
*
* @author taktod
*/
public class Page extends OggPage {
/** logger */
// private Logger logger = Logger.getLogger(Page.class);
/**
* constructor
* @param version
* @param zeroFill
* @param logicEndFlag
* @param logicStartFlag
* @param packetContinurousFlag
*/
public Page(Bit8 version,
Bit1 packetContinurousFlag,
Bit1 logicStartFlag,
Bit1 logicEndFlag,
Bit5 zeroFill) {
super(version, packetContinurousFlag, logicStartFlag, logicEndFlag, zeroFill);
}
/**
* {@inheritDoc}
*/
@Override
public void minimumLoad(IReadChannel channel) throws Exception {
super.minimumLoad(channel);
super.update();
}
/**
* {@inheritDoc}
*/
@Override
public void load(IReadChannel channel) throws Exception {
channel.position(getPosition() + 27 + getSegmentSizeList().size());
List<IFrame> frameList = getFrameList();
int targetSize = 0;
for(Bit8 size : getSegmentSizeList()) {
if(size.get() == 0xFF) {
targetSize += 0xFF;
continue;
}
targetSize += size.get();
ByteBuffer buffer = BufferUtil.safeRead(channel, targetSize);
targetSize = 0;
// 解析したい。
IReadChannel bufferChannel = new ByteReadChannel(buffer);
// TODO for other container, load is not the timing for making frame.
// should I obey them?
IFrame frame = (IFrame)getStartPage().getAnalyzer().analyze(bufferChannel);
if(frame instanceof AudioFrame) {
AudioFrame audioFrame = (AudioFrame) frame;
audioFrame.setTimebase(audioFrame.getSampleRate());
audioFrame.setPts(getStartPage().getPassedTic());
getStartPage().setPassedTic(audioFrame.getPts() + audioFrame.getSampleNum());
}
// check frame hashCode, if same, just egnore.(already wrote.)
if(frameList.size() == 0 || frameList.get(frameList.size() - 1).hashCode() != frame.hashCode()) {
frameList.add(frame);
}
}
channel.position(getPosition() + getSize());
}
/**
* {@inheritDoc}
*/
@Override
protected void requestUpdate() throws Exception {
ByteBuffer headerBuffer = getHeaderBuffer();
ByteBuffer buffer = ByteBuffer.allocate(getSize());
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.put(headerBuffer);
for(IFrame frame : getFrameList()) {
ByteBuffer data = frame.getData();
buffer.put(data);
}
ByteBuffer tmpBuffer = buffer.duplicate();
tmpBuffer.flip();
// make crc32
Crc32 crc32 = new Crc32();
while(tmpBuffer.remaining() > 0) {
crc32.update(tmpBuffer.get());
}
// write crc32
buffer.position(22);
buffer.putInt((int)crc32.getValue());
buffer.position(tmpBuffer.position());
buffer.flip();
setData(buffer);
}
}