Package com.wowza.wms.stream
Interface IMediaWriter
- 
 public interface IMediaWriterIMediaWriter: generic media writer interface. The flv recording system using this interface to persist .flv data captured from the Flash client. These classes are referenced in [install-dir]/conf/MediaWriters.xml. Example IMediaWriter implementation: MediaWriterFLVBasicThis is a basic IMediaWriter implementation that can handle record and append. import java.io.*; import java.nio.ByteBuffer; import java.util.*; import com.wowza.util.*; import com.wowza.wms.stream.*; import com.wowza.wms.amf.AMFData; import com.wowza.wms.logging.*; public class MediaWriterFLV implements IMediaWriter { private IMediaStream parent = null; private MediaWriterItem mediaWriterItem = null; private long[] currentTCs = new long[3]; private long duration = 0; private Map extraMetadata = new HashMap(); private boolean versionFile = false; public void setMediaWriterItem(MediaWriterItem mediaWriterItem) { this.mediaWriterItem = mediaWriterItem; } public void setParent(IMediaStream parent) { this.parent = parent; } public void writePackets(List audioPackets, List videoPackets, List dataPackets, List audioTCs, List videoTCs, List dataTCs, List dataTypes, boolean isFirst, boolean isLast) { File newFile = this.parent.getStreamFile(); boolean localAppend = this.parent.isAppend(); if (isFirst) { long startTC = 0; if (newFile.exists()) { if (localAppend) startTC = FLVUtils.getLastTC(newFile); else { if (versionFile) FileUtils.versionFile(newFile); else { try { newFile.delete(); } catch (Exception e) { } } } } else localAppend = false; this.currentTCs[FLVUtils.FLV_TCINDEXAUDIO] = startTC; this.currentTCs[FLVUtils.FLV_TCINDEXVIDEO] = startTC; this.currentTCs[FLVUtils.FLV_TCINDEXDATA] = startTC; } else localAppend = true; try { if (newFile.getParentFile() == null) WMSLoggerFactory.getLogger(MediaWriterFLV.class).warn("MediaWriterFLV: File path does not exist: "+newFile.getPath()); else if (!newFile.getParentFile().exists()) WMSLoggerFactory.getLogger(MediaWriterFLV.class).warn("MediaWriterFLV: Folder does not exist: "+newFile.getParentFile().getPath()); else if (newFile.exists() && !newFile.canWrite()) WMSLoggerFactory.getLogger(MediaWriterFLV.class).warn("MediaWriterFLV: Cannot write to file (permission error): "+newFile.getPath()); FileOutputStream ds = new FileOutputStream(newFile, localAppend); if (isFirst) { if (!localAppend) { FLVUtils.writeHeader(ds, 0.0, extraMetadata); boolean writeZeroPacket = true; while(true) { if (audioPackets.size() == 0) break; ByteBuffer data = (ByteBuffer)audioPackets.get(0); long tcA = ((Long)audioTCs.get(0)).longValue(); if (tcA == 0 && data.limit() == 0) writeZeroPacket = false; break; } if (writeZeroPacket) { FLVUtils.writeChunk(ds, null, 0, this.currentTCs[FLVUtils.FLV_TCINDEXAUDIO], (byte) 0x08); // write zero length audio block } } } FLVUtils.writePackets(ds, audioPackets, videoPackets, dataPackets, audioTCs, videoTCs, dataTCs, dataTypes, currentTCs); ds.flush(); ds.close(); } catch (Exception e) { WMSLoggerFactory.getLogger(MediaWriterFLV.class).error( "MediaWriterFLV: Error writing to file: "+newFile.getPath()+" :"+e.toString()); e.printStackTrace(); } if (isLast) { duration = Math.max(Math.max(currentTCs[FLVUtils.FLV_TCINDEXAUDIO], currentTCs[FLVUtils.FLV_TCINDEXVIDEO]), currentTCs[FLVUtils.FLV_TCINDEXDATA]); double durationSecs = ((double)duration) / 1000.0; FLVUtils.writeDuration(newFile, durationSecs); } } public Map getExtraMetadata() { return extraMetadata; } public void setExtraMetadata(Map extraMetadata) { this.extraMetadata = extraMetadata; } public boolean isVersionFile() { return versionFile; } public void setVersionFile(boolean versionFile) { this.versionFile = versionFile; } public void putMetaData(String name, AMFData value) { this.extraMetadata.put(name, value); } }To use this class, edit [install-dir]/conf/MediaWriter and replace the definition for the flv MediaWriter: <MediaWriter> <Name>flv</Name> <Description>FLV Media Writer</Description> <FileExtension>flv</FileExtension> <ClassBase>com.wowza.wms.plugin.mediawriter.flv.MediaWriterFLVBasic</ClassBase> </MediaWriter>Example IMediaWriter implementation: MediaWriterFLVMetadataThis example illustrates how to write custom metadata into the recorded flv file on the fly. public class MediaWriterFLVMetadata implements IMediaWriter { private IMediaStream parent = null; private MediaWriterItem mediaWriterItem = null; private long[] currentTCs = new long[3]; private long duration = 0; private File tmpFile = null; private Map extraMetadata = new HashMap(); private boolean versionFile = false; public void setMediaWriterItem(MediaWriterItem mediaWriterItem) { this.mediaWriterItem = mediaWriterItem; } public void setParent(IMediaStream parent) { this.parent = parent; } public void writePackets(List audioPackets, List videoPackets, List dataPackets, List audioTCs, List videoTCs, List dataTCs, boolean isFirst, boolean isLast) { File newFile = this.parent.getStreamFile(); try { if (tmpFile == null) tmpFile = File.createTempFile("wowza", "flv"); } catch (Exception e) { WMSLoggerFactory.getLogger(MediaWriterFLVBasic.class).error( "MediaWriterFLVMetadata: Error createTempFile: "+ tmpFile+" :"+e.toString()); } boolean localAppend = this.parent.isAppend(); if (isFirst) { AMFDataArray keyFrames = null; long startTC = 0; if (newFile.exists()) { if (localAppend) { startTC = FLVUtils.getLastTC(newFile); keyFrames = getKeyFrames(newFile); copyPacketsToTmpFile(newFile, tmpFile); } if (versionFile) FileUtils.versionFile(newFile); else { try { newFile.delete(); } catch (Exception e) { } } } else localAppend = false; if (keyFrames == null) keyFrames = new AMFDataArray(); extraMetadata.put("keyFrames", keyFrames); this.currentTCs[FLVUtils.FLV_TCINDEXAUDIO] = startTC; this.currentTCs[FLVUtils.FLV_TCINDEXVIDEO] = startTC; this.currentTCs[FLVUtils.FLV_TCINDEXDATA] = startTC; } else localAppend = true; AMFDataArray keyFrames = (AMFDataArray)extraMetadata.get("keyFrames"); long timecode = this.currentTCs[FLVUtils.FLV_TCINDEXVIDEO]; int size = videoPackets.size(); for(int i=0;i<size;i++) { ByteBuffer data = (ByteBuffer)videoPackets.get(i); int firstByte = data.get(0); timecode += ((Long)videoTCs.get(i)).longValue(); if (FLVUtils.getFrameType(firstByte) == FLVUtils.FLV_KFRAME) { double durationSecs = ((double)timecode) / 1000.0; AMFDataObj dataObj = new AMFDataObj(); dataObj.put("name", new AMFDataItem("keyframe "+durationSecs)); dataObj.put("time", new AMFDataItem(durationSecs)); keyFrames.add(dataObj); } } try { FileOutputStream ds = new FileOutputStream(tmpFile, localAppend); FLVUtils.writePackets(ds, audioPackets, videoPackets, dataPackets, audioTCs, videoTCs, dataTCs, currentTCs); ds.flush(); ds.close(); } catch (Exception e) { WMSLoggerFactory.getLogger(MediaWriterFLVBasic.class).error( "MediaWriterFLVMetadata: Error writing to tmp file: "+ newFile.getPath()+" :"+e.toString()); } if (isLast) { duration = Math.max(Math.max(currentTCs[FLVUtils.FLV_TCINDEXAUDIO], currentTCs[FLVUtils.FLV_TCINDEXVIDEO]), currentTCs[FLVUtils.FLV_TCINDEXDATA]); double durationSecs = ((double)duration) / 1000.0; try { AMFPacket packet = null; FileOutputStream ds = new FileOutputStream(newFile); FileInputStream di = new FileInputStream(tmpFile); FLVUtils.writeHeader(ds, durationSecs, extraMetadata); while((packet = FLVUtils.readChunk(di)) != null) { FLVUtils.writeChunk(ds, packet.getDataBuffer(), packet.getSize(), packet.getTimecode(), (byte)packet.getType()); } di.close(); ds.flush(); ds.close(); tmpFile.delete(); } catch (Exception e) { WMSLoggerFactory.getLogger(MediaWriterFLVBasic.class).error( "MediaWriterFLVMetadata: Error tmp writing to file: "+ newFile.getPath()+" :"+e.toString()); } } } private void copyPacketsToTmpFile(File newFile, File tmpFile) { AMFDataArray keyFrames = null; try { AMFPacket packet = null; FileOutputStream ds = new FileOutputStream(tmpFile); FileInputStream di = new FileInputStream(newFile); FLVUtils.readHeader(di); FLVUtils.readChunk(di); // skip metaData packet while((packet = FLVUtils.readChunk(di)) != null) { FLVUtils.writeChunk(ds, packet.getDataBuffer(), packet.getSize(), packet.getTimecode(), (byte)packet.getType()); } di.close(); ds.flush(); ds.close(); } catch (Exception e) { WMSLoggerFactory.getLogger(MediaWriterFLVBasic.class).error( "MediaWriterFLVMetadata: Error copyPacketsToTmpFile: "+ newFile.getPath()+" :"+e.toString()); } } private AMFDataArray getKeyFrames(File newFile) { AMFDataArray keyFrames = null; try { BufferedInputStream inStream = new BufferedInputStream(new FileInputStream(newFile)); FLVUtils.readHeader(inStream); AMFPacket packet = FLVUtils.readChunk(inStream); if (packet.getType() == IVHost.CONTENTTYPE_DATA0 || packet.getType() == IVHost.CONTENTTYPE_DATA3) { byte[] mbytes = packet.getData(); int moffset = 0; if (packet.getType() == IVHost.CONTENTTYPE_DATA3 && mbytes.length > 0) { if (mbytes[0] == 0) moffset = 1; } AMFDataList dataList = new AMFDataList(mbytes, moffset, mbytes.length-moffset); if (dataList.size() > 1) { if (dataList.get(1).getType() == AMFData.DATA_TYPE_MIXED_ARRAY) { AMFDataMixedArray metaValues = (AMFDataMixedArray)dataList.get(1); if (metaValues.containsKey("keyFrames")) keyFrames = (AMFDataArray)metaValues.get("keyFrames"); } } } inStream.close(); } catch (Exception e) { WMSLoggerFactory.getLogger(MediaWriterFLVBasic.class).error( "MediaWriterFLVMetadata: Error getKeyFrames: "+ newFile.getPath()+" :"+e.toString()); } return keyFrames; } public boolean isVersionFile() { return versionFile; } public void setVersionFile(boolean versionFile) { this.versionFile = versionFile; } public void putMetaData(String name, AMFData value) { this.extraMetadata.put(name, value); } }To use this class, edit [install-dir]/conf/MediaWriter and replace the definition for the flv MediaWriter: <MediaWriter> <Name>flv</Name> <Description>FLV Media Writer</Description> <FileExtension>flv</FileExtension> <ClassBase>com.wowza.wms.plugin.mediawriter.flv.MediaWriterFLVMetadata</ClassBase> </MediaWriter>
- 
- 
Method SummaryAll Methods Instance Methods Abstract Methods Modifier and Type Method Description longgetDuration()Get the recorded duration of the file in secondsbooleanisAudioWaitForVideoKeyFrame()Set to true if waiting for first video key frame to start recording audiobooleanisDataWaitForVideoKeyFrame()booleanisUseCalculatedAudioTimecodes()Set to true to calculate timecodes based on samples per-frame (more accurate, default is true)booleanisVersionFile()Return true if the old file is to be versionedbooleanisWaitForVideoKeyFrame()get wait for key framevoidputMetaData(String name, AMFData value)Add metadata to the metadata packet.voidsetAudioWaitForVideoKeyFrame(boolean audioWaitForVideoKeyFrame)Set to true if waiting for first video key frame to start recording audiovoidsetDataWaitForVideoKeyFrame(boolean dataWaitForVideoKeyFrame)voidsetMediaWriterItem(com.wowza.wms.stream.MediaWriterItem mediaWriterItem)Set the media write definitionvoidsetParent(IMediaStream parent)Set the parent stream for this media write objectvoidsetUseCalculatedAudioTimecodes(boolean calulateTimecodes)Set to true to calculate timecodes based on samples per-frame (more accurate, default is true)voidsetVersionFile(boolean versionFile)Set to true if the old file is to be versionedvoidsetWaitForVideoKeyFrame(boolean waitForVideoKeyFrame)Set to true if you want the recorder to skip opening frames until it hits a key framevoidwritePackets(java.util.List audioPackets, java.util.List videoPackets, java.util.List dataPackets, java.util.List audioTCs, java.util.List videoTCs, java.util.List dataTCs, java.util.List dataTypes, boolean isFirst, boolean isLast)Invoked each time a set of packets are ready to be presisted.
 
- 
- 
- 
Method Detail- 
writePacketsvoid writePackets(java.util.List audioPackets, java.util.List videoPackets, java.util.List dataPackets, java.util.List audioTCs, java.util.List videoTCs, java.util.List dataTCs, java.util.List dataTypes, boolean isFirst, boolean isLast)Invoked each time a set of packets are ready to be presisted.- Parameters:
- audioPackets- List of audio packets
- videoPackets- List of video packets
- dataPackets- List of data packets
- audioTCs- List of audio timecodes
- videoTCs- List of video timecodes
- dataTCs- List of data timecodes
- dataTypes- list of integer packets types (IVHost.CONTENTTYPE_DATA0, IVHost.CONTENTTYPE_DATA3) - if null assumed to be IVHost.CONTENTTYPE_DATA0
- isFirst- true if first packet to be written
- isLast- false if last packet to be written
 
 - 
setMediaWriterItemvoid setMediaWriterItem(com.wowza.wms.stream.MediaWriterItem mediaWriterItem) Set the media write definition- Parameters:
- mediaWriterItem- media write definition
 
 - 
setParentvoid setParent(IMediaStream parent) Set the parent stream for this media write object- Parameters:
- parent-
 
 - 
isVersionFileboolean isVersionFile() Return true if the old file is to be versioned- Returns:
- true if the old file is to be versioned
 
 - 
setVersionFilevoid setVersionFile(boolean versionFile) Set to true if the old file is to be versioned- Parameters:
- versionFile-
 
 - 
isWaitForVideoKeyFrameboolean isWaitForVideoKeyFrame() get wait for key frame- Returns:
- wait for key frame
 
 - 
setWaitForVideoKeyFramevoid setWaitForVideoKeyFrame(boolean waitForVideoKeyFrame) Set to true if you want the recorder to skip opening frames until it hits a key frame- Parameters:
- waitForVideoKeyFrame- wait for key frame
 
 - 
putMetaDatavoid putMetaData(String name, AMFData value)Add metadata to the metadata packet. Only metadata added before the first call to writePackets will be included in the file- Parameters:
- name- field name
- value- metadata value
 
 - 
getDurationlong getDuration() Get the recorded duration of the file in seconds- Returns:
- recorded duration of the file in seconds
 
 - 
isAudioWaitForVideoKeyFrameboolean isAudioWaitForVideoKeyFrame() Set to true if waiting for first video key frame to start recording audio- Returns:
- true if waiting for first video key frame to start recording audio
 
 - 
setAudioWaitForVideoKeyFramevoid setAudioWaitForVideoKeyFrame(boolean audioWaitForVideoKeyFrame) Set to true if waiting for first video key frame to start recording audio- Parameters:
- audioWaitForVideoKeyFrame- true if waiting for first video key frame to start recording audio
 
 - 
isDataWaitForVideoKeyFrameboolean isDataWaitForVideoKeyFrame() 
 - 
setDataWaitForVideoKeyFramevoid setDataWaitForVideoKeyFrame(boolean dataWaitForVideoKeyFrame) 
 - 
isUseCalculatedAudioTimecodesboolean isUseCalculatedAudioTimecodes() Set to true to calculate timecodes based on samples per-frame (more accurate, default is true)- Returns:
- true to calculate timecodes based on samples per-frame
 
 - 
setUseCalculatedAudioTimecodesvoid setUseCalculatedAudioTimecodes(boolean calulateTimecodes) Set to true to calculate timecodes based on samples per-frame (more accurate, default is true)- Parameters:
- calulateTimecodes- true to calculate timecodes based on samples per-frame
 
 
- 
 
-