org.red5.io.flv.impl.FLV Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ant-media-server Show documentation
Show all versions of ant-media-server Show documentation
Ant Media Server supports RTMP, RTSP, MP4, HLS, WebRTC, Adaptive Streaming, etc.
/*
* RED5 Open Source Media Server - https://github.com/Red5/ Copyright 2006-2016 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.io.flv.impl;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import org.apache.mina.core.buffer.IoBuffer;
import org.red5.cache.ICacheStore;
import org.red5.cache.ICacheable;
import org.red5.cache.impl.NoCacheImpl;
import org.red5.io.ITag;
import org.red5.io.ITagReader;
import org.red5.io.ITagWriter;
import org.red5.io.IoConstants;
import org.red5.io.flv.IFLV;
import org.red5.io.flv.meta.IMetaData;
import org.red5.io.flv.meta.IMetaService;
import org.red5.io.flv.meta.MetaData;
import org.red5.io.flv.meta.MetaService;
import org.red5.media.processor.IPostProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A FLVImpl implements the FLV api
*
* @author The Red5 Project
* @author Dominick Accattato ([email protected])
* @author Luke Hubbard, Codegent Ltd ([email protected])
* @author Paul Gregoire ([email protected])
*/
public class FLV implements IFLV {
protected static Logger log = LoggerFactory.getLogger(FLV.class);
private static ICacheStore cache;
// stores (in-order) the writer post-process implementations
private static LinkedList> writePostProcessors;
private File file;
private boolean generateMetadata;
private IMetaService metaService;
private IMetaData, ?> metaData;
/*
* 0x08 AUDIO Contains an audio packet similar to a SWF SoundStreamBlock plus codec information 0x09 VIDEO Contains a video packet similar to a SWF VideoFrame plus codec
* information 0x12 META Contains two AMF packets, the name of the event and the data to go with it soundType (byte & 0x01) == 0 0: mono, 1: stereo soundSize (byte & 0x02) == 1 0:
* 8-bit, 2: 16-bit soundRate (byte & 0x0C) == 2 0: 5.5kHz, 1: 11kHz, 2: 22kHz, 3: 44kHz soundFormat (byte & 0xf0) == 4 0: Uncompressed, 1: ADPCM, 2: MP3, 5: Nellymoser 8kHz mono,
* 6: Nellymoser codecID (byte & 0x0f) == 0 2: Sorensen H.263, 3: Screen video, 4: On2 VP6 frameType (byte & 0xf0) == 4 1: keyframe, 2: inter frame, 3: disposable inter frame
* http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf
*/
/**
* Default constructor, used by Spring so that parameters may be injected.
*/
public FLV() {
}
/**
* Create FLV from given file source
*
* @param file
* File source
*/
public FLV(File file) {
this(file, false);
}
/**
* Create FLV from given file source and with specified metadata generation option
*
* @param file
* File source
* @param generateMetadata
* Metadata generation option
*/
public FLV(File file, boolean generateMetadata) {
this.file = file;
this.generateMetadata = generateMetadata;
if (!generateMetadata) {
try {
FLVReader reader = new FLVReader(this.file);
ITag tag = null;
int count = 0;
while (reader.hasMoreTags() && (++count < 5)) {
tag = reader.readTag();
if (tag.getDataType() == IoConstants.TYPE_METADATA) {
if (metaService == null) {
metaService = new MetaService();
}
metaData = metaService.readMetaData(tag.getBody());
}
}
reader.close();
} catch (Exception e) {
log.error("An error occured looking for metadata", e);
}
}
}
/**
* Sets the cache implementation to be used.
*
* @param cache
* Cache store
*/
@Override
public void setCache(ICacheStore cache) {
FLV.cache = cache;
}
/**
* Sets a writer post processor.
*
* @param writerPostProcessor IPostProcess implementation class name
*/
@SuppressWarnings("unchecked")
public void setWriterPostProcessor(String writerPostProcessor) {
if (writePostProcessors == null) {
writePostProcessors = new LinkedList<>();
}
try {
writePostProcessors.add((Class) Class.forName(writerPostProcessor));
} catch (Exception e) {
log.debug("Write post process implementation: {} was not found", writerPostProcessor);
}
}
/**
* Sets a group of writer post processors.
*
* @param writerPostProcessors IPostProcess implementation class names
*/
@SuppressWarnings("unchecked")
public void setWriterPostProcessors(Set writerPostProcessors) {
if (writePostProcessors == null) {
writePostProcessors = new LinkedList<>();
}
for (String writerPostProcessor : writerPostProcessors) {
try {
writePostProcessors.add((Class) Class.forName(writerPostProcessor));
} catch (Exception e) {
log.debug("Write post process implementation: {} was not found", writerPostProcessor);
}
}
}
public LinkedList> getWritePostProcessors() {
return writePostProcessors;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasMetaData() {
return metaData != null;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings({ "rawtypes" })
@Override
public IMetaData getMetaData() throws FileNotFoundException {
metaService.setFile(file);
return null;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasKeyFrameData() {
//if (hasMetaData()) {
// return !((MetaData) metaData).getKeyframes().isEmpty();
//}
return false;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public void setKeyFrameData(Map keyframedata) {
if (!hasMetaData()) {
metaData = new MetaData();
}
//The map is expected to contain two entries named "times" and "filepositions",
//both of which contain a map keyed by index and time or position values.
Map times = new HashMap<>();
Map filepositions = new HashMap<>();
if (keyframedata.containsKey("times")) {
Map inTimes = (Map) keyframedata.get("times");
for (Object o : inTimes.entrySet()) {
Map.Entry entry = (Map.Entry) o;
times.put(entry.getKey(), entry.getValue());
}
}
((MetaData) metaData).put("times", times);
//
if (keyframedata.containsKey("filepositions")) {
Map inPos = (Map) keyframedata.get("filepositions");
for (Object o : inPos.entrySet()) {
Map.Entry entry = (Map.Entry) o;
filepositions.put(entry.getKey(), entry.getValue());
}
}
((MetaData) metaData).put("filepositions", filepositions);
}
/**
* {@inheritDoc}
*/
@SuppressWarnings({ "rawtypes" })
@Override
public Map getKeyFrameData() {
Map keyframes = null;
//if (hasMetaData()) {
// keyframes = ((MetaData) metaData).getKeyframes();
//}
return keyframes;
}
/**
* {@inheritDoc}
*/
@Override
public void refreshHeaders() throws IOException {
}
/**
* {@inheritDoc}
*/
@Override
public void flushHeaders() throws IOException {
}
/**
* {@inheritDoc}
*/
@Override
public ITagReader getReader() throws IOException {
FLVReader reader = null;
IoBuffer fileData;
String fileName = file.getName();
// if no cache is set an NPE will be thrown
if (cache == null) {
log.info("FLV cache is null, forcing NoCacheImpl instance");
cache = NoCacheImpl.getInstance();
}
ICacheable ic = cache.get(fileName);
// look in the cache before reading the file from the disk
if (null == ic || (null == ic.getByteBuffer())) {
if (file.exists()) {
log.debug("File size: {}", file.length());
reader = new FLVReader(file, generateMetadata);
// get a ref to the mapped byte buffer
fileData = reader.getFileData();
// offer the uncached file to the cache
if (fileData != null && cache.offer(fileName, fileData)) {
log.debug("Item accepted by the cache: {}", fileName);
} else {
log.debug("Item will not be cached: {}", fileName);
}
} else {
boolean result = file.createNewFile();
log.info("Creating new file: {} and result is {}", file, result);
}
} else {
fileData = IoBuffer.wrap(ic.getBytes());
reader = new FLVReader(fileData, generateMetadata);
}
return reader;
}
/**
* {@inheritDoc}
*/
@Override
public ITagReader readerFromNearestKeyFrame(int seekPoint) {
return null;
}
/**
* {@inheritDoc}
*/
@Override
public ITagWriter getWriter() throws IOException {
log.info("getWriter: {}", file);
return new FLVWriter(file.toPath(), false);
}
/** {@inheritDoc} */
@Override
public ITagWriter getAppendWriter() throws IOException {
log.info("getAppendWriter: {}", file);
return new FLVWriter(file.toPath(), true);
}
/**
* {@inheritDoc}
*/
@Override
public ITagWriter writerFromNearestKeyFrame(int seekPoint) {
return null;
}
/** {@inheritDoc} */
@SuppressWarnings({ "rawtypes" })
@Override
public void setMetaData(IMetaData meta) throws IOException {
log.info("setMetaData: {}", meta);
if (metaService == null) {
metaService = new MetaService(file);
}
//if the file is not checked the write may produce an NPE
if (metaService.getFile() == null) {
metaService.setFile(file);
}
metaService.write(meta);
metaData = meta;
}
/** {@inheritDoc} */
@Override
public void setMetaService(IMetaService service) {
metaService = service;
}
}