Skip to content

Commit 2707884

Browse files
committed
Support interfaces 10 and 11 simultaneously
Since autocxx subclasses don't support multiple inheritance, we have to subclass all the ContentDecryptionModule_NN versions in C++ and then subclass a second time in Rust. This means we do now have some C++ in our tree (hence the Chromium-derived .clang-format), but it's cleaner than the other options. For Host_NN, we can do dynamic dispatch in Rust. I've added a macro to reduce the boilerplate, but there's still some. Even though the code's not the cleanest, I think this feature is important enough to justify the added complexity. The new C++ header exposed a bug in autocxx, which I've fixed in [1] and incorporated into our fork. Note that, while Chromium parses the "x-cdm-*-versions" manifest keys as comma-separated lists, Firefox parses each as a single number. I've put the comma-separated versions in the Firefox manifest too because the bug might get fixed by the time Firefox drops interface 10 support. [1] google/autocxx#1486
1 parent 51f90be commit 2707884

File tree

10 files changed

+279
-34
lines changed

10 files changed

+279
-34
lines changed

.clang-format

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
BasedOnStyle: Chromium
2+
InsertBraces: true
3+
InsertNewlineAtEOF: true
4+
5+
Standard: Cpp11

Cargo.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ To build OpenWV, follow these steps:
4949
in the last section—`libwidevinecdm.so` on Linux, `widevinecdm.dll` on Windows,
5050
or `libwidevinecdm.dylib` on macOS.*
5151

52-
### Firefox (versions 136+ only)
52+
### Firefox
5353
1. Open `about:support` and note your "Profile Directory".
5454
2. Open `about:config`. Set `media.gmp-widevinecdm.autoupdate` to `false`
5555
(creating it if needed), and set `media.gmp-widevinecdm.version` to `openwv`

