Using License Chains with Windows Media Rights Manager 10 SDK

Andrea Pruneda
Microsoft Corporation
February 2005
 

Applies to:
   Microsoft® Windows Media® Rights Manager 10 Software Development Kit (SDK)
  

Contents

Introduction

How License Chaining Works

Implementing a License Chain

    Protecting Content

    Setting Rights in a License Chain

    Issuing a Chained License

    Example Code

For More Information

Introduction

License chaining is a new feature of Microsoft® Windows Media® Rights Manager 10 that allows a high volume of licenses to be renewed quickly. A license chain consists of two parts, a leaf license and a root license. Both licenses are required to complete the chain and to access protected content.

A simple license is self-contained. When it expires, the entire license is no longer valid and a new one must be issued before the content can be played again. A simple license is the only type that is used in Windows Media Digital Rights Management (DRM) versions 9 and earlier.

A chained license is composed of a leaf license and a root license. The leaf license unlocks the content, and the root license unlocks the leaf license. In order to access the content, the DRM component of the player must locate both the root and leaf licenses to complete the chain. Windows Media DRM 10 supports both simple and chained licenses.

The root license and the leaf license each contain a set of rights. Both licenses must be valid and both must allow the actions that are being requested. For example, to play content using a chained license, the root and leaf licenses must contain the right AllowPlay = True. If either the root or leaf license expires, the user cannot access the content until that license element is renewed.

The license chain format is especially useful for the following reasons:
  • Multiple content items can share a root license, but use different leaf licenses.
  • Restrictions (such as expiration or counted plays) can be included in just one license element.

License configurations, such as the rights and combination of leaf and root licenses, are flexible and can be set up according to your business model.

For example, by using license chains, a subscription service with a large catalog can issue one root license with time restrictions and individual leaf licenses. When the subscription expires, only one root license needs to be renewed. For example, when a new consumer signs up for the subscription service, they are issued 10,000 leaf licenses that don't expire and one root license that expires in 30 days. When the subscription expires each month, the consumer renews the subscription and is issued a new root license valid for an additional 30 days. The result is that the renewal process is much faster since only one license needs to be issued to renew the subscription rather than thousands of licenses.

Back to the top of this pageBack to Top


How License Chaining Works

In a license chain, the root license is bound to the computer or device, and unlocks the leaf license. The leaf license is bound to the root license, and unlocks the content. So when a player tries to open protected content, it must reconstruct the license chain.

The following diagram shows how the player reconstructs a license chain.

Diagram that shows how a player reconstructs a license chain
  1. The player uses the key ID in the content header to find the leaf license.
  2. The leaf license contains an uplink ID, which matches the key ID of its root license.
  3. The root license unlocks the leaf license.
  4. The content key in the leaf license unlocks the content.

Back to the top of this pageBack to Top


Implementing a License Chain

This section describes how to set up a license chain, including how to protect the content, how to set the rights, and how to issue the licenses. Example code is included at the end of this section.

Protecting Content

When you protect content for which you want to use a license chain, you should add the uplink ID to the content header using the WMRMHeader.AddUplink method. The key ID that is already required during the protection process points to the leaf license, and the uplink ID in the leaf license points to the root license. While the uplink ID is not required in the content header, it is recommended. When the uplink ID is included, license requests for this content will include license state data that indicates information about existing licenses for this content on the client. The license issuer can find out whether the content already has a license and, if so, how many counted actions are remaining and when the license will expire. For example, to improve the user experience, the license issuer might renew a root license before it expires or renew a leaf license that has used all of its copy counts.

Setting Rights in a License Chain

Each license in the chain must contain a valid set of rights. The DRM component of the media player evaluates the rights in both licenses in the chain as follows:
  • An action is permitted only if the right is present in both the root and leaf licenses.
  • If a root license specifies any output protection levels, all output protection levels in the leaf license are ignored.
  • If both root and leaf licenses include counts, the counts in both are decremented when both licenses are used. When only the root license is used, only the count in the root license is decremented, such as in license synchronization.
  • To allow content to be burned as part of a playlist, the AllowPlaylistBurn right must be granted in both the root and the leaf licenses. However, the PlaylistBurnTrackCount and MaxPlaylistBurnCount rights are supported only in a leaf license.

