Skip to main content

wmv_decoder/
api.rs

1//! Public library API.
2
3#[cfg(target_os = "uefi")]
4use std::collections::hash_map::DefaultHasher;
5use std::collections::{HashMap, VecDeque};
6#[cfg(target_os = "uefi")]
7use std::hash::BuildHasherDefault;
8use std::io::{Read, Seek, SeekFrom};
9
10use crate::asf::{AsfFile, AsfPayload, VideoStreamInfo};
11use crate::decoder::{MacroblockDecoder, YuvFrame};
12use crate::error::{DecoderError, Result};
13use crate::vc1::{PictureHeader, SequenceHeader, vc1_unescape_buffer};
14#[cfg(feature = "audio")]
15use crate::wma::{PcmFrameF32, WmaDecoder, WmaProDecoder};
16use crate::wmv2::{Wmv2FrameHeader, Wmv2FrameType, Wmv2Params};
17
18/// A decoded video frame with timing metadata.
19#[derive(Clone)]
20pub struct DecodedFrame {
21    pub pts_ms: u32,
22    pub is_key_frame: bool,
23    pub frame: YuvFrame,
24}
25
26/// A decoded audio frame with timing metadata.
27#[cfg(feature = "audio")]
28#[derive(Clone)]
29pub struct DecodedAudioFrame {
30    pub pts_ms: u32,
31    pub frame: PcmFrameF32,
32}
33
34/// WMV2 (Windows Media Video 8) decoder.
35///
36/// The picture header parsing and macroblock decode paths are aligned with upstream.
37pub struct Wmv2Decoder {
38    params: Wmv2Params,
39    mb_dec: MacroblockDecoder,
40    cur: YuvFrame,
41    locked_hdr_off: Option<usize>,
42}
43
44impl Wmv2Decoder {
45    /// Create a decoder for a fixed resolution.
46    ///
47    /// `extradata` is the 4-byte WMV2 ext header typically carried in ASF stream properties.
48    pub fn new(width: u32, height: u32, extradata: &[u8]) -> Self {
49        let params = Wmv2Params::new(width, height);
50        let mut mb_dec = MacroblockDecoder::new(width, height);
51        mb_dec.wmv2_set_extradata(extradata);
52        let cur = YuvFrame::new(width, height);
53        Self {
54            params,
55            mb_dec,
56            cur,
57            locked_hdr_off: None,
58        }
59    }
60
61    pub fn width(&self) -> u32 {
62        self.params.width
63    }
64
65    pub fn height(&self) -> u32 {
66        self.params.height
67    }
68
69    /// Borrow the internal YUV420p frame buffer.
70    ///
71    /// The returned reference stays valid until the next successful decode.
72    pub fn current_frame(&self) -> &YuvFrame {
73        &self.cur
74    }
75
76    /// Decode one assembled WMV2 frame payload.
77    ///
78    /// Returns `Ok(None)` if no plausible picture header can be found.
79    pub fn decode_frame(
80        &mut self,
81        payload: &[u8],
82        is_key_frame: bool,
83    ) -> Result<Option<&YuvFrame>> {
84        if payload.is_empty() {
85            return Ok(None);
86        }
87
88        let mut best_score: i64 = -1;
89        let mut best_off: usize = 0;
90        let mut best_hdr: Option<Wmv2FrameHeader> = None;
91
92        // Try the previously locked offset first, then fall back to a small scan.
93        let mut offs: Vec<usize> = Vec::with_capacity(18);
94        if let Some(o) = self.locked_hdr_off {
95            offs.push(o);
96        }
97        for o in 0..=16 {
98            if Some(o) != self.locked_hdr_off {
99                offs.push(o);
100            }
101        }
102
103        for off in offs {
104            if off > payload.len() {
105                continue;
106            }
107            let cands = Wmv2FrameHeader::parse_candidates(
108                &payload[off..],
109                self.mb_dec.width_mb,
110                self.mb_dec.height_mb,
111            );
112            if cands.is_empty() {
113                continue;
114            }
115            for h in cands {
116                // ASF keyframe marking should correspond to WMV2 I pictures.
117                if is_key_frame && h.frame_type != Wmv2FrameType::I {
118                    continue;
119                }
120
121                // upstream-aligned scoring strategy.
122                let mut sc: i64 = if h.frame_skipped {
123                    1
124                } else if is_key_frame {
125                    2
126                } else {
127                    self.mb_dec.probe_wmv2_payload(&payload[off..], &h) as i64
128                };
129
130                if Some(off) == self.locked_hdr_off {
131                    sc += 64;
132                }
133
134                if sc > best_score {
135                    best_score = sc;
136                    best_off = off;
137                    best_hdr = Some(h);
138                }
139            }
140        }
141
142        let Some(hdr) = best_hdr else {
143            return Ok(None);
144        };
145
146        if self.locked_hdr_off.is_none() {
147            self.locked_hdr_off = Some(best_off);
148        }
149
150        let frame_data = &payload[best_off..];
151        self.mb_dec
152            .decode_wmv2_frame(frame_data, &hdr, &self.params, &mut self.cur)?;
153        Ok(Some(&self.cur))
154    }
155
156    /// Decode and return an owned frame buffer (clone).
157    pub fn decode_frame_owned(
158        &mut self,
159        payload: &[u8],
160        is_key_frame: bool,
161    ) -> Result<Option<YuvFrame>> {
162        let Some(f) = self.decode_frame(payload, is_key_frame)? else {
163            return Ok(None);
164        };
165        Ok(Some(f.clone()))
166    }
167}
168
169/// Native WMV3 (VC-1 Simple/Main profile) decoder.
170pub struct Wmv3Decoder {
171    seq: SequenceHeader,
172    mb_dec: MacroblockDecoder,
173    cur: YuvFrame,
174}
175
176impl Wmv3Decoder {
177    pub fn new(width: u32, height: u32, extradata: &[u8]) -> Result<Self> {
178        let mut seq = SequenceHeader::parse(extradata)?;
179        seq.width = width;
180        seq.height = height;
181        seq.display_width = width;
182        seq.display_height = height;
183
184        // VC-1 Simple/Main reconstructs a complete macroblock surface, not only
185        // the display rectangle.  FFmpeg likewise sets h_edge_pos/v_edge_pos to
186        // mb_width*16 / mb_height*16.  Those non-display pixels are real coded
187        // reference samples: later P/B motion compensation may legally point
188        // into the final partial macroblock row/column.  Decoding directly into
189        // a width*height surface discards them (e.g. rows 1080..1087 for 1080p)
190        // and turns later MC into visible-edge replication.
191        let coded_width = width
192            .checked_add(15)
193            .ok_or_else(|| DecoderError::InvalidData("WMV3 width overflow".into()))?
194            / 16
195            * 16;
196        let coded_height = height
197            .checked_add(15)
198            .ok_or_else(|| DecoderError::InvalidData("WMV3 height overflow".into()))?
199            / 16
200            * 16;
201
202        Ok(Self {
203            seq,
204            mb_dec: MacroblockDecoder::new(coded_width, coded_height),
205            cur: YuvFrame::new(coded_width, coded_height),
206        })
207    }
208
209    pub fn width(&self) -> u32 {
210        self.seq.width
211    }
212    pub fn height(&self) -> u32 {
213        self.seq.height
214    }
215
216    fn visible_frame(&self) -> YuvFrame {
217        let width = self.seq.width as usize;
218        let height = self.seq.height as usize;
219        let src_width = self.cur.width as usize;
220        let src_height = self.cur.height as usize;
221        debug_assert!(width <= src_width && height <= src_height);
222
223        let mut out = YuvFrame::new(self.seq.width, self.seq.height);
224        for y in 0..height {
225            let src = y * src_width;
226            let dst = y * width;
227            out.y[dst..dst + width].copy_from_slice(&self.cur.y[src..src + width]);
228        }
229
230        let cw = width / 2;
231        let ch = height / 2;
232        let src_cw = src_width / 2;
233        for y in 0..ch {
234            let src = y * src_cw;
235            let dst = y * cw;
236            out.cb[dst..dst + cw].copy_from_slice(&self.cur.cb[src..src + cw]);
237            out.cr[dst..dst + cw].copy_from_slice(&self.cur.cr[src..src + cw]);
238        }
239        out
240    }
241
242    pub fn decode_frame_owned(
243        &mut self,
244        payload: &[u8],
245        is_key_frame: bool,
246        pts_ms: u32,
247    ) -> Result<Option<YuvFrame>> {
248        if payload.is_empty() {
249            return Ok(None);
250        }
251        let mb_w = self.seq.width.div_ceil(16) as usize;
252        let mb_h = self.seq.height.div_ceil(16) as usize;
253        let hdr = PictureHeader::parse(payload, &self.seq, pts_ms, mb_w, mb_h)?;
254        if is_key_frame
255            && !matches!(
256                hdr.frame_type,
257                crate::vc1::FrameType::I | crate::vc1::FrameType::BI
258            )
259        {
260            log::debug!(
261                "ASF key-frame flag disagrees with WMV3 PTYPE: {:?}",
262                hdr.frame_type
263            );
264        }
265        self.mb_dec
266            .decode_frame(payload, &hdr, &self.seq, &mut self.cur)?;
267        // Keep the macroblock-aligned surface internally for future references;
268        // only crop when handing a frame to callers/rendering.
269        Ok(Some(self.visible_frame()))
270    }
271}
272
273/// Native VC-1 Advanced Profile decoder for ASF/WVC1 streams.
274///
275/// The compressed picture reconstruction is shared with the WMV3 decoder,
276/// while WVC1 uses Advanced Profile sequence/entry-point syntax and VC-1
277/// emulation-prevention escaping at the transport boundary.
278pub struct Wvc1Decoder {
279    seq: SequenceHeader,
280    mb_dec: MacroblockDecoder,
281    cur: YuvFrame,
282}
283
284impl Wvc1Decoder {
285    pub fn new(asf_width: u32, asf_height: u32, extradata: &[u8]) -> Result<Self> {
286        let mut seq = SequenceHeader::parse_wvc1(extradata)?;
287        if seq.interlace {
288            return Err(DecoderError::Unsupported(
289                "VC-1 Advanced interlaced pictures are not implemented by the native decoder"
290                    .into(),
291            ));
292        }
293        if seq.range_mapy.is_some() || seq.range_mapuv.is_some() {
294            return Err(DecoderError::Unsupported(
295                "VC-1 Advanced range mapping is not implemented by the native decoder".into(),
296            ));
297        }
298
299        // ASF BITMAPINFOHEADER dimensions describe the displayed video while
300        // the Advanced sequence/entry-point headers describe the coded
301        // reference surface. They normally match; refuse contradictory
302        // metadata rather than silently decoding with the wrong MB geometry.
303        if asf_width != 0 && asf_width > seq.width {
304            return Err(DecoderError::InvalidData(format!(
305                "WVC1 ASF width {asf_width} exceeds coded width {}",
306                seq.width
307            )));
308        }
309        if asf_height != 0 && asf_height > seq.height {
310            return Err(DecoderError::InvalidData(format!(
311                "WVC1 ASF height {asf_height} exceeds coded height {}",
312                seq.height
313            )));
314        }
315        if asf_width != 0 {
316            seq.display_width = asf_width;
317        }
318        if asf_height != 0 {
319            seq.display_height = asf_height;
320        }
321
322        let coded_width = seq
323            .width
324            .checked_add(15)
325            .ok_or_else(|| DecoderError::InvalidData("WVC1 width overflow".into()))?
326            / 16
327            * 16;
328        let coded_height = seq
329            .height
330            .checked_add(15)
331            .ok_or_else(|| DecoderError::InvalidData("WVC1 height overflow".into()))?
332            / 16
333            * 16;
334
335        Ok(Self {
336            seq,
337            mb_dec: MacroblockDecoder::new(coded_width, coded_height),
338            cur: YuvFrame::new(coded_width, coded_height),
339        })
340    }
341
342    pub fn width(&self) -> u32 {
343        self.seq.display_width.min(self.seq.width)
344    }
345
346    pub fn height(&self) -> u32 {
347        self.seq.display_height.min(self.seq.height)
348    }
349
350    fn visible_frame(&self) -> YuvFrame {
351        let width = self.seq.display_width.min(self.seq.width) as usize;
352        let height = self.seq.display_height.min(self.seq.height) as usize;
353        let src_width = self.cur.width as usize;
354        let mut out = YuvFrame::new(width as u32, height as u32);
355
356        for y in 0..height {
357            let src = y * src_width;
358            let dst = y * width;
359            out.y[dst..dst + width].copy_from_slice(&self.cur.y[src..src + width]);
360        }
361
362        let cw = width / 2;
363        let ch = height / 2;
364        let src_cw = src_width / 2;
365        for y in 0..ch {
366            let src = y * src_cw;
367            let dst = y * cw;
368            out.cb[dst..dst + cw].copy_from_slice(&self.cur.cb[src..src + cw]);
369            out.cr[dst..dst + cw].copy_from_slice(&self.cur.cr[src..src + cw]);
370        }
371        out
372    }
373
374    fn marker_at(data: &[u8], from: usize) -> Option<usize> {
375        if data.len() < 4 || from > data.len().saturating_sub(4) {
376            return None;
377        }
378        (from..=data.len() - 4).find(|&i| data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1)
379    }
380
381    /// Convert one ASF WVC1 media object into the raw Advanced picture
382    /// bitstream consumed by `PictureHeader`/`MacroblockDecoder`.
383    ///
384    /// FFmpeg accepts both bare escaped WVC1 pictures and start-code-delimited
385    /// VC-1 access units. Handle both forms and apply in-band entry points.
386    fn prepare_picture_payload(&mut self, payload: &[u8]) -> Result<Vec<u8>> {
387        let Some(first) = Self::marker_at(payload, 0) else {
388            return Ok(vc1_unescape_buffer(payload));
389        };
390
391        // Bytes before the first recognized marker are not picture syntax.
392        // WVC1 ASF normally starts at the marker when markers are present.
393        let mut pos = first;
394        let mut frame: Option<Vec<u8>> = None;
395        while let Some(start) = Self::marker_at(payload, pos) {
396            let marker = payload[start + 3];
397            let next = Self::marker_at(payload, start + 4).unwrap_or(payload.len());
398            let body = &payload[start + 4..next];
399            match marker {
400                0x0d => {
401                    frame = Some(vc1_unescape_buffer(body));
402                }
403                0x0e => {
404                    let entry = vc1_unescape_buffer(body);
405                    let old_width = self.seq.width;
406                    let old_height = self.seq.height;
407                    self.seq.apply_wvc1_entry_point(&entry)?;
408                    if self.seq.width != old_width || self.seq.height != old_height {
409                        return Err(DecoderError::Unsupported(format!(
410                            "in-band WVC1 coded-size change {}x{} -> {}x{} requires decoder reinitialization",
411                            old_width, old_height, self.seq.width, self.seq.height
412                        )));
413                    }
414                    if self.seq.interlace {
415                        return Err(DecoderError::Unsupported(
416                            "VC-1 Advanced interlaced pictures are not implemented by the native decoder"
417                                .into(),
418                        ));
419                    }
420                    if self.seq.range_mapy.is_some() || self.seq.range_mapuv.is_some() {
421                        return Err(DecoderError::Unsupported(
422                            "VC-1 Advanced range mapping is not implemented by the native decoder"
423                                .into(),
424                        ));
425                    }
426                }
427                0x0b => {
428                    return Err(DecoderError::Unsupported(
429                        "VC-1 Advanced slice start codes are not implemented by the native decoder"
430                            .into(),
431                    ));
432                }
433                0x0c => {
434                    return Err(DecoderError::Unsupported(
435                        "VC-1 Advanced field start codes require interlaced decoding".into(),
436                    ));
437                }
438                0x0f => {
439                    // A new sequence header changes coded dimensions/profile
440                    // state and requires decoder-buffer reinitialization. ASF
441                    // WVC1 carries the stable sequence header in extradata;
442                    // reject an in-band format switch instead of reusing stale
443                    // reference surfaces.
444                    return Err(DecoderError::Unsupported(
445                        "in-band VC-1 Advanced sequence changes are not supported".into(),
446                    ));
447                }
448                0x0a | 0x00..=0x09 => {}
449                _ => {}
450            }
451            if next >= payload.len() {
452                break;
453            }
454            pos = next;
455        }
456
457        frame.ok_or_else(|| {
458            DecoderError::InvalidData("WVC1 access unit has no VC-1 frame start code".into())
459        })
460    }
461
462    pub fn decode_frame_owned(
463        &mut self,
464        payload: &[u8],
465        is_key_frame: bool,
466        pts_ms: u32,
467    ) -> Result<Option<YuvFrame>> {
468        if payload.is_empty() {
469            return Ok(None);
470        }
471        let frame_payload = self.prepare_picture_payload(payload)?;
472        if frame_payload.is_empty() {
473            return Ok(None);
474        }
475        let mb_w = self.seq.width.div_ceil(16) as usize;
476        let mb_h = self.seq.height.div_ceil(16) as usize;
477        let hdr = PictureHeader::parse(&frame_payload, &self.seq, pts_ms, mb_w, mb_h)?;
478        if is_key_frame
479            && !matches!(
480                hdr.frame_type,
481                crate::vc1::FrameType::I | crate::vc1::FrameType::BI
482            )
483        {
484            log::debug!(
485                "ASF key-frame flag disagrees with WVC1 PTYPE: {:?}",
486                hdr.frame_type
487            );
488        }
489        self.mb_dec
490            .decode_frame(&frame_payload, &hdr, &self.seq, &mut self.cur)?;
491        Ok(Some(self.visible_frame()))
492    }
493}
494
495enum VideoCodecDecoder {
496    Wmv12(Wmv2Decoder),
497    Wmv3(Wmv3Decoder),
498    Wvc1(Wvc1Decoder),
499}
500
501impl VideoCodecDecoder {
502    fn decode_frame_owned(
503        &mut self,
504        payload: &[u8],
505        is_key: bool,
506        pts_ms: u32,
507    ) -> Result<Option<YuvFrame>> {
508        match self {
509            Self::Wmv12(d) => d.decode_frame_owned(payload, is_key),
510            Self::Wmv3(d) => d.decode_frame_owned(payload, is_key, pts_ms),
511            Self::Wvc1(d) => d.decode_frame_owned(payload, is_key, pts_ms),
512        }
513    }
514}
515
516// ─────────────────────────────────────────────────────────────────────────────
517// ASF media-object reassembly (frame reassembly)
518// ─────────────────────────────────────────────────────────────────────────────
519
520#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
521struct FrameKey {
522    stream_number: u8,
523    object_id: u32,
524}
525
526#[derive(Debug, Clone)]
527struct FrameAssembly {
528    total: usize,
529    pts_ms: u32,
530    is_key: bool,
531    data: Vec<u8>,
532    ranges: Vec<(usize, usize)>,
533}
534
535impl FrameAssembly {
536    fn new(total: usize, pts_ms: u32, is_key: bool) -> Self {
537        Self {
538            total,
539            pts_ms,
540            is_key,
541            data: vec![0u8; total],
542            ranges: Vec::new(),
543        }
544    }
545
546    fn insert(&mut self, offset: usize, frag: &[u8]) {
547        if self.total == 0 || offset >= self.total || frag.is_empty() {
548            return;
549        }
550        let end = (offset + frag.len()).min(self.total);
551        let n = end - offset;
552        self.data[offset..end].copy_from_slice(&frag[..n]);
553        self.add_range(offset, end);
554    }
555
556    fn add_range(&mut self, start: usize, end: usize) {
557        if start >= end {
558            return;
559        }
560        self.ranges.push((start, end));
561        self.ranges.sort_by_key(|r| r.0);
562
563        let mut merged: Vec<(usize, usize)> = Vec::with_capacity(self.ranges.len());
564        for (s, e) in self.ranges.drain(..) {
565            if let Some(last) = merged.last_mut()
566                && s <= last.1
567            {
568                last.1 = last.1.max(e);
569                continue;
570            }
571            merged.push((s, e));
572        }
573        self.ranges = merged;
574    }
575
576    fn covered_len(&self) -> usize {
577        self.ranges.iter().map(|(s, e)| e - s).sum()
578    }
579
580    fn is_complete(&self) -> bool {
581        self.total > 0
582            && self.covered_len() >= self.total
583            && self.ranges.len() == 1
584            && self.ranges[0] == (0, self.total)
585    }
586}
587
588#[cfg(target_os = "uefi")]
589type InFlightMap = HashMap<FrameKey, FrameAssembly, BuildHasherDefault<DefaultHasher>>;
590#[cfg(not(target_os = "uefi"))]
591type InFlightMap = HashMap<FrameKey, FrameAssembly>;
592
593#[derive(Default)]
594struct FrameAssembler {
595    in_flight: InFlightMap,
596}
597
598impl FrameAssembler {
599    fn push(&mut self, payload: AsfPayload) -> Option<(u32, bool, Vec<u8>)> {
600        if payload.data.is_empty() {
601            return None;
602        }
603
604        let key = FrameKey {
605            stream_number: payload.stream_number,
606            object_id: payload.object_id,
607        };
608
609        // Fast path: complete media object in one payload (or size unknown).
610        if payload.obj_offset == 0 {
611            let osz = payload.obj_size as usize;
612            if osz == 0 || osz == payload.data.len() {
613                return Some((payload.pts_ms, payload.is_key_frame, payload.data));
614            }
615        }
616
617        // If the total object size is unknown, we cannot reliably reassemble.
618        if payload.obj_size == 0 {
619            return Some((payload.pts_ms, payload.is_key_frame, payload.data));
620        }
621
622        let total = payload.obj_size as usize;
623        let entry = self
624            .in_flight
625            .entry(key)
626            .or_insert_with(|| FrameAssembly::new(total, payload.pts_ms, payload.is_key_frame));
627
628        // Update meta (first PTS wins; keyframe if any fragment says so).
629        entry.is_key |= payload.is_key_frame;
630        entry.insert(payload.obj_offset as usize, &payload.data);
631
632        if entry.is_complete() {
633            let assembly = self.in_flight.remove(&key).unwrap();
634            return Some((assembly.pts_ms, assembly.is_key, assembly.data));
635        }
636        None
637    }
638}
639
640/// ASF + WMV2 decoding pipeline.
641///
642/// This type owns the `Read+Seek` source, parses ASF headers, reassembles media objects
643/// and decodes WMV2 frames into `YuvFrame`.
644pub struct AsfWmv2Decoder<R: Read + Seek> {
645    reader: R,
646    asf: AsfFile,
647    video_info: VideoStreamInfo,
648    assembler: FrameAssembler,
649    /// Completed ASF media objects already returned by `AsfFile::read_packet`
650    /// but not yet consumed by `next_frame`. A single ASF packet may contain
651    /// multiple compressed payloads/media objects; dropping the tail after
652    /// returning the first decoded picture corrupts the VC-1 reference chain.
653    pending_payloads: VecDeque<AsfPayload>,
654    decoder: VideoCodecDecoder,
655}
656
657/// ASF + WMA decoding pipeline (WMA v1/v2 and WMA Professional).
658///
659/// This type owns the `Read+Seek` source, parses ASF headers, reassembles media objects
660/// and decodes WMA packets into PCM.
661#[cfg(feature = "audio")]
662enum AudioCodecDecoder {
663    Wma12(Box<WmaDecoder>),
664    WmaPro(Box<WmaProDecoder>),
665}
666
667#[cfg(feature = "audio")]
668impl AudioCodecDecoder {
669    fn sample_rate(&self) -> u32 {
670        match self {
671            Self::Wma12(d) => d.sample_rate(),
672            Self::WmaPro(d) => d.sample_rate(),
673        }
674    }
675
676    fn channels(&self) -> u16 {
677        match self {
678            Self::Wma12(d) => d.channels(),
679            Self::WmaPro(d) => d.channels(),
680        }
681    }
682
683    fn decode_packet(&mut self, packet: &[u8], pts_ms: u32) -> Result<Option<PcmFrameF32>> {
684        match self {
685            Self::Wma12(d) => d.decode_packet(packet, pts_ms),
686            Self::WmaPro(d) => d.decode_packet(packet, pts_ms),
687        }
688    }
689}
690
691#[cfg(feature = "audio")]
692pub struct AsfWmaDecoder<R: Read + Seek> {
693    reader: R,
694    asf: AsfFile,
695    audio_stream_number: u8,
696    audio_format_tag: u16,
697    audio_block_align: u16,
698    decoder: AudioCodecDecoder,
699    assembler: FrameAssembler,
700    /// Completed ASF media objects already returned by `AsfFile::read_packet`
701    /// but not yet consumed by `next_frame`. As with WMV video, one ASF packet
702    /// can contain multiple WMA media objects. Returning one decoded PCM frame
703    /// must not discard the remaining objects because WMA superframes / bit
704    /// reservoir state depends on consuming the stream strictly in order.
705    pending_payloads: VecDeque<AsfPayload>,
706    last_pts_ms: u32,
707    flushed_eof: bool,
708}
709
710#[cfg(feature = "audio")]
711impl<R: Read + Seek> AsfWmaDecoder<R> {
712    /// Open an ASF/WMV stream and initialize the WMA decoder.
713    ///
714    /// The decoder selects the first audio stream with format tag 0x0160 (WMAv1),
715    /// 0x0161 (WMAv2), or 0x0162 (WMA Professional).
716    pub fn open(mut reader: R) -> Result<Self> {
717        let asf = AsfFile::open(&mut reader)?;
718        let mut chosen = None;
719        for a in asf.audio_streams.iter() {
720            if matches!(a.format_tag, 0x0160..=0x0162) {
721                chosen = Some(a.clone());
722                break;
723            }
724        }
725        let Some(audio_info) = chosen else {
726            return Err(DecoderError::Unsupported(
727                "No supported WMA (0x0160/0x0161/0x0162) audio stream found".into(),
728            ));
729        };
730
731        reader.seek(SeekFrom::Start(asf.data_offset))?;
732        let decoder = match audio_info.format_tag {
733            0x0162 => AudioCodecDecoder::WmaPro(Box::new(WmaProDecoder::new(&audio_info)?)),
734            _ => AudioCodecDecoder::Wma12(Box::new(WmaDecoder::new(&audio_info)?)),
735        };
736
737        Ok(Self {
738            reader,
739            asf,
740            audio_stream_number: audio_info.stream_number,
741            audio_format_tag: audio_info.format_tag,
742            audio_block_align: audio_info.block_align,
743            decoder,
744            assembler: FrameAssembler::default(),
745            pending_payloads: VecDeque::new(),
746            last_pts_ms: 0,
747            flushed_eof: false,
748        })
749    }
750
751    pub fn sample_rate(&self) -> u32 {
752        self.decoder.sample_rate()
753    }
754
755    pub fn channels(&self) -> u16 {
756        self.decoder.channels()
757    }
758
759    pub fn duration_ms(&self) -> Option<u64> {
760        self.asf.play_duration_ms
761    }
762
763    /// Decode the next audio frame.
764    ///
765    /// Returns `Ok(None)` on end-of-stream.
766    pub fn next_frame(&mut self) -> Result<Option<DecodedAudioFrame>> {
767        loop {
768            // `AsfFile::read_packet` can complete multiple WMA media objects in
769            // one ASF packet. `next_frame` returns only one PCM frame, so keep
770            // the packet tail across calls exactly like `AsfWmv2Decoder`.
771            // Dropping those objects desynchronizes stateful WMA superframes /
772            // bit reservoir and eventually produces frame-length/bitstream
773            // overflow errors even though the ASF stream itself is valid.
774            let payload = if let Some(payload) = self.pending_payloads.pop_front() {
775                payload
776            } else {
777                let payloads = match self.asf.read_packet(&mut self.reader) {
778                    Ok(p) => p,
779                    Err(DecoderError::EndOfStream) => {
780                        if self.flushed_eof {
781                            return Ok(None);
782                        }
783                        self.flushed_eof = true;
784                        if let Some(frame) = self.decoder.decode_packet(&[], self.last_pts_ms)? {
785                            return Ok(Some(DecodedAudioFrame {
786                                pts_ms: frame.pts_ms,
787                                frame,
788                            }));
789                        }
790                        return Ok(None);
791                    }
792                    Err(e) => return Err(e),
793                };
794                self.pending_payloads.extend(
795                    payloads
796                        .into_iter()
797                        .filter(|payload| payload.stream_number == self.audio_stream_number),
798                );
799                let Some(payload) = self.pending_payloads.pop_front() else {
800                    continue;
801                };
802                payload
803            };
804
805            let Some((pts_ms, _is_key, data)) = self.assembler.push(payload) else {
806                continue;
807            };
808            self.last_pts_ms = pts_ms;
809            let audio_format_tag = self.audio_format_tag;
810            let audio_block_align = self.audio_block_align;
811            let media_object_len = data.len();
812            let frame = self.decoder.decode_packet(&data, pts_ms).map_err(|err| {
813                let context = format!(
814                    "WMA tag=0x{audio_format_tag:04x} block_align={audio_block_align} media_object_len={media_object_len} pts_ms={pts_ms}",
815                );
816                match err {
817                    DecoderError::InvalidData(message) => {
818                        DecoderError::InvalidData(format!("{context}: {message}"))
819                    }
820                    DecoderError::Unsupported(message) => {
821                        DecoderError::Unsupported(format!("{context}: {message}"))
822                    }
823                    other => other,
824                }
825            })?;
826            if let Some(frame) = frame {
827                return Ok(Some(DecodedAudioFrame { pts_ms, frame }));
828            }
829        }
830    }
831}
832
833impl<R: Read + Seek> AsfWmv2Decoder<R> {
834    /// Open an ASF/WMV stream and initialize the WMV2 decoder.
835    ///
836    /// The decoder selects the first video stream whose FourCC is WMV1, WMV2, WMV3, or WVC1.
837    pub fn open(mut reader: R) -> Result<Self> {
838        let asf = AsfFile::open(&mut reader)?;
839        let mut video_info: Option<VideoStreamInfo> = None;
840        for v in asf.video_streams.iter() {
841            let four_cc = std::str::from_utf8(&v.codec_four_cc)
842                .unwrap_or("")
843                .to_uppercase();
844            if matches!(four_cc.as_str(), "WMV3" | "WMV2" | "WMV1" | "WVC1") {
845                video_info = Some(v.clone());
846                break;
847            }
848        }
849        let Some(video_info) = video_info else {
850            return Err(DecoderError::Unsupported(
851                "No supported WMV1/WMV2/WMV3/WVC1 video stream found".into(),
852            ));
853        };
854
855        reader.seek(SeekFrom::Start(asf.data_offset))?;
856
857        let four_cc = std::str::from_utf8(&video_info.codec_four_cc)
858            .unwrap_or("")
859            .to_uppercase();
860        let decoder = if four_cc == "WMV3" {
861            VideoCodecDecoder::Wmv3(Wmv3Decoder::new(
862                video_info.width,
863                video_info.height,
864                &video_info.extra_data,
865            )?)
866        } else if four_cc == "WVC1" {
867            VideoCodecDecoder::Wvc1(Wvc1Decoder::new(
868                video_info.width,
869                video_info.height,
870                &video_info.extra_data,
871            )?)
872        } else {
873            VideoCodecDecoder::Wmv12(Wmv2Decoder::new(
874                video_info.width,
875                video_info.height,
876                &video_info.extra_data,
877            ))
878        };
879
880        Ok(Self {
881            reader,
882            asf,
883            video_info,
884            assembler: FrameAssembler::default(),
885            pending_payloads: VecDeque::new(),
886            decoder,
887        })
888    }
889
890    /// Return the selected video stream info.
891    pub fn video_stream_info(&self) -> &VideoStreamInfo {
892        &self.video_info
893    }
894
895    pub fn duration_ms(&self) -> Option<u64> {
896        self.asf.play_duration_ms
897    }
898
899    /// Decode the next video frame.
900    ///
901    /// Returns `Ok(None)` on end-of-stream.
902    pub fn next_frame(&mut self) -> Result<Option<DecodedFrame>> {
903        loop {
904            // `AsfFile::read_packet` returns every completed media object from
905            // one ASF packet. In particular ASF compressed payloads may carry
906            // many tiny WMV pictures in the same packet. `next_frame` returns
907            // only one picture to its caller, so preserve the remaining media
908            // objects here and consume them before reading another ASF packet.
909            //
910            // The old implementation iterated the local `payloads` vector and
911            // returned as soon as the first picture decoded. Rust then dropped
912            // the rest of that vector. Losing even a skip/P picture changes the
913            // VC-1 reference state and makes all following predicted pictures
914            // decode against the wrong anchor.
915            let payload = if let Some(payload) = self.pending_payloads.pop_front() {
916                payload
917            } else {
918                let payloads = match self.asf.read_packet(&mut self.reader) {
919                    Ok(p) => p,
920                    Err(DecoderError::EndOfStream) => return Ok(None),
921                    Err(e) => return Err(e),
922                };
923                self.pending_payloads.extend(
924                    payloads
925                        .into_iter()
926                        .filter(|payload| payload.stream_number == self.video_info.stream_number),
927                );
928                let Some(payload) = self.pending_payloads.pop_front() else {
929                    continue;
930                };
931                payload
932            };
933
934            let Some((pts_ms, is_key, data)) = self.assembler.push(payload) else {
935                continue;
936            };
937
938            if let Some(frame) = self.decoder.decode_frame_owned(&data, is_key, pts_ms)? {
939                return Ok(Some(DecodedFrame {
940                    pts_ms,
941                    is_key_frame: is_key,
942                    frame,
943                }));
944            }
945        }
946    }
947}