PlayReady License Acquisition Fails After Upgrading to Silverlight 5 and Another Issue


What is the issue?

Your Silverlight 4 player application might encounter a 6028: No valid simple or leaf license is available to create the decryptor error when using Silverlight 5 runtime with persistent licenses. This error may occur regardless whether your PlayReady license server is on PlayReady Server SDK 2.0 or 1.5.2.

This occurs only if license is persistent and has expired. If you delete mspr.hds, the HDS store, you can successfully acquire license again. However, once the acquired license expires, you will face the same 6028 error.

This error may occur as long as a player application runs in Silverlight 5 runtime:

1.       You upgrade your Silverlight 4 player application so that it “targets” Silverlight 5;

2.       You did not upgrade to Silverlight 4, but your users upgrade their Silverlight runtime to Silverlight 5.

NOTE: If you have been using non-persistent license, you will NOT have this issue.

What happened?

Prior to Silverlight 5, if there is no valid license for “whatever” reason, a license acquisition attempt is spawned automatically. Here, the “whatever” reasons could be any of the following:

1.       No existing license matching the KID on the system,

2.       License for KID has expired,

3.       License for uplink ID has expired,

4.       No complete usable license chains,

5.       The licenses matching the KID lack the proper rights for playback.

However in Silverlight 5, a license acquisition attempt is made automatically only if there exists no license (valid or invalid – meaning expired, no playback rights, etc.) on the client. If there exists a license that is unusable (e.g. an expired persistent license), a MediaElement.MediaFailed event is erroneously fired. This causes automatic license acquisition not to be triggered. If you happen to use non-persistent license, since no license exist upon channel/asset switch, license acquisition attempt will be spawned automatically and there is no issue.

How to fix this issue?

In order to fix this issue, the recommendation is to make code change in player application. Customers can work around this issue by performing the following changes to their player applications.

  1. If not already implemented, create a handler for the MediaElement.MediaFailed, SMFPlayer.MediaFailed or SmoothStreamingMediaElement.MediaFailed event depending on which Silverlight library/plugin you use for your player application.
  2. In the MediaFailed event handler inspect the error code that is returned, if

a)      The error = AG_E_DRM_NO_ROOT_RIGHTS_TO_CREATE_DECRYPTOR (6025) then a LicenseAcquirer should be created and used to acquire the root license for the content. If this error is returned it is most likely that an automatic acquisition has already been attempted. This implies that either the license acquisition challenge was successful yet it did not return the correct license (or set of licenses) to fully enable the license. Check that the client has the root license or that your license server is configured correctly to issue licenses with the correct content key IDs and set of rights to successfully enable the content to be decrypted.

b)      The error = AG_E_DRM_NO_RIGHTS_TO_CREATE_DECRYPTOR (6026) or AG_E_DRM_NO_LEAF_RIGHTS_TO_CREATE_DECRYPTOR (6028) then a LicenseAcquirer object should be created and used to acquire the leaf or simple license for the content. In general this is indicative of a PlayReady license server handler configuration error.

c)      Others.

3. After the required processing was completed in step #2, try playing the PlaylistItem or MediaSource again. This time the client should have the correct license rights to playback the content.

How to implement?

We first implement MediaFailed event handler as below. Depending on which player library or plugin you use (Silverlight MediaElement, SSME in Smooth Streaming Client or SMFPlayer in MMPPF/SMF), you need to use the MediaFailed event. The following code is for the case in which MMPPF is used.

void player_MediaFailed(object sender, CustomEventArgs<Exception> e)

        {

            switch (e.Value.Message.Substring(0, 4))

            {

                case “6028”:   //this is for a fix of Sliverlight 5 bug with persistent license and use of ExpirationDate   

                    Guid keyID    = player.WRMHeader.KID;

                    string la_url = player.WRMHeader.LA_URL;

                    string customData = “Any, or per your app needs”;

                    this.AcquireLicense(customData, keyID, la_url);

                    break;

                default:

                    MessageBox.Show(string.Format(“{0}: {1}”, e.Value.Message.Substring(0, 4), e.Value.Message.Substring(5) + “\n” + e.Value.StackTrace), “From Error Handling”, MessageBoxButton.OK);

                    break;

            }

        }

Then we add the following methods for performing license acquisition:

