Adding DVR features to live video

About DVR support

Note: Flash Media Streaming Server does not support DVR functionality.

A DVR (digital video recorder) lets viewers pause live video and resumes playback from the paused location. Viewers can also rewind a live event, play the recorded section, and seek to the live section again. Flash Media Server 3.5 adds support for DVR features. Just write a few lines of code and a video player can act like a DVR. Examples of DVR applications are instant replay and “catch-up” services.

Note: Adobe is updating the FLVPlayback component to support DVR features. Check the Flash Media Server Help and Support Center at www.adobe.com/go/learn_fms_docs_en to see if the component is available.

Publishing video for DVR playback

When you play a stream with DVR features, you do not play a live stream, you play a recorded stream. When you’re viewing the stream “live”, you’re really viewing the recorded stream just after it was recorded.

To publish a stream for a DVR video player from a client, use the following code in your SWF file or AIR application:

NetStream.publish("myvideo", "record") 
NetStream.publish("myvideo", "append")

To publish a stream for a DVR video player from the server, use the following server-side code:

Stream.record("record")

The Stream.record() method has two new parameters, maxDuration and maxSize, that let you specify the maximum length and file size of a stream. The following code publishes a stream with a maximum recording length of 10 mins (600 seconds) and an unlimited file size:

Stream.record("record", 600, -1)

To subscribe to a stream published for a DVR video player, use the following code:

NetStream.play("myvideo", 0, -1) 

The previous code allows viewers joining an event late to view from the beginning.

To return to the beginning of a stream at any time, call the following:

NetStream.seek(0)

To start recording in the middle of an event, call the Server-Side ActionScript Stream.record() method. Calling this method lets you start and stop recording at any time.

To create a button that seeks to the latest available part of the recording (which is considered “live”), seek to a large number, for example:

NetStream.seek(1000000)

Using Flash Media Live Encoder to capture video for DVR playback

You can use Flash Media Live Encoder 3 to capture video for DVR playback. Earlier versions of Flash Media Live Encoder do not support recording to the server. For more information, see http://www.adobe.com/go/fme.

Example: Custom capture, publish, and DVR playback

This example is a client application that does the following:

  • Captures and encodes video.

  • Displays the video as it’s captured.

  • Streams video from the client to Flash Media Server.

  • Streams video from Flash Media Server back to the client.

  • Displays the video streamed from the server in a player that lets you rewind and pause live video.

Note: To test this code, create a RootInstall/applications/dvr folder on the server. Open the RootInstall/documentation/samples/dvr/DVR.swf file to connect to the application.
  1. On Flash Media Server, create a RootInstall/applications/dvr folder.

  2. In Flash, create an ActionScript file and save it as DVR.as.

  3. Copy and paste the following code into the Script window:

    package { 
        import flash.display.MovieClip; 
        import flash.net.NetConnection; 
        import flash.events.*;  
        import flash.net.NetStream; 
        import flash.media.Video; 
        import flash.media.Camera; 
        import flash.media.Microphone; 
        import fl.controls.Button; 
        public class DVR extends MovieClip 
        { 
            private var nc:NetConnection; 
            private var ns:NetStream; 
            private var nsPlayer:NetStream; 
            private var vid:Video; 
            private var vidPlayer:Video; 
            private var cam:Camera; 
            private var mic:Microphone; 
            private var pauseBtn:Button; 
            private var rewindBtn:Button; 
            private var playBtn:Button; 
            private var dvrFlag:Boolean; 
             
            public function DVR() 
            { 
                nc = new NetConnection(); 
                nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus); 
                nc.connect("rtmp://localhost/dvr"); 
                setupButtons(); 
                dvrFlag = true; 
            } 
            private function onNetStatus(event:NetStatusEvent):void{ 
                trace(event.info.code); 
                switch(event.info.code){ 
                    case "NetConnection.Connect.Success": 
                        publishCamera(); 
                        displayPublishingVideo(); 
                        displayPlaybackVideo(); 
                        break; 
                    case "NetStream.Play.Start": 
                        trace("dvrFlag " + dvrFlag); 
                        if(dvrFlag){ 
                            nsPlayer.seek(1000000); 
                            dvrFlag = false; 
                        }      
                        break; 
                } 
            }     
             
            private function onAsyncError(event:AsyncErrorEvent):void{ 
                    trace(event.text); 
            } 
             
            private function onClick(event:MouseEvent):void { 
                switch(event.currentTarget){ 
                    case rewindBtn: 
                        nsPlayer.seek(nsPlayer.time - 5); 
                        break; 
                    case pauseBtn: 
                        nsPlayer.pause(); 
                        break; 
                    case playBtn: 
                        nsPlayer.resume(); 
                        break; 
                } 
               
            } 
             
            private function publishCamera() { 
                cam = Camera.getCamera(); 
                mic = Microphone.getMicrophone(); 
                ns = new NetStream(nc); 
                ns.client = new CustomClient(); 
                ns.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus); 
                ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError); 
                ns.attachCamera(cam); 
                ns.attachAudio(mic); 
                ns.publish("myvideo", "record"); 
            } 
            private function displayPublishingVideo():void { 
                vid = new Video(cam.width, cam.height); 
                vid.x = 10; 
                vid.y = 10; 
                vid.attachCamera(cam); 
                addChild(vid);  
            } 
            private function displayPlaybackVideo():void{ 
                nsPlayer = new NetStream(nc); 
                nsPlayer.client = new CustomClient();             
                nsPlayer.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus); 
                nsPlayer.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError); 
                nsPlayer.play("myvideo", 0, -1); 
                vidPlayer = new Video(cam.width, cam.height); 
                vidPlayer.x = cam.width + 20; 
                vidPlayer.y = 10; 
                vidPlayer.attachNetStream(nsPlayer); 
                addChild(vidPlayer); 
            } 
             
            private function setupButtons():void { 
                rewindBtn = new Button(); 
                pauseBtn = new Button(); 
                playBtn = new Button(); 
                 
                rewindBtn.width = 50; 
                pauseBtn.width = 50; 
                playBtn.width = 50; 
                rewindBtn.move(180,150); 
                pauseBtn.move(235,150); 
                playBtn.move(290,150); 
                rewindBtn.label = "Rewind"; 
                pauseBtn.label = "Pause"; 
                playBtn.label = "Play"; 
                
                rewindBtn.addEventListener(MouseEvent.CLICK, onClick); 
                pauseBtn.addEventListener(MouseEvent.CLICK, onClick); 
                playBtn.addEventListener(MouseEvent.CLICK, onClick); 
                addChild(rewindBtn); 
                addChild(pauseBtn); 
                addChild(playBtn); 
            } 
        } 
    } 
    class CustomClient { 
        public function onMetaData(info:Object):void { 
               trace("metadata: duration=" + info.duration); 
        } 
    }
  4. Save the DVR.as file.

  5. Choose File > New > Flash File (ActionScript 3.0) and click OK.

  6. Save the file as DVR.fla in the same folder as the DVR.as file.

  7. Open the Components Panel, drag a Button to the Stage, and delete it.

    This action adds the button to the Library. The button is added to the application at runtime.

  8. Choose File > Publish Settings. Click the Flash tab. Click Script Settings and enter DVR as the Document class. Click the checkmark to validate the path.

  9. Save the file and choose Control > Test Movie to run the application.

