wmv_decoder/
asf.rs

1//! ASF (Advanced Systems Format) container parser.
2
3use std::io::{Read, Seek, SeekFrom};
4
5use byteorder::{LittleEndian, ReadBytesExt};
6
7use crate::error::{DecoderError, Result};
8
9// ─── Known ASF GUIDs ─────────────────────────────────────────────────────────
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct Guid(pub [u8; 16]);
13
14impl Guid {
15    pub fn read<R: Read>(r: &mut R) -> Result<Self> {
16        let mut buf = [0u8; 16];
17        r.read_exact(&mut buf)?;
18        Ok(Guid(buf))
19    }
20}
21
22impl std::fmt::Display for Guid {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        let b = &self.0;
25        write!(
26            f,
27            "{:02X}{:02X}{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
28            b[3], b[2], b[1], b[0], b[5], b[4], b[7], b[6], b[8], b[9], b[10], b[11], b[12],
29            b[13], b[14], b[15]
30        )
31    }
32}
33
34// Well-known GUIDs (stored in little-endian GUID format)
35pub const GUID_ASF_HEADER: Guid =
36    Guid([0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C]);
37pub const GUID_ASF_DATA: Guid =
38    Guid([0x36, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C]);
39pub const GUID_FILE_PROPERTIES: Guid =
40    Guid([0xA1, 0xDC, 0xAB, 0x8C, 0x47, 0xA9, 0xCF, 0x11, 0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65]);
41pub const GUID_STREAM_PROPERTIES: Guid =
42    Guid([0x91, 0x07, 0xDC, 0xB7, 0xB7, 0xA9, 0xCF, 0x11, 0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65]);
43pub const GUID_STREAM_TYPE_VIDEO: Guid =
44    Guid([0xC0, 0xEF, 0x19, 0xBC, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B]);
45pub const GUID_STREAM_TYPE_AUDIO: Guid =
46    Guid([0x40, 0x9E, 0x69, 0xF8, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B]);
47
48// ─── ASF Object Header ───────────────────────────────────────────────────────
49
50#[derive(Debug)]
51pub struct ObjectHeader {
52    pub guid: Guid,
53    pub size: u64,
54}
55
56impl ObjectHeader {
57    pub fn read<R: Read>(r: &mut R) -> Result<Self> {
58        let guid = Guid::read(r)?;
59        let size = r.read_u64::<LittleEndian>()?;
60        if size < 24 {
61            return Err(DecoderError::InvalidData("ASF object size < 24".into()));
62        }
63        Ok(Self { guid, size })
64    }
65
66    pub fn payload_size(&self) -> u64 {
67        self.size - 24
68    }
69}
70
71// ─── Stream Information ───────────────────────────────────────────────────────
72
73#[derive(Debug, Clone)]
74pub struct VideoStreamInfo {
75    pub stream_number: u8,
76    pub width: u32,
77    pub height: u32,
78    pub codec_four_cc: [u8; 4],
79    pub extra_data: Vec<u8>,
80}
81
82#[derive(Debug, Clone)]
83pub struct AudioStreamInfo {
84    pub stream_number: u8,
85    pub format_tag: u16,
86    pub channels: u16,
87    pub sample_rate: u32,
88    pub bit_rate: u32,
89    pub block_align: u16,
90    pub bits_per_sample: u16,
91    pub extra_data: Vec<u8>,
92    /// ASF audio descrambling (interleaving) parameters (upstream: ds_span/ds_packet_size/ds_chunk_size).
93    /// span==0 or 1 means descrambling disabled.
94    pub ds_span: u8,
95    pub ds_packet_size: u16,
96    pub ds_chunk_size: u16,
97}
98
99// ─── Output Payload (Complete Media Object) ──────────────────────────────────
100
101#[derive(Debug, Clone)]
102pub struct AsfPayload {
103    pub stream_number: u8,
104    pub object_id: u32,
105    pub obj_offset: u32,
106    pub obj_size: u32,
107    pub pts_ms: u32,
108    pub duration_ms: u16,
109    pub is_key_frame: bool,
110    pub data: Vec<u8>,
111}
112
113#[derive(Debug, Default, Clone)]
114struct AsfStreamState {
115    pkt: Vec<u8>,
116    frag_offset_sum: usize,
117    pkt_clean: bool,
118    seq: u32,
119    pts_ms: u32,
120    is_key: bool,
121}
122
123#[derive(Debug, Clone, Copy, Default)]
124struct AsfAudioDescramble {
125    span: u8,
126    packet_size: u16,
127    chunk_size: u16,
128}
129
130// ─── Top-level ASF File (stateful demuxer) ───────────────────────────────────
131
132pub struct AsfFile {
133    pub video_streams: Vec<VideoStreamInfo>,
134    pub audio_streams: Vec<AudioStreamInfo>,
135
136    pub data_offset: u64,
137    pub packet_count: u64,
138
139    pub packet_size: u32,     // upstream: s->packet_size = hdr.max_pktsize
140    pub min_packet_size: u32, // upstream: hdr.min_pktsize
141    pub preroll_ms: u32,      // upstream: hdr.preroll
142
143    is_audio_stream: [bool; 128],
144    audio_descramble: [AsfAudioDescramble; 128],
145
146    // Per-stream reassembly state.
147    streams: [AsfStreamState; 128],
148}
149
150impl AsfFile {
151    /// Parse the ASF header and locate the Data section.
152    pub fn open<R: Read + Seek>(reader: &mut R) -> Result<Self> {
153        let hdr = ObjectHeader::read(reader)?;
154        if hdr.guid != GUID_ASF_HEADER {
155            return Err(DecoderError::InvalidData("Not an ASF file".into()));
156        }
157
158        let _num_headers = reader.read_u32::<LittleEndian>()?;
159        let _reserved1 = reader.read_u8()?;
160        let _reserved2 = reader.read_u8()?;
161
162        let mut video_streams = Vec::new();
163        let mut audio_streams = Vec::new();
164        let mut is_audio_stream = [false; 128];
165        let mut audio_descramble = std::array::from_fn(|_| AsfAudioDescramble::default());
166
167        let mut packet_count = 0u64;
168        let mut min_pktsize = 0u32;
169        let mut max_pktsize = 0u32;
170        let mut preroll_ms = 0u32;
171
172        let header_end = hdr.size;
173        let mut pos = 24u64 + 4 + 1 + 1;
174
175        while pos < header_end {
176            let obj = ObjectHeader::read(reader)?;
177            let obj_end = pos + obj.size;
178
179            if obj.guid == GUID_FILE_PROPERTIES {
180                // ASFMainHeader: we follow upstream's `asf_read_file_properties`.
181                reader.seek(SeekFrom::Current(16 + 8 + 8))?; // file_id + file_size + create_time
182                packet_count = reader.read_u64::<LittleEndian>()?; // data_packets_count
183                reader.seek(SeekFrom::Current(8 + 8))?; // play_time + send_time
184
185                // preroll is a QWORD in ASF; upstream uses the low 32 bits.
186                preroll_ms = reader.read_u32::<LittleEndian>()?;
187                let _preroll_hi_ignored = reader.read_u32::<LittleEndian>()?;
188
189                let _flags = reader.read_u32::<LittleEndian>()?;
190                min_pktsize = reader.read_u32::<LittleEndian>()?;
191                max_pktsize = reader.read_u32::<LittleEndian>()?;
192                let _max_bitrate = reader.read_u32::<LittleEndian>()?;
193            } else if obj.guid == GUID_STREAM_PROPERTIES {
194                let stream_type = Guid::read(reader)?;
195                let _error_correct = Guid::read(reader)?;
196                let _time_offset = reader.read_u64::<LittleEndian>()?;
197                let type_specific_len = reader.read_u32::<LittleEndian>()? as usize;
198                let _err_correct_len = reader.read_u32::<LittleEndian>()?;
199                let flags = reader.read_u16::<LittleEndian>()?;
200                let stream_number = (flags & 0x7F) as u8;
201                let _reserved = reader.read_u32::<LittleEndian>()?;
202
203                if stream_type == GUID_STREAM_TYPE_VIDEO {
204                    let _enc_width = reader.read_u32::<LittleEndian>()?;
205                    let _enc_height = reader.read_u32::<LittleEndian>()?;
206                    reader.read_u8()?;
207                    let fmt_data_size = reader.read_u16::<LittleEndian>()? as usize;
208
209                    let _bi_size = reader.read_u32::<LittleEndian>()?;
210                    let width = reader.read_u32::<LittleEndian>()?;
211                    let height_i = reader.read_i32::<LittleEndian>()?;
212                    let height = height_i.unsigned_abs();
213                    let _planes = reader.read_u16::<LittleEndian>()?;
214                    let _bit_count = reader.read_u16::<LittleEndian>()?;
215                    let mut four_cc = [0u8; 4];
216                    reader.read_exact(&mut four_cc)?;
217                    reader.seek(SeekFrom::Current(20))?;
218
219                    let extra_len = if fmt_data_size > 40 { fmt_data_size - 40 } else { 0 };
220                    let mut extra_data = vec![0u8; extra_len];
221                    reader.read_exact(&mut extra_data)?;
222
223                    video_streams.push(VideoStreamInfo {
224                        stream_number,
225                        width,
226                        height,
227                        codec_four_cc: four_cc,
228                        extra_data,
229                    });
230                } else if stream_type == GUID_STREAM_TYPE_AUDIO {
231                    is_audio_stream[stream_number as usize] = true;
232
233                    let format_tag = reader.read_u16::<LittleEndian>()?;
234                    let channels = reader.read_u16::<LittleEndian>()?;
235                    let sample_rate = reader.read_u32::<LittleEndian>()?;
236                    let bit_rate = reader.read_u32::<LittleEndian>()? * 8; // avg bytes/sec -> bps
237                    let block_align = reader.read_u16::<LittleEndian>()?;
238                    let bits_per_sample = reader.read_u16::<LittleEndian>()?;
239
240                    let (cb_size, base_len) = if type_specific_len >= 18 {
241                        (reader.read_u16::<LittleEndian>()? as usize, 18usize)
242                    } else {
243                        (0usize, 16usize)
244                    };
245
246                    let mut extra_data = vec![0u8; cb_size];
247                    if cb_size != 0 {
248                        reader.read_exact(&mut extra_data)?;
249                    }
250
251                    let consumed = base_len + cb_size;
252                    let remain = type_specific_len.saturating_sub(consumed);
253                    if remain != 0 {
254                        reader.seek(SeekFrom::Current(remain as i64))?;
255                    }
256
257                    let mut ds_span: u8 = 0;
258                    let mut ds_packet_size: u16 = 0;
259                    let mut ds_chunk_size: u16 = 0;
260                    let pos2 = reader.stream_position()?;
261                    if (obj_end as i128) - (pos2 as i128) >= 8 {
262                        ds_span = reader.read_u8()?;
263                        ds_packet_size = reader.read_u16::<LittleEndian>()?;
264                        ds_chunk_size = reader.read_u16::<LittleEndian>()?;
265                        let _ds_data_size = reader.read_u16::<LittleEndian>()?;
266                        let _ds_silence = reader.read_u8()?;
267
268                        if ds_span > 1 {
269                            if ds_chunk_size == 0
270                                || (ds_packet_size / ds_chunk_size) <= 1
271                                || (ds_packet_size % ds_chunk_size) != 0
272                            {
273                                ds_span = 0;
274                            }
275                        }
276                    }
277
278                    audio_descramble[stream_number as usize] = AsfAudioDescramble {
279                        span: ds_span,
280                        packet_size: ds_packet_size,
281                        chunk_size: ds_chunk_size,
282                    };
283
284                    audio_streams.push(AudioStreamInfo {
285                        stream_number,
286                        format_tag,
287                        channels,
288                        sample_rate,
289                        bit_rate,
290                        block_align,
291                        bits_per_sample,
292                        extra_data,
293                        ds_span,
294                        ds_packet_size,
295                        ds_chunk_size,
296                    });
297                } else {
298                    reader.seek(SeekFrom::Current(type_specific_len as i64))?;
299                }
300            }
301
302            pos = obj_end;
303            reader.seek(SeekFrom::Start(obj_end))?;
304        }
305
306        let data_obj = ObjectHeader::read(reader)?;
307        if data_obj.guid != GUID_ASF_DATA {
308            return Err(DecoderError::InvalidData(
309                "Expected ASF Data Object after header".into(),
310            ));
311        }
312
313        // Data Object payload begins with a FileID GUID (16), total packets (8), reserved (2).
314        reader.seek(SeekFrom::Current(16 + 8 + 2))?;
315        let data_offset = reader.stream_position()?;
316
317        if max_pktsize == 0 {
318            return Err(DecoderError::InvalidData("ASF max packet size is 0".into()));
319        }
320
321        Ok(Self {
322            video_streams,
323            audio_streams,
324            data_offset,
325            packet_count,
326            packet_size: max_pktsize,
327            min_packet_size: min_pktsize,
328            preroll_ms,
329            is_audio_stream,
330            audio_descramble,
331            streams: std::array::from_fn(|_| AsfStreamState::default()),
332        })
333    }
334
335    fn descramble_audio_if_needed(&self, stream_num: u8, data: Vec<u8>) -> Vec<u8> {
336        let ds = self.audio_descramble[stream_num as usize];
337        if ds.span <= 1 {
338            return data;
339        }
340        let span = ds.span as usize;
341        let packet_size = ds.packet_size as usize;
342        let chunk_size = ds.chunk_size as usize;
343        if chunk_size == 0 {
344            return data;
345        }
346        if data.len() != packet_size.saturating_mul(span) {
347            return data;
348        }
349        if packet_size % chunk_size != 0 {
350            return data;
351        }
352        let chunks_per_packet = packet_size / chunk_size;
353        if chunks_per_packet <= 1 {
354            return data;
355        }
356
357        // Packet descrambling (upstream asfdec_f.c)
358        let mut out = vec![0u8; data.len()];
359        let mut offset: usize = 0;
360        while offset < data.len() {
361            let off = offset / chunk_size;
362            let row = off / span;
363            let col = off % span;
364            let idx = row + col * chunks_per_packet;
365            let src = idx * chunk_size;
366            if src + chunk_size > data.len() || offset + chunk_size > out.len() {
367                return data;
368            }
369            out[offset..offset + chunk_size].copy_from_slice(&data[src..src + chunk_size]);
370            offset += chunk_size;
371        }
372        out
373    }
374
375    #[inline(always)]
376    fn read_2bits_from_buf(buf: &[u8], i: &mut usize, code: u8, def: u32) -> Result<u32> {
377        match code & 3 {
378            0 => Ok(def),
379            1 => {
380                if *i + 1 > buf.len() {
381                    return Err(DecoderError::InvalidData("ASF packet truncated".into()));
382                }
383                let v = buf[*i] as u32;
384                *i += 1;
385                Ok(v)
386            }
387            2 => {
388                if *i + 2 > buf.len() {
389                    return Err(DecoderError::InvalidData("ASF packet truncated".into()));
390                }
391                let v = u16::from_le_bytes([buf[*i], buf[*i + 1]]) as u32;
392                *i += 2;
393                Ok(v)
394            }
395            3 => {
396                if *i + 4 > buf.len() {
397                    return Err(DecoderError::InvalidData("ASF packet truncated".into()));
398                }
399                let v = u32::from_le_bytes([buf[*i], buf[*i + 1], buf[*i + 2], buf[*i + 3]]);
400                *i += 4;
401                Ok(v)
402            }
403            _ => unreachable!(),
404        }
405    }
406
407    /// Read the next ASF packet and return any completed media objects.
408    ///
409    /// This is stateful and matches upstream's `asf_get_packet` + `asf_parse_packet` assembly.
410    pub fn read_packet<R: Read + Seek>(&mut self, reader: &mut R) -> Result<Vec<AsfPayload>> {
411        let pkt_size = self.packet_size as usize;
412        if pkt_size == 0 {
413            return Err(DecoderError::InvalidData("ASF packet size is 0".into()));
414        }
415
416        let mut buf = vec![0u8; pkt_size];
417        match reader.read_exact(&mut buf) {
418            Ok(()) => {}
419            Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Err(DecoderError::EndOfStream),
420            Err(e) => return Err(DecoderError::Io(e)),
421        }
422
423        const FRAME_HEADER_SIZE: i32 = 6; // upstream's constant
424
425        let mut out: Vec<AsfPayload> = Vec::new();
426
427        // Packet-level state (mirrors ASFContext fields used by upstream).
428        let mut i: usize = 0;
429
430        // Error correction header handling (conservative, fixed-size packet mode).
431        // Common case: 0x82 0x00 0x00.
432        if buf.len() >= 3 && buf[0] == 0x82 && buf[1] == 0 && buf[2] == 0 {
433            i = 3;
434        } else if (buf[0] & 0x80) != 0 {
435            let ec_len = (buf[0] & 0x0F) as usize;
436            i = 1 + ec_len;
437            if i > buf.len() {
438                // Drop this packet.
439                return Ok(out);
440            }
441        }
442
443        if i + 2 > buf.len() {
444            return Ok(out);
445        }
446        let packet_flags = buf[i];
447        let packet_property = buf[i + 1];
448        i += 2;
449
450        // packet_length / sequence / padsize (upstream DO_2BITS)
451        let packet_length = Self::read_2bits_from_buf(&buf, &mut i, packet_flags >> 5, self.packet_size)? as u32;
452        let _seq_ignored = Self::read_2bits_from_buf(&buf, &mut i, packet_flags >> 1, 0)?;
453        let mut padsize = Self::read_2bits_from_buf(&buf, &mut i, packet_flags >> 3, 0)? as u32;
454
455        if packet_length == 0 || packet_length >= (1u32 << 29) {
456            return Ok(out);
457        }
458        if padsize >= packet_length {
459            return Ok(out);
460        }
461
462        if i + 6 > buf.len() {
463            return Ok(out);
464        }
465        let packet_timestamp = u32::from_le_bytes([buf[i], buf[i + 1], buf[i + 2], buf[i + 3]]);
466        i += 4;
467        let _duration = u16::from_le_bytes([buf[i], buf[i + 1]]);
468        i += 2;
469
470        let (packet_segsizetype, mut packet_segments): (u8, i32) = if (packet_flags & 0x01) != 0 {
471            if i >= buf.len() {
472                return Ok(out);
473            }
474            let st = buf[i];
475            i += 1;
476            (st, (st & 0x3f) as i32)
477        } else {
478            (0x80u8, 1)
479        };
480
481        // Header length so far.
482        let header_len = i as u32;
483        if header_len > packet_length.saturating_sub(padsize) {
484            return Ok(out);
485        }
486
487        // upstream: packet_size_left = packet_length - padsize - header_len
488        let mut packet_size_left: i32 = (packet_length - padsize - header_len) as i32;
489
490        // upstream: if packet_length < min_pktsize, extend padsize.
491        if packet_length < self.min_packet_size {
492            padsize = padsize.saturating_add(self.min_packet_size - packet_length);
493        }
494        let mut packet_padsize: i32 = padsize as i32;
495
496        // Payload-level state.
497        let mut packet_time_start: u32 = 0;
498        let mut packet_time_delta: u8 = 0;
499        let mut packet_multi_size: i32 = 0;
500
501        // The current payload header fields.
502        let mut cur_stream_num: u8 = 0;
503        let mut packet_seq: u32 = 0;
504        let mut packet_frag_offset: u32 = 0;
505        let mut packet_replic_size: u32 = 0;
506        let mut packet_key_frame: bool = false;
507        let mut packet_frag_size: u32 = 0;
508        let mut packet_frag_timestamp: u32 = 0;
509        let mut packet_obj_size: u32 = 0;
510
511        // Parse payloads within this packet.
512        loop {
513            if packet_size_left < FRAME_HEADER_SIZE || (packet_segments < 1 && packet_time_start == 0) {
514                // End-of-packet; ignore remaining + padding.
515                let _ = packet_padsize;
516                break;
517            }
518
519            if packet_time_start == 0 {
520                // asf_read_frame_header
521                if i >= buf.len() {
522                    break;
523                }
524                let num = buf[i];
525                i += 1;
526                packet_size_left -= 1;
527
528                packet_segments -= 1;
529                packet_key_frame = (num & 0x80) != 0;
530                cur_stream_num = num & 0x7f;
531
532                // packet_seq / frag_offset / replic_size are variable length depending on packet_property
533                let mut before = i;
534                packet_seq = Self::read_2bits_from_buf(&buf, &mut i, packet_property >> 4, 0)?;
535                packet_size_left -= (i - before) as i32;
536
537                before = i;
538                packet_frag_offset = Self::read_2bits_from_buf(&buf, &mut i, packet_property >> 2, 0)?;
539                packet_size_left -= (i - before) as i32;
540
541                before = i;
542                packet_replic_size = Self::read_2bits_from_buf(&buf, &mut i, packet_property, 0)?;
543                packet_size_left -= (i - before) as i32;
544
545                // rsize: how many bytes were consumed by this frame header (excluding the initial 'num' already counted)
546                // We mirror upstream's checks by comparing against packet_size_left.
547                if (packet_replic_size as i32) > packet_size_left {
548                    // Drop remainder of this packet.
549                    break;
550                }
551
552                packet_obj_size = 0;
553
554                if packet_replic_size >= 8 {
555                    if i + 8 > buf.len() {
556                        break;
557                    }
558                    packet_obj_size = u32::from_le_bytes([buf[i], buf[i + 1], buf[i + 2], buf[i + 3]]);
559                    i += 4;
560                    packet_frag_timestamp = u32::from_le_bytes([buf[i], buf[i + 1], buf[i + 2], buf[i + 3]]);
561                    i += 4;
562                    packet_size_left -= 8;
563
564                    let skip = (packet_replic_size - 8) as usize;
565                    if i + skip > buf.len() {
566                        break;
567                    }
568                    i += skip;
569                    packet_size_left -= skip as i32;
570                } else if packet_replic_size == 1 {
571                    // multipacket - frag_offset is beginning timestamp
572                    packet_time_start = packet_frag_offset;
573                    packet_frag_offset = 0;
574                    packet_frag_timestamp = packet_timestamp;
575
576                    if i >= buf.len() {
577                        break;
578                    }
579                    packet_time_delta = buf[i];
580                    i += 1;
581                    packet_size_left -= 1;
582                } else if packet_replic_size != 0 {
583                    // upstream treats this as invalid.
584                    break;
585                }
586
587                // frag_size
588                if (packet_flags & 0x01) != 0 {
589                    let before = i;
590                    packet_frag_size = Self::read_2bits_from_buf(&buf, &mut i, packet_segsizetype >> 6, 0)?;
591                    let consumed = (i - before) as i32;
592                    packet_size_left -= consumed;
593
594                    if packet_frag_size == 0 {
595                        break;
596                    }
597
598                    // upstream: allow the fragment to eat padding.
599                    if packet_frag_size as i32 > packet_size_left {
600                        if packet_frag_size as i32 > packet_size_left + packet_padsize {
601                            break;
602                        }
603                        let diff = packet_frag_size as i32 - packet_size_left;
604                        packet_size_left += diff;
605                        packet_padsize -= diff;
606                    }
607                } else {
608                    // Single payload: rest of packet (excluding this header) is the fragment.
609                    packet_frag_size = packet_size_left as u32;
610                }
611
612                if packet_replic_size == 1 {
613                    packet_multi_size = packet_frag_size as i32;
614                    if packet_multi_size > packet_size_left {
615                        break;
616                    }
617                }
618            }
619
620            // Multipacket: each sub-payload begins with a 1-byte size.
621            if packet_replic_size == 1 {
622                packet_frag_timestamp = packet_time_start;
623                packet_time_start = packet_time_start.wrapping_add(packet_time_delta as u32);
624
625                if i >= buf.len() {
626                    break;
627                }
628                let sz = buf[i] as u32;
629                i += 1;
630                packet_size_left -= 1;
631                packet_multi_size -= 1;
632
633                packet_obj_size = sz;
634                packet_frag_size = sz;
635                packet_frag_offset = 0;
636
637                if packet_multi_size < packet_obj_size as i32 {
638                    // Drop remaining bytes in the multipacket.
639                    let drop = packet_multi_size.max(0) as usize;
640                    if i + drop > buf.len() {
641                        break;
642                    }
643                    i += drop;
644                    packet_size_left -= drop as i32;
645                    packet_time_start = 0;
646                    packet_multi_size = 0;
647                    continue;
648                }
649
650                packet_multi_size -= packet_obj_size as i32;
651
652                // Audio is treated as keyframe by upstream.
653                packet_key_frame = true;
654            }
655
656            let frag_size = packet_frag_size as usize;
657            if frag_size == 0 {
658                break;
659            }
660            if packet_size_left < frag_size as i32 {
661                break;
662            }
663            if i + frag_size > buf.len() {
664                break;
665            }
666
667            // Copy fragment bytes.
668            let data = &buf[i..i + frag_size];
669            i += frag_size;
670            packet_size_left -= frag_size as i32;
671
672            // For non-multipacket payloads, force reading a new frame header next.
673            if packet_replic_size != 1 {
674                packet_time_start = 0;
675            }
676
677            let pts_ms = packet_frag_timestamp.saturating_sub(self.preroll_ms);
678
679            if packet_obj_size == 0 {
680                // Unknown object size: emit as-is.
681                out.push(AsfPayload {
682                    stream_number: cur_stream_num,
683                    object_id: packet_seq,
684                    obj_offset: 0,
685                    obj_size: data.len() as u32,
686                    pts_ms,
687                    duration_ms: 0,
688                    is_key_frame: packet_key_frame,
689                    data: data.to_vec(),
690                });
691                continue;
692            }
693
694            // Per-stream reassembly (upstream's ASFStream::pkt/frag_offset logic).
695            let st = &mut self.streams[cur_stream_num as usize];
696
697            if st.frag_offset_sum == 0 && packet_frag_offset != 0 {
698                // upstream: skip unexpected non-zero fragment offset when no in-flight object.
699                continue;
700            }
701
702            let obj_size = packet_obj_size as usize;
703            let frag_off = packet_frag_offset as usize;
704
705            let need_new = st.pkt.len() != obj_size || st.frag_offset_sum + frag_size > st.pkt.len();
706            if need_new {
707                st.pkt.clear();
708                st.pkt.resize(obj_size, 0);
709                st.frag_offset_sum = 0;
710                st.pkt_clean = false;
711                st.seq = packet_seq;
712                st.pts_ms = pts_ms;
713                st.is_key = packet_key_frame || self.is_audio_stream[cur_stream_num as usize];
714            }
715
716            if frag_off >= st.pkt.len() || frag_size > st.pkt.len().saturating_sub(frag_off) {
717                continue;
718            }
719
720            if frag_off != st.frag_offset_sum && !st.pkt_clean {
721                // upstream: zero-fill remainder once if offsets jump.
722                for b in &mut st.pkt[st.frag_offset_sum..] {
723                    *b = 0;
724                }
725                st.pkt_clean = true;
726            }
727
728            st.pkt[frag_off..frag_off + frag_size].copy_from_slice(data);
729            st.frag_offset_sum += frag_size;
730
731            if st.frag_offset_sum == st.pkt.len() {
732                // IMPORTANT: end the mutable borrow of self.streams[] before calling any &self method.
733                // We must cache fields from `st` into locals and reset `st` state first.
734                let seq = st.seq;
735                let pts_ms_full = st.pts_ms;
736                let is_key_full = st.is_key;
737
738                let mut full = std::mem::take(&mut st.pkt);
739                st.frag_offset_sum = 0;
740                st.pkt_clean = false;
741
742                // `st` is no longer used after this point, so the mutable borrow ends here.
743                if self.is_audio_stream[cur_stream_num as usize] {
744                    full = self.descramble_audio_if_needed(cur_stream_num, full);
745                }
746
747                out.push(AsfPayload {
748                    stream_number: cur_stream_num,
749                    object_id: seq,
750                    obj_offset: 0,
751                    obj_size: full.len() as u32,
752                    pts_ms: pts_ms_full,
753                    duration_ms: 0,
754                    is_key_frame: is_key_full,
755                    data: full,
756                });
757            }
758        }
759
760        Ok(out)
761    }
762}