Skip to content

Conversation

@tobbee
Copy link
Collaborator

@tobbee tobbee commented Oct 3, 2025

Overview

Implements support for processing fragmented MP4 files incrementally without loading entire
files into memory. This is good for handling large media files, live streaming scenarios,
and network streams where fragments arrive over time.

Core Features

1. StreamFile API (mp4/stream.go)

New StreamFile type enables incremental processing of fragmented MP4:

  • DecodeStream/InitDecodeStream: Read init segment (ftyp, moov) and stop
  • ProcessFragments: Iteratively process fragments with callbacks
  • Sliding window memory management with configurable retention
  • Fragment lifecycle callbacks for processing and cleanup control
  • SampleAccessor interface for efficient sample access:
    • GetSample: Fetch individual samples (1-based indexing)
    • GetSampleRange: Fetch sample ranges efficiently
    • GetSamples: Fetch all samples for a track

2. BoxSeekReader (mp4/boxseekreader.go)

Enables lazy mdat processing on non-seekable streams:

  • Emulates seeking using buffered reading
  • Auto-growing buffer sized for current needs
  • Peek operations for looking ahead without consuming data
  • Explicit buffer management with ResetBuffer() to prevent corruption

3. FtypBox and StypBox improvements

Added Copy() methods for deep copying:

  • Prevents corruption from shared mutable state (underlying data []byte slices)
  • Used in stream processing when collecting boxes

Buffer Management Strategy

  • ReadFullBox returns slice view of buffer (no copy for performance)
  • Callers must use data immediately before next operation
  • ResetBuffer() explicitly clears buffer after parsing
  • Buffer clearing after box parsing, not during ReadFullBox
  • For mdat seeks, ResetBuffer() called after seek to avoid corruption

Demonstration: stream-encrypt example

New examples/stream-encrypt application demonstrates the API in practice:

  • HTTP streaming server with chunked transfer encoding
  • Refragmentation: Split fragments into smaller pieces using GetSampleRange()
  • Encryption: Apply CENC/CBCS on-the-fly without buffering entire fragments
  • Memory efficient: True streaming without loading full fragments
  • Design highlights:
    • Preserves ftyp/styp boxes from input
    • Styp boxes only at segment boundaries (not duplicated in sub-fragments)
    • Sub-fragments share sequence numbers (prevents 32-bit overflow)

Testing

  • Comprehensive test suite using external black-box testing (mp4_test package)
  • Tests cover streaming, callbacks, sliding windows, retention, sample access
  • All tests use public API only per project guidelines
  • Example includes decrypt/re-encrypt round-trip verification

Files Changed

  • mp4/stream.go (+607 lines) - StreamFile API
  • mp4/stream_test.go (+435 lines) - Streaming API tests
  • mp4/boxseekreader.go (+357 lines) - Seekable wrapper for non-seekable streams
  • mp4/boxseekreader_test.go (+513 lines) - BoxSeekReader tests
  • mp4/ftyp.go, mp4/styp.go - Added Copy() methods
  • examples/stream-encrypt/* (+1149 lines) - Demonstration application

Related

Copy link
Contributor

@Diniboy1123 Diniboy1123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool stuff! Would have been very useful for me as well, as I basically do the same thing with your library. :)

In the end I figured that parsing and encryption doesn't take more than a few milliseconds anyway, so I didn't really bother pushing a streaming API, but it definitely has its use. For example this is also useful for larger files, when I don't want to load the entire file into memory.

Just got a small comment regarding a probably "pushed by accident" binary.

Add Copy method for StypBox and FtypBox.

Implements support for processing fragmented MP4 files incrementally without
loading entire files into memory. This is essential for handling large media
files, live streaming scenarios, and network streams where fragments arrive
over time.

Key components:

1. StreamFile API (stream.go)
   - DecodeStream/InitDecodeStream: Reads init segment (ftyp, moov) and stops
   - ProcessFragments: Iteratively processes fragments with callbacks
   - Sliding window memory management with configurable retention
   - Fragment lifecycle callbacks for processing and cleanup control

2. BoxSeekReader (boxseekreader.go)
   - Emulates seeking on non-seekable streams using buffered reading
   - Auto-growing buffer sized for current needs
   - Peek operations for looking ahead without consuming data

3. SampleAccessor interface
   - GetSample: Fetch individual samples (1-based indexing)
   - GetSampleRange: Fetch ranges of samples efficiently
   - GetSamples: Fetch all samples for a track

Buffer management:
- ReadFullBox returns a slice view of the buffer (no copy for performance)
- Callers must use data immediately before next operation
- ResetBuffer() explicitly clears buffer after parsing is complete
- Buffer clearing happens after box parsing, not during ReadFullBox
- For mdat seeks, ResetBuffer() called after seek to avoid buffer corruption

Testing:
- Comprehensive test suite using external black-box testing (mp4_test package)
- Tests cover streaming, callbacks, sliding windows, retention, sample access
- All tests use public API only per project guidelines
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants