/*
* RED5 Open Source Flash Server - http://code.google.com/p/red5/
*
* Copyright 2006-2014 by respective authors (see below). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.red5.server.stream;
import org.red5.io.ITag;
import org.red5.io.ITagReader;
import org.red5.io.flv.IKeyFrameDataAnalyzer;
import org.red5.io.flv.IKeyFrameDataAnalyzer.KeyFrameMeta;
import org.red5.server.net.rtmp.event.AudioData;
import org.red5.server.net.rtmp.event.IRTMPEvent;
import org.red5.server.net.rtmp.event.Invoke;
import org.red5.server.net.rtmp.event.Notify;
import org.red5.server.net.rtmp.event.Unknown;
import org.red5.server.net.rtmp.event.VideoData;
import org.red5.server.net.rtmp.message.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Represents stream source that is file
*/
public class FileStreamSource implements ISeekableStreamSource, Constants {
/**
* Logger
*/
protected static Logger log = LoggerFactory.getLogger(FileStreamSource.class);
/**
* Tag reader
*/
private ITagReader reader;
/**
* Key frame metadata
*/
private KeyFrameMeta keyFrameMeta;
/**
* Creates file stream source with tag reader
* @param reader Tag reader
*/
public FileStreamSource(ITagReader reader) {
this.reader = reader;
}
/**
* Closes tag reader
*/
public void close() {
reader.close();
}
/**
* Get tag from queue and convert to message
* @return RTMP event
*/
public IRTMPEvent dequeue() {
if (reader.hasMoreTags()) {
ITag tag = reader.readTag();
IRTMPEvent msg;
switch (tag.getDataType()) {
case TYPE_AUDIO_DATA:
msg = new AudioData(tag.getBody());
break;
case TYPE_VIDEO_DATA:
msg = new VideoData(tag.getBody());
break;
case TYPE_INVOKE:
msg = new Invoke(tag.getBody());
break;
case TYPE_NOTIFY:
msg = new Notify(tag.getBody());
break;
default:
log.warn("Unexpected type? {}", tag.getDataType());
msg = new Unknown(tag.getDataType(), tag.getBody());
break;
}
msg.setTimestamp(tag.getTimestamp());
//msg.setSealed(true);
return msg;
}
return null;
}
/** {@inheritDoc} */
public boolean hasMore() {
return reader.hasMoreTags();
}
/** {@inheritDoc} */
public int seek(int ts) {
log.trace("Seek ts: {}", ts);
if (keyFrameMeta == null) {
if (!(reader instanceof IKeyFrameDataAnalyzer)) {
// Seeking not supported
return ts;
}
keyFrameMeta = ((IKeyFrameDataAnalyzer) reader).analyzeKeyFrames();
}
if (keyFrameMeta.positions.length == 0) {
// no video keyframe metainfo, it's an audio-only FLV we skip the seek for now.
// TODO add audio-seek capability
return ts;
}
int frame = 0;
for (int i = 0; i < keyFrameMeta.positions.length; i++) {
if (keyFrameMeta.timestamps[i] > ts) {
break;
}
frame = i;
}
reader.position(keyFrameMeta.positions[frame]);
return keyFrameMeta.timestamps[frame];
}
}