Limiting the size and duration of recordings

You can limit the maximum size and duration of recordings using parameters in the Application.xml configuration file, Server-Side ActionScript, and the Authorization plug-in. Set these values to prevent using excessive disk space. The following are the Application.xml file parameters:

XML element

Description

Application/StreamManager/Recording/MaxDuration

The maximum duration of a recording, in seconds. The default value is -1, which enforces no maximum duration.

Application/StreamManager/Recording/MaxDurationCap

The maximum duration of a recording cap, in seconds. The default value is -1, which enforces no cap on maximum duration.

The server-side Stream.record() method cannot override this value. The Authorization plug-in can override this value.

Application/StreamManager/Recording/MaxSize

The maximum size of a recording, in kilobytes. The default value is -1, which enforces no maximum size.

Application/StreamManager/Recording/MaxSizeCap

The maximum size of a recording cap, in kilobytes. The default value is -1, which enforces no cap on maximum size.

The server-side Stream.record() method cannot override this value. The Authorization plug-in can override this value.

In addition to setting maximum values, in the Application.xml file you can set maximum cap values. Server-side scripts cannot override these caps. CDNs can use these caps to set a limit that clients cannot override.

Note: The Authorization plug-in can override any values set in the Application.xml file.

To set values in Server-Side ActionScript, call Stream.record() and pass values for the maxDuration and maxSize parameters. The following code limits the recording to 5 minutes and sets an unlimited maximum file size (up to the value of MaxSizeCap):

s.record("record", 300, -1);

The server truncates recordings greater than MaxCapSize and MaxCapDuration.

Scaling DVR applications

To build large-scale applications, use the server-side NetConnection class to chain multiple servers together. In this scenario, a client can request a stream that does not reside on the server to which it is connected. Use the server-side ProxyStream class to create a look-up mechanism for finding streams in the server chain.

You can set values in the Vhost.xml configuration file to configure the disk cache that holds the streams:

XML element

Attribute

Description

VirtualHost/Proxy/CacheDir

enabled

Determines whether the disk cache is enabled.

 

useAppDir

Specifies whether to separate the cache subdirectories by application.

VirtualHost/Proxy/CacheDir/Path

 

The root directory of the disk cache.

VirtualHost/Proxy/CacheDir/MaxSize

 

The maximum size of the disk in gigabytes. The default value is 32. The value 0 disables the disk cache. The value -1 specifies no maximum value.

VirtualHost/Proxy/RequestTimeout

 

The maximum amount of time to wait for a response to a request (for metadata, content, and so on) from an upstream server., in seconds. The default value is 2 seconds.

If a server has multiple virtual hosts, point each virtual host to its own cache directory.

If the server runs out of disk space on an intermediate or edge server while writing to the CacheDir, it logs the following warning message in the core.xx.log for each segment that fails to write to disk:I/O Failed on cached stream file C:\Program Files\Adobe\Flash Media Server 3.5\cache\streams\00\proxyapp\10.192.16.125\C\Program Files\Adobe\Flash Media Server 3.5_361\applications\primaryapp\streams\_definst_\sample1_1500kbps.f4v\0000000000000000 during write: 28 No space left on device.

Logging

Streams played in a DVR video player are played as recorded streams. These streams log the same events in the log files as every recorded stream.