Guidelines

When setting rights for license chains, different results can be achieved, depending on how you set the rights in the leaf and root licenses, especially for counts and expiration. Use the following guidelines when setting rights in a license chain.
  • Setting basic rights.
    You should explicitly allow and deny the rights in both leaf and root licenses, rather than depending on default values. For example, specify AllowPlay = True in both licenses.
  • Setting counted actions.
    Counted actions in a root license are not tracked individually per content item. For example, if 10 content items share a root license that allows one play, only one item can be played once. So, to specify counted actions per content item, you would specify counted actions in each leaf license only. For example, suppose a subscription service wants to let users copy each content item to a device up to five times. The service would then specify AllowCopy = 5 in each leaf license.
    License rights are flexible, and you can use this behavior in a creative way. For example, you could include extra copy or burn counts in the root license, and the user could choose which content items to use them with.
  • Setting expiration dates.
    Specify expiration dates in the root license. For example, if a subscription service has content that expires at the end of each month, specify the expiration date in the root license only. When users renew their subscription, the root license is reissued with a new expiration date.
  • Setting restrictions.
    Specify any restrictions in the root license. First, restrictions in a root license take precedence over those specified in a leaf license. Second, as technologies change, specifying these restrictions in only the root allow you to make changes to the content policy for your business model by simply renewing the root license.

Issuing a Chained License

Issuing a chained license follows a similar process to issuing a simple license—you retrieve the content header from the license challenge (unless you predeliver licenses), generate the corresponding keys, set the rights, and then send the licenses to the client.

However, when issuing a chained license, unless you are issuing the license for the first time, you do not always need to issue both a root and a leaf license. For example, during a subscription renewal, you might only need to issue a root license. At other times, you might only need to issue new leaf licenses for items the user does not have yet. The Windows Media® Rights Manager 10 SDK provides a new interface that, if the content uses a chained license, returns license state data about any existing licenses on the client. For example, you could determine that the client already has a root license that expires in 5 days, or a leaf license with 10 remaining play counts.

The Windows Media Rights Manager SDK provides the following new interfaces for working with chained licenses:
  • Because the key for a root license must be 8 bytes long, use the WMRMKeys.GenerateKeyEx method to generate the root license key. Continue to generate 7-byte keys for simple and leaf licenses.
  • The leaf license must contain the key and key ID of the root license (also called the uplink key and key ID). Use the WMRMLicGen.UplinkKey and WMRMLicGen.UplinkKid properties to specify these.
  • Use the WMRMChallenge.Uplinks property to retrieve information about existing chained licenses on the client. (This information is included in the license request as long as the uplink ID is specified in the content header when the content is protected.) Then you can determine whether to issue a root and/or a leaf license. Use the WMRMUplinkCollection, WMRMUplink, and WMRMLicenseStateData objects to process the data.

Example Code

This section provides the two code examples to show you how to work with chained licenses.
  • The first code example shows how to specify the uplink ID while protecting content.
  • The second code example shows how to issue a chained license in response to a license request that includes a content header.

For readability, error checking is not included.

Example Code for Protecting Content

<%@ LANGUAGE="VBScript"%>
<%
Response.Buffer = True
Response.Expires = 0
On Error Resume Next
Do
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
' Declare variables.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
Dim KeyObj ' WMRMKeys object
Dim HeaderObj ' WMRMHeader object
Dim ProtectObj ' WMRMProtect object

Dim Seed ' License key seed
Dim ContentServerPrivKey ' Private key of the content server
Dim ContentServerPubKey ' Public key of the content server
Dim LeafKID ' Key ID of the leaf license
Dim RootKID ' Key ID of the root license
Dim ContentKey ' Key of the leaf license
Dim Header ' Content header
Dim LicURL ' License acquisition URL
Dim SecVersion ' Required individualization version
Dim InputFile ' Path and name of the input file
Dim OutputFile ' Path and name of the protected file

'"""""""""""""""""""""""""""""""""""""""""""""""""""""
' Set variables.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
Seed = <replace with your license key seed>
ContentServerPrivKey = <replace with private key of content server>
ContentServerPubKey = <replace with public key of content server>
LicURL = <replace with your license acquisition URL>
SecVersion = <replace with required individualization version>
InputFile = <replace with input file name and path>
OutputFile = <replace with output file name and path>

