* Add non-owning conversions via ConversionOptions.ownsOutput
A conversion with ownsOutput: false only adds tracks to the output and
drives their media data; starting, finalizing, and metadata tags remain
the caller's responsibility. This lets multiple conversions and
directly-added user tracks compose on a single Output (see upstream
issue #436).
- ownsOutput: false allows a pre-populated output (state must still be
'pending') and seeds track-capacity accounting from existing tracks
- execute() requires the output to be started and never finalizes it
- cancel() closes only the conversion's own sources, releasing internal
synchronizer waiters, and leaves the output usable
- tags cannot be combined with ownsOutput: false
- isValid requires at least one contributed track instead of the
format's minimum track counts
Prototype for API discussion; default (owning) behavior is unchanged.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AJdfnbY9AFh9i9dKgtrj6E
* Add external-audio example using a non-owning conversion
Demonstrates composing a user-owned audio track (synthesized voiceover
via OfflineAudioContext + AudioBufferSource) onto a picked video with
Conversion.init({ ownsOutput: false }), including progress reporting
and playback/download of the result.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AJdfnbY9AFh9i9dKgtrj6E
* Release synchronizer waiters when canceling during output finalization
A non-owning conversion's cancel() previously no-oped entirely when the
output (owned by someone else) was already finalizing or finalized,
leaving pump loops parked in the track synchronizer and hanging
execute() forever. Now it still marks the conversion canceled and
releases parked waiters in that state, without force-closing sources
(finalization owns flushing them at that point).
Also adds coverage: non-owning onProgress monotonicity, canceling one
of two sibling conversions, capacity seeding across sequential inits,
exact metadata exclusivity, and cancel-before-execute.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AJdfnbY9AFh9i9dKgtrj6E
* Document non-owning conversions on the converting-media-files guide page
Adds the doc section requested in #436: what ownsOutput: false does, the
required choreography (add tracks -> output.start() before execute() ->
run the conversion concurrently with your own sources -> finalize), the
cancellation split (conversion.cancel() leaves the output alive; cancel
both for a full abort and tear the output down on error paths), isValid
semantics in this mode, and the tags restriction with the
setMetadataTags() alternative. Also cross-links the fresh-output rule to
the new section. VitePress build passes with dead-link checking on.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
* Clean up conversion logic, add Output.tracks and .hasEnoughTracks(), move new conversion tests around, remove external audio example
* non-owning -> composable, and update docs
* Update
---------
Co-authored-by: Claude <[email protected]>
Co-authored-by: Vanilagy <[email protected]>
* add a test
* recognize as input format
* scaffold flac demuxer
* implement getting metadata
* Implement mime type
* read all metadata + deduplicate stubs
* Read first packet
* copyright headers
* read the first packet
* Get entire first packet, work on advancing
* iterate over all samples
* testable with bun
* stub out metadata support
* parse descriptive metadata
* All in 1 file
seems more appropriate to the philosophy
* some parameters are not needed anymore all within 1 class
* skip over bytes we are sure are not the syncword
* run prettier
* timestamp is determined based on passed blocks, not maximumBlockSize
* no binary search needed!
* simplifications
* Finish demuxer reading sequentially
* more tests + add a file with a seektable
* don't throw if (this.audioInfo.minimumBlockSize !== this.audioInfo.maximumBlockSize
* Add docs
* Update format compatibility table
* Simplification
* confirm conversion is working
* Finish
* Resolve TODO comment
* Support images (read-only)
* Returning description as Uint8Array
* Cleanup of demuxer
* Misc renames
* Object on same line
* Resolve first batch of comments
* Throw errors on corrupt blocks, correctly use requestSlice()
* Fix description field
* Explain why last frame is a bit shorter
* Add FLAC to README
* Put track backings below demuxer
* convert to methods
* Reorder container checking
* getBlockSize() -> readBlockSize()
* bitStream -> bitstream
* better naming for bytes
* use .skip()
* Don't return blockSize twice in readFlacFrameHeader
* Use enum + switch to distinguish Flac block types
* Use else-if
* Use else-if
* Compressed switch statement
* readCodedNumber
* Update flac-misc.ts
* We don't need the bits variable at all
* reorder functions in flac-demuxer
* Handle gracefully not being able to load another sample
* Update flac-demuxer.ts
* blockingbit null
* use async instead of promise.resolve
* use binary search
* Replace recursion with while loop
* Add mutex to getPacket()
* Load more data not in getPacketAtIndex, but outside
* Apply suggestion from @Vanilagy
Co-authored-by: David P. <[email protected]>
* computeDuration() reads last packet
* Update flac-demuxer.ts
* share vorbis comment reading logic
* reuse vorbis comment writing logic, set vendor always to "Mediabunny"
* flush after writign
* Use FileSlice.tempFromBytes
* assert !== null
* fixing nitpicks
* apply suggestions
* fix ogg
* We are now muxing images
* apply suggestion
* Update src/flac/flac-muxer.ts
Co-authored-by: David P. <[email protected]>
* apply suggestion
* readSampleRate()
* seek outside writeHeader()
* mention vorbis metadata + add to metadatatags comment
* should be able to -> can
* Add test in metadata tags
* Add test for packets being byte identical after remuxing
* compare to null
* no casting to uint8array
* make test pass
* Update flac-muxer.ts
* Call validateAudioChunkMetadata() and validateAndNormalizeTimestamp()
* `onFrame` option
* emit frames using onFrame
* Run prettier over files
* Fix FLAC PICTURE block logic, small other changes
* Update docs
* Remove .only modifier
* fix remuxing and add test
* Don't throw error if parsing fails in header, since hitting a syncword might just be coincidential
* Fix remaining type errors
---------
Co-authored-by: David P. <[email protected]>