1use crate::error::{Error, Result};
38use crate::objects;
39use crate::tag::ApduTag;
40use alloc::vec::Vec;
41use dvb_common::{Parse, Serialize};
42
43pub mod tag {
45 use crate::tag::ApduTag;
46 pub const VERIFY_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0xA0, 0x00);
48 pub const VERIFY_REPLY: ApduTag = ApduTag::from_bytes(0x9F, 0xA0, 0x01);
50 pub const CAPABILITIES_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0xA0, 0x02);
52 pub const CAPABILITIES_REPLY: ApduTag = ApduTag::from_bytes(0x9F, 0xA0, 0x03);
54 pub const START_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0xA0, 0x04);
56 pub const START_REPLY: ApduTag = ApduTag::from_bytes(0x9F, 0xA0, 0x05);
58 pub const PLAY_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0xA0, 0x06);
60 pub const STATUS_ERROR: ApduTag = ApduTag::from_bytes(0x9F, 0xA0, 0x07);
62 pub const CONTROL_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0xA0, 0x08);
64 pub const INFO_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0xA0, 0x09);
66 pub const INFO_REPLY: ApduTag = ApduTag::from_bytes(0x9F, 0xA0, 0x0A);
68 pub const STOP: ApduTag = ApduTag::from_bytes(0x9F, 0xA0, 0x0B);
70 pub const END: ApduTag = ApduTag::from_bytes(0x9F, 0xA0, 0x0C);
72 pub const ASSET_END: ApduTag = ApduTag::from_bytes(0x9F, 0xA0, 0x0D);
74 pub const UPDATE_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0xA0, 0x0E);
76 pub const UPDATE_REPLY: ApduTag = ApduTag::from_bytes(0x9F, 0xA0, 0x0F);
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86#[cfg_attr(feature = "serde", derive(serde::Serialize))]
87#[non_exhaustive]
88pub enum PlayerVerifyStatus {
89 Ok,
91 Error,
93 Reserved(u8),
95}
96impl PlayerVerifyStatus {
97 #[must_use]
99 pub fn from_u8(v: u8) -> Self {
100 match v {
101 0x00 => Self::Ok,
102 0x01 => Self::Error,
103 other => Self::Reserved(other),
104 }
105 }
106 #[must_use]
108 pub fn to_u8(self) -> u8 {
109 match self {
110 Self::Ok => 0x00,
111 Self::Error => 0x01,
112 Self::Reserved(v) => v,
113 }
114 }
115 #[must_use]
117 pub fn name(&self) -> &'static str {
118 match self {
119 Self::Ok => "ok",
120 Self::Error => "error",
121 Self::Reserved(_) => "reserved",
122 }
123 }
124}
125dvb_common::impl_spec_display!(PlayerVerifyStatus, Reserved);
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq)]
131#[cfg_attr(feature = "serde", derive(serde::Serialize))]
132#[non_exhaustive]
133pub enum InputStatus {
134 Ok,
136 RequestRefused,
138 InsufficientBitrate,
140 NoSessionsAvailable,
142 Reserved(u8),
144}
145impl InputStatus {
146 #[must_use]
148 pub fn from_u8(v: u8) -> Self {
149 match v {
150 0x00 => Self::Ok,
151 0x01 => Self::RequestRefused,
152 0x02 => Self::InsufficientBitrate,
153 0x03 => Self::NoSessionsAvailable,
154 other => Self::Reserved(other),
155 }
156 }
157 #[must_use]
159 pub fn to_u8(self) -> u8 {
160 match self {
161 Self::Ok => 0x00,
162 Self::RequestRefused => 0x01,
163 Self::InsufficientBitrate => 0x02,
164 Self::NoSessionsAvailable => 0x03,
165 Self::Reserved(v) => v,
166 }
167 }
168 #[must_use]
170 pub fn name(&self) -> &'static str {
171 match self {
172 Self::Ok => "ok",
173 Self::RequestRefused => "request_refused",
174 Self::InsufficientBitrate => "insufficient_bitrate",
175 Self::NoSessionsAvailable => "no_sessions_available",
176 Self::Reserved(_) => "reserved",
177 }
178 }
179}
180dvb_common::impl_spec_display!(InputStatus, Reserved);
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq)]
186#[cfg_attr(feature = "serde", derive(serde::Serialize))]
187#[non_exhaustive]
188pub enum PlayStatus {
189 PlayNotPossible,
191 Unrecoverable,
193 ContentBlocked,
195 Reserved(u8),
197}
198impl PlayStatus {
199 #[must_use]
201 pub fn from_u8(v: u8) -> Self {
202 match v {
203 0x01 => Self::PlayNotPossible,
204 0x02 => Self::Unrecoverable,
205 0x03 => Self::ContentBlocked,
206 other => Self::Reserved(other),
207 }
208 }
209 #[must_use]
211 pub fn to_u8(self) -> u8 {
212 match self {
213 Self::PlayNotPossible => 0x01,
214 Self::Unrecoverable => 0x02,
215 Self::ContentBlocked => 0x03,
216 Self::Reserved(v) => v,
217 }
218 }
219 #[must_use]
221 pub fn name(&self) -> &'static str {
222 match self {
223 Self::PlayNotPossible => "play_not_possible",
224 Self::Unrecoverable => "unrecoverable_error",
225 Self::ContentBlocked => "content_blocked",
226 Self::Reserved(_) => "reserved",
227 }
228 }
229}
230dvb_common::impl_spec_display!(PlayStatus, Reserved);
231
232#[derive(Debug, Clone, Copy, PartialEq, Eq)]
236#[cfg_attr(feature = "serde", derive(serde::Serialize))]
237#[non_exhaustive]
238pub enum SeekMode {
239 Absolute,
241 Relative,
243 Reserved(u8),
245}
246impl SeekMode {
247 #[must_use]
249 pub fn from_u8(v: u8) -> Self {
250 match v {
251 0x00 => Self::Absolute,
252 0x01 => Self::Relative,
253 other => Self::Reserved(other),
254 }
255 }
256 #[must_use]
258 pub fn to_u8(self) -> u8 {
259 match self {
260 Self::Absolute => 0x00,
261 Self::Relative => 0x01,
262 Self::Reserved(v) => v,
263 }
264 }
265 #[must_use]
267 pub fn name(&self) -> &'static str {
268 match self {
269 Self::Absolute => "absolute",
270 Self::Relative => "relative",
271 Self::Reserved(_) => "reserved",
272 }
273 }
274}
275dvb_common::impl_spec_display!(SeekMode, Reserved);
276
277#[derive(Debug, Clone, Copy, PartialEq, Eq)]
281#[cfg_attr(feature = "serde", derive(serde::Serialize))]
282#[non_exhaustive]
283pub enum UpdateStatus {
284 Ok,
286 RequestRefused,
288 Reserved(u8),
290}
291impl UpdateStatus {
292 #[must_use]
294 pub fn from_u8(v: u8) -> Self {
295 match v {
296 0x00 => Self::Ok,
297 0x01 => Self::RequestRefused,
298 other => Self::Reserved(other),
299 }
300 }
301 #[must_use]
303 pub fn to_u8(self) -> u8 {
304 match self {
305 Self::Ok => 0x00,
306 Self::RequestRefused => 0x01,
307 Self::Reserved(v) => v,
308 }
309 }
310 #[must_use]
312 pub fn name(&self) -> &'static str {
313 match self {
314 Self::Ok => "ok",
315 Self::RequestRefused => "request_refused",
316 Self::Reserved(_) => "reserved",
317 }
318 }
319}
320dvb_common::impl_spec_display!(UpdateStatus, Reserved);
321
322#[derive(Debug, Clone, PartialEq, Eq)]
329#[cfg_attr(feature = "serde", derive(serde::Serialize))]
330pub struct PlayerVerifyReq<'a> {
331 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
333 pub service_location: &'a [u8],
334}
335
336const SERVICE_LOCATION_PREFIX: usize = 2;
338
339impl<'a> Parse<'a> for PlayerVerifyReq<'a> {
340 type Error = Error;
341 fn parse(bytes: &'a [u8]) -> Result<Self> {
342 let body = objects::parse_apdu_header(bytes, tag::VERIFY_REQ, "CICAM_player_verify_req")?;
343 let service_location = parse_service_location(body, "CICAM_player_verify_req")?;
344 Ok(Self { service_location })
345 }
346}
347impl Serialize for PlayerVerifyReq<'_> {
348 type Error = Error;
349 fn serialized_len(&self) -> usize {
350 objects::apdu_len(SERVICE_LOCATION_PREFIX + self.service_location.len())
351 }
352 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
353 let body_len = SERVICE_LOCATION_PREFIX + self.service_location.len();
354 let pos = objects::write_apdu_header(tag::VERIFY_REQ, body_len, buf)?;
355 write_service_location(self.service_location, &mut buf[pos..]);
356 Ok(pos + body_len)
357 }
358}
359
360fn parse_service_location<'a>(body: &'a [u8], what: &'static str) -> Result<&'a [u8]> {
361 if body.len() < SERVICE_LOCATION_PREFIX {
362 return Err(Error::BufferTooShort {
363 need: SERVICE_LOCATION_PREFIX,
364 have: body.len(),
365 what,
366 });
367 }
368 let len = u16::from_be_bytes([body[0], body[1]]) as usize;
369 let end = SERVICE_LOCATION_PREFIX + len;
370 if body.len() < end {
371 return Err(Error::LengthMismatch {
372 what,
373 declared: len,
374 actual: body.len().saturating_sub(SERVICE_LOCATION_PREFIX),
375 });
376 }
377 Ok(&body[SERVICE_LOCATION_PREFIX..end])
378}
379
380fn write_service_location(loc: &[u8], buf: &mut [u8]) {
381 buf[0..2].copy_from_slice(&(loc.len() as u16).to_be_bytes());
382 buf[2..2 + loc.len()].copy_from_slice(loc);
383}
384
385#[derive(Debug, Clone, Copy, PartialEq, Eq)]
391#[cfg_attr(feature = "serde", derive(serde::Serialize))]
392pub struct PlayerVerifyReply {
393 pub player_verify_status: PlayerVerifyStatus,
395}
396
397const VERIFY_REPLY_BODY: usize = 1;
398
399impl<'a> Parse<'a> for PlayerVerifyReply {
400 type Error = Error;
401 fn parse(bytes: &'a [u8]) -> Result<Self> {
402 let body =
403 objects::parse_apdu_header(bytes, tag::VERIFY_REPLY, "CICAM_player_verify_reply")?;
404 if body.len() < VERIFY_REPLY_BODY {
405 return Err(Error::BufferTooShort {
406 need: VERIFY_REPLY_BODY,
407 have: body.len(),
408 what: "CICAM_player_verify_reply",
409 });
410 }
411 Ok(Self {
412 player_verify_status: PlayerVerifyStatus::from_u8(body[0]),
413 })
414 }
415}
416impl Serialize for PlayerVerifyReply {
417 type Error = Error;
418 fn serialized_len(&self) -> usize {
419 objects::apdu_len(VERIFY_REPLY_BODY)
420 }
421 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
422 let pos = objects::write_apdu_header(tag::VERIFY_REPLY, VERIFY_REPLY_BODY, buf)?;
423 buf[pos] = self.player_verify_status.to_u8();
424 Ok(pos + VERIFY_REPLY_BODY)
425 }
426}
427
428#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
434#[cfg_attr(feature = "serde", derive(serde::Serialize))]
435pub struct PlayerCapabilitiesReq;
436
437impl<'a> Parse<'a> for PlayerCapabilitiesReq {
438 type Error = Error;
439 fn parse(bytes: &'a [u8]) -> Result<Self> {
440 objects::parse_empty_apdu(
441 bytes,
442 tag::CAPABILITIES_REQ,
443 "CICAM_player_capabilities_req",
444 )?;
445 Ok(Self)
446 }
447}
448impl Serialize for PlayerCapabilitiesReq {
449 type Error = Error;
450 fn serialized_len(&self) -> usize {
451 objects::empty_apdu_len()
452 }
453 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
454 objects::serialize_empty_apdu(tag::CAPABILITIES_REQ, buf)
455 }
456}
457
458#[derive(Debug, Clone, Copy, PartialEq, Eq)]
466#[cfg_attr(feature = "serde", derive(serde::Serialize))]
467pub struct ComponentType {
468 pub stream_content: u8,
470 pub component_type: u8,
472}
473
474#[derive(Debug, Clone, PartialEq, Eq)]
476#[cfg_attr(feature = "serde", derive(serde::Serialize))]
477pub struct PlayerCapabilitiesReply {
478 pub component_types: Vec<ComponentType>,
480}
481
482const CAPABILITIES_PREFIX: usize = 2;
484const COMPONENT_TYPE_LEN: usize = 2;
485const STREAM_CONTENT_MASK: u8 = 0x0F;
486
487impl<'a> Parse<'a> for PlayerCapabilitiesReply {
488 type Error = Error;
489 fn parse(bytes: &'a [u8]) -> Result<Self> {
490 let body = objects::parse_apdu_header(
491 bytes,
492 tag::CAPABILITIES_REPLY,
493 "CICAM_player_capabilities_reply",
494 )?;
495 if body.len() < CAPABILITIES_PREFIX {
496 return Err(Error::BufferTooShort {
497 need: CAPABILITIES_PREFIX,
498 have: body.len(),
499 what: "CICAM_player_capabilities_reply",
500 });
501 }
502 let n = u16::from_be_bytes([body[0], body[1]]) as usize;
503 let mut pos = CAPABILITIES_PREFIX;
504 let mut component_types = Vec::with_capacity(n);
505 for _ in 0..n {
506 if pos + COMPONENT_TYPE_LEN > body.len() {
507 return Err(Error::BufferTooShort {
508 need: pos + COMPONENT_TYPE_LEN,
509 have: body.len(),
510 what: "CICAM_player_capabilities_reply entry",
511 });
512 }
513 component_types.push(ComponentType {
514 stream_content: (body[pos] >> 4) & STREAM_CONTENT_MASK,
516 component_type: body[pos + 1],
517 });
518 pos += COMPONENT_TYPE_LEN;
519 }
520 Ok(Self { component_types })
521 }
522}
523impl Serialize for PlayerCapabilitiesReply {
524 type Error = Error;
525 fn serialized_len(&self) -> usize {
526 objects::apdu_len(CAPABILITIES_PREFIX + self.component_types.len() * COMPONENT_TYPE_LEN)
527 }
528 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
529 let body_len = CAPABILITIES_PREFIX + self.component_types.len() * COMPONENT_TYPE_LEN;
530 let mut pos = objects::write_apdu_header(tag::CAPABILITIES_REPLY, body_len, buf)?;
531 buf[pos..pos + 2].copy_from_slice(&(self.component_types.len() as u16).to_be_bytes());
532 pos += CAPABILITIES_PREFIX;
533 for c in &self.component_types {
534 buf[pos] = ((c.stream_content & STREAM_CONTENT_MASK) << 4) | 0x0F;
536 buf[pos + 1] = c.component_type;
537 pos += COMPONENT_TYPE_LEN;
538 }
539 Ok(pos)
540 }
541}
542
543#[derive(Debug, Clone, PartialEq, Eq)]
549#[cfg_attr(feature = "serde", derive(serde::Serialize))]
550pub struct PlayerStartReq<'a> {
551 pub input_max_bitrate: u16,
553 pub output_max_bitrate: u16,
555 pub linear_channel: bool,
557 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
559 pub pmt: &'a [u8],
560}
561
562const START_REQ_PREFIX: usize = 2 + 2 + 1 + 2;
564const LINEAR_CHANNEL_BIT: u8 = 0x80;
565
566impl<'a> Parse<'a> for PlayerStartReq<'a> {
567 type Error = Error;
568 fn parse(bytes: &'a [u8]) -> Result<Self> {
569 let body = objects::parse_apdu_header(bytes, tag::START_REQ, "CICAM_player_start_req")?;
570 if body.len() < START_REQ_PREFIX {
571 return Err(Error::BufferTooShort {
572 need: START_REQ_PREFIX,
573 have: body.len(),
574 what: "CICAM_player_start_req",
575 });
576 }
577 let input_max_bitrate = u16::from_be_bytes([body[0], body[1]]);
578 let output_max_bitrate = u16::from_be_bytes([body[2], body[3]]);
579 let linear_channel = body[4] & LINEAR_CHANNEL_BIT != 0;
580 let pmt_length = u16::from_be_bytes([body[5], body[6]]) as usize;
581 let end = START_REQ_PREFIX + pmt_length;
582 if body.len() < end {
583 return Err(Error::LengthMismatch {
584 what: "CICAM_player_start_req PMT",
585 declared: pmt_length,
586 actual: body.len().saturating_sub(START_REQ_PREFIX),
587 });
588 }
589 Ok(Self {
590 input_max_bitrate,
591 output_max_bitrate,
592 linear_channel,
593 pmt: &body[START_REQ_PREFIX..end],
594 })
595 }
596}
597impl Serialize for PlayerStartReq<'_> {
598 type Error = Error;
599 fn serialized_len(&self) -> usize {
600 objects::apdu_len(START_REQ_PREFIX + self.pmt.len())
601 }
602 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
603 let body_len = START_REQ_PREFIX + self.pmt.len();
604 let pos = objects::write_apdu_header(tag::START_REQ, body_len, buf)?;
605 buf[pos..pos + 2].copy_from_slice(&self.input_max_bitrate.to_be_bytes());
606 buf[pos + 2..pos + 4].copy_from_slice(&self.output_max_bitrate.to_be_bytes());
607 buf[pos + 4] = if self.linear_channel {
609 LINEAR_CHANNEL_BIT
610 } else {
611 0
612 };
613 buf[pos + 5..pos + 7].copy_from_slice(&(self.pmt.len() as u16).to_be_bytes());
614 buf[pos + START_REQ_PREFIX..pos + body_len].copy_from_slice(self.pmt);
615 Ok(pos + body_len)
616 }
617}
618
619#[derive(Debug, Clone, Copy, PartialEq, Eq)]
625#[cfg_attr(feature = "serde", derive(serde::Serialize))]
626pub struct PlayerStartReply {
627 pub lts_id: u8,
630 pub input_status: InputStatus,
632}
633
634const START_REPLY_BODY: usize = 2;
635
636impl<'a> Parse<'a> for PlayerStartReply {
637 type Error = Error;
638 fn parse(bytes: &'a [u8]) -> Result<Self> {
639 let body = objects::parse_apdu_header(bytes, tag::START_REPLY, "CICAM_player_start_reply")?;
640 if body.len() < START_REPLY_BODY {
641 return Err(Error::BufferTooShort {
642 need: START_REPLY_BODY,
643 have: body.len(),
644 what: "CICAM_player_start_reply",
645 });
646 }
647 Ok(Self {
648 lts_id: body[0],
649 input_status: InputStatus::from_u8(body[1]),
650 })
651 }
652}
653impl Serialize for PlayerStartReply {
654 type Error = Error;
655 fn serialized_len(&self) -> usize {
656 objects::apdu_len(START_REPLY_BODY)
657 }
658 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
659 let pos = objects::write_apdu_header(tag::START_REPLY, START_REPLY_BODY, buf)?;
660 buf[pos] = self.lts_id;
661 buf[pos + 1] = self.input_status.to_u8();
662 Ok(pos + START_REPLY_BODY)
663 }
664}
665
666#[derive(Debug, Clone, PartialEq, Eq)]
673#[cfg_attr(feature = "serde", derive(serde::Serialize))]
674pub struct PlayerPlayReq<'a> {
675 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
677 pub service_location: &'a [u8],
678}
679
680impl<'a> Parse<'a> for PlayerPlayReq<'a> {
681 type Error = Error;
682 fn parse(bytes: &'a [u8]) -> Result<Self> {
683 let body = objects::parse_apdu_header(bytes, tag::PLAY_REQ, "CICAM_player_play_req")?;
684 let service_location = parse_service_location(body, "CICAM_player_play_req")?;
685 Ok(Self { service_location })
686 }
687}
688impl Serialize for PlayerPlayReq<'_> {
689 type Error = Error;
690 fn serialized_len(&self) -> usize {
691 objects::apdu_len(SERVICE_LOCATION_PREFIX + self.service_location.len())
692 }
693 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
694 let body_len = SERVICE_LOCATION_PREFIX + self.service_location.len();
695 let pos = objects::write_apdu_header(tag::PLAY_REQ, body_len, buf)?;
696 write_service_location(self.service_location, &mut buf[pos..]);
697 Ok(pos + body_len)
698 }
699}
700
701#[derive(Debug, Clone, Copy, PartialEq, Eq)]
707#[cfg_attr(feature = "serde", derive(serde::Serialize))]
708pub struct PlayerStatusError {
709 pub valid_lts_id: bool,
711 pub lts_id: u8,
713 pub player_status: PlayStatus,
715}
716
717const STATUS_ERROR_BODY: usize = 3;
719const VALID_LTS_ID_BIT: u8 = 0x01;
720
721impl<'a> Parse<'a> for PlayerStatusError {
722 type Error = Error;
723 fn parse(bytes: &'a [u8]) -> Result<Self> {
724 let body =
725 objects::parse_apdu_header(bytes, tag::STATUS_ERROR, "CICAM_player_status_error")?;
726 if body.len() < STATUS_ERROR_BODY {
727 return Err(Error::BufferTooShort {
728 need: STATUS_ERROR_BODY,
729 have: body.len(),
730 what: "CICAM_player_status_error",
731 });
732 }
733 Ok(Self {
734 valid_lts_id: body[0] & VALID_LTS_ID_BIT != 0,
735 lts_id: body[1],
736 player_status: PlayStatus::from_u8(body[2]),
737 })
738 }
739}
740impl Serialize for PlayerStatusError {
741 type Error = Error;
742 fn serialized_len(&self) -> usize {
743 objects::apdu_len(STATUS_ERROR_BODY)
744 }
745 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
746 let pos = objects::write_apdu_header(tag::STATUS_ERROR, STATUS_ERROR_BODY, buf)?;
747 buf[pos] = if self.valid_lts_id {
749 VALID_LTS_ID_BIT
750 } else {
751 0
752 };
753 buf[pos + 1] = self.lts_id;
754 buf[pos + 2] = self.player_status.to_u8();
755 Ok(pos + STATUS_ERROR_BODY)
756 }
757}
758
759#[derive(Debug, Clone, Copy, PartialEq, Eq)]
765#[cfg_attr(feature = "serde", derive(serde::Serialize))]
766#[non_exhaustive]
767pub enum ControlCommand {
768 SetPosition {
770 seek_mode: SeekMode,
772 seek_position: i32,
774 },
775 SetSpeed {
777 speed: i16,
779 },
780 Reserved(u8),
782}
783impl ControlCommand {
784 #[must_use]
786 pub fn command(&self) -> u8 {
787 match self {
788 Self::SetPosition { .. } => 0x01,
789 Self::SetSpeed { .. } => 0x02,
790 Self::Reserved(v) => *v,
791 }
792 }
793}
794
795#[derive(Debug, Clone, Copy, PartialEq, Eq)]
797#[cfg_attr(feature = "serde", derive(serde::Serialize))]
798pub struct PlayerControlReq {
799 pub lts_id: u8,
801 pub command: ControlCommand,
803}
804
805const CONTROL_PREFIX: usize = 2;
807const CONTROL_CMD_SET_POSITION: u8 = 0x01;
808const CONTROL_CMD_SET_SPEED: u8 = 0x02;
809const SET_POSITION_EXTRA: usize = 1 + 4;
811const SET_SPEED_EXTRA: usize = 2;
812
813impl PlayerControlReq {
814 fn body_len(&self) -> usize {
815 CONTROL_PREFIX
816 + match self.command {
817 ControlCommand::SetPosition { .. } => SET_POSITION_EXTRA,
818 ControlCommand::SetSpeed { .. } => SET_SPEED_EXTRA,
819 ControlCommand::Reserved(_) => 0,
820 }
821 }
822}
823
824impl<'a> Parse<'a> for PlayerControlReq {
825 type Error = Error;
826 fn parse(bytes: &'a [u8]) -> Result<Self> {
827 let body = objects::parse_apdu_header(bytes, tag::CONTROL_REQ, "CICAM_player_control_req")?;
828 if body.len() < CONTROL_PREFIX {
829 return Err(Error::BufferTooShort {
830 need: CONTROL_PREFIX,
831 have: body.len(),
832 what: "CICAM_player_control_req",
833 });
834 }
835 let lts_id = body[0];
836 let command = match body[1] {
837 CONTROL_CMD_SET_POSITION => {
838 if body.len() < CONTROL_PREFIX + SET_POSITION_EXTRA {
839 return Err(Error::BufferTooShort {
840 need: CONTROL_PREFIX + SET_POSITION_EXTRA,
841 have: body.len(),
842 what: "CICAM_player_control_req set_position",
843 });
844 }
845 ControlCommand::SetPosition {
846 seek_mode: SeekMode::from_u8(body[2]),
847 seek_position: i32::from_be_bytes([body[3], body[4], body[5], body[6]]),
848 }
849 }
850 CONTROL_CMD_SET_SPEED => {
851 if body.len() < CONTROL_PREFIX + SET_SPEED_EXTRA {
852 return Err(Error::BufferTooShort {
853 need: CONTROL_PREFIX + SET_SPEED_EXTRA,
854 have: body.len(),
855 what: "CICAM_player_control_req set_speed",
856 });
857 }
858 ControlCommand::SetSpeed {
859 speed: i16::from_be_bytes([body[2], body[3]]),
860 }
861 }
862 other => ControlCommand::Reserved(other),
863 };
864 Ok(Self { lts_id, command })
865 }
866}
867impl Serialize for PlayerControlReq {
868 type Error = Error;
869 fn serialized_len(&self) -> usize {
870 objects::apdu_len(self.body_len())
871 }
872 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
873 let body_len = self.body_len();
874 let pos = objects::write_apdu_header(tag::CONTROL_REQ, body_len, buf)?;
875 buf[pos] = self.lts_id;
876 buf[pos + 1] = self.command.command();
877 match self.command {
878 ControlCommand::SetPosition {
879 seek_mode,
880 seek_position,
881 } => {
882 buf[pos + 2] = seek_mode.to_u8();
883 buf[pos + 3..pos + 7].copy_from_slice(&seek_position.to_be_bytes());
884 }
885 ControlCommand::SetSpeed { speed } => {
886 buf[pos + 2..pos + 4].copy_from_slice(&speed.to_be_bytes());
887 }
888 ControlCommand::Reserved(_) => {}
889 }
890 Ok(pos + body_len)
891 }
892}
893
894#[derive(Debug, Clone, Copy, PartialEq, Eq)]
900#[cfg_attr(feature = "serde", derive(serde::Serialize))]
901pub struct PlayerInfoReq {
902 pub lts_id: u8,
904}
905
906const LTS_ID_BODY: usize = 1;
907
908impl<'a> Parse<'a> for PlayerInfoReq {
909 type Error = Error;
910 fn parse(bytes: &'a [u8]) -> Result<Self> {
911 Ok(Self {
912 lts_id: parse_lts_id_body(bytes, tag::INFO_REQ, "CICAM_player_info_req")?,
913 })
914 }
915}
916impl Serialize for PlayerInfoReq {
917 type Error = Error;
918 fn serialized_len(&self) -> usize {
919 objects::apdu_len(LTS_ID_BODY)
920 }
921 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
922 serialize_lts_id_body(self.lts_id, tag::INFO_REQ, buf)
923 }
924}
925
926fn parse_lts_id_body(bytes: &[u8], expected: ApduTag, what: &'static str) -> Result<u8> {
927 let body = objects::parse_apdu_header(bytes, expected, what)?;
928 if body.len() < LTS_ID_BODY {
929 return Err(Error::BufferTooShort {
930 need: LTS_ID_BODY,
931 have: body.len(),
932 what,
933 });
934 }
935 Ok(body[0])
936}
937
938fn serialize_lts_id_body(lts_id: u8, tag: ApduTag, buf: &mut [u8]) -> Result<usize> {
939 let pos = objects::write_apdu_header(tag, LTS_ID_BODY, buf)?;
940 buf[pos] = lts_id;
941 Ok(pos + LTS_ID_BODY)
942}
943
944#[derive(Debug, Clone, Copy, PartialEq, Eq)]
950#[cfg_attr(feature = "serde", derive(serde::Serialize))]
951pub struct PlayerInfoReply {
952 pub lts_id: u8,
954 pub duration: u32,
956 pub position: u32,
958 pub speed: i16,
960}
961
962const INFO_REPLY_BODY: usize = 1 + 4 + 4 + 2;
964
965impl<'a> Parse<'a> for PlayerInfoReply {
966 type Error = Error;
967 fn parse(bytes: &'a [u8]) -> Result<Self> {
968 let body = objects::parse_apdu_header(bytes, tag::INFO_REPLY, "CICAM_player_info_reply")?;
969 if body.len() < INFO_REPLY_BODY {
970 return Err(Error::BufferTooShort {
971 need: INFO_REPLY_BODY,
972 have: body.len(),
973 what: "CICAM_player_info_reply",
974 });
975 }
976 Ok(Self {
977 lts_id: body[0],
978 duration: u32::from_be_bytes([body[1], body[2], body[3], body[4]]),
979 position: u32::from_be_bytes([body[5], body[6], body[7], body[8]]),
980 speed: i16::from_be_bytes([body[9], body[10]]),
981 })
982 }
983}
984impl Serialize for PlayerInfoReply {
985 type Error = Error;
986 fn serialized_len(&self) -> usize {
987 objects::apdu_len(INFO_REPLY_BODY)
988 }
989 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
990 let pos = objects::write_apdu_header(tag::INFO_REPLY, INFO_REPLY_BODY, buf)?;
991 buf[pos] = self.lts_id;
992 buf[pos + 1..pos + 5].copy_from_slice(&self.duration.to_be_bytes());
993 buf[pos + 5..pos + 9].copy_from_slice(&self.position.to_be_bytes());
994 buf[pos + 9..pos + 11].copy_from_slice(&self.speed.to_be_bytes());
995 Ok(pos + INFO_REPLY_BODY)
996 }
997}
998
999#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1005#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1006pub struct PlayerStop {
1007 pub lts_id: u8,
1009}
1010
1011impl<'a> Parse<'a> for PlayerStop {
1012 type Error = Error;
1013 fn parse(bytes: &'a [u8]) -> Result<Self> {
1014 Ok(Self {
1015 lts_id: parse_lts_id_body(bytes, tag::STOP, "CICAM_player_stop")?,
1016 })
1017 }
1018}
1019impl Serialize for PlayerStop {
1020 type Error = Error;
1021 fn serialized_len(&self) -> usize {
1022 objects::apdu_len(LTS_ID_BODY)
1023 }
1024 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
1025 serialize_lts_id_body(self.lts_id, tag::STOP, buf)
1026 }
1027}
1028
1029#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1031#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1032pub struct PlayerEnd {
1033 pub lts_id: u8,
1035}
1036
1037impl<'a> Parse<'a> for PlayerEnd {
1038 type Error = Error;
1039 fn parse(bytes: &'a [u8]) -> Result<Self> {
1040 Ok(Self {
1041 lts_id: parse_lts_id_body(bytes, tag::END, "CICAM_player_end")?,
1042 })
1043 }
1044}
1045impl Serialize for PlayerEnd {
1046 type Error = Error;
1047 fn serialized_len(&self) -> usize {
1048 objects::apdu_len(LTS_ID_BODY)
1049 }
1050 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
1051 serialize_lts_id_body(self.lts_id, tag::END, buf)
1052 }
1053}
1054
1055#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1061#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1062pub struct PlayerAssetEnd {
1063 pub lts_id: u8,
1065 pub beginning: bool,
1067}
1068
1069const ASSET_END_BODY: usize = 2;
1071const BEGINNING_BIT: u8 = 0x01;
1072const ASSET_END_RESERVED: u8 = 0x7F << 1;
1074
1075impl<'a> Parse<'a> for PlayerAssetEnd {
1076 type Error = Error;
1077 fn parse(bytes: &'a [u8]) -> Result<Self> {
1078 let body = objects::parse_apdu_header(bytes, tag::ASSET_END, "CICAM_player_asset_end")?;
1079 if body.len() < ASSET_END_BODY {
1080 return Err(Error::BufferTooShort {
1081 need: ASSET_END_BODY,
1082 have: body.len(),
1083 what: "CICAM_player_asset_end",
1084 });
1085 }
1086 Ok(Self {
1087 lts_id: body[0],
1088 beginning: body[1] & BEGINNING_BIT != 0,
1089 })
1090 }
1091}
1092impl Serialize for PlayerAssetEnd {
1093 type Error = Error;
1094 fn serialized_len(&self) -> usize {
1095 objects::apdu_len(ASSET_END_BODY)
1096 }
1097 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
1098 let pos = objects::write_apdu_header(tag::ASSET_END, ASSET_END_BODY, buf)?;
1099 buf[pos] = self.lts_id;
1100 buf[pos + 1] = ASSET_END_RESERVED | u8::from(self.beginning);
1102 Ok(pos + ASSET_END_BODY)
1103 }
1104}
1105
1106#[derive(Debug, Clone, PartialEq, Eq)]
1112#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1113pub struct PlayerUpdateReq<'a> {
1114 pub lts_id: u8,
1116 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
1118 pub pmt: &'a [u8],
1119}
1120
1121const UPDATE_REQ_PREFIX: usize = 1 + 2;
1123
1124impl<'a> Parse<'a> for PlayerUpdateReq<'a> {
1125 type Error = Error;
1126 fn parse(bytes: &'a [u8]) -> Result<Self> {
1127 let body = objects::parse_apdu_header(bytes, tag::UPDATE_REQ, "CICAM_player_update_req")?;
1128 if body.len() < UPDATE_REQ_PREFIX {
1129 return Err(Error::BufferTooShort {
1130 need: UPDATE_REQ_PREFIX,
1131 have: body.len(),
1132 what: "CICAM_player_update_req",
1133 });
1134 }
1135 let lts_id = body[0];
1136 let pmt_length = u16::from_be_bytes([body[1], body[2]]) as usize;
1137 let end = UPDATE_REQ_PREFIX + pmt_length;
1138 if body.len() < end {
1139 return Err(Error::LengthMismatch {
1140 what: "CICAM_player_update_req PMT",
1141 declared: pmt_length,
1142 actual: body.len().saturating_sub(UPDATE_REQ_PREFIX),
1143 });
1144 }
1145 Ok(Self {
1146 lts_id,
1147 pmt: &body[UPDATE_REQ_PREFIX..end],
1148 })
1149 }
1150}
1151impl Serialize for PlayerUpdateReq<'_> {
1152 type Error = Error;
1153 fn serialized_len(&self) -> usize {
1154 objects::apdu_len(UPDATE_REQ_PREFIX + self.pmt.len())
1155 }
1156 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
1157 let body_len = UPDATE_REQ_PREFIX + self.pmt.len();
1158 let pos = objects::write_apdu_header(tag::UPDATE_REQ, body_len, buf)?;
1159 buf[pos] = self.lts_id;
1160 buf[pos + 1..pos + 3].copy_from_slice(&(self.pmt.len() as u16).to_be_bytes());
1161 buf[pos + UPDATE_REQ_PREFIX..pos + body_len].copy_from_slice(self.pmt);
1162 Ok(pos + body_len)
1163 }
1164}
1165
1166#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1175#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1176pub struct PlayerUpdateReply {
1177 pub lts_id: u8,
1179 pub update_status: UpdateStatus,
1181}
1182
1183const UPDATE_REPLY_BODY: usize = 2;
1184
1185impl<'a> Parse<'a> for PlayerUpdateReply {
1186 type Error = Error;
1187 fn parse(bytes: &'a [u8]) -> Result<Self> {
1188 let body =
1189 objects::parse_apdu_header(bytes, tag::UPDATE_REPLY, "CICAM_player_update_reply")?;
1190 if body.len() < UPDATE_REPLY_BODY {
1191 return Err(Error::BufferTooShort {
1192 need: UPDATE_REPLY_BODY,
1193 have: body.len(),
1194 what: "CICAM_player_update_reply",
1195 });
1196 }
1197 Ok(Self {
1198 lts_id: body[0],
1199 update_status: UpdateStatus::from_u8(body[1]),
1200 })
1201 }
1202}
1203impl Serialize for PlayerUpdateReply {
1204 type Error = Error;
1205 fn serialized_len(&self) -> usize {
1206 objects::apdu_len(UPDATE_REPLY_BODY)
1207 }
1208 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
1209 let pos = objects::write_apdu_header(tag::UPDATE_REPLY, UPDATE_REPLY_BODY, buf)?;
1210 buf[pos] = self.lts_id;
1211 buf[pos + 1] = self.update_status.to_u8();
1212 Ok(pos + UPDATE_REPLY_BODY)
1213 }
1214}
1215
1216#[derive(Debug, Clone, PartialEq, Eq)]
1222#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1223#[non_exhaustive]
1224pub enum CicamPlayerApdu<'a> {
1225 VerifyReq(#[cfg_attr(feature = "serde", serde(borrow))] PlayerVerifyReq<'a>),
1227 VerifyReply(PlayerVerifyReply),
1229 CapabilitiesReq(PlayerCapabilitiesReq),
1231 CapabilitiesReply(PlayerCapabilitiesReply),
1233 StartReq(#[cfg_attr(feature = "serde", serde(borrow))] PlayerStartReq<'a>),
1235 StartReply(PlayerStartReply),
1237 PlayReq(#[cfg_attr(feature = "serde", serde(borrow))] PlayerPlayReq<'a>),
1239 StatusError(PlayerStatusError),
1241 ControlReq(PlayerControlReq),
1243 InfoReq(PlayerInfoReq),
1245 InfoReply(PlayerInfoReply),
1247 Stop(PlayerStop),
1249 End(PlayerEnd),
1251 AssetEnd(PlayerAssetEnd),
1253 UpdateReq(#[cfg_attr(feature = "serde", serde(borrow))] PlayerUpdateReq<'a>),
1255 UpdateReply(PlayerUpdateReply),
1257}
1258
1259impl<'a> CicamPlayerApdu<'a> {
1260 pub fn parse(body: &'a [u8]) -> Result<Self> {
1262 if body.len() < 3 {
1263 return Err(Error::BufferTooShort {
1264 need: 3,
1265 have: body.len(),
1266 what: "cicam_player apdu_tag",
1267 });
1268 }
1269 let t = ApduTag::from_bytes(body[0], body[1], body[2]);
1270 match t {
1271 tag::VERIFY_REQ => Ok(Self::VerifyReq(PlayerVerifyReq::parse(body)?)),
1272 tag::VERIFY_REPLY => Ok(Self::VerifyReply(PlayerVerifyReply::parse(body)?)),
1273 tag::CAPABILITIES_REQ => Ok(Self::CapabilitiesReq(PlayerCapabilitiesReq::parse(body)?)),
1274 tag::CAPABILITIES_REPLY => Ok(Self::CapabilitiesReply(PlayerCapabilitiesReply::parse(
1275 body,
1276 )?)),
1277 tag::START_REQ => Ok(Self::StartReq(PlayerStartReq::parse(body)?)),
1278 tag::START_REPLY => Ok(Self::StartReply(PlayerStartReply::parse(body)?)),
1279 tag::PLAY_REQ => Ok(Self::PlayReq(PlayerPlayReq::parse(body)?)),
1280 tag::STATUS_ERROR => Ok(Self::StatusError(PlayerStatusError::parse(body)?)),
1281 tag::CONTROL_REQ => Ok(Self::ControlReq(PlayerControlReq::parse(body)?)),
1282 tag::INFO_REQ => Ok(Self::InfoReq(PlayerInfoReq::parse(body)?)),
1283 tag::INFO_REPLY => Ok(Self::InfoReply(PlayerInfoReply::parse(body)?)),
1284 tag::STOP => Ok(Self::Stop(PlayerStop::parse(body)?)),
1285 tag::END => Ok(Self::End(PlayerEnd::parse(body)?)),
1286 tag::ASSET_END => Ok(Self::AssetEnd(PlayerAssetEnd::parse(body)?)),
1287 tag::UPDATE_REQ => Ok(Self::UpdateReq(PlayerUpdateReq::parse(body)?)),
1288 tag::UPDATE_REPLY => Ok(Self::UpdateReply(PlayerUpdateReply::parse(body)?)),
1289 _ => Err(Error::UnexpectedApduTag {
1290 got: t.as_u24(),
1291 expected: tag::VERIFY_REQ.as_u24(),
1292 what: "cicam_player",
1293 }),
1294 }
1295 }
1296}
1297
1298impl Serialize for CicamPlayerApdu<'_> {
1299 type Error = Error;
1300 fn serialized_len(&self) -> usize {
1301 match self {
1302 Self::VerifyReq(o) => o.serialized_len(),
1303 Self::VerifyReply(o) => o.serialized_len(),
1304 Self::CapabilitiesReq(o) => o.serialized_len(),
1305 Self::CapabilitiesReply(o) => o.serialized_len(),
1306 Self::StartReq(o) => o.serialized_len(),
1307 Self::StartReply(o) => o.serialized_len(),
1308 Self::PlayReq(o) => o.serialized_len(),
1309 Self::StatusError(o) => o.serialized_len(),
1310 Self::ControlReq(o) => o.serialized_len(),
1311 Self::InfoReq(o) => o.serialized_len(),
1312 Self::InfoReply(o) => o.serialized_len(),
1313 Self::Stop(o) => o.serialized_len(),
1314 Self::End(o) => o.serialized_len(),
1315 Self::AssetEnd(o) => o.serialized_len(),
1316 Self::UpdateReq(o) => o.serialized_len(),
1317 Self::UpdateReply(o) => o.serialized_len(),
1318 }
1319 }
1320 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
1321 match self {
1322 Self::VerifyReq(o) => o.serialize_into(buf),
1323 Self::VerifyReply(o) => o.serialize_into(buf),
1324 Self::CapabilitiesReq(o) => o.serialize_into(buf),
1325 Self::CapabilitiesReply(o) => o.serialize_into(buf),
1326 Self::StartReq(o) => o.serialize_into(buf),
1327 Self::StartReply(o) => o.serialize_into(buf),
1328 Self::PlayReq(o) => o.serialize_into(buf),
1329 Self::StatusError(o) => o.serialize_into(buf),
1330 Self::ControlReq(o) => o.serialize_into(buf),
1331 Self::InfoReq(o) => o.serialize_into(buf),
1332 Self::InfoReply(o) => o.serialize_into(buf),
1333 Self::Stop(o) => o.serialize_into(buf),
1334 Self::End(o) => o.serialize_into(buf),
1335 Self::AssetEnd(o) => o.serialize_into(buf),
1336 Self::UpdateReq(o) => o.serialize_into(buf),
1337 Self::UpdateReply(o) => o.serialize_into(buf),
1338 }
1339 }
1340}
1341
1342#[cfg(test)]
1343mod tests {
1344 use super::*;
1345
1346 #[test]
1347 fn verify_req_round_trips_and_bites() {
1348 let req = PlayerVerifyReq {
1349 service_location: &[0x3C, 0x53, 0x4C, 0x3E], };
1351 let bytes = req.to_bytes();
1352 assert_eq!(
1354 bytes,
1355 [0x9F, 0xA0, 0x00, 0x06, 0x00, 0x04, 0x3C, 0x53, 0x4C, 0x3E]
1356 );
1357 assert_eq!(PlayerVerifyReq::parse(&bytes).unwrap(), req);
1358 let other = PlayerVerifyReq {
1359 service_location: &[0x3C, 0x53, 0x4C, 0x00],
1360 };
1361 assert_ne!(bytes, other.to_bytes());
1362 }
1363
1364 #[test]
1365 fn verify_reply_round_trips() {
1366 let r = PlayerVerifyReply {
1367 player_verify_status: PlayerVerifyStatus::Error,
1368 };
1369 let bytes = r.to_bytes();
1370 assert_eq!(bytes, [0x9F, 0xA0, 0x01, 0x01, 0x01]);
1371 assert_eq!(PlayerVerifyReply::parse(&bytes).unwrap(), r);
1372 }
1373
1374 #[test]
1375 fn capabilities_req_round_trips() {
1376 let bytes = PlayerCapabilitiesReq.to_bytes();
1377 assert_eq!(bytes, [0x9F, 0xA0, 0x02, 0x00]);
1378 assert_eq!(
1379 PlayerCapabilitiesReq::parse(&bytes).unwrap(),
1380 PlayerCapabilitiesReq
1381 );
1382 }
1383
1384 #[test]
1385 fn capabilities_reply_two_entries() {
1386 let r = PlayerCapabilitiesReply {
1387 component_types: alloc::vec![
1388 ComponentType {
1389 stream_content: 0x01,
1390 component_type: 0x03,
1391 },
1392 ComponentType {
1393 stream_content: 0x02,
1394 component_type: 0x05,
1395 },
1396 ],
1397 };
1398 let bytes = r.to_bytes();
1399 assert_eq!(bytes[0..4], [0x9F, 0xA0, 0x03, 0x06]);
1401 assert_eq!(&bytes[4..6], &[0x00, 0x02]);
1402 assert_eq!(&bytes[6..10], &[0x1F, 0x03, 0x2F, 0x05]);
1403 assert_eq!(PlayerCapabilitiesReply::parse(&bytes).unwrap(), r);
1404 let mut other = r.clone();
1405 other.component_types[0].component_type = 0x04;
1406 assert_ne!(bytes, other.to_bytes());
1407 }
1408
1409 #[test]
1410 fn start_req_round_trips_and_bites() {
1411 let req = PlayerStartReq {
1413 input_max_bitrate: 0x0034,
1414 output_max_bitrate: 0x0040,
1415 linear_channel: true,
1416 pmt: &[0x02, 0xB0, 0x12],
1417 };
1418 let bytes = req.to_bytes();
1419 assert_eq!(bytes[0..4], [0x9F, 0xA0, 0x04, 0x0A]);
1421 assert_eq!(&bytes[4..6], &[0x00, 0x34]); assert_eq!(&bytes[6..8], &[0x00, 0x40]); assert_eq!(bytes[8], 0x80); assert_eq!(&bytes[9..11], &[0x00, 0x03]); assert_eq!(&bytes[11..14], &[0x02, 0xB0, 0x12]);
1426 assert_eq!(PlayerStartReq::parse(&bytes).unwrap(), req);
1427 let mut other = req.clone();
1428 other.linear_channel = false;
1429 assert_eq!(other.to_bytes()[8], 0x00);
1430 assert_ne!(bytes, other.to_bytes());
1431 }
1432
1433 #[test]
1434 fn start_reply_round_trips() {
1435 let r = PlayerStartReply {
1436 lts_id: 0x07,
1437 input_status: InputStatus::InsufficientBitrate,
1438 };
1439 let bytes = r.to_bytes();
1440 assert_eq!(bytes, [0x9F, 0xA0, 0x05, 0x02, 0x07, 0x02]);
1441 assert_eq!(PlayerStartReply::parse(&bytes).unwrap(), r);
1442 }
1443
1444 #[test]
1445 fn play_req_round_trips() {
1446 let req = PlayerPlayReq {
1447 service_location: &[0xAA, 0xBB],
1448 };
1449 let bytes = req.to_bytes();
1450 assert_eq!(bytes, [0x9F, 0xA0, 0x06, 0x04, 0x00, 0x02, 0xAA, 0xBB]);
1451 assert_eq!(PlayerPlayReq::parse(&bytes).unwrap(), req);
1452 }
1453
1454 #[test]
1455 fn status_error_round_trips_and_bites() {
1456 let e = PlayerStatusError {
1457 valid_lts_id: true,
1458 lts_id: 0x09,
1459 player_status: PlayStatus::ContentBlocked,
1460 };
1461 let bytes = e.to_bytes();
1462 assert_eq!(bytes, [0x9F, 0xA0, 0x07, 0x03, 0x01, 0x09, 0x03]);
1464 assert_eq!(PlayerStatusError::parse(&bytes).unwrap(), e);
1465 let mut other = e;
1466 other.valid_lts_id = false;
1467 assert_eq!(other.to_bytes()[4], 0x00);
1468 assert_ne!(bytes, other.to_bytes());
1469 }
1470
1471 #[test]
1472 fn control_req_set_position_round_trips_and_bites() {
1473 let c = PlayerControlReq {
1474 lts_id: 0x03,
1475 command: ControlCommand::SetPosition {
1476 seek_mode: SeekMode::Relative,
1477 seek_position: -1000,
1478 },
1479 };
1480 let bytes = c.to_bytes();
1481 assert_eq!(bytes[0..4], [0x9F, 0xA0, 0x08, 0x07]);
1483 assert_eq!(bytes[4], 0x03);
1484 assert_eq!(bytes[5], 0x01); assert_eq!(bytes[6], 0x01); assert_eq!(&bytes[7..11], &(-1000i32).to_be_bytes());
1487 assert_eq!(PlayerControlReq::parse(&bytes).unwrap(), c);
1488 let mut other = c;
1489 other.command = ControlCommand::SetPosition {
1490 seek_mode: SeekMode::Absolute,
1491 seek_position: -1000,
1492 };
1493 assert_ne!(bytes, other.to_bytes());
1494 }
1495
1496 #[test]
1497 fn control_req_set_speed_round_trips() {
1498 let c = PlayerControlReq {
1499 lts_id: 0x01,
1500 command: ControlCommand::SetSpeed { speed: -100 },
1501 };
1502 let bytes = c.to_bytes();
1503 assert_eq!(bytes[0..4], [0x9F, 0xA0, 0x08, 0x04]);
1505 assert_eq!(bytes[4], 0x01);
1506 assert_eq!(bytes[5], 0x02);
1507 assert_eq!(&bytes[6..8], &(-100i16).to_be_bytes());
1508 assert_eq!(PlayerControlReq::parse(&bytes).unwrap(), c);
1509 }
1510
1511 #[test]
1512 fn info_req_reply_round_trip() {
1513 let req = PlayerInfoReq { lts_id: 0x05 };
1514 let rb = req.to_bytes();
1515 assert_eq!(rb, [0x9F, 0xA0, 0x09, 0x01, 0x05]);
1516 assert_eq!(PlayerInfoReq::parse(&rb).unwrap(), req);
1517
1518 let reply = PlayerInfoReply {
1519 lts_id: 0x05,
1520 duration: 0xFFFF_FFFF,
1521 position: 0x0000_003C,
1522 speed: 100,
1523 };
1524 let bytes = reply.to_bytes();
1525 assert_eq!(bytes[0..4], [0x9F, 0xA0, 0x0A, 0x0B]);
1527 assert_eq!(bytes[4], 0x05);
1528 assert_eq!(&bytes[5..9], &[0xFF, 0xFF, 0xFF, 0xFF]);
1529 assert_eq!(&bytes[9..13], &[0x00, 0x00, 0x00, 0x3C]);
1530 assert_eq!(&bytes[13..15], &100i16.to_be_bytes());
1531 assert_eq!(PlayerInfoReply::parse(&bytes).unwrap(), reply);
1532 }
1533
1534 #[test]
1535 fn stop_end_round_trip() {
1536 let stop = PlayerStop { lts_id: 0x02 };
1537 assert_eq!(stop.to_bytes(), [0x9F, 0xA0, 0x0B, 0x01, 0x02]);
1538 assert_eq!(PlayerStop::parse(&stop.to_bytes()).unwrap(), stop);
1539
1540 let end = PlayerEnd { lts_id: 0x02 };
1541 assert_eq!(end.to_bytes(), [0x9F, 0xA0, 0x0C, 0x01, 0x02]);
1542 assert_eq!(PlayerEnd::parse(&end.to_bytes()).unwrap(), end);
1543 }
1544
1545 #[test]
1546 fn asset_end_round_trips_and_reserved_is_7f() {
1547 let e = PlayerAssetEnd {
1548 lts_id: 0x04,
1549 beginning: true,
1550 };
1551 let bytes = e.to_bytes();
1552 assert_eq!(bytes, [0x9F, 0xA0, 0x0D, 0x02, 0x04, 0xFF]);
1554 assert_eq!(PlayerAssetEnd::parse(&bytes).unwrap(), e);
1555 let other = PlayerAssetEnd {
1556 lts_id: 0x04,
1557 beginning: false,
1558 };
1559 assert_eq!(other.to_bytes(), [0x9F, 0xA0, 0x0D, 0x02, 0x04, 0xFE]);
1561 }
1562
1563 #[test]
1564 fn update_req_round_trips_two_byte_pmt() {
1565 let req = PlayerUpdateReq {
1566 lts_id: 0x08,
1567 pmt: &[0x02, 0xB0],
1568 };
1569 let bytes = req.to_bytes();
1570 assert_eq!(
1572 bytes,
1573 [0x9F, 0xA0, 0x0E, 0x05, 0x08, 0x00, 0x02, 0x02, 0xB0]
1574 );
1575 assert_eq!(PlayerUpdateReq::parse(&bytes).unwrap(), req);
1576 }
1577
1578 #[test]
1579 fn update_reply_uses_9fa00f_not_start_reply_slip() {
1580 let r = PlayerUpdateReply {
1581 lts_id: 0x06,
1582 update_status: UpdateStatus::RequestRefused,
1583 };
1584 let bytes = r.to_bytes();
1585 assert_eq!(&bytes[0..3], &[0x9F, 0xA0, 0x0F]);
1587 assert_ne!(&bytes[0..3], &[0x9F, 0xA0, 0x05]); assert_eq!(bytes, [0x9F, 0xA0, 0x0F, 0x02, 0x06, 0x01]);
1589 assert_eq!(PlayerUpdateReply::parse(&bytes).unwrap(), r);
1590 assert_eq!(tag::UPDATE_REPLY.as_u24(), 0x009F_A00F);
1591 }
1592
1593 #[test]
1594 fn dispatch_routes_every_tag() {
1595 let cases: alloc::vec::Vec<alloc::vec::Vec<u8>> = alloc::vec![
1596 PlayerVerifyReq {
1597 service_location: &[]
1598 }
1599 .to_bytes(),
1600 PlayerVerifyReply {
1601 player_verify_status: PlayerVerifyStatus::Ok
1602 }
1603 .to_bytes(),
1604 PlayerCapabilitiesReq.to_bytes(),
1605 PlayerCapabilitiesReply {
1606 component_types: alloc::vec![]
1607 }
1608 .to_bytes(),
1609 PlayerStartReq {
1610 input_max_bitrate: 0,
1611 output_max_bitrate: 0,
1612 linear_channel: false,
1613 pmt: &[]
1614 }
1615 .to_bytes(),
1616 PlayerStartReply {
1617 lts_id: 0,
1618 input_status: InputStatus::Ok
1619 }
1620 .to_bytes(),
1621 PlayerPlayReq {
1622 service_location: &[]
1623 }
1624 .to_bytes(),
1625 PlayerStatusError {
1626 valid_lts_id: false,
1627 lts_id: 0,
1628 player_status: PlayStatus::Unrecoverable
1629 }
1630 .to_bytes(),
1631 PlayerControlReq {
1632 lts_id: 0,
1633 command: ControlCommand::SetSpeed { speed: 0 }
1634 }
1635 .to_bytes(),
1636 PlayerInfoReq { lts_id: 0 }.to_bytes(),
1637 PlayerInfoReply {
1638 lts_id: 0,
1639 duration: 0,
1640 position: 0,
1641 speed: 0
1642 }
1643 .to_bytes(),
1644 PlayerStop { lts_id: 0 }.to_bytes(),
1645 PlayerEnd { lts_id: 0 }.to_bytes(),
1646 PlayerAssetEnd {
1647 lts_id: 0,
1648 beginning: false
1649 }
1650 .to_bytes(),
1651 PlayerUpdateReq {
1652 lts_id: 0,
1653 pmt: &[0x00]
1654 }
1655 .to_bytes(),
1656 PlayerUpdateReply {
1657 lts_id: 0,
1658 update_status: UpdateStatus::Ok
1659 }
1660 .to_bytes(),
1661 ];
1662 for c in &cases {
1663 let parsed = CicamPlayerApdu::parse(c).unwrap();
1664 assert_eq!(&parsed.to_bytes(), c);
1665 }
1666 assert!(matches!(
1668 CicamPlayerApdu::parse(&[0x9F, 0xA0, 0x7E, 0x00]),
1669 Err(Error::UnexpectedApduTag { .. })
1670 ));
1671 }
1672}