Skip to main content

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