보호된 내용 작업 과정 이해

Flash Player 10.1 이상, Adobe AIR 2.0 이상

중요 : Flash Player 11.5 이상에는 Adobe Access 모듈이 통합되어 있으므로 SystemUpdater.update(SystemUpdaterType.DRM) )를 호출하는 업데이트 단계가 불필요합니다. 여기에는 다음 브라우저와 플랫폼이 포함됩니다.

  • Flash Player 11.5 ActiveX 컨트롤(Intel 프로세서 기반 Windows 8의 Internet Explorer를 제외한 모든 플랫폼)

  • Flash Player 11.5 플러그인(모든 브라우저)

  • Adobe AIR(데스크톱 및 모바일)

따라서 다음과 같은 경우에는 업데이트 단계를 계속 실행해야 합니다.

  • Intel 프로세서 기반 Windows 8의 Internet Explorer

  • Flash Player 11.4 이하(Google Chrome 22 이상(모든 플랫폼) 또는 21 이상(Windows)은 제외)

참고: Flash Player 11.5 이상이 설치된 시스템에서는 여전히 SystemUpdater.update(SystemUpdaterType.DRM) 를 안전하게 호출할 수 있지만 아무것도 다운로드되지 않습니다.

다음의 개략적인 작업 과정은 응용 프로그램에서 보호된 내용을 가져오고 재생하는 방법을 보여 줍니다. 이 작업 과정은 응용 프로그램이 Adobe Access로 보호된 내용을 재생하기 위해 설계된 것으로 가정합니다.

  1. 내용 메타데이터를 가져옵니다.

  2. 필요한 경우 Flash Player에 대한 업데이트를 처리합니다.

  3. 라이센스를 로컬로 사용할 수 있는지 여부를 확인합니다. 사용할 수 있는 경우 라이센스를 로드하고 7단계로 이동합니다. 사용할 수 없는 경우 4단계로 이동합니다.

  4. 인증이 필요한지 여부를 확인합니다. 필요하지 않은 경우 7단계로 이동합니다.

  5. 인증이 필요한 경우 사용자에게서 인증 자격 증명을 얻어 라이센스 서버로 전달합니다.

  6. 도메인 등록이 필요한 경우 도메인에 가입합니다(AIR 3.0 이상).

  7. 인증이 성공하면 서버에서 라이센스를 다운로드합니다.

  8. 내용을 재생합니다.

오류가 발생하지 않았고 사용자에게 내용을 볼 수 있는 권한이 있는 경우 NetStream 객체가 DRMStatusEvent 객체를 전달합니다. 그런 다음 응용 프로그램이 재생을 시작합니다. DRMStatusEvent 객체에는 사용자의 정책과 권한을 식별하는 관련 바우처 정보가 포함되어 있습니다. 예를 들어 오프라인에서 내용을 사용할 수 있게 할 수 있는지 여부 또는 라이센스가 만료되는 시기와 같은 정보가 포함됩니다. 응용 프로그램은 이 데이터를 사용하여 사용자의 정책 상태를 사용자에게 알릴 수 있습니다. 예를 들어 사용자가 내용을 볼 수 있는 남은 날짜 수를 상태 표시줄에 표시할 수 있습니다.

사용자에게 오프라인 액세스가 허용되는 경우 바우처가 캐시되고 암호화된 내용이 사용자의 시스템으로 다운로드됩니다. 내용은 라이센스 캐시 기간에 정의된 기간 동안 액세스할 수 있습니다. 이벤트의 detail 속성에 "DRM.voucherObtained" 가 포함됩니다. 응용 프로그램은 해당 내용을 오프라인으로 사용할 수 있도록 하기 위해 내용을 로컬에 저장할 위치를 결정합니다. 또한 DRMManager 클래스를 사용하여 바우처를 미리 로드할 수도 있습니다.

참고: 바우처의 캐시 및 미리 로드는 AIR 및 Flash Player에서 모두 지원됩니다. 그러나 암호화된 내용을 다운로드 및 저장하는 것은 AIR에서만 지원됩니다.

해당 응용 프로그램에서 오류 이벤트를 명시적으로 처리해야 합니다. 이러한 이벤트에는 사용자가 유효한 자격 증명을 입력하지만 암호화된 내용을 보호하는 바우처에서 내용에 대한 액세스를 제한하는 경우가 포함됩니다. 예를 들어 내용에 액세스할 수 있는 권한에 대한 비용을 지불하지 않은 경우 인증된 사용자가 내용에 액세스할 수 없습니다. 동일한 제작자의 등록된 멤버 두 명이 둘 중 한 명만 비용을 지불한 내용을 공유하려고 하는 경우에도 이러한 오류가 발생할 수 있습니다. 응용 프로그램은 사용자에게 오류에 대해 알리고 대안을 제시해야 합니다. 일반적인 대안은 등록하고 보기 권한에 대한 비용을 지불하는 방법과 관련한 지침입니다.

