ISOMedia (ISOMedia v0.1.0)

Copy Markdown View Source

Lossless ISOBMFF (MP4/MOV/M4A/HEIF) box surgery in pure Elixir.

Parse any ISO Base Media file into a tree of ISOMedia.Box structs (every box, including unknown/vendor boxes, preserved byte-for-byte), edit it, and re-serialize. The invariant throughout is serialize(parse(file)) == file.

This module is the top-level entry point:

trim, extract_track, concat, fragment, and defragment outputs can be chained in memory (no disk round-trip); faststart/1/fix_chunk_offsets/1 require an original parsed mdat and raise on a synthesized one.

iex> {:ok, boxes} = ISOMedia.parse(<<8::32, "free">>)
iex> ISOMedia.serialize(boxes)
<<8::32, "free">>

Summary

Functions

Losslessly concatenate compatible clips end-to-end. See ISOMedia.Concat.concat/1.

Defragment a fragmented MP4 tree into a progressive one. See ISOMedia.Defragment.defragment/1.

Extract a single track into a new box tree (then write/2 or serialize/1).

Move moov before mdat (faststart) and fix chunk offsets. See ISOMedia.Offsets.faststart/1.

Recompute stco/co64 chunk offsets for the current box arrangement. See ISOMedia.Offsets.fix_chunk_offsets/1.

Repack a progressive tree into a multiplexed fragmented MP4. See ISOMedia.Fragment.fragment/2.

Parse a binary into {:ok, [%ISOMedia.Box{}]}. See ISOMedia.Parser.parse/2.

Read a file and parse it. Pass lazy: true to keep large leaf payloads (≥ :lazy_threshold, default 1 MB) as ISOMedia.FileSlice references instead of loading them, so files larger than memory can be processed.

Decode a track's sample tables into [%ISOMedia.Sample{}] (progressive or fragmented).

Serialize a box or list of boxes back to a binary.

Split a fragmented tree into a CMAF init segment + media segments. See ISOMedia.Segment.split/1.

List the track_ids present in the movie.

Losslessly trim every track to the time range [start_sec, end_sec).

Serialize boxes and write them to path, streaming any FileSlice payloads disk→disk (memory-safe for large files). Raises if path is one of the tree's FileSlice sources (you cannot stream-overwrite the file you are reading).

Write a fragmented tree's init + media segment files into dir. See ISOMedia.Segment.write_segments/3.

Types

tree()

@type tree() :: [ISOMedia.Box.t()]

Functions

concat(inputs)

@spec concat([tree()]) :: tree()

Losslessly concatenate compatible clips end-to-end. See ISOMedia.Concat.concat/1.

defragment(boxes)

@spec defragment(tree()) :: tree()

Defragment a fragmented MP4 tree into a progressive one. See ISOMedia.Defragment.defragment/1.

extract_track(boxes, track_id)

@spec extract_track(tree(), pos_integer()) :: tree()

Extract a single track into a new box tree (then write/2 or serialize/1).

faststart(boxes)

@spec faststart(tree()) :: tree()

Move moov before mdat (faststart) and fix chunk offsets. See ISOMedia.Offsets.faststart/1.

fix_chunk_offsets(boxes)

@spec fix_chunk_offsets(tree()) :: tree()

Recompute stco/co64 chunk offsets for the current box arrangement. See ISOMedia.Offsets.fix_chunk_offsets/1.

fragment(boxes, opts \\ [])

@spec fragment(
  tree(),
  keyword()
) :: tree()

Repack a progressive tree into a multiplexed fragmented MP4. See ISOMedia.Fragment.fragment/2.

parse(binary, opts \\ [])

@spec parse(
  binary(),
  keyword()
) :: {:ok, tree()} | {:error, String.t()}

Parse a binary into {:ok, [%ISOMedia.Box{}]}. See ISOMedia.Parser.parse/2.

read(path, opts \\ [])

@spec read(
  Path.t(),
  keyword()
) :: {:ok, tree()} | {:error, term()}

Read a file and parse it. Pass lazy: true to keep large leaf payloads (≥ :lazy_threshold, default 1 MB) as ISOMedia.FileSlice references instead of loading them, so files larger than memory can be processed.

samples(boxes, track_id)

@spec samples(tree(), pos_integer()) :: [ISOMedia.Sample.t()]

Decode a track's sample tables into [%ISOMedia.Sample{}] (progressive or fragmented).

serialize(boxes)

@spec serialize(tree()) :: binary()

Serialize a box or list of boxes back to a binary.

split_segments(boxes)

@spec split_segments(tree()) :: %{init: tree(), segments: [tree()]}

Split a fragmented tree into a CMAF init segment + media segments. See ISOMedia.Segment.split/1.

track_ids(boxes)

@spec track_ids(tree()) :: [pos_integer()]

List the track_ids present in the movie.

trim(boxes, start_sec, end_sec)

@spec trim(tree(), number(), number()) :: tree()

Losslessly trim every track to the time range [start_sec, end_sec).

write(path, boxes)

@spec write(Path.t(), tree()) :: :ok | {:error, File.posix()}

Serialize boxes and write them to path, streaming any FileSlice payloads disk→disk (memory-safe for large files). Raises if path is one of the tree's FileSlice sources (you cannot stream-overwrite the file you are reading).

write_segments(dir, boxes, opts \\ [])

@spec write_segments(Path.t(), tree(), keyword()) ::
  {:ok, [Path.t()]} | {:error, term()}

Write a fragmented tree's init + media segment files into dir. See ISOMedia.Segment.write_segments/3.