private void AcquireLicense(string customData, Guid keyId, string url)

        {

            Uri uri = new Uri(url);

            LicenseAcquirer objLicenseAcquirer = new LicenseAcquirer();

            //CustomLicenseAcquirer objLicenseAcquirer = new CustomLicenseAcquirer();

            objLicenseAcquirer.ChallengeCustomData = customData;

            // Set the License URI to proper License Server address.

            objLicenseAcquirer.LicenseServerUriOverride = uri;

            objLicenseAcquirer.AcquireLicenseCompleted += new EventHandler<AcquireLicenseCompletedEventArgs>(OnLicenseAcquired);

            objLicenseAcquirer.AcquireLicenseAsync(keyId, ContentKeyType.Aes128Bit, Guid.Empty);

        }

 

        private void OnLicenseAcquired(object sender, AcquireLicenseCompletedEventArgs e)

        {

            string msg = string.Empty;

            if (e.Error != null)

            {

                msg = e.Error.Message;

            }

            else if (e.Cancelled)

            {

                msg = “Acquire license operation cancelled”;

            }

            else

            {

                msg = string.Format(“ResponseCustomData={0}.”, e.ResponseCustomData);

            }

            MessageBox.Show(msg, “AcquireLicense”, MessageBoxButton.OK);

            //reset source of CustomSMFPlayer/MediaElement so that playback can start again.

            int index = player.CurrentPlaylistItem != null ? player.Playlist.IndexOf(player.CurrentPlaylistItem) : 0;

            player.GoToPlaylistItem(index);

        }

In the player_MediaFailed event handler, notice that we obtain key ID and license acquisition URL through a call to player.WRMHeader custom property as shown below.

      Guid keyID    = player.WRMHeader.KID;

       string la_url = player.WRMHeader.LA_URL;

If you know your key ID and license acquisition URL in any other way (such as through user selecting an asset which is mapped to a key ID somehow), you can just use your values without using player.WRMHeader.

For some customers, a key management server is used to store the mapping between a content ID and its encryption key ID. License server is responsible to retrieve the key ID from key management server based on a content ID included in a license acquisition request (for example through a query string parameter). In this case, content ID is used and key ID in the above code is irrelevant since key ID will be retrieved by the license server based on content ID.

How to get key ID and license acquisition URL?

In case you need to get key ID and/or license acquisition URL programmatically in your application, one way is to retrieve it from client manifest file. <ProtectionHeader> node in client manifest file contains all these information. Fortunately SSME provides an API to retrieve this info.

First we add a class definition as a schema for those parameters we want to retrieve from protection header.

    public class WRMHeader

    {

        public Guid KID { get; set; }

        public string LA_URL { get; set; }

    }

The reason we name the class as WRMHeader is that these information can be found in as shown below:

<WRMHEADER xmlns=\”http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader\” version=\”4.0.0.0\”><DATA><PROTECTINFO><KEYLEN>16</KEYLEN><ALGID>AESCTR</ALGID></PROTECTINFO><KID>OWjhtr3u9k+rdo1ILY0rag==</KID><CHECKSUM>brp3wSG3M7g=</CHECKSUM><LA_URL>http://localhost/playready/rightsmanager.asmx </LA_URL></DATA></WRMHEADER>

This <WRMHEADER> is contained in SmoothStreamingMediaElement.ManifestInfo.ProtectionInfo. ProtectionHeader.ProtectionData.

The following code should be added to your derived (custom) SMFPlayer class.

Add a public property:

public WRMHeader WRMHeader { get; set; }

Add a private method in setting the property value. It is critical to set the value of this property by calling the following private method at the right time otherwise the manifest data is not available (details below).

private void SetWRMHeader()

        {

            if (this.SmoothStreamingMediaElement.ManifestInfo != null && this.SmoothStreamingMediaElement.ManifestInfo.ProtectionInfo != null)

            {

                ProtectionInfo objProtectionInfo = this.SmoothStreamingMediaElement.ManifestInfo.ProtectionInfo;

                byte[] protectionData = objProtectionInfo.ProtectionHeader.ProtectionData;

                string protectionDataString = System.Text.Encoding.Unicode.GetString(protectionData, 0, protectionData.Length);

                //protectionDataString contains Unicode characters other than XML, and Linq to XML does not seem to work for this “XML” segment

                int start, end;

                string keyId, la_url;

                //start = protectionDataString.IndexOf(“<WRMHEADER”);

                //end = protectionDataString.IndexOf(“</WRMHEADER>”);

                //string xml = protectionDataString.Substring(start, end + 12 – start);

                //System.Xml.Linq.XDocument objXDocument = System.Xml.Linq.XDocument.Parse(xml);

                //System.Xml.Linq.XElement objXElement;

                //IEnumerable<string> attributes = from node in objXDocument.Descendants(“DATA”) select node.Element(“KID”).Value;

                //objXElement = objXDocument.Descendants(“KID”).FirstOrDefault<System.Xml.Linq.XElement>();

                //keyId = objXElement.Value;

                //objXElement = objXDocument.Element(“WRMHEADER”).Element(“DATA”).Element(“LA_URL”);

                //la_url = objXElement.Value;

                start = protectionDataString.IndexOf(“<KID>”);

                end = protectionDataString.IndexOf(“</KID>”);

                keyId = protectionDataString.Substring(start + 5, end – start – 5);

                start = protectionDataString.IndexOf(“<LA_URL>”);

                end = protectionDataString.IndexOf(“</LA_URL>”);

                la_url = protectionDataString.Substring(start + 8, end – start – 8);

                WRMHeader objWRMHeader = new WRMHeader();

                objWRMHeader.KID    = new Guid(Convert.FromBase64String(keyId));

                objWRMHeader.LA_URL = la_url;

                this.WRMHeader = objWRMHeader;

            }

        }

 