상세한 API 작업 과정

여기서는 보호된 내용에 대한 작업 과정을 자세하게 살펴봅니다. 이 작업 과정에서는 Adobe Access로 보호된 내용을 재생하는 데 사용되는 특정 API에 대해 설명합니다.

  1. URLLoader 객체를 사용하여 보호된 내용의 메타데이터 파일 바이트를 로드합니다. 이 객체를 metadata_bytes 와 같은 변수로 설정합니다.

    Adobe Access로 제어되는 모든 내용에는 Adobe Access 메타데이터가 있습니다. 내용을 패키지화하는 경우 내용과 함께 이 메타데이터를 별도의 메타데이터 파일(.metadata)로 저장할 수 있습니다. 자세한 내용은 Adobe Access 설명서를 참조하십시오.

  2. DRMContentData 인스턴스를 만듭니다. 다음 코드를 try-catch 블록에 넣습니다.

    new DRMContentData( metadata_bytes )

    여기서 metadata_bytes 는 1단계에서 가져온 URLLoader 객체입니다.

  3. (Flash Player에만 해당) 런타임에서 Adobe Access 모듈이 있는지 여부를 확인합니다. 찾을 수 없는 경우 DRMErrorEvent 오류 코드 3344 또는 DRMErrorEvent 오류 코드 3343과 함께 IllegalOperationError가 발생합니다.

    이 오류를 처리하려면 SystemUpdater API를 사용하여 Adobe 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 설치 프로그램에서 Adobe Access 모듈 업데이트 및 필요한 런타임 업데이트를 처리합니다.
  4. DRMManager 객체에서 전달된 DRMStatusEvent 및 DRMErrorEvent를 수신하기 위한 리스너를 만듭니다.

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

    DRMStatusEvent 리스너에서는 바우처가 유효한지(null이 아닌지) 확인하고 DRMErrorEvent 리스너에서는 DRMErrorEvents를 처리합니다. 자세한 내용은 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)

    인증이 실패하는 경우 DRMAuthenticationErrorEvent , 인증이 성공하는 경우 DRMAuthenticationCompleteEvent 를 DRMManager에서 전달합니다. 이러한 이벤트에 대한 리스너를 생성합니다.

  10. 인증 방법이 UNKNOWN 인 경우 사용자 정의 인증 방법을 사용해야 합니다. 이 경우 ActionScript 3.0 API를 사용하지 않고 오프라인 방식으로 수행되는 인증을 내용 공급자가 준비해 둡니다. 사용자 정의 인증 절차에서는 DRMManager.setAuthenticationToken() 메서드에 전달할 수 있는 인증 토큰을 생성해야 합니다.

  11. 인증이 실패하면 응용 프로그램에서 9단계로 돌아가야 합니다. 응용 프로그램에 반복되는 인증 실패를 처리 및 제한하기 위한 메커니즘이 있어야 합니다. 예를 들어 세 번의 시도 후에 사용자에게 인증이 실패하여 내용을 재생할 수 없다는 메시지를 표시합니다.

  12. 사용자에게 자격 증명을 입력하라는 메시지를 표시하지 않고 저장된 토큰을 사용하려면 DRMManager.setAuthenticationToken() 메서드로 토큰을 설정합니다. 그러면 라이센스 서버에서 라이센스를 다운로드하여 8단계와 같이 내용을 재생할 수 있습니다.

  13. (선택 사항) 인증이 성공하면 메모리에 캐시된 바이트 배열인 인증 토큰을 캡처할 수 있습니다. 이 토큰을 가져오는 데는 DRMAuthenticationCompleteEvent.token 속성을 사용합니다. 인증 토큰을 저장하여 사용하면 사용자가 이 내용에 대한 자격 증명을 매번 입력할 필요가 없습니다. 라이센스 서버에서 인증 토큰의 유효 기간을 확인합니다.

  14. 인증이 성공하면 라이센스 서버에서 라이센스를 다운로드합니다.

    DRMManager.loadvoucher(drmContentData, LoadVoucherSetting.FORCE_REFRESH)

    로드가 완료되면 DRMManager 객체에서 DRMStatusEvent.DRM_STATUS를 전달합니다. 이 이벤트를 수신하고 전달된 후에 내용을 재생할 수 있습니다.

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

DRMContentData 및 세션 객체

