了解受保护的内容工作流程

Flash Player 10.1 和更高版本,Adobe AIR 2.0 和更高版本

以下高级工作流程显示应用程序如何检索并播放受保护的内容。该工作流程假设应用程序专门设计用于播放受 Flash Access 保护的内容:

  1. 获取内容元数据。

  2. 处理对 Flash Player 的更新(如果需要)。

  3. 检查许可证在本地是否可用。如果可用,则加载该许可证并转到步骤 7。否则,转到步骤 4。

  4. 检查是否需要进行身份验证。如果不需要,可以转到步骤 7。

  5. 如果需要进行身份验证,则从用户处获取身份验证凭据并传递给许可证服务器。

  6. 一旦身份验证成功,即可从服务器下载许可证。

  7. 播放内容。

如果未出错并且成功授权用户查看内容,NetStream 对象将调度 DRMStatusEvent 对象。应用程序将开始回放。DRMStatusEvent 对象保留相关凭证信息,这些信息用于标识用户的策略和权限。例如,将保留有关是否可以脱机使用内容或许可证何时到期的信息。应用程序可以使用此数据来通知用户其策略的状态。例如,应用程序可在状态栏上显示用户可以观看相应内容的剩余天数。

(仅限 AIR)如果允许用户脱机访问,则将缓存凭证,并且将加密的内容下载到用户的计算机上。在脱机租用期间定义的持续时间内可以访问该内容。事件中的 detail 属性包含"DRM.voucherObtained"。应用程序决定内容的本地存储位置,以使内容可脱机使用。您还可以使用 DRMManager 类预加载凭证。

所有与 DRM 相关的错误都会导致应用程序调度 DRMErrorEvent 事件对象,或者在 AIR 中调度 DRMAuthenticationErrorEvent 对象。应用程序负责显式处理所有其他错误事件。这些事件包括以下情况:用户输入有效凭据,但是保护加密内容的凭证限制对内容的访问。例如,如果未针对权限支付费用,已经过身份验证的用户将无法访问该内容。当同一发布者的两个注册成员试图共享仅其中一人付费的内容时,也可能发生这种情况。应用程序应通知用户该错误,并提供替代建议。典型的替代建议是提供有关如何注册查看权限并进行付费的说明。

详细的 API 工作流程

此工作流程提供了保护内容工作流程的更详细视图。此工作流程介绍了用于播放受 Flash Access 保护的内容的特定 API。

  1. 使用 URLLoader 对象,加载受保护内容的元数据文件的字节。将此对象设置为变量,例如 metadata_bytes

    由 Flash Access 控制的所有内容都包含 Flash Access 元数据。打包内容时,可同时将此元数据另存为一个单独的元数据文件 (.metadata)。有关详细信息,请参阅 Flash Access 文档。

  2. 创建 DRMContentData 实例。将此代码置于 try-catch 块中:

    new DRMContentData(metadata_bytes)

    其中 metadata_bytes 是步骤 1 中获取的 URLLoader 对象。

  3. (仅限 Flash Player)运行时检查 Flash Access 模块。如果找不到,则会引发错误代码为 3344 的 IllegalOperationError。

    要处理此错误,请使用 SystemUpdater API 下载 Flash Access 模块。下载此模块后,SystemUpdater 对象将调度 COMPLETE 事件。调度此事件时,包括将返回到步骤 2 的此事件的事件侦听器。下面的代码演示了这些步骤:

    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); 
    } 

    如果必须更新播放器本身,则会调度状态事件。有关处理此事件的详细信息,请参阅侦听更新事件

    注: 在 AIR 应用程序中,AIR 安装程序将处理更新 Flash Access 模块和所需的运行时更新。
  4. 创建侦听器以侦听从 DRMManager 对象调度的 DRMStatusEvent 和 DRMErrorEvent:

    DRMManager.addEventListener(DRMStatusEvent.DRM_STATUS, onDRMStatus); 
    DRMManager.addEventListener(DRMErrorEvent.DRM_ERROR, onDRMError);

    在 DRMStatusEvent 侦听器中,检查凭证是否有效(不是 null)。在 DRMErrorEvent 侦听器中,处理 DRMErrorEvent。请参阅使用 DRMStatusEvent 类使用 DRMErrorEvent 类

  5. 加载播放内容所需的凭证(许可证)。

    首先,尝试加载本地存储的许可证以播放内容:

    DRMManager.loadvoucher(drmContentData, LoadVoucherSetting.LOCAL_ONLY)

    加载完成后,DRMManager 对象将调度 DRMStatusEvent.DRM_Status

  6. 如果 DRMVoucher 对象不是 null,则凭证有效。跳到步骤 13.

  7. 如果 DRMVoucher 对象为 null,请检查此内容的策略所需的身份验证方法。使用 DRMContentData.authenticationMethod 属性。

  8. 如果身份验证方法是 ANONYMOUS,请转到步骤 13。

  9. 如果身份验证方法是 USERNAME_AND_PASSWORD,您的应用程序必须提供一个允许用户输入凭据的机制。将这些凭据传递给许可证服务器以对用户进行身份验证:

    DRMManager.authenticate(metadata.serverURL, metadata.domain, username, password)

    如果身份验证失败,DRMManager 调度 DRMAuthenticationErrorEvent,如果身份验证成功,则调度 DRMAuthenticationCompleteEvent。为这些事件创建侦听器。

  10. 如果身份验证失败,您的应用程序必须返回步骤 9。确保您的应用程序具有处理和限制重复的身份验证失败次数的机制。例如,经过三次尝试后,向用户显示一则消息,指示身份验证失败,无法播放内容。

  11. 如果身份验证成功,则从许可证服务器下载许可证:

    DRMManager.loadvoucher(drmContentData, LoadVoucherSetting.FORCE_REFRESH)

    加载完成后,DRMManager 对象调度 DRMStatusEvent.DRM_STATUS。侦听此事件,在调度该事件时,您可以播放内容。

  12. (可选)如果身份验证成功,您可以捕获身份验证令牌,该令牌是在内存中缓存的字节数组。获取此令牌,其属性为 DRMAuthenticationCompleteEvent.token。您可以存储并使用该身份验证令牌,以便用户不必为此内容重复输入凭据。许可证服务器确定身份验证令牌的有效期。

    要使用存储的令牌,而不是提示用户输入凭据,请使用 DRMManager.setAuthenticationToken() 方法设置该令牌。然后,从许可证服务器下载许可证并按照步骤 8 中的说明播放内容。

  13. 创建 NetStream 对象,然后调用其 play() 方法以播放视频:

    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); 

