/* * 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.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.apache.log4j.Logger; import com.ttProject.media.flv.tag.AudioTag; import com.ttProject.media.flv.tag.VideoTag; /** * Flvのtagのソートをおこなう * getTagsにデータをいれると、見合ったデータが返ってくる * audioとvideoのデータがflipしていてもいいが、audioだけ or videoだけでflipしていると扱えないものとする。 * @author taktod */ public class FlvTagOrderManager { /** ロガー */ private static final Logger logger = Logger.getLogger(FlvTagOrderManager.class); /** 処理待ちaudioTagリスト */ private final List<Tag> audioTags = new ArrayList<Tag>(); /** 処理待ちvideoTagリスト */ private final List<Tag> videoTags = new ArrayList<Tag>(); /** 各メディアトラックがもうなくなったかのフラグ */ private boolean videoEndFlg = false; private boolean audioEndFlg = false; /** ソート用の比較オブジェクト */ private final TagComparator tagSort = new TagComparator(); /** 比較に用いるindex値を設定、設定数のタグは無条件で保持になります */ private final int videoCompIndex = 5; // TODO ここでのaudioの完了までの判定indexが大きすぎて、音がないデータになった場合にこまったことになる。 private final int audioCompIndex = 20; // ちょっと大きいけど、まぁconvertの遅延もあるし、いいだろw /** * 全初期化する。 */ public void reset() { audioTags.clear(); videoTags.clear(); audioEndFlg = false; videoEndFlg = false; } /** * tagを追加する。 * @return */ public synchronized void addTag(Tag tag) throws Exception { // 対象のタグをaudioTagsとvideoTagsに登録する。 if(tag instanceof AudioTag) { if(audioTags.size() == 0) { audioTags.add(tag); } else { AudioTag aTag = (AudioTag) tag; Tag prevTag = audioTags.get(audioTags.size() - 1); if(prevTag.getTimestamp() > tag.getTimestamp()) { logger.warn("音声データのflipがありました。codec:" + aTag.getCodec() + "prev:" + prevTag.getTimestamp() + ", current:" + tag.getTimestamp()); if(audioTags.size() > audioCompIndex) { prevTag = audioTags.get(audioTags.size() - audioCompIndex); if(prevTag.getTimestamp() > tag.getTimestamp()) { logger.warn("再チャレンジ音声データのflipがありました。prev:" + prevTag.getTimestamp() + ", current:" + tag.getTimestamp()); return; } } } audioTags.add(tag); Collections.sort(audioTags, tagSort); } } else if(tag instanceof VideoTag) { if(videoTags.size() == 0) { videoTags.add(tag); } else { Tag prevTag = videoTags.get(videoTags.size() - 1); if(prevTag.getTimestamp() > tag.getTimestamp()) { logger.warn("映像データのflipがありました。"); if(videoTags.size() > videoCompIndex) { prevTag = videoTags.get(videoTags.size() - videoCompIndex); if(prevTag.getTimestamp() > tag.getTimestamp()) { logger.warn("再チャレンジ映像データのflipがありました。"); return; } } } videoTags.add(tag); Collections.sort(videoTags, tagSort); } } } /** * tagを一括して追加する * @param tags * @throws Exception */ public void addTags(List<Tag> tags) throws Exception { for(Tag tag : tags) { addTag(tag); } } /** * 確定済みのタグを取り出す * @return */ public synchronized List<Tag> getCompleteTags() { List<Tag> result = new ArrayList<Tag>(); // データが定まったtagを応答します。 Tag videoTag = null; Tag audioTag = null; while(true) { // audioTagsとvideoTagsからデータを取り出して、順番に応答していきます。 if(videoTag == null) { if(videoTags.size() < videoCompIndex) { break; } videoTag = videoTags.get(0); } if(audioTag == null) { if(audioTags.size() < audioCompIndex) { break; } audioTag = audioTags.get(0); } if(videoTag.getTimestamp() <= audioTag.getTimestamp()) { // videoTagの方が有用 result.add(videoTags.remove(0)); videoTag = null; } else { result.add(audioTags.remove(0)); audioTag = null; } } if(audioEndFlg && audioTags.size() == 0) { result.addAll(videoTags); videoTags.clear(); } if(videoEndFlg && videoTags.size() == 0) { result.addAll(audioTags); audioTags.clear(); } return result; } /** * 映像データがもうないというフラグ設定 */ public void setNomoreVideo() { setVideoEndFlg(true); } public void setVideoEndFlg(boolean flg) { videoEndFlg = flg; } /** * 音声データがもうないというフラグ設定 */ public void setNomoreAudio() { setAudioEndFlg(true); } public void setAudioEndFlg(boolean flg) { audioEndFlg = flg; } /** * {@inheritDoc} */ @Override public String toString() { return "manager:\n" + audioTags.toString() + "\n" + videoTags.toString(); } private class TagComparator implements Comparator<Tag> { @Override public int compare(Tag t1, Tag t2) { return t1.getTimestamp() - t2.getTimestamp(); } } }