DRMContentData 는 작성된 경우 Flash Player DRM 모듈을 참조하는 세션 객체로 사용됩니다. 이 DRMContentData 를 수신하는 모든 DRMManager API는 해당 특정 DRM 모듈을 사용합니다. 그러나 DRMContentData 를 사용하지 않는 두 개의 DRMManager API가 있습니다. 이러한 규칙은 다음과 같습니다.
  1. authenticate()

  2. setAuthenticationToken()

연결된 DRMContentData 가 없으므로 이러한 DRMManager API를 호출하면 디스크의 최신 DRM 모듈이 사용됩니다. 이렇게 되면 응용 프로그램의 DRM 작업 과정 도중에 DRM 모듈의 업데이트가 발생할 경우 문제가 될 수 있습니다. 다음과 같은 시나리오를 생각해 보십시오.
  1. 응용 프로그램은 AdobeCP1 을 DRM 모듈로 사용하는 DRMContentData 객체 contentData1 을 만듭니다.

  2. 응용 프로그램은 DRMManager.authenticate(contentData1.serverURL,...) 메서드를 호출합니다.

  3. 응용 프로그램은 DRMManager.loadVoucher(contentData1, ...) 메서드를 호출합니다.

응용 프로그램이 2단계에 도달하기 전에 DRM 모듈에 업데이트가 발생할 경우 DRMManager.authenticate() 메서드는 AdobeCP2 를 DRM 모듈로 사용하여 인증을 끝냅니다. 3단계의 loadVoucher() 메서드는 여전히 AdobeCP1 을 DRM 모듈로 사용하므로 실패합니다. DRM 모듈 업데이트를 호출하는 다른 응용 프로그램으로 인해 업데이트가 발생했을 수 있습니다. 응용 프로그램 시작 시에 DRM 모듈 업데이트를 호출하면 이러한 시나리오를 방지할 수 있습니다.

DRM 관련 이벤트

응용 프로그램이 보호된 내용을 재생하려고 시도하면 런타임에서 다음과 같은 여러 이벤트를 전달합니다.

  • DRMManager에 의해 전달된 DRMDeviceGroupErrorEvent(AIR에만 해당)

  • NetStream에서 전달하는 DRMAuthenticateEvent(AIR에만 해당)

  • DRMManager에서 전달하는 DRMAuthenticationCompleteEvent

  • DRMManager에서 전달하는 DRMAuthenticationErrorEvent

  • NetStream 및 DRMManager에서 전달하는 DRMErrorEvent

  • NetStream 및 DRMManager에서 전달하는 DRMStatusEvent

  • StatusEvent

  • NetStatusEvent. 자세한 내용은 업데이트 이벤트 수신 을 참조하십시오.

Adobe Access로 보호된 내용을 지원하려면 DRM 이벤트를 처리하는 이벤트 리스너를 추가합니다.

오프라인 재생을 위해 바우처 미리 로드

Adobe Access로 보호된 내용을 재생하는 데 필요한 바우처(라이센스)를 미리 로드할 수 있습니다. 미리 로드된 바우처를 사용하면 사용자가 인터넷에 연결되어 있는지 여부에 관계없이 내용을 볼 수 있습니다. 이때 미리 로드 프로세스 자체에도 인터넷 연결이 필요합니다. NetStream 클래스 preloadEmbeddedMetadata() 메서드 및 DRMManager 클래스를 사용하여 바우처를 미리 로드할 수 있습니다. AIR 2.0 이상에서는 DRMContentData 객체를 사용하여 바우처를 직접 미리 로드할 수 있습니다. 내용과 독립적으로 DRMContentData 객체를 업데이트할 수 있기 때문에 이 방법을 사용하는 것이 좋습니다. preloadEmbeddedData() 메서드는 내용에서 DRMContentData를 가져옵니다.

DRMContentData 사용

다음 단계에서는 DRMContentData 객체를 사용하여 보호된 미디어 파일에 대한 바우처를 미리 로드하는 작업 과정에 대해 설명합니다.

  1. 패키지화된 내용에 대한 이진 메타데이터를 가져옵니다. Adobe Access Java Reference Packager를 사용하는 경우 이 메타데이터 파일이 .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 속성 값이 flash.net.drm.AuthenticationMethod.USERNAME_AND_PASSWORD 인 경우에는 바우처를 로드하기 전에 미디어 권한 서버에서 사용자를 인증해야 합니다. DRMContentData 객체의 serverURL domain 속성은 사용자의 자격 증명과 함께 DRMManager authenticate() 메서드로 전달할 수 있습니다.

  7. 파일 파싱이 완료되면 onPlayStatus() 콜백 함수가 호출됩니다. onDRMContentData() 함수가 호출되지 않은 경우에는 파일에 바우처를 가져오는 데 필요한 메타데이터가 포함되지 않습니다. 이 호출이 누락되었다는 것은 Adobe 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(); 
    } 
} 
}