build.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ enum BuildError {
1212

1313
fn main() -> Result<(), BuildError> {
1414
let bindings_rs = "src/lib.rs";
15-
let mut autocxx = autocxx_build::Builder::new(bindings_rs, &[PathBuf::from("third-party/cdm")])
15+
let autocxx_incs = &[PathBuf::from("src"), PathBuf::from("third-party/cdm")];
16+
let mut autocxx = autocxx_build::Builder::new(bindings_rs, autocxx_incs)
1617
.extra_clang_args(&["-std=c++14"])
1718
.build()?;
1819
autocxx.std("c++14").compile("cdm-api");

manifest-chromium.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
"description": "OpenWV Widevine-compatible CDM",
55
"version": "9999",
66
"x-cdm-module-versions": "4",
7-
"x-cdm-interface-versions": "11",
8-
"x-cdm-host-versions": "11",
7+
"x-cdm-interface-versions": "10,11",
8+
"x-cdm-host-versions": "10,11",
99
"x-cdm-codecs": "vp8,vp09,avc1,av01",
1010
"x-cdm-persistent-license-support": false,
1111
"x-cdm-supported-encryption-schemes": [

manifest-firefox.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
"description": "OpenWV Widevine-compatible CDM",
55
"version": "1",
66
"x-cdm-module-versions": "4",
7-
"x-cdm-interface-versions": "11",
8-
"x-cdm-host-versions": "11",
7+
"x-cdm-interface-versions": "10,11",
8+
"x-cdm-host-versions": "10,11",
99
"x-cdm-codecs": "",
1010
"x-cdm-persistent-license-support": false,
1111
"x-cdm-supported-encryption-schemes": [

src/common_cdm.h

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#ifndef COMMON_CDM_H_
2+
#define COMMON_CDM_H_
3+
4+
#include "content_decryption_module.h"
5+
6+
namespace cdm {
7+
8+
class CDM_CLASS_API CommonCdm : public cdm::ContentDecryptionModule_10,
9+
public cdm::ContentDecryptionModule_11 {
10+
public:
11+
cdm::ContentDecryptionModule_10* As10() { return this; };
12+
cdm::ContentDecryptionModule_11* As11() { return this; };
13+
14+
void Initialize(bool allow_distinctive_identifier,
15+
bool allow_persistent_state,
16+
bool use_hw_secure_codecs) override = 0;
17+
void GetStatusForPolicy(uint32_t promise_id,
18+
const Policy& policy) override = 0;
19+
void SetServerCertificate(uint32_t promise_id,
20+
const uint8_t* server_certificate_data,
21+
uint32_t server_certificate_data_size) override = 0;
22+
void CreateSessionAndGenerateRequest(uint32_t promise_id,
23+
SessionType session_type,
24+
InitDataType init_data_type,
25+
const uint8_t* init_data,
26+
uint32_t init_data_size) override = 0;
27+
void LoadSession(uint32_t promise_id,
28+
SessionType session_type,
29+
const char* session_id,
30+
uint32_t session_id_size) override = 0;
31+
void UpdateSession(uint32_t promise_id,
32+
const char* session_id,
33+
uint32_t session_id_size,
34+
const uint8_t* response,
35+
uint32_t response_size) override = 0;
36+
void CloseSession(uint32_t promise_id,
37+
const char* session_id,
38+
uint32_t session_id_size) override = 0;
39+
void RemoveSession(uint32_t promise_id,
40+
const char* session_id,
41+
uint32_t session_id_size) override = 0;
42+
void TimerExpired(void* context) override = 0;
43+
Status Decrypt(const InputBuffer_2& encrypted_buffer,
44+
DecryptedBlock* decrypted_buffer) override = 0;
45+
Status InitializeAudioDecoder(
46+
const AudioDecoderConfig_2& audio_decoder_config) override = 0;
47+
Status InitializeVideoDecoder(
48+
const VideoDecoderConfig_2& video_decoder_config) override = 0;
49+
void DeinitializeDecoder(StreamType decoder_type) override = 0;
50+
void ResetDecoder(StreamType decoder_type) override = 0;
51+
Status DecryptAndDecodeFrame(const InputBuffer_2& encrypted_buffer,
52+
VideoFrame* video_frame) override = 0;
53+
Status DecryptAndDecodeSamples(const InputBuffer_2& encrypted_buffer,
54+
AudioFrames* audio_frames) override = 0;
55+
void OnPlatformChallengeResponse(
56+
const PlatformChallengeResponse& response) override = 0;
57+
void OnQueryOutputProtectionStatus(QueryResult result,
58+
uint32_t link_mask,
59+
uint32_t output_protection_mask) override =
60+
0;
61+
void OnStorageId(uint32_t version,
62+
const uint8_t* storage_id,
63+
uint32_t storage_id_size) override = 0;
64+
void Destroy() override = 0;
65+
66+
protected:
67+
CommonCdm() {}
68+
~CommonCdm() {}
69+
};
70+
71+
} // namespace cdm
72+
73+
#endif // COMMON_CDM_H_

src/common_host.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
use std::ffi::{c_char, c_void};
2+
use std::pin::Pin;
3+
4+
use crate::ffi::cdm;
5+
6+
/// Trait abstracting over different `Host_NN` interface versions. Note that we
7+
/// only define the methods OpenWV actually uses, for brevity and better
8+
/// compatibility across versions.
9+
#[allow(non_snake_case)]
10+
pub trait CommonHost {
11+
fn Allocate(self: Pin<&mut Self>, capacity: u32) -> *mut cdm::Buffer;
12+
fn OnInitialized(self: Pin<&mut Self>, success: bool);
13+
fn OnResolveKeyStatusPromise(self: Pin<&mut Self>, promise_id: u32, key_status: cdm::KeyStatus);
14+
unsafe fn OnResolveNewSessionPromise(
15+
self: Pin<&mut Self>,
16+
promise_id: u32,
17+
session_id: *const c_char,
18+
session_id_size: u32,
19+
);
20+
fn OnResolvePromise(self: Pin<&mut Self>, promise_id: u32);
21+
unsafe fn OnRejectPromise(
22+
self: Pin<&mut Self>,
23+
promise_id: u32,
24+
exception: cdm::Exception,
25+
system_code: u32,
26+
error_message: *const c_char,
27+
error_message_size: u32,
28+
);
29+
unsafe fn OnSessionMessage(
30+
self: Pin<&mut Self>,
31+
session_id: *const c_char,
32+
session_id_size: u32,
33+
message_type: cdm::MessageType,
34+
message: *const c_char,
35+
message_size: u32,
36+
);
37+
unsafe fn OnSessionKeysChange(
38+
self: Pin<&mut Self>,
39+
session_id: *const c_char,
40+
session_id_size: u32,
41+
has_additional_usable_key: bool,
42+
keys_info: *const cdm::KeyInformation,
43+
keys_info_count: u32,
44+
);
45+
unsafe fn OnSessionClosed(
46+
self: Pin<&mut Self>,
47+
session_id: *const c_char,
48+
session_id_size: u32,
49+
);
50+
}
51+
52+
pub unsafe fn downcast_host<T: CommonHost + 'static>(
53+
ptr: *mut c_void,
54+
) -> Option<&'static mut dyn CommonHost> {
55+
let typed_ptr: *mut T = ptr.cast();
56+
let concrete_ref = unsafe { typed_ptr.as_mut() };
57+
concrete_ref.map(|x| x as &mut dyn CommonHost)
58+
}
59+
60+
macro_rules! impl_common_host {
61+
($target:path) => {
62+
impl CommonHost for $target {
63+
fn Allocate(self: Pin<&mut Self>, capacity: u32) -> *mut cdm::Buffer {
64+
self.Allocate(capacity)
65+
}
66+
67+
fn OnInitialized(self: Pin<&mut Self>, success: bool) {
68+
self.OnInitialized(success)
69+
}
70+
71+
fn OnResolveKeyStatusPromise(
72+
self: Pin<&mut Self>,
73+
promise_id: u32,
74+
key_status: cdm::KeyStatus,
75+
) {
76+
self.OnResolveKeyStatusPromise(promise_id, key_status)
77+
}
78+
79+
unsafe fn OnResolveNewSessionPromise(
80+
self: Pin<&mut Self>,
81+
promise_id: u32,
82+
session_id: *const c_char,
83+
session_id_size: u32,
84+
) {
85+
unsafe { self.OnResolveNewSessionPromise(promise_id, session_id, session_id_size) }
86+
}
87+
88+
fn OnResolvePromise(self: Pin<&mut Self>, promise_id: u32) {
89+
self.OnResolvePromise(promise_id)
90+
}
91+
92+
unsafe fn OnRejectPromise(
93+
self: Pin<&mut Self>,
94+
promise_id: u32,
95+
exception: cdm::Exception,
96+
system_code: u32,
97+
error_message: *const c_char,
98+
error_message_size: u32,
99+
) {
100+
unsafe {
101+
self.OnRejectPromise(
102+
promise_id,
103+
exception,
104+
system_code,
105+
error_message,
106+
error_message_size,
107+
)
108+
}
109+
}
110+
111+
unsafe fn OnSessionMessage(
112+
self: Pin<&mut Self>,
113+
session_id: *const c_char,
114+
session_id_size: u32,
115+
message_type: cdm::MessageType,
116+
message: *const c_char,
117+
message_size: u32,
118+
) {
119+
unsafe {
120+
self.OnSessionMessage(
121+
session_id,
122+
session_id_size,
123+
message_type,
124+
message,
125+
message_size,
126+
)
127+
}
128+
}
129+
130+
unsafe fn OnSessionKeysChange(
131+
self: Pin<&mut Self>,
132+
session_id: *const c_char,
133+
session_id_size: u32,
134+
has_additional_usable_key: bool,
135+
keys_info: *const cdm::KeyInformation,
136+
keys_info_count: u32,
137+
) {
138+
unsafe {
139+
self.OnSessionKeysChange(
140+
session_id,
141+
session_id_size,
142+
has_additional_usable_key,
143+
keys_info,
144+
keys_info_count,
145+
)
146+
}
147+
}
148+
149+
unsafe fn OnSessionClosed(
150+
self: Pin<&mut Self>,
151+
session_id: *const c_char,
152+
session_id_size: u32,
153+
) {
154+
unsafe { self.OnSessionClosed(session_id, session_id_size) }
155+
}
156+
}
157+
};
158+
}
159+
160+
impl_common_host!(cdm::Host_10);
161+
impl_common_host!(cdm::Host_11);

src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use autocxx::include_cpp;
33
mod config;
44
mod util;
55

6+
mod common_host;
67
mod content_key;
78
mod decrypt;
89
mod init_data;
@@ -15,9 +16,12 @@ mod wvd_file;
1516

1617
use openwv::OpenWv;
1718
include_cpp! {
18-
#include "content_decryption_module.h"
19+
#include "common_cdm.h"
1920
safety!(unsafe)
20-
subclass!("cdm::ContentDecryptionModule_11", OpenWv)
21+
// FIXME: We can directly subclass from `cdm::ContentDecryptionModule_NN`
22+
// here if autocxx ever supports multiple inheritance for subclasses.
23+
subclass!("cdm::CommonCdm", OpenWv)
24+
generate!("cdm::Host_10")
2125
generate!("cdm::Host_11")
2226
generate!("cdm::Buffer")
2327
generate!("cdm::DecryptedBlock")

0 commit comments

Comments
 (0)