/**
* This file is part of jFlvTool.
*
* jFlvTool is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* jFlvTool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* file name : TagReader.java
* authors : Jon Keys
* created : June 28, 2007, 3:52 PM
* copyright : Sony Digital Authoring Services
*
* modifications:
* Date: Name: Description:
* ---------- --------------- ----------------------------------------------
* June 28, 2007 Jon Keys Creation
*/
package edu.washington.cs.oneswarm.ui.gwt.server.ffmpeg.jflv.metadata;
import java.util.ArrayList;
import java.util.HashMap;
import edu.washington.cs.oneswarm.ui.gwt.server.ffmpeg.jflv.io.FileWriter;
import edu.washington.cs.oneswarm.ui.gwt.server.ffmpeg.jflv.io.IOHelper;
import edu.washington.cs.oneswarm.ui.gwt.server.ffmpeg.jflv.parse.TagParser;
import edu.washington.cs.oneswarm.ui.gwt.server.ffmpeg.jflv.tags.*;
/**
*
* @author Jon Keys
*/
public class TagBroker {
private double frameRate;
private int startingByteOffset;
private TagStor stor;
private IOHelper ioh;
private FlvHeader flvh;
private ArrayList<TagStor> tags;
private TagParser tp;
/**
* Creates a new instance of TagBroker
*/
public TagBroker(IOHelper ioh, FlvHeader flvh) {
frameRate = 0;
stor = null;
this.startingByteOffset = flvh.getDataOffset() + 4;
tp = new TagParser(ioh);
tp.setTotalByteOffset(startingByteOffset);
tp.readTags();
tags = tp.getTags();
this.ioh = ioh;
this.flvh = flvh;
}
public long getDuration() {
long dur = 0;
if (flvh.hasVideo()) {
dur = tags.get(tp.getLastVideoTag()).getTimestamp();
} else if (flvh.hasAudio()) {
dur = tags.get(tp.getLastAudioTag()).getTimestamp();
} else {
dur = tags.get(tags.size() - 1).getTimestamp();
}
return dur;
}// getDuration()
public long getLastTabReadAttemptPos() {
return tp.getLastTabReadAttemptPos();
}
public void writeTags() {
FileWriter fw = ioh.getFileWriter(flvh);
fw.setDebug(ioh.isDebug());
fw.setTags(tags);
fw.setInStream(ioh.getFileReader().getStream());
flvh.setHasAudio((tp.getLastAudioTag() != -1));
flvh.setHasVideo((tp.getLastVideoTag() != -1));
fw.writeTags();
}
public void recalcKeyFrameTagOffsets() {
ArrayList<Double> keyFrameTagOffsets = new ArrayList<Double>();
long totalByteOffset = startingByteOffset;
int totMetaBytes = 0;
for (TagStor ts : tags) {
if (ts.getType() == FlvTag.VIDEO) {
if (((VideoTag) ts.getTag()).getFrameType() == VideoTag.KEYFRAME) {
keyFrameTagOffsets.add(new Double(totalByteOffset));
}
} else if (ts.getType() == FlvTag.META) {
if (ts.isNew()) {
totMetaBytes += ts.getDataSize() + 15;
} else {
totMetaBytes += ts.getDataSize();
}
}// else
totalByteOffset += ts.getDataSize() + 15;
}// for
tp.setKeyFrameTagOffsets(keyFrameTagOffsets);
tp.setTotalByteOffset(totalByteOffset);
tp.setTotalMetaSize(totMetaBytes);
}// recalcTagOffsets()
public void addTags(ArrayList<TagStor> tags2add, boolean keepFrameRate, boolean overwrite) {
frameRate = tp.getFrameseq().getFrameRate();
if (keepFrameRate && frameRate == 0) {
System.out
.println("Error adding new tags: current tag doesn't fit into existing framerate");
return;
} else if (keepFrameRate) {
for (TagStor ts : tags2add) {
if (ts.getTimestamp() % (1000 / frameRate) != 0) {
System.out
.println("Error adding new tags: current tag doesn't fit into existing framerate");
return;
}
}// for
}// else
int pos = 0;
for (TagStor ts : tags2add) {
TagStor nextTag = findNextTag(ts.getTimestamp());
if (nextTag == null) {
tags.add(ts);
// System.out.println("added new tag to end");
continue;
}
pos = tags.indexOf(nextTag);
if (ts.getTimestamp() == nextTag.getTimestamp() && ts.getType() == nextTag.getType()) {
// IF type = meta AND (the new.meta.event != next.meta.event OR
// can overwrite)
if (ts.getType() == FlvTag.META
&& (!((MetaTag) ts.getTag()).getEvent().equals(
((MetaTag) nextTag.getTag()).getEvent()) || !overwrite)) {
tags.add(pos, ts);
// System.out.println("inserted tag at : " + pos);
tp.setTags(tags);
pushTags(pos);
} else {
// System.out.println("replaced tag at : " + pos);
tags.remove(pos);
tags.add(pos, ts);
tp.setTags(tags);
}// else
} else {
tags.add(pos, ts);
// System.out.println("inserted tag at : " + pos);
tp.setTags(tags);
pushTags(pos);
}// else
}// for
}// addTags()
private void pushTags(int pos) {
if (pos <= tp.getLastAudioTag()) {
tp.setLastAudioTag(tp.getLastAudioTag() + 1);
}
if (pos <= tp.getLastVideoTag()) {
tp.setLastVideoTag(tp.getLastVideoTag() + 1);
}
if (pos <= tp.getFirstVideoTag()) {
tp.setFirstVideoTag(tp.getFirstVideoTag() + 1);
}
if (pos <= tp.getFirstAudioTag()) {
tp.setFirstAudioTag(tp.getFirstAudioTag() + 1);
}
if (pos <= tp.getOnMetaTag()) {
tp.setOnMetaTag(tp.getOnMetaTag() + 1);
}
}// pushTags()
private TagStor findNextTag(long timestamp) {
TagStor nextTag = null;
for (int i = 0; i < tags.size(); i++) {
if (tags.get(i).getTimestamp() >= timestamp) {
nextTag = tags.get(i);
break;
}
}// for
return nextTag;
}// findNextTag()
public int getVideoWidth() {
int vwidth = 0;
if (tp.getFirstVideoTag() != -1) {
if (((VideoTag) tags.get(tp.getFirstVideoTag()).getTag()).getWidth() == 0
&& tp.getOnMetaTag() != -1) {
try {
HashMap<String, Object> mdata = (HashMap<String, Object>) (((MetaTag) tags.get(
tp.getOnMetaTag()).getTag()).getMetaData());
vwidth = ((Double) mdata.get("width")).intValue();
} catch (Exception e) {
// do nothing
}
} else {
vwidth = ((VideoTag) tags.get(tp.getFirstVideoTag()).getTag()).getWidth();
}// else
}
return vwidth;
}// getVideoWidth()
public int getVideoHeight() {
int vheight = 0;
if (tp.getFirstVideoTag() != -1) {
if (((VideoTag) tags.get(tp.getFirstVideoTag()).getTag()).getHeight() == 0
&& tp.getOnMetaTag() != -1) {
try {
HashMap<String, Object> mdata = (HashMap<String, Object>) (((MetaTag) tags.get(
tp.getOnMetaTag()).getTag()).getMetaData());
vheight = ((Double) mdata.get("height")).intValue();
} catch (Exception e) {
// do nothing
}
} else {
vheight = ((VideoTag) tags.get(tp.getFirstVideoTag()).getTag()).getHeight();
}// else
}
return vheight;
}// getVideoHeight()
public long getTotalDataSize() {
return (tp.getTotalVideoSize() + tp.getTotalAudioSize() + tp.getTotalMetaSize());
}
public float getVideoDataRate(float duration) {
float videoDataRate = 0;
if (tp.getTotalVideoSize() != 0) {
videoDataRate = tp.getVideoDataSize() / duration * 8 / 1000;
}
return videoDataRate;
}// getVideoDataRate()
public float getAudioDataRate(float duration) {
float audioDataRate = 0;
if (tp.getTotalAudioSize() != 0) {
audioDataRate = tp.getAudioDataSize() / duration * 8 / 1000;
}
return audioDataRate;
}// getAudioDataRate()
public double getLastKeyFrameTimeStamp() {
double lkfts = 0;
double lkfTime = (tp.getKeyFrameTagTimes().get(tp.getKeyFrameTagTimes().size() - 1))
.doubleValue();
if (lkfTime != 0) {
lkfts = lkfTime;
}
return lkfts;
}// getLastKeyFrameTimeStamp()
public int getAudioCodecId() {
return ((AudioTag) tags.get(tp.getFirstAudioTag()).getTag()).getSoundFormat();
}
public int getFirstVideoTag() {
return tp.getFirstVideoTag();
}
public int getVideoCcodecId() {
int firstVideoTag = tp.getFirstVideoTag();
if (firstVideoTag > 0) {
return ((VideoTag) tags.get(firstVideoTag).getTag()).getCodecId();
} else {
return 0;
}
}
public long getAudioDelay() {
long audioDelay = 0;
if (tp.getFirstVideoTag() != -1) {
audioDelay = tags.get(tp.getFirstVideoTag()).getTimestamp() / 1000;
}
return audioDelay;
}// getAudioDelay()
public boolean canSeekToEnd() {
return (((VideoTag) tags.get(tp.getLastVideoTag()).getTag()).getFrameType() == VideoTag.KEYFRAME);
}
public boolean isStero() {
return (((AudioTag) tags.get(tp.getFirstAudioTag()).getTag()).getSoundType() == AudioTag.STEREO);
}
public int getAudioSampleRate() {
return ((AudioTag) tags.get(tp.getFirstAudioTag()).getTag()).getSoundRate();
}
public int getAudioSampleSize() {
return ((AudioTag) tags.get(tp.getFirstAudioTag()).getTag()).getSoundSampleSize();
}
public ArrayList<TagStor> getTags() {
return tags;
}
public double getFrameRate() {
return this.frameRate;
}
public long getTotalAudioSize() {
return tp.getTotalAudioSize();
}
public long getTotalVideoSize() {
return tp.getTotalVideoSize();
}
public long getTotalMetaSize() {
return tp.getTotalMetaSize();
}
public long getTotalByteOffset() {
return tp.getTotalByteOffset();
}
public AMFObject getKeyFrameTags() {
AMFObject keyFrameTags = new AMFObject();
keyFrameTags.put("times", tp.getKeyFrameTagTimes());
keyFrameTags.put("filepositions", tp.getKeyFrameTagOffsets());
return keyFrameTags;
}// getKeyFrameTags()
public ArrayList<Object> getCuePointTags() {
return tp.getCuePointTags();
}
public int getLastAudioTag() {
return tp.getLastAudioTag();
}
public int getLastVideoTag() {
return tp.getLastVideoTag();
}
public int getFirstAudioTag() {
return tp.getFirstAudioTag();
}
}// TagBroker