Important
: Flash Player 11.5 and above integrates
the Adobe Access module, so the update step (calling
SystemUpdater.update(SystemUpdaterType.DRM)
)
is unnecessary. This includes the following browsers and platforms:
-
Flash Player 11.5 ActiveX control, for all platforms
except Internet Explorer on Windows 8 on Intel processors
-
Flash Player 11.5 plugin, for all browsers
-
Adobe AIR (desktop and mobile)
This means that the update step is
still required
in the
following cases:
-
Internet Explorer on Windows 8 on Intel processors
-
Flash Player 11.4 and below, except on Google Chrome 22 and
above (all platforms) or 21 and above (Windows)
Note:
You can still safely call
SystemUpdater.update(SystemUpdaterType.DRM)
on
a system with Flash Player 11.5 or higher, but nothing is downloaded.
The following high-level workflow shows that how an application
can retrieve and play protected content. The workflow assumes that
the application is designed specifically to play content protected
by Adobe Access:
-
Get the content metadata.
-
Handle updates to Flash Player, if needed.
-
Check if a license is available locally. If so, load it and
go to step 7. If not, go to step 4.
-
Check if authentication is required. If not, you can go to
step 7.
-
If authentication is required, get the authentication credentials
from the user and pass them to the license server.
-
If domain registration is required, join the domain (AIR
3.0 and higher).
-
Once authentication succeeds, download the license from the
server.
-
Play the content.
If an error has not occurred and the user was successfully authorized
to view the content, the NetStream object dispatches a DRMStatusEvent
object. The application then begins playback. The DRMStatusEvent
object holds the related voucher information, which identifies the
user’s policy and permissions. For example, it holds information
regarding whether the content can be made available offline or when
the license expires. The application can use this data to inform
the user of the status of their policy. For example, the application
can display the number of remaining days the user has for viewing
the content in a status bar.
If the user is allowed offline access, the voucher is cached,
and the encrypted content is downloaded to the user’s machine. The
content is made accessible for the duration defined in the license
caching duration. The
detail
property in the event
contains
"DRM.voucherObtained"
. The application
decides where to store the content locally in order for it to be
available offline. You can also preload vouchers using the DRMManager
class.
Note:
Caching and pre-loading of vouchers is supported in both
AIR and Flash Player. However, downloading and storing encrypted
content is supported only in AIR.
It is the application’s responsibility to explicitly handle the
error events. These events include cases where the user inputs valid
credentials, but the voucher protecting the encrypted content restricts
the access to the content. For example, an authenticated user cannot
access content if the rights have not been paid for. This case can
also occur when two registered members of the same publisher attempt
to share content that only one of them has paid for. The application
must inform the user of the error and provide an alternative suggestion.
A typical alternative suggestion is instructions in how to register
and pay for viewing rights.
Detailed API workflow
This workflow provides a more detailed view of the protected-content
workflow. This workflow describes the specific APIs used to play
content protected by Adobe Access.
-
Using a URLLoader object, load the bytes of the protected
content’s metadata file. Set this object to a variable, such as
metadata_bytes
.
All
content controlled by Adobe Access has Adobe Access metadata. When
the content is packaged, this metadata can be saved as a separate
metadata file (.metadata) alongside the content. For more information,
see the Adobe Access documentation.
-
Create a DRMContentData instance. Put this code into a try-catch
block:
new DRMContentData(
metadata_bytes
)
where
metadata_bytes
is
the URLLoader object obtained in step 1.
-
(Flash Player only) The runtime checks for the Adobe Access
module. If not found, an IllegalOperationError with DRMErrorEvent
error code 3344 or DRMErrorEvent error code 3343 is thrown.
To
handle this error, download the Adobe Access module using the SystemUpdater
API. After this module is downloaded, the SystemUpdater object dispatches
a COMPLETE event. Include an event listener for this event that returns
to step 2 when this event is dispatched. The following code demonstrates
these steps:
flash.system.SystemUpdater.addEventListener(Event.COMPLETE, updateCompleteHandler);
flash.system.SystemUpdater.update(flash.system.SystemUpdaterType.DRM)
private function updateCompleteHandler (event:Event):void {
/*redo step 2*/
drmContentData = new DRMContentData(metadata_bytes);
}
If the player itself must be updated, a status
event is dispatched. For more information on handling this event,
see
Listening for an update event
.
Note:
In AIR applications, the AIR
installer handles updating the Adobe Access module and required
runtime updates.
-
Create listeners to listen for the DRMStatusEvent and DRMErrorEvent dispatched
from the DRMManager object:
DRMManager.addEventListener(DRMStatusEvent.DRM_STATUS, onDRMStatus);
DRMManager.addEventListener(DRMErrorEvent.DRM_ERROR, onDRMError);
In
the DRMStatusEvent listener, check that the voucher is valid (not
null). In the DRMErrorEvent listener, handle DRMErrorEvents. See
Using the DRMStatusEvent class
and
Using the DRMErrorEvent class
.
-
Load the voucher (license) that is required to play the content.
First,
try to load a locally stored license to play the content:
DRMManager.loadvoucher(drmContentData, LoadVoucherSetting.LOCAL_ONLY)
After
loading completes, the DRMManager object dispatches
DRMStatusEvent.DRM_Status
.
-
If the DRMVoucher object is not null, the voucher is valid.
Skip to step 13.
-
If the DRMVoucher object is null, check the authentication
method required by the policy for this content. Use the
DRMContentData.authenticationMethod
property.
-
If the authentication method is
ANONYMOUS
,
go to step 13.
-
If the authentication method is
USERNAME_AND_PASSWORD
,
your application must provide a mechanism to let the user enter
credentials. Pass these credentials to the license server to authenticate
the user:
DRMManager.authenticate(metadata.serverURL, metadata.domain, username, password)
The
DRMManager dispatches a
DRMAuthenticationErrorEvent
if authentication
fails or a
DRMAuthenticationCompleteEvent
if authentication
succeeds. Create listeners for these events.
-
If the authentication method is
UNKNOWN
,
a custom authentication method must be used. In this case, the content
provider has arranged for authentication to be done in an out-of-band
manner by not using the ActionScript 3.0 APIs. The custom authentication
procedure must produce an authentication token that can be passed
to the
DRMManager.setAuthenticationToken()
method.
-
If authentication fails, your application must return to
step 9. Ensure that your application has a mechanism to handle and
limit repeated authentication failures. For example, after three
attempts, you display a message to the user indicating the authentication
has failed and content cannot be played.
-
To use the stored token instead of prompting the user to
enter credentials, set the token with
DRMManager.setAuthenticationToken()
method. You
then download the license from the license server and play content
as in step 8.
-
(optional) If authentication succeeds, you can capture the
authentication token, which is a byte array cached in memory. Get
this token with the
DRMAuthenticationCompleteEvent.token
property.
You can store and use the authentication token so that the user
does not have to repeatedly enter credentials for this content.
The license server determines the valid period of the authentication
token.
-
If authentication succeeds, download the license from the
license server:
DRMManager.loadvoucher(drmContentData, LoadVoucherSetting.FORCE_REFRESH)
After
loading completes, the DRMManager object dispatches DRMStatusEvent.DRM_STATUS.
Listen for this event, and when it is dispatched, you can play the
content.
-
Play the video by creating a NetStream object and then calling
its
play()
method:
stream = new NetStream(connection);
stream.addEventListener(DRMStatusEvent.DRM _STATUS, drmStatusHandler);
stream.addEventListener(DRMErrorEvent.DRM_ERROR, drmErrorHandler);
stream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
stream.client = new CustomClient();
video.attachNetStream(stream);
stream.play(videoURL);
DRMContentData and session objects
When
DRMContentData
is created, it will
be used as a session object that refers to the Flash Player DRM
module. All the
DRMManager
APIs that receives this
DRMContentData
will
use that particular DRM module. However, there are 2
DRMManager
APIs
that does not use
DRMContentData
. They are:
-
authenticate()
-
setAuthenticationToken()
Since there is no
DRMContentData
associated,
invoking these
DRMManager
APIs will use the latest
DRM module from the disk. This may become a problem if an update
of the DRM module happens in the middle of the application’s DRM workflow.
Consider the following scenario:
-
The application creates
a
DRMContentData
object
contentData1
,
which uses
AdobeCP1
as the DRM module.
-
The application invokes the
DRMManager.authenticate(contentData1.serverURL,...)
method.
-
The application invokes the
DRMManager.loadVoucher(contentData1, ...)
method.
If an update happens for the DRM module before the application
can get to step 2, then the
DRMManager.authenticate()
method
will end up authenticating using
AdobeCP2
as the DRM module.
The
loadVoucher()
method in step 3 will fail since
it is still using
AdobeCP1
as the DRM module. The update
may have happened due to another application invoking the DRM module update.You
can avoid this scenario by invoking the DRM module update on application
startup.
DRM-related events
The runtime dispatches numerous events when
an application attempts to play protected content:
-
DRMDeviceGroupErrorEvent (AIR only), dispatched by
DRMManager
-
DRMAuthenticateEvent (AIR only), dispatched by NetStream
-
DRMAuthenticationCompleteEvent, dispatched by DRMManager
-
DRMAuthenticationErrorEvent, dispatched by DRMManager
-
DRMErrorEvent, dispatched by NetStream and DRMManager
-
DRMStatusEvent, dispatched by NetStream and DRMManager
-
StatusEvent
-
NetStatusEvent. See
Listening for an update event
To support content protected by Adobe Access, add event listeners
for handling the DRM events.
Pre-loading vouchers for offline playback
You can preload the vouchers
(licenses) required to play content protected by Adobe Access. Pre-loaded
vouchers allow users to view the content whether they have an active
Internet connection. (The preload process itself requires an Internet
connection.) You can use the NetStream class
preloadEmbeddedMetadata()
method
and the DRMManager class to preload vouchers. In AIR 2.0 and later,
you can use a DRMContentData object to preload vouchers directly.
This technique is preferable because it lets you update the DRMContentData
object independent of the content. (The
preloadEmbeddedData()
method
fetches DRMContentData from the content.)
Using DRMContentData
The following steps describe the workflow for pre-loading the
voucher for a protected media file using a DRMContentData object.
-
Get the binary metadata for the packaged content. If
using the Adobe Access Java Reference Packager, this metadata file
is automatically generated with a
.metadata
extension. You
could, for example, download this metadata using the URLLoader class.
-
Create a DRMContentData object, passing the metadata to the
constructor function:
var drmData:DRMContentData = new DRMContentData( metadata );
-
The rest of the steps are identical to the workflow described
in
Understanding the protected content workflow
.
Using preloadEmbeddedMetadata()
The following steps describe the workflow for pre-loading the
voucher for a DRM-protected media file using
preloadEmbeddedMetadata()
:
-
Download and store the media file. (DRM metadata can
only be pre-loaded from locally stored files.)
-
Create the NetConnection and NetStream objects, supplying
implementations for the
onDRMContentData()
and
onPlayStatus()
callback functions
of the NetStream client object.
-
Create a NetStreamPlayOptions object and set the
stream
property
to the URL of the local media file.
-
Call the NetStream
preloadEmbeddedMetadata()
,
passing in the NetStreamPlayOptions object identifying the media
file to parse.
-
If the media file contains DRM metadata, then the
onDRMContentData()
callback
function is invoked. The metadata is passed to this function as
a DRMContentData object.
-
Use the DRMContentData object to obtain the voucher using
the DRMManager
loadVoucher()
method.
If the
value of the
authenticationMethod
property of the
DRMContentData
object
is
flash.net.drm.AuthenticationMethod.USERNAME_AND_PASSWORD
,
authenticate the user on the media rights server before loading
the voucher. The
serverURL
and
domain
properties
of the DRMContentData object can be passed to the DRMManager
authenticate()
method,
along with the user’s credentials.
-
The
onPlayStatus()
callback function is
invoked when file parsing is complete. If the
onDRMContentData()
function
has not been called, the file does not contain the metadata required
to obtain a voucher. This missing call also possibly means that
Adobe Access does not protect this file.
The following code example for AIR illustrates how to preload
a voucher for a local media file:
package
{
import flash.display.Sprite;
import flash.events.DRMAuthenticationCompleteEvent;
import flash.events.DRMAuthenticationErrorEvent;
import flash.events.DRMErrorEvent;
import flash.ev ents.DRMStatusEvent;
import flash.events.NetStatusEvent;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.net.NetStreamPlayOptions;
import flash.net.drm.AuthenticationMethod;
import flash.net.drm.DRMContentData;
import flash.net.drm.DRMManager;
import flash.net.drm.LoadVoucherSetting;
public class DRMPreloader extends Sprite
{
private var videoURL:String = "app-storage:/video.flv";
private var userName:String = "user";
private var password:String = "password";
private var preloadConnection:NetConnection;
private var preloadStream:NetStream;
private var drmManager:DRMManager = DRMManager.getDRMManager();
private var drmContentData:DRMContentData;
public function DRMPreloader():void {
drmManager.addEventListener(
DRMAuthenticationCompleteEvent.AUTHENTICATION_COMPLETE,
onAuthenticationComplete);
drmManager.addEventListener(DRMAuthenticationErrorEvent.AUTHENTICATION_ERROR,
onAuthenticationError);
drmManager.addEventListener(DRMStatusEvent.DRM_STATUS, onDRMStatus);
drmManager.addEventListener(DRMErrorEvent.DRM_ERROR, onDRMError);
preloadConnection = new NetConnection();
preloadConnection.addEventListener(NetStatusEvent.NET_STATUS, onConnect);
preloadConnection.connect(null);
}
private function onConnect( event:NetStatusEvent ):void
{
preloadMetadata();
}
private function preloadMetadata():void
{
preloadStream = new NetStream( preloadConnection );
preloadStream.client = this;
var options:NetStreamPlayOptions = new NetStreamPlayOptions();
options.streamName = videoURL;
preloadStream.preloadEmbeddedData( options );
}
public function onDRMContentData( drmMetadata:DRMContentData ):void
{
drmContentData = drmMetadata;
if ( drmMetadata.authenticationMethod == AuthenticationMethod.USERNAME_AND_PASSWORD )
{
authenticateUser();
}
else
{
getVoucher();
}
}
private function getVoucher():void
{
drmManager.loadVoucher( drmContentData, LoadVoucherSetting.ALLOW_SERVER );
}
private function authenticateUser():void
{
drmManager.authenticate( drmContentData.serverURL, drmContentData.domain, userName, password );
}
private function onAuthenticationError( event:DRMAuthenticationErrorEvent ):void
{
trace( "Authentication error: " + event.errorID + ", " + event.subErrorID );
}
private function onAuthenticationComplete( event:DRMAuthenticationCompleteEvent ):void
{
trace( "Authenticated to: " + event.serverURL + ", domain: " + event.domain );
getVoucher();
}
private function onDRMStatus( event:DRMStatusEvent ):void
{
trace( "DRM Status: " + event.detail);
trace("--Voucher allows offline playback = " + event.isAvailableOffline );
trace("--Voucher already cached = " + event.isLocal );
trace("--Voucher required authentication = " + !event.isAnonymous );
}
private function onDRMError( event:DRMErrorEvent ):void
{
trace( "DRM error event: " + event.errorID + ", " + event.subErrorID + ", " + event.text );
}
public function onPlayStatus( info:Object ):void
{
preloadStream.close();
}
}
}
|
|
|