diff --git a/crates/pptx/src/read.rs b/crates/pptx/src/read.rs
--- a/crates/pptx/src/read.rs
+++ b/crates/pptx/src/read.rs
@@ -1133,6 +1133,7 @@ fn read_slide(imp: &mut Imp, part: &Part, doc: &Doc, id: SlideId, layout: Layout
     }
     if let Some(t) = root.child("timing") {
         s.animations = timing::animations(imp, part, &ctx, t, &s.shapes);
+        timing::media_settings(t, &ctx, &mut s.shapes);
     }
     if let Some(ext) = root.child("extLst").filter(|e| !has_rel_refs(e)) {
         s.raw_ext = Some(ext.elements().map(|e| part.keep(e)).collect());
diff --git a/crates/pptx/src/read/timing.rs b/crates/pptx/src/read/timing.rs
--- a/crates/pptx/src/read/timing.rs
+++ b/crates/pptx/src/read/timing.rs
@@ -1,7 +1,7 @@
 //! Slide transitions (`p:transition`) and animations (`p:timing`).
 
 use deckcraft_model::anim::{AnimClass, AnimStart, Animation, BuildRole, TextBuild, Transition, build_filled, paragraph_build_roles};
-use deckcraft_model::{Shape, TextBody, find_shape};
+use deckcraft_model::{Shape, ShapeKind, TextBody, find_shape, find_shape_mut};
 
 use super::shapes::IdCtx;
 use super::{Imp, Part, dml};
@@ -94,6 +94,35 @@ fn max_dur(e: &El, depth: usize) -> Option<u32> {
     best
 }
 
+/// Volume, mute, Loop until Stopped, Hide While Not Playing and Play Full Screen live in the timing tree
+/// (`p:video`/`p:audio` > `p:cMediaNode`), not on the picture: copy them onto the target's `MediaClip`.
+pub fn media_settings(timing: &El, ctx: &IdCtx, shapes: &mut [Shape]) {
+    let mut nodes = vec![];
+    for tag in ["video", "audio"] {
+        timing.find_all(tag, &mut nodes);
+    }
+    for node in nodes.into_iter().take(10_000) {
+        let Some(cm) = node.child("cMediaNode") else { continue };
+        let Some(id) = cm.path(&["tgtEl", "spTgt"]).and_then(|t| t.u32("spid")).and_then(|f| ctx.lookup(f)) else { continue };
+        let Some(ShapeKind::Media(clip)) = find_shape_mut(shapes, id).map(|s| &mut s.kind) else { continue };
+        if let Some(v) = cm.attr("vol").and_then(|v| v.trim().parse::<f64>().ok()).filter(|v| v.is_finite()) {
+            clip.volume = (v / 100_000.0).clamp(0.0, 1.0);
+        }
+        if cm.bool("mute") == Some(true) {
+            clip.volume = 0.0;
+        }
+        if cm.bool("showWhenStopped") == Some(false) {
+            clip.hide_while_not_playing = true;
+        }
+        if cm.child("cTn").and_then(|c| c.attr("repeatCount")) == Some("indefinite") {
+            clip.loop_play = true;
+        }
+        if node.bool("fullScrn") == Some(true) {
+            clip.full_screen = true;
+        }
+    }
+}
+
 /// Flatten the timing tree into the Animation Pane list.
 pub fn animations(imp: &mut Imp, part: &Part, ctx: &IdCtx, timing: &El, shapes: &[Shape]) -> Vec<Animation> {
     let _ = (imp, part);
diff --git a/crates/pptx/tests/it/main.rs b/crates/pptx/tests/it/main.rs
--- a/crates/pptx/tests/it/main.rs
+++ b/crates/pptx/tests/it/main.rs
@@ -13,6 +13,7 @@ mod kept_parts;
 mod locks;
 mod malformed;
 mod math_ns;
+mod media_timing;
 mod notes;
 mod ole;
 mod ole_vml;
diff --git a/crates/pptx/tests/it/media_timing.rs b/crates/pptx/tests/it/media_timing.rs
new file mode 100644
--- /dev/null
+++ b/crates/pptx/tests/it/media_timing.rs
@@ -0,0 +1,76 @@
+//! Media settings PowerPoint keeps in the timing tree, spliced into an exported deck and read back.
+
+use std::io::{Cursor, Read, Write};
+use std::sync::Arc;
+
+use deckcraft_geom::Xfrm;
+use deckcraft_model::{MediaClip, ShapeId, ShapeKind, defaults};
+
+fn node(tag: &str, attrs: &str, spid: u32, media: &str, ctn: &str) -> String {
+    format!(
+        r#"<p:par><p:cTn id="2" fill="hold"><p:childTnLst><p:{tag}{attrs}><p:cMediaNode{media}><p:cTn id="3"{ctn}/><p:tgtEl><p:spTgt spid="{spid}"/></p:tgtEl></p:cMediaNode></p:{tag}></p:childTnLst></p:cTn></p:par>"#
+    )
+}
+
+fn clips(nodes: impl Fn(u32, u32) -> String) -> Vec<MediaClip> {
+    let mut p = defaults::new_presentation(None);
+    let video = p.add_media("clip.mp4", "video/mp4", b"video".to_vec()).expect("media");
+    let audio = p.add_media("clip.m4a", "audio/mp4", b"audio".to_vec()).expect("media");
+    let mut ids = vec![];
+    for (media, is_video) in [(video, true), (audio, false)] {
+        let id = ShapeId(p.alloc_id().expect("id"));
+        ids.push(id.0);
+        let s = deckcraft_model::Shape {
+            id,
+            name: "Clip".into(),
+            xfrm: Some(Xfrm::new(40.0, 40.0, 200.0, 100.0)),
+            kind: ShapeKind::Media(MediaClip { media, video: is_video, volume: 1.0, ..Default::default() }),
+            ..Default::default()
+        };
+        Arc::make_mut(&mut p.slides[0]).shapes.push(s);
+    }
+    let timing = format!(
+        r#"<p:timing><p:tnLst><p:par><p:cTn id="1" dur="indefinite" restart="never" nodeType="tmRoot"><p:childTnLst>{}</p:childTnLst></p:cTn></p:par></p:tnLst></p:timing>"#,
+        nodes(ids[0], ids[1])
+    );
+    let bytes = deckcraft_pptx::export(&p).expect("export");
+    let mut z = zip::ZipArchive::new(Cursor::new(&bytes)).expect("zip");
+    let mut out = zip::ZipWriter::new(Cursor::new(Vec::new()));
+    for i in 0..z.len() {
+        let mut f = z.by_index(i).expect("entry");
+        let mut d = vec![];
+        f.read_to_end(&mut d).expect("read");
+        if f.name() == "ppt/slides/slide1.xml" {
+            let s = String::from_utf8(d).expect("utf8");
+            assert!(!s.contains("<p:timing"));
+            let end = s.rfind("</p:sld>").expect("end");
+            d = format!("{}{timing}{}", &s[..end], &s[end..]).into_bytes();
+        }
+        out.start_file(f.name(), zip::write::SimpleFileOptions::default()).expect("start");
+        out.write_all(&d).expect("write");
+    }
+    let q = deckcraft_pptx::import(&out.finish().expect("finish").into_inner()).expect("import");
+    q.slides[0]
+        .shapes
+        .iter()
+        .filter_map(|s| match &s.kind {
+            ShapeKind::Media(m) => Some(m.clone()),
+            _ => None,
+        })
+        .collect()
+}
+
+#[test]
+fn video_settings_are_read_from_the_timing_tree() {
+    let c = clips(|v, _| node("video", r#" fullScrn="1""#, v, r#" vol="80000" showWhenStopped="0""#, r#" repeatCount="indefinite""#));
+    assert_eq!((c[0].volume, c[0].hide_while_not_playing, c[0].loop_play, c[0].full_screen), (0.8, true, true, true));
+    // The sound next to it has no node: defaults stay.
+    assert_eq!((c[1].volume, c[1].hide_while_not_playing, c[1].loop_play, c[1].full_screen), (1.0, false, false, false));
+}
+
+#[test]
+fn muted_audio_has_no_volume_and_other_clips_keep_theirs() {
+    let c = clips(|_, a| node("audio", "", a, r#" vol="50000" mute="1""#, ""));
+    assert_eq!(c[0].volume, 1.0);
+    assert_eq!((c[1].volume, c[1].loop_play, c[1].full_screen), (0.0, false, false));
+}
