1use playready::{Cdm, Device, Pssh, cdm::Session, pssh::WrmHeader};
2use safer_ffi::prelude::*;
3
4#[derive_ReprC(rename = "playready_cdm")]
5#[repr(opaque)]
6pub struct FfiCdm {
7 inner: Cdm,
8}
9
10#[derive_ReprC(rename = "playready_session")]
11#[repr(opaque)]
12pub struct FfiSession {
13 inner: Session,
14}
15
16#[derive_ReprC(rename = "playready_pssh")]
17#[repr(opaque)]
18pub struct FfiPssh {
19 inner: Pssh,
20}
21
22#[derive_ReprC(rename = "playready_wrm_header")]
23#[repr(opaque)]
24pub struct FfiWrmHeader {
25 inner: WrmHeader,
26}
27
28impl From<WrmHeader> for FfiWrmHeader {
29 fn from(value: WrmHeader) -> Self {
30 Self { inner: value }
31 }
32}
33
34#[derive_ReprC(rename = "playready_kid_ck")]
35#[repr(C)]
36pub struct FfiKidCk {
37 pub kid: [u8; 16],
38 pub ck: repr_c::Box<[u8]>,
39}
40
41#[ffi_export]
46pub fn playready_cdm_create_from_prd(
47 prd_path: char_p::Ref<'_>,
48 error_msg: Out<'_, Option<char_p::Box>>,
49) -> Option<repr_c::Box<FfiCdm>> {
50 let device = match Device::from_prd(prd_path.to_str()) {
51 Ok(device) => device,
52 Err(err) => {
53 error_msg.write(Some(char_p::new(format!("{err:?}"))));
54 return None;
55 }
56 };
57
58 Some(
59 Box::new(FfiCdm {
60 inner: Cdm::from_device(device),
61 })
62 .into(),
63 )
64}
65
66#[ffi_export]
70pub fn playready_cdm_open_session(cdm: &FfiCdm) -> repr_c::Box<FfiSession> {
71 Box::new(FfiSession {
72 inner: cdm.inner.open_session(),
73 })
74 .into()
75}
76
77#[ffi_export]
82pub fn playready_pssh_from_bytes(
83 bytes: c_slice::Ref<'_, u8>,
84 error_msg: Out<'_, Option<char_p::Box>>,
85) -> Option<repr_c::Box<FfiPssh>> {
86 match Pssh::from_bytes(bytes.as_slice()) {
87 Ok(inner) => Some(Box::new(FfiPssh { inner }).into()),
88 Err(err) => {
89 error_msg.write(Some(char_p::new(format!("{err:?}"))));
90 None
91 }
92 }
93}
94
95#[ffi_export]
100pub fn playready_pssh_from_b64(
101 b64: char_p::Ref<'_>,
102 error_msg: Out<'_, Option<char_p::Box>>,
103) -> Option<repr_c::Box<FfiPssh>> {
104 match Pssh::from_b64(b64.to_bytes()) {
105 Ok(inner) => Some(Box::new(FfiPssh { inner }).into()),
106 Err(err) => {
107 error_msg.write(Some(char_p::new(format!("{err:?}"))));
108 None
109 }
110 }
111}
112
113#[ffi_export]
117pub fn playready_pssh_get_first_wrm_header(pssh: &FfiPssh) -> Option<repr_c::Box<FfiWrmHeader>> {
118 let wrm_headers = pssh.inner.wrm_headers();
119 let wrm_header: FfiWrmHeader = wrm_headers.into_iter().next()?.into();
120 Some(Box::new(wrm_header).into())
121}
122
123#[ffi_export]
129pub fn playready_session_get_license_challenge(
130 session: &FfiSession,
131 wrm_header: repr_c::Box<FfiWrmHeader>,
132 error_msg: Out<'_, Option<char_p::Box>>,
133) -> Option<char_p::Box> {
134 let wrm_header = wrm_header.into();
135
136 match session.inner.get_license_challenge(wrm_header.inner) {
137 Ok(s) => Some(char_p::new(s)),
138 Err(err) => {
139 error_msg.write(Some(char_p::new(format!("{err:?}"))));
140 None
141 }
142 }
143}
144
145#[ffi_export]
150pub fn playready_session_get_keys_from_challenge_response(
151 session: &FfiSession,
152 response: char_p::Ref<'_>,
153 error_msg: Out<'_, Option<char_p::Box>>,
154) -> Option<repr_c::Vec<FfiKidCk>> {
155 match session
156 .inner
157 .get_keys_from_challenge_response(response.to_str())
158 {
159 Ok(keys) => Some(
160 keys.into_iter()
161 .map(|kid_ck| FfiKidCk {
162 kid: kid_ck.0.into(),
163 ck: Box::<[u8]>::from(kid_ck.1).into(),
164 })
165 .collect::<Vec<_>>()
166 .into(),
167 ),
168 Err(err) => {
169 error_msg.write(Some(char_p::new(format!("{err:?}"))));
170 None
171 }
172 }
173}
174
175#[ffi_export]
177pub fn playready_cdm_free(cdm: Option<repr_c::Box<FfiCdm>>) {
178 drop(cdm)
179}
180
181#[ffi_export]
183pub fn playready_session_free(session: Option<repr_c::Box<FfiSession>>) {
184 drop(session)
185}
186
187#[ffi_export]
189pub fn playready_pssh_free(pssh: Option<repr_c::Box<FfiPssh>>) {
190 drop(pssh)
191}
192
193#[ffi_export]
195pub fn playready_license_challenge_free(challenge: Option<char_p::Box>) {
196 drop(challenge)
197}
198
199#[ffi_export]
201pub fn playready_keys_free(keys: repr_c::Vec<FfiKidCk>) {
202 drop(keys)
203}
204
205#[ffi_export]
207pub fn playready_error_message_free(error_msg: Option<char_p::Box>) {
208 drop(error_msg)
209}
210
211pub fn generate_headers() -> std::io::Result<()> {
212 safer_ffi::headers::builder()
213 .to_file("playready.h")?
214 .generate()
215}