1use std::io;
4
5use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event};
6use quick_xml::{Reader, Writer};
7
8use crate::duration::xs_duration;
9use crate::error::MpdError;
10use crate::model::*;
11use crate::parse::{INFINITY_TOKEN, NEGATIVE_INFINITY_TOKEN};
12use crate::xs_datetime::format_xs_datetime;
13
14const ROOT_NAME: &str = "MPD";
15const BARE_ROOT_TAG: &str = "<MPD>";
16const MPD_ROOT_SLACK: usize = 256;
17const PREFIX_SEPARATOR: char = ':';
18const PREFIX_DVB: &str = "dvb";
19const PREFIX_XLINK: &str = "xlink";
20const PREFIX_URLPARAM: &str = "urlparam";
21const PREFIX_CENC: &str = "cenc";
22const PREFIX_XMLNS: &str = "xmlns";
23const PREFIX_XML: &str = "xml";
24const NS_MPD: &str = "urn:mpeg:dash:schema:mpd:2011";
25const NS_DVB: &str = "urn:dvb:dash:dash-extensions:2014-1";
26const NS_CENC: &str = "urn:mpeg:cenc:2013";
27const NS_XLINK: &str = "http://www.w3.org/1999/xlink";
28const NS_URLPARAM: &str = "urn:mpeg:dash:schema:urlparam:2014";
29
30#[derive(Debug, Default, PartialEq, Eq)]
31struct Prefixes {
32 dvb: bool,
33 xlink: bool,
34 urlparam: bool,
35 cenc: bool,
36}
37
38impl Prefixes {
39 fn emitted_by(xml: &str) -> Result<Self, MpdError> {
40 let mut found = Self::default();
41 let mut reader = Reader::from_str(xml);
42 loop {
43 let event = reader.read_event().map_err(|e| MpdError::Invalid {
44 what: "written MPD",
45 detail: e.to_string(),
46 })?;
47 let start = match &event {
48 Event::Eof => return Ok(found),
49 Event::Start(e) | Event::Empty(e) => e,
50 _ => continue,
51 };
52 found.note(start.name().as_ref())?;
53 for attr in start.attributes() {
54 let attr = attr.map_err(|e| MpdError::Invalid {
55 what: "written MPD",
56 detail: e.to_string(),
57 })?;
58 found.note(attr.key.as_ref())?;
59 }
60 }
61 }
62
63 fn note(&mut self, qname: &str) -> Result<(), MpdError> {
64 let Some((prefix, _)) = qname.split_once(PREFIX_SEPARATOR) else {
65 return Ok(());
66 };
67 match prefix {
68 PREFIX_DVB => self.dvb = true,
69 PREFIX_XLINK => self.xlink = true,
70 PREFIX_URLPARAM => self.urlparam = true,
71 PREFIX_CENC => self.cenc = true,
72 PREFIX_XMLNS | PREFIX_XML => {}
73 other => {
74 return Err(MpdError::Invalid {
75 what: "namespace prefix",
76 detail: other.to_owned(),
77 });
78 }
79 }
80 Ok(())
81 }
82}
83
84impl Mpd {
85 pub fn to_xml(&self) -> Result<String, MpdError> {
92 let mut w = Writer::new_with_indent(Vec::new(), b' ', 2);
93 let _ = w.write_event(Event::Decl(BytesDecl::new("1.0", Some("utf-8"), None)));
94 w.write_event(Event::Start(BytesStart::new(ROOT_NAME)))
95 .map_err(io_err)?;
96 let bare_len = w.get_ref().len();
97 self.write_body(&mut w)?;
98 w.write_event(Event::End(BytesEnd::new(ROOT_NAME)))
99 .map_err(io_err)?;
100 let doc = w.into_inner();
101 let root_at = bare_len - BARE_ROOT_TAG.len();
102 let (head_sep, rest) = (&doc[..root_at], &doc[bare_len..]);
103 let used = Prefixes::emitted_by(&String::from_utf8_lossy(&doc[root_at..]))?;
104 let mut root_writer = Writer::new_with_indent(Vec::new(), b' ', 2);
105 root_writer
106 .write_event(Event::Start(self.root_start(&used)?))
107 .map_err(io_err)?;
108 let mut out = Vec::with_capacity(doc.len() + MPD_ROOT_SLACK);
109 out.extend_from_slice(head_sep);
110 out.extend_from_slice(&root_writer.into_inner());
111 out.extend_from_slice(rest);
112 let mut out = String::from_utf8_lossy(&out).into_owned();
113 out.push('\n');
114 Ok(out)
115 }
116
117 fn root_start(&self, ns: &Prefixes) -> Result<BytesStart<'static>, MpdError> {
118 let mut root = BytesStart::new(ROOT_NAME);
119 root.push_attribute(("xmlns", NS_MPD));
120 if ns.dvb {
121 root.push_attribute(("xmlns:dvb", NS_DVB));
122 }
123 if ns.xlink {
124 root.push_attribute(("xmlns:xlink", NS_XLINK));
125 }
126 if ns.urlparam {
127 root.push_attribute(("xmlns:urlparam", NS_URLPARAM));
128 }
129 if !self.profiles.is_empty() {
130 root.push_attribute(("profiles", self.profiles.join(",").as_str()));
131 }
132 root.push_attribute(("type", self.kind.name()));
133 if let Some(min_buffer) = self.min_buffer_time.map(xs_duration) {
134 root.push_attribute(("minBufferTime", min_buffer.as_str()));
135 }
136 if ns.cenc {
137 root.push_attribute(("xmlns:cenc", NS_CENC));
138 }
139 if let Some(v) = self.availability_start_time_us {
140 root.push_attribute((
141 "availabilityStartTime",
142 format_xs_datetime(v)
143 .map_err(|e| MpdError::Invalid {
144 what: "availabilityStartTime",
145 detail: e.to_string(),
146 })?
147 .as_str(),
148 ));
149 }
150 if let Some(v) = self.publish_time_us {
151 root.push_attribute((
152 "publishTime",
153 format_xs_datetime(v)
154 .map_err(|e| MpdError::Invalid {
155 what: "publishTime",
156 detail: e.to_string(),
157 })?
158 .as_str(),
159 ));
160 }
161 if let Some(v) = self.minimum_update_period {
162 root.push_attribute(("minimumUpdatePeriod", xs_duration(v).as_str()));
163 }
164 if let Some(v) = self.time_shift_buffer_depth {
165 root.push_attribute(("timeShiftBufferDepth", xs_duration(v).as_str()));
166 }
167 if let Some(v) = self.suggested_presentation_delay {
168 root.push_attribute(("suggestedPresentationDelay", xs_duration(v).as_str()));
169 }
170 if let Some(v) = self.media_presentation_duration {
171 root.push_attribute(("mediaPresentationDuration", xs_duration(v).as_str()));
172 }
173 if let Some(v) = &self.id {
174 root.push_attribute(("id", v.as_str()));
175 }
176 if let Some(v) = self.availability_end_time_us {
177 root.push_attribute((
178 "availabilityEndTime",
179 format_xs_datetime(v)
180 .map_err(|e| MpdError::Invalid {
181 what: "availabilityEndTime",
182 detail: e.to_string(),
183 })?
184 .as_str(),
185 ));
186 }
187 if let Some(v) = self.max_segment_duration {
188 root.push_attribute(("maxSegmentDuration", xs_duration(v).as_str()));
189 }
190 if let Some(v) = self.max_subsegment_duration {
191 root.push_attribute(("maxSubsegmentDuration", xs_duration(v).as_str()));
192 }
193 Ok(root)
194 }
195
196 fn write_body(&self, w: &mut Writer<Vec<u8>>) -> Result<(), MpdError> {
197 for b in &self.base_urls {
198 write_base_url(w, b)?;
199 }
200 for l in &self.locations {
201 w.write_event(Event::Start(BytesStart::new("Location")))
202 .map_err(io_err)?;
203 w.write_event(Event::Text(BytesText::new(l)))
204 .map_err(io_err)?;
205 w.write_event(Event::End(BytesEnd::new("Location")))
206 .map_err(io_err)?;
207 }
208 for pl in &self.patch_locations {
209 write_patch_location(w, pl)?;
210 }
211 for sd in &self.service_descriptions {
212 write_service_description(w, sd)?;
213 }
214 for p in &self.periods {
215 write_period(w, p)?;
216 }
217 for d in &self.essential_properties {
218 write_descriptor(w, "EssentialProperty", d)?;
219 }
220 for d in &self.supplemental_properties {
221 write_descriptor(w, "SupplementalProperty", d)?;
222 }
223 for u in &self.utc_timings {
224 write_utc_timing(w, u)?;
225 }
226 if let Some(cs) = &self.content_steering {
227 write_content_steering(w, cs)?;
228 }
229 Ok(())
230 }
231}
232
233fn write_text_element(
234 w: &mut Writer<Vec<u8>>,
235 e: BytesStart<'_>,
236 name: &str,
237 text: &str,
238) -> Result<(), MpdError> {
239 if text.is_empty() {
240 return w.write_event(Event::Empty(e)).map_err(io_err);
241 }
242 w.write_event(Event::Start(e)).map_err(io_err)?;
243 w.write_event(Event::Text(BytesText::new(text)))
244 .map_err(io_err)?;
245 w.write_event(Event::End(BytesEnd::new(name)))
246 .map_err(io_err)
247}
248
249fn write_utc_timing(w: &mut Writer<Vec<u8>>, u: &UtcTiming) -> Result<(), MpdError> {
250 let mut e = BytesStart::new("UTCTiming");
251 e.push_attribute(("schemeIdUri", u.scheme_id_uri.as_str()));
252 e.push_attribute(("value", u.value.as_str()));
253 if let Some(v) = &u.id {
254 e.push_attribute(("id", v.as_str()));
255 }
256 w.write_event(Event::Empty(e)).map_err(io_err)
257}
258
259fn write_patch_location(w: &mut Writer<Vec<u8>>, p: &PatchLocation) -> Result<(), MpdError> {
260 let mut e = BytesStart::new("PatchLocation");
261 if let Some(v) = &p.service_location {
262 e.push_attribute(("serviceLocation", v.as_str()));
263 }
264 if let Some(v) = p.ttl {
265 e.push_attribute(("ttl", format_xs_double(v).as_str()));
266 }
267 write_text_element(w, e, "PatchLocation", &p.url)
268}
269
270fn write_content_steering(w: &mut Writer<Vec<u8>>, c: &ContentSteering) -> Result<(), MpdError> {
271 let mut e = BytesStart::new("ContentSteering");
272 if let Some(v) = &c.default_service_location {
273 e.push_attribute(("defaultServiceLocation", v.as_str()));
274 }
275 if c.query_before_start {
276 e.push_attribute(("queryBeforeStart", "true"));
277 }
278 if let Some(v) = c.client_requirement {
279 e.push_attribute(("clientRequirement", if v { "true" } else { "false" }));
280 }
281 write_text_element(w, e, "ContentSteering", &c.url)
282}
283
284fn write_event(w: &mut Writer<Vec<u8>>, ev: &StreamEvent) -> Result<(), MpdError> {
285 if let Some(u) = ev.unsupported {
286 return Err(MpdError::Unsupported(u));
287 }
288 let mut e = BytesStart::new("Event");
289 e.push_attribute((
290 "presentationTime",
291 ev.presentation_time.to_string().as_str(),
292 ));
293 if let Some(v) = ev.duration {
294 e.push_attribute(("duration", v.to_string().as_str()));
295 }
296 if let Some(v) = ev.id {
297 e.push_attribute(("id", v.to_string().as_str()));
298 }
299 if let Some(v) = ev.status {
300 e.push_attribute(("status", v.name()));
301 }
302 if let Some(v) = &ev.message_data {
303 e.push_attribute(("messageData", v.as_str()));
304 }
305 let has_children = !(ev.supplemental_properties.is_empty()
306 && ev.essential_properties.is_empty()
307 && ev.service_descriptions.is_empty());
308 if !has_children {
309 return write_text_element(w, e, "Event", &ev.text);
310 }
311 w.write_event(Event::Start(e)).map_err(io_err)?;
312 if !ev.text.is_empty() {
313 w.write_event(Event::Text(BytesText::new(&ev.text)))
314 .map_err(io_err)?;
315 }
316 for sd in &ev.service_descriptions {
317 write_service_description(w, sd)?;
318 }
319 for d in &ev.supplemental_properties {
320 write_descriptor(w, "SupplementalProperty", d)?;
321 }
322 for d in &ev.essential_properties {
323 write_descriptor(w, "EssentialProperty", d)?;
324 }
325 w.write_event(Event::End(BytesEnd::new("Event")))
326 .map_err(io_err)
327}
328
329fn write_event_stream(w: &mut Writer<Vec<u8>>, es: &EventStream) -> Result<(), MpdError> {
330 let mut e = BytesStart::new("EventStream");
331 if let Some(x) = &es.xlink {
332 write_xlink_attrs(&mut e, x);
333 }
334 e.push_attribute(("schemeIdUri", es.scheme_id_uri.as_str()));
335 if let Some(v) = &es.value {
336 e.push_attribute(("value", v.as_str()));
337 }
338 if let Some(v) = es.timescale {
339 e.push_attribute(("timescale", v.to_string().as_str()));
340 }
341 if let Some(v) = es.presentation_time_offset {
342 e.push_attribute(("presentationTimeOffset", v.to_string().as_str()));
343 }
344 w.write_event(Event::Start(e)).map_err(io_err)?;
345 for ev in &es.events {
346 write_event(w, ev)?;
347 }
348 for b in &es.base_urls {
349 write_base_url(w, b)?;
350 }
351 for r in &es.request_params {
352 write_request_param(w, r)?;
353 }
354 for d in &es.essential_properties {
355 write_descriptor(w, "EssentialProperty", d)?;
356 }
357 for d in &es.supplemental_properties {
358 write_descriptor(w, "SupplementalProperty", d)?;
359 }
360 w.write_event(Event::End(BytesEnd::new("EventStream")))
361 .map_err(io_err)
362}
363
364fn write_producer_reference_time(
365 w: &mut Writer<Vec<u8>>,
366 p: &ProducerReferenceTime,
367) -> Result<(), MpdError> {
368 let mut e = BytesStart::new("ProducerReferenceTime");
369 e.push_attribute(("id", p.id.to_string().as_str()));
370 if p.inband {
371 e.push_attribute(("inband", "true"));
372 }
373 if p.kind != ProducerReferenceKind::Encoder {
374 e.push_attribute(("type", p.kind.name()));
375 }
376 if let Some(v) = &p.application_scheme {
377 e.push_attribute(("applicationScheme", v.as_str()));
378 }
379 e.push_attribute(("wallClockTime", p.wall_clock_time.as_str()));
380 e.push_attribute(("presentationTime", p.presentation_time.to_string().as_str()));
381 match &p.utc_timing {
382 None => w.write_event(Event::Empty(e)).map_err(io_err),
383 Some(u) => {
384 w.write_event(Event::Start(e)).map_err(io_err)?;
385 write_utc_timing(w, u)?;
386 w.write_event(Event::End(BytesEnd::new("ProducerReferenceTime")))
387 .map_err(io_err)
388 }
389 }
390}
391
392fn write_resync(w: &mut Writer<Vec<u8>>, r: &Resync) -> Result<(), MpdError> {
393 let mut e = BytesStart::new("Resync");
394 if r.kind != 0 {
395 e.push_attribute(("type", r.kind.to_string().as_str()));
396 }
397 if let Some(v) = r.dt {
398 e.push_attribute(("dT", v.to_string().as_str()));
399 }
400 if let Some(v) = r.d_imax {
401 e.push_attribute(("dImax", format_xs_double(v).as_str()));
402 }
403 if let Some(v) = r.d_imin {
404 e.push_attribute(("dImin", format_xs_double(v).as_str()));
405 }
406 if r.marker {
407 e.push_attribute(("marker", "true"));
408 }
409 w.write_event(Event::Empty(e)).map_err(io_err)
410}
411
412fn write_label(w: &mut Writer<Vec<u8>>, name: &str, label: &Label) -> Result<(), MpdError> {
413 let mut e = BytesStart::new(name);
414 if label.id != 0 {
415 e.push_attribute(("id", label.id.to_string().as_str()));
416 }
417 if let Some(v) = &label.lang {
418 e.push_attribute(("lang", v.as_str()));
419 }
420 w.write_event(Event::Start(e)).map_err(io_err)?;
421 w.write_event(Event::Text(BytesText::new(&label.text)))
422 .map_err(io_err)?;
423 w.write_event(Event::End(BytesEnd::new(name)))
424 .map_err(io_err)
425}
426
427fn write_preselection(w: &mut Writer<Vec<u8>>, p: &Preselection) -> Result<(), MpdError> {
428 if let Some(u) = p.unsupported {
429 return Err(MpdError::Unsupported(u));
430 }
431 let mut e = BytesStart::new("Preselection");
432 if let Some(v) = &p.id {
433 e.push_attribute(("id", v.as_str()));
434 }
435 e.push_attribute((
436 "preselectionComponents",
437 p.preselection_components.join(" ").as_str(),
438 ));
439 if let Some(v) = &p.lang {
440 e.push_attribute(("lang", v.as_str()));
441 }
442 if let Some(v) = p.order {
443 e.push_attribute(("order", v.name()));
444 }
445 if let Some(v) = &p.common.profiles {
446 e.push_attribute(("profiles", v.as_str()));
447 }
448 if let Some(v) = p.common.width {
449 e.push_attribute(("width", v.to_string().as_str()));
450 }
451 if let Some(v) = p.common.height {
452 e.push_attribute(("height", v.to_string().as_str()));
453 }
454 if let Some(v) = &p.common.sar {
455 e.push_attribute(("sar", v.as_str()));
456 }
457 if let Some(v) = &p.common.frame_rate {
458 e.push_attribute(("frameRate", v.as_str()));
459 }
460 if let Some(v) = &p.common.audio_sampling_rate {
461 e.push_attribute(("audioSamplingRate", v.as_str()));
462 }
463 if let Some(v) = &p.common.mime_type {
464 e.push_attribute(("mimeType", v.as_str()));
465 }
466 if let Some(v) = &p.common.codecs {
467 e.push_attribute(("codecs", v.as_str()));
468 }
469 if let Some(v) = p.common.max_playout_rate {
470 e.push_attribute(("maxPlayoutRate", format_xs_double(v).as_str()));
471 }
472 if let Some(v) = p.common.coding_dependency {
473 e.push_attribute(("codingDependency", if v { "true" } else { "false" }));
474 }
475 if let Some(v) = &p.segment_profiles {
476 e.push_attribute(("segmentProfiles", v.as_str()));
477 }
478 if let Some(v) = &p.container_profiles {
479 e.push_attribute(("containerProfiles", v.as_str()));
480 }
481 if let Some(v) = p.maximum_sap_period {
482 e.push_attribute(("maximumSAPPeriod", format_xs_double(v).as_str()));
483 }
484 if let Some(v) = p.start_with_sap {
485 e.push_attribute(("startWithSAP", v.to_string().as_str()));
486 }
487 if let Some(v) = &p.scan_type {
488 e.push_attribute(("scanType", v.as_str()));
489 }
490 if let Some(v) = p.selection_priority {
491 e.push_attribute(("selectionPriority", v.to_string().as_str()));
492 }
493 if let Some(v) = &p.tag {
494 e.push_attribute(("tag", v.as_str()));
495 }
496 w.write_event(Event::Start(e)).map_err(io_err)?;
497 for d in &p.frame_packings {
498 write_descriptor(w, "FramePacking", d)?;
499 }
500 for d in &p.audio_channel_configurations {
501 write_descriptor(w, "AudioChannelConfiguration", d)?;
502 }
503 for d in &p.content_protections {
504 write_descriptor(w, "ContentProtection", d)?;
505 }
506 if let Some(d) = &p.output_protection {
507 write_descriptor(w, "OutputProtection", d)?;
508 }
509 for d in &p.essential_properties {
510 write_descriptor(w, "EssentialProperty", d)?;
511 }
512 for d in &p.supplemental_properties {
513 write_descriptor(w, "SupplementalProperty", d)?;
514 }
515 for d in &p.inband_event_streams {
516 write_descriptor(w, "InbandEventStream", d)?;
517 }
518 for l in &p.group_labels {
519 write_label(w, "GroupLabel", l)?;
520 }
521 for l in &p.labels {
522 write_label(w, "Label", l)?;
523 }
524 for t in &p.producer_reference_times {
525 write_producer_reference_time(w, t)?;
526 }
527 for r in &p.resyncs {
528 write_resync(w, r)?;
529 }
530 for d in &p.accessibilities {
531 write_descriptor(w, "Accessibility", d)?;
532 }
533 for d in &p.roles {
534 write_descriptor(w, "Role", d)?;
535 }
536 for d in &p.ratings {
537 write_descriptor(w, "Rating", d)?;
538 }
539 for d in &p.viewpoints {
540 write_descriptor(w, "Viewpoint", d)?;
541 }
542 w.write_event(Event::End(BytesEnd::new("Preselection")))
543 .map_err(io_err)
544}
545
546fn write_service_description(
547 w: &mut Writer<Vec<u8>>,
548 sd: &ServiceDescription,
549) -> Result<(), MpdError> {
550 let mut e = BytesStart::new("ServiceDescription");
551 if let Some(v) = sd.id {
552 e.push_attribute(("id", v.to_string().as_str()));
553 }
554 w.write_event(Event::Start(e)).map_err(io_err)?;
555 if let Some(lat) = &sd.latency {
556 let mut le = BytesStart::new("Latency");
557 if let Some(v) = lat.target {
558 le.push_attribute(("target", v.to_string().as_str()));
559 }
560 if let Some(v) = lat.min {
561 le.push_attribute(("min", v.to_string().as_str()));
562 }
563 if let Some(v) = lat.max {
564 le.push_attribute(("max", v.to_string().as_str()));
565 }
566 if let Some(v) = lat.reference_id {
567 le.push_attribute(("referenceId", v.to_string().as_str()));
568 }
569 w.write_event(Event::Empty(le)).map_err(io_err)?;
570 }
571 if let Some(pr) = &sd.playback_rate {
572 let mut pe = BytesStart::new("PlaybackRate");
573 if let Some(v) = &pr.min {
574 pe.push_attribute(("min", v.as_str()));
575 }
576 if let Some(v) = &pr.max {
577 pe.push_attribute(("max", v.as_str()));
578 }
579 w.write_event(Event::Empty(pe)).map_err(io_err)?;
580 }
581 w.write_event(Event::End(BytesEnd::new("ServiceDescription")))
582 .map_err(io_err)?;
583 Ok(())
584}
585
586fn io_err(e: io::Error) -> MpdError {
587 MpdError::Write {
588 message: e.to_string(),
589 }
590}
591
592fn write_base_url(w: &mut Writer<Vec<u8>>, b: &BaseUrl) -> Result<(), MpdError> {
593 let mut e = BytesStart::new("BaseURL");
594 if let Some(v) = &b.service_location {
595 e.push_attribute(("serviceLocation", v.as_str()));
596 }
597 if let Some(v) = b.priority {
598 e.push_attribute(("dvb:priority", v.to_string().as_str()));
599 }
600 if let Some(v) = b.weight {
601 e.push_attribute(("dvb:weight", v.to_string().as_str()));
602 }
603 if let Some(v) = b.availability_time_offset {
604 e.push_attribute(("availabilityTimeOffset", format_xs_double(v).as_str()));
605 }
606 if let Some(v) = b.availability_time_complete {
607 e.push_attribute(("availabilityTimeComplete", if v { "true" } else { "false" }));
608 }
609 if let Some(v) = b.time_shift_buffer_depth {
610 e.push_attribute(("timeShiftBufferDepth", xs_duration(v).as_str()));
611 }
612 if b.url.is_empty() && e.attributes().count() == 0 {
613 w.write_event(Event::Empty(e)).map_err(io_err)?;
614 } else {
615 w.write_event(Event::Start(e)).map_err(io_err)?;
616 if !b.url.is_empty() {
617 w.write_event(Event::Text(BytesText::new(&b.url)))
618 .map_err(io_err)?;
619 }
620 w.write_event(Event::End(BytesEnd::new("BaseURL")))
621 .map_err(io_err)?;
622 }
623 Ok(())
624}
625
626fn format_xs_double(v: f64) -> String {
627 if v == f64::INFINITY {
628 INFINITY_TOKEN.to_string()
629 } else if v == f64::NEG_INFINITY {
630 NEGATIVE_INFINITY_TOKEN.to_string()
631 } else {
632 v.to_string()
633 }
634}
635
636fn write_range(r: &ByteRange) -> String {
637 match r.last {
638 Some(l) => format!("{}-{}", r.first, l),
639 None => format!("{}-", r.first),
640 }
641}
642
643fn write_url_type(w: &mut Writer<Vec<u8>>, name: &str, u: &UrlType) -> Result<(), MpdError> {
644 let mut e = BytesStart::new(name);
645 if let Some(v) = &u.source_url {
646 e.push_attribute(("sourceURL", v.as_str()));
647 }
648 if let Some(v) = &u.range {
649 e.push_attribute(("range", write_range(v).as_str()));
650 }
651 w.write_event(Event::Empty(e)).map_err(io_err)?;
652 Ok(())
653}
654
655fn write_base_info(
656 _w: &mut Writer<Vec<u8>>,
657 e: &mut BytesStart<'_>,
658 info: &BaseInfo,
659) -> Result<(), MpdError> {
660 if let Some(v) = info.timescale {
661 e.push_attribute(("timescale", v.to_string().as_str()));
662 }
663 if let Some(v) = info.presentation_time_offset {
664 e.push_attribute(("presentationTimeOffset", v.to_string().as_str()));
665 }
666 if let Some(v) = info.ept_delta {
667 e.push_attribute(("eptDelta", v.to_string().as_str()));
668 }
669 if let Some(v) = info.pd_delta {
670 e.push_attribute(("pdDelta", v.to_string().as_str()));
671 }
672 if let Some(v) = info.presentation_duration {
673 e.push_attribute(("presentationDuration", v.to_string().as_str()));
674 }
675 if let Some(v) = info.time_shift_buffer_depth {
676 e.push_attribute(("timeShiftBufferDepth", xs_duration(v).as_str()));
677 }
678 if let Some(v) = &info.index_range {
679 e.push_attribute(("indexRange", write_range(v).as_str()));
680 }
681 if let Some(v) = info.index_range_exact {
682 e.push_attribute(("indexRangeExact", if v { "true" } else { "false" }));
683 }
684 Ok(())
685}
686
687fn write_segment_base(w: &mut Writer<Vec<u8>>, sb: &SegmentBase) -> Result<(), MpdError> {
688 let mut e = BytesStart::new("SegmentBase");
689 write_base_info(w, &mut e, &sb.info)?;
690 if let Some(v) = sb.info.availability_time_offset {
691 e.push_attribute(("availabilityTimeOffset", format_xs_double(v).as_str()));
692 }
693 if let Some(v) = sb.info.availability_time_complete {
694 e.push_attribute(("availabilityTimeComplete", if v { "true" } else { "false" }));
695 }
696 let has_init = sb.info.initialization.is_some();
697 let has_ri = sb.info.representation_index.is_some();
698 if !has_init && !has_ri {
699 w.write_event(Event::Empty(e)).map_err(io_err)?;
700 return Ok(());
701 }
702 w.write_event(Event::Start(e)).map_err(io_err)?;
703 if let Some(u) = &sb.info.initialization {
704 write_url_type(w, "Initialization", u)?;
705 }
706 if let Some(u) = &sb.info.representation_index {
707 write_url_type(w, "RepresentationIndex", u)?;
708 }
709 w.write_event(Event::End(BytesEnd::new("SegmentBase")))
710 .map_err(io_err)?;
711 Ok(())
712}
713
714fn write_timeline_entry(w: &mut Writer<Vec<u8>>, te: &TimelineEntry) -> Result<(), MpdError> {
715 let mut e = BytesStart::new("S");
716 if let Some(v) = te.t {
717 e.push_attribute(("t", v.to_string().as_str()));
718 }
719 if let Some(v) = te.n {
720 e.push_attribute(("n", v.to_string().as_str()));
721 }
722 e.push_attribute(("d", te.d.to_string().as_str()));
723 if te.r != 0 {
724 e.push_attribute(("r", te.r.to_string().as_str()));
725 }
726 if let Some(v) = te.k {
727 e.push_attribute(("k", v.to_string().as_str()));
728 }
729 w.write_event(Event::Empty(e)).map_err(io_err)?;
730 Ok(())
731}
732
733fn write_timeline(w: &mut Writer<Vec<u8>>, t: &SegmentTimeline) -> Result<(), MpdError> {
734 let e = BytesStart::new("SegmentTimeline");
735 w.write_event(Event::Start(e)).map_err(io_err)?;
736 for te in &t.entries {
737 write_timeline_entry(w, te)?;
738 }
739 w.write_event(Event::End(BytesEnd::new("SegmentTimeline")))
740 .map_err(io_err)?;
741 Ok(())
742}
743
744fn write_segment_list(w: &mut Writer<Vec<u8>>, sl: &SegmentList) -> Result<(), MpdError> {
745 let mut e = BytesStart::new("SegmentList");
746 write_base_info(w, &mut e, &sl.multi.base)?;
747 if let Some(v) = sl.multi.base.availability_time_offset {
748 e.push_attribute(("availabilityTimeOffset", format_xs_double(v).as_str()));
749 }
750 if let Some(v) = sl.multi.base.availability_time_complete {
751 e.push_attribute(("availabilityTimeComplete", if v { "true" } else { "false" }));
752 }
753 if let Some(v) = sl.multi.duration {
754 e.push_attribute(("duration", v.to_string().as_str()));
755 }
756 if let Some(v) = sl.multi.start_number {
757 e.push_attribute(("startNumber", v.to_string().as_str()));
758 }
759 if let Some(v) = sl.multi.end_number {
760 e.push_attribute(("endNumber", v.to_string().as_str()));
761 }
762 if let Some(x) = &sl.xlink {
763 e.push_attribute(("xlink:href", x.href.as_str()));
764 if x.actuate != Actuate::OnRequest {
765 e.push_attribute(("xlink:actuate", x.actuate.name()));
766 }
767 }
768 let has_children = sl.multi.base.initialization.is_some()
769 || sl.multi.base.representation_index.is_some()
770 || sl.multi.timeline.is_some()
771 || sl.multi.bitstream_switching.is_some()
772 || !sl.urls.is_empty();
773 if !has_children {
774 w.write_event(Event::Empty(e)).map_err(io_err)?;
775 return Ok(());
776 }
777 w.write_event(Event::Start(e)).map_err(io_err)?;
778 if let Some(u) = &sl.multi.base.initialization {
779 write_url_type(w, "Initialization", u)?;
780 }
781 if let Some(u) = &sl.multi.base.representation_index {
782 write_url_type(w, "RepresentationIndex", u)?;
783 }
784 if let Some(t) = &sl.multi.timeline {
785 write_timeline(w, t)?;
786 }
787 if let Some(u) = &sl.multi.bitstream_switching {
788 write_url_type(w, "BitstreamSwitching", u)?;
789 }
790 for u in &sl.urls {
791 write_segment_url(w, u)?;
792 }
793 w.write_event(Event::End(BytesEnd::new("SegmentList")))
794 .map_err(io_err)?;
795 Ok(())
796}
797
798fn write_segment_url(w: &mut Writer<Vec<u8>>, u: &SegmentUrl) -> Result<(), MpdError> {
799 let mut e = BytesStart::new("SegmentURL");
800 if let Some(v) = &u.media {
801 e.push_attribute(("media", v.as_str()));
802 }
803 if let Some(v) = &u.media_range {
804 e.push_attribute(("mediaRange", write_range(v).as_str()));
805 }
806 if let Some(v) = &u.index {
807 e.push_attribute(("index", v.as_str()));
808 }
809 if let Some(v) = &u.index_range {
810 e.push_attribute(("indexRange", write_range(v).as_str()));
811 }
812 w.write_event(Event::Empty(e)).map_err(io_err)?;
813 Ok(())
814}
815
816fn write_segment_template(w: &mut Writer<Vec<u8>>, st: &SegmentTemplate) -> Result<(), MpdError> {
817 let mut e = BytesStart::new("SegmentTemplate");
818 write_base_info(w, &mut e, &st.multi.base)?;
819 if let Some(v) = st.multi.duration {
820 e.push_attribute(("duration", v.to_string().as_str()));
821 }
822 if let Some(v) = st.multi.start_number {
823 e.push_attribute(("startNumber", v.to_string().as_str()));
824 }
825 if let Some(v) = st.multi.end_number {
826 e.push_attribute(("endNumber", v.to_string().as_str()));
827 }
828 if let Some(v) = &st.initialization {
829 e.push_attribute(("initialization", v.as_str()));
830 }
831 if let Some(v) = &st.media {
832 e.push_attribute(("media", v.as_str()));
833 }
834 if let Some(v) = &st.index {
835 e.push_attribute(("index", v.as_str()));
836 }
837 if let Some(v) = &st.bitstream_switching {
838 e.push_attribute(("bitstreamSwitching", v.as_str()));
839 }
840 if let Some(v) = st.multi.base.availability_time_offset {
841 e.push_attribute(("availabilityTimeOffset", format_xs_double(v).as_str()));
842 }
843 if let Some(v) = st.multi.base.availability_time_complete {
844 e.push_attribute(("availabilityTimeComplete", if v { "true" } else { "false" }));
845 }
846 let has_children = st.multi.base.initialization.is_some()
847 || st.multi.base.representation_index.is_some()
848 || st.multi.timeline.is_some()
849 || st.multi.bitstream_switching.is_some();
850 if !has_children {
851 w.write_event(Event::Empty(e)).map_err(io_err)?;
852 return Ok(());
853 }
854 w.write_event(Event::Start(e)).map_err(io_err)?;
855 if let Some(u) = &st.multi.base.initialization {
856 write_url_type(w, "Initialization", u)?;
857 }
858 if let Some(u) = &st.multi.base.representation_index {
859 write_url_type(w, "RepresentationIndex", u)?;
860 }
861 if let Some(t) = &st.multi.timeline {
862 write_timeline(w, t)?;
863 }
864 if let Some(u) = &st.multi.bitstream_switching {
865 write_url_type(w, "BitstreamSwitching", u)?;
866 }
867 w.write_event(Event::End(BytesEnd::new("SegmentTemplate")))
868 .map_err(io_err)?;
869 Ok(())
870}
871
872fn write_segment_layer(w: &mut Writer<Vec<u8>>, layer: &SegmentLayer) -> Result<(), MpdError> {
873 if let Some(b) = &layer.base {
874 write_segment_base(w, b)?;
875 }
876 if let Some(l) = &layer.list {
877 write_segment_list(w, l)?;
878 }
879 if let Some(t) = &layer.template {
880 write_segment_template(w, t)?;
881 }
882 Ok(())
883}
884
885fn write_descriptor(w: &mut Writer<Vec<u8>>, name: &str, d: &Descriptor) -> Result<(), MpdError> {
886 let mut e = BytesStart::new(name);
887 e.push_attribute(("schemeIdUri", d.scheme_id_uri.as_str()));
888 if let Some(v) = &d.value {
889 e.push_attribute(("value", v.as_str()));
890 }
891 if let Some(v) = &d.id {
892 e.push_attribute(("id", v.as_str()));
893 }
894 if let Some(v) = &d.default_kid {
895 e.push_attribute(("cenc:default_KID", v.as_str()));
896 }
897 let has_children = d.url_query.is_some() || d.pssh.is_some();
898 if has_children {
899 w.write_event(Event::Start(e)).map_err(io_err)?;
900 if let Some(p) = &d.pssh {
901 w.write_event(Event::Start(BytesStart::new("cenc:pssh")))
902 .map_err(io_err)?;
903 w.write_event(Event::Text(BytesText::new(p)))
904 .map_err(io_err)?;
905 w.write_event(Event::End(BytesEnd::new("cenc:pssh")))
906 .map_err(io_err)?;
907 }
908 if let Some(q) = &d.url_query {
909 let mut qe = BytesStart::new("urlparam:UrlQueryInfo");
910 qe.push_attribute((
911 "useMPDUrlQuery",
912 if q.use_mpd_url_query { "true" } else { "false" },
913 ));
914 push_query_attrs(&mut qe, q);
915 w.write_event(Event::Empty(qe)).map_err(io_err)?;
916 }
917 w.write_event(Event::End(BytesEnd::new(name)))
918 .map_err(io_err)?;
919 } else {
920 w.write_event(Event::Empty(e)).map_err(io_err)?;
921 }
922 Ok(())
923}
924
925fn push_query_attrs(e: &mut BytesStart<'_>, q: &UrlQueryInfo) {
926 if let Some(v) = &q.query_string {
927 e.push_attribute(("queryString", v.as_str()));
928 }
929 if let Some(v) = &q.query_template {
930 e.push_attribute(("queryTemplate", v.as_str()));
931 }
932}
933
934fn write_request_param(w: &mut Writer<Vec<u8>>, r: &RequestParam) -> Result<(), MpdError> {
935 let mut e = BytesStart::new("RequestParam");
936 if let Some(x) = &r.xlink {
937 write_xlink_attrs(&mut e, x);
938 }
939 if r.query.use_mpd_url_query {
940 e.push_attribute(("useMPDUrlQuery", "true"));
941 }
942 push_query_attrs(&mut e, &r.query);
943 if let Some(v) = &r.include_in_requests {
944 e.push_attribute(("includeInRequests", v.as_str()));
945 }
946 if let Some(v) = &r.header_param_source {
947 e.push_attribute(("headerParamSource", v.as_str()));
948 }
949 if let Some(v) = r.same_origin_only {
950 e.push_attribute(("sameOriginOnly", if v { "true" } else { "false" }));
951 }
952 if let Some(v) = &r.header {
953 e.push_attribute(("header", v.as_str()));
954 }
955 w.write_event(Event::Empty(e)).map_err(io_err)
956}
957
958fn write_xlink_attrs(e: &mut BytesStart<'_>, x: &Xlink) {
959 e.push_attribute(("xlink:href", x.href.as_str()));
960 if x.actuate != Actuate::OnRequest {
961 e.push_attribute(("xlink:actuate", x.actuate.name()));
962 }
963}
964
965fn write_period(w: &mut Writer<Vec<u8>>, p: &Period) -> Result<(), MpdError> {
966 let mut e = BytesStart::new("Period");
967 if let Some(v) = &p.id {
968 e.push_attribute(("id", v.as_str()));
969 }
970 if let Some(v) = p.start {
971 e.push_attribute(("start", xs_duration(v).as_str()));
972 }
973 if let Some(v) = p.duration {
974 e.push_attribute(("duration", xs_duration(v).as_str()));
975 }
976 if p.bitstream_switching {
977 e.push_attribute(("bitstreamSwitching", "true"));
978 }
979 if let Some(x) = &p.xlink {
980 write_xlink_attrs(&mut e, x);
981 }
982 w.write_event(Event::Start(e)).map_err(io_err)?;
983 if let Some(im) = &p.imported_mpd {
984 let mut ie = BytesStart::new("ImportedMPD");
985 if let Some(v) = im.earliest_resolution_time_offset {
986 ie.push_attribute(("earliestResolutionTimeOffset", v.to_string().as_str()));
987 }
988 w.write_event(Event::Start(ie)).map_err(io_err)?;
989 w.write_event(Event::Text(BytesText::new(&im.url)))
990 .map_err(io_err)?;
991 w.write_event(Event::End(BytesEnd::new("ImportedMPD")))
992 .map_err(io_err)?;
993 }
994 for b in &p.base_urls {
995 write_base_url(w, b)?;
996 }
997 write_segment_layer(w, &p.segment)?;
998 if let Some(d) = &p.asset_identifier {
999 write_descriptor(w, "AssetIdentifier", d)?;
1000 }
1001 for es in &p.event_streams {
1002 write_event_stream(w, es)?;
1003 }
1004 for sd in &p.service_descriptions {
1005 write_service_description(w, sd)?;
1006 }
1007 for a in &p.adaptation_sets {
1008 write_adaptation_set(w, a)?;
1009 }
1010 for d in &p.essential_properties {
1011 write_descriptor(w, "EssentialProperty", d)?;
1012 }
1013 for d in &p.supplemental_properties {
1014 write_descriptor(w, "SupplementalProperty", d)?;
1015 }
1016 for pre in &p.preselections {
1017 write_preselection(w, pre)?;
1018 }
1019 w.write_event(Event::End(BytesEnd::new("Period")))
1020 .map_err(io_err)?;
1021 Ok(())
1022}
1023
1024fn write_adaptation_set(w: &mut Writer<Vec<u8>>, a: &AdaptationSet) -> Result<(), MpdError> {
1025 let mut e = BytesStart::new("AdaptationSet");
1026 if let Some(v) = a.id {
1027 e.push_attribute(("id", v.to_string().as_str()));
1028 }
1029 if let Some(v) = &a.content_type {
1030 e.push_attribute(("contentType", v.as_str()));
1031 }
1032 if let Some(v) = &a.common.mime_type {
1033 e.push_attribute(("mimeType", v.as_str()));
1034 }
1035 if let Some(v) = a.common.max_playout_rate {
1036 e.push_attribute(("maxPlayoutRate", v.to_string().as_str()));
1037 }
1038 if let Some(v) = a.common.coding_dependency {
1039 e.push_attribute(("codingDependency", if v { "true" } else { "false" }));
1040 }
1041 if a.segment_alignment {
1042 e.push_attribute(("segmentAlignment", "true"));
1043 }
1044 if let Some(v) = a.start_with_sap {
1045 e.push_attribute(("startWithSAP", v.to_string().as_str()));
1046 }
1047 if let Some(v) = &a.par {
1048 e.push_attribute(("par", v.as_str()));
1049 }
1050 if let Some(v) = &a.lang {
1051 e.push_attribute(("lang", v.as_str()));
1052 }
1053 if let Some(v) = a.group {
1054 e.push_attribute(("group", v.to_string().as_str()));
1055 }
1056 if let Some(v) = a.bitstream_switching {
1057 e.push_attribute(("bitstreamSwitching", if v { "true" } else { "false" }));
1058 }
1059 if let Some(v) = &a.common.profiles {
1060 e.push_attribute(("profiles", v.as_str()));
1061 }
1062 if let Some(v) = a.common.width {
1063 e.push_attribute(("width", v.to_string().as_str()));
1064 }
1065 if let Some(v) = a.common.height {
1066 e.push_attribute(("height", v.to_string().as_str()));
1067 }
1068 if let Some(v) = &a.common.sar {
1069 e.push_attribute(("sar", v.as_str()));
1070 }
1071 if let Some(v) = &a.common.frame_rate {
1072 e.push_attribute(("frameRate", v.as_str()));
1073 }
1074 if let Some(v) = &a.common.audio_sampling_rate {
1075 e.push_attribute(("audioSamplingRate", v.as_str()));
1076 }
1077 if let Some(v) = &a.common.codecs {
1078 e.push_attribute(("codecs", v.as_str()));
1079 }
1080 if let Some(x) = &a.xlink {
1081 write_xlink_attrs(&mut e, x);
1082 }
1083
1084 w.write_event(Event::Start(e)).map_err(io_err)?;
1085 for d in &a.audio_channel_configurations {
1086 write_descriptor(w, "AudioChannelConfiguration", d)?;
1087 }
1088 for d in &a.content_protections {
1089 write_descriptor(w, "ContentProtection", d)?;
1090 }
1091 for d in &a.essential_properties {
1092 write_descriptor(w, "EssentialProperty", d)?;
1093 }
1094 for d in &a.supplemental_properties {
1095 write_descriptor(w, "SupplementalProperty", d)?;
1096 }
1097 for d in &a.inband_event_streams {
1098 write_descriptor(w, "InbandEventStream", d)?;
1099 }
1100 for l in &a.labels {
1101 write_label(w, "Label", l)?;
1102 }
1103 for p in &a.producer_reference_times {
1104 write_producer_reference_time(w, p)?;
1105 }
1106 for r in &a.resyncs {
1107 write_resync(w, r)?;
1108 }
1109 for d in &a.accessibilities {
1110 write_descriptor(w, "Accessibility", d)?;
1111 }
1112 for d in &a.roles {
1113 write_descriptor(w, "Role", d)?;
1114 }
1115 for b in &a.base_urls {
1116 write_base_url(w, b)?;
1117 }
1118 write_segment_layer(w, &a.segment)?;
1119 for r in &a.representations {
1120 write_representation(w, r)?;
1121 }
1122
1123 w.write_event(Event::End(BytesEnd::new("AdaptationSet")))
1124 .map_err(io_err)?;
1125 Ok(())
1126}
1127
1128fn write_representation(w: &mut Writer<Vec<u8>>, r: &Representation) -> Result<(), MpdError> {
1129 let mut e = BytesStart::new("Representation");
1130 e.push_attribute(("id", r.id.as_str()));
1131 if let Some(v) = &r.common.mime_type {
1132 e.push_attribute(("mimeType", v.as_str()));
1133 }
1134 if let Some(v) = &r.common.codecs {
1135 e.push_attribute(("codecs", v.as_str()));
1136 }
1137 e.push_attribute(("bandwidth", r.bandwidth.to_string().as_str()));
1138 if let Some(v) = r.common.width {
1139 e.push_attribute(("width", v.to_string().as_str()));
1140 }
1141 if let Some(v) = r.common.height {
1142 e.push_attribute(("height", v.to_string().as_str()));
1143 }
1144 if let Some(v) = &r.common.frame_rate {
1145 e.push_attribute(("frameRate", v.as_str()));
1146 }
1147 if let Some(v) = &r.common.sar {
1148 e.push_attribute(("sar", v.as_str()));
1149 }
1150 if let Some(v) = &r.common.audio_sampling_rate {
1151 e.push_attribute(("audioSamplingRate", v.as_str()));
1152 }
1153 if let Some(v) = r.quality_ranking {
1154 e.push_attribute(("qualityRanking", v.to_string().as_str()));
1155 }
1156 if !r.dependency_ids.is_empty() {
1157 e.push_attribute(("dependencyId", r.dependency_ids.join(" ").as_str()));
1158 }
1159 if let Some(v) = &r.common.profiles {
1160 e.push_attribute(("profiles", v.as_str()));
1161 }
1162 if let Some(v) = r.common.max_playout_rate {
1163 e.push_attribute(("maxPlayoutRate", v.to_string().as_str()));
1164 }
1165 if let Some(v) = r.common.coding_dependency {
1166 e.push_attribute(("codingDependency", if v { "true" } else { "false" }));
1167 }
1168
1169 w.write_event(Event::Start(e)).map_err(io_err)?;
1170 for d in &r.audio_channel_configurations {
1171 write_descriptor(w, "AudioChannelConfiguration", d)?;
1172 }
1173 for d in &r.content_protections {
1174 write_descriptor(w, "ContentProtection", d)?;
1175 }
1176 for d in &r.essential_properties {
1177 write_descriptor(w, "EssentialProperty", d)?;
1178 }
1179 for d in &r.supplemental_properties {
1180 write_descriptor(w, "SupplementalProperty", d)?;
1181 }
1182 for d in &r.inband_event_streams {
1183 write_descriptor(w, "InbandEventStream", d)?;
1184 }
1185 for p in &r.producer_reference_times {
1186 write_producer_reference_time(w, p)?;
1187 }
1188 for rs in &r.resyncs {
1189 write_resync(w, rs)?;
1190 }
1191 for b in &r.base_urls {
1192 write_base_url(w, b)?;
1193 }
1194 write_segment_layer(w, &r.segment)?;
1195
1196 w.write_event(Event::End(BytesEnd::new("Representation")))
1197 .map_err(io_err)?;
1198 Ok(())
1199}
1200
1201#[cfg(test)]
1202mod prefix_tests {
1203 use super::*;
1204
1205 #[test]
1206 fn the_predeclared_xml_prefix_needs_no_declaration() {
1207 let mut found = Prefixes::default();
1208 found.note("xml:lang").expect("xml is bound by definition");
1209 assert_eq!(found, Prefixes::default());
1210 assert!(found.note("bogus:attr").is_err());
1211 }
1212}