'"""""""""""""""""""""""""""""""""""""""""""""""""""""
' Generate root and leaf key IDs.
' In a subscription model, you might only need to
' generate the root KID value one time, and retrieve it
' from a database for other content items.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
Set KeyObj = Server.CreateObject("WMRMObjs.WMRMKeys")
RootKID = KeyObj.GenerateKeyID()
LeafKID = KeyObj.GenerateKeyID()

'"""""""""""""""""""""""""""""""""""""""""""""""""""""
' Generate the content key for the leaf license.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
KeyObj.Seed = Seed
KeyObj.KeyID = LeafKID
ContentKey = KeyObj.GenerateKey()

'"""""""""""""""""""""""""""""""""""""""""""""""""""""
'Set values into the content header and sign it.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
Set HeaderObj = Server.CreateObject("WMRMObjs.WMRMHeader")
HeaderObj.KeyID = LeafKID
HeaderObj.AddUplink(RootKID)
HeaderObj.LicenseAcqURL = LicURL
Call HeaderObj.SetCheckSum(ContentKey)
HeaderObj.IndividualizedVersion = SecVersion
Call HeaderObj.Sign(ContentServerPrivKey)
Header = HeaderObj.Header

'"""""""""""""""""""""""""""""""""""""""""""""""""""""
'Protect the content.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
Set ProtectObj = Server.CreateObject("WMRMObjs.WMRMProtect")
ProtectObj.InputFile = InputFile
ProtectObj.Key = ContentKey
ProtectObj.Header = Header
ProtectObj.V1KeyID = LeafKID
Call ProtectObj.ProtectFile(OutputFile)

Response.Write "The file has been protected."

Loop While False

If Err.Number <> 0 Then
Response.Write CStr(Hex(err.number)) + Err.Description
End if

'"""""""""""""""""""""""""""""""""""""""""""""""""""""
'Clear objects.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
Set KeyObj = Nothing
Set HeaderObj = Nothing
Set ProtectObj = Nothing
%>
Example Code for Issuing a Chained License

<%@ LANGUAGE="VBScript"%>
<%
Response.Buffer = True
Response.Expires = 0
Do
On Error Resume Next

'"""""""""""""""""""""""""""""""""""""""""""""""""""""
' Declare variables.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
Dim ChallengeObj ' WMRMChallenge object
Dim ChainCollObj ' WMRMUplinkCollection object
Dim ChainObj ' WMRMUplink object
Dim LeafLicenseStateObj ' WMRMLicenseStateData object
Dim RootLicenseStateObj ' WMRMLicenseStateData object
Dim HeaderObj ' WMRMHeader object
Dim KeyObj ' WMRMKeys object
Dim RightsObj ' WMRMRights object
Dim LicenseObj ' WMRMLicGen object
Dim ResponseObj ' WMRMResponse object
Dim ContentServerPubKey ' Public key of the content server
Dim Seed ' License key seed
Dim Delivery ' Delivery flag
Dim Silent ' Silent delivery flag
Dim strLicenseRequested ' License request string
Dim varClientInfo ' Client information
Dim varHeader ' Content header
Dim blnResult ' Signature verification
Dim LeafKID ' Key ID of a leaf license
Dim IndiVersion ' Security version of the DRM component
Dim RootKID ' Key ID of the root license
Dim RootCat ' License state data for root licenses
Dim RCountArray ' Count array
Dim RDateArray ' Date array
Dim IssueRoot ' Root license flag
Dim Rcounts ' Remaining counts
Dim RstartDate ' Start date
Dim RExpDate ' Expiration date
Dim LeafCat ' License state data for leaf licenses
Dim LCountArray ' Count array
Dim LDateArray ' Date array
Dim IssueLeaf ' Leaf license flag
Dim Lcounts ' Remaining counts
Dim LstartDate ' Start date
Dim LExpDate ' Expiration date
Dim LeafKey ' Key for the leaf license
Dim LeafRights ' Rights string for the leaf license
Dim LeafLicense ' License for the leaf license
Dim RootKey ' Key for the root license
Dim RootRights ' Rights string for the root license
Dim RootLicense ' Root license
Dim LicResponse ' License response

