Skip to content

TVWIT/toMp4.js

Repository files navigation

toMp4
turn streams into files
npm install @invintusmedia/tomp4

 

you've got an HLS stream, or some .ts segments, or fMP4 chunks.

you want an .mp4 file.

import toMp4 from '@invintusmedia/tomp4'

const mp4 = await toMp4('https://example.com/stream.m3u8')
mp4.download('my-video.mp4')

that's it. no ffmpeg. no wasm. no dependencies.

 

from different sources

// HLS playlist (auto-picks highest quality)
const mp4 = await toMp4('https://example.com/master.m3u8')

// single .ts segment
const mp4 = await toMp4('https://example.com/segment.ts')

// raw bytes you already have
const mp4 = await toMp4(uint8Array)

// pick your quality
const hls = await toMp4.parseHls('https://example.com/master.m3u8')
console.log(hls.qualities) // ['1080p', '720p', '480p']
const mp4 = await toMp4(hls.select('720p'))

clip to time range

// one-step: download HLS + clip (only fetches needed segments)
const mp4 = await toMp4('https://example.com/stream.m3u8', {
  startTime: 0,
  endTime: 30
})

// clip existing data (frame-accurate using edit lists)
const mp4 = await toMp4(data, {
  startTime: 5,
  endTime: 15
})

stitch multiple fMP4 segments

// live stream saved as 4-second fMP4 chunks (each with init+data)
const segments = [segment1, segment2, segment3] // Uint8Array[]
const mp4 = toMp4.stitchFmp4(segments)
mp4.download('combined-stream.mp4')

// or with separate init segment
const mp4 = toMp4.stitchFmp4(dataSegments, { init: initSegment })

stitch multiple MPEG-TS segments

// combine .ts segments into a single MP4
const segments = [segment1, segment2, segment3] // Uint8Array[]
const mp4 = toMp4.stitchTs(segments)
mp4.download('combined.mp4')

// or concatenate into a single continuous TS stream
const tsData = toMp4.concatTs(segments)

analyze without converting

const info = toMp4.analyze(tsData)

info.duration      // 99.5 (seconds)
info.keyframes     // [{index: 0, time: 0}, {index: 150, time: 5.0}, ...]
info.videoCodec    // "H.264/AVC"
info.audioCodec    // "AAC"

progress callback

const mp4 = await toMp4(url, {
  onProgress: (msg, info) => {
    if (info?.percent !== undefined) {
      console.log(`${info.percent}% - ${msg}`)
    }
  }
})
// 10% - Downloading: 10%
// 50% - Downloaded 5.2 MB
// 60% - Frames: 300 video, 450 audio
// 100% - Complete

use the result

mp4.download('video.mp4')   // trigger download
video.src = mp4.toURL()     // play in video element
mp4.data                    // Uint8Array
mp4.toBlob()                // Blob
mp4.toArrayBuffer()         // ArrayBuffer
mp4.revokeURL()             // free memory

 

what it does

remuxes video. no transcoding.

input output
.ts (MPEG-TS) .mp4
.m4s (fMP4) .mp4
.m3u8 (HLS) .mp4

video: H.264, H.265
audio: AAC

 

what it doesn't do

  • transcode (no converting h264→h265, etc)
  • handle DRM/encrypted streams
  • support MP3, AC-3 audio (yet)

 

browser + node

works in both. ~50kb minified.

<script type="module">
  import toMp4 from '@invintusmedia/tomp4'
</script>

 

MIT