/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under The MIT license.
*/
package com.ttProject.media.flv;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import com.ttProject.media.Manager;
import com.ttProject.media.flv.tag.AudioTag;
import com.ttProject.media.flv.tag.MetaTag;
import com.ttProject.media.flv.tag.VideoTag;
import com.ttProject.nio.channels.ByteReadChannel;
import com.ttProject.nio.channels.IReadChannel;
import com.ttProject.util.BufferUtil;
/**
* flvデータを管理します
* @author taktod
*/
public class FlvManager extends Manager<Tag> {
/** データタイプ */
private enum Type {
Audio(0x08),
Video(0x09),
Meta(0x12),
Unknown(-1);
private final int value;
private Type(int value) {
this.value = value;
}
public int intValue() {
return value;
}
public static Type getType(int value) throws Exception {
for(Type t : values()) {
if(t.intValue() == value) {
return t;
}
}
throw new Exception("解析不能なタグデータを受けとりました。" + Integer.toHexString(value));
}
};
/**
* {@inheritDoc}
*/
@Override
public List<Tag> getUnits(ByteBuffer data) throws Exception {
ByteBuffer buffer = appendBuffer(data);
if(buffer == null) {
return null;
}
IReadChannel bufferChannel = new ByteReadChannel(buffer);
List<Tag> result = new ArrayList<Tag>();
while(true) {
int position = bufferChannel.position();
Tag tag = getUnit(bufferChannel);
// tagを読み込むのに必要なデータ量があるか確認しておく。
if(tag == null) {
// データが足りない
buffer.position(position);
break;
}
tag.analyze(bufferChannel, false);
result.add(tag);
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public Tag getUnit(IReadChannel source) throws Exception {
// データ量が11バイト以上ないと解析不能
if(source.size() - source.position() < 11) {
return null;
}
int position = source.position();
ByteBuffer buffer = BufferUtil.safeRead(source, 11);
Type type = Type.getType(buffer.get());
int innerSize = analyzeInnerSize(buffer);
int timestamp = analyzeTimestamp(buffer);
@SuppressWarnings("unused")
int trackId = analyzeTrackId(buffer);
if(source.size() - source.position() < innerSize + 4) {
// タグの終端まで必要なデータがない場合
source.position(position);
return null;
}
switch(type) {
case Audio:
return new AudioTag(position, innerSize + 15, timestamp);
case Video:
return new VideoTag(position, innerSize + 15, timestamp);
case Meta:
return new MetaTag(position, innerSize + 15, timestamp);
default:
throw new RuntimeException("解析不能なflvデータタグを受けとりました。:" + type);
}
}
private int analyzeInnerSize(ByteBuffer buffer) {
return ((buffer.get() & 0xFF) << 16) + ((buffer.get() & 0xFF) << 8) + (buffer.get() & 0xFF);
}
private int analyzeTimestamp(ByteBuffer buffer) {
return ((buffer.get() & 0xFF) << 16) + ((buffer.get() & 0xFF) << 8) + (buffer.get() & 0xFF) + ((buffer.get() & 0xFF) << 24);
}
private int analyzeTrackId(ByteBuffer buffer) {
return ((buffer.get() & 0xFF) << 16) + ((buffer.get() & 0xFF) << 8) + (buffer.get() & 0xFF);
}
}