'"""""""""""""""""""""""""""""""""""""""""""""""""""""
' Set variables.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
ContentServerPubKey = <replace with content server public key>
Seed = <replace with license key seed>

Delivery = ""
Silent = True


'"""""""""""""""""""""""""""""""""""""""""""""""""""""
' Check the request for silent or non-silent delivery.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
If (Request.Form("nonsilent") <> "") Then Silent = False


'"""""""""""""""""""""""""""""""""""""""""""""""""""""
' Retrieve the license request.
' Get the client information and content header.
'
' This sample does not apply to a predelivery model.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
Set ChallengeObj = Server.CreateObject("WMRMObjs.WMRMChallenge")
strLicenseRequested = Request.Form("challenge")
ChallengeObj.Challenge = strLicenseRequested
varClientInfo = ChallengeObj.ClientInfo
varHeader = ChallengeObj.Header


'"""""""""""""""""""""""""""""""""""""""""""""""""""""
' Put the content header into the header object. Retrieve
' the KID and required individualization version.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
Set HeaderObj = Server.CreateObject("WMRMObjs.WMRMHeader")
HeaderObj.Header = varHeader
blnResult = HeaderObj.Verify(ContentServerPubKey)
LeafKID = HeaderObj.KeyID
IndiVersion = HeaderObj.IndividualizedVersion
' If IndividualizedVersion is not specified in the
' content header, reset the error.
If (Err.Number <> 0) Then Err.clear


'"""""""""""""""""""""""""""""""""""""""""""""""""""""
' Retrieve the license state data to determine which license
' elements to issue.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
Set ChainCollObj = ChallengeObj.Uplinks

If (Err.Number <> 0) Then ' No license state data available
' You might retrieve the root KID from a database.
RootKID = <Replace with a root KID>

' Since no license state data is available, issue both licenses.
IssueRoot = True
IssueLeaf = True
Else

' Get license state data and key ID for the root license.
' Item 1 in the collection corresponds to the root.
Set ChainObj = ChainCollObj.item(1)
RootKID = ChainObj.KID

' Get license state data to determine whether to issue a root license.
Set RootLicenseStateObj = ChainObj.LicenseState
RootCat = cstr(RootLicenseStateObj.Category)

Select Case RootCat
' In this example, a root license is only issued when an existing
' root license with rights cannot be found (case 0).

case 0
' The root license has no rights.
IssueRoot = True

case 1
' The root license has unlimited play counts.
IssueRoot = False

case 2
' The root license has remaining play counts.
RCountArray = RootLicenseStateObj.Counts
RCounts = cstr(RCountArray(0))
IssueRoot = False

case 3
' The root license has a start date.
RDateArray = RootLicenseStateObj.Dates
RStartDate = cstr(RDateArray(0))
IssueRoot = False

case 4
' The root license has an expiration date.
RDateArray = RootLicenseStateObj.Dates
RExpDate = cstr(RDateArray(0))
IssueRoot = False

case 5
' The root license has start and expiration dates.
RDateArray = RootLicenseStateObj.Dates
RStartDate = cstr(RDateArray(0))
RExpDate = cstr(RDateArray(1))
IssueRoot = False

case 6
' The root license has a start date and remaining play counts.
RDateArray = RootLicenseStateObj.Dates
RStartDate = cstr(RDateArray(0))
RCountArray = RootLicenseStateObj.Counts
RCounts = cstr(RCountArray(0))
IssueRoot = False

case 7
' The root license has an expiration date and remaining play counts.
RDateArray = RootLicenseStateObj.Dates
RExpDate = cstr(RDateArray(0))
RCountArray = RootLicenseStateObj.Counts
RCounts = cstr(RCountArray(0))
IssueRoot = False

case 8
' The root license has start and expiration dates,
' and remaining play counts.
RDateArray = RootLicenseStateObj.Dates
RStartDate = cstr(RDateArray(0))
RExpDate = cstr(RDateArray(1))
RCountArray = RootLicenseStateObj.Counts
RCounts = cstr(RCountArray(0))
IssueRoot = False

