Server-side stream recording with Red5

This tutorial has been updated. Please check out the new post.

While most developers are content to record video using the NetStream.publish(“streamName”, “record”) API, it is sometimes useful to take FLV snippets from the publishing stream instead. In my opinion, this is one of the greatest features of Flash Media Server (FMS) and Red5. Utilizing this strategy allows the application developer to precisely control when and how much of the video is recorded. While recording via the NetStream function has been available in Red5 since the beginning, recording video from the server-side application has not. In this post, I’ll demonstrate Red5’s ability to record an FLV with a very simple pair of publish and subscribe flash applications.

I’ll jump right in by creating a simple ApplicationAdapter subclass. This step isn’t really necessary, however I feel that it is good practice when creating Red5 applications. As your application grows in functionality, you’ll have the most essential building block ready to go.

Application.java

public class Application extends ApplicationAdapter {
	
	...

	/**
	 * Delegate method used to accept/reject incoming connection requests.
	 * 
	 * @param conn
	 * @param params
	 * @return true/false
	 */
	@Override
	public boolean roomConnect(IConnection conn, Object[] params) {
		log.debug("New connection attempt from " + conn.getRemoteAddress() + "...");
		// Insure that the listeners are properly attached.
		return super.roomConnect(conn, params);
	}

	/**
	 * Delegate method which logs connection/client/user disconnections.
	 * 
	 * @param conn
	 */
	@Override
	public void roomDisconnect(IConnection conn) {
		log.debug("Connection closed by " + conn.getRemoteAddress() + "...");
		// Call the super class to insure that all listeners are properly
		// dismissed.
		super.roomDisconnect(conn);
	}
}

As you can see, there isn’t much to it, just a couple of logging statements to track new connections to the application. Next, I’ll define a simple service bean to handle the NetConnection calls to start and stop the stream recordings.

StreamManager.java

public class StreamManager {

	...

	/**
	 * Start recording the publishing stream for the specified
	 * IConnection.
	 * 
	 * @param conn
	 */
	public void recordShow(IConnection conn) {
		log.debug("Recording show for: " + conn.getScope().getContextPath());
		String streamName = String.valueOf(System.currentTimeMillis());
		// Get a reference to the current broadcast stream.
		ClientBroadcastStream stream = (ClientBroadcastStream) app.getBroadcastStream(
				conn.getScope(), "hostStream");
		try {
			// Save the stream to disk.
			stream.saveAs(streamName, false);
		} catch (Exception e) {
			log.error("Error while saving stream: " + streamName, e);
		}
	}

	/**
	 * Stops recording the publishing stream for the specified
	 * IConnection.
	 * 
	 * @param conn
	 */
	public void stopRecordingShow(IConnection conn) {
		log.debug("Stop recording show for: " + conn.getScope().getContextPath());
		// Get a reference to the current broadcast stream.
		ClientBroadcastStream stream = (ClientBroadcastStream) app.getBroadcastStream(
				conn.getScope(), "hostStream");
		// Stop recording.
		stream.stopRecording();
	}

	...
}

This is where the meat of the application lies. The broadcast client starts recording the stream by calling streamManager.recordShow() and stops it by calling streamManager.stopRecordingShow(). In the example broadcast application, I’ve added a button to the Stage and created a simple onClick function to invoke the server-side methods.

BroadcastController.as

        /**
	 * Handle the record button click events.
	 */
	private function onClick(ev:Object):Void {
		// Record the stream by triggering a server event.
		if (ev.target.label == "Record") {
			// Tell the remote server to start recording.
			conn.call("streamManager.recordShow", null);
			// Re-label the button.
			button.label = "Stop";
		// Stop recording the stream.
		} else if (ev.target.label == "Stop") {
			// Tell the remote server to stop recording.
			conn.call("streamManager.stopRecordingShow", null);
			// Re-label the button.
			button.label = "Record";
		}
	}

Take note, the name of the recorded stream differs from that of the publishing stream. I am using a simple timestamp as a unique name for each new FLV. It is even possible to reuse an existing FLV and append new video data to it. Simply change the second argument of the stream.saveAs() method call to true while reusing the same stream name. Check the streams directory of the recorder application for the output files. Reviewing the FLVs is as easy as opening them up with an FLV player[1].

While this is an interesting bit of functionality, the possibilities are much greater. Consider, for example, a security application that needs to record video when the camera detects movement and also needs to have a unique file for each incident. With some minor tweaks to this example, you’ve got exactly that. In addition, it would be quite easy to extend this application to extract a JPEG preview snapshot from each FLV using FFMPEG[2].

The example source code for this post can be downloaded here.