与 DRM 相关的事件

应用程序尝试播放保护的内容时,运行时会调度许多事件:

  • DRMAuthenticateEvent(仅限 AIR),由 NetStream 调度

  • DRMAuthenticationCompleteEvent,由 DRMManager 调度

  • DRMAuthenticationErrorEvent,由 DRMManager 调度

  • DRMErrorEvent,由 NetStream 和 DRMManager 调度

  • DRMStatusEvent,由 NetStream 和 DRMManager 调度

  • StatusEvent

  • NetStatusEvent。请参阅侦听更新事件

要支持 Flash Access 保护的内容,请添加处理 DRM 事件的事件侦听器。

预加载用于脱机播放的凭证

您可以预加载播放 Flash Access 保护的内容所需的凭证(许可证)。通过预加载的凭证,无论是否有活动的 Internet 连接,用户都可以查看内容。(预加载过程本身需要 Internet 连接。)您可以使用 NetStream 类 preloadEmbeddedMetadata() 方法和 DRMManager 类预加载凭证。在 AIR 2.0 和更高版本中,您可以使用 DRMContentData 对象直接预加载凭证。此技术更可取,因为它允许您更新与内容独立的 DRMContentData 对象。(preloadEmbeddedData() 方法从内容中获取 DRMContentData。)

使用 DRMContentData

下列步骤介绍了使用 DRMContentData 对象为保护的媒体文件预加载凭证的工作流程。

  1. 获取打包内容的二进制元数据。如果使用的是 Flash Access Java Reference 包装程序,则会自动生成带有 .metadata 扩展名的元数据文件。例如,可以使用 URLLoader 类下载此元数据。

  2. 创建一个 DRMContentData 对象,将此元数据传递给构造函数:

    var drmData:DRMContentData = new DRMContentData( metadata );
  3. 其余步骤与了解受保护的内容工作流程中介绍的工作流程相同。

使用 preloadEmbeddedMetadata()

下列步骤介绍了使用 preloadEmbeddedMetadata() 为 DRM 保护的媒体文件预加载凭证的工作流程:

  1. 下载并存储媒体文件。(只能从本地存储的文件中预加载 DRM 元数据。)

  2. 创建 NetConnection 和 NetStream 对象,为 NetStream 客户端对象的 onDRMContentData()onPlayStatus() 回调函数提供实现。

  3. 创建 NetStreamPlayOptions 对象,并将 stream 属性设置为本地媒体文件的 URL。

  4. 调用 NetStream preloadEmbeddedMetadata(),并传入 NetStreamPlayOptions 对象,从而标识要分析的媒体文件。

  5. 如果媒体文件包含 DRM 元数据,则调用 onDRMContentData() 回调函数。将元数据作为 DRMContentData 对象传递到此函数。

  6. 使用 DRMContentData 对象通过 DRMManager loadVoucher() 方法获取凭证。

    如果 DRMContentData 对象的 authenticationMethod 属性的值为 userNameAndPassword,则在加载凭证之前对媒体权限服务器上的用户进行身份验证。可以将 DRMContentData 对象的 serverURLdomain 属性传递给 DRMManager authenticate() 方法,附带用户的凭据。

  7. 文件分析完毕后将调用 onPlayStatus() 回调函数。如果尚未调用 onDRMContentData() 函数,则文件不包含获取凭证所需的元数据。此缺失调用还可能意味着 Flash Access 不保护此文件。

以下 AIR 代码示例说明如何为本地媒体文件预加载凭证:

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