/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under The MIT license.
*/
package com.ttProject.container.flv.type;
import java.nio.ByteBuffer;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.log4j.Logger;
import com.ttProject.container.flv.FlvTag;
import com.ttProject.container.flv.amf.Amf0Value;
import com.ttProject.nio.channels.ByteReadChannel;
import com.ttProject.nio.channels.IReadChannel;
import com.ttProject.unit.extra.bit.Bit8;
import com.ttProject.util.BufferUtil;
/**
* metaTag
* 12 xx xx xx tt tt tt tt 00 00 00 [AMF0(onMetaData(string))] [AMF0ObjectMapData] xx xx xx xx
*
* TODO このタグは、前のプログラムでは動作が微妙だったので・・・(sizeがふらふらかわって使いにくい)
* 今回はもっとしっかり動作するようにしたいところ。
* たしかデータを追加すると、サイズがかわってしまって、タイミングによっては正しいデータがとれないとかあったはず。
* データとして、mapの形し、書き込み時に復元するとデータサイズがかわってしまうのが、難点だった・・・
* さて、どうするかね・・・
* データが追加されたら、bufferの中身を書き換えるみたいな動作が一番いいかもしれないね。
* @author taktod
*/
public class MetaTag extends FlvTag {
/** logger */
@SuppressWarnings("unused")
private Logger logger = Logger.getLogger(MetaTag.class);
/** string(fixed) */
private final String title = "onMetaData";
/** metaData */
private final Map<String, Object> data = new LinkedHashMap<String, Object>();
/** correspond buffer. */
private ByteBuffer rawBuffer = null;
/**
* constructor
* @param tagType
*/
public MetaTag(Bit8 tagType) {
super(tagType);
}
/**
* constructor
*/
public MetaTag() {
this(new Bit8(0x12));
}
/**
* {@inheritDoc}
*/
@Override
public void load(IReadChannel channel) throws Exception {
channel.position(getPosition() + 11);
// load the all Data on rawBuffer first.
rawBuffer = BufferUtil.safeRead(channel, getSize() - 15);
// analyzeData.
IReadChannel bufferChannel = new ByteReadChannel(rawBuffer.duplicate());
String tag = (String)Amf0Value.getValueObject(bufferChannel);
if(!title.equals(tag)) {
throw new Exception("start code is not onMetaData");
}
// check the data.
while(bufferChannel.position() < bufferChannel.size()) {
Object data = Amf0Value.getValueObject(bufferChannel);
if(!(data instanceof Map<?, ?>)) {
throw new Exception("data is not described by map. unexpected data.");
}
@SuppressWarnings("unchecked")
Map<String, Object> object = (Map<String, Object>) data;
for(Entry<String, Object> entry : object.entrySet()) {
this.data.put(entry.getKey(), entry.getValue());
}
}
// check prevTagSize.
if(getPrevTagSize() != BufferUtil.safeRead(channel, 4).getInt()) {
throw new Exception("size data is corrupted.");
}
}
/**
* {@inheritDoc}
*/
@Override
public void minimumLoad(IReadChannel channel) throws Exception {
super.minimumLoad(channel);
}
/**
* {@inheritDoc}
*/
@Override
protected void requestUpdate() throws Exception {
if(rawBuffer == null) {
throw new Exception("rawBuffer is undefined.");
}
ByteBuffer startBuffer = getStartBuffer();
ByteBuffer tailBuffer = getTailBuffer();
// rawBuffer is ref only once(if changed, make rawBuffer again.)
setData(BufferUtil.connect(
startBuffer,
rawBuffer,
tailBuffer
));
}
/**
* add data.
* @param key
* @param value
* @throws Exception
*/
public void put(String key, Object value) throws Exception {
data.put(key, value);
updateData();
}
/**
* remove data
* @param key
* @throws Exception
*/
public void remove(String key) throws Exception {
data.remove(key);
updateData();
}
/**
* data and size are changed by metaData update.
* the position of all flvTag is changed. so, position of file is nonsence now.
* @throws Exception
*/
private void updateData() throws Exception {
rawBuffer = BufferUtil.connect(
Amf0Value.getValueBuffer(title),
Amf0Value.getValueBuffer(data));
setSize(rawBuffer.remaining() + 15);
update();
}
/**
* ref the data.
* @param key
* @return
*/
public Object get(String key) {
return data.get(key);
}
}