1. http://www.wimpyplayer.com Wimpy Desktop FLV Player is a free cross platform (Mac and PC) standalone Flash Video FLV player for you, which will allow you to watch your FLV and SWF videos from your desktop. Check it out.

2. http://www.ffmpeg.org/ FFmpeg is a complete solution to record, convert and stream audio and video. It includes libavcodec, the leading audio/video codec library. FFmpeg is developed under Linux, but it can compiled under most operating systems, including Windows.

Be Sociable, Share!

68 thoughts on “Server-side stream recording with Red5

  1. Carl Sziebert

    @Ian:

    Recording is a function of the server and is not supported directly by the flash client. Quality of the stream is simple a matter of the camera used and the capture settings specified when configuring the input source. I’d suggest that you review the livedocs for the Camera object.

  2. Y. Kamesh Rao

    Hi Carl,

    Thanks for the nice post…

    Actually we are facing a very strange problem, where we are unable to record multiple concurrent streams at our server. Once the server starts recording a stream, it doesn’t allow another simultaneous or subsequent request to start recording the new and seperate stream. One stream at a time recording works perfectly fine.

    Another problem/difference I noticed in my code was, at the server, roomConnect / roomDisconnect did not get called during every connection request from the flex component. But instead, appConnect / appDisconnect method get called. (If this makes any sense).

    We are stuck here since many days…Any help or pointer regarding this would be really helpful.

    Thanks in Advance,
    Y. Kamesh Rao

  3. Paul

    I deployed the Application and StreamManager to Red5 but red5 can’t load them. I have no idea why.
    I’ve compiled your project (with ant clean compile). I created a new directory called “recorder”/WEB-INF in the webapps folder in Red5. And I put in there the folder classes, lib and red5 configuration files (red5-web.properties, red5-web.xml, web.xml) that i modified so that they are correct. When I call the application localhost/recorder, I get a 503 error. It seems like it can’t load this class: “net.sziebert.tutorials.Application”. Because when I change this web handler to the red file Application Adapter org.red5.server.adapter.ApplicationAdapter I can reach the application.

    Have you got any idea why it can’t find this class? net.sziebert.tutorials.Application
    It’s in the folder: “red5directory/webapps/recorder/WEB-INF/classes/net/sziebert/tutorials/Application.class

    Thank you

  4. Carl Sziebert

    Paul:

    Not sure why it can’t load the classes. Could have something to do with the folder structure you’ve created. The easiest way to deploy the app is to copy the web directory to $RED5_HOME/webapps and rename it to recorder. Try that and let me know what the outcome is. If you have a stack trace that would be helpful in diagnosing your issues.

  5. Igor

    Hi Carl,

    I need to broadcast flv file, with ability to connect and disconnect users to stream. I can’t find any docs how to do this. Could you give me some directions or provide me some info about this issue. I would really appreciate it.

  6. GD.TV

    Can I stream with one streaming client and start/stop recording with a different application? Can RED5 accept 2 different connections?

  7. carl Post author

    Using the server-side stream recording technique described in the tutorial, it is completely possible to start/stop recording from a client that is not publishing the stream.

  8. George Vieira

    Carl,

    Thanx a lot for this demo. Great job.
    I have some problem, however, in the .flv recorder from the stream, the beggining of the video looks like is missing some images – probably it lacks de initial KeyFrame. Do you have some hint to fix this?

    Thank you

  9. Pingback: record streaming - StartTags.com

  10. Jaume (Barcelona)

    Hi Carl, i tried to understand the aim of broadcast recorder and i don’t know if usefull for me, but i’ll try to explain my idea.
    I want emule a radio online (only microphone), if is possible in live mode, but i dont know what i need.
    Broadcast recorder its ok for me?

    Thank u so much for your tutorial and your time.

  11. Matt

    Is there a way to record automatically every stream that is published? I’m using the latest FFMpeg to publish videos to red5 via RTMP, and there is no way I can invoke the “recordShow()” function…

  12. Pingback: Carl Sziebert » Server-side stream recording example updated

  13. calmchess

    In the following line app is never defined and throws an error every time. I’ve tried several ways to define app with something that will work but come up short every time…..what is app defined as? How do i resolve the null pointer exception that i’m receiving. I hope this thread is still active.

    ClientBroadcastStream stream = (ClientBroadcastStream) app.getBroadcastStream(conn.getScope(), “testStream”);

  14. Stir Zoltan

    Hello!

    I have tried to make use of your article, however I’m having some trouble recording videos. I’m using Red5 ver. 0.8 and oflaDemo on server-side. Videos record however, they seem to be broken. It keeps skipping frames, and jumping ahead on play(the frames are there, they are just skipped). Did you encounter anything like this?

Comments are closed.