Skip to main content

broadcast_dash/
write.rs

1//! Serialise the MPD model back to XML (ISO/IEC 23009-1).
2
3use 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    /// Render the model as an MPD XML document.
86    ///
87    /// # Errors
88    /// [`MpdError::Write`] if the in-memory writer fails (it cannot in
89    /// practice) and [`MpdError::Invalid`] if a stored instant cannot be
90    /// formatted.
91    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}