|
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.
Using DVR with dynamic streaming
To use DVR with a dynamic streaming application, enable absolute
time code on the server. Absolute time code keeps DVR-enabled, multi-bitrate
streams synchronized at the server.
The encoder must use absolute time code. Flash Media Live Encoder
supports absolute time code, Flash Player does not. Third-party
encoders also use absolute time code. Contact your encoder vendor
to learn how to enable absoulte time code for your encoder.
Important: The streams must be in sync, the server
does not sync the streams. Absolute time code ensures that the
server captures all the information required to keep encoder-synchronized
streams in sync.
Enable absolute time codeEnable absolute time code in the Application.xml configuration
file at the application level. When enabled, the server assumes
that incoming live streams have timestamps that are based on an
absolute clock, such as a SMPTE time signal contained within the
encoder's input source. The default value is false.
Create a new text or XML file and save it to the application’s
folder as “Application.xml”.
Note: For an example of an application-level
Application.xml file, open rootinstall/applications/vod/Application.xml
in a text editor.
Add the following to the XML file:
<Application>
<StreamManager>
<Live>
<AssumeAbsoluteTime>true</AssumeAbsoluteTime>
</Live>
</StreamManager>
</Application
Save the file.
For more information about working with configuration files,
see Configuring
a single application.
Publish and record streams for DVR with dynamic streamingTo publish and record DVR streams with dynamic streaming,
pass "appendWithGap" to the NetStream.publish() or Stream.record() methods.
When you publish or record a stream in "appendWithGap" mode,
the recorded stream preserves gaps created when a stream stops and
restarts. In "append" mode, the server eliminates
the gaps which can cause streams to lose sync. Gaps can occur when
an encoder goes offline and comes back online. Gaps are visible
to the client when a recorded file is played.
Publishing, playing, and seeking DVR videoTo publish a stream for a DVR video player from a client,
use "record" or "append" flags,
as in the following:
NetStream.publish("myvideo", "record")
NetStream.publish("myvideo", "append")
To publish a stream for a DVR video player from the server, call Stream.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)
When a client plays a stream published for a DVR video player,
they don’t play a live stream, they play a recorded stream. When
a client views the stream “live”, they’re really viewing the recorded
stream just after it was recorded.
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 the duration
of the stream (the length of the data recorded on the server) minus
the value of NetStream.bufferTime. Subtract bufferTime to
make playback as close to instanteous as possible. Flash Player
doesn’t resume playback until the buffer is full.
To calculate a stream's duration, set a time stamp in the onMetaData callback function.
public function onMetaData(info:Object):void{
trace("metadata:duration=" + info.duration);
duration = info.duration;
trace("stamp: " + stamp);
stamp = getTimer();
}
To calculate the value to pass to the seek() method,
calculate the current duration, and subtract the bufferTime minus
an additional 2 seconds for safety.
private function getSeekToLiveValue():uint{
currentDuration = Number((getTimer()-stamp)/1000) + duration;
trace("currentDuration: " + currentDuration);
seekVal = (currentDuration - nsPlayer.bufferLength) - 2;
return seekVal;
}
To seek to “live”, call the getSeekToLiveValue() function
on the playback NetStream object:
private function onClick(event:MouseEvent):void {
switch(event.currentTarget){
case rewindBtn:
nsPlayer.seek(nsPlayer.time - 5);
break;
case seekBtn:
trace("seekToEndValue " + getSeekToEndValue());
nsPlayer.seek(getSeekToLiveValue());
break;
}
}
Note: The previous code is in the DVR sample in the rootinstall/documentation/samples/dvr
folder on the server.
Using Flash Media Live Encoder to capture video for DVR playbackYou 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 playbackThis 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.
On Flash Media Server, create a RootInstall/applications/dvr
folder.
In Flash, create an ActionScript file and save it as DVR.as.
Copy and paste the following code into the Script window:
package {
import flash.display.MovieClip;
import flash.utils.getTimer;
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 seekBtn:Button;
private var dvrFlag:Boolean;
private var stamp:uint;
private var duration:uint;
private var currentDuration:uint;
private var seekVal;uint;
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(getSeekToLiveValue());
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;
case seekBtn:
nsPlayer.seek(getSeekToLiveValue());
break;
}
}
private function publishCamera() {
cam = Camera.getCamera();
mic = Microphone.getMicrophone();
ns = new NetStream(nc);
ns.client = this;
ns.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
ns.attachCamera(cam);
ns.attachAudio(mic);
ns.publish("video", "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 = this;
nsPlayer.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
nsPlayer.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
nsPlayer.play("video", 0, -1);
vidPlayer = new Video(cam.width, cam.height);
vidPlayer.x = cam.width + 20;
vidPlayer.y = 10;
vidPlayer.attachNetStream(nsPlayer);
addChild(vidPlayer);
}
private function getSeekToLiveValue():uint{
currentDuration = Number((getTimer()-stamp)/1000) + duration;
trace("currentDuration: " + currentDuration);
seekVal = (currentDuration - nsPlayer.bufferTime) - 2;
trace("seekVal: " + seekVal);
return seekVal;
}
private function setupButtons():void {
rewindBtn = new Button();
pauseBtn = new Button();
playBtn = new Button();
seekBtn = new Button();
rewindBtn.width = 52;
pauseBtn.width = 52;
playBtn.width = 52;
seekBtn.width = 100;
rewindBtn.move(180,150);
pauseBtn.move(235,150);
playBtn.move(290,150);
seekBtn.move(345, 150);
rewindBtn.label = "Rew 5s";
pauseBtn.label = "Pause";
playBtn.label = "Play";
seekBtn.label = "Seek to Live";
rewindBtn.addEventListener(MouseEvent.CLICK, onClick);
pauseBtn.addEventListener(MouseEvent.CLICK, onClick);
playBtn.addEventListener(MouseEvent.CLICK, onClick);
seekBtn.addEventListener(MouseEvent.CLICK, onClick);
addChild(rewindBtn);
addChild(pauseBtn);
addChild(playBtn);
addChild(seekBtn);
}
public function onMetaData(info:Object):void {
trace("metadata:duration = " + info.duration);
stamp = getTimer();
trace("stamp: " + stamp);
duration = info.duration;
}
}
}
Save the DVR.as file.
Choose File > New > Flash File (ActionScript
3.0) and click OK.
Save the file as DVR.fla in the same folder as the DVR.as
file.
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.
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.
Save the file and choose Control > Test Movie
to run the application.
Limiting the size and duration of recordingsYou 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 applicationsTo 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.
LoggingStreams 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.
|
|
|