[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Bug#1108891: kodi-inputstream-adaptive: Trixie version of inputstream-adaptive crashes with Widevine CDM



On 2025-07-15 21:07:56 +0300, Vasyl Gello wrote:
> Package: kodi-inputstream-adaptive
> Version: 21.5.9+ds-1
> Followup-For: Bug #1108891
> X-Debbugs-Cc: debian-release@lists.debian.org
> 
> Hi Mark,
> 
> Thanks for pointing this out! My analysis shows that the CDM fix
> was more or less sorted out in 21.5.15+ds-1 that I have prepared
> for upload. 
> 
> The test DEB for you is:
> http://temp.sh/XXsbF/kodi-inputstream-adaptive_21.5.15ds-1_amd64.deb
> 
> Please confirm that everything works (or does not work) for you now.
> 
> CC'ing #debian-release team with a broader question: since all Kodi
> and addon point releases for 21.x are bugfix-only, is it possible to
> get chromium-style approval for src:kodi and src:kodi-* in Debian 13?
> 
> That would solve ton of user woes and, basically, that was my ideal
> when I started the maintainership back in 2020.
> 
> For the record, I attach the debdiff between current version in trixie
> (21.5.9+ds-1) and the fixed (21.5.15+ds-1). It is quite big (250 KB)
> because of clang-format run during the backport. However it is all
> backport of a bugfix, therefore I barely see the reason to add the
> actual patch sequence to debian/patches compared to new version upload.
> 
> Dear colleagues from release team, what do you think?

It's too late now, but for the future: best way to get input from us is
via unblock (pre-approval) requests. Mails to the mailing list have a
high chance to be missed.

Cheers

> 
> Sincerely,
> Vasyl 

> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/azure-pipelines.yml kodi-inputstream-adaptive-21.5.15+ds/azure-pipelines.yml
> --- kodi-inputstream-adaptive-21.5.9+ds/azure-pipelines.yml	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/azure-pipelines.yml	2025-07-06 07:54:23.000000000 +0000
> @@ -39,11 +39,11 @@
>            ARCHITECTURE: x64
>            CONFIGURATION: Release
>            WINSTORE: -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0.17763.0"
> -        ARM32-UWP:
> -          GENERATOR: "Visual Studio 17 2022"
> -          ARCHITECTURE: ARM
> -          CONFIGURATION: Release
> -          WINSTORE: -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0.17763.0"
> +        #ARM32-UWP:
> +        #  GENERATOR: "Visual Studio 17 2022"
> +        #  ARCHITECTURE: ARM
> +        #  CONFIGURATION: Release
> +        #  WINSTORE: -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0.17763.0"
>          #ARM64-UWP:
>          #  GENERATOR: "Visual Studio 17 2022"
>          #  ARCHITECTURE: ARM64
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/debian/changelog kodi-inputstream-adaptive-21.5.15+ds/debian/changelog
> --- kodi-inputstream-adaptive-21.5.9+ds/debian/changelog	2025-01-27 08:45:13.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/debian/changelog	2025-07-15 16:51:54.000000000 +0000
> @@ -1,3 +1,9 @@
> +kodi-inputstream-adaptive (21.5.15+ds-1) unstable; urgency=high
> +
> +  * New upstream version 21.5.15+ds (Closes: #1108891)
> +
> + -- Vasyl Gello <vasek.gello@gmail.com>  Tue, 15 Jul 2025 16:51:54 +0000
> +
>  kodi-inputstream-adaptive (21.5.9+ds-1) unstable; urgency=high
>  
>    * New upstream version 21.5.9+ds
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/depends/common/pugixml/flags.txt kodi-inputstream-adaptive-21.5.15+ds/depends/common/pugixml/flags.txt
> --- kodi-inputstream-adaptive-21.5.9+ds/depends/common/pugixml/flags.txt	1970-01-01 00:00:00.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/depends/common/pugixml/flags.txt	2025-07-06 07:54:23.000000000 +0000
> @@ -0,0 +1 @@
> +-DCMAKE_POLICY_VERSION_MINIMUM=3.5
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/depends/common/rapidjson/flags.txt kodi-inputstream-adaptive-21.5.15+ds/depends/common/rapidjson/flags.txt
> --- kodi-inputstream-adaptive-21.5.9+ds/depends/common/rapidjson/flags.txt	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/depends/common/rapidjson/flags.txt	2025-07-06 07:54:23.000000000 +0000
> @@ -1 +1 @@
> --DRAPIDJSON_BUILD_DOC=OFF -DRAPIDJSON_BUILD_EXAMPLES=OFF -DRAPIDJSON_BUILD_TESTS=OFF -DRAPIDJSON_BUILD_CXX17=ON -DRAPIDJSON_HAS_STDSTRING=ON
> +-DRAPIDJSON_BUILD_DOC=OFF -DRAPIDJSON_BUILD_EXAMPLES=OFF -DRAPIDJSON_BUILD_TESTS=OFF -DRAPIDJSON_BUILD_CXX17=ON -DRAPIDJSON_HAS_STDSTRING=ON -DCMAKE_POLICY_VERSION_MINIMUM=3.5
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/inputstream.adaptive/addon.xml.in kodi-inputstream-adaptive-21.5.15+ds/inputstream.adaptive/addon.xml.in
> --- kodi-inputstream-adaptive-21.5.9+ds/inputstream.adaptive/addon.xml.in	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/inputstream.adaptive/addon.xml.in	2025-07-06 07:54:23.000000000 +0000
> @@ -1,7 +1,7 @@
>  <?xml version="1.0" encoding="UTF-8"?>
>  <addon
>    id="inputstream.adaptive"
> -  version="21.5.9"
> +  version="21.5.15"
>    name="InputStream Adaptive"
>    provider-name="peak3d, Team Kodi">
>    <requires>@ADDON_DEPENDS@</requires>
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/inputstream.adaptive/changelog.txt kodi-inputstream-adaptive-21.5.15+ds/inputstream.adaptive/changelog.txt
> --- kodi-inputstream-adaptive-21.5.9+ds/inputstream.adaptive/changelog.txt	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/inputstream.adaptive/changelog.txt	2025-07-06 07:54:23.000000000 +0000
> @@ -1,3 +1,26 @@
> +v21.5.15 (2025-07-06)
> +- Fix playback with IP with port number
> +
> +v21.5.14 (2025-05-15)
> +- Fix playback problem due to URLs containing dots on paths
> +
> +v21.5.13 (2025-05-04)
> +- Fix crash with Widevine CDM 4.10.2891.0
> +- [Widevine] Updated CDM interface
> +
> +v21.5.12 (2025-04-27)
> +- [Dash] Fix SegmentTemplate TSB to prevent use extra live delay workarounds
> +
> +v21.5.11 (2025-04-15)
> +- Fix live delay that was causing broken playback in some cases
> +
> +v21.5.10 (2025-04-06)
> +- INFO: Widevine CDM 4.10.2891.0 cannot be used to play videos, if you installed it reinstall an old version
> +- [ClearKey] Fix playback with streams that have mixed unencrypted/encrypted segments
> +- [HLS] Fix timeshift on single period video stream
> +- [HLS] Fix possible repeated frames on live stream with discontinuities
> +- Fix playback with SMIL URL manifests
> +
>  v21.5.9 (2025-01-19)
>  - Fix packaging errors
>  
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/lib/cdm/cdm/debug.cpp kodi-inputstream-adaptive-21.5.15+ds/lib/cdm/cdm/debug.cpp
> --- kodi-inputstream-adaptive-21.5.9+ds/lib/cdm/cdm/debug.cpp	1970-01-01 00:00:00.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/lib/cdm/cdm/debug.cpp	2025-07-06 07:54:23.000000000 +0000
> @@ -0,0 +1,57 @@
> +/*
> + *  Copyright (C) 2025 Team Kodi
> + *  This file is part of Kodi - https://kodi.tv
> + *
> + *  SPDX-License-Identifier: GPL-2.0-or-later
> + *  See LICENSES/README.md for more information.
> + */
> +
> +#include "debug.h"
> +
> +#include <cstdarg>
> +#include <cstdio>
> +
> +#ifdef _MSC_VER
> +#define snprintf _snprintf
> +#endif
> +
> +struct dbgContext
> +{
> +  const char* name;
> +  void (*msgCallback)(const CDM_DBG::LogLevel level, const char* msg);
> +};
> +
> +dbgContext debugContext = {"WV-CDM-Library", nullptr};
> +
> +void CDM_DBG::Log(const CDM_DBG::LogLevel level, const char* format, ...)
> +{
> +  if (!debugContext.msgCallback)
> +    return;
> +
> +  char msg[2048];
> +  const int len = snprintf(msg, sizeof(msg), "[%s] ", debugContext.name);
> +  if (len < 0 || len >= sizeof(msg))
> +  {
> +    debugContext.msgCallback(CDM_DBG::LogLevel::ERROR,
> +                             "Cannot print log string: Context name too long");
> +    return;
> +  }
> +
> +  va_list ap;
> +  va_start(ap, format);
> +  const int formattedLen = vsnprintf(msg + len, sizeof(msg) - len, format, ap);
> +  va_end(ap);
> +  if (formattedLen < 0 || formattedLen >= (sizeof(msg) - len))
> +  {
> +    debugContext.msgCallback(CDM_DBG::LogLevel::ERROR,
> +                             "Cannot print log string: Text content too long");
> +    return;
> +  }
> +
> +  debugContext.msgCallback(level, msg);
> +}
> +
> +void CDM_DBG::SetDBGMsgCallback(void (*msgcb)(const CDM_DBG::LogLevel level, const char* msg))
> +{
> +  debugContext.msgCallback = msgcb;
> +}
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/lib/cdm/cdm/debug.h kodi-inputstream-adaptive-21.5.15+ds/lib/cdm/cdm/debug.h
> --- kodi-inputstream-adaptive-21.5.9+ds/lib/cdm/cdm/debug.h	1970-01-01 00:00:00.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/lib/cdm/cdm/debug.h	2025-07-06 07:54:23.000000000 +0000
> @@ -0,0 +1,27 @@
> +/*
> + *  Copyright (C) 2025 Team Kodi
> + *  This file is part of Kodi - https://kodi.tv
> + *
> + *  SPDX-License-Identifier: GPL-2.0-or-later
> + *  See LICENSES/README.md for more information.
> + */
> +
> +#pragma once
> +
> +namespace CDM_DBG
> +{
> +enum class LogLevel
> +{
> +  DEBUG,
> +  INFO,
> +  WARNING,
> +  ERROR,
> +  FATAL
> +};
> +
> +void Log(const LogLevel level, const char* format, ...);
> +#define LogF(level, format, ...) Log((level), ("%s: " format), __FUNCTION__, ##__VA_ARGS__)
> +
> +void SetDBGMsgCallback(void (*msgcb)(const LogLevel level, const char* msg));
> +
> +} // namespace cdm
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/lib/cdm/cdm/media/cdm/api/content_decryption_module.h kodi-inputstream-adaptive-21.5.15+ds/lib/cdm/cdm/media/cdm/api/content_decryption_module.h
> --- kodi-inputstream-adaptive-21.5.9+ds/lib/cdm/cdm/media/cdm/api/content_decryption_module.h	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/lib/cdm/cdm/media/cdm/api/content_decryption_module.h	2025-07-06 07:54:23.000000000 +0000
> @@ -12,7 +12,6 @@
>  #include <type_traits>
>  
>  #include "content_decryption_module_export.h"
> -#include "content_decryption_module_proxy.h"
>  
>  #if defined(_MSC_VER)
>  typedef unsigned char uint8_t;
> @@ -51,1731 +50,1728 @@
>  
>  extern "C" {
>  
> -  CDM_API void INITIALIZE_CDM_MODULE();
> +CDM_API void INITIALIZE_CDM_MODULE();
>  
> -  CDM_API void DeinitializeCdmModule();
> +CDM_API void DeinitializeCdmModule();
>  
> -  // Returns a pointer to the requested CDM Host interface upon success.
> -  // Returns NULL if the requested CDM Host interface is not supported.
> -  // The caller should cast the returned pointer to the type matching
> -  // |host_interface_version|.
> -  typedef void* (*GetCdmHostFunc)(int host_interface_version, void* user_data);
> -
> -  // Returns a pointer to the requested CDM upon success.
> -  // Returns NULL if an error occurs or the requested |cdm_interface_version| or
> -  // |key_system| is not supported or another error occurs.
> -  // The caller should cast the returned pointer to the type matching
> -  // |cdm_interface_version|.
> -  // Caller retains ownership of arguments and must call Destroy() on the returned
> -  // object.
> -  CDM_API void* CreateCdmInstance(int cdm_interface_version,
> -    const char* key_system,
> -    uint32_t key_system_size,
> -    GetCdmHostFunc get_cdm_host_func,
> -    void* user_data);
> +// Returns a pointer to the requested CDM Host interface upon success.
> +// Returns NULL if the requested CDM Host interface is not supported.
> +// The caller should cast the returned pointer to the type matching
> +// |host_interface_version|.
> +typedef void* (*GetCdmHostFunc)(int host_interface_version, void* user_data);
> +
> +// Returns a pointer to the requested CDM upon success.
> +// Returns NULL if an error occurs or the requested |cdm_interface_version| or
> +// |key_system| is not supported or another error occurs.
> +// The caller should cast the returned pointer to the type matching
> +// |cdm_interface_version|.
> +// Caller retains ownership of arguments and must call Destroy() on the returned
> +// object.
> +CDM_API void* CreateCdmInstance(int cdm_interface_version,
> +                                const char* key_system,
> +                                uint32_t key_system_size,
> +                                GetCdmHostFunc get_cdm_host_func,
> +                                void* user_data);
>  
> -  CDM_API const char* GetCdmVersion();
> +CDM_API const char* GetCdmVersion();
>  
>  }  // extern "C"
>  
>  namespace cdm {
>  
> -  enum Status : uint32_t {
> -    kSuccess = 0,
> -    kNeedMoreData,  // Decoder needs more data to produce a decoded frame/sample.
> -    kNoKey,         // The required decryption key is not available.
> -    kInitializationError,    // Initialization error.
> -    kDecryptError,           // Decryption failed.
> -    kDecodeError,            // Error decoding audio or video.
> -    kDeferredInitialization  // Decoder is not ready for initialization.
> -  };
> -  CHECK_TYPE(Status, 4, 4);
> -
> -  // Exceptions used by the CDM to reject promises.
> -  // https://w3c.github.io/encrypted-media/#exceptions
> -  enum Exception : uint32_t {
> -    kExceptionTypeError,
> -    kExceptionNotSupportedError,
> -    kExceptionInvalidStateError,
> -    kExceptionQuotaExceededError
> -  };
> -  CHECK_TYPE(Exception, 4, 4);
> -
> -  // The encryption scheme. The definitions are from ISO/IEC 23001-7:2016.
> -  enum class EncryptionScheme : uint32_t {
> -    kUnencrypted = 0,
> -    kCenc,  // 'cenc' subsample encryption using AES-CTR mode.
> -    kCbcs   // 'cbcs' pattern encryption using AES-CBC mode.
> -  };
> -  CHECK_TYPE(EncryptionScheme, 4, 4);
> -
> -  // The pattern used for pattern encryption. Note that ISO/IEC 23001-7:2016
> -  // defines each block to be 16-bytes.
> -  struct Pattern {
> -    uint32_t crypt_byte_block;  // Count of the encrypted blocks.
> -    uint32_t skip_byte_block;   // Count of the unencrypted blocks.
> -  };
> -  CHECK_TYPE(Pattern, 8, 8);
> -
> -  enum class ColorRange : uint8_t {
> -    kInvalid,
> -    kLimited,  // 709 color range with RGB values ranging from 16 to 235.
> -    kFull,     // Full RGB color range with RGB values from 0 to 255.
> -    kDerived   // Range is defined by |transfer_id| and |matrix_id|.
> -  };
> -  CHECK_TYPE(ColorRange, 1, 1);
> -
> -  // Described in ISO 23001-8:2016, section 7. All the IDs are in the range
> -  // [0, 255] so 8-bit integer is sufficient. An unspecified ColorSpace should be
> -  // {2, 2, 2, ColorRange::kInvalid}, where value 2 means "Unspecified" for all
> -  // the IDs, as defined by the spec.
> -  struct ColorSpace {
> -    uint8_t primary_id;   // 7.1 colour primaries, table 2
> -    uint8_t transfer_id;  // 7.2 transfer characteristics, table 3
> -    uint8_t matrix_id;    // 7.3 matrix coefficients, table 4
> -    ColorRange range;
> -  };
> -  CHECK_TYPE(ColorSpace, 4, 4);
> -
> -  // Time is defined as the number of seconds since the Epoch
> -  // (00:00:00 UTC, January 1, 1970), not including any added leap second.
> -  // Also see Time definition in spec: https://w3c.github.io/encrypted-media/#time
> -  // Note that Time is defined in millisecond accuracy in the spec but in second
> -  // accuracy here.
> -  typedef double Time;
> -
> -  // An input buffer can be split into several continuous subsamples.
> -  // A SubsampleEntry specifies the number of clear and cipher bytes in each
> -  // subsample. For example, the following buffer has three subsamples:
> -  //
> -  // |<----- subsample1 ----->|<----- subsample2 ----->|<----- subsample3 ----->|
> -  // |   clear1   |  cipher1  |  clear2  |   cipher2   | clear3 |    cipher3    |
> -  //
> -  // For decryption, all of the cipher bytes in a buffer should be concatenated
> -  // (in the subsample order) into a single logical stream. The clear bytes should
> -  // not be considered as part of decryption.
> -  //
> -  // Stream to decrypt:   |  cipher1  |   cipher2   |    cipher3    |
> -  // Decrypted stream:    | decrypted1|  decrypted2 |   decrypted3  |
> -  //
> -  // After decryption, the decrypted bytes should be copied over the position
> -  // of the corresponding cipher bytes in the original buffer to form the output
> -  // buffer. Following the above example, the decrypted buffer should be:
> +enum Status : uint32_t {
> +  kSuccess = 0,
> +  kNeedMoreData,  // Decoder needs more data to produce a decoded frame/sample.
> +  kNoKey,         // The required decryption key is not available.
> +  kInitializationError,    // Initialization error.
> +  kDecryptError,           // Decryption failed.
> +  kDecodeError,            // Error decoding audio or video.
> +  kDeferredInitialization  // Decoder is not ready for initialization.
> +};
> +CHECK_TYPE(Status, 4, 4);
> +
> +// Exceptions used by the CDM to reject promises.
> +// https://w3c.github.io/encrypted-media/#exceptions
> +enum Exception : uint32_t {
> +  kExceptionTypeError,
> +  kExceptionNotSupportedError,
> +  kExceptionInvalidStateError,
> +  kExceptionQuotaExceededError
> +};
> +CHECK_TYPE(Exception, 4, 4);
> +
> +// The encryption scheme. The definitions are from ISO/IEC 23001-7:2016.
> +enum class EncryptionScheme : uint32_t {
> +  kUnencrypted = 0,
> +  kCenc,  // 'cenc' subsample encryption using AES-CTR mode.
> +  kCbcs   // 'cbcs' pattern encryption using AES-CBC mode.
> +};
> +CHECK_TYPE(EncryptionScheme, 4, 4);
> +
> +// The pattern used for pattern encryption. Note that ISO/IEC 23001-7:2016
> +// defines each block to be 16-bytes.
> +struct Pattern {
> +  uint32_t crypt_byte_block;  // Count of the encrypted blocks.
> +  uint32_t skip_byte_block;   // Count of the unencrypted blocks.
> +};
> +CHECK_TYPE(Pattern, 8, 8);
> +
> +enum class ColorRange : uint8_t {
> +  kInvalid,
> +  kLimited,  // 709 color range with RGB values ranging from 16 to 235.
> +  kFull,     // Full RGB color range with RGB values from 0 to 255.
> +  kDerived   // Range is defined by |transfer_id| and |matrix_id|.
> +};
> +CHECK_TYPE(ColorRange, 1, 1);
> +
> +// Described in ISO 23001-8:2016, section 7. All the IDs are in the range
> +// [0, 255] so 8-bit integer is sufficient. An unspecified ColorSpace should be
> +// {2, 2, 2, ColorRange::kInvalid}, where value 2 means "Unspecified" for all
> +// the IDs, as defined by the spec.
> +struct ColorSpace {
> +  uint8_t primary_id;   // 7.1 colour primaries, table 2
> +  uint8_t transfer_id;  // 7.2 transfer characteristics, table 3
> +  uint8_t matrix_id;    // 7.3 matrix coefficients, table 4
> +  ColorRange range;
> +};
> +CHECK_TYPE(ColorSpace, 4, 4);
> +
> +// Time is defined as the number of seconds since the Epoch
> +// (00:00:00 UTC, January 1, 1970), not including any added leap second.
> +// Also see Time definition in spec: https://w3c.github.io/encrypted-media/#time
> +// Note that Time is defined in millisecond accuracy in the spec but in second
> +// accuracy here.
> +typedef double Time;
> +
> +// An input buffer can be split into several continuous subsamples.
> +// A SubsampleEntry specifies the number of clear and cipher bytes in each
> +// subsample. For example, the following buffer has three subsamples:
> +//
> +// |<----- subsample1 ----->|<----- subsample2 ----->|<----- subsample3 ----->|
> +// |   clear1   |  cipher1  |  clear2  |   cipher2   | clear3 |    cipher3    |
> +//
> +// For decryption, all of the cipher bytes in a buffer should be concatenated
> +// (in the subsample order) into a single logical stream. The clear bytes should
> +// not be considered as part of decryption.
> +//
> +// Stream to decrypt:   |  cipher1  |   cipher2   |    cipher3    |
> +// Decrypted stream:    | decrypted1|  decrypted2 |   decrypted3  |
> +//
> +// After decryption, the decrypted bytes should be copied over the position
> +// of the corresponding cipher bytes in the original buffer to form the output
> +// buffer. Following the above example, the decrypted buffer should be:
> +//
> +// |<----- subsample1 ----->|<----- subsample2 ----->|<----- subsample3 ----->|
> +// |   clear1   | decrypted1|  clear2  |  decrypted2 | clear3 |   decrypted3  |
> +//
> +struct SubsampleEntry {
> +  uint32_t clear_bytes;
> +  uint32_t cipher_bytes;
> +};
> +CHECK_TYPE(SubsampleEntry, 8, 8);
> +
> +// Represents an input buffer to be decrypted (and possibly decoded). It does
> +// not own any pointers in this struct. If |encryption_scheme| = kUnencrypted,
> +// the data is unencrypted.
> +// Note that this struct is organized so that sizeof(InputBuffer_2)
> +// equals the sum of sizeof() all members in both 32-bit and 64-bit compiles.
> +// Padding has been added to keep the fields aligned.
> +struct InputBuffer_2 {
> +  const uint8_t* data;  // Pointer to the beginning of the input data.
> +  uint32_t data_size;   // Size (in bytes) of |data|.
> +
> +  EncryptionScheme encryption_scheme;
> +
> +  const uint8_t* key_id;  // Key ID to identify the decryption key.
> +  uint32_t key_id_size;   // Size (in bytes) of |key_id|.
> +  uint32_t : 32;          // Padding.
> +
> +  const uint8_t* iv;  // Initialization vector.
> +  uint32_t iv_size;   // Size (in bytes) of |iv|.
> +  uint32_t : 32;      // Padding.
> +
> +  const struct SubsampleEntry* subsamples;
> +  uint32_t num_subsamples;  // Number of subsamples in |subsamples|.
> +  uint32_t : 32;            // Padding.
> +
> +  // |pattern| is required if |encryption_scheme| specifies pattern encryption.
> +  Pattern pattern;
> +
> +  int64_t timestamp;  // Presentation timestamp in microseconds.
> +};
> +CHECK_TYPE(InputBuffer_2, 64, 80);
> +
> +enum AudioCodec : uint32_t { kUnknownAudioCodec = 0, kCodecVorbis, kCodecAac };
> +CHECK_TYPE(AudioCodec, 4, 4);
> +
> +struct AudioDecoderConfig_2 {
> +  AudioCodec codec;
> +  int32_t channel_count;
> +  int32_t bits_per_channel;
> +  int32_t samples_per_second;
> +
> +  // Optional byte data required to initialize audio decoders, such as the
> +  // vorbis setup header.
> +  uint8_t* extra_data;
> +  uint32_t extra_data_size;
> +
> +  // Encryption scheme.
> +  EncryptionScheme encryption_scheme;
> +};
> +CHECK_TYPE(AudioDecoderConfig_2, 28, 32);
> +
> +// Supported sample formats for AudioFrames.
> +enum AudioFormat : uint32_t {
> +  kUnknownAudioFormat = 0,  // Unknown format value. Used for error reporting.
> +  kAudioFormatU8,           // Interleaved unsigned 8-bit w/ bias of 128.
> +  kAudioFormatS16,          // Interleaved signed 16-bit.
> +  kAudioFormatS32,          // Interleaved signed 32-bit.
> +  kAudioFormatF32,          // Interleaved float 32-bit.
> +  kAudioFormatPlanarS16,    // Signed 16-bit planar.
> +  kAudioFormatPlanarF32,    // Float 32-bit planar.
> +};
> +CHECK_TYPE(AudioFormat, 4, 4);
> +
> +// Surface formats based on FOURCC labels, see: http://www.fourcc.org/yuv.php
> +// Values are chosen to be consistent with Chromium's VideoPixelFormat values.
> +enum VideoFormat : uint32_t {
> +  kUnknownVideoFormat = 0,  // Unknown format value. Used for error reporting.
> +  kYv12 = 1,                // 12bpp YVU planar 1x1 Y, 2x2 VU samples.
> +  kI420 = 2,                // 12bpp YUV planar 1x1 Y, 2x2 UV samples.
> +
> +  // In the following formats, each sample uses 16-bit in storage, while the
> +  // sample value is stored in the least significant N bits where N is
> +  // specified by the number after "P". For example, for YUV420P9, each Y, U,
> +  // and V sample is stored in the least significant 9 bits in a 2-byte block.
> +  kYUV420P9 = 16,
> +  kYUV420P10 = 17,
> +  kYUV422P9 = 18,
> +  kYUV422P10 = 19,
> +  kYUV444P9 = 20,
> +  kYUV444P10 = 21,
> +  kYUV420P12 = 22,
> +  kYUV422P12 = 23,
> +  kYUV444P12 = 24,
> +};
> +CHECK_TYPE(VideoFormat, 4, 4);
> +
> +struct Size {
> +  int32_t width;
> +  int32_t height;
> +};
> +CHECK_TYPE(Size, 8, 8);
> +
> +enum VideoCodec : uint32_t {
> +  kUnknownVideoCodec = 0,
> +  kCodecVp8,
> +  kCodecH264,
> +  kCodecVp9,
> +  kCodecAv1
> +};
> +CHECK_TYPE(VideoCodec, 4, 4);
> +
> +enum VideoCodecProfile : uint32_t {
> +  kUnknownVideoCodecProfile = 0,
> +  kProfileNotNeeded,
> +  kH264ProfileBaseline,
> +  kH264ProfileMain,
> +  kH264ProfileExtended,
> +  kH264ProfileHigh,
> +  kH264ProfileHigh10,
> +  kH264ProfileHigh422,
> +  kH264ProfileHigh444Predictive,
> +  kVP9Profile0,
> +  kVP9Profile1,
> +  kVP9Profile2,
> +  kVP9Profile3,
> +  kAv1ProfileMain,
> +  kAv1ProfileHigh,
> +  kAv1ProfilePro
> +};
> +CHECK_TYPE(VideoCodecProfile, 4, 4);
> +
> +// Deprecated: New CDM implementations should use VideoDecoderConfig_3.
> +// Note that this struct is organized so that sizeof(VideoDecoderConfig_2)
> +// equals the sum of sizeof() all members in both 32-bit and 64-bit compiles.
> +// Padding has been added to keep the fields aligned.
> +struct VideoDecoderConfig_2 {
> +  VideoCodec codec;
> +  VideoCodecProfile profile;
> +  VideoFormat format;
> +  uint32_t : 32;  // Padding.
> +
> +  // Width and height of video frame immediately post-decode. Not all pixels
> +  // in this region are valid.
> +  Size coded_size;
> +
> +  // Optional byte data required to initialize video decoders, such as H.264
> +  // AAVC data.
> +  uint8_t* extra_data;
> +  uint32_t extra_data_size;
> +
> +  // Encryption scheme.
> +  EncryptionScheme encryption_scheme;
> +};
> +CHECK_TYPE(VideoDecoderConfig_2, 36, 40);
> +
> +struct VideoDecoderConfig_3 {
> +  VideoCodec codec;
> +  VideoCodecProfile profile;
> +  VideoFormat format;
> +  ColorSpace color_space;
> +
> +  // Width and height of video frame immediately post-decode. Not all pixels
> +  // in this region are valid.
> +  Size coded_size;
> +
> +  // Optional byte data required to initialize video decoders, such as H.264
> +  // AAVC data.
> +  uint8_t* extra_data;
> +  uint32_t extra_data_size;
> +
> +  EncryptionScheme encryption_scheme;
> +};
> +CHECK_TYPE(VideoDecoderConfig_3, 36, 40);
> +
> +enum StreamType : uint32_t { kStreamTypeAudio = 0, kStreamTypeVideo = 1 };
> +CHECK_TYPE(StreamType, 4, 4);
> +
> +// Structure provided to ContentDecryptionModule::OnPlatformChallengeResponse()
> +// after a platform challenge was initiated via Host::SendPlatformChallenge().
> +// All values will be NULL / zero in the event of a challenge failure.
> +struct PlatformChallengeResponse {
> +  // |challenge| provided during Host::SendPlatformChallenge() combined with
> +  // nonce data and signed with the platform's private key.
> +  const uint8_t* signed_data;
> +  uint32_t signed_data_length;
> +
> +  // RSASSA-PKCS1-v1_5-SHA256 signature of the |signed_data| block.
> +  const uint8_t* signed_data_signature;
> +  uint32_t signed_data_signature_length;
> +
> +  // X.509 device specific certificate for the |service_id| requested.
> +  const uint8_t* platform_key_certificate;
> +  uint32_t platform_key_certificate_length;
> +};
> +CHECK_TYPE(PlatformChallengeResponse, 24, 48);
> +
> +// The current status of the associated key. The valid types are defined in the
> +// spec: https://w3c.github.io/encrypted-media/#dom-mediakeystatus
> +enum KeyStatus : uint32_t {
> +  kUsable = 0,
> +  kInternalError = 1,
> +  kExpired = 2,
> +  kOutputRestricted = 3,
> +  kOutputDownscaled = 4,
> +  kStatusPending = 5,
> +  kReleased = 6
> +};
> +CHECK_TYPE(KeyStatus, 4, 4);
> +
> +// The current status of the associated key. The valid types are defined in the
> +// spec: https://w3c.github.io/encrypted-media/#dom-mediakeystatus
> +// Note: For forward compatibility, Host implementations must gracefully handle
> +// unexpected (new) enum values, e.g. no-op. This is used by the CDM Interfaces
> +// starting from CDM_12.
> +enum class KeyStatus_2 : uint32_t {
> +  kUsable = 0,
> +  kInternalError = 1,
> +  kExpired = 2,
> +  kOutputRestricted = 3,
> +  kOutputDownscaled = 4,
> +  kStatusPending = 5,
> +  kReleased = 6,
> +  kUsableInFuture = 7
> +};
> +CHECK_TYPE(KeyStatus_2, 4, 4);
> +
> +// Used when passing arrays of key information. Does not own the referenced
> +// data. |system_code| is an additional error code for unusable keys and
> +// should be 0 when |status| == kUsable.
> +struct KeyInformation {
> +  const uint8_t* key_id;
> +  uint32_t key_id_size;
> +  KeyStatus status;
> +  uint32_t system_code;
> +};
> +CHECK_TYPE(KeyInformation, 16, 24);
> +
> +// Used when passing arrays of key information. Does not own the referenced
> +// data. |system_code| is an additional error code for unusable keys and
> +// should be 0 when |status| == kUsable. Used by CDM12 and beyond.
> +struct KeyInformation_2 {
> +  const uint8_t* key_id;
> +  uint32_t key_id_size;
> +  KeyStatus_2 status;
> +  uint32_t system_code;
> +};
> +CHECK_TYPE(KeyInformation_2, 16, 24);
> +
> +// Supported output protection methods for use with EnableOutputProtection() and
> +// returned by OnQueryOutputProtectionStatus().
> +enum OutputProtectionMethods : uint32_t {
> +  kProtectionNone = 0,
> +  kProtectionHDCP = 1 << 0
> +};
> +CHECK_TYPE(OutputProtectionMethods, 4, 4);
> +
> +// Connected output link types returned by OnQueryOutputProtectionStatus().
> +enum OutputLinkTypes : uint32_t {
> +  kLinkTypeNone = 0,
> +  kLinkTypeUnknown = 1 << 0,
> +  kLinkTypeInternal = 1 << 1,
> +  kLinkTypeVGA = 1 << 2,
> +  kLinkTypeHDMI = 1 << 3,
> +  kLinkTypeDVI = 1 << 4,
> +  kLinkTypeDisplayPort = 1 << 5,
> +  kLinkTypeNetwork = 1 << 6
> +};
> +CHECK_TYPE(OutputLinkTypes, 4, 4);
> +
> +// Result of the QueryOutputProtectionStatus() call.
> +enum QueryResult : uint32_t { kQuerySucceeded = 0, kQueryFailed };
> +CHECK_TYPE(QueryResult, 4, 4);
> +
> +// The Initialization Data Type. The valid types are defined in the spec:
> +// https://w3c.github.io/encrypted-media/format-registry/initdata/index.html#registry
> +enum InitDataType : uint32_t { kCenc = 0, kKeyIds = 1, kWebM = 2 };
> +CHECK_TYPE(InitDataType, 4, 4);
> +
> +// The type of session to create. The valid types are defined in the spec:
> +// https://w3c.github.io/encrypted-media/#dom-mediakeysessiontype
> +enum SessionType : uint32_t {
> +  kTemporary = 0,
> +  kPersistentLicense = 1
> +};
> +CHECK_TYPE(SessionType, 4, 4);
> +
> +// The type of the message event.  The valid types are defined in the spec:
> +// https://w3c.github.io/encrypted-media/#dom-mediakeymessagetype
> +enum MessageType : uint32_t {
> +  kLicenseRequest = 0,
> +  kLicenseRenewal = 1,
> +  kLicenseRelease = 2,
> +  kIndividualizationRequest = 3
> +};
> +CHECK_TYPE(MessageType, 4, 4);
> +
> +enum HdcpVersion : uint32_t {
> +  kHdcpVersionNone,
> +  kHdcpVersion1_0,
> +  kHdcpVersion1_1,
> +  kHdcpVersion1_2,
> +  kHdcpVersion1_3,
> +  kHdcpVersion1_4,
> +  kHdcpVersion2_0,
> +  kHdcpVersion2_1,
> +  kHdcpVersion2_2,
> +  kHdcpVersion2_3
> +};
> +CHECK_TYPE(HdcpVersion, 4, 4);
> +
> +struct Policy {
> +  HdcpVersion min_hdcp_version;
> +};
> +CHECK_TYPE(Policy, 4, 4);
> +
> +// Represents a buffer created by Allocator implementations.
> +class CDM_CLASS_API Buffer {
> + public:
> +  // Destroys the buffer in the same context as it was created.
> +  virtual void Destroy() = 0;
> +
> +  virtual uint32_t Capacity() const = 0;
> +  virtual uint8_t* Data() = 0;
> +  virtual void SetSize(uint32_t size) = 0;
> +  virtual uint32_t Size() const = 0;
> +
> + protected:
> +  Buffer() {}
> +  virtual ~Buffer() {}
> +
> + private:
> +  Buffer(const Buffer&);
> +  void operator=(const Buffer&);
> +};
> +
> +// Represents a decrypted block that has not been decoded.
> +class CDM_CLASS_API DecryptedBlock {
> + public:
> +  virtual void SetDecryptedBuffer(Buffer* buffer) = 0;
> +  virtual Buffer* DecryptedBuffer() = 0;
> +
> +  // TODO(tomfinegan): Figure out if timestamp is really needed. If it is not,
> +  // we can just pass Buffer pointers around.
> +  virtual void SetTimestamp(int64_t timestamp) = 0;
> +  virtual int64_t Timestamp() const = 0;
> +
> + protected:
> +  DecryptedBlock() {}
> +  virtual ~DecryptedBlock() {}
> +};
> +
> +// This intentionally avoids using an enum, since it will be used to do math
> +// with other enums, which is deprecated in C++20.
> +using VideoPlane = uint32_t;
> +constexpr VideoPlane kYPlane = 0;
> +constexpr VideoPlane kUPlane = 1;
> +constexpr VideoPlane kVPlane = 2;
> +constexpr VideoPlane kMaxPlanes = 3;
> +CHECK_TYPE(VideoPlane, 4, 4);
> +
> +class CDM_CLASS_API VideoFrame {
> + public:
> +  virtual void SetFormat(VideoFormat format) = 0;
> +  virtual VideoFormat Format() const = 0;
> +
> +  virtual void SetSize(cdm::Size size) = 0;
> +  virtual cdm::Size Size() const = 0;
> +
> +  virtual void SetFrameBuffer(Buffer* frame_buffer) = 0;
> +  virtual Buffer* FrameBuffer() = 0;
> +
> +  virtual void SetPlaneOffset(VideoPlane plane, uint32_t offset) = 0;
> +  virtual uint32_t PlaneOffset(VideoPlane plane) = 0;
> +
> +  virtual void SetStride(VideoPlane plane, uint32_t stride) = 0;
> +  virtual uint32_t Stride(VideoPlane plane) = 0;
> +
> +  // Sets and gets the presentation timestamp which is in microseconds.
> +  virtual void SetTimestamp(int64_t timestamp) = 0;
> +  virtual int64_t Timestamp() const = 0;
> +
> + protected:
> +  VideoFrame() {}
> +  virtual ~VideoFrame() {}
> +};
> +
> +// Represents a decoded video frame. The CDM should call the interface methods
> +// to set the frame attributes. See DecryptAndDecodeFrame().
> +class CDM_CLASS_API VideoFrame_2 {
> + public:
> +  virtual void SetFormat(VideoFormat format) = 0;
> +  virtual void SetSize(cdm::Size size) = 0;
> +  virtual void SetFrameBuffer(Buffer* frame_buffer) = 0;
> +  virtual void SetPlaneOffset(VideoPlane plane, uint32_t offset) = 0;
> +  virtual void SetStride(VideoPlane plane, uint32_t stride) = 0;
> +  // Sets the presentation timestamp which is in microseconds.
> +  virtual void SetTimestamp(int64_t timestamp) = 0;
> +  virtual void SetColorSpace(ColorSpace color_space) = 0;
> +
> + protected:
> +  VideoFrame_2() {}
> +  virtual ~VideoFrame_2() {}
> +};
> +
> +// Represents decrypted and decoded audio frames. AudioFrames can contain
> +// multiple audio output buffers, which are serialized into this format:
> +//
> +// |<------------------- serialized audio buffer ------------------->|
> +// | int64_t timestamp | int64_t length | length bytes of audio data |
> +//
> +// For example, with three audio output buffers, the AudioFrames will look
> +// like this:
> +//
> +// |<----------------- AudioFrames ------------------>|
> +// | audio buffer 0 | audio buffer 1 | audio buffer 2 |
> +class CDM_CLASS_API AudioFrames {
> + public:
> +  virtual void SetFrameBuffer(Buffer* buffer) = 0;
> +  virtual Buffer* FrameBuffer() = 0;
> +
> +  // The CDM must call this method, providing a valid format, when providing
> +  // frame buffers. Planar data should be stored end to end; e.g.,
> +  // |ch1 sample1||ch1 sample2|....|ch1 sample_last||ch2 sample1|...
> +  virtual void SetFormat(AudioFormat format) = 0;
> +  virtual AudioFormat Format() const = 0;
> +
> + protected:
> +  AudioFrames() {}
> +  virtual ~AudioFrames() {}
> +};
> +
> +// FileIO interface provides a way for the CDM to store data in a file in
> +// persistent storage. This interface aims only at providing basic read/write
> +// capabilities and should not be used as a full fledged file IO API.
> +// Each CDM and origin (e.g. HTTPS, "foo.example.com", 443) combination has
> +// its own persistent storage. All instances of a given CDM associated with a
> +// given origin share the same persistent storage.
> +// Note to implementors of this interface:
> +// Per-origin storage and the ability for users to clear it are important.
> +// See http://www.w3.org/TR/encrypted-media/#privacy-storedinfo.
> +class CDM_CLASS_API FileIO {
> + public:
> +  // Opens the file with |file_name| for read and write.
> +  // FileIOClient::OnOpenComplete() will be called after the opening
> +  // operation finishes.
> +  // - When the file is opened by a CDM instance, it will be classified as "in
> +  //   use". In this case other CDM instances in the same domain may receive
> +  //   kInUse status when trying to open it.
> +  // - |file_name| must only contain letters (A-Za-z), digits(0-9), or "._-".
> +  //   It must not start with an underscore ('_'), and must be at least 1
> +  //   character and no more than 256 characters long.
> +  virtual void Open(const char* file_name, uint32_t file_name_size) = 0;
> +
> +  // Reads the contents of the file. FileIOClient::OnReadComplete() will be
> +  // called with the read status. Read() should not be called if a previous
> +  // Read() or Write() call is still pending; otherwise OnReadComplete() will
> +  // be called with kInUse.
> +  virtual void Read() = 0;
> +
> +  // Writes |data_size| bytes of |data| into the file.
> +  // FileIOClient::OnWriteComplete() will be called with the write status.
> +  // All existing contents in the file will be overwritten. Calling Write() with
> +  // NULL |data| will clear all contents in the file. Write() should not be
> +  // called if a previous Write() or Read() call is still pending; otherwise
> +  // OnWriteComplete() will be called with kInUse.
> +  virtual void Write(const uint8_t* data, uint32_t data_size) = 0;
> +
> +  // Closes the file if opened, destroys this FileIO object and releases any
> +  // resources allocated. The CDM must call this method when it finished using
> +  // this object. A FileIO object must not be used after Close() is called.
> +  virtual void Close() = 0;
> +
> + protected:
> +  FileIO() {}
> +  virtual ~FileIO() {}
> +};
> +
> +// Responses to FileIO calls. All responses will be called asynchronously.
> +// When kError is returned, the FileIO object could be in an error state. All
> +// following calls (other than Close()) could return kError. The CDM should
> +// still call Close() to destroy the FileIO object.
> +class CDM_CLASS_API FileIOClient {
> + public:
> +  enum class Status : uint32_t { kSuccess = 0, kInUse, kError };
> +
> +  // Response to a FileIO::Open() call with the open |status|.
> +  virtual void OnOpenComplete(Status status) = 0;
> +
> +  // Response to a FileIO::Read() call to provide |data_size| bytes of |data|
> +  // read from the file.
> +  // - kSuccess indicates that all contents of the file has been successfully
> +  //   read. In this case, 0 |data_size| means that the file is empty.
> +  // - kInUse indicates that there are other read/write operations pending.
> +  // - kError indicates read failure, e.g. the storage is not open or cannot be
> +  //   fully read.
> +  virtual void OnReadComplete(Status status,
> +                              const uint8_t* data,
> +                              uint32_t data_size) = 0;
> +
> +  // Response to a FileIO::Write() call.
> +  // - kSuccess indicates that all the data has been written into the file
> +  //   successfully.
> +  // - kInUse indicates that there are other read/write operations pending.
> +  // - kError indicates write failure, e.g. the storage is not open or cannot be
> +  //   fully written. Upon write failure, the contents of the file should be
> +  //   regarded as corrupt and should not used.
> +  virtual void OnWriteComplete(Status status) = 0;
> +
> + protected:
> +  FileIOClient() {}
> +  virtual ~FileIOClient() {}
> +};
> +
> +// Metrics that will be reported from the CDM through the ReportMetrics()
> +// function. To add a new metric, please add it to the end of this enum list
> +// without changing any existing enum values.
> +// Note: For forward compatibility, Host implementations must gracefully handle
> +// unexpected (new) enum values, e.g. no-op.
> +enum MetricName : uint32_t {
> +  kSdkVersion,
> +  kCertificateSerialNumber,
> +  kDecoderBypassBlockCount,
> +};
> +CHECK_TYPE(MetricName, 4, 4);
> +
> +class CDM_CLASS_API Host_10;
> +class CDM_CLASS_API Host_11;
> +class CDM_CLASS_API Host_12;
> +
> +// ContentDecryptionModule interface that all CDMs need to implement.
> +// The interface is versioned for backward compatibility.
> +// Note: ContentDecryptionModule implementations must use the allocator
> +// provided in CreateCdmInstance() to allocate any Buffer that needs to
> +// be passed back to the caller. Implementations must call Buffer::Destroy()
> +// when a Buffer is created that will never be returned to the caller.
> +class CDM_CLASS_API ContentDecryptionModule_10 {
> + public:
> +  static const int kVersion = 10;
> +  static const bool kIsStable = true;
> +  typedef Host_10 Host;
> +
> +  // Initializes the CDM instance, providing information about permitted
> +  // functionalities. The CDM must respond by calling Host::OnInitialized()
> +  // with whether the initialization succeeded. No other calls will be made by
> +  // the host before Host::OnInitialized() returns.
> +  // If |allow_distinctive_identifier| is false, messages from the CDM,
> +  // such as message events, must not contain a Distinctive Identifier,
> +  // even in an encrypted form.
> +  // If |allow_persistent_state| is false, the CDM must not attempt to
> +  // persist state. Calls to CreateFileIO() will fail.
> +  // If |use_hw_secure_codecs| is true, the CDM must ensure the decryption key
> +  // and video buffers (compressed and uncompressed) are securely protected by
> +  // hardware.
> +  virtual void Initialize(bool allow_distinctive_identifier,
> +                          bool allow_persistent_state,
> +                          bool use_hw_secure_codecs) = 0;
> +
> +  // Gets the key status if the CDM has a hypothetical key with the |policy|.
> +  // The CDM must respond by calling either Host::OnResolveKeyStatusPromise()
> +  // with the result key status or Host::OnRejectPromise() if an unexpected
> +  // error happened or this method is not supported.
> +  virtual void GetStatusForPolicy(uint32_t promise_id,
> +                                  const Policy& policy) = 0;
> +
> +  // SetServerCertificate(), CreateSessionAndGenerateRequest(), LoadSession(),
> +  // UpdateSession(), CloseSession(), and RemoveSession() all accept a
> +  // |promise_id|, which must be passed to the completion Host method
> +  // (e.g. Host::OnResolveNewSessionPromise()).
> +
> +  // Provides a server certificate to be used to encrypt messages to the
> +  // license server. The CDM must respond by calling either
> +  // Host::OnResolvePromise() or Host::OnRejectPromise().
> +  // If the CDM does not support server certificates, the promise should be
> +  // rejected with kExceptionNotSupportedError. If |server_certificate_data|
> +  // is empty, reject with kExceptionTypeError. Any other error should be
> +  // rejected with kExceptionInvalidStateError or kExceptionQuotaExceededError.
> +  // TODO(crbug.com/796417): Add support for the promise to return true or
> +  // false, rather than using kExceptionNotSupportedError to mean false.
> +  virtual void SetServerCertificate(uint32_t promise_id,
> +                                    const uint8_t* server_certificate_data,
> +                                    uint32_t server_certificate_data_size) = 0;
> +
> +  // Creates a session given |session_type|, |init_data_type|, and |init_data|.
> +  // The CDM must respond by calling either Host::OnResolveNewSessionPromise()
> +  // or Host::OnRejectPromise().
> +  virtual void CreateSessionAndGenerateRequest(uint32_t promise_id,
> +                                               SessionType session_type,
> +                                               InitDataType init_data_type,
> +                                               const uint8_t* init_data,
> +                                               uint32_t init_data_size) = 0;
> +
> +  // Loads the session of type |session_type| specified by |session_id|.
> +  // The CDM must respond by calling either Host::OnResolveNewSessionPromise()
> +  // or Host::OnRejectPromise(). If the session is not found, call
> +  // Host::OnResolveNewSessionPromise() with session_id = NULL.
> +  virtual void LoadSession(uint32_t promise_id,
> +                           SessionType session_type,
> +                           const char* session_id,
> +                           uint32_t session_id_size) = 0;
> +
> +  // Updates the session with |response|. The CDM must respond by calling
> +  // either Host::OnResolvePromise() or Host::OnRejectPromise().
> +  virtual void UpdateSession(uint32_t promise_id,
> +                             const char* session_id,
> +                             uint32_t session_id_size,
> +                             const uint8_t* response,
> +                             uint32_t response_size) = 0;
> +
> +  // Requests that the CDM close the session. The CDM must respond by calling
> +  // either Host::OnResolvePromise() or Host::OnRejectPromise() when the request
> +  // has been processed. This may be before the session is closed. Once the
> +  // session is closed, Host::OnSessionClosed() must also be called.
> +  virtual void CloseSession(uint32_t promise_id,
> +                            const char* session_id,
> +                            uint32_t session_id_size) = 0;
> +
> +  // Removes any stored session data associated with this session. Will only be
> +  // called for persistent sessions. The CDM must respond by calling either
> +  // Host::OnResolvePromise() or Host::OnRejectPromise() when the request has
> +  // been processed.
> +  virtual void RemoveSession(uint32_t promise_id,
> +                             const char* session_id,
> +                             uint32_t session_id_size) = 0;
> +
> +  // Performs scheduled operation with |context| when the timer fires.
> +  virtual void TimerExpired(void* context) = 0;
> +
> +  // Decrypts the |encrypted_buffer|.
>    //
> -  // |<----- subsample1 ----->|<----- subsample2 ----->|<----- subsample3 ----->|
> -  // |   clear1   | decrypted1|  clear2  |  decrypted2 | clear3 |   decrypted3  |
> +  // Returns kSuccess if decryption succeeded, in which case the callee
> +  // should have filled the |decrypted_buffer| and passed the ownership of
> +  // |data| in |decrypted_buffer| to the caller.
> +  // Returns kNoKey if the CDM did not have the necessary decryption key
> +  // to decrypt.
> +  // Returns kDecryptError if any other error happened.
> +  // If the return value is not kSuccess, |decrypted_buffer| should be ignored
> +  // by the caller.
> +  virtual Status Decrypt(const InputBuffer_2& encrypted_buffer,
> +                         DecryptedBlock* decrypted_buffer) = 0;
> +
> +  // Initializes the CDM audio decoder with |audio_decoder_config|. This
> +  // function must be called before DecryptAndDecodeSamples() is called.
>    //
> -  struct SubsampleEntry {
> -    uint32_t clear_bytes;
> -    uint32_t cipher_bytes;
> -  };
> -  CHECK_TYPE(SubsampleEntry, 8, 8);
> -
> -  // Represents an input buffer to be decrypted (and possibly decoded). It does
> -  // not own any pointers in this struct. If |iv_size| = 0, the data is
> -  // unencrypted.
> -  // Deprecated: New CDM implementations should use InputBuffer_2.
> -  struct InputBuffer_1 {
> -    const uint8_t* data;  // Pointer to the beginning of the input data.
> -    uint32_t data_size;   // Size (in bytes) of |data|.
> -
> -    const uint8_t* key_id;  // Key ID to identify the decryption key.
> -    uint32_t key_id_size;   // Size (in bytes) of |key_id|.
> -
> -    const uint8_t* iv;  // Initialization vector.
> -    uint32_t iv_size;   // Size (in bytes) of |iv|.
> -
> -    const struct SubsampleEntry* subsamples;
> -    uint32_t num_subsamples;  // Number of subsamples in |subsamples|.
> -
> -    int64_t timestamp;  // Presentation timestamp in microseconds.
> -  };
> -  CHECK_TYPE(InputBuffer_1, 40, 72);
> -
> -  // Represents an input buffer to be decrypted (and possibly decoded). It does
> -  // not own any pointers in this struct. If |encryption_scheme| = kUnencrypted,
> -  // the data is unencrypted.
> -  // Note that this struct is organized so that sizeof(InputBuffer_2)
> -  // equals the sum of sizeof() all members in both 32-bit and 64-bit compiles.
> -  // Padding has been added to keep the fields aligned.
> -  struct InputBuffer_2 {
> -    const uint8_t* data;  // Pointer to the beginning of the input data.
> -    uint32_t data_size;   // Size (in bytes) of |data|.
> -
> -    EncryptionScheme encryption_scheme;
> -
> -    const uint8_t* key_id;  // Key ID to identify the decryption key.
> -    uint32_t key_id_size;   // Size (in bytes) of |key_id|.
> -    uint32_t : 32;          // Padding.
> -
> -               const uint8_t* iv;  // Initialization vector.
> -               uint32_t iv_size;   // Size (in bytes) of |iv|.
> -               uint32_t : 32;      // Padding.
> -
> -                          const struct SubsampleEntry* subsamples;
> -                          uint32_t num_subsamples;  // Number of subsamples in |subsamples|.
> -                          uint32_t : 32;            // Padding.
> -
> -                                                    // |pattern| is required if |encryption_scheme| specifies pattern encryption.
> -                                     Pattern pattern;
> -
> -                                     int64_t timestamp;  // Presentation timestamp in microseconds.
> -  };
> -  CHECK_TYPE(InputBuffer_2, 64, 80);
> -
> -  enum AudioCodec : uint32_t { kUnknownAudioCodec = 0, kCodecVorbis, kCodecAac };
> -  CHECK_TYPE(AudioCodec, 4, 4);
> -
> -  // Deprecated: New CDM implementations should use AudioDecoderConfig_2.
> -  struct AudioDecoderConfig_1 {
> -    AudioCodec codec;
> -    int32_t channel_count;
> -    int32_t bits_per_channel;
> -    int32_t samples_per_second;
> -
> -    // Optional byte data required to initialize audio decoders, such as the
> -    // vorbis setup header.
> -    uint8_t* extra_data;
> -    uint32_t extra_data_size;
> -  };
> -  CHECK_TYPE(AudioDecoderConfig_1, 24, 32);
> -
> -  struct AudioDecoderConfig_2 {
> -    AudioCodec codec;
> -    int32_t channel_count;
> -    int32_t bits_per_channel;
> -    int32_t samples_per_second;
> -
> -    // Optional byte data required to initialize audio decoders, such as the
> -    // vorbis setup header.
> -    uint8_t* extra_data;
> -    uint32_t extra_data_size;
> -
> -    // Encryption scheme.
> -    EncryptionScheme encryption_scheme;
> -  };
> -  CHECK_TYPE(AudioDecoderConfig_2, 28, 32);
> -
> -  // Supported sample formats for AudioFrames.
> -  enum AudioFormat : uint32_t {
> -    kUnknownAudioFormat = 0,  // Unknown format value. Used for error reporting.
> -    kAudioFormatU8,           // Interleaved unsigned 8-bit w/ bias of 128.
> -    kAudioFormatS16,          // Interleaved signed 16-bit.
> -    kAudioFormatS32,          // Interleaved signed 32-bit.
> -    kAudioFormatF32,          // Interleaved float 32-bit.
> -    kAudioFormatPlanarS16,    // Signed 16-bit planar.
> -    kAudioFormatPlanarF32,    // Float 32-bit planar.
> -  };
> -  CHECK_TYPE(AudioFormat, 4, 4);
> -
> -  // Surface formats based on FOURCC labels, see: http://www.fourcc.org/yuv.php
> -  // Values are chosen to be consistent with Chromium's VideoPixelFormat values.
> -  enum VideoFormat : uint32_t {
> -    kUnknownVideoFormat = 0,  // Unknown format value. Used for error reporting.
> -    kYv12 = 1,                // 12bpp YVU planar 1x1 Y, 2x2 VU samples.
> -    kI420 = 2,                // 12bpp YUV planar 1x1 Y, 2x2 UV samples.
> -
> -                              // In the following formats, each sample uses 16-bit in storage, while the
> -                              // sample value is stored in the least significant N bits where N is
> -                              // specified by the number after "P". For example, for YUV420P9, each Y, U,
> -                              // and V sample is stored in the least significant 9 bits in a 2-byte block.
> -                              kYUV420P9 = 16,
> -                              kYUV420P10 = 17,
> -                              kYUV422P9 = 18,
> -                              kYUV422P10 = 19,
> -                              kYUV444P9 = 20,
> -                              kYUV444P10 = 21,
> -                              kYUV420P12 = 22,
> -                              kYUV422P12 = 23,
> -                              kYUV444P12 = 24,
> -  };
> -  CHECK_TYPE(VideoFormat, 4, 4);
> -
> -  struct Size {
> -    int32_t width;
> -    int32_t height;
> -  };
> -  CHECK_TYPE(Size, 8, 8);
> -
> -  enum VideoCodec : uint32_t {
> -    kUnknownVideoCodec = 0,
> -    kCodecVp8,
> -    kCodecH264,
> -    kCodecVp9,
> -    kCodecAv1
> -  };
> -  CHECK_TYPE(VideoCodec, 4, 4);
> -
> -  enum VideoCodecProfile : uint32_t {
> -    kUnknownVideoCodecProfile = 0,
> -    kProfileNotNeeded,
> -    kH264ProfileBaseline,
> -    kH264ProfileMain,
> -    kH264ProfileExtended,
> -    kH264ProfileHigh,
> -    kH264ProfileHigh10,
> -    kH264ProfileHigh422,
> -    kH264ProfileHigh444Predictive,
> -    // VP9 Profiles are only passed in starting from CDM_9.
> -    kVP9Profile0,
> -    kVP9Profile1,
> -    kVP9Profile2,
> -    kVP9Profile3,
> -    kAv1ProfileMain,
> -    kAv1ProfileHigh,
> -    kAv1ProfilePro
> -  };
> -  CHECK_TYPE(VideoCodecProfile, 4, 4);
> -
> -  // Deprecated: New CDM implementations should use VideoDecoderConfig_3.
> -  struct VideoDecoderConfig_1 {
> -    VideoCodec codec;
> -    VideoCodecProfile profile;
> -    VideoFormat format;
> -
> -    // Width and height of video frame immediately post-decode. Not all pixels
> -    // in this region are valid.
> -    Size coded_size;
> -
> -    // Optional byte data required to initialize video decoders, such as H.264
> -    // AAVC data.
> -    uint8_t* extra_data;
> -    uint32_t extra_data_size;
> -  };
> -  CHECK_TYPE(VideoDecoderConfig_1, 28, 40);
> -
> -  // Deprecated: New CDM implementations should use VideoDecoderConfig_3.
> -  // Note that this struct is organized so that sizeof(VideoDecoderConfig_2)
> -  // equals the sum of sizeof() all members in both 32-bit and 64-bit compiles.
> -  // Padding has been added to keep the fields aligned.
> -  struct VideoDecoderConfig_2 {
> -    VideoCodec codec;
> -    VideoCodecProfile profile;
> -    VideoFormat format;
> -    uint32_t : 32;  // Padding.
> -
> -                    // Width and height of video frame immediately post-decode. Not all pixels
> -                    // in this region are valid.
> -               Size coded_size;
> -
> -               // Optional byte data required to initialize video decoders, such as H.264
> -               // AAVC data.
> -               uint8_t* extra_data;
> -               uint32_t extra_data_size;
> -
> -               // Encryption scheme.
> -               EncryptionScheme encryption_scheme;
> -  };
> -  CHECK_TYPE(VideoDecoderConfig_2, 36, 40);
> -
> -  struct VideoDecoderConfig_3 {
> -    VideoCodec codec;
> -    VideoCodecProfile profile;
> -    VideoFormat format;
> -    ColorSpace color_space;
> -
> -    // Width and height of video frame immediately post-decode. Not all pixels
> -    // in this region are valid.
> -    Size coded_size;
> -
> -    // Optional byte data required to initialize video decoders, such as H.264
> -    // AAVC data.
> -    uint8_t* extra_data;
> -    uint32_t extra_data_size;
> -
> -    EncryptionScheme encryption_scheme;
> -  };
> -  CHECK_TYPE(VideoDecoderConfig_3, 36, 40);
> -
> -  enum StreamType : uint32_t { kStreamTypeAudio = 0, kStreamTypeVideo = 1 };
> -  CHECK_TYPE(StreamType, 4, 4);
> -
> -  // Structure provided to ContentDecryptionModule::OnPlatformChallengeResponse()
> -  // after a platform challenge was initiated via Host::SendPlatformChallenge().
> -  // All values will be NULL / zero in the event of a challenge failure.
> -  struct PlatformChallengeResponse {
> -    // |challenge| provided during Host::SendPlatformChallenge() combined with
> -    // nonce data and signed with the platform's private key.
> -    const uint8_t* signed_data;
> -    uint32_t signed_data_length;
> -
> -    // RSASSA-PKCS1-v1_5-SHA256 signature of the |signed_data| block.
> -    const uint8_t* signed_data_signature;
> -    uint32_t signed_data_signature_length;
> -
> -    // X.509 device specific certificate for the |service_id| requested.
> -    const uint8_t* platform_key_certificate;
> -    uint32_t platform_key_certificate_length;
> -  };
> -  CHECK_TYPE(PlatformChallengeResponse, 24, 48);
> -
> -  // The current status of the associated key. The valid types are defined in the
> -  // spec: https://w3c.github.io/encrypted-media/#idl-def-MediaKeyStatus
> -  enum KeyStatus : uint32_t {
> -    kUsable = 0,
> -    kInternalError = 1,
> -    kExpired = 2,
> -    kOutputRestricted = 3,
> -    kOutputDownscaled = 4,
> -    kStatusPending = 5,
> -    kReleased = 6
> -  };
> -  CHECK_TYPE(KeyStatus, 4, 4);
> -
> -  // Used when passing arrays of key information. Does not own the referenced
> -  // data. |system_code| is an additional error code for unusable keys and
> -  // should be 0 when |status| == kUsable.
> -  struct KeyInformation {
> -    const uint8_t* key_id;
> -    uint32_t key_id_size;
> -    KeyStatus status;
> -    uint32_t system_code;
> -  };
> -  CHECK_TYPE(KeyInformation, 16, 24);
> -
> -  // Supported output protection methods for use with EnableOutputProtection() and
> -  // returned by OnQueryOutputProtectionStatus().
> -  enum OutputProtectionMethods : uint32_t {
> -    kProtectionNone = 0,
> -    kProtectionHDCP = 1 << 0
> -  };
> -  CHECK_TYPE(OutputProtectionMethods, 4, 4);
> -
> -  // Connected output link types returned by OnQueryOutputProtectionStatus().
> -  enum OutputLinkTypes : uint32_t {
> -    kLinkTypeNone = 0,
> -    kLinkTypeUnknown = 1 << 0,
> -    kLinkTypeInternal = 1 << 1,
> -    kLinkTypeVGA = 1 << 2,
> -    kLinkTypeHDMI = 1 << 3,
> -    kLinkTypeDVI = 1 << 4,
> -    kLinkTypeDisplayPort = 1 << 5,
> -    kLinkTypeNetwork = 1 << 6
> -  };
> -  CHECK_TYPE(OutputLinkTypes, 4, 4);
> -
> -  // Result of the QueryOutputProtectionStatus() call.
> -  enum QueryResult : uint32_t { kQuerySucceeded = 0, kQueryFailed };
> -  CHECK_TYPE(QueryResult, 4, 4);
> -
> -  // The Initialization Data Type. The valid types are defined in the spec:
> -  // https://w3c.github.io/encrypted-media/format-registry/initdata/index.html#registry
> -  enum InitDataType : uint32_t { kCenc = 0, kKeyIds = 1, kWebM = 2 };
> -  CHECK_TYPE(InitDataType, 4, 4);
> -
> -  // The type of session to create. The valid types are defined in the spec:
> -  // https://w3c.github.io/encrypted-media/#idl-def-SessionType
> -  enum SessionType : uint32_t {
> -    kTemporary = 0,
> -    kPersistentLicense = 1,
> -    kPersistentUsageRecord = 2
> -  };
> -  CHECK_TYPE(SessionType, 4, 4);
> -
> -  // The type of the message event.  The valid types are defined in the spec:
> -  // https://w3c.github.io/encrypted-media/#idl-def-MediaKeyMessageType
> -  enum MessageType : uint32_t {
> -    kLicenseRequest = 0,
> -    kLicenseRenewal = 1,
> -    kLicenseRelease = 2,
> -    // Only supported by Host_10 and later. On Host_9 and earlier, it's undefined
> -    // behavior. For example, the host can drop the message or send it using
> -    // other message type.
> -    kIndividualizationRequest = 3
> -  };
> -  CHECK_TYPE(MessageType, 4, 4);
> -
> -  enum HdcpVersion : uint32_t {
> -    kHdcpVersionNone,
> -    kHdcpVersion1_0,
> -    kHdcpVersion1_1,
> -    kHdcpVersion1_2,
> -    kHdcpVersion1_3,
> -    kHdcpVersion1_4,
> -    kHdcpVersion2_0,
> -    kHdcpVersion2_1,
> -    kHdcpVersion2_2,
> -    kHdcpVersion2_3
> -  };
> -  CHECK_TYPE(HdcpVersion, 4, 4);
> -
> -  struct Policy {
> -    HdcpVersion min_hdcp_version;
> -  };
> -  CHECK_TYPE(Policy, 4, 4);
> -
> -  // Represents a buffer created by Allocator implementations.
> -  class CDM_CLASS_API Buffer {
> -  public:
> -    // Destroys the buffer in the same context as it was created.
> -    virtual void Destroy() = 0;
> -
> -    virtual uint32_t Capacity() const = 0;
> -    virtual uint8_t* Data() = 0;
> -    virtual void SetSize(uint32_t size) = 0;
> -    virtual uint32_t Size() const = 0;
> -
> -  protected:
> -    Buffer() {}
> -    virtual ~Buffer() {}
> -
> -  private:
> -    Buffer(const Buffer&);
> -    void operator=(const Buffer&);
> -  };
> -
> -  // Represents a decrypted block that has not been decoded.
> -  class CDM_CLASS_API DecryptedBlock {
> -  public:
> -    virtual void SetDecryptedBuffer(Buffer* buffer) = 0;
> -    virtual Buffer* DecryptedBuffer() = 0;
> -
> -    // TODO(tomfinegan): Figure out if timestamp is really needed. If it is not,
> -    // we can just pass Buffer pointers around.
> -    virtual void SetTimestamp(int64_t timestamp) = 0;
> -    virtual int64_t Timestamp() const = 0;
> -
> -  protected:
> -    DecryptedBlock() {}
> -    virtual ~DecryptedBlock() {}
> -  };
> -
> -  enum VideoPlane : uint32_t {
> -    kYPlane = 0,
> -    kUPlane = 1,
> -    kVPlane = 2,
> -    kMaxPlanes = 3,
> -  };
> -  CHECK_TYPE(VideoPlane, 4, 4);
> -
> -  class CDM_CLASS_API VideoFrame {
> -  public:
> -    virtual void SetFormat(VideoFormat format) = 0;
> -    virtual VideoFormat Format() const = 0;
> -
> -    virtual void SetSize(cdm::Size size) = 0;
> -    virtual cdm::Size Size() const = 0;
> -
> -    virtual void SetFrameBuffer(Buffer* frame_buffer) = 0;
> -    virtual Buffer* FrameBuffer() = 0;
> -
> -    virtual void SetPlaneOffset(VideoPlane plane, uint32_t offset) = 0;
> -    virtual uint32_t PlaneOffset(VideoPlane plane) = 0;
> -
> -    virtual void SetStride(VideoPlane plane, uint32_t stride) = 0;
> -    virtual uint32_t Stride(VideoPlane plane) = 0;
> -
> -    // Sets and gets the presentation timestamp which is in microseconds.
> -    virtual void SetTimestamp(int64_t timestamp) = 0;
> -    virtual int64_t Timestamp() const = 0;
> -
> -  protected:
> -    VideoFrame() {}
> -    virtual ~VideoFrame() {}
> -  };
> -
> -  // Represents a decoded video frame. The CDM should call the interface methods
> -  // to set the frame attributes. See DecryptAndDecodeFrame().
> -  class CDM_CLASS_API VideoFrame_2 {
> -  public:
> -    virtual void SetFormat(VideoFormat format) = 0;
> -    virtual void SetSize(cdm::Size size) = 0;
> -    virtual void SetFrameBuffer(Buffer* frame_buffer) = 0;
> -    virtual void SetPlaneOffset(VideoPlane plane, uint32_t offset) = 0;
> -    virtual void SetStride(VideoPlane plane, uint32_t stride) = 0;
> -    // Sets the presentation timestamp which is in microseconds.
> -    virtual void SetTimestamp(int64_t timestamp) = 0;
> -    virtual void SetColorSpace(ColorSpace color_space) = 0;
> -
> -  protected:
> -    VideoFrame_2() {}
> -    virtual ~VideoFrame_2() {}
> -  };
> +  // Returns kSuccess if the |audio_decoder_config| is supported and the CDM
> +  // audio decoder is successfully initialized.
> +  // Returns kInitializationError if |audio_decoder_config| is not supported.
> +  // The CDM may still be able to do Decrypt().
> +  // Returns kDeferredInitialization if the CDM is not ready to initialize the
> +  // decoder at this time. Must call Host::OnDeferredInitializationDone() once
> +  // initialization is complete.
> +  virtual Status InitializeAudioDecoder(
> +      const AudioDecoderConfig_2& audio_decoder_config) = 0;
>  
> -  // Represents decrypted and decoded audio frames. AudioFrames can contain
> -  // multiple audio output buffers, which are serialized into this format:
> +  // Initializes the CDM video decoder with |video_decoder_config|. This
> +  // function must be called before DecryptAndDecodeFrame() is called.
>    //
> -  // |<------------------- serialized audio buffer ------------------->|
> -  // | int64_t timestamp | int64_t length | length bytes of audio data |
> +  // Returns kSuccess if the |video_decoder_config| is supported and the CDM
> +  // video decoder is successfully initialized.
> +  // Returns kInitializationError if |video_decoder_config| is not supported.
> +  // The CDM may still be able to do Decrypt().
> +  // Returns kDeferredInitialization if the CDM is not ready to initialize the
> +  // decoder at this time. Must call Host::OnDeferredInitializationDone() once
> +  // initialization is complete.
> +  virtual Status InitializeVideoDecoder(
> +      const VideoDecoderConfig_2& video_decoder_config) = 0;
> +
> +  // De-initializes the CDM decoder and sets it to an uninitialized state. The
> +  // caller can initialize the decoder again after this call to re-initialize
> +  // it. This can be used to reconfigure the decoder if the configuration
> +  // changes.
> +  virtual void DeinitializeDecoder(StreamType decoder_type) = 0;
> +
> +  // Resets the CDM decoder to an initialized clean state. All internal buffers
> +  // MUST be flushed.
> +  virtual void ResetDecoder(StreamType decoder_type) = 0;
> +
> +  // Decrypts the |encrypted_buffer| and decodes the decrypted buffer into a
> +  // |video_frame|. Upon end-of-stream, the caller should call this function
> +  // repeatedly with empty |encrypted_buffer| (|data| == NULL) until
> +  // kNeedMoreData is returned.
>    //
> -  // For example, with three audio output buffers, the AudioFrames will look
> -  // like this:
> +  // Returns kSuccess if decryption and decoding both succeeded, in which case
> +  // the callee will have filled the |video_frame| and passed the ownership of
> +  // |frame_buffer| in |video_frame| to the caller.
> +  // Returns kNoKey if the CDM did not have the necessary decryption key
> +  // to decrypt.
> +  // Returns kNeedMoreData if more data was needed by the decoder to generate
> +  // a decoded frame (e.g. during initialization and end-of-stream).
> +  // Returns kDecryptError if any decryption error happened.
> +  // Returns kDecodeError if any decoding error happened.
> +  // If the return value is not kSuccess, |video_frame| should be ignored by
> +  // the caller.
> +  virtual Status DecryptAndDecodeFrame(const InputBuffer_2& encrypted_buffer,
> +                                       VideoFrame* video_frame) = 0;
> +
> +  // Decrypts the |encrypted_buffer| and decodes the decrypted buffer into
> +  // |audio_frames|. Upon end-of-stream, the caller should call this function
> +  // repeatedly with empty |encrypted_buffer| (|data| == NULL) until only empty
> +  // |audio_frames| is produced.
>    //
> -  // |<----------------- AudioFrames ------------------>|
> -  // | audio buffer 0 | audio buffer 1 | audio buffer 2 |
> -  class CDM_CLASS_API AudioFrames {
> -  public:
> -    virtual void SetFrameBuffer(Buffer* buffer) = 0;
> -    virtual Buffer* FrameBuffer() = 0;
> -
> -    // The CDM must call this method, providing a valid format, when providing
> -    // frame buffers. Planar data should be stored end to end; e.g.,
> -    // |ch1 sample1||ch1 sample2|....|ch1 sample_last||ch2 sample1|...
> -    virtual void SetFormat(AudioFormat format) = 0;
> -    virtual AudioFormat Format() const = 0;
> -
> -  protected:
> -    AudioFrames() {}
> -    virtual ~AudioFrames() {}
> -  };
> -
> -  // FileIO interface provides a way for the CDM to store data in a file in
> -  // persistent storage. This interface aims only at providing basic read/write
> -  // capabilities and should not be used as a full fledged file IO API.
> -  // Each CDM and origin (e.g. HTTPS, "foo.example.com", 443) combination has
> -  // its own persistent storage. All instances of a given CDM associated with a
> -  // given origin share the same persistent storage.
> -  // Note to implementors of this interface:
> -  // Per-origin storage and the ability for users to clear it are important.
> -  // See http://www.w3.org/TR/encrypted-media/#privacy-storedinfo.
> -  class CDM_CLASS_API FileIO {
> -  public:
> -    // Opens the file with |file_name| for read and write.
> -    // FileIOClient::OnOpenComplete() will be called after the opening
> -    // operation finishes.
> -    // - When the file is opened by a CDM instance, it will be classified as "in
> -    //   use". In this case other CDM instances in the same domain may receive
> -    //   kInUse status when trying to open it.
> -    // - |file_name| must only contain letters (A-Za-z), digits(0-9), or "._-".
> -    //   It must not start with an underscore ('_'), and must be at least 1
> -    //   character and no more than 256 characters long.
> -    virtual void Open(const char* file_name, uint32_t file_name_size) = 0;
> -
> -    // Reads the contents of the file. FileIOClient::OnReadComplete() will be
> -    // called with the read status. Read() should not be called if a previous
> -    // Read() or Write() call is still pending; otherwise OnReadComplete() will
> -    // be called with kInUse.
> -    virtual void Read() = 0;
> -
> -    // Writes |data_size| bytes of |data| into the file.
> -    // FileIOClient::OnWriteComplete() will be called with the write status.
> -    // All existing contents in the file will be overwritten. Calling Write() with
> -    // NULL |data| will clear all contents in the file. Write() should not be
> -    // called if a previous Write() or Read() call is still pending; otherwise
> -    // OnWriteComplete() will be called with kInUse.
> -    virtual void Write(const uint8_t* data, uint32_t data_size) = 0;
> -
> -    // Closes the file if opened, destroys this FileIO object and releases any
> -    // resources allocated. The CDM must call this method when it finished using
> -    // this object. A FileIO object must not be used after Close() is called.
> -    virtual void Close() = 0;
> -
> -  protected:
> -    FileIO() {}
> -    virtual ~FileIO() {}
> -  };
> -
> -  // Responses to FileIO calls. All responses will be called asynchronously.
> -  // When kError is returned, the FileIO object could be in an error state. All
> -  // following calls (other than Close()) could return kError. The CDM should
> -  // still call Close() to destroy the FileIO object.
> -  class CDM_CLASS_API FileIOClient {
> -  public:
> -    enum class Status : uint32_t { kSuccess = 0, kInUse, kError };
> -
> -    // Response to a FileIO::Open() call with the open |status|.
> -    virtual void OnOpenComplete(Status status) = 0;
> -
> -    // Response to a FileIO::Read() call to provide |data_size| bytes of |data|
> -    // read from the file.
> -    // - kSuccess indicates that all contents of the file has been successfully
> -    //   read. In this case, 0 |data_size| means that the file is empty.
> -    // - kInUse indicates that there are other read/write operations pending.
> -    // - kError indicates read failure, e.g. the storage is not open or cannot be
> -    //   fully read.
> -    virtual void OnReadComplete(Status status,
> -      const uint8_t* data,
> -      uint32_t data_size) = 0;
> -
> -    // Response to a FileIO::Write() call.
> -    // - kSuccess indicates that all the data has been written into the file
> -    //   successfully.
> -    // - kInUse indicates that there are other read/write operations pending.
> -    // - kError indicates write failure, e.g. the storage is not open or cannot be
> -    //   fully written. Upon write failure, the contents of the file should be
> -    //   regarded as corrupt and should not used.
> -    virtual void OnWriteComplete(Status status) = 0;
> -
> -  protected:
> -    FileIOClient() {}
> -    virtual ~FileIOClient() {}
> -  };
> -
> -  class CDM_CLASS_API Host_9;
> -  class CDM_CLASS_API Host_10;
> -  class CDM_CLASS_API Host_11;
> -
> -  // ContentDecryptionModule interface that all CDMs need to implement.
> -  // The interface is versioned for backward compatibility.
> -  // Note: ContentDecryptionModule implementations must use the allocator
> -  // provided in CreateCdmInstance() to allocate any Buffer that needs to
> -  // be passed back to the caller. Implementations must call Buffer::Destroy()
> -  // when a Buffer is created that will never be returned to the caller.
> -  class CDM_CLASS_API ContentDecryptionModule_9 {
> -  public:
> -    static const int kVersion = 9;
> -    typedef Host_9 Host;
> -
> -    // Initializes the CDM instance, providing information about permitted
> -    // functionalities.
> -    // If |allow_distinctive_identifier| is false, messages from the CDM,
> -    // such as message events, must not contain a Distinctive Identifier,
> -    // even in an encrypted form.
> -    // If |allow_persistent_state| is false, the CDM must not attempt to
> -    // persist state. Calls to CreateFileIO() will fail.
> -    virtual void Initialize(bool allow_distinctive_identifier,
> -      bool allow_persistent_state) = 0;
> -
> -    // Gets the key status if the CDM has a hypothetical key with the |policy|.
> -    // The CDM must respond by calling either Host::OnResolveKeyStatusPromise()
> -    // with the result key status or Host::OnRejectPromise() if an unexpected
> -    // error happened or this method is not supported.
> -    virtual void GetStatusForPolicy(uint32_t promise_id,
> -      const Policy& policy) = 0;
> -
> -    // SetServerCertificate(), CreateSessionAndGenerateRequest(), LoadSession(),
> -    // UpdateSession(), CloseSession(), and RemoveSession() all accept a
> -    // |promise_id|, which must be passed to the completion Host method
> -    // (e.g. Host::OnResolveNewSessionPromise()).
> -
> -    // Provides a server certificate to be used to encrypt messages to the
> -    // license server. The CDM must respond by calling either
> -    // Host::OnResolvePromise() or Host::OnRejectPromise().
> -    virtual void SetServerCertificate(uint32_t promise_id,
> -      const uint8_t* server_certificate_data,
> -      uint32_t server_certificate_data_size) = 0;
> -
> -    // Creates a session given |session_type|, |init_data_type|, and |init_data|.
> -    // The CDM must respond by calling either Host::OnResolveNewSessionPromise()
> -    // or Host::OnRejectPromise().
> -    virtual void CreateSessionAndGenerateRequest(uint32_t promise_id,
> -      SessionType session_type,
> -      InitDataType init_data_type,
> -      const uint8_t* init_data,
> -      uint32_t init_data_size) = 0;
> -
> -    // Loads the session of type |session_type| specified by |session_id|.
> -    // The CDM must respond by calling either Host::OnResolveNewSessionPromise()
> -    // or Host::OnRejectPromise(). If the session is not found, call
> -    // Host::OnResolveNewSessionPromise() with session_id = NULL.
> -    virtual void LoadSession(uint32_t promise_id,
> -      SessionType session_type,
> -      const char* session_id,
> -      uint32_t session_id_size) = 0;
> -
> -    // Updates the session with |response|. The CDM must respond by calling
> -    // either Host::OnResolvePromise() or Host::OnRejectPromise().
> -    virtual void UpdateSession(uint32_t promise_id,
> -      const char* session_id,
> -      uint32_t session_id_size,
> -      const uint8_t* response,
> -      uint32_t response_size) = 0;
> -
> -    // Requests that the CDM close the session. The CDM must respond by calling
> -    // either Host::OnResolvePromise() or Host::OnRejectPromise() when the request
> -    // has been processed. This may be before the session is closed. Once the
> -    // session is closed, Host::OnSessionClosed() must also be called.
> -    virtual void CloseSession(uint32_t promise_id,
> -      const char* session_id,
> -      uint32_t session_id_size) = 0;
> -
> -    // Removes any stored session data associated with this session. Will only be
> -    // called for persistent sessions. The CDM must respond by calling either
> -    // Host::OnResolvePromise() or Host::OnRejectPromise() when the request has
> -    // been processed.
> -    virtual void RemoveSession(uint32_t promise_id,
> -      const char* session_id,
> -      uint32_t session_id_size) = 0;
> -
> -    // Performs scheduled operation with |context| when the timer fires.
> -    virtual void TimerExpired(void* context) = 0;
> -
> -    // Decrypts the |encrypted_buffer|.
> -    //
> -    // Returns kSuccess if decryption succeeded, in which case the callee
> -    // should have filled the |decrypted_buffer| and passed the ownership of
> -    // |data| in |decrypted_buffer| to the caller.
> -    // Returns kNoKey if the CDM did not have the necessary decryption key
> -    // to decrypt.
> -    // Returns kDecryptError if any other error happened.
> -    // If the return value is not kSuccess, |decrypted_buffer| should be ignored
> -    // by the caller.
> -    virtual Status Decrypt(const InputBuffer_1& encrypted_buffer,
> -      DecryptedBlock* decrypted_buffer) = 0;
> -
> -    // Initializes the CDM audio decoder with |audio_decoder_config|. This
> -    // function must be called before DecryptAndDecodeSamples() is called.
> -    //
> -    // Returns kSuccess if the |audio_decoder_config| is supported and the CDM
> -    // audio decoder is successfully initialized.
> -    // Returns kInitializationError if |audio_decoder_config| is not supported.
> -    // The CDM may still be able to do Decrypt().
> -    // Returns kDeferredInitialization if the CDM is not ready to initialize the
> -    // decoder at this time. Must call Host::OnDeferredInitializationDone() once
> -    // initialization is complete.
> -    virtual Status InitializeAudioDecoder(
> -      const AudioDecoderConfig_1& audio_decoder_config) = 0;
> -
> -    // Initializes the CDM video decoder with |video_decoder_config|. This
> -    // function must be called before DecryptAndDecodeFrame() is called.
> -    //
> -    // Returns kSuccess if the |video_decoder_config| is supported and the CDM
> -    // video decoder is successfully initialized.
> -    // Returns kInitializationError if |video_decoder_config| is not supported.
> -    // The CDM may still be able to do Decrypt().
> -    // Returns kDeferredInitialization if the CDM is not ready to initialize the
> -    // decoder at this time. Must call Host::OnDeferredInitializationDone() once
> -    // initialization is complete.
> -    virtual Status InitializeVideoDecoder(
> -      const VideoDecoderConfig_1& video_decoder_config) = 0;
> -
> -    // De-initializes the CDM decoder and sets it to an uninitialized state. The
> -    // caller can initialize the decoder again after this call to re-initialize
> -    // it. This can be used to reconfigure the decoder if the configuration
> -    // changes.
> -    virtual void DeinitializeDecoder(StreamType decoder_type) = 0;
> -
> -    // Resets the CDM decoder to an initialized clean state. All internal buffers
> -    // MUST be flushed.
> -    virtual void ResetDecoder(StreamType decoder_type) = 0;
> -
> -    // Decrypts the |encrypted_buffer| and decodes the decrypted buffer into a
> -    // |video_frame|. Upon end-of-stream, the caller should call this function
> -    // repeatedly with empty |encrypted_buffer| (|data| == NULL) until
> -    // kNeedMoreData is returned.
> -    //
> -    // Returns kSuccess if decryption and decoding both succeeded, in which case
> -    // the callee will have filled the |video_frame| and passed the ownership of
> -    // |frame_buffer| in |video_frame| to the caller.
> -    // Returns kNoKey if the CDM did not have the necessary decryption key
> -    // to decrypt.
> -    // Returns kNeedMoreData if more data was needed by the decoder to generate
> -    // a decoded frame (e.g. during initialization and end-of-stream).
> -    // Returns kDecryptError if any decryption error happened.
> -    // Returns kDecodeError if any decoding error happened.
> -    // If the return value is not kSuccess, |video_frame| should be ignored by
> -    // the caller.
> -    virtual Status DecryptAndDecodeFrame(const InputBuffer_1& encrypted_buffer,
> -      VideoFrame* video_frame) = 0;
> -
> -    // Decrypts the |encrypted_buffer| and decodes the decrypted buffer into
> -    // |audio_frames|. Upon end-of-stream, the caller should call this function
> -    // repeatedly with empty |encrypted_buffer| (|data| == NULL) until only empty
> -    // |audio_frames| is produced.
> -    //
> -    // Returns kSuccess if decryption and decoding both succeeded, in which case
> -    // the callee will have filled |audio_frames| and passed the ownership of
> -    // |data| in |audio_frames| to the caller.
> -    // Returns kNoKey if the CDM did not have the necessary decryption key
> -    // to decrypt.
> -    // Returns kNeedMoreData if more data was needed by the decoder to generate
> -    // audio samples (e.g. during initialization and end-of-stream).
> -    // Returns kDecryptError if any decryption error happened.
> -    // Returns kDecodeError if any decoding error happened.
> -    // If the return value is not kSuccess, |audio_frames| should be ignored by
> -    // the caller.
> -    virtual Status DecryptAndDecodeSamples(const InputBuffer_1& encrypted_buffer,
> -      AudioFrames* audio_frames) = 0;
> -
> -    // Called by the host after a platform challenge was initiated via
> -    // Host::SendPlatformChallenge().
> -    virtual void OnPlatformChallengeResponse(
> +  // Returns kSuccess if decryption and decoding both succeeded, in which case
> +  // the callee will have filled |audio_frames| and passed the ownership of
> +  // |data| in |audio_frames| to the caller.
> +  // Returns kNoKey if the CDM did not have the necessary decryption key
> +  // to decrypt.
> +  // Returns kNeedMoreData if more data was needed by the decoder to generate
> +  // audio samples (e.g. during initialization and end-of-stream).
> +  // Returns kDecryptError if any decryption error happened.
> +  // Returns kDecodeError if any decoding error happened.
> +  // If the return value is not kSuccess, |audio_frames| should be ignored by
> +  // the caller.
> +  virtual Status DecryptAndDecodeSamples(const InputBuffer_2& encrypted_buffer,
> +                                         AudioFrames* audio_frames) = 0;
> +
> +  // Called by the host after a platform challenge was initiated via
> +  // Host::SendPlatformChallenge().
> +  virtual void OnPlatformChallengeResponse(
>        const PlatformChallengeResponse& response) = 0;
>  
> -    // Called by the host after a call to Host::QueryOutputProtectionStatus(). The
> -    // |link_mask| is a bit mask of OutputLinkTypes and |output_protection_mask|
> -    // is a bit mask of OutputProtectionMethods. If |result| is kQueryFailed,
> -    // then |link_mask| and |output_protection_mask| are undefined and should
> -    // be ignored.
> -    virtual void OnQueryOutputProtectionStatus(
> +  // Called by the host after a call to Host::QueryOutputProtectionStatus(). The
> +  // |link_mask| is a bit mask of OutputLinkTypes and |output_protection_mask|
> +  // is a bit mask of OutputProtectionMethods. If |result| is kQueryFailed,
> +  // then |link_mask| and |output_protection_mask| are undefined and should
> +  // be ignored.
> +  virtual void OnQueryOutputProtectionStatus(
>        QueryResult result,
>        uint32_t link_mask,
>        uint32_t output_protection_mask) = 0;
>  
> -    // Called by the host after a call to Host::RequestStorageId(). If the
> -    // version of the storage ID requested is available, |storage_id| and
> -    // |storage_id_size| are set appropriately. |version| will be the same as
> -    // what was requested, unless 0 (latest) was requested, in which case
> -    // |version| will be the actual version number for the |storage_id| returned.
> -    // If the requested version is not available, null/zero will be provided as
> -    // |storage_id| and |storage_id_size|, respectively, and |version| should be
> -    // ignored.
> -    virtual void OnStorageId(uint32_t version,
> -      const uint8_t* storage_id,
> -      uint32_t storage_id_size) = 0;
> -
> -    // Destroys the object in the same context as it was created.
> -    virtual void Destroy() = 0;
> -
> -  protected:
> -    ContentDecryptionModule_9() {}
> -    virtual ~ContentDecryptionModule_9() {}
> -  };
> -
> -  // ContentDecryptionModule interface that all CDMs need to implement.
> -  // The interface is versioned for backward compatibility.
> -  // Note: ContentDecryptionModule implementations must use the allocator
> -  // provided in CreateCdmInstance() to allocate any Buffer that needs to
> -  // be passed back to the caller. Implementations must call Buffer::Destroy()
> -  // when a Buffer is created that will never be returned to the caller.
> -  class CDM_CLASS_API ContentDecryptionModule_10 {
> -  public:
> -    static const int kVersion = 10;
> -    static const bool kIsStable = true;
> -    typedef Host_10 Host;
> -
> -    // Initializes the CDM instance, providing information about permitted
> -    // functionalities. The CDM must respond by calling Host::OnInitialized()
> -    // with whether the initialization succeeded. No other calls will be made by
> -    // the host before Host::OnInitialized() returns.
> -    // If |allow_distinctive_identifier| is false, messages from the CDM,
> -    // such as message events, must not contain a Distinctive Identifier,
> -    // even in an encrypted form.
> -    // If |allow_persistent_state| is false, the CDM must not attempt to
> -    // persist state. Calls to CreateFileIO() will fail.
> -    // If |use_hw_secure_codecs| is true, the CDM must ensure the decryption key
> -    // and video buffers (compressed and uncompressed) are securely protected by
> -    // hardware.
> -    virtual void Initialize(bool allow_distinctive_identifier,
> -      bool allow_persistent_state,
> -      bool use_hw_secure_codecs) = 0;
> -
> -    // Gets the key status if the CDM has a hypothetical key with the |policy|.
> -    // The CDM must respond by calling either Host::OnResolveKeyStatusPromise()
> -    // with the result key status or Host::OnRejectPromise() if an unexpected
> -    // error happened or this method is not supported.
> -    virtual void GetStatusForPolicy(uint32_t promise_id,
> -      const Policy& policy) = 0;
> -
> -    // SetServerCertificate(), CreateSessionAndGenerateRequest(), LoadSession(),
> -    // UpdateSession(), CloseSession(), and RemoveSession() all accept a
> -    // |promise_id|, which must be passed to the completion Host method
> -    // (e.g. Host::OnResolveNewSessionPromise()).
> -
> -    // Provides a server certificate to be used to encrypt messages to the
> -    // license server. The CDM must respond by calling either
> -    // Host::OnResolvePromise() or Host::OnRejectPromise().
> -    // If the CDM does not support server certificates, the promise should be
> -    // rejected with kExceptionNotSupportedError. If |server_certificate_data|
> -    // is empty, reject with kExceptionTypeError. Any other error should be
> -    // rejected with kExceptionInvalidStateError or kExceptionQuotaExceededError.
> -    // TODO(crbug.com/796417): Add support for the promise to return true or
> -    // false, rather than using kExceptionNotSupportedError to mean false.
> -    virtual void SetServerCertificate(uint32_t promise_id,
> -      const uint8_t* server_certificate_data,
> -      uint32_t server_certificate_data_size) = 0;
> -
> -    // Creates a session given |session_type|, |init_data_type|, and |init_data|.
> -    // The CDM must respond by calling either Host::OnResolveNewSessionPromise()
> -    // or Host::OnRejectPromise().
> -    virtual void CreateSessionAndGenerateRequest(uint32_t promise_id,
> -      SessionType session_type,
> -      InitDataType init_data_type,
> -      const uint8_t* init_data,
> -      uint32_t init_data_size) = 0;
> -
> -    // Loads the session of type |session_type| specified by |session_id|.
> -    // The CDM must respond by calling either Host::OnResolveNewSessionPromise()
> -    // or Host::OnRejectPromise(). If the session is not found, call
> -    // Host::OnResolveNewSessionPromise() with session_id = NULL.
> -    virtual void LoadSession(uint32_t promise_id,
> -      SessionType session_type,
> -      const char* session_id,
> -      uint32_t session_id_size) = 0;
> -
> -    // Updates the session with |response|. The CDM must respond by calling
> -    // either Host::OnResolvePromise() or Host::OnRejectPromise().
> -    virtual void UpdateSession(uint32_t promise_id,
> -      const char* session_id,
> -      uint32_t session_id_size,
> -      const uint8_t* response,
> -      uint32_t response_size) = 0;
> -
> -    // Requests that the CDM close the session. The CDM must respond by calling
> -    // either Host::OnResolvePromise() or Host::OnRejectPromise() when the request
> -    // has been processed. This may be before the session is closed. Once the
> -    // session is closed, Host::OnSessionClosed() must also be called.
> -    virtual void CloseSession(uint32_t promise_id,
> -      const char* session_id,
> -      uint32_t session_id_size) = 0;
> -
> -    // Removes any stored session data associated with this session. Will only be
> -    // called for persistent sessions. The CDM must respond by calling either
> -    // Host::OnResolvePromise() or Host::OnRejectPromise() when the request has
> -    // been processed.
> -    virtual void RemoveSession(uint32_t promise_id,
> -      const char* session_id,
> -      uint32_t session_id_size) = 0;
> -
> -    // Performs scheduled operation with |context| when the timer fires.
> -    virtual void TimerExpired(void* context) = 0;
> -
> -    // Decrypts the |encrypted_buffer|.
> -    //
> -    // Returns kSuccess if decryption succeeded, in which case the callee
> -    // should have filled the |decrypted_buffer| and passed the ownership of
> -    // |data| in |decrypted_buffer| to the caller.
> -    // Returns kNoKey if the CDM did not have the necessary decryption key
> -    // to decrypt.
> -    // Returns kDecryptError if any other error happened.
> -    // If the return value is not kSuccess, |decrypted_buffer| should be ignored
> -    // by the caller.
> -    virtual Status Decrypt(const InputBuffer_2& encrypted_buffer,
> -      DecryptedBlock* decrypted_buffer) = 0;
> -
> -    // Initializes the CDM audio decoder with |audio_decoder_config|. This
> -    // function must be called before DecryptAndDecodeSamples() is called.
> -    //
> -    // Returns kSuccess if the |audio_decoder_config| is supported and the CDM
> -    // audio decoder is successfully initialized.
> -    // Returns kInitializationError if |audio_decoder_config| is not supported.
> -    // The CDM may still be able to do Decrypt().
> -    // Returns kDeferredInitialization if the CDM is not ready to initialize the
> -    // decoder at this time. Must call Host::OnDeferredInitializationDone() once
> -    // initialization is complete.
> -    virtual Status InitializeAudioDecoder(
> +  // Called by the host after a call to Host::RequestStorageId(). If the
> +  // version of the storage ID requested is available, |storage_id| and
> +  // |storage_id_size| are set appropriately. |version| will be the same as
> +  // what was requested, unless 0 (latest) was requested, in which case
> +  // |version| will be the actual version number for the |storage_id| returned.
> +  // If the requested version is not available, null/zero will be provided as
> +  // |storage_id| and |storage_id_size|, respectively, and |version| should be
> +  // ignored.
> +  virtual void OnStorageId(uint32_t version,
> +                           const uint8_t* storage_id,
> +                           uint32_t storage_id_size) = 0;
> +
> +  // Destroys the object in the same context as it was created.
> +  virtual void Destroy() = 0;
> +
> + protected:
> +  ContentDecryptionModule_10() {}
> +  virtual ~ContentDecryptionModule_10() {}
> +};
> +
> +// ContentDecryptionModule interface that all CDMs need to implement.
> +// The interface is versioned for backward compatibility.
> +// Note: ContentDecryptionModule implementations must use the allocator
> +// provided in CreateCdmInstance() to allocate any Buffer that needs to
> +// be passed back to the caller. Implementations must call Buffer::Destroy()
> +// when a Buffer is created that will never be returned to the caller.
> +class CDM_CLASS_API ContentDecryptionModule_11 {
> + public:
> +  static const int kVersion = 11;
> +  static const bool kIsStable = true;
> +  typedef Host_11 Host;
> +
> +  // Initializes the CDM instance, providing information about permitted
> +  // functionalities. The CDM must respond by calling Host::OnInitialized()
> +  // with whether the initialization succeeded. No other calls will be made by
> +  // the host before Host::OnInitialized() returns.
> +  // If |allow_distinctive_identifier| is false, messages from the CDM,
> +  // such as message events, must not contain a Distinctive Identifier,
> +  // even in an encrypted form.
> +  // If |allow_persistent_state| is false, the CDM must not attempt to
> +  // persist state. Calls to CreateFileIO() will fail.
> +  // If |use_hw_secure_codecs| is true, the CDM must ensure the decryption key
> +  // and video buffers (compressed and uncompressed) are securely protected by
> +  // hardware.
> +  virtual void Initialize(bool allow_distinctive_identifier,
> +                          bool allow_persistent_state,
> +                          bool use_hw_secure_codecs) = 0;
> +
> +  // Gets the key status if the CDM has a hypothetical key with the |policy|.
> +  // The CDM must respond by calling either Host::OnResolveKeyStatusPromise()
> +  // with the result key status or Host::OnRejectPromise() if an unexpected
> +  // error happened or this method is not supported.
> +  virtual void GetStatusForPolicy(uint32_t promise_id,
> +                                  const Policy& policy) = 0;
> +
> +  // SetServerCertificate(), CreateSessionAndGenerateRequest(), LoadSession(),
> +  // UpdateSession(), CloseSession(), and RemoveSession() all accept a
> +  // |promise_id|, which must be passed to the completion Host method
> +  // (e.g. Host::OnResolveNewSessionPromise()).
> +
> +  // Provides a server certificate to be used to encrypt messages to the
> +  // license server. The CDM must respond by calling either
> +  // Host::OnResolvePromise() or Host::OnRejectPromise().
> +  // If the CDM does not support server certificates, the promise should be
> +  // rejected with kExceptionNotSupportedError. If |server_certificate_data|
> +  // is empty, reject with kExceptionTypeError. Any other error should be
> +  // rejected with kExceptionInvalidStateError or kExceptionQuotaExceededError.
> +  // TODO(crbug.com/796417): Add support for the promise to return true or
> +  // false, rather than using kExceptionNotSupportedError to mean false.
> +  virtual void SetServerCertificate(uint32_t promise_id,
> +                                    const uint8_t* server_certificate_data,
> +                                    uint32_t server_certificate_data_size) = 0;
> +
> +  // Creates a session given |session_type|, |init_data_type|, and |init_data|.
> +  // The CDM must respond by calling either Host::OnResolveNewSessionPromise()
> +  // or Host::OnRejectPromise().
> +  virtual void CreateSessionAndGenerateRequest(uint32_t promise_id,
> +                                               SessionType session_type,
> +                                               InitDataType init_data_type,
> +                                               const uint8_t* init_data,
> +                                               uint32_t init_data_size) = 0;
> +
> +  // Loads the session of type |session_type| specified by |session_id|.
> +  // The CDM must respond by calling either Host::OnResolveNewSessionPromise()
> +  // or Host::OnRejectPromise(). If the session is not found, call
> +  // Host::OnResolveNewSessionPromise() with session_id = NULL.
> +  virtual void LoadSession(uint32_t promise_id,
> +                           SessionType session_type,
> +                           const char* session_id,
> +                           uint32_t session_id_size) = 0;
> +
> +  // Updates the session with |response|. The CDM must respond by calling
> +  // either Host::OnResolvePromise() or Host::OnRejectPromise().
> +  virtual void UpdateSession(uint32_t promise_id,
> +                             const char* session_id,
> +                             uint32_t session_id_size,
> +                             const uint8_t* response,
> +                             uint32_t response_size) = 0;
> +
> +  // Requests that the CDM close the session. The CDM must respond by calling
> +  // either Host::OnResolvePromise() or Host::OnRejectPromise() when the request
> +  // has been processed. This may be before the session is closed. Once the
> +  // session is closed, Host::OnSessionClosed() must also be called.
> +  virtual void CloseSession(uint32_t promise_id,
> +                            const char* session_id,
> +                            uint32_t session_id_size) = 0;
> +
> +  // Removes any stored session data associated with this session. Will only be
> +  // called for persistent sessions. The CDM must respond by calling either
> +  // Host::OnResolvePromise() or Host::OnRejectPromise() when the request has
> +  // been processed.
> +  virtual void RemoveSession(uint32_t promise_id,
> +                             const char* session_id,
> +                             uint32_t session_id_size) = 0;
> +
> +  // Performs scheduled operation with |context| when the timer fires.
> +  virtual void TimerExpired(void* context) = 0;
> +
> +  // Decrypts the |encrypted_buffer|.
> +  //
> +  // Returns kSuccess if decryption succeeded, in which case the callee
> +  // should have filled the |decrypted_buffer| and passed the ownership of
> +  // |data| in |decrypted_buffer| to the caller.
> +  // Returns kNoKey if the CDM did not have the necessary decryption key
> +  // to decrypt.
> +  // Returns kDecryptError if any other error happened.
> +  // If the return value is not kSuccess, |decrypted_buffer| should be ignored
> +  // by the caller.
> +  virtual Status Decrypt(const InputBuffer_2& encrypted_buffer,
> +                         DecryptedBlock* decrypted_buffer) = 0;
> +
> +  // Initializes the CDM audio decoder with |audio_decoder_config|. This
> +  // function must be called before DecryptAndDecodeSamples() is called.
> +  //
> +  // Returns kSuccess if the |audio_decoder_config| is supported and the CDM
> +  // audio decoder is successfully initialized.
> +  // Returns kInitializationError if |audio_decoder_config| is not supported.
> +  // The CDM may still be able to do Decrypt().
> +  // Returns kDeferredInitialization if the CDM is not ready to initialize the
> +  // decoder at this time. Must call Host::OnDeferredInitializationDone() once
> +  // initialization is complete.
> +  virtual Status InitializeAudioDecoder(
>        const AudioDecoderConfig_2& audio_decoder_config) = 0;
>  
> -    // Initializes the CDM video decoder with |video_decoder_config|. This
> -    // function must be called before DecryptAndDecodeFrame() is called.
> -    //
> -    // Returns kSuccess if the |video_decoder_config| is supported and the CDM
> -    // video decoder is successfully initialized.
> -    // Returns kInitializationError if |video_decoder_config| is not supported.
> -    // The CDM may still be able to do Decrypt().
> -    // Returns kDeferredInitialization if the CDM is not ready to initialize the
> -    // decoder at this time. Must call Host::OnDeferredInitializationDone() once
> -    // initialization is complete.
> -    virtual Status InitializeVideoDecoder(
> +  // Initializes the CDM video decoder with |video_decoder_config|. This
> +  // function must be called before DecryptAndDecodeFrame() is called.
> +  //
> +  // Returns kSuccess if the |video_decoder_config| is supported and the CDM
> +  // video decoder is successfully initialized.
> +  // Returns kInitializationError if |video_decoder_config| is not supported.
> +  // The CDM may still be able to do Decrypt().
> +  // Returns kDeferredInitialization if the CDM is not ready to initialize the
> +  // decoder at this time. Must call Host::OnDeferredInitializationDone() once
> +  // initialization is complete.
> +  virtual Status InitializeVideoDecoder(
>        const VideoDecoderConfig_2& video_decoder_config) = 0;
>  
> -    // De-initializes the CDM decoder and sets it to an uninitialized state. The
> -    // caller can initialize the decoder again after this call to re-initialize
> -    // it. This can be used to reconfigure the decoder if the configuration
> -    // changes.
> -    virtual void DeinitializeDecoder(StreamType decoder_type) = 0;
> -
> -    // Resets the CDM decoder to an initialized clean state. All internal buffers
> -    // MUST be flushed.
> -    virtual void ResetDecoder(StreamType decoder_type) = 0;
> -
> -    // Decrypts the |encrypted_buffer| and decodes the decrypted buffer into a
> -    // |video_frame|. Upon end-of-stream, the caller should call this function
> -    // repeatedly with empty |encrypted_buffer| (|data| == NULL) until
> -    // kNeedMoreData is returned.
> -    //
> -    // Returns kSuccess if decryption and decoding both succeeded, in which case
> -    // the callee will have filled the |video_frame| and passed the ownership of
> -    // |frame_buffer| in |video_frame| to the caller.
> -    // Returns kNoKey if the CDM did not have the necessary decryption key
> -    // to decrypt.
> -    // Returns kNeedMoreData if more data was needed by the decoder to generate
> -    // a decoded frame (e.g. during initialization and end-of-stream).
> -    // Returns kDecryptError if any decryption error happened.
> -    // Returns kDecodeError if any decoding error happened.
> -    // If the return value is not kSuccess, |video_frame| should be ignored by
> -    // the caller.
> -    virtual Status DecryptAndDecodeFrame(const InputBuffer_2& encrypted_buffer,
> -      VideoFrame* video_frame) = 0;
> -
> -    // Decrypts the |encrypted_buffer| and decodes the decrypted buffer into
> -    // |audio_frames|. Upon end-of-stream, the caller should call this function
> -    // repeatedly with empty |encrypted_buffer| (|data| == NULL) until only empty
> -    // |audio_frames| is produced.
> -    //
> -    // Returns kSuccess if decryption and decoding both succeeded, in which case
> -    // the callee will have filled |audio_frames| and passed the ownership of
> -    // |data| in |audio_frames| to the caller.
> -    // Returns kNoKey if the CDM did not have the necessary decryption key
> -    // to decrypt.
> -    // Returns kNeedMoreData if more data was needed by the decoder to generate
> -    // audio samples (e.g. during initialization and end-of-stream).
> -    // Returns kDecryptError if any decryption error happened.
> -    // Returns kDecodeError if any decoding error happened.
> -    // If the return value is not kSuccess, |audio_frames| should be ignored by
> -    // the caller.
> -    virtual Status DecryptAndDecodeSamples(const InputBuffer_2& encrypted_buffer,
> -      AudioFrames* audio_frames) = 0;
> -
> -    // Called by the host after a platform challenge was initiated via
> -    // Host::SendPlatformChallenge().
> -    virtual void OnPlatformChallengeResponse(
> +  // De-initializes the CDM decoder and sets it to an uninitialized state. The
> +  // caller can initialize the decoder again after this call to re-initialize
> +  // it. This can be used to reconfigure the decoder if the configuration
> +  // changes.
> +  virtual void DeinitializeDecoder(StreamType decoder_type) = 0;
> +
> +  // Resets the CDM decoder to an initialized clean state. All internal buffers
> +  // MUST be flushed.
> +  virtual void ResetDecoder(StreamType decoder_type) = 0;
> +
> +  // Decrypts the |encrypted_buffer| and decodes the decrypted buffer into a
> +  // |video_frame|. Upon end-of-stream, the caller should call this function
> +  // repeatedly with empty |encrypted_buffer| (|data| == NULL) until
> +  // kNeedMoreData is returned.
> +  //
> +  // Returns kSuccess if decryption and decoding both succeeded, in which case
> +  // the callee will have filled the |video_frame| and passed the ownership of
> +  // |frame_buffer| in |video_frame| to the caller.
> +  // Returns kNoKey if the CDM did not have the necessary decryption key
> +  // to decrypt.
> +  // Returns kNeedMoreData if more data was needed by the decoder to generate
> +  // a decoded frame (e.g. during initialization and end-of-stream).
> +  // Returns kDecryptError if any decryption error happened.
> +  // Returns kDecodeError if any decoding error happened.
> +  // If the return value is not kSuccess, |video_frame| should be ignored by
> +  // the caller.
> +  virtual Status DecryptAndDecodeFrame(const InputBuffer_2& encrypted_buffer,
> +                                       VideoFrame* video_frame) = 0;
> +
> +  // Decrypts the |encrypted_buffer| and decodes the decrypted buffer into
> +  // |audio_frames|. Upon end-of-stream, the caller should call this function
> +  // repeatedly with empty |encrypted_buffer| (|data| == NULL) until only empty
> +  // |audio_frames| is produced.
> +  //
> +  // Returns kSuccess if decryption and decoding both succeeded, in which case
> +  // the callee will have filled |audio_frames| and passed the ownership of
> +  // |data| in |audio_frames| to the caller.
> +  // Returns kNoKey if the CDM did not have the necessary decryption key
> +  // to decrypt.
> +  // Returns kNeedMoreData if more data was needed by the decoder to generate
> +  // audio samples (e.g. during initialization and end-of-stream).
> +  // Returns kDecryptError if any decryption error happened.
> +  // Returns kDecodeError if any decoding error happened.
> +  // If the return value is not kSuccess, |audio_frames| should be ignored by
> +  // the caller.
> +  virtual Status DecryptAndDecodeSamples(const InputBuffer_2& encrypted_buffer,
> +                                         AudioFrames* audio_frames) = 0;
> +
> +  // Called by the host after a platform challenge was initiated via
> +  // Host::SendPlatformChallenge().
> +  virtual void OnPlatformChallengeResponse(
>        const PlatformChallengeResponse& response) = 0;
>  
> -    // Called by the host after a call to Host::QueryOutputProtectionStatus(). The
> -    // |link_mask| is a bit mask of OutputLinkTypes and |output_protection_mask|
> -    // is a bit mask of OutputProtectionMethods. If |result| is kQueryFailed,
> -    // then |link_mask| and |output_protection_mask| are undefined and should
> -    // be ignored.
> -    virtual void OnQueryOutputProtectionStatus(
> +  // Called by the host after a call to Host::QueryOutputProtectionStatus(). The
> +  // |link_mask| is a bit mask of OutputLinkTypes and |output_protection_mask|
> +  // is a bit mask of OutputProtectionMethods. If |result| is kQueryFailed,
> +  // then |link_mask| and |output_protection_mask| are undefined and should
> +  // be ignored.
> +  virtual void OnQueryOutputProtectionStatus(
>        QueryResult result,
>        uint32_t link_mask,
>        uint32_t output_protection_mask) = 0;
>  
> -    // Called by the host after a call to Host::RequestStorageId(). If the
> -    // version of the storage ID requested is available, |storage_id| and
> -    // |storage_id_size| are set appropriately. |version| will be the same as
> -    // what was requested, unless 0 (latest) was requested, in which case
> -    // |version| will be the actual version number for the |storage_id| returned.
> -    // If the requested version is not available, null/zero will be provided as
> -    // |storage_id| and |storage_id_size|, respectively, and |version| should be
> -    // ignored.
> -    virtual void OnStorageId(uint32_t version,
> -      const uint8_t* storage_id,
> -      uint32_t storage_id_size) = 0;
> -
> -    // Destroys the object in the same context as it was created.
> -    virtual void Destroy() = 0;
> -
> -  protected:
> -    ContentDecryptionModule_10() {}
> -    virtual ~ContentDecryptionModule_10() {}
> -  };
> -
> -  // ----- Note: CDM interface(s) below still in development and not stable! -----
> -
> -  // ContentDecryptionModule interface that all CDMs need to implement.
> -  // The interface is versioned for backward compatibility.
> -  // Note: ContentDecryptionModule implementations must use the allocator
> -  // provided in CreateCdmInstance() to allocate any Buffer that needs to
> -  // be passed back to the caller. Implementations must call Buffer::Destroy()
> -  // when a Buffer is created that will never be returned to the caller.
> -  class CDM_CLASS_API ContentDecryptionModule_11 {
> -  public:
> -    static const int kVersion = 11;
> -    static const bool kIsStable = false;
> -    typedef Host_11 Host;
> -
> -    // Initializes the CDM instance, providing information about permitted
> -    // functionalities. The CDM must respond by calling Host::OnInitialized()
> -    // with whether the initialization succeeded. No other calls will be made by
> -    // the host before Host::OnInitialized() returns.
> -    // If |allow_distinctive_identifier| is false, messages from the CDM,
> -    // such as message events, must not contain a Distinctive Identifier,
> -    // even in an encrypted form.
> -    // If |allow_persistent_state| is false, the CDM must not attempt to
> -    // persist state. Calls to CreateFileIO() will fail.
> -    // If |use_hw_secure_codecs| is true, the CDM must ensure the decryption key
> -    // and video buffers (compressed and uncompressed) are securely protected by
> -    // hardware.
> -    virtual void Initialize(bool allow_distinctive_identifier,
> -      bool allow_persistent_state,
> -      bool use_hw_secure_codecs) = 0;
> -
> -    // Gets the key status if the CDM has a hypothetical key with the |policy|.
> -    // The CDM must respond by calling either Host::OnResolveKeyStatusPromise()
> -    // with the result key status or Host::OnRejectPromise() if an unexpected
> -    // error happened or this method is not supported.
> -    virtual void GetStatusForPolicy(uint32_t promise_id,
> -      const Policy& policy) = 0;
> -
> -    // SetServerCertificate(), CreateSessionAndGenerateRequest(), LoadSession(),
> -    // UpdateSession(), CloseSession(), and RemoveSession() all accept a
> -    // |promise_id|, which must be passed to the completion Host method
> -    // (e.g. Host::OnResolveNewSessionPromise()).
> -
> -    // Provides a server certificate to be used to encrypt messages to the
> -    // license server. The CDM must respond by calling either
> -    // Host::OnResolvePromise() or Host::OnRejectPromise().
> -    // If the CDM does not support server certificates, the promise should be
> -    // rejected with kExceptionNotSupportedError. If |server_certificate_data|
> -    // is empty, reject with kExceptionTypeError. Any other error should be
> -    // rejected with kExceptionInvalidStateError or kExceptionQuotaExceededError.
> -    // TODO(crbug.com/796417): Add support for the promise to return true or
> -    // false, rather than using kExceptionNotSupportedError to mean false.
> -    virtual void SetServerCertificate(uint32_t promise_id,
> -      const uint8_t* server_certificate_data,
> -      uint32_t server_certificate_data_size) = 0;
> -
> -    // Creates a session given |session_type|, |init_data_type|, and |init_data|.
> -    // The CDM must respond by calling either Host::OnResolveNewSessionPromise()
> -    // or Host::OnRejectPromise().
> -    virtual void CreateSessionAndGenerateRequest(uint32_t promise_id,
> -      SessionType session_type,
> -      InitDataType init_data_type,
> -      const uint8_t* init_data,
> -      uint32_t init_data_size) = 0;
> -
> -    // Loads the session of type |session_type| specified by |session_id|.
> -    // The CDM must respond by calling either Host::OnResolveNewSessionPromise()
> -    // or Host::OnRejectPromise(). If the session is not found, call
> -    // Host::OnResolveNewSessionPromise() with session_id = NULL.
> -    virtual void LoadSession(uint32_t promise_id,
> -      SessionType session_type,
> -      const char* session_id,
> -      uint32_t session_id_size) = 0;
> -
> -    // Updates the session with |response|. The CDM must respond by calling
> -    // either Host::OnResolvePromise() or Host::OnRejectPromise().
> -    virtual void UpdateSession(uint32_t promise_id,
> -      const char* session_id,
> -      uint32_t session_id_size,
> -      const uint8_t* response,
> -      uint32_t response_size) = 0;
> -
> -    // Requests that the CDM close the session. The CDM must respond by calling
> -    // either Host::OnResolvePromise() or Host::OnRejectPromise() when the request
> -    // has been processed. This may be before the session is closed. Once the
> -    // session is closed, Host::OnSessionClosed() must also be called.
> -    virtual void CloseSession(uint32_t promise_id,
> -      const char* session_id,
> -      uint32_t session_id_size) = 0;
> -
> -    // Removes any stored session data associated with this session. Removes all
> -    // license(s) and key(s) associated with the session, whether they are in
> -    // memory, persistent store, or both. For persistent session types, other
> -    // session data (e.g. record of license destruction) will be cleared as
> -    // defined for each session type once a release message acknowledgment is
> -    // processed by UpdateSession(). The CDM must respond by calling either
> -    // Host::OnResolvePromise() or Host::OnRejectPromise() when the request has
> -    // been processed.
> -    virtual void RemoveSession(uint32_t promise_id,
> -      const char* session_id,
> -      uint32_t session_id_size) = 0;
> -
> -    // Performs scheduled operation with |context| when the timer fires.
> -    virtual void TimerExpired(void* context) = 0;
> -
> -    // Decrypts the |encrypted_buffer|.
> -    //
> -    // Returns kSuccess if decryption succeeded, in which case the callee
> -    // should have filled the |decrypted_buffer| and passed the ownership of
> -    // |data| in |decrypted_buffer| to the caller.
> -    // Returns kNoKey if the CDM did not have the necessary decryption key
> -    // to decrypt.
> -    // Returns kDecryptError if any other error happened.
> -    // If the return value is not kSuccess, |decrypted_buffer| should be ignored
> -    // by the caller.
> -    virtual Status Decrypt(const InputBuffer_2& encrypted_buffer,
> -      DecryptedBlock* decrypted_buffer) = 0;
> -
> -    // Initializes the CDM audio decoder with |audio_decoder_config|. This
> -    // function must be called before DecryptAndDecodeSamples() is called.
> -    //
> -    // Returns kSuccess if the |audio_decoder_config| is supported and the CDM
> -    // audio decoder is successfully initialized.
> -    // Returns kInitializationError if |audio_decoder_config| is not supported.
> -    // The CDM may still be able to do Decrypt().
> -    // Returns kDeferredInitialization if the CDM is not ready to initialize the
> -    // decoder at this time. Must call Host::OnDeferredInitializationDone() once
> -    // initialization is complete.
> -    virtual Status InitializeAudioDecoder(
> +  // Called by the host after a call to Host::RequestStorageId(). If the
> +  // version of the storage ID requested is available, |storage_id| and
> +  // |storage_id_size| are set appropriately. |version| will be the same as
> +  // what was requested, unless 0 (latest) was requested, in which case
> +  // |version| will be the actual version number for the |storage_id| returned.
> +  // If the requested version is not available, null/zero will be provided as
> +  // |storage_id| and |storage_id_size|, respectively, and |version| should be
> +  // ignored.
> +  virtual void OnStorageId(uint32_t version,
> +                           const uint8_t* storage_id,
> +                           uint32_t storage_id_size) = 0;
> +
> +  // Destroys the object in the same context as it was created.
> +  virtual void Destroy() = 0;
> +
> + protected:
> +  ContentDecryptionModule_11() {}
> +  virtual ~ContentDecryptionModule_11() {}
> +};
> +
> +// ----- Note: CDM interface(s) below still in development and not stable! -----
> +
> +// ContentDecryptionModule interface that all CDMs need to implement.
> +// The interface is versioned for backward compatibility.
> +// Note: ContentDecryptionModule implementations must use the allocator
> +// provided in CreateCdmInstance() to allocate any Buffer that needs to
> +// be passed back to the caller. Implementations must call Buffer::Destroy()
> +// when a Buffer is created that will never be returned to the caller.
> +class CDM_CLASS_API ContentDecryptionModule_12 {
> + public:
> +  static const int kVersion = 12;
> +  static const bool kIsStable = false;
> +  typedef Host_12 Host;
> +
> +  // Initializes the CDM instance, providing information about permitted
> +  // functionalities. The CDM must respond by calling Host::OnInitialized()
> +  // with whether the initialization succeeded. No other calls will be made by
> +  // the host before Host::OnInitialized() returns.
> +  // If |allow_distinctive_identifier| is false, messages from the CDM,
> +  // such as message events, must not contain a Distinctive Identifier,
> +  // even in an encrypted form.
> +  // If |allow_persistent_state| is false, the CDM must not attempt to
> +  // persist state. Calls to CreateFileIO() will fail.
> +  // If |use_hw_secure_codecs| is true, the CDM must ensure the decryption key
> +  // and video buffers (compressed and uncompressed) are securely protected by
> +  // hardware.
> +  virtual void Initialize(bool allow_distinctive_identifier,
> +                          bool allow_persistent_state,
> +                          bool use_hw_secure_codecs) = 0;
> +
> +  // Gets the key status if the CDM has a hypothetical key with the |policy|.
> +  // The CDM must respond by calling either Host::OnResolveKeyStatusPromise()
> +  // with the result key status or Host::OnRejectPromise() if an unexpected
> +  // error happened or this method is not supported.
> +  virtual void GetStatusForPolicy(uint32_t promise_id,
> +                                  const Policy& policy) = 0;
> +
> +  // SetServerCertificate(), CreateSessionAndGenerateRequest(), LoadSession(),
> +  // UpdateSession(), CloseSession(), and RemoveSession() all accept a
> +  // |promise_id|, which must be passed to the completion Host method
> +  // (e.g. Host::OnResolveNewSessionPromise()).
> +
> +  // Provides a server certificate to be used to encrypt messages to the
> +  // license server. The CDM must respond by calling either
> +  // Host::OnResolvePromise() or Host::OnRejectPromise().
> +  // If the CDM does not support server certificates, the promise should be
> +  // rejected with kExceptionNotSupportedError. If |server_certificate_data|
> +  // is empty, reject with kExceptionTypeError. Any other error should be
> +  // rejected with kExceptionInvalidStateError or kExceptionQuotaExceededError.
> +  // TODO(crbug.com/796417): Add support for the promise to return true or
> +  // false, rather than using kExceptionNotSupportedError to mean false.
> +  virtual void SetServerCertificate(uint32_t promise_id,
> +                                    const uint8_t* server_certificate_data,
> +                                    uint32_t server_certificate_data_size) = 0;
> +
> +  // Creates a session given |session_type|, |init_data_type|, and |init_data|.
> +  // The CDM must respond by calling either Host::OnResolveNewSessionPromise()
> +  // or Host::OnRejectPromise().
> +  virtual void CreateSessionAndGenerateRequest(uint32_t promise_id,
> +                                               SessionType session_type,
> +                                               InitDataType init_data_type,
> +                                               const uint8_t* init_data,
> +                                               uint32_t init_data_size) = 0;
> +
> +  // Loads the session of type |session_type| specified by |session_id|.
> +  // The CDM must respond by calling either Host::OnResolveNewSessionPromise()
> +  // or Host::OnRejectPromise(). If the session is not found, call
> +  // Host::OnResolveNewSessionPromise() with session_id = NULL.
> +  virtual void LoadSession(uint32_t promise_id,
> +                           SessionType session_type,
> +                           const char* session_id,
> +                           uint32_t session_id_size) = 0;
> +
> +  // Updates the session with |response|. The CDM must respond by calling
> +  // either Host::OnResolvePromise() or Host::OnRejectPromise().
> +  virtual void UpdateSession(uint32_t promise_id,
> +                             const char* session_id,
> +                             uint32_t session_id_size,
> +                             const uint8_t* response,
> +                             uint32_t response_size) = 0;
> +
> +  // Requests that the CDM close the session. The CDM must respond by calling
> +  // either Host::OnResolvePromise() or Host::OnRejectPromise() when the request
> +  // has been processed. This may be before the session is closed. Once the
> +  // session is closed, Host::OnSessionClosed() must also be called.
> +  virtual void CloseSession(uint32_t promise_id,
> +                            const char* session_id,
> +                            uint32_t session_id_size) = 0;
> +
> +  // Removes any stored session data associated with this session. Removes all
> +  // license(s) and key(s) associated with the session, whether they are in
> +  // memory, persistent store, or both. For persistent session types, other
> +  // session data (e.g. record of license destruction) will be cleared as
> +  // defined for each session type once a release message acknowledgment is
> +  // processed by UpdateSession(). The CDM must respond by calling either
> +  // Host::OnResolvePromise() or Host::OnRejectPromise() when the request has
> +  // been processed.
> +  virtual void RemoveSession(uint32_t promise_id,
> +                             const char* session_id,
> +                             uint32_t session_id_size) = 0;
> +
> +  // Performs scheduled operation with |context| when the timer fires.
> +  virtual void TimerExpired(void* context) = 0;
> +
> +  // Decrypts the |encrypted_buffer|.
> +  //
> +  // Returns kSuccess if decryption succeeded, in which case the callee
> +  // should have filled the |decrypted_buffer| and passed the ownership of
> +  // |data| in |decrypted_buffer| to the caller.
> +  // Returns kNoKey if the CDM did not have the necessary decryption key
> +  // to decrypt.
> +  // Returns kDecryptError if any other error happened.
> +  // If the return value is not kSuccess, |decrypted_buffer| should be ignored
> +  // by the caller.
> +  virtual Status Decrypt(const InputBuffer_2& encrypted_buffer,
> +                         DecryptedBlock* decrypted_buffer) = 0;
> +
> +  // Initializes the CDM audio decoder with |audio_decoder_config|. This
> +  // function must be called before DecryptAndDecodeSamples() is called.
> +  //
> +  // Returns kSuccess if the |audio_decoder_config| is supported and the CDM
> +  // audio decoder is successfully initialized.
> +  // Returns kInitializationError if |audio_decoder_config| is not supported.
> +  // The CDM may still be able to do Decrypt().
> +  // Returns kDeferredInitialization if the CDM is not ready to initialize the
> +  // decoder at this time. Must call Host::OnDeferredInitializationDone() once
> +  // initialization is complete.
> +  virtual Status InitializeAudioDecoder(
>        const AudioDecoderConfig_2& audio_decoder_config) = 0;
>  
> -    // Initializes the CDM video decoder with |video_decoder_config|. This
> -    // function must be called before DecryptAndDecodeFrame() is called.
> -    //
> -    // Returns kSuccess if the |video_decoder_config| is supported and the CDM
> -    // video decoder is successfully initialized.
> -    // Returns kInitializationError if |video_decoder_config| is not supported.
> -    // The CDM may still be able to do Decrypt().
> -    // Returns kDeferredInitialization if the CDM is not ready to initialize the
> -    // decoder at this time. Must call Host::OnDeferredInitializationDone() once
> -    // initialization is complete.
> -    virtual Status InitializeVideoDecoder(
> +  // Initializes the CDM video decoder with |video_decoder_config|. This
> +  // function must be called before DecryptAndDecodeFrame() is called.
> +  //
> +  // Returns kSuccess if the |video_decoder_config| is supported and the CDM
> +  // video decoder is successfully initialized.
> +  // Returns kInitializationError if |video_decoder_config| is not supported.
> +  // The CDM may still be able to do Decrypt().
> +  // Returns kDeferredInitialization if the CDM is not ready to initialize the
> +  // decoder at this time. Must call Host::OnDeferredInitializationDone() once
> +  // initialization is complete.
> +  virtual Status InitializeVideoDecoder(
>        const VideoDecoderConfig_3& video_decoder_config) = 0;
>  
> -    // De-initializes the CDM decoder and sets it to an uninitialized state. The
> -    // caller can initialize the decoder again after this call to re-initialize
> -    // it. This can be used to reconfigure the decoder if the configuration
> -    // changes.
> -    virtual void DeinitializeDecoder(StreamType decoder_type) = 0;
> -
> -    // Resets the CDM decoder to an initialized clean state. All internal buffers
> -    // MUST be flushed.
> -    virtual void ResetDecoder(StreamType decoder_type) = 0;
> -
> -    // Decrypts the |encrypted_buffer| and decodes the decrypted buffer into a
> -    // |video_frame|. Upon end-of-stream, the caller should call this function
> -    // repeatedly with empty |encrypted_buffer| (|data| == NULL) until
> -    // kNeedMoreData is returned.
> -    //
> -    // Returns kSuccess if decryption and decoding both succeeded, in which case
> -    // the callee will have filled the |video_frame| and passed the ownership of
> -    // |frame_buffer| in |video_frame| to the caller.
> -    // Returns kNoKey if the CDM did not have the necessary decryption key
> -    // to decrypt.
> -    // Returns kNeedMoreData if more data was needed by the decoder to generate
> -    // a decoded frame (e.g. during initialization and end-of-stream).
> -    // Returns kDecryptError if any decryption error happened.
> -    // Returns kDecodeError if any decoding error happened.
> -    // If the return value is not kSuccess, |video_frame| should be ignored by
> -    // the caller.
> -    virtual Status DecryptAndDecodeFrame(const InputBuffer_2& encrypted_buffer,
> -      VideoFrame_2* video_frame) = 0;
> -
> -    // Decrypts the |encrypted_buffer| and decodes the decrypted buffer into
> -    // |audio_frames|. Upon end-of-stream, the caller should call this function
> -    // repeatedly with empty |encrypted_buffer| (|data| == NULL) until only empty
> -    // |audio_frames| is produced.
> -    //
> -    // Returns kSuccess if decryption and decoding both succeeded, in which case
> -    // the callee will have filled |audio_frames| and passed the ownership of
> -    // |data| in |audio_frames| to the caller.
> -    // Returns kNoKey if the CDM did not have the necessary decryption key
> -    // to decrypt.
> -    // Returns kNeedMoreData if more data was needed by the decoder to generate
> -    // audio samples (e.g. during initialization and end-of-stream).
> -    // Returns kDecryptError if any decryption error happened.
> -    // Returns kDecodeError if any decoding error happened.
> -    // If the return value is not kSuccess, |audio_frames| should be ignored by
> -    // the caller.
> -    virtual Status DecryptAndDecodeSamples(const InputBuffer_2& encrypted_buffer,
> -      AudioFrames* audio_frames) = 0;
> -
> -    // Called by the host after a platform challenge was initiated via
> -    // Host::SendPlatformChallenge().
> -    virtual void OnPlatformChallengeResponse(
> +  // De-initializes the CDM decoder and sets it to an uninitialized state. The
> +  // caller can initialize the decoder again after this call to re-initialize
> +  // it. This can be used to reconfigure the decoder if the configuration
> +  // changes.
> +  virtual void DeinitializeDecoder(StreamType decoder_type) = 0;
> +
> +  // Resets the CDM decoder to an initialized clean state. All internal buffers
> +  // MUST be flushed.
> +  virtual void ResetDecoder(StreamType decoder_type) = 0;
> +
> +  // Decrypts the |encrypted_buffer| and decodes the decrypted buffer into a
> +  // |video_frame|. Upon end-of-stream, the caller should call this function
> +  // repeatedly with empty |encrypted_buffer| (|data| == NULL) until
> +  // kNeedMoreData is returned.
> +  //
> +  // Returns kSuccess if decryption and decoding both succeeded, in which case
> +  // the callee will have filled the |video_frame| and passed the ownership of
> +  // |frame_buffer| in |video_frame| to the caller.
> +  // Returns kNoKey if the CDM did not have the necessary decryption key
> +  // to decrypt.
> +  // Returns kNeedMoreData if more data was needed by the decoder to generate
> +  // a decoded frame (e.g. during initialization and end-of-stream).
> +  // Returns kDecryptError if any decryption error happened.
> +  // Returns kDecodeError if any decoding error happened.
> +  // If the return value is not kSuccess, |video_frame| should be ignored by
> +  // the caller.
> +  virtual Status DecryptAndDecodeFrame(const InputBuffer_2& encrypted_buffer,
> +                                       VideoFrame_2* video_frame) = 0;
> +
> +  // Decrypts the |encrypted_buffer| and decodes the decrypted buffer into
> +  // |audio_frames|. Upon end-of-stream, the caller should call this function
> +  // repeatedly with empty |encrypted_buffer| (|data| == NULL) until only empty
> +  // |audio_frames| is produced.
> +  //
> +  // Returns kSuccess if decryption and decoding both succeeded, in which case
> +  // the callee will have filled |audio_frames| and passed the ownership of
> +  // |data| in |audio_frames| to the caller.
> +  // Returns kNoKey if the CDM did not have the necessary decryption key
> +  // to decrypt.
> +  // Returns kNeedMoreData if more data was needed by the decoder to generate
> +  // audio samples (e.g. during initialization and end-of-stream).
> +  // Returns kDecryptError if any decryption error happened.
> +  // Returns kDecodeError if any decoding error happened.
> +  // If the return value is not kSuccess, |audio_frames| should be ignored by
> +  // the caller.
> +  virtual Status DecryptAndDecodeSamples(const InputBuffer_2& encrypted_buffer,
> +                                         AudioFrames* audio_frames) = 0;
> +
> +  // Called by the host after a platform challenge was initiated via
> +  // Host::SendPlatformChallenge().
> +  virtual void OnPlatformChallengeResponse(
>        const PlatformChallengeResponse& response) = 0;
>  
> -    // Called by the host after a call to Host::QueryOutputProtectionStatus(). The
> -    // |link_mask| is a bit mask of OutputLinkTypes and |output_protection_mask|
> -    // is a bit mask of OutputProtectionMethods. If |result| is kQueryFailed,
> -    // then |link_mask| and |output_protection_mask| are undefined and should
> -    // be ignored.
> -    virtual void OnQueryOutputProtectionStatus(
> +  // Called by the host after a call to Host::QueryOutputProtectionStatus(). The
> +  // |link_mask| is a bit mask of OutputLinkTypes and |output_protection_mask|
> +  // is a bit mask of OutputProtectionMethods. If |result| is kQueryFailed,
> +  // then |link_mask| and |output_protection_mask| are undefined and should
> +  // be ignored.
> +  virtual void OnQueryOutputProtectionStatus(
>        QueryResult result,
>        uint32_t link_mask,
>        uint32_t output_protection_mask) = 0;
>  
> -    // Called by the host after a call to Host::RequestStorageId(). If the
> -    // version of the storage ID requested is available, |storage_id| and
> -    // |storage_id_size| are set appropriately. |version| will be the same as
> -    // what was requested, unless 0 (latest) was requested, in which case
> -    // |version| will be the actual version number for the |storage_id| returned.
> -    // If the requested version is not available, null/zero will be provided as
> -    // |storage_id| and |storage_id_size|, respectively, and |version| should be
> -    // ignored.
> -    virtual void OnStorageId(uint32_t version,
> -      const uint8_t* storage_id,
> -      uint32_t storage_id_size) = 0;
> -
> -    // Destroys the object in the same context as it was created.
> -    virtual void Destroy() = 0;
> -
> -  protected:
> -    ContentDecryptionModule_11() {}
> -    virtual ~ContentDecryptionModule_11() {}
> -  };
> -
> -  class CDM_CLASS_API Host_9 {
> -  public:
> -    static const int kVersion = 9;
> -
> -    // Returns a Buffer* containing non-zero members upon success, or NULL on
> -    // failure. The caller owns the Buffer* after this call. The buffer is not
> -    // guaranteed to be zero initialized. The capacity of the allocated Buffer
> -    // is guaranteed to be not less than |capacity|.
> -    virtual Buffer* Allocate(uint32_t capacity) = 0;
> -
> -    // Requests the host to call ContentDecryptionModule::TimerFired() |delay_ms|
> -    // from now with |context|.
> -    virtual void SetTimer(int64_t delay_ms, void* context) = 0;
> -
> -    // Returns the current wall time.
> -    virtual Time GetCurrentWallTime() = 0;
> -
> -    // Called by the CDM when a key status is available in response to
> -    // GetStatusForPolicy().
> -    virtual void OnResolveKeyStatusPromise(uint32_t promise_id,
> -      KeyStatus key_status) = 0;
> -
> -    // Called by the CDM when a session is created or loaded and the value for the
> -    // MediaKeySession's sessionId attribute is available (|session_id|).
> -    // This must be called before OnSessionMessage() or
> -    // OnSessionKeysChange() is called for the same session. |session_id_size|
> -    // should not include null termination.
> -    // When called in response to LoadSession(), the |session_id| must be the
> -    // same as the |session_id| passed in LoadSession(), or NULL if the
> -    // session could not be loaded.
> -    virtual void OnResolveNewSessionPromise(uint32_t promise_id,
> -      const char* session_id,
> -      uint32_t session_id_size) = 0;
> -
> -    // Called by the CDM when a session is updated or released.
> -    virtual void OnResolvePromise(uint32_t promise_id) = 0;
> -
> -    // Called by the CDM when an error occurs as a result of one of the
> -    // ContentDecryptionModule calls that accept a |promise_id|.
> -    // |exception| must be specified. |error_message| and |system_code|
> -    // are optional. |error_message_size| should not include null termination.
> -    virtual void OnRejectPromise(uint32_t promise_id,
> -      Exception exception,
> -      uint32_t system_code,
> -      const char* error_message,
> -      uint32_t error_message_size) = 0;
> -
> -    // Called by the CDM when it has a message for session |session_id|.
> -    // Size parameters should not include null termination.
> -    virtual void OnSessionMessage(const char* session_id,
> -      uint32_t session_id_size,
> -      MessageType message_type,
> -      const char* message,
> -      uint32_t message_size) = 0;
> -
> -    // Called by the CDM when there has been a change in keys or their status for
> -    // session |session_id|. |has_additional_usable_key| should be set if a
> -    // key is newly usable (e.g. new key available, previously expired key has
> -    // been renewed, etc.) and the browser should attempt to resume playback.
> -    // |keys_info| is the list of key IDs for this session along with their
> -    // current status. |keys_info_count| is the number of entries in |keys_info|.
> -    // Size parameter for |session_id| should not include null termination.
> -    virtual void OnSessionKeysChange(const char* session_id,
> -      uint32_t session_id_size,
> -      bool has_additional_usable_key,
> -      const KeyInformation* keys_info,
> -      uint32_t keys_info_count) = 0;
> -
> -    // Called by the CDM when there has been a change in the expiration time for
> -    // session |session_id|. This can happen as the result of an Update() call
> -    // or some other event. If this happens as a result of a call to Update(),
> -    // it must be called before resolving the Update() promise. |new_expiry_time|
> -    // represents the time after which the key(s) in the session will no longer
> -    // be usable for decryption. It can be 0 if no such time exists or if the
> -    // license explicitly never expires. Size parameter should not include null
> -    // termination.
> -    virtual void OnExpirationChange(const char* session_id,
> -      uint32_t session_id_size,
> -      Time new_expiry_time) = 0;
> -
> -    // Called by the CDM when session |session_id| is closed. Size
> -    // parameter should not include null termination.
> -    virtual void OnSessionClosed(const char* session_id,
> -      uint32_t session_id_size) = 0;
> -
> -    // The following are optional methods that may not be implemented on all
> -    // platforms.
> -
> -    // Sends a platform challenge for the given |service_id|. |challenge| is at
> -    // most 256 bits of data to be signed. Once the challenge has been completed,
> -    // the host will call ContentDecryptionModule::OnPlatformChallengeResponse()
> -    // with the signed challenge response and platform certificate. Size
> -    // parameters should not include null termination.
> -    virtual void SendPlatformChallenge(const char* service_id,
> -      uint32_t service_id_size,
> -      const char* challenge,
> -      uint32_t challenge_size) = 0;
> -
> -    // Attempts to enable output protection (e.g. HDCP) on the display link. The
> -    // |desired_protection_mask| is a bit mask of OutputProtectionMethods. No
> -    // status callback is issued, the CDM must call QueryOutputProtectionStatus()
> -    // periodically to ensure the desired protections are applied.
> -    virtual void EnableOutputProtection(uint32_t desired_protection_mask) = 0;
> -
> -    // Requests the current output protection status. Once the host has the status
> -    // it will call ContentDecryptionModule::OnQueryOutputProtectionStatus().
> -    virtual void QueryOutputProtectionStatus() = 0;
> -
> -    // Must be called by the CDM if it returned kDeferredInitialization during
> -    // InitializeAudioDecoder() or InitializeVideoDecoder().
> -    virtual void OnDeferredInitializationDone(StreamType stream_type,
> -      Status decoder_status) = 0;
> -
> -    // Creates a FileIO object from the host to do file IO operation. Returns NULL
> -    // if a FileIO object cannot be obtained. Once a valid FileIO object is
> -    // returned, |client| must be valid until FileIO::Close() is called. The
> -    // CDM can call this method multiple times to operate on different files.
> -    virtual FileIO* CreateFileIO(FileIOClient* client) = 0;
> -
> -    // Requests a specific version of the storage ID. A storage ID is a stable,
> -    // device specific ID used by the CDM to securely store persistent data. The
> -    // ID will be returned by the host via ContentDecryptionModule::OnStorageId().
> -    // If |version| is 0, the latest version will be returned. All |version|s
> -    // that are greater than or equal to 0x80000000 are reserved for the CDM and
> -    // should not be supported or returned by the host. The CDM must not expose
> -    // the ID outside the client device, even in encrypted form.
> -    virtual void RequestStorageId(uint32_t version) = 0;
> -
> -  protected:
> -    Host_9() {}
> -    virtual ~Host_9() {}
> -  };
> -
> -  class CDM_CLASS_API Host_10 {
> -  public:
> -    static const int kVersion = 10;
> -
> -    // Returns a Buffer* containing non-zero members upon success, or NULL on
> -    // failure. The caller owns the Buffer* after this call. The buffer is not
> -    // guaranteed to be zero initialized. The capacity of the allocated Buffer
> -    // is guaranteed to be not less than |capacity|.
> -    virtual Buffer* Allocate(uint32_t capacity) = 0;
> -
> -    // Requests the host to call ContentDecryptionModule::TimerFired() |delay_ms|
> -    // from now with |context|.
> -    virtual void SetTimer(int64_t delay_ms, void* context) = 0;
> -
> -    // Returns the current wall time.
> -    virtual Time GetCurrentWallTime() = 0;
> -
> -    // Called by the CDM with the result after the CDM instance was initialized.
> -    virtual void OnInitialized(bool success) = 0;
> -
> -    // Called by the CDM when a key status is available in response to
> -    // GetStatusForPolicy().
> -    virtual void OnResolveKeyStatusPromise(uint32_t promise_id,
> -      KeyStatus key_status) = 0;
> -
> -    // Called by the CDM when a session is created or loaded and the value for the
> -    // MediaKeySession's sessionId attribute is available (|session_id|).
> -    // This must be called before OnSessionMessage() or
> -    // OnSessionKeysChange() is called for the same session. |session_id_size|
> -    // should not include null termination.
> -    // When called in response to LoadSession(), the |session_id| must be the
> -    // same as the |session_id| passed in LoadSession(), or NULL if the
> -    // session could not be loaded.
> -    virtual void OnResolveNewSessionPromise(uint32_t promise_id,
> -      const char* session_id,
> -      uint32_t session_id_size) = 0;
> -
> -    // Called by the CDM when a session is updated or released.
> -    virtual void OnResolvePromise(uint32_t promise_id) = 0;
> -
> -    // Called by the CDM when an error occurs as a result of one of the
> -    // ContentDecryptionModule calls that accept a |promise_id|.
> -    // |exception| must be specified. |error_message| and |system_code|
> -    // are optional. |error_message_size| should not include null termination.
> -    virtual void OnRejectPromise(uint32_t promise_id,
> -      Exception exception,
> -      uint32_t system_code,
> -      const char* error_message,
> -      uint32_t error_message_size) = 0;
> -
> -    // Called by the CDM when it has a message for session |session_id|.
> -    // Size parameters should not include null termination.
> -    virtual void OnSessionMessage(const char* session_id,
> -      uint32_t session_id_size,
> -      MessageType message_type,
> -      const char* message,
> -      uint32_t message_size) = 0;
> -
> -    // Called by the CDM when there has been a change in keys or their status for
> -    // session |session_id|. |has_additional_usable_key| should be set if a
> -    // key is newly usable (e.g. new key available, previously expired key has
> -    // been renewed, etc.) and the browser should attempt to resume playback.
> -    // |keys_info| is the list of key IDs for this session along with their
> -    // current status. |keys_info_count| is the number of entries in |keys_info|.
> -    // Size parameter for |session_id| should not include null termination.
> -    virtual void OnSessionKeysChange(const char* session_id,
> -      uint32_t session_id_size,
> -      bool has_additional_usable_key,
> -      const KeyInformation* keys_info,
> -      uint32_t keys_info_count) = 0;
> -
> -    // Called by the CDM when there has been a change in the expiration time for
> -    // session |session_id|. This can happen as the result of an Update() call
> -    // or some other event. If this happens as a result of a call to Update(),
> -    // it must be called before resolving the Update() promise. |new_expiry_time|
> -    // represents the time after which the key(s) in the session will no longer
> -    // be usable for decryption. It can be 0 if no such time exists or if the
> -    // license explicitly never expires. Size parameter should not include null
> -    // termination.
> -    virtual void OnExpirationChange(const char* session_id,
> -      uint32_t session_id_size,
> -      Time new_expiry_time) = 0;
> -
> -    // Called by the CDM when session |session_id| is closed. Size
> -    // parameter should not include null termination.
> -    virtual void OnSessionClosed(const char* session_id,
> -      uint32_t session_id_size) = 0;
> -
> -    // The following are optional methods that may not be implemented on all
> -    // platforms.
> -
> -    // Sends a platform challenge for the given |service_id|. |challenge| is at
> -    // most 256 bits of data to be signed. Once the challenge has been completed,
> -    // the host will call ContentDecryptionModule::OnPlatformChallengeResponse()
> -    // with the signed challenge response and platform certificate. Size
> -    // parameters should not include null termination.
> -    virtual void SendPlatformChallenge(const char* service_id,
> -      uint32_t service_id_size,
> -      const char* challenge,
> -      uint32_t challenge_size) = 0;
> -
> -    // Attempts to enable output protection (e.g. HDCP) on the display link. The
> -    // |desired_protection_mask| is a bit mask of OutputProtectionMethods. No
> -    // status callback is issued, the CDM must call QueryOutputProtectionStatus()
> -    // periodically to ensure the desired protections are applied.
> -    virtual void EnableOutputProtection(uint32_t desired_protection_mask) = 0;
> -
> -    // Requests the current output protection status. Once the host has the status
> -    // it will call ContentDecryptionModule::OnQueryOutputProtectionStatus().
> -    virtual void QueryOutputProtectionStatus() = 0;
> -
> -    // Must be called by the CDM if it returned kDeferredInitialization during
> -    // InitializeAudioDecoder() or InitializeVideoDecoder().
> -    virtual void OnDeferredInitializationDone(StreamType stream_type,
> -      Status decoder_status) = 0;
> -
> -    // Creates a FileIO object from the host to do file IO operation. Returns NULL
> -    // if a FileIO object cannot be obtained. Once a valid FileIO object is
> -    // returned, |client| must be valid until FileIO::Close() is called. The
> -    // CDM can call this method multiple times to operate on different files.
> -    virtual FileIO* CreateFileIO(FileIOClient* client) = 0;
> -
> -    // Requests a specific version of the storage ID. A storage ID is a stable,
> -    // device specific ID used by the CDM to securely store persistent data. The
> -    // ID will be returned by the host via ContentDecryptionModule::OnStorageId().
> -    // If |version| is 0, the latest version will be returned. All |version|s
> -    // that are greater than or equal to 0x80000000 are reserved for the CDM and
> -    // should not be supported or returned by the host. The CDM must not expose
> -    // the ID outside the client device, even in encrypted form.
> -    virtual void RequestStorageId(uint32_t version) = 0;
> -
> -  protected:
> -    Host_10() {}
> -    virtual ~Host_10() {}
> -  };
> -
> -  class CDM_CLASS_API Host_11 {
> -  public:
> -    static const int kVersion = 11;
> -
> -    // Returns a Buffer* containing non-zero members upon success, or NULL on
> -    // failure. The caller owns the Buffer* after this call. The buffer is not
> -    // guaranteed to be zero initialized. The capacity of the allocated Buffer
> -    // is guaranteed to be not less than |capacity|.
> -    virtual Buffer* Allocate(uint32_t capacity) = 0;
> -
> -    // Requests the host to call ContentDecryptionModule::TimerFired() |delay_ms|
> -    // from now with |context|.
> -    virtual void SetTimer(int64_t delay_ms, void* context) = 0;
> -
> -    // Returns the current wall time.
> -    virtual Time GetCurrentWallTime() = 0;
> -
> -    // Called by the CDM with the result after the CDM instance was initialized.
> -    virtual void OnInitialized(bool success) = 0;
> -
> -    // Called by the CDM when a key status is available in response to
> -    // GetStatusForPolicy().
> -    virtual void OnResolveKeyStatusPromise(uint32_t promise_id,
> -      KeyStatus key_status) = 0;
> -
> -    // Called by the CDM when a session is created or loaded and the value for the
> -    // MediaKeySession's sessionId attribute is available (|session_id|).
> -    // This must be called before OnSessionMessage() or
> -    // OnSessionKeysChange() is called for the same session. |session_id_size|
> -    // should not include null termination.
> -    // When called in response to LoadSession(), the |session_id| must be the
> -    // same as the |session_id| passed in LoadSession(), or NULL if the
> -    // session could not be loaded.
> -    virtual void OnResolveNewSessionPromise(uint32_t promise_id,
> -      const char* session_id,
> -      uint32_t session_id_size) = 0;
> -
> -    // Called by the CDM when a session is updated or released.
> -    virtual void OnResolvePromise(uint32_t promise_id) = 0;
> -
> -    // Called by the CDM when an error occurs as a result of one of the
> -    // ContentDecryptionModule calls that accept a |promise_id|.
> -    // |exception| must be specified. |error_message| and |system_code|
> -    // are optional. |error_message_size| should not include null termination.
> -    virtual void OnRejectPromise(uint32_t promise_id,
> -      Exception exception,
> -      uint32_t system_code,
> -      const char* error_message,
> -      uint32_t error_message_size) = 0;
> -
> -    // Called by the CDM when it has a message for session |session_id|.
> -    // Size parameters should not include null termination.
> -    virtual void OnSessionMessage(const char* session_id,
> -      uint32_t session_id_size,
> -      MessageType message_type,
> -      const char* message,
> -      uint32_t message_size) = 0;
> -
> -    // Called by the CDM when there has been a change in keys or their status for
> -    // session |session_id|. |has_additional_usable_key| should be set if a
> -    // key is newly usable (e.g. new key available, previously expired key has
> -    // been renewed, etc.) and the browser should attempt to resume playback.
> -    // |keys_info| is the list of key IDs for this session along with their
> -    // current status. |keys_info_count| is the number of entries in |keys_info|.
> -    // Size parameter for |session_id| should not include null termination.
> -    virtual void OnSessionKeysChange(const char* session_id,
> -      uint32_t session_id_size,
> -      bool has_additional_usable_key,
> -      const KeyInformation* keys_info,
> -      uint32_t keys_info_count) = 0;
> -
> -    // Called by the CDM when there has been a change in the expiration time for
> -    // session |session_id|. This can happen as the result of an Update() call
> -    // or some other event. If this happens as a result of a call to Update(),
> -    // it must be called before resolving the Update() promise. |new_expiry_time|
> -    // represents the time after which the key(s) in the session will no longer
> -    // be usable for decryption. It can be 0 if no such time exists or if the
> -    // license explicitly never expires. Size parameter should not include null
> -    // termination.
> -    virtual void OnExpirationChange(const char* session_id,
> -      uint32_t session_id_size,
> -      Time new_expiry_time) = 0;
> -
> -    // Called by the CDM when session |session_id| is closed. Size
> -    // parameter should not include null termination.
> -    virtual void OnSessionClosed(const char* session_id,
> -      uint32_t session_id_size) = 0;
> -
> -    // The following are optional methods that may not be implemented on all
> -    // platforms.
> -
> -    // Sends a platform challenge for the given |service_id|. |challenge| is at
> -    // most 256 bits of data to be signed. Once the challenge has been completed,
> -    // the host will call ContentDecryptionModule::OnPlatformChallengeResponse()
> -    // with the signed challenge response and platform certificate. Size
> -    // parameters should not include null termination.
> -    virtual void SendPlatformChallenge(const char* service_id,
> -      uint32_t service_id_size,
> -      const char* challenge,
> -      uint32_t challenge_size) = 0;
> -
> -    // Attempts to enable output protection (e.g. HDCP) on the display link. The
> -    // |desired_protection_mask| is a bit mask of OutputProtectionMethods. No
> -    // status callback is issued, the CDM must call QueryOutputProtectionStatus()
> -    // periodically to ensure the desired protections are applied.
> -    virtual void EnableOutputProtection(uint32_t desired_protection_mask) = 0;
> -
> -    // Requests the current output protection status. Once the host has the status
> -    // it will call ContentDecryptionModule::OnQueryOutputProtectionStatus().
> -    virtual void QueryOutputProtectionStatus() = 0;
> -
> -    // Must be called by the CDM if it returned kDeferredInitialization during
> -    // InitializeAudioDecoder() or InitializeVideoDecoder().
> -    virtual void OnDeferredInitializationDone(StreamType stream_type,
> -      Status decoder_status) = 0;
> -
> -    // Creates a FileIO object from the host to do file IO operation. Returns NULL
> -    // if a FileIO object cannot be obtained. Once a valid FileIO object is
> -    // returned, |client| must be valid until FileIO::Close() is called. The
> -    // CDM can call this method multiple times to operate on different files.
> -    virtual FileIO* CreateFileIO(FileIOClient* client) = 0;
> -
> -    // Requests a CdmProxy that proxies part of CDM functionalities to a different
> -    // entity, e.g. a hardware CDM module. A CDM instance can have at most one
> -    // CdmProxy throughout its lifetime, which must be requested and initialized
> -    // during CDM instance initialization time, i.e. in or after CDM::Initialize()
> -    // and before OnInitialized() is called, to ensure proper connection of the
> -    // CdmProxy and the media player (e.g. hardware decoder). The CdmProxy is
> -    // owned by the host and is guaranteed to be valid throughout the CDM
> -    // instance's lifetime. The CDM must ensure that the |client| remain valid
> -    // before the CDM instance is destroyed. Returns null if CdmProxy is not
> -    // supported, called before CDM::Initialize(), RequestCdmProxy() is called
> -    // more than once, or called after the CDM instance has been initialized.
> -    virtual CdmProxy* RequestCdmProxy(CdmProxyClient* client) = 0;
> -
> -    // Requests a specific version of the storage ID. A storage ID is a stable,
> -    // device specific ID used by the CDM to securely store persistent data. The
> -    // ID will be returned by the host via ContentDecryptionModule::OnStorageId().
> -    // If |version| is 0, the latest version will be returned. All |version|s
> -    // that are greater than or equal to 0x80000000 are reserved for the CDM and
> -    // should not be supported or returned by the host. The CDM must not expose
> -    // the ID outside the client device, even in encrypted form.
> -    virtual void RequestStorageId(uint32_t version) = 0;
> -
> -  protected:
> -    Host_11() {}
> -    virtual ~Host_11() {}
> -  };
> +  // Called by the host after a call to Host::RequestStorageId(). If the
> +  // version of the storage ID requested is available, |storage_id| and
> +  // |storage_id_size| are set appropriately. |version| will be the same as
> +  // what was requested, unless 0 (latest) was requested, in which case
> +  // |version| will be the actual version number for the |storage_id| returned.
> +  // If the requested version is not available, null/zero will be provided as
> +  // |storage_id| and |storage_id_size|, respectively, and |version| should be
> +  // ignored.
> +  virtual void OnStorageId(uint32_t version,
> +                           const uint8_t* storage_id,
> +                           uint32_t storage_id_size) = 0;
> +
> +  // Destroys the object in the same context as it was created.
> +  virtual void Destroy() = 0;
> +
> + protected:
> +  ContentDecryptionModule_12() {}
> +  virtual ~ContentDecryptionModule_12() {}
> +};
> +
> +class CDM_CLASS_API Host_10 {
> + public:
> +  static const int kVersion = 10;
> +
> +  // Returns a Buffer* containing non-zero members upon success, or NULL on
> +  // failure. The caller owns the Buffer* after this call. The buffer is not
> +  // guaranteed to be zero initialized. The capacity of the allocated Buffer
> +  // is guaranteed to be not less than |capacity|.
> +  virtual Buffer* Allocate(uint32_t capacity) = 0;
> +
> +  // Requests the host to call ContentDecryptionModule::TimerFired() |delay_ms|
> +  // from now with |context|.
> +  virtual void SetTimer(int64_t delay_ms, void* context) = 0;
> +
> +  // Returns the current wall time.
> +  virtual Time GetCurrentWallTime() = 0;
> +
> +  // Called by the CDM with the result after the CDM instance was initialized.
> +  virtual void OnInitialized(bool success) = 0;
> +
> +  // Called by the CDM when a key status is available in response to
> +  // GetStatusForPolicy().
> +  virtual void OnResolveKeyStatusPromise(uint32_t promise_id,
> +                                         KeyStatus key_status) = 0;
> +
> +  // Called by the CDM when a session is created or loaded and the value for the
> +  // MediaKeySession's sessionId attribute is available (|session_id|).
> +  // This must be called before OnSessionMessage() or
> +  // OnSessionKeysChange() is called for the same session. |session_id_size|
> +  // should not include null termination.
> +  // When called in response to LoadSession(), the |session_id| must be the
> +  // same as the |session_id| passed in LoadSession(), or NULL if the
> +  // session could not be loaded.
> +  virtual void OnResolveNewSessionPromise(uint32_t promise_id,
> +                                          const char* session_id,
> +                                          uint32_t session_id_size) = 0;
> +
> +  // Called by the CDM when a session is updated or released.
> +  virtual void OnResolvePromise(uint32_t promise_id) = 0;
> +
> +  // Called by the CDM when an error occurs as a result of one of the
> +  // ContentDecryptionModule calls that accept a |promise_id|.
> +  // |exception| must be specified. |error_message| and |system_code|
> +  // are optional. |error_message_size| should not include null termination.
> +  virtual void OnRejectPromise(uint32_t promise_id,
> +                               Exception exception,
> +                               uint32_t system_code,
> +                               const char* error_message,
> +                               uint32_t error_message_size) = 0;
> +
> +  // Called by the CDM when it has a message for session |session_id|.
> +  // Size parameters should not include null termination.
> +  virtual void OnSessionMessage(const char* session_id,
> +                                uint32_t session_id_size,
> +                                MessageType message_type,
> +                                const char* message,
> +                                uint32_t message_size) = 0;
> +
> +  // Called by the CDM when there has been a change in keys or their status for
> +  // session |session_id|. |has_additional_usable_key| should be set if a
> +  // key is newly usable (e.g. new key available, previously expired key has
> +  // been renewed, etc.) and the browser should attempt to resume playback.
> +  // |keys_info| is the list of key IDs for this session along with their
> +  // current status. |keys_info_count| is the number of entries in |keys_info|.
> +  // Size parameter for |session_id| should not include null termination.
> +  virtual void OnSessionKeysChange(const char* session_id,
> +                                   uint32_t session_id_size,
> +                                   bool has_additional_usable_key,
> +                                   const KeyInformation* keys_info,
> +                                   uint32_t keys_info_count) = 0;
> +
> +  // Called by the CDM when there has been a change in the expiration time for
> +  // session |session_id|. This can happen as the result of an Update() call
> +  // or some other event. If this happens as a result of a call to Update(),
> +  // it must be called before resolving the Update() promise. |new_expiry_time|
> +  // represents the time after which the key(s) in the session will no longer
> +  // be usable for decryption. It can be 0 if no such time exists or if the
> +  // license explicitly never expires. Size parameter should not include null
> +  // termination.
> +  virtual void OnExpirationChange(const char* session_id,
> +                                  uint32_t session_id_size,
> +                                  Time new_expiry_time) = 0;
> +
> +  // Called by the CDM when session |session_id| is closed. Size
> +  // parameter should not include null termination.
> +  virtual void OnSessionClosed(const char* session_id,
> +                               uint32_t session_id_size) = 0;
> +
> +  // The following are optional methods that may not be implemented on all
> +  // platforms.
> +
> +  // Sends a platform challenge for the given |service_id|. |challenge| is at
> +  // most 256 bits of data to be signed. Once the challenge has been completed,
> +  // the host will call ContentDecryptionModule::OnPlatformChallengeResponse()
> +  // with the signed challenge response and platform certificate. Size
> +  // parameters should not include null termination.
> +  virtual void SendPlatformChallenge(const char* service_id,
> +                                     uint32_t service_id_size,
> +                                     const char* challenge,
> +                                     uint32_t challenge_size) = 0;
> +
> +  // Attempts to enable output protection (e.g. HDCP) on the display link. The
> +  // |desired_protection_mask| is a bit mask of OutputProtectionMethods. No
> +  // status callback is issued, the CDM must call QueryOutputProtectionStatus()
> +  // periodically to ensure the desired protections are applied.
> +  virtual void EnableOutputProtection(uint32_t desired_protection_mask) = 0;
> +
> +  // Requests the current output protection status. Once the host has the status
> +  // it will call ContentDecryptionModule::OnQueryOutputProtectionStatus().
> +  virtual void QueryOutputProtectionStatus() = 0;
> +
> +  // Must be called by the CDM if it returned kDeferredInitialization during
> +  // InitializeAudioDecoder() or InitializeVideoDecoder().
> +  virtual void OnDeferredInitializationDone(StreamType stream_type,
> +                                            Status decoder_status) = 0;
> +
> +  // Creates a FileIO object from the host to do file IO operation. Returns NULL
> +  // if a FileIO object cannot be obtained. Once a valid FileIO object is
> +  // returned, |client| must be valid until FileIO::Close() is called. The
> +  // CDM can call this method multiple times to operate on different files.
> +  virtual FileIO* CreateFileIO(FileIOClient* client) = 0;
> +
> +  // Requests a specific version of the storage ID. A storage ID is a stable,
> +  // device specific ID used by the CDM to securely store persistent data. The
> +  // ID will be returned by the host via ContentDecryptionModule::OnStorageId().
> +  // If |version| is 0, the latest version will be returned. All |version|s
> +  // that are greater than or equal to 0x80000000 are reserved for the CDM and
> +  // should not be supported or returned by the host. The CDM must not expose
> +  // the ID outside the client device, even in encrypted form.
> +  virtual void RequestStorageId(uint32_t version) = 0;
> +
> + protected:
> +  Host_10() {}
> +  virtual ~Host_10() {}
> +};
> +
> +class CDM_CLASS_API Host_11 {
> + public:
> +  static const int kVersion = 11;
> +
> +  // Returns a Buffer* containing non-zero members upon success, or NULL on
> +  // failure. The caller owns the Buffer* after this call. The buffer is not
> +  // guaranteed to be zero initialized. The capacity of the allocated Buffer
> +  // is guaranteed to be not less than |capacity|.
> +  virtual Buffer* Allocate(uint32_t capacity) = 0;
> +
> +  // Requests the host to call ContentDecryptionModule::TimerFired() |delay_ms|
> +  // from now with |context|.
> +  virtual void SetTimer(int64_t delay_ms, void* context) = 0;
> +
> +  // Returns the current wall time.
> +  virtual Time GetCurrentWallTime() = 0;
> +
> +  // Called by the CDM with the result after the CDM instance was initialized.
> +  virtual void OnInitialized(bool success) = 0;
> +
> +  // Called by the CDM when a key status is available in response to
> +  // GetStatusForPolicy().
> +  virtual void OnResolveKeyStatusPromise(uint32_t promise_id,
> +                                         KeyStatus key_status) = 0;
> +
> +  // Called by the CDM when a session is created or loaded and the value for the
> +  // MediaKeySession's sessionId attribute is available (|session_id|).
> +  // This must be called before OnSessionMessage() or
> +  // OnSessionKeysChange() is called for the same session. |session_id_size|
> +  // should not include null termination.
> +  // When called in response to LoadSession(), the |session_id| must be the
> +  // same as the |session_id| passed in LoadSession(), or NULL if the
> +  // session could not be loaded.
> +  virtual void OnResolveNewSessionPromise(uint32_t promise_id,
> +                                          const char* session_id,
> +                                          uint32_t session_id_size) = 0;
> +
> +  // Called by the CDM when a session is updated or released.
> +  virtual void OnResolvePromise(uint32_t promise_id) = 0;
> +
> +  // Called by the CDM when an error occurs as a result of one of the
> +  // ContentDecryptionModule calls that accept a |promise_id|.
> +  // |exception| must be specified. |error_message| and |system_code|
> +  // are optional. |error_message_size| should not include null termination.
> +  virtual void OnRejectPromise(uint32_t promise_id,
> +                               Exception exception,
> +                               uint32_t system_code,
> +                               const char* error_message,
> +                               uint32_t error_message_size) = 0;
> +
> +  // Called by the CDM when it has a message for session |session_id|.
> +  // Size parameters should not include null termination.
> +  virtual void OnSessionMessage(const char* session_id,
> +                                uint32_t session_id_size,
> +                                MessageType message_type,
> +                                const char* message,
> +                                uint32_t message_size) = 0;
> +
> +  // Called by the CDM when there has been a change in keys or their status for
> +  // session |session_id|. |has_additional_usable_key| should be set if a
> +  // key is newly usable (e.g. new key available, previously expired key has
> +  // been renewed, etc.) and the browser should attempt to resume playback.
> +  // |keys_info| is the list of key IDs for this session along with their
> +  // current status. |keys_info_count| is the number of entries in |keys_info|.
> +  // Size parameter for |session_id| should not include null termination.
> +  virtual void OnSessionKeysChange(const char* session_id,
> +                                   uint32_t session_id_size,
> +                                   bool has_additional_usable_key,
> +                                   const KeyInformation* keys_info,
> +                                   uint32_t keys_info_count) = 0;
> +
> +  // Called by the CDM when there has been a change in the expiration time for
> +  // session |session_id|. This can happen as the result of an Update() call
> +  // or some other event. If this happens as a result of a call to Update(),
> +  // it must be called before resolving the Update() promise. |new_expiry_time|
> +  // represents the time after which the key(s) in the session will no longer
> +  // be usable for decryption. It can be 0 if no such time exists or if the
> +  // license explicitly never expires. Size parameter should not include null
> +  // termination.
> +  virtual void OnExpirationChange(const char* session_id,
> +                                  uint32_t session_id_size,
> +                                  Time new_expiry_time) = 0;
> +
> +  // Called by the CDM when session |session_id| is closed. Size
> +  // parameter should not include null termination.
> +  virtual void OnSessionClosed(const char* session_id,
> +                               uint32_t session_id_size) = 0;
> +
> +  // The following are optional methods that may not be implemented on all
> +  // platforms.
> +
> +  // Sends a platform challenge for the given |service_id|. |challenge| is at
> +  // most 256 bits of data to be signed. Once the challenge has been completed,
> +  // the host will call ContentDecryptionModule::OnPlatformChallengeResponse()
> +  // with the signed challenge response and platform certificate. Size
> +  // parameters should not include null termination.
> +  virtual void SendPlatformChallenge(const char* service_id,
> +                                     uint32_t service_id_size,
> +                                     const char* challenge,
> +                                     uint32_t challenge_size) = 0;
> +
> +  // Attempts to enable output protection (e.g. HDCP) on the display link. The
> +  // |desired_protection_mask| is a bit mask of OutputProtectionMethods. No
> +  // status callback is issued, the CDM must call QueryOutputProtectionStatus()
> +  // periodically to ensure the desired protections are applied.
> +  virtual void EnableOutputProtection(uint32_t desired_protection_mask) = 0;
> +
> +  // Requests the current output protection status. Once the host has the status
> +  // it will call ContentDecryptionModule::OnQueryOutputProtectionStatus().
> +  virtual void QueryOutputProtectionStatus() = 0;
> +
> +  // Must be called by the CDM if it returned kDeferredInitialization during
> +  // InitializeAudioDecoder() or InitializeVideoDecoder().
> +  virtual void OnDeferredInitializationDone(StreamType stream_type,
> +                                            Status decoder_status) = 0;
> +
> +  // Creates a FileIO object from the host to do file IO operation. Returns NULL
> +  // if a FileIO object cannot be obtained. Once a valid FileIO object is
> +  // returned, |client| must be valid until FileIO::Close() is called. The
> +  // CDM can call this method multiple times to operate on different files.
> +  virtual FileIO* CreateFileIO(FileIOClient* client) = 0;
> +
> +  // Requests a specific version of the storage ID. A storage ID is a stable,
> +  // device specific ID used by the CDM to securely store persistent data. The
> +  // ID will be returned by the host via ContentDecryptionModule::OnStorageId().
> +  // If |version| is 0, the latest version will be returned. All |version|s
> +  // that are greater than or equal to 0x80000000 are reserved for the CDM and
> +  // should not be supported or returned by the host. The CDM must not expose
> +  // the ID outside the client device, even in encrypted form.
> +  virtual void RequestStorageId(uint32_t version) = 0;
> +
> +  // Reports the metric |metric_name| with value |value| to the host. Can be
> +  // called by the CDM at any time. May report the same metric multiple times
> +  // during the lifetime of the CDM.
> +  virtual void ReportMetrics(MetricName metric_name, uint64_t value) = 0;
> +
> + protected:
> +  Host_11() {}
> +  virtual ~Host_11() {}
> +};
> +
> +class CDM_CLASS_API Host_12 {
> + public:
> +  static const int kVersion = 12;
> +
> +  // Returns a Buffer* containing non-zero members upon success, or NULL on
> +  // failure. The caller owns the Buffer* after this call. The buffer is not
> +  // guaranteed to be zero initialized. The capacity of the allocated Buffer
> +  // is guaranteed to be not less than |capacity|.
> +  virtual Buffer* Allocate(uint32_t capacity) = 0;
> +
> +  // Requests the host to call ContentDecryptionModule::TimerFired() |delay_ms|
> +  // from now with |context|.
> +  virtual void SetTimer(int64_t delay_ms, void* context) = 0;
> +
> +  // Returns the current wall time.
> +  virtual Time GetCurrentWallTime() = 0;
> +
> +  // Called by the CDM with the result after the CDM instance was initialized.
> +  virtual void OnInitialized(bool success) = 0;
> +
> +  // Called by the CDM when a key status is available in response to
> +  // GetStatusForPolicy().
> +  virtual void OnResolveKeyStatusPromise(uint32_t promise_id,
> +                                         KeyStatus_2 key_status) = 0;
> +
> +  // Called by the CDM when a session is created or loaded and the value for the
> +  // MediaKeySession's sessionId attribute is available (|session_id|).
> +  // This must be called before OnSessionMessage() or
> +  // OnSessionKeysChange() is called for the same session. |session_id_size|
> +  // should not include null termination.
> +  // When called in response to LoadSession(), the |session_id| must be the
> +  // same as the |session_id| passed in LoadSession(), or NULL if the
> +  // session could not be loaded.
> +  virtual void OnResolveNewSessionPromise(uint32_t promise_id,
> +                                          const char* session_id,
> +                                          uint32_t session_id_size) = 0;
> +
> +  // Called by the CDM when a session is updated or released.
> +  virtual void OnResolvePromise(uint32_t promise_id) = 0;
> +
> +  // Called by the CDM when an error occurs as a result of one of the
> +  // ContentDecryptionModule calls that accept a |promise_id|.
> +  // |exception| must be specified. |error_message| and |system_code|
> +  // are optional. |error_message_size| should not include null termination.
> +  virtual void OnRejectPromise(uint32_t promise_id,
> +                               Exception exception,
> +                               uint32_t system_code,
> +                               const char* error_message,
> +                               uint32_t error_message_size) = 0;
> +
> +  // Called by the CDM when it has a message for session |session_id|.
> +  // Size parameters should not include null termination.
> +  virtual void OnSessionMessage(const char* session_id,
> +                                uint32_t session_id_size,
> +                                MessageType message_type,
> +                                const char* message,
> +                                uint32_t message_size) = 0;
> +
> +  // Called by the CDM when there has been a change in keys or their status for
> +  // session |session_id|. |has_additional_usable_key| should be set if a
> +  // key is newly usable (e.g. new key available, previously expired key has
> +  // been renewed, etc.) and the browser should attempt to resume playback.
> +  // |keys_info| is the list of key IDs for this session along with their
> +  // current status. |keys_info_count| is the number of entries in |keys_info|.
> +  // Size parameter for |session_id| should not include null termination.
> +  virtual void OnSessionKeysChange(const char* session_id,
> +                                   uint32_t session_id_size,
> +                                   bool has_additional_usable_key,
> +                                   const KeyInformation_2* keys_info,
> +                                   uint32_t keys_info_count) = 0;
> +
> +  // Called by the CDM when there has been a change in the expiration time for
> +  // session |session_id|. This can happen as the result of an Update() call
> +  // or some other event. If this happens as a result of a call to Update(),
> +  // it must be called before resolving the Update() promise. |new_expiry_time|
> +  // represents the time after which the key(s) in the session will no longer
> +  // be usable for decryption. It can be 0 if no such time exists or if the
> +  // license explicitly never expires. Size parameter should not include null
> +  // termination.
> +  virtual void OnExpirationChange(const char* session_id,
> +                                  uint32_t session_id_size,
> +                                  Time new_expiry_time) = 0;
> +
> +  // Called by the CDM when session |session_id| is closed. Size
> +  // parameter should not include null termination.
> +  virtual void OnSessionClosed(const char* session_id,
> +                               uint32_t session_id_size) = 0;
> +
> +  // The following are optional methods that may not be implemented on all
> +  // platforms.
> +
> +  // Sends a platform challenge for the given |service_id|. |challenge| is at
> +  // most 256 bits of data to be signed. Once the challenge has been completed,
> +  // the host will call ContentDecryptionModule::OnPlatformChallengeResponse()
> +  // with the signed challenge response and platform certificate. Size
> +  // parameters should not include null termination.
> +  virtual void SendPlatformChallenge(const char* service_id,
> +                                     uint32_t service_id_size,
> +                                     const char* challenge,
> +                                     uint32_t challenge_size) = 0;
> +
> +  // Attempts to enable output protection (e.g. HDCP) on the display link. The
> +  // |desired_protection_mask| is a bit mask of OutputProtectionMethods. No
> +  // status callback is issued, the CDM must call QueryOutputProtectionStatus()
> +  // periodically to ensure the desired protections are applied.
> +  virtual void EnableOutputProtection(uint32_t desired_protection_mask) = 0;
> +
> +  // Requests the current output protection status. Once the host has the status
> +  // it will call ContentDecryptionModule::OnQueryOutputProtectionStatus().
> +  virtual void QueryOutputProtectionStatus() = 0;
> +
> +  // Must be called by the CDM if it returned kDeferredInitialization during
> +  // InitializeAudioDecoder() or InitializeVideoDecoder().
> +  virtual void OnDeferredInitializationDone(StreamType stream_type,
> +                                            Status decoder_status) = 0;
> +
> +  // Creates a FileIO object from the host to do file IO operation. Returns NULL
> +  // if a FileIO object cannot be obtained. Once a valid FileIO object is
> +  // returned, |client| must be valid until FileIO::Close() is called. The
> +  // CDM can call this method multiple times to operate on different files.
> +  virtual FileIO* CreateFileIO(FileIOClient* client) = 0;
> +
> +  // Requests a specific version of the storage ID. A storage ID is a stable,
> +  // device specific ID used by the CDM to securely store persistent data. The
> +  // ID will be returned by the host via ContentDecryptionModule::OnStorageId().
> +  // If |version| is 0, the latest version will be returned. All |version|s
> +  // that are greater than or equal to 0x80000000 are reserved for the CDM and
> +  // should not be supported or returned by the host. The CDM must not expose
> +  // the ID outside the client device, even in encrypted form.
> +  virtual void RequestStorageId(uint32_t version) = 0;
> +
> +  // Reports the metric |metric_name| with value |value| to the host. Can be
> +  // called by the CDM at any time. May report the same metric multiple times
> +  // during the lifetime of the CDM.
> +  virtual void ReportMetrics(MetricName metric_name, uint64_t value) = 0;
> +
> + protected:
> +  Host_12() {}
> +  virtual ~Host_12() {}
> +};
>  
>  }  // namespace cdm
>  
> -#endif  // CDM_CONTENT_DECRYPTION_MODULE_H_
> \ No newline at end of file
> +#endif  // CDM_CONTENT_DECRYPTION_MODULE_H_
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/lib/cdm/cdm/media/cdm/api/content_decryption_module_proxy.h kodi-inputstream-adaptive-21.5.15+ds/lib/cdm/cdm/media/cdm/api/content_decryption_module_proxy.h
> --- kodi-inputstream-adaptive-21.5.9+ds/lib/cdm/cdm/media/cdm/api/content_decryption_module_proxy.h	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/lib/cdm/cdm/media/cdm/api/content_decryption_module_proxy.h	1970-01-01 00:00:00.000000000 +0000
> @@ -1,101 +0,0 @@
> -/*
> - *  Copyright (C) 2017 The Chromium Authors. All rights reserved.
> - *  This file is part of Kodi - https://kodi.tv
> - *
> - *  SPDX-License-Identifier: BSD-3-Clause
> - *  See LICENSES/README.md for more information.
> - */
> -
> -#ifndef CDM_CONTENT_DECRYPTION_MODULE_PROXY_H_
> -#define CDM_CONTENT_DECRYPTION_MODULE_PROXY_H_
> -#include "content_decryption_module_export.h"
> -#if defined(_MSC_VER)
> -typedef unsigned char uint8_t;
> -typedef unsigned int uint32_t;
> -typedef unsigned __int64 uint64_t;
> -#else
> -#include <stdint.h>
> -#endif
> -namespace cdm {
> -class CDM_CLASS_API CdmProxyClient;
> -// A proxy class for the CDM.
> -// In general, the interpretation of the CdmProxy and CdmProxyClient method
> -// parameters are protocol dependent. For enum parameters, values outside the
> -// enum range may not work.
> -class CDM_CLASS_API CdmProxy {
> - public:
> -  enum Function : uint32_t {
> -    // For Intel Negotiate Crypto SessionKey Exchange (CSME) path to call
> -    // ID3D11VideoContext::NegotiateCryptoSessionKeyExchange.
> -    kIntelNegotiateCryptoSessionKeyExchange = 1,
> -    // There will be more values in the future e.g. for D3D11 RSA method.
> -  };
> -  // Initializes the proxy. The results will be returned in
> -  // CdmProxyClient::OnInitialized().
> -  virtual void Initialize() = 0;
> -  // Processes and updates the state of the proxy.
> -  // |output_data_size| is required by some protocol to set up the output data.
> -  // The operation may fail if the |output_data_size| is wrong. The results will
> -  // be returned in CdmProxyClient::OnProcessed().
> -  virtual void Process(Function function,
> -                       uint32_t crypto_session_id,
> -                       const uint8_t* input_data,
> -                       uint32_t input_data_size,
> -                       uint32_t output_data_size) = 0;
> -  // Creates a crypto session for handling media.
> -  // If extra data has to be passed to further setup the media crypto session,
> -  // pass the data as |input_data|. The results will be returned in
> -  // CdmProxyClient::OnMediaCryptoSessionCreated().
> -  virtual void CreateMediaCryptoSession(const uint8_t* input_data,
> -                                        uint32_t input_data_size) = 0;
> -  // Sets a key for the session identified by |crypto_session_id|.
> -  virtual void SetKey(uint32_t crypto_session_id,
> -                      const uint8_t* key_id,
> -                      uint32_t key_id_size,
> -                      const uint8_t* key_blob,
> -                      uint32_t key_blob_size) = 0;
> -  // Removes a key for the session identified by |crypto_session_id|.
> -  virtual void RemoveKey(uint32_t crypto_session_id,
> -                         const uint8_t* key_id,
> -                         uint32_t key_id_size) = 0;
> - protected:
> -  CdmProxy() {}
> -  virtual ~CdmProxy() {}
> -};
> -// Responses to CdmProxy calls. All responses will be called asynchronously.
> -class CDM_CLASS_API CdmProxyClient {
> - public:
> -  enum Status : uint32_t {
> -    kOk,
> -    kFail,
> -  };
> -  enum Protocol : uint32_t {
> -    kNone = 0,  // No protocol supported. Can be used in failure cases.
> -    kIntelConvergedSecurityAndManageabilityEngine,  // Method using Intel CSME.
> -    // There will be more values in the future e.g. kD3D11RsaHardware,
> -    // kD3D11RsaSoftware to use the D3D11 RSA method.
> -  };
> -  // Callback for Initialize(). If the proxy created a crypto session, then the
> -  // ID for the crypto session is |crypto_session_id|.
> -  virtual void OnInitialized(Status status,
> -                             Protocol protocol,
> -                             uint32_t crypto_session_id) = 0;
> -  // Callback for Process(). |output_data| is the output of processing.
> -  virtual void OnProcessed(Status status,
> -                           const uint8_t* output_data,
> -                           uint32_t output_data_size) = 0;
> -  // Callback for CreateMediaCryptoSession(). On success:
> -  // - |crypto_session_id| is the ID for the created crypto session.
> -  // - |output_data| is extra value, if any.
> -  // Otherwise, |crypto_session_id| and |output_data| should be ignored.
> -  virtual void OnMediaCryptoSessionCreated(Status status,
> -                                           uint32_t crypto_session_id,
> -                                           uint64_t output_data) = 0;
> -  // Called when there is a hardware reset and all the hardware context is lost.
> -  virtual void NotifyHardwareReset() = 0;
> - protected:
> -  CdmProxyClient() {}
> -  virtual ~CdmProxyClient() {}
> -};
> -}  // namespace cdm
> -#endif  // CDM_CONTENT_DECRYPTION_MODULE_PROXY_H_
> \ No newline at end of file
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/lib/cdm/cdm/media/cdm/cdm_adapter.cc kodi-inputstream-adaptive-21.5.15+ds/lib/cdm/cdm/media/cdm/cdm_adapter.cc
> --- kodi-inputstream-adaptive-21.5.9+ds/lib/cdm/cdm/media/cdm/cdm_adapter.cc	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/lib/cdm/cdm/media/cdm/cdm_adapter.cc	2025-07-06 07:54:23.000000000 +0000
> @@ -8,11 +8,13 @@
>  
>  #include "cdm_adapter.h"
>  
> -//! @todo: provide an appropriate interface for log output
> -#include "../../src/utils/log.h"
> +#include "../../debug.h"
> +#include "../base/limits.h"
>  
>  #include <chrono>
> +#include <cstring>
>  #include <thread>
> +
>  #include <sys/stat.h>
>  
>  #ifdef _WIN32
> @@ -21,8 +23,6 @@
>  
>  #define DCHECK(condition) assert(condition)
>  
> -#include "../base/limits.h"
> -
>  #ifdef __APPLE__
>  #include <sys/time.h>
>  //clock_gettime is not implemented on OSX
> @@ -39,6 +39,8 @@
>  #endif
>  #endif
>  
> +using namespace CDM_DBG;
> +
>  namespace media {
>  
>  uint64_t gtc()
> @@ -68,12 +70,12 @@
>  
>    switch (host_interface_version)
>    {
> -    case cdm::Host_9::kVersion:
> -      return static_cast<cdm::Host_9*>(adapter);
>      case cdm::Host_10::kVersion:
>        return static_cast<cdm::Host_10*>(adapter);
>      case cdm::Host_11::kVersion:
>        return static_cast<cdm::Host_11*>(adapter);
> +    case cdm::Host_12::kVersion:
> +      return static_cast<cdm::Host_12*>(adapter);
>      default:
>        return nullptr;
>    }
> @@ -147,22 +149,6 @@
>  
>  }  // namespace
>  
> -cdm::AudioDecoderConfig_1 ToAudioDecoderConfig_1(
> -  const cdm::AudioDecoderConfig_2& config) {
> -  return{ config.codec,
> -    config.channel_count,
> -    config.bits_per_channel,
> -    config.samples_per_second,
> -    config.extra_data,
> -    config.extra_data_size };
> -}
> -
> -cdm::VideoDecoderConfig_1 ToVideoDecoderConfig_1(
> -  const cdm::VideoDecoderConfig_3& config) {
> -  return{ config.codec,      config.profile,    config.format,
> -    config.coded_size, config.extra_data, config.extra_data_size };
> -}
> -
>  cdm::VideoDecoderConfig_2 ToVideoDecoderConfig_2(
>    const cdm::VideoDecoderConfig_3& config) {
>    return{ config.codec,
> @@ -174,13 +160,6 @@
>      config.encryption_scheme };
>  }
>  
> -cdm::InputBuffer_1 ToInputBuffer_1(const cdm::InputBuffer_2& buffer) {
> -  return{ buffer.data,       buffer.data_size,
> -    buffer.key_id,     buffer.key_id_size,
> -    buffer.iv,         buffer.iv_size,
> -    buffer.subsamples, buffer.num_subsamples,
> -    buffer.timestamp };
> -}
>  
>  /*******************************         CdmAdapter        ****************************************/
>  
> @@ -198,7 +177,6 @@
>  , key_system_(key_system)
>  , cdm_config_(cdm_config)
>  , active_buffer_(0)
> -, cdm9_(0), cdm10_(0), cdm11_(0)
>  {
>    //DCHECK(!key_system_.empty());
>    Initialize();
> @@ -206,12 +184,12 @@
>  
>  CdmAdapter::~CdmAdapter()
>  {
> -  if (cdm9_)
> -    cdm9_->Destroy(), cdm9_ = nullptr;
> -  else if (cdm10_)
> -    cdm10_->Destroy(), cdm10_ = nullptr;
> +  if (cdm12_)
> +    cdm12_->Destroy(), cdm12_ = nullptr;
>    else if (cdm11_)
>      cdm11_->Destroy(), cdm11_ = nullptr;
> +  else if (cdm10_)
> +    cdm10_->Destroy(), cdm10_ = nullptr;
>    else
>      return;
>  
> @@ -223,14 +201,14 @@
>  void CdmAdapter::Initialize()
>  {
>    m_isClosingSession = false;
> -  if (cdm9_ || cdm10_ || cdm11_)
> +  if (cdm12_ || cdm11_ || cdm10_)
>    {
> -    if (cdm9_)
> -      cdm9_->Destroy(), cdm9_ = nullptr;
> -    else if (cdm10_)
> -      cdm10_->Destroy(), cdm10_ = nullptr;
> +    if (cdm12_)
> +      cdm12_->Destroy(), cdm12_ = nullptr;
>      else if (cdm11_)
>        cdm11_->Destroy(), cdm11_ = nullptr;
> +    else if (cdm10_)
> +      cdm10_->Destroy(), cdm10_ = nullptr;
>      base::UnloadNativeLibrary(library_);
>      library_ = 0;
>    }
> @@ -240,8 +218,7 @@
>  
>    if (!library_)
>    {
> -    LOG::LogF(LOGERROR, "%s: Failed to load library: %s", __FUNCTION__,
> -             error.ToString().c_str());
> +    LogF(LogLevel::ERROR, "Failed to load library: %s", error.ToString().c_str());
>      return;
>    }
>  
> @@ -258,7 +235,7 @@
>    }
>  
>    std::string version{get_cdm_verion_func()};
> -  LOG::LogF(LOGDEBUG, "CDM version: %s", version.c_str());
> +  Log(LogLevel::DEBUG, "CDM version: %s", version.c_str());
>  
>  #if defined(OS_WIN)
>    // Load DXVA before sandbox lockdown to give CDM access to Output Protection
> @@ -268,27 +245,32 @@
>  
>    init_cdm_func();
>  
> -  cdm11_ = static_cast<cdm::ContentDecryptionModule_11*>(create_cdm_func(11, key_system_.data(), key_system_.size(), GetCdmHost, this));
> +  cdm12_ = static_cast<cdm::ContentDecryptionModule_12*>(create_cdm_func(
> +      12, key_system_.data(), static_cast<uint32_t>(key_system_.size()), GetCdmHost, this));
>  
> -  if (!cdm11_)
> +  if (!cdm12_)
>    {
> -    cdm10_ = static_cast<cdm::ContentDecryptionModule_10*>(create_cdm_func(10, key_system_.data(), key_system_.size(), GetCdmHost, this));
> +    cdm11_ = static_cast<cdm::ContentDecryptionModule_11*>(create_cdm_func(
> +        11, key_system_.data(), static_cast<uint32_t>(key_system_.size()), GetCdmHost, this));
>  
> -    if (!cdm10_)
> -      cdm9_ = reinterpret_cast<cdm::ContentDecryptionModule_9*>(create_cdm_func(9, key_system_.data(), key_system_.size(), GetCdmHost, this));
> +    if (!cdm11_)
> +    {
> +      cdm10_ = static_cast<cdm::ContentDecryptionModule_10*>(create_cdm_func(
> +          10, key_system_.data(), static_cast<uint32_t>(key_system_.size()), GetCdmHost, this));
> +    }
>    }
>  
> -  if (cdm9_ || cdm10_ || cdm11_)
> +  if (cdm12_ || cdm11_ || cdm10_)
>    {
> -    if (cdm9_)
> -      cdm9_->Initialize(cdm_config_.allow_distinctive_identifier,
> -        cdm_config_.allow_persistent_state);
> -    else if(cdm10_)
> -      cdm10_->Initialize(cdm_config_.allow_distinctive_identifier,
> +    if(cdm12_)
> +      cdm12_->Initialize(cdm_config_.allow_distinctive_identifier,
>          cdm_config_.allow_persistent_state, false);
>      else if (cdm11_)
>        cdm11_->Initialize(cdm_config_.allow_distinctive_identifier,
>          cdm_config_.allow_persistent_state, false);
> +    else if (cdm10_)
> +      cdm10_->Initialize(cdm_config_.allow_distinctive_identifier,
> +        cdm_config_.allow_persistent_state, false);
>    }
>    else
>    {
> @@ -318,15 +300,15 @@
>      server_certificate_data_size > limits::kMaxCertificateLength) {
>    return;
>    }
> -  if (cdm9_)
> -    cdm9_->SetServerCertificate(promise_id, server_certificate_data,
> -      server_certificate_data_size);
> -  else if (cdm10_)
> -    cdm10_->SetServerCertificate(promise_id, server_certificate_data,
> +  
> +  if (cdm12_)
> +    cdm12_->SetServerCertificate(promise_id, server_certificate_data,
>        server_certificate_data_size);
>    else if (cdm11_)
>      cdm11_->SetServerCertificate(promise_id, server_certificate_data,
>        server_certificate_data_size);
> +  else if (cdm10_)
> +    cdm10_->SetServerCertificate(promise_id, server_certificate_data, server_certificate_data_size);
>  }
>  
>  void CdmAdapter::CreateSessionAndGenerateRequest(uint32_t promise_id,
> @@ -335,21 +317,15 @@
>    const uint8_t* init_data,
>    uint32_t init_data_size)
>  {
> -  if (cdm9_)
> -    cdm9_->CreateSessionAndGenerateRequest(
> -      promise_id, session_type,
> -      init_data_type, init_data,
> -      init_data_size);
> -  else  if (cdm10_)
> -    cdm10_->CreateSessionAndGenerateRequest(
> -      promise_id, session_type,
> -      init_data_type, init_data,
> -      init_data_size);
> -  else  if (cdm11_)
> -    cdm11_->CreateSessionAndGenerateRequest(
> -      promise_id, session_type,
> -      init_data_type, init_data,
> -      init_data_size);
> +  if (cdm12_)
> +    cdm12_->CreateSessionAndGenerateRequest(promise_id, session_type, init_data_type, init_data,
> +                                            init_data_size);
> +  else if (cdm11_)
> +    cdm11_->CreateSessionAndGenerateRequest(promise_id, session_type, init_data_type, init_data,
> +                                            init_data_size);
> +  else if (cdm10_)
> +    cdm10_->CreateSessionAndGenerateRequest(promise_id, session_type, init_data_type, init_data,
> +                                            init_data_size);
>  }
>  
>  void CdmAdapter::LoadSession(uint32_t promise_id,
> @@ -357,15 +333,12 @@
>    const char* session_id,
>    uint32_t session_id_size)
>  {
> -  if (cdm9_)
> -    cdm9_->LoadSession(promise_id, session_type,
> -      session_id, session_id_size);
> -  else if (cdm10_)
> -    cdm10_->LoadSession(promise_id, session_type,
> -      session_id, session_id_size);
> +  if (cdm12_)
> +    cdm12_->LoadSession(promise_id, session_type, session_id, session_id_size);
>    else if (cdm11_)
> -    cdm11_->LoadSession(promise_id, session_type,
> -      session_id, session_id_size);
> +    cdm11_->LoadSession(promise_id, session_type, session_id, session_id_size);
> +  else if (cdm10_)
> +    cdm10_->LoadSession(promise_id, session_type, session_id, session_id_size);
>  }
>  
>  void CdmAdapter::UpdateSession(uint32_t promise_id,
> @@ -374,15 +347,12 @@
>    const uint8_t* response,
>    uint32_t response_size)
>  {
> -  if (cdm9_)
> -    cdm9_->UpdateSession(promise_id, session_id, session_id_size,
> -            response, response_size);
> -  else if(cdm10_)
> -    cdm10_->UpdateSession(promise_id, session_id, session_id_size,
> -            response, response_size);
> +  if (cdm12_)
> +    cdm12_->UpdateSession(promise_id, session_id, session_id_size, response, response_size);
>    else if (cdm11_)
> -    cdm11_->UpdateSession(promise_id, session_id, session_id_size,
> -      response, response_size);
> +    cdm11_->UpdateSession(promise_id, session_id, session_id_size, response, response_size);
> +  else if (cdm10_)
> +    cdm10_->UpdateSession(promise_id, session_id, session_id_size, response, response_size);
>  }
>  
>  void CdmAdapter::CloseSession(uint32_t promise_id,
> @@ -394,12 +364,12 @@
>      m_isClosingSession = true;
>    }
>    m_sessionClosingCond.notify_all();
> -  if (cdm9_)
> -    cdm9_->CloseSession(promise_id, session_id, session_id_size);
> -  else if (cdm10_)
> -    cdm10_->CloseSession(promise_id, session_id, session_id_size);
> +  if (cdm12_)
> +    cdm12_->CloseSession(promise_id, session_id, session_id_size);
>    else if (cdm11_)
>      cdm11_->CloseSession(promise_id, session_id, session_id_size);
> +  else if (cdm10_)
> +    cdm10_->CloseSession(promise_id, session_id, session_id_size);
>    // remove any shared_ptr references left
>    m_asyncTimerTasks.clear();
>  }
> @@ -408,22 +378,22 @@
>    const char* session_id,
>    uint32_t session_id_size)
>  {
> -  if (cdm9_)
> -    cdm9_->RemoveSession(promise_id, session_id, session_id_size);
> -  else if (cdm10_)
> -    cdm10_->RemoveSession(promise_id, session_id, session_id_size);
> +  if (cdm12_)
> +    cdm12_->RemoveSession(promise_id, session_id, session_id_size);
>    else if (cdm11_)
>      cdm11_->RemoveSession(promise_id, session_id, session_id_size);
> +  else if (cdm10_)
> +    cdm10_->RemoveSession(promise_id, session_id, session_id_size);
>  }
>  
>  void CdmAdapter::TimerExpired(void* context)
>  {
> -  if (cdm9_)
> -    cdm9_->TimerExpired(context);
> -  else if (cdm10_)
> -    cdm10_->TimerExpired(context);
> +  if (cdm12_)
> +    cdm12_->TimerExpired(context);
>    else if (cdm11_)
>      cdm11_->TimerExpired(context);
> +  else if (cdm10_)
> +    cdm10_->TimerExpired(context);
>  }
>  
>  cdm::Status CdmAdapter::Decrypt(const cdm::InputBuffer_2& encrypted_buffer,
> @@ -438,15 +408,12 @@
>    active_buffer_ = decrypted_buffer->DecryptedBuffer();
>    cdm::Status ret;
>  
> -  if (cdm9_)
> -    ret = cdm9_->Decrypt(ToInputBuffer_1(encrypted_buffer), decrypted_buffer);
> +  if (cdm12_)
> +    ret = cdm12_->Decrypt(encrypted_buffer, decrypted_buffer);
> +  else if (cdm11_)
> +    ret = cdm11_->Decrypt(encrypted_buffer, decrypted_buffer);
>    else if (cdm10_)
>      ret = cdm10_->Decrypt(encrypted_buffer, decrypted_buffer);
> -  else if (cdm11_)
> -  {
> -    cdm::InputBuffer_2 tmp(encrypted_buffer);
> -    ret = cdm11_->Decrypt(tmp, decrypted_buffer);
> -  }
>  
>    active_buffer_ = 0;
>    return ret;
> @@ -455,45 +422,47 @@
>  cdm::Status CdmAdapter::InitializeAudioDecoder(
>    const cdm::AudioDecoderConfig_2& audio_decoder_config)
>  {
> -  if (cdm9_)
> -    return cdm9_->InitializeAudioDecoder(ToAudioDecoderConfig_1(audio_decoder_config));
> -  else if (cdm10_)
> -    return cdm10_->InitializeAudioDecoder(audio_decoder_config);
> +  if (cdm12_)
> +    return cdm12_->InitializeAudioDecoder(audio_decoder_config);
>    else if (cdm11_)
>      return cdm11_->InitializeAudioDecoder(audio_decoder_config);
> +  else if (cdm10_)
> +    return cdm10_->InitializeAudioDecoder(audio_decoder_config);
> +
>    return cdm::kDeferredInitialization;
>  }
>  
>  cdm::Status CdmAdapter::InitializeVideoDecoder(
>    const cdm::VideoDecoderConfig_3& video_decoder_config)
>  {
> -  if (cdm9_)
> -    return cdm9_->InitializeVideoDecoder(ToVideoDecoderConfig_1(video_decoder_config));
> +  if (cdm12_)
> +    return cdm12_->InitializeVideoDecoder(video_decoder_config);
> +  else if (cdm11_)
> +    return cdm11_->InitializeVideoDecoder(ToVideoDecoderConfig_2(video_decoder_config));
>    else if (cdm10_)
>      return cdm10_->InitializeVideoDecoder(ToVideoDecoderConfig_2(video_decoder_config));
> -  else if (cdm11_)
> -    return cdm11_->InitializeVideoDecoder(video_decoder_config);
> +
>    return cdm::kDeferredInitialization;
>  }
>  
>  void CdmAdapter::DeinitializeDecoder(cdm::StreamType decoder_type)
>  {
> -  if (cdm9_)
> -    cdm9_->DeinitializeDecoder(decoder_type);
> -  else if (cdm10_)
> -    cdm10_->DeinitializeDecoder(decoder_type);
> +  if (cdm12_)
> +    cdm12_->DeinitializeDecoder(decoder_type);
>    else if (cdm11_)
>      cdm11_->DeinitializeDecoder(decoder_type);
> +  else if (cdm10_)
> +    cdm10_->DeinitializeDecoder(decoder_type);
>  }
>  
>  void CdmAdapter::ResetDecoder(cdm::StreamType decoder_type)
>  {
> -  if (cdm9_)
> -    cdm9_->ResetDecoder(decoder_type);
> -  else if (cdm10_)
> -    cdm10_->ResetDecoder(decoder_type);
> +  if (cdm12_)
> +    cdm12_->ResetDecoder(decoder_type);
>    else if (cdm11_)
>      cdm11_->ResetDecoder(decoder_type);
> +  else if (cdm10_)
> +    cdm10_->ResetDecoder(decoder_type);
>  }
>  
>  cdm::Status CdmAdapter::DecryptAndDecodeFrame(const cdm::InputBuffer_2& encrypted_buffer,
> @@ -502,12 +471,12 @@
>    std::lock_guard<std::mutex> lock(decrypt_mutex_);
>    cdm::Status ret(cdm::kDeferredInitialization);
>  
> -  if (cdm9_)
> -    ret = cdm9_->DecryptAndDecodeFrame(ToInputBuffer_1(encrypted_buffer), video_frame);
> -  else if (cdm10_)
> -    ret = cdm10_->DecryptAndDecodeFrame(encrypted_buffer, video_frame);
> +  if (cdm12_)
> +    ret = cdm12_->DecryptAndDecodeFrame(encrypted_buffer, video_frame);
>    else if (cdm11_)
>      ret = cdm11_->DecryptAndDecodeFrame(encrypted_buffer, video_frame);
> +  else if (cdm10_)
> +    ret = cdm10_->DecryptAndDecodeFrame(encrypted_buffer, video_frame);
>  
>    active_buffer_ = 0;
>    return ret;
> @@ -517,39 +486,36 @@
>    cdm::AudioFrames* audio_frames)
>  {
>    std::lock_guard<std::mutex> lock(decrypt_mutex_);
> -  if (cdm9_)
> -    return cdm9_->DecryptAndDecodeSamples(ToInputBuffer_1(encrypted_buffer), audio_frames);
> -  else if (cdm10_)
> -    return cdm10_->DecryptAndDecodeSamples(encrypted_buffer, audio_frames);
> +  if (cdm12_)
> +    return cdm12_->DecryptAndDecodeSamples(encrypted_buffer, audio_frames);
>    else if (cdm11_)
>      return cdm11_->DecryptAndDecodeSamples(encrypted_buffer, audio_frames);
> +  else if (cdm10_)
> +    return cdm10_->DecryptAndDecodeSamples(encrypted_buffer, audio_frames);
>    return cdm::kDeferredInitialization;
>  }
>  
>  void CdmAdapter::OnPlatformChallengeResponse(
>    const cdm::PlatformChallengeResponse& response)
>  {
> -  if (cdm9_)
> -    cdm9_->OnPlatformChallengeResponse(response);
> -  else if (cdm10_)
> -    cdm10_->OnPlatformChallengeResponse(response);
> +  if (cdm12_)
> +    cdm12_->OnPlatformChallengeResponse(response);
>    else if (cdm11_)
>      cdm11_->OnPlatformChallengeResponse(response);
> +  else if (cdm10_)
> +    cdm10_->OnPlatformChallengeResponse(response);
>  }
>  
>  void CdmAdapter::OnQueryOutputProtectionStatus(cdm::QueryResult result,
>    uint32_t link_mask,
>    uint32_t output_protection_mask)
>  {
> -  if (cdm9_)
> -    cdm9_->OnQueryOutputProtectionStatus(result, link_mask,
> -      output_protection_mask);
> -  else if (cdm10_)
> -    cdm10_->OnQueryOutputProtectionStatus(result, link_mask,
> -      output_protection_mask);
> +  if (cdm12_)
> +    cdm12_->OnQueryOutputProtectionStatus(result, link_mask, output_protection_mask);
>    else if (cdm11_)
> -    cdm11_->OnQueryOutputProtectionStatus(result, link_mask,
> -      output_protection_mask);
> +    cdm11_->OnQueryOutputProtectionStatus(result, link_mask, output_protection_mask);
> +  else if (cdm10_)
> +    cdm10_->OnQueryOutputProtectionStatus(result, link_mask, output_protection_mask);
>  }
>  
>  /******************************** HOST *****************************************/
> @@ -624,14 +590,35 @@
>      char* bufferPtr{buffer};
>      for (uint32_t j{0}; j < keys_info[i].key_id_size; ++j)
>        bufferPtr += std::snprintf(bufferPtr, 3, "%02X", (int)keys_info[i].key_id[j]);
> -    LOG::Log(LOGDEBUG, "%s: Sessionkey %s status: %d syscode: %u", __func__, buffer,
> -                 keys_info[i].status, keys_info[i].system_code);
> +    Log(LogLevel::DEBUG, "OnSessionKeysChange: KID %s, Status: %d, System code: %u", buffer,
> +        keys_info[i].status, keys_info[i].system_code);
>  
>      SendClientMessage(session_id, session_id_size, CdmAdapterClient::kSessionKeysChange,
>        keys_info[i].key_id, keys_info[i].key_id_size, keys_info[i].status);
>    }
>  }
>  
> +void CdmAdapter::OnSessionKeysChange(const char* session_id,
> +                                     uint32_t session_id_size,
> +                                     bool has_additional_usable_key,
> +                                     const cdm::KeyInformation_2* keys_info,
> +                                     uint32_t keys_info_count)
> +{
> +  for (uint32_t i(0); i < keys_info_count; ++i)
> +  {
> +    char buffer[128];
> +    char* bufferPtr{buffer};
> +    for (uint32_t j{0}; j < keys_info[i].key_id_size; ++j)
> +      bufferPtr += std::snprintf(bufferPtr, 3, "%02X", (int)keys_info[i].key_id[j]);
> +    Log(LogLevel::DEBUG, "OnSessionKeysChange: KID %s, Status: %d, System code: %u", buffer,
> +        keys_info[i].status, keys_info[i].system_code);
> +
> +    SendClientMessage(session_id, session_id_size, CdmAdapterClient::kSessionKeysChange,
> +                      keys_info[i].key_id, keys_info[i].key_id_size,
> +                      static_cast<uint32_t>(keys_info[i].status));
> +  }
> +}
> +
>  void CdmAdapter::OnExpirationChange(const char* session_id,
>                    uint32_t session_id_size,
>                    cdm::Time new_expiry_time)
> @@ -673,12 +660,14 @@
>    return new CdmFileIoImpl(cdm_base_path_, client);
>  }
>  
> -
> -// Host_9 specific implementations
>  void CdmAdapter::OnResolveKeyStatusPromise(uint32_t promise_id, cdm::KeyStatus key_status)
>  {
>  }
>  
> +void CdmAdapter::OnResolveKeyStatusPromise(uint32_t promise_id, cdm::KeyStatus_2 key_status)
> +{
> +}
> +
>  void CdmAdapter::OnRejectPromise(uint32_t promise_id, cdm::Exception exception,
>    uint32_t system_code, const char* error_message, uint32_t error_message_size)
>  {
> @@ -692,15 +681,21 @@
>  
>  void CdmAdapter::RequestStorageId(uint32_t version)
>  {
> -  if (cdm10_)
> -    cdm10_->OnStorageId(1, nullptr, 0);
> +  if (cdm12_)
> +    cdm12_->OnStorageId(version, nullptr, 0);
>    else if (cdm11_)
> -    cdm11_->OnStorageId(1, nullptr, 0);
> +    cdm11_->OnStorageId(version, nullptr, 0);
> +  else if (cdm10_)
> +    cdm10_->OnStorageId(version, nullptr, 0);
> +}
> +
> +void CdmAdapter::ReportMetrics(cdm::MetricName metric_name, uint64_t value)
> +{
>  }
>  
>  void CdmAdapter::OnInitialized(bool success)
>  {
> -  LOG::LogF(LOGDEBUG, "CDM is initialized: %s", success ? "true" : "false");
> +  Log(LogLevel::DEBUG, "CDM is initialized: %s", success ? "true" : "false");
>  }
>  
>  
> @@ -751,14 +746,14 @@
>      }
>    } else
>      status = cdm::FileIOClient::Status::kSuccess;
> -  client_->OnReadComplete(status, data_buffer_, sz);
> +  client_->OnReadComplete(status, data_buffer_, static_cast<uint32_t>(sz));
>  }
>  
>  void CdmFileIoImpl::Write(const uint8_t* data, uint32_t data_size)
>  {
>    if (!ExistsDir(base_path_.c_str()) && !CreateDirs(base_path_.c_str()))
>    {
> -    LOG::LogF(LOGERROR, "Cannot create directory: %s", base_path_.c_str());
> +    LogF(LogLevel::ERROR, "Cannot create directory: %s", base_path_.c_str());
>      client_->OnWriteComplete(cdm::FileIOClient::Status::kError);
>      return;
>    }
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/lib/cdm/cdm/media/cdm/cdm_adapter.h kodi-inputstream-adaptive-21.5.15+ds/lib/cdm/cdm/media/cdm/cdm_adapter.h
> --- kodi-inputstream-adaptive-21.5.9+ds/lib/cdm/cdm/media/cdm/cdm_adapter.h	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/lib/cdm/cdm/media/cdm/cdm_adapter.h	2025-07-06 07:54:23.000000000 +0000
> @@ -69,16 +69,16 @@
>    cdm::Size m_size;
>    cdm::ColorSpace m_colorSpace;
>  
> -  uint32_t m_planeOffsets[cdm::VideoPlane::kMaxPlanes];
> -  uint32_t m_stride[cdm::VideoPlane::kMaxPlanes];
> +  uint32_t m_planeOffsets[cdm::kMaxPlanes];
> +  uint32_t m_stride[cdm::kMaxPlanes];
>  
>    uint64_t m_pts;
>  };
>  
> -class CdmAdapter : public std::enable_shared_from_this<CdmAdapter>
> -  , public cdm::Host_9
> -  , public cdm::Host_10
> -  , public cdm::Host_11
> +class CdmAdapter : public std::enable_shared_from_this<CdmAdapter>,
> +                   public cdm::Host_10,
> +                   public cdm::Host_11,
> +                   public cdm::Host_12
>  {
>   public:
>     void timerfunc(CdmAdapter* adp, int64_t delay, void* context);
> @@ -158,6 +158,9 @@
>    void OnResolveKeyStatusPromise(uint32_t promise_id,
>      cdm::KeyStatus key_status) override;
>  
> +  // Used by CDM12 and beyond
> +  void OnResolveKeyStatusPromise(uint32_t promise_id, cdm::KeyStatus_2 key_status) override;
> +
>  	void OnResolveNewSessionPromise(uint32_t promise_id,
>      const char* session_id,
>      uint32_t session_id_size) override;
> @@ -182,6 +185,13 @@
>      const cdm::KeyInformation* keys_info,
>      uint32_t keys_info_count) override;
>  
> +  // Used by CDM12 and beyond
> +	void OnSessionKeysChange(const char* session_id,
> +    uint32_t session_id_size,
> +    bool has_additional_usable_key,
> +    const cdm::KeyInformation_2* keys_info,
> +    uint32_t keys_info_count) override;
> +
>  	void OnExpirationChange(const char* session_id,
>      uint32_t session_id_size,
>      cdm::Time new_expiry_time) override;
> @@ -205,7 +215,7 @@
>  
>  	void RequestStorageId(uint32_t version) override;
>  
> -  cdm::CdmProxy* RequestCdmProxy(cdm::CdmProxyClient* client) override { return nullptr; };
> +  void ReportMetrics(cdm::MetricName metric_name, uint64_t value) override;
>  
>    void OnInitialized(bool success) override;
>  
> @@ -250,9 +260,9 @@
>    cdm::MessageType message_type_;
>    cdm::Buffer *active_buffer_;
>  
> -  cdm::ContentDecryptionModule_9 *cdm9_;
> -  cdm::ContentDecryptionModule_10 *cdm10_;
> -  cdm::ContentDecryptionModule_11 *cdm11_;
> +  cdm::ContentDecryptionModule_10* cdm10_{nullptr};
> +  cdm::ContentDecryptionModule_11* cdm11_{nullptr};
> +  cdm::ContentDecryptionModule_12* cdm12_{nullptr};
>  
>    DISALLOW_COPY_AND_ASSIGN(CdmAdapter);
>  };
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/lib/cdm/CMakeLists.txt kodi-inputstream-adaptive-21.5.15+ds/lib/cdm/CMakeLists.txt
> --- kodi-inputstream-adaptive-21.5.9+ds/lib/cdm/CMakeLists.txt	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/lib/cdm/CMakeLists.txt	2025-07-06 07:54:23.000000000 +0000
> @@ -10,6 +10,7 @@
>  endif()
>  
>  add_library(cdm_library STATIC
> +  cdm/debug.cpp
>    cdm/base/native_library.cc
>    cdm/base/native_library_${CDMTYPE}
>    cdm/media/cdm/cdm_adapter.cc
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/src/common/AdaptiveStream.cpp kodi-inputstream-adaptive-21.5.15+ds/src/common/AdaptiveStream.cpp
> --- kodi-inputstream-adaptive-21.5.9+ds/src/common/AdaptiveStream.cpp	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/src/common/AdaptiveStream.cpp	2025-07-06 07:54:23.000000000 +0000
> @@ -676,6 +676,12 @@
>        size_t segPosDelay =
>            static_cast<size_t>((m_tree->m_liveDelay * current_rep_->GetTimescale()) / segDur);
>  
> +      //! @todo: hackish workaround, when segment duration is same of live delay, force at least 1 position delay
> +      //! otherwise the current segment will be last available on the timeline,
> +      //! therefore when GetNextSegment is executed will return nullptr and the below (todo) BUG condition stop the playback
> +      if (segPosDelay == 0)
> +        segPosDelay = 1;
> +
>        if (segPos > segPosDelay)
>          segPos -= segPosDelay;
>        else
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/src/common/AdaptiveStream.h kodi-inputstream-adaptive-21.5.15+ds/src/common/AdaptiveStream.h
> --- kodi-inputstream-adaptive-21.5.9+ds/src/common/AdaptiveStream.h	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/src/common/AdaptiveStream.h	2025-07-06 07:54:23.000000000 +0000
> @@ -14,6 +14,7 @@
>  
>  #include <atomic>
>  #include <condition_variable>
> +#include <chrono>
>  #include <map>
>  #include <mutex>
>  #include <string>
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/src/decrypters/Helpers.cpp kodi-inputstream-adaptive-21.5.15+ds/src/decrypters/Helpers.cpp
> --- kodi-inputstream-adaptive-21.5.9+ds/src/decrypters/Helpers.cpp	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/src/decrypters/Helpers.cpp	2025-07-06 07:54:23.000000000 +0000
> @@ -81,8 +81,19 @@
>    // to avoid possible collisions, so we include the first directory path after the domain name
>    // e.g. http://localhost:1234/addonservicename/other_dir/get_license?id=xyz
>    // domain will result: http://localhost/addonservicename/
> +  // The port number will be removed, since add-ons should use each time a different random
> +  // port number to avoid conflicts with other add-ons and/or system services
>    if (STRING::Contains(baseDomain, "127.0.0.1") || STRING::Contains(baseDomain, "localhost"))
>    {
> +    const size_t bdStartPos = baseDomain.find("://") + 3;
> +    const size_t bdPortPos = baseDomain.find_first_of(':', bdStartPos);
> +    if (bdPortPos != std::string::npos)
> +    {
> +      // Remove the port number
> +      // e.g. "http://localhost:1234"; will remove ":1234"
> +      baseDomain.erase(bdPortPos);
> +    }
> +
>      const size_t domainStartPos = url.find("://") + 3;
>      const size_t pathStartPos = url.find_first_of('/', domainStartPos);
>      if (pathStartPos != std::string::npos)
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/src/decrypters/widevine/WVCdmAdapter.cpp kodi-inputstream-adaptive-21.5.15+ds/src/decrypters/widevine/WVCdmAdapter.cpp
> --- kodi-inputstream-adaptive-21.5.9+ds/src/decrypters/widevine/WVCdmAdapter.cpp	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/src/decrypters/widevine/WVCdmAdapter.cpp	2025-07-06 07:54:23.000000000 +0000
> @@ -11,6 +11,7 @@
>  #include "CdmFixedBuffer.h"
>  #include "WVCencSingleSampleDecrypter.h"
>  #include "WVDecrypter.h"
> +#include "cdm/debug.h"
>  #include "decrypters/Helpers.h"
>  #include "utils/FileUtils.h"
>  #include "utils/log.h"
> @@ -28,6 +29,30 @@
>  #else
>    constexpr const char* LIBRARY_FILENAME = "libwidevinecdm.so";
>  #endif
> +
> +void DebugLog(const CDM_DBG::LogLevel level, const char* msg)
> +{
> +  switch (level)
> +  {
> +    case CDM_DBG::LogLevel::ERROR:
> +      LOG::Log(LOGERROR, msg);
> +      break;
> +    case CDM_DBG::LogLevel::WARNING:
> +      LOG::Log(LOGWARNING, msg);
> +      break;
> +    case CDM_DBG::LogLevel::INFO:
> +      LOG::Log(LOGINFO, msg);
> +      break;
> +    case CDM_DBG::LogLevel::DEBUG:
> +      LOG::Log(LOGDEBUG, msg);
> +      break;
> +    case CDM_DBG::LogLevel::FATAL:
> +      LOG::Log(LOGFATAL, msg);
> +      break;
> +    default:
> +      break;
> +  }
> +}
>  } // unnamed namespace
>  
>  CWVCdmAdapter::CWVCdmAdapter(std::string_view licenseURL,
> @@ -36,6 +61,8 @@
>                               CWVDecrypter* host)
>    : m_licenseUrl(licenseURL), m_host(host), m_codecInstance(nullptr)
>  {
> +  CDM_DBG::SetDBGMsgCallback(DebugLog);
> +
>    if (m_host->GetLibraryPath().empty())
>    {
>      LOG::LogF(LOGERROR, "Widevine CDM library path not specified");
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/src/decrypters/widevine/WVCencSingleSampleDecrypter.cpp kodi-inputstream-adaptive-21.5.15+ds/src/decrypters/widevine/WVCencSingleSampleDecrypter.cpp
> --- kodi-inputstream-adaptive-21.5.9+ds/src/decrypters/widevine/WVCencSingleSampleDecrypter.cpp	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/src/decrypters/widevine/WVCencSingleSampleDecrypter.cpp	2025-07-06 07:54:23.000000000 +0000
> @@ -1125,7 +1125,7 @@
>      picture->decodedDataSize = videoFrame.FrameBuffer()->Size();
>      picture->videoBufferHandle = static_cast<CdmFixedBuffer*>(videoFrame.FrameBuffer())->Buffer();
>  
> -    for (size_t i = 0; i < cdm::VideoPlane::kMaxPlanes; ++i)
> +    for (size_t i = 0; i < cdm::kMaxPlanes; ++i)
>      {
>        picture->planeOffsets[i] = videoFrame.PlaneOffset(static_cast<cdm::VideoPlane>(i));
>        picture->stride[i] = videoFrame.Stride(static_cast<cdm::VideoPlane>(i));
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/src/parser/DASHTree.cpp kodi-inputstream-adaptive-21.5.15+ds/src/parser/DASHTree.cpp
> --- kodi-inputstream-adaptive-21.5.9+ds/src/parser/DASHTree.cpp	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/src/parser/DASHTree.cpp	2025-07-06 07:54:23.000000000 +0000
> @@ -1102,16 +1102,8 @@
>  
>            segmentsCount = std::max<size_t>(durationMs / segDurMs, 1);
>  
> -          if (available_time_ == 0)
> -          {
> -            time = tsbStart * segTemplate->GetTimescale() / 1000;
> -            segNumber = tsbStart / segDurMs;
> -          }
> -          else
> -          {
> -            time += tsbStart * segTemplate->GetTimescale() / 1000;
> -            segNumber += tsbStart / segDurMs;
> -          }
> +          segNumber += (tsbStart - periodStartMs) / segDurMs;
> +          time += (segNumber - segTemplate->GetStartNumber()) * segTemplate->GetDuration();
>          }
>          else if (periodDurMs > 0)
>          {
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/src/parser/HLSTree.cpp kodi-inputstream-adaptive-21.5.15+ds/src/parser/HLSTree.cpp
> --- kodi-inputstream-adaptive-21.5.9+ds/src/parser/HLSTree.cpp	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/src/parser/HLSTree.cpp	2025-07-06 07:54:23.000000000 +0000
> @@ -802,15 +802,18 @@
>          }
>  
>          FreeSegments(period, rep);
> -        rep->Timeline().Swap(newSegments);
>  
>          rep->SetStartNumber(mediaSequenceNbr);
> +
> +        // Update MEDIA-SEQUENCE for next period
> +        mediaSequenceNbr += newSegments.GetSize();
> +
> +        rep->Timeline().Swap(newSegments);
>        }
>  
>        isSkipUntilDiscont = false;
>        ++discontCount;
>  
> -      mediaSequenceNbr += rep->Timeline().GetSize();
>        currentSegNumber = mediaSequenceNbr;
>  
>        CPeriod* newPeriod = FindDiscontinuityPeriod(m_discontSeq + discontCount);
> @@ -910,8 +913,7 @@
>    uint64_t totalTimeMs = 0;
>    if (discontCount > 0 || m_hasDiscontSeq)
>    {
> -    // On live stream you dont know the period end, so dont set the period duration in advance
> -    if (!m_isLive && adp->GetStreamType() != StreamType::SUBTITLE)
> +    if (adp->GetStreamType() != StreamType::SUBTITLE)
>      {
>        uint64_t periodDuration =
>            (rep->GetDuration() * m_periods[discontCount]->GetTimescale()) / rep->GetTimescale();
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/src/samplereader/FragmentedSampleReader.cpp kodi-inputstream-adaptive-21.5.15+ds/src/samplereader/FragmentedSampleReader.cpp
> --- kodi-inputstream-adaptive-21.5.9+ds/src/samplereader/FragmentedSampleReader.cpp	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/src/samplereader/FragmentedSampleReader.cpp	2025-07-06 07:54:23.000000000 +0000
> @@ -466,6 +466,9 @@
>        return;
>      }
>    }
> +  else {
> +    m_protectedDesc = nullptr;
> +  }
>  
>    LOG::LogF(LOGDEBUG, "Codec fourcc: %s (%u)", CODEC::FourCCToString(desc->GetFormat()).c_str(),
>              desc->GetFormat());
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/src/test/TestDASHTree.cpp kodi-inputstream-adaptive-21.5.15+ds/src/test/TestDASHTree.cpp
> --- kodi-inputstream-adaptive-21.5.9+ds/src/test/TestDASHTree.cpp	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/src/test/TestDASHTree.cpp	2025-07-06 07:54:23.000000000 +0000
> @@ -286,7 +286,7 @@
>    auto& segments = tree->m_periods[0]->GetAdaptationSets()[0]->GetRepresentations()[0]->Timeline();
>  
>    EXPECT_EQ(segments.GetSize(), 450);
> -  EXPECT_EQ(segments.Get(0)->m_number, 404314437);
> +  EXPECT_EQ(segments.Get(0)->m_number, 404305524);
>  }
>  
>  TEST_F(DASHTreeTest, CalculateCorrectSegmentNumbersFromSegmentTemplateWithOldPublishTime)
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/src/test/TestUtils.cpp kodi-inputstream-adaptive-21.5.15+ds/src/test/TestUtils.cpp
> --- kodi-inputstream-adaptive-21.5.9+ds/src/test/TestUtils.cpp	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/src/test/TestUtils.cpp	2025-07-06 07:54:23.000000000 +0000
> @@ -44,9 +44,12 @@
>    EXPECT_EQ(URL::GetBaseDomain(url), "https://www.foo.bar";);
>  
>    url = "https://www.foo.bar:1234";;
> -  EXPECT_EQ(URL::GetBaseDomain(url), "https://www.foo.bar";);
> +  EXPECT_EQ(URL::GetBaseDomain(url), "https://www.foo.bar:1234";);
>  
>    url = "https://www.foo.bar:1234/mpd/test.mpd?ping=pong";;
> +  EXPECT_EQ(URL::GetBaseDomain(url), "https://www.foo.bar:1234";);
> +
> +  url = "https://www.foo.bar/example/smil:rtmp.smil/playlist.m3u8?ping=pong";;
>    EXPECT_EQ(URL::GetBaseDomain(url), "https://www.foo.bar";);
>  }
>  
> @@ -131,6 +134,17 @@
>    otherUrl = "./";
>    EXPECT_EQ(URL::Join(baseUrl, otherUrl), "https://foo.bar/sub1/sub2/";);
>  
> +
> +  baseUrl = "https://foo.bar/sub1../sub2./";;
> +  otherUrl = ".ending";
> +  EXPECT_EQ(URL::Join(baseUrl, otherUrl), "https://foo.bar/sub1../sub2./.ending";);
> +
> +  otherUrl = "./.ending/.";
> +  EXPECT_EQ(URL::Join(baseUrl, otherUrl), "https://foo.bar/sub1../sub2./.ending/";);
> +
> +  otherUrl = "./.ending/./";
> +  EXPECT_EQ(URL::Join(baseUrl, otherUrl), "https://foo.bar/sub1../sub2./.ending/";);
> +
>    // Less common and malformed test cases
>  
>    baseUrl = "https://foo.bar/sub1/sub2/";;
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/src/utils/log.h kodi-inputstream-adaptive-21.5.15+ds/src/utils/log.h
> --- kodi-inputstream-adaptive-21.5.9+ds/src/utils/log.h	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/src/utils/log.h	2025-07-06 07:54:23.000000000 +0000
> @@ -17,7 +17,6 @@
>  
>  #include <utility>
>  
> -// To keep in sync with SSDLogLevel on SSD_dll.h
>  enum LogLevel
>  {
>    LOGDEBUG,
> diff -Nru kodi-inputstream-adaptive-21.5.9+ds/src/utils/UrlUtils.cpp kodi-inputstream-adaptive-21.5.15+ds/src/utils/UrlUtils.cpp
> --- kodi-inputstream-adaptive-21.5.9+ds/src/utils/UrlUtils.cpp	2025-01-19 09:11:59.000000000 +0000
> +++ kodi-inputstream-adaptive-21.5.15+ds/src/utils/UrlUtils.cpp	2025-07-06 07:54:23.000000000 +0000
> @@ -78,6 +78,29 @@
>    return true;
>  }
>  
> +void RemovePrefixSingleDot(std::string& url)
> +{
> +  size_t pos{0};
> +  // Remove occurrences of "/./" preserving the separator
> +  while ((pos = url.find("/./")) != std::string::npos)
> +  {
> +    url.erase(pos, 2);
> +  }
> +
> +  if (StringUtils::EndsWith(url, "/."))
> +    url.pop_back(); // Delete the dot and preserve the separator
> +}
> +
> +void RemovePrefixDoubleDot(std::string& url)
> +{
> +  size_t pos{0};
> +  // Remove occurrences of "/../" preserving the separator
> +  while ((pos = url.find("/../")) != std::string::npos)
> +  {
> +    url.erase(pos, 3);
> +  }
> +}
> +
>  /*
>   * \brief Remove and resolve special dot's from the end of the url.
>   *        e.g. "http://foo.bar/sub1/sub2/.././"; will result "http://foo.bar/sub1/";
> @@ -88,18 +111,18 @@
>    size_t numSegsRemove{0};
>    size_t currPos{0};
>    size_t startPos{url.size() - 2};
> -  while ((currPos = url.rfind("/", startPos)) != std::string::npos)
> +  while ((currPos = url.rfind('/', startPos)) != std::string::npos)
>    {
>      // Stop to ignore "/../" from the start of string, e.g. ignored --> "../../something/../" <-- handled
> -    if (url.substr(currPos + 1, startPos - currPos + 1) != PREFIX_DOUBLE_DOT)
> +    if (currPos == 0 || url.substr(currPos + 1, startPos - currPos + 1) != PREFIX_DOUBLE_DOT)
>        break;
>      startPos = currPos - 1;
>      numSegsRemove++;
>    }
>  
>    // Remove special prefixes
> -  UTILS::STRING::ReplaceAll(url, PREFIX_DOUBLE_DOT, "");
> -  UTILS::STRING::ReplaceAll(url, PREFIX_SINGLE_DOT, "");
> +  RemovePrefixSingleDot(url);
> +  RemovePrefixDoubleDot(url);
>  
>    size_t addrsStartPos{0};
>    if (IsUrlAbsolute(url))
> @@ -257,18 +280,16 @@
>      if (paramsPos != std::string::npos)
>        url.erase(paramsPos);
>  
> -    const size_t domainStartPos = url.find("://") + 3;
> -    // Try remove url port number and path
> -    const size_t port = url.find_first_of(':', domainStartPos);
> -    if (port != std::string::npos)
> -      url.erase(port);
> -    else
> -    {
> -      // Try remove the path
> -      const size_t slashPos = url.find_first_of('/', domainStartPos);
> -      if (slashPos != std::string::npos)
> -        url.erase(slashPos);
> -    }
> +    const size_t schemeEndPos = url.find("://");
> +    if (schemeEndPos == std::string::npos)
> +      return ""; // Not valid
> +
> +    const size_t domainStartPos = schemeEndPos + 3;
> +    const size_t pathPos = url.find_first_of('/', domainStartPos);
> +
> +    if (pathPos != std::string::npos)
> +      url.erase(pathPos); // remove from slash
> +
>      return url;
>    }
>    return "";
> @@ -284,8 +305,6 @@
>  
>    if (relativeUrl == ".") // Ignore single dot
>      relativeUrl.clear();
> -  else if (relativeUrl.compare(0, 2, PREFIX_SINGLE_DOT) == 0) // Ignore prefix ./
> -    relativeUrl.erase(0, 2);
>  
>    // Sanitize for missing backslash
>    if (relativeUrl == ".." || StringUtils::EndsWith(relativeUrl, "/.."))


-- 
Sebastian Ramacher


Reply to: