Add a video to a slide in a presentation
Video in PresentationML follows the same package pattern as audio: a slide contains markup for a clickable visual object, and relationships connect that object to media data in the package.
Package shape
A slide can reference a video file from non-visual picture properties:
<p:pic>
<p:nvPicPr>
<p:cNvPr id="7" name="video">
<a:hlinkClick r:id="" action="ppaction://media"/>
</p:cNvPr>
<p:nvPr>
<a:videoFile r:link="rVideo1"/>
</p:nvPr>
</p:nvPicPr>
</p:pic>
The slide part relationship item resolves rVideo1 to the stored media data. A complete PowerPoint-compatible video insertion normally also includes a preview image, media reference relationship, timing data, and shape geometry.
videoFile is defined inside the non-visual properties of the picture or shape. The visible object sits on the slide like any other drawing object, while playback is described in the slide timing tree. The non-visual drawing ID is used by that timing data to refer back to the media object.
The video timing schema uses CT_TLMediaNodeVideo, whose common media node child is required and whose fullScrn attribute defaults to false.
The full insertion shape normally includes:
p:cNvPr, including ana:hlinkClickaction ofppaction://media,p:cNvPicPrand picture locks,p:nvPrwitha:videoFile,- a video relationship to the media data part,
- a media reference relationship,
- an image part plus
a:blipfor the preview frame, - shape properties such as
a:off,a:stretch, anda:fillRect.
Rust workflow
Create an MP4 media data part, add video and media reference relationships from the target slide, and insert the video picture markup:
#![allow(unused)] fn main() { pub fn add_video_to_slide( path: &Path, slide_index: usize, video_bytes: &[u8], ) -> Result<(Vec<u8>, String, String), Box<dyn std::error::Error>> { let mut document = PresentationDocument::new_from_file_with_settings(path, lazy_settings())?; let presentation_part = document.presentation_part()?; let Some(slide_part) = presentation_part.slide_parts(&document).nth(slide_index) else { return Err("slide index out of range".into()); }; let media_part = document.create_media_data_part_by_type(MediaDataPartType::Mp4)?; media_part.set_data(&mut document, video_bytes.to_vec())?; let video_relationship_id = slide_part.add_video_reference_relationship(&mut document, &media_part)?; let media_relationship_id = slide_part.add_media_reference_relationship(&mut document, &media_part)?; let slide_xml = slide_part.data_as_str(&document)?.unwrap_or_default(); let updated_xml = insert_video_picture(slide_xml, &video_relationship_id, &media_relationship_id)?; slide_part.set_data(&mut document, updated_xml.into_bytes())?; let mut buffer = Cursor::new(Vec::new()); document.save(&mut buffer)?; Ok(( buffer.into_inner(), video_relationship_id, media_relationship_id, )) } }
For production presentations, add a preview image and timing tree entries if the target application requires them. The example above covers the package media part, video relationship, media relationship, and slide-level a:videoFile markup.