dvb_si/descriptors/
subtitling.rs1use crate::error::{Error, Result};
8use crate::text::LangCode;
9use crate::traits::Descriptor;
10use dvb_common::{Parse, Serialize};
11
12pub const TAG: u8 = 0x59;
14const HEADER_LEN: usize = 2;
15const ENTRY_LEN: usize = 8;
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize))]
20pub struct SubtitlingEntry {
21 pub language_code: LangCode,
23 pub subtitling_type: u8,
26 pub composition_page_id: u16,
28 pub ancillary_page_id: u16,
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize))]
35pub struct SubtitlingDescriptor {
36 pub entries: Vec<SubtitlingEntry>,
38}
39
40impl<'a> Parse<'a> for SubtitlingDescriptor {
41 type Error = crate::error::Error;
42 fn parse(bytes: &'a [u8]) -> Result<Self> {
43 if bytes.len() < HEADER_LEN {
44 return Err(Error::BufferTooShort {
45 need: HEADER_LEN,
46 have: bytes.len(),
47 what: "SubtitlingDescriptor header",
48 });
49 }
50 if bytes[0] != TAG {
51 return Err(Error::InvalidDescriptor {
52 tag: bytes[0],
53 reason: "unexpected tag for subtitling_descriptor",
54 });
55 }
56 let length = bytes[1] as usize;
57 if bytes.len() < HEADER_LEN + length {
58 return Err(Error::BufferTooShort {
59 need: HEADER_LEN + length,
60 have: bytes.len(),
61 what: "SubtitlingDescriptor body",
62 });
63 }
64 if length % ENTRY_LEN != 0 {
65 return Err(Error::InvalidDescriptor {
66 tag: TAG,
67 reason: "subtitling_descriptor length must be a multiple of 8",
68 });
69 }
70 let body = &bytes[HEADER_LEN..HEADER_LEN + length];
71 let mut entries = Vec::with_capacity(length / ENTRY_LEN);
72 for chunk in body.chunks_exact(ENTRY_LEN) {
73 entries.push(SubtitlingEntry {
74 language_code: LangCode([chunk[0], chunk[1], chunk[2]]),
75 subtitling_type: chunk[3],
76 composition_page_id: u16::from_be_bytes([chunk[4], chunk[5]]),
77 ancillary_page_id: u16::from_be_bytes([chunk[6], chunk[7]]),
78 });
79 }
80 Ok(Self { entries })
81 }
82}
83
84impl Serialize for SubtitlingDescriptor {
85 type Error = crate::error::Error;
86 fn serialized_len(&self) -> usize {
87 HEADER_LEN + self.entries.len() * ENTRY_LEN
88 }
89
90 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
91 let len = self.serialized_len();
92 if buf.len() < len {
93 return Err(Error::OutputBufferTooSmall {
94 need: len,
95 have: buf.len(),
96 });
97 }
98 buf[0] = TAG;
99 buf[1] = (self.entries.len() * ENTRY_LEN) as u8;
100 let mut pos = HEADER_LEN;
101 for e in &self.entries {
102 buf[pos..pos + 3].copy_from_slice(&e.language_code.0);
103 buf[pos + 3] = e.subtitling_type;
104 buf[pos + 4..pos + 6].copy_from_slice(&e.composition_page_id.to_be_bytes());
105 buf[pos + 6..pos + 8].copy_from_slice(&e.ancillary_page_id.to_be_bytes());
106 pos += ENTRY_LEN;
107 }
108 Ok(len)
109 }
110}
111
112impl<'a> Descriptor<'a> for SubtitlingDescriptor {
113 const TAG: u8 = TAG;
114 fn descriptor_length(&self) -> u8 {
115 (self.entries.len() * ENTRY_LEN) as u8
116 }
117}
118
119impl<'a> crate::traits::DescriptorDef<'a> for SubtitlingDescriptor {
120 const TAG: u8 = TAG;
121 const NAME: &'static str = "SUBTITLING";
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn parse_single_entry() {
130 let bytes = [TAG, 8, b'e', b'n', b'g', 0x10, 0x00, 0x01, 0x00, 0x02];
131 let d = SubtitlingDescriptor::parse(&bytes).unwrap();
132 assert_eq!(d.entries.len(), 1);
133 assert_eq!(d.entries[0].language_code, LangCode(*b"eng"));
134 assert_eq!(d.entries[0].subtitling_type, 0x10);
135 assert_eq!(d.entries[0].composition_page_id, 1);
136 assert_eq!(d.entries[0].ancillary_page_id, 2);
137 }
138
139 #[test]
140 fn parse_rejects_wrong_tag() {
141 assert!(matches!(
142 SubtitlingDescriptor::parse(&[0x5A, 0]).unwrap_err(),
143 Error::InvalidDescriptor { tag: 0x5A, .. }
144 ));
145 }
146
147 #[test]
148 fn parse_rejects_length_not_multiple_of_8() {
149 let bytes = [TAG, 7, 0, 0, 0, 0, 0, 0, 0];
150 assert!(matches!(
151 SubtitlingDescriptor::parse(&bytes).unwrap_err(),
152 Error::InvalidDescriptor { .. }
153 ));
154 }
155
156 #[test]
157 fn serialize_round_trip() {
158 let d = SubtitlingDescriptor {
159 entries: vec![
160 SubtitlingEntry {
161 language_code: LangCode(*b"fra"),
162 subtitling_type: 0x10,
163 composition_page_id: 0x1234,
164 ancillary_page_id: 0x5678,
165 },
166 SubtitlingEntry {
167 language_code: LangCode(*b"deu"),
168 subtitling_type: 0x20,
169 composition_page_id: 0,
170 ancillary_page_id: 0,
171 },
172 ],
173 };
174 let mut buf = vec![0u8; d.serialized_len()];
175 d.serialize_into(&mut buf).unwrap();
176 assert_eq!(SubtitlingDescriptor::parse(&buf).unwrap(), d);
177 }
178
179 #[test]
180 fn empty_descriptor_valid() {
181 let d = SubtitlingDescriptor::parse(&[TAG, 0]).unwrap();
182 assert_eq!(d.entries.len(), 0);
183 }
184}