diff --git a/crates/codecs/src/mxf.rs b/crates/codecs/src/mxf.rs
--- a/crates/codecs/src/mxf.rs
+++ b/crates/codecs/src/mxf.rs
@@ -191,6 +191,12 @@ fn start_frames(tc: &Timecode, rate: FrameRate) -> i64 {
     ((n + d / 2) / d) as i64
 }
 
+/// Bytes read from the front of every picture to find its slice header when the index has no
+/// temporal offsets (`avc_presentation_order`), and the longer read for pictures where that is
+/// not enough.
+const AVC_ORDER_HEAD: usize = 2 << 10;
+const AVC_ORDER_HEAD_MAX: usize = 16 << 10;
+
 impl MxfSource {
     pub fn open(name: &str, bytes: Arc<[u8]>) -> crate::Result<Self> {
         Self::open_reader(name, Arc::new(filmcraft_media::reader::MemReader(bytes)))
@@ -229,13 +235,17 @@ impl MxfSource {
             vpts = t.samples.iter().map(|s| s.pts).collect();
             vorder = t.display_order.clone();
             if t.needs_reorder && matches!(t.codec, Codec::Avc { .. }) {
-                let heads: Vec<Vec<u8>> = (0..t.samples.len())
-                    .map(|i| {
-                        let s = t.samples[i];
-                        filmcraft_media::reader::read_range(&*bytes.0, s.offset, (s.size as usize).min(16 << 10)).unwrap_or_default()
-                    })
-                    .collect();
-                match avc_presentation_order(&heads) {
+                // Only the slice headers are needed. The first picture of a stream has its
+                // parameter sets and SEI in front of the slice, so read a short head of every
+                // picture first and the long one only if some slice header does not fit.
+                let heads = |cap: usize| -> Vec<Vec<u8>> {
+                    t.samples.iter().map(|s| filmcraft_media::reader::read_range(&*bytes.0, s.offset, (s.size as usize).min(cap)).unwrap_or_default()).collect()
+                };
+                let order = avc_presentation_order(&heads(AVC_ORDER_HEAD)).or_else(|| {
+                    let all_short = t.samples.iter().all(|s| s.size as usize <= AVC_ORDER_HEAD);
+                    if all_short { None } else { avc_presentation_order(&heads(AVC_ORDER_HEAD_MAX)) }
+                });
+                match order {
                     Some(pts) => {
                         vorder = vec![0; pts.len()];
                         for (s, &d) in pts.iter().enumerate() {
diff --git a/crates/codecs/tests/it/mxf_oracle.rs b/crates/codecs/tests/it/mxf_oracle.rs
--- a/crates/codecs/tests/it/mxf_oracle.rs
+++ b/crates/codecs/tests/it/mxf_oracle.rs
@@ -5,6 +5,7 @@
 use crate::common;
 
 use std::path::{Path, PathBuf};
+use std::sync::Arc;
 
 use common::*;
 use filmcraft_codecs::MxfSource;
@@ -24,6 +25,12 @@ fn spec(name: &str) -> Vec<String> {
             NOISE,
             &["-t", "2", "-c:v", "libx264", "-bf", "2", "-g", "12", "-pix_fmt", "yuv420p", "-c:a", "pcm_s16le", "-ac", "2", "-timecode", "01:00:00:00"],
         ]),
+        // the same at 720p and a high rate: pictures far longer than the head read for the order
+        "mxf_h264_bf2_720p.mxf" => cat(&[
+            &["-f", "lavfi", "-i", "testsrc2=size=1280x720:rate=25"],
+            NOISE,
+            &["-t", "2", "-c:v", "libx264", "-bf", "2", "-g", "12", "-b:v", "12M", "-pix_fmt", "yuv420p", "-c:a", "pcm_s16le", "-ac", "2"],
+        ]),
         "mxf_h264_2997df.mxf" => cat(&[
             &["-f", "lavfi", "-i", "testsrc2=size=320x240:rate=30000/1001"],
             NOISE,
@@ -111,6 +118,7 @@ fn spec(name: &str) -> Vec<String> {
 
 pub const ALL: &[&str] = &[
     "mxf_h264_bf2.mxf",
+    "mxf_h264_bf2_720p.mxf",
     "mxf_h264_2997df.mxf",
     "mxf_dnxhr_lb.mxf",
     "mxf_prores.mxf",
@@ -225,6 +233,41 @@ fn h264_long_gop_with_b_frames_bit_exact() {
     check_audio_exact(&ff, &f, &[]);
 }
 
+/// Reordering B pictures reads only a short head of every picture, not the pictures.
+#[test]
+fn h264_long_gop_order_needs_only_the_slice_headers() {
+    use std::sync::Mutex;
+
+    /// Remembers the length of every read.
+    struct Counting(filmcraft_media::SharedReader, Arc<Mutex<Vec<usize>>>);
+    impl filmcraft_media::reader::ByteReader for Counting {
+        fn len(&self) -> u64 {
+            self.0.len()
+        }
+        fn read_at(&self, offset: u64, buf: &mut [u8]) -> std::io::Result<()> {
+            self.1.lock().unwrap().push(buf.len());
+            self.0.read_at(offset, buf)
+        }
+    }
+
+    let ff = filmcraft_testkit::require_ffmpeg!();
+    let f = make(&ff, "mxf_h264_bf2_720p.mxf");
+    let data = bytes(&f);
+    let reads = Arc::new(Mutex::new(Vec::new()));
+    let reader = Arc::new(Counting(Arc::new(filmcraft_media::reader::MemReader(data.clone())), reads.clone()));
+    let src = MxfSource::open_reader("bf2_720p.mxf", reader).unwrap();
+    let t = &src.file().tracks[src.file().track_of_kind(filmcraft_mxf::TrackKind::Picture).unwrap()];
+    assert!(t.needs_reorder && !t.temporal_offsets);
+    assert!(t.samples.iter().all(|s| s.size > 16 << 10), "the pictures must be longer than the long head read");
+    // one 2 KiB head per picture, and no 16 KiB read (the fallback for a slice header that does
+    // not fit in the short head)
+    let reads = reads.lock().unwrap();
+    assert_eq!(reads.iter().filter(|&&n| n == 2 << 10).count(), t.samples.len());
+    assert!(!reads.contains(&(16 << 10)));
+    // the order found from the short heads is the right one
+    check_video(&ff, &f, "yuv420p", (2, 2), 1, 0, 10);
+}
+
 #[test]
 fn h264_2997_drop_frame_timecode_and_24_bit_audio() {
     let ff = filmcraft_testkit::require_ffmpeg!();