case 9
' The root license expires after first use.
IssueRoot = False

End Select

' Get license state data to determine whether to issue a leaf license.
' Item 0 in the collection corresponds to the leaf.
Set ChainObj = ChainCollObj.item(0)

' Get license state data to determine whether to issue a leaf license.
Set LeafLicenseStateObj = ChainObj.LicenseState
LeafCat = cstr(LeafLicenseStateObj.Category)

Select Case LeafCat
' In this example, a leaf license is only issued when an existing
' leaf license with rights cannot be found (case 0).

case 0
' The leaf license has no rights.
IssueLeaf = True

case 1
' The leaf license has unlimited play counts.
IssueLeaf = False

case 2
' The leaf license has remaining play counts.
LCountArray = LeafLicenseStateObj.Counts
LCounts = cstr(LCountArray(0))
IssueLeaf = False

case 3
' The leaf license has a start date.
LDateArray = LeafLicenseStateObj.Dates
LStartDate = cstr(LDateArray(0))
IssueLeaf = False

case 4
' The leaf license has an expiration date.
LDateArray = LeafLicenseStateObj.Dates
LExpDate = cstr(LDateArray(0))
IssueLeaf = False

case 5
' The leaf license has start and expiration dates.
LDateArray = LeafLicenseStateObj.Dates
LStartDate = cstr(LDateArray(0))
LExpDate = cstr(LDateArray(1))
IssueLeaf = False

case 6
' The leaf license has a start date and remaining play counts.
LDateArray = LeafLicenseStateObj.Dates
LStartDate = cstr(LDateArray(0))
LCountArray = LeafLicenseStateObj.Counts
LCounts = cstr(LCountArray(0))
IssueLeaf = False

case 7
' The leaf license has an expiration date and remaining play counts.
LDateArray = LeafLicenseStateObj.Dates
LExpDate = cstr(LDateArray(0))
LCountArray = LeafLicenseStateObj.Counts
LCounts = cstr(LCountArray(0))
IssueLeaf = False

case 8
' The leaf license has start and expiration dates,
' and remaining play counts.
LDateArray = LeafLicenseStateObj.Dates
LStartDate = cstr(LDateArray(0))
LExpDate = cstr(LDateArray(1))
LCountArray = LeafLicenseStateObj.Counts
LCounts = cstr(LCountArray(0))
IssueLeaf = False

case 9
' The root license expires after first use.
IssueLeaf = False

End Select

End if


'"""""""""""""""""""""""""""""""""""""""""""""""""""""
' Generate the keys.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
Set KeyObj = Server.CreateObject("WMRMObjs.WMRMKeys")
KeyObj.KeyID = LeafKID
KeyObj.Seed = Seed
LeafKey = KeyObj.GenerateKey()

' Generate the 8-byte key for the root license.
KeyObj.KeyID = RootKID
RootKey = KeyObj.GenerateKeyEx(8)


'"""""""""""""""""""""""""""""""""""""""""""""""""""""
' Set the rights for the leaf license.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
Set RightsObj = Server.CreateObject("WMRMObjs.WMRMRights")
RightsObj.AllowPlay = True
RightsObj.AllowCopy = True
RightsObj.AllowCollaborativePlay = False
RightsObj.AllowBackupRestore = False
RightsObj.AllowPlaylistBurn = False
RightsObj.CopyCount = 5
RightsObj.GracePeriod = 24
RightsObj.MinimumClientSDKSecurity = 3000
RightsObj.MinimumSecurityLevel = 1000

' Get the rights string.
LeafRights = RightsObj.GetAllRights


'"""""""""""""""""""""""""""""""""""""""""""""""""""""
' Set the rights for the root license.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
RightsObj.Reset()
RightsObj.AllowPlay = True
RightsObj.AllowCopy = True
RightsObj.AllowCollaborativePlay = False
RightsObj.AllowBackupRestore = False
RightsObj.AllowPlaylistBurn = False
RightsObj.GracePeriod = 24
RightsObj.MinimumClientSDKSecurity = 3000
RightsObj.MinimumSecurityLevel = 1000
RightsObj.ExpirationDate = "#20051231Z#"
RightsObj.DeleteOnClockRollback = False
RightsObj.DisableOnClockRollback = True

