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