Hi,
I'm a user of Red5. Thank you very much for providing the great open
source software.
I'm using org.red5.server.stream.ClientBroadcastStream to record video
stream into flv files.
I expected that calling ClientBroadcastStream#saveAs(name, isAppend)
multiple times allows me to change the file without any
frame-overwrapping and any frame-loss.
However, current SVN trunk and v0.6.2 do not behave as I expected. By
calling saveAs() twice (with different file names), two files are
created and both files just keep growing.
Calling stopRecording() before second saveAs() works. However, some
flames seem to be lost. This seems to be due to unsubscribing from
the pipe by stopRecording() and skipping some frames before the next
keyframe.
Subscribing new FileConsumer followed by unsubscribing the original
FileConsumer results in overwrapping some frames among files.
After some trial and error, I made some changes in the following
source code in SVN trunk so that I can change files without any
frame-overwrapping and any frame-loss. The new code with my patch
changes files at the next keyframe.
org.red5.server.stream.ClientBroadcastStream
org.red5.server.stream.consumer.FileConsumer
I think this patch might be useful for other users too. So, I'm
wondering if this patch could be incorporated into the main stream of
Red5. Any comments and helps will be appreciated.
Best regards,
-----------------------------------------
Ryo Neyama
Utagoe Inc.
email: [EMAIL PROTECTED]
Tel: 03-3461-1118 / Fax: 03-3461-1119
http://www.utagoe.com/
http://channel.is/
-----------------------------------------
Index:
C:/work/WorkingDraft/eclipse-ws/red5_server/src/org/red5/server/stream/consumer/FileConsumer.java
===================================================================
---
C:/work/WorkingDraft/eclipse-ws/red5_server/src/org/red5/server/stream/consumer/FileConsumer.java
(revision 2305)
+++
C:/work/WorkingDraft/eclipse-ws/red5_server/src/org/red5/server/stream/consumer/FileConsumer.java
(working copy)
@@ -44,6 +44,8 @@
import org.red5.server.messaging.OOBControlMessage;
import org.red5.server.messaging.PipeConnectionEvent;
import org.red5.server.net.rtmp.event.IRTMPEvent;
+import org.red5.server.net.rtmp.event.VideoData;
+import org.red5.server.net.rtmp.event.VideoData.FrameType;
import org.red5.server.net.rtmp.message.Constants;
import org.red5.server.stream.IStreamData;
import org.red5.server.stream.message.RTMPMessage;
@@ -87,6 +89,10 @@
* Start timestamp
*/
private int startTimestamp;
+ /**
+ * Next file
+ */
+ private File nextFile;
/**
* Creates file consumer
@@ -96,10 +102,21 @@
public FileConsumer(IScope scope, File file) {
this.scope = scope;
this.file = file;
+ this.nextFile = null;
offset = 0;
lastTimestamp = 0;
startTimestamp = -1;
}
+
+ /**
+ * Changes file
+ * @param file
+ */
+ public synchronized void changeFile(File file) {
+ if (!file.equals(this.file)) {
+ this.nextFile = file;
+ }
+ }
/**
* Push message through pipe
@@ -119,11 +136,21 @@
if (!(message instanceof RTMPMessage)) {
return;
}
- if (writer == null) {
- init();
- }
RTMPMessage rtmpMsg = (RTMPMessage) message;
final IRTMPEvent msg = rtmpMsg.getBody();
+ synchronized (this) {
+ if (writer == null) {
+ init();
+ } else if (nextFile != null && msg instanceof VideoData
&& ((VideoData) msg).getFrameType() == FrameType.KEYFRAME) {
+ this.file = this.nextFile;
+ this.nextFile = null;
+ this.writer = null;
+ offset = 0;
+ lastTimestamp = 0;
+ startTimestamp = -1;
+ init();
+ }
+ }
if (startTimestamp == -1) {
startTimestamp = msg.getTimestamp();
}
Index:
C:/work/WorkingDraft/eclipse-ws/red5_server/src/org/red5/server/stream/ClientBroadcastStream.java
===================================================================
---
C:/work/WorkingDraft/eclipse-ws/red5_server/src/org/red5/server/stream/ClientBroadcastStream.java
(revision 2305)
+++
C:/work/WorkingDraft/eclipse-ws/red5_server/src/org/red5/server/stream/ClientBroadcastStream.java
(working copy)
@@ -569,15 +569,20 @@
if (log.isDebugEnabled()) {
log.debug("Recording file: " + file.getCanonicalPath());
}
- recordingFile = new FileConsumer(scope, file);
- Map<Object, Object> paramMap = new HashMap<Object, Object>();
- if (isAppend) {
- paramMap.put("mode", "append");
+ if (recordingFile == null) {
+ recordingFile = new FileConsumer(scope, file);
+ Map<Object, Object> paramMap = new HashMap<Object,
Object>();
+ if (isAppend) {
+ paramMap.put("mode", "append");
+ } else {
+ paramMap.put("mode", "record");
+ }
+ recordPipe.subscribe(recordingFile, paramMap);
+ recording = true;
} else {
- paramMap.put("mode", "record");
+ recordingFile.changeFile(file);
+ recordingFilename = filename;
}
- recordPipe.subscribe(recordingFile, paramMap);
- recording = true;
recordingFilename = filename;
}
_______________________________________________
osflash mailing list
[email protected]
http://osflash.org/mailman/listinfo/osflash_osflash.org