' Get the rights string.
RootRights = RightsObj.GetAllRights


'"""""""""""""""""""""""""""""""""""""""""""""""""""""
' Generate a leaf license.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
Set LicenseObj = Server.CreateObject("WMRMObjs.WMRMLicgen")
LicenseObj.KeyID = LeafKID
LicenseObj.SetKey "", LeafKey
LicenseObj.Priority = 10
LicenseObj.Rights = LeafRights
LicenseObj.ClientInfo = varClientInfo
LicenseObj.BindToPubKey = ContentServerPubKey
LicenseObj.IndividualizedVersion = IndiVersion

' Specify the key and key ID for the root license.
LicenseObj.UplinkKid = RootKID
LicenseObj.UplinkKey = RootKey

LeafLicense = LicenseObj.GetLicenseToDeliver()


'"""""""""""""""""""""""""""""""""""""""""""""""""""""
'Generate a root license.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
Set LicenseObj = Nothing
Set LicenseObj = Server.CreateObject("WMRMObjs.WMRMLicgen")
LicenseObj.KeyID = RootKID
LicenseObj.SetKey "", RootKey
LicenseObj.Priority = 10
LicenseObj.Rights = RootRights
LicenseObj.ClientInfo = varClientInfo
LicenseObj.BindToPubKey = ContentServerPubKey
LicenseObj.IndividualizedVersion = IndiVersion
RootLicense = LicenseObj.GetLicenseToDeliver()

Delivery = "deliver"

Loop While False


'"""""""""""""""""""""""""""""""""""""""""""""""""""""
' Send an error message or deliver the license.
'"""""""""""""""""""""""""""""""""""""""""""""""""""""
If (Delivery = "" and Err.Number <> 0) Then
Response.Write cstr(hex(err.number)) + " :" & Err.Description

ElseIf (Delivery = "deliver") Then
Set ResponseObj = Server.CreateObject("WMRMObjs.WMRMResponse")

If (IssueLeaf = True) Then
' Include a leaf license in the license response.
Call ResponseObj.AddLicense("2.0.0.0", LeafLicense)
End If

If (IssueRoot = True) Then
' Include the root license in the license response.
Call ResponseObj.AddLicense("2.0.0.0", RootLicense)
End If

If (Silent = True) Then
LicResponse = ResponseObj.GetLicenseResponse()
Response.Write LicResponse

ElseIf (Silent = False) Then
' This replaces quotes to pass a value.
' ResponseObj.ReplaceQuotesWith = """""" ' For VBScript.
ResponseObj.ReplaceQuotesWith = "/""" ' For JScript.
LicResponse = ResponseObj.GetLicenseResponse()

' This simple_ns.asp is a sample file that is included with
' the Windows Media Rights Manager SDK.
%>
<!-- #include file="simple_ns.asp" -->
<%
End If
End If


' Clear objects.
Set ChallengeObj = Nothing
Set ChainCollObj = Nothing
Set ChainObj = Nothing
Set LicenseStateObj = Nothing
Set HeaderObj = Nothing
Set KeyObj = Nothing
Set RightsObj = Nothing
Set LicenseObj = Nothing
Set ResponseObj = Nothing
%>

Back to the top of this pageBack to Top


For More Information

  • Creating a Windows Media Player 10 Subscription Online Store white paper (http://www.microsoft.com/windows/windowsmedia/knowledgecenter/
    technicalarticles.aspx#digitalrightsmanagement).
  • For general information about Windows Media® technologies, see the Windows Media Web page (http://www.microsoft.com/windows/windowsmedia/).
  • To learn more about the components of the Windows Media 10 SDK, see the About the Windows Media SDK Components Web page (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnanchor/html/anch_winmedsdk.asp).
  • To download the Windows Media Rights Manager 10 SDK, go to the Windows Media Licensing Form Web page (http://wmlicense.smdisp.net/licenserequest/) and submit a License Request Form online. You will receive a License Agreement from Microsoft by e-mail. Sign and return the agreement. You will then be sent a password and a link to download the SDK.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值