/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.media.flv.tag; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import com.ttProject.media.flv.Tag; import com.ttProject.media.flv.amf.Amf0Value; import com.ttProject.nio.channels.IReadChannel; import com.ttProject.util.BufferUtil; /** * medaデータ * 12 xx xx xx tt tt tt tt 00 00 00 [AMF0でonMetaData(文字列)] [AMF0でObjectMapデータ] xx xx xx xx * xxの部分はサイズ、先頭のサイズと終端のサイズと2つある。 * ttの部分はtimestamp * * @author taktod */ public class MetaTag extends Tag { /** メタデータの基本文字列 */ private final String title = "onMetaData"; /** メタデータの中身 */ private final Map<String, Object> data = new LinkedHashMap<String, Object>(); /** メタデータの実データ */ private ByteBuffer rawData = null; /** * データの設定 * @param key * @param data */ public void putData(String key, Object data) { rawData = null; // 追加したら、すでに計算済みのrawDataを破棄して、あたらしいデータを追加しておきます。 this.data.put(key, data); } /** * データの削除 * @param key */ public void removeData(String key) { rawData = null; this.data.remove(key); } /** * データの参照 * @param key * @return */ public Object getData(String key) { return data.get(key); } /** * コンストラクタ(メモリーからつくられる場合) */ public MetaTag() { super(); } /** * コンストラクタ(ファイルベースの場合・・・だがMetaTagとしては、デフォルトでデータを読み込んでdataをつくっておきたいところ・・・) * @param size * @param position * @param timestamp */ public MetaTag(final int position, final int size, final int timestamp) { super(position, size, timestamp); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public void analyze(IReadChannel ch, boolean atBegin) throws Exception { super.analyze(ch, atBegin); // 実データを読み込む ch.position(getPosition() + 11); // データを読み込む String tag = (String)Amf0Value.getValueObject(ch); if(!"onMetaData".equals(tag)) { throw new Exception("先頭がonMetaDataになっていませんでした。"); } while(ch.position() < getPosition() + super.getSize() - 4) { // tailの部分があるので4バイト引いておく。 Object data = Amf0Value.getValueObject(ch); if(!(data instanceof Map<?, ?>)) { throw new Exception("内部データの構成がMapではありませんでした。"); } Map<String, Object> object = (Map<String, Object>)data; for(Entry<String, Object> entry : object.entrySet()) { this.data.put(entry.getKey(), entry.getValue()); } } // tailについて確認しておく。 if(BufferUtil.safeRead(ch, 4).getInt() != super.getSize() - 4) { throw new Exception("tailByteの長さが狂ってます"); } rawData = null; } /** * {@inheritDoc} */ @Override public void writeTag(WritableByteChannel target) throws Exception { if(rawData == null) { getSize(); } // 頭の11バイト書き込み target.write(getHeaderBuffer((byte)0x12)); // 実データ部書き込みdata // 内容を書き込む rawData.position(0); target.write(rawData); target.write(getTailBuffer()); } /** * {@inheritDoc} */ @Override public int getSize() { // 実データbufferを作成して計算しなおしておく。 if(rawData == null) { try { // onMetaDataの文字列の部分 ByteBuffer titleBuffer = Amf0Value.getValueBuffer(title); ByteBuffer dataBuffer = Amf0Value.getValueBuffer(data); rawData = ByteBuffer.allocate(titleBuffer.remaining() + dataBuffer.remaining()); rawData.put(titleBuffer); rawData.put(dataBuffer); rawData.flip(); super.setSize(rawData.remaining() + 15); } catch (Exception e) { throw new RuntimeException("不明な例外が発生しました。"); } } return super.getSize(); } /** * {@inheritDoc} */ @Override public ByteBuffer getBuffer() throws Exception { // 全体のデータサイズを知っておく必要がある。 ByteBuffer buffer = ByteBuffer.allocate(getSize()); buffer.put(getHeaderBuffer((byte)0x12)); rawData.position(0); buffer.put(rawData); buffer.put(getTailBuffer()); buffer.flip(); return buffer; } /** * {@inheritDoc} */ @Override public String toString() { return "metaTag ts:" + getTimestamp() + " pos:" + getPosition() + " sz:" + getSize(); } }