|
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 objectsWhen 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 eventsThe 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();
}
}
}
|
|
|