Technical Note: In the above string variable (xml, commented), it contains the full <WRMHEADER> node. In order to get the <KID> and <LA_URL> values, ideally we should use Linq to XML to parse the string variable (xml). However, it does not seem to work for me probably due to the reason that the string may contain certain characters other than XML. I had to resort to string manipulation.

Next we need to expose SSME in the derived (custom) SMFPlayer:

        //for easy access to SSME

        private SmoothStreamingMediaElement SmoothStreamingMediaElement

        {

            get

            {

                SmoothStreamingMediaElement objSmoothStreamingMediaElement = ActiveMediaPlugin.VisualElement as SmoothStreamingMediaElement;

                return objSmoothStreamingMediaElement;

            }

        }

Finally, we need to call the private method SetWRMHeader() to set the value of the public property public WRMHeader WRMHeader in ManifestReady event handler as below:

void SmoothStreamingMediaElement_ManifestReady(object sender, EventArgs e)

        {

            this.SetWRMHeader();

        }

Of course, for this we need the following:

this.SmoothStreamingMediaElement.ManifestReady += new EventHandler<EventArgs>(SmoothStreamingMediaElement_ManifestReady);

To Wrap Up:

We discussed the Silverlight 5 bug related to PlayReady license acquisition when persistent license is used. We presented a work around to this bug.

ACKNOWLEDGMENT:

Special acknowledgment goes to Siddharth Mantri, Sekhar Chintalapati and Miao Miao for their thorough review and testing.

 

Second Issue:

There is another issue with upgrade to Silverlight 5:

Does your Silverlight video player application fit the following profile?

  1. It runs in Out of Browser (OOB) mode with elevated trust;
  2. It references certain MMPPF (formerly SMF) plugins such as below:
  • Microsoft.SilverlightMediaFramework.Plugins.Monitoring.dll
  • Microsoft.SilverlightMediaFramework.Plugins.TimedText.dll

If your application fits the above criteria, after you upgrade your application to Silverlight 5, your video will not play, regardless whether it is smooth streaming or progressive download. If you enable logging console, you will see the following error:

Unable To Locate A Media Plugin To Play This Media Severity: “Warning” Sender: Player” Type: “UnableToLocateMediaPlugin” Message: “Unable To Locate A Media Plugin To Play This Media”

Furthermore, as long as there is a single plugin Silverlight player fails to load, smooth streaming plugin or progressive download plugin will also fail to load, hence video playback will fail, regardless it is smooth streaming or progressive download.

How to repro?

You can reproduce the issue without any coding, by following the following steps:

  1. Install Silverlight 5 Tools for Visual Studio, which also contains Silverlight 5 SDK;
  2. Create a new Silverlight SMF Smooth Streaming Application in Visual Studio 2010 (as shown below). 
  3. When you create a new Silverlight 5 project, the following four references are missing and should be added (see screenshot below):

    Also make sure Specific Version property is set to false for all the references.

    All references should be pointing to the original install location of the DLL’s such as C:\Program Files (x86)\Microsoft SDKs\Microsoft Silverlight Media Framework\v2.6\Silverlight\Bin\Microsoft.SilverlightMediaFramework.Plugins.SmoothStreaming.dll. If its path is pointing to a location under your project after adding reference, try to clean up debug folder and close/reopen Visual Studio.

  4. Open the Property dialog of the Silverlight 5 project, on Silverlight tab, make sure “Target Silverlight Version” is set to Silverlight 5. It may be necessary to remove/re-add those SMF assemblies under References.
  5. Set the properties such that the Silverlight 5 application can run OOB with Elevated Trust.

  6. Test: to make sure this simple smooth streaming player works both in-browser and OOB without any issue.

  7. Next add references to Microsoft.SilverlightMediaFramework.Plugins.Monitoring.dll and/or Microsoft.SilverlightMediaFramework.Plugins.TimedText.dll and test. You will see that, while it still works in-browser, it fails to play video in OOB mode. If you add logging code you can see the error message mentioned above. NOTE:
    Microsoft.SilverlightMediaFramework.Plugins.Monitoring.dll depends on Microsoft.SilverlightMediaFramework.Plugins.Logging.dll
    and Microsoft.SilverlightMediaFramework.Plugins.Diagnostics.dll.

In fact the key is not OOB versus in-browser. Instead the key is “Require elevated trust” setting for either OOB or in-browser mode. Once “Require elevated trust” is checked, MEF is broken and certain plugins will fail to load.

There is a new change in Silverlight 5 – a security change for apps with elevated trust: On operating systems that support P/Invoke (i.e., Windows), the Silverlight 5 CLR treats trusted apps as full trust code. This means that trusted apps may call SecurityCritical methods and use unverifiable IL, instead of being limited to calling Transparent or SecuritySafeCritical methods. This is not the case in Silverlight versions before 5: Security Transparent methods are only allowed to call either Security Transparent methods or Security Safe Critical methods, but never allowed to call Security Critical methods. For a detailed discussion of Silverlight 5 security, please see here http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-components-postattachments/00-10-25-05-64/Silverlight-Security-Overview-v5.docx.

In fact, if you capture the exception, the message looks like below: Attempt by security transparent method …… to access security critical type “Microsoft.Web.Media.SmoothStreaming.SmoothStreamingMediaElement”.

This issue has been fixed and you need to download MMPPF 2.6.1 here: http://smf.codeplex.com/releases/view/82027 .

Comments (13)

  1. Martin Pikas says:

    Is there any solution to the second issue (elevated trust/ media plugin)?

  2. A solution to the second issue will be available soon.

  3. The second issue has been fixed in MMPPF 2.6.1 here: smf.codeplex.com/…/82027

  4. Is a proper fix in the works for the first issue?

    There are some cases when updating an old Silverlight 4 player is not a valid option.

    <RANT>It seems incredible to have Silverlight 5 **plugin** breaking existing Silverlight 4 apps and requiring unsafe workarounds (parsing textual error messages for instance!). Especially as 'secure' video delivery is one of the rare niches where Silverlight has no real competition (yet).</RANT>

  5. piteerus says:

    Your solution does not work for me. Could you please check my discussion at codeplex (smf.codeplex.com/…/349783)

  6. I looked at your question on codeplex: it seems you are using non-persistent PlayReady license. As indicated in my blog, this issue with Silverlight 5 only occurs for persistent license after it expires. There is no such issue if you use non-persistent license. Are you sure you are facing the same issue as discussed in this blog?

  7. piteerus says:

    Correct me if I'm wrong, but when my persistent license expire to play the content I have to get another persistent license otherwise it'll not work right?

  8. You are correct, piteerus, if you are using persisten license. If persistent license is used and you have an expired license in your license store, you will face this issue in Silverlight 5 runtime.

  9. Could we have an official word here whether the new (5.1) release fixes the remaining issue?

    Thanks.

  10. Yes, Sliverlight 5.1 fixes this issue. But it contains a change which causes this issue: blogs.msdn.com/…/how-to-resolve-error-6030-in-silverlight-5-1-with-playready-protected-content.aspx

  11. Ok thanks, that's in line with what we experienced, but as these errors occur only on specific configurations, we'd rather have confirmation.

  12. OzgurOzan says:

    Turkish Tivibu #TTNET must read that because still cannot get licence aquisation for silverlight 5 and their service works with silverlight 4

  13. KatiaVS says:

    SilverLight 5.1 does not automatically acquire root license after the license is expired.

    The player stops playing giving this error:  SmoothStreamingErrorOccurred error :6025 No valid root license is available to create the decryptor.

    In the PlayReady service there is no license acquisition requested logged only for the first request done manually.

    How to setup SilverLight to automatically acquire root license before the license expired in order to prevent the user to stop watching the film?

    If this is not possible, is there any class to implement this request manually? For that we need to access the license data and request a new license acquisition prior the expiration or removal date.

    Thanks for your help on that.

Skip to main content