package org.red5.io.flv.meta;
/*
* RED5 Open Source Flash Server - http://www.osflash.org/red5
*
* Copyright (c) 2006-2009 by respective authors (see below). All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any later
* version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this library; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import org.apache.mina.common.ByteBuffer;
import org.red5.io.ITag;
import org.red5.io.IoConstants;
import org.red5.io.amf.Input;
import org.red5.io.amf.Output;
import org.red5.io.flv.impl.FLVReader;
import org.red5.io.flv.impl.FLVWriter;
import org.red5.io.flv.impl.Tag;
import org.red5.io.object.Deserializer;
import org.red5.io.object.Serializer;
/**
* MetaService represents a MetaData service in Spring
*
* @author The Red5 Project (red5@osflash.org)
* @author Dominick Accattato (daccattato@gmail.com)
* @author Luke Hubbard, Codegent Ltd (luke@codegent.com)
*/
public class MetaService implements IMetaService {
/**
* Source file
*/
File file;
/**
* File input stream
*/
@SuppressWarnings("unused")
private FileInputStream fis;
/**
* File output stream
*/
private FileOutputStream fos;
/**
* Serializer
*/
private Serializer serializer;
/**
* Deserializer
*/
private Deserializer deserializer;
/**
* Merges metadata objects
*/
private Resolver resolver;
/**
* @return Returns the resolver.
*/
public Resolver getResolver() {
return resolver;
}
/**
* @param resolver The resolver to set.
*/
public void setResolver(Resolver resolver) {
this.resolver = resolver;
}
/**
* @return Returns the deserializer.
*/
public Deserializer getDeserializer() {
return deserializer;
}
/**
* @param deserializer The deserializer to set.
*/
public void setDeserializer(Deserializer deserializer) {
this.deserializer = deserializer;
}
/**
* @return Returns the serializer.
*/
public Serializer getSerializer() {
return serializer;
}
/**
* @param serializer The serializer to set.
*/
public void setSerializer(Serializer serializer) {
this.serializer = serializer;
}
/**
* MetaService constructor
*/
public MetaService() {
super();
}
public MetaService(File poFil) {
this();
this.file = poFil;
}
/**
* {@inheritDoc}
*/
public void write(IMetaData<?, ?> meta) throws IOException {
// Get cue points, FLV reader and writer
IMetaCue[] metaArr = meta.getMetaCue();
FLVReader reader = new FLVReader(file, false);
FLVWriter writer = new FLVWriter(fos, false);
ITag tag = null;
// Read first tag
if (reader.hasMoreTags()) {
tag = reader.readTag();
if (tag.getDataType() == IoConstants.TYPE_METADATA) {
if (!reader.hasMoreTags())
throw new IOException(
"File we're writing is metadata only?");
}
}
meta.setDuration(((double) reader.getDuration() / 1000));
meta.setVideoCodecId(reader.getVideoCodecId());
meta.setAudioCodecId(reader.getAudioCodecId());
ITag injectedTag = injectMetaData(meta, tag);
injectedTag.setPreviousTagSize(0);
tag.setPreviousTagSize(injectedTag.getBodySize());
writer.writeHeader();
writer.writeTag(injectedTag);
writer.writeTag(tag);
int cuePointTimeStamp = 0;
int counter = 0;
if (metaArr != null) {
Arrays.sort(metaArr);
cuePointTimeStamp = getTimeInMilliseconds(metaArr[0]);
}
while (reader.hasMoreTags()) {
tag = reader.readTag();
// if there are cuePoints in the array
if (counter < metaArr.length) {
// If the tag has a greater timestamp than the
// cuePointTimeStamp, then inject the tag
while (tag.getTimestamp() > cuePointTimeStamp) {
injectedTag = injectMetaCue(metaArr[counter], tag);
writer.writeTag(injectedTag);
tag.setPreviousTagSize(injectedTag.getBodySize());
// Advance to the next CuePoint
counter++;
if (counter > (metaArr.length - 1)) {
break;
}
cuePointTimeStamp = getTimeInMilliseconds(metaArr[counter]);
}
}
if (tag.getDataType() != IoConstants.TYPE_METADATA) {
writer.writeTag(tag);
}
}
writer.close();
}
/**
* Merges the two Meta objects according to user
*
* @param metaData
* First metadata object
* @param md
* Second metadata object
* @return Merged metadata
*/
@SuppressWarnings("unused")
private IMeta mergeMeta(IMetaData<?, ?> metaData, IMetaData<?, ?> md) {
return new Resolver().resolve(metaData, md);
}
/**
* Injects metadata (other than Cue points) into a tag
*
* @param meta
* Metadata
* @param tag
* Tag
* @return New tag with injected metadata
*/
private ITag injectMetaData(IMetaData<?, ?> meta, ITag tag) {
ByteBuffer bb = ByteBuffer.allocate(1000);
bb.setAutoExpand(true);
Output out = new Output(bb);
Serializer ser = new Serializer();
ser.serialize(out, "onMetaData");
ser.serialize(out, meta);
ByteBuffer tmpBody = out.buf().flip();
int tmpBodySize = out.buf().limit();
int tmpPreviousTagSize = tag.getPreviousTagSize();
byte tmpDataType = IoConstants.TYPE_METADATA;
int tmpTimestamp = 0;
return new Tag(tmpDataType, tmpTimestamp, tmpBodySize, tmpBody,
tmpPreviousTagSize);
}
/**
* Injects metadata (Cue Points) into a tag
*
* @param meta
* Metadata (cue points)
* @param tag
* Tag
* @return ITag tag New tag with injected metadata
*/
private ITag injectMetaCue(IMetaCue meta, ITag tag) {
// IMeta meta = (MetaCue) cue;
Output out = new Output(ByteBuffer.allocate(1000));
Serializer ser = new Serializer();
ser.serialize(out, "onCuePoint");
ser.serialize(out, meta);
ByteBuffer tmpBody = out.buf().flip();
int tmpBodySize = out.buf().limit();
int tmpPreviousTagSize = tag.getPreviousTagSize();
byte tmpDataType = IoConstants.TYPE_METADATA;
int tmpTimestamp = getTimeInMilliseconds(meta);
return new Tag(tmpDataType, tmpTimestamp, tmpBodySize, tmpBody,
tmpPreviousTagSize);
}
/**
* Returns a timestamp of cue point in milliseconds
*
* @param metaCue
* Cue point
* @return int time Timestamp of given cue point (in milliseconds)
*/
private int getTimeInMilliseconds(IMetaCue metaCue) {
return (int) (metaCue.getTime() * 1000.00);
}
/**
* {@inheritDoc}
*/
public void writeMetaData(IMetaData<?, ?> metaData) {
IMetaCue meta = (MetaCue<?, ?>) metaData;
Output out = new Output(ByteBuffer.allocate(1000));
serializer.serialize(out, "onCuePoint");
serializer.serialize(out, meta);
}
/**
* {@inheritDoc}
*/
public void writeMetaCue() {
}
/**
* @return Returns the file.
*/
public File getFile() {
return file;
}
/**
* @param file The file to set.
*/
public void setFile(File file) {
this.file = file;
}
/** {@inheritDoc} */
public void setInStream(FileInputStream fis) {
this.fis = fis;
}
/** {@inheritDoc} */
public void setOutStream(FileOutputStream fos) {
this.fos = fos;
}
/** {@inheritDoc} */
// TODO need to fix
public MetaData<?, ?> readMetaData(ByteBuffer buffer) {
MetaData<?, ?> retMeta = new MetaData<String, Object>();
Input input = new Input(buffer);
if (deserializer == null) {
deserializer = new Deserializer();
}
String metaType = deserializer.deserialize(input, String.class);
Map<String, ?> m = deserializer.deserialize(input, Map.class);
retMeta.putAll(m);
return retMeta;
}
/** {@inheritDoc} */
public IMetaCue[] readMetaCue() {
// TODO Auto-generated method stub
return null;
}
}