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

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



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?

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, "/.."))

Reply to: