gpac / mp4box.js

JavaScript version of GPAC's MP4Box tool
https://gpac.github.io/mp4box.js/
BSD 3-Clause "New" or "Revised" License
1.94k stars 326 forks source link

How to save to a file using showSaveFilePicker and FileSystemWritableFileStream #285

Closed onthegit closed 2 years ago

onthegit commented 2 years ago

Hello, is it possible to periodically save the mp4 to file instead of saving it in buffer for createObjectURL?

Scenario: User choose file to save with showSaveFilePicker. The MP4 file is encoded with webcodecs API and addTrack and addSample. The resulting inmemory stream can be saved with mp4box.save(filename). Is there a way to save the stream periodically (save and free memory) to file with FileSystemWritableFileStream.write() (handler for FileSystemWritableFileStream received from window.showSaveFilePicker).

When using addTrack and then addSample, the onSegment callback is never called.

Thank you.

iGerman00 commented 2 years ago

Any solution for this?

hughfenghen commented 1 year ago

@iGerman00 https://github.com/hughfenghen/WebAV/blob/main/packages/av-canvas/src/mp4-utils.ts#L364
This repository code is still unstable, maybe in the future it will packaged an MP4 + WebCodecs tool and release it to npm.

export function file2stream (
  file,
  timeSlice
) {
  let timerId = 0

  let sendedBoxIdx = 0
  const boxes = file.boxes
  const deltaBuf = (): ArrayBuffer => {
    const ds = new mp4box.DataStream()
    ds.endianness = mp4box.DataStream.BIG_ENDIAN
    for (let i = sendedBoxIdx; i < boxes.length; i++) {
      boxes[i].write(ds)
    }
    sendedBoxIdx = boxes.length
    return ds.buffer
  }

  let stoped = false
  let exit = null
  const stream = new ReadableStream({
    start (ctrl) {
      timerId = self.setInterval(() => {
        ctrl.enqueue(deltaBuf())
      }, timeSlice)

      exit = () => {
        clearInterval(timerId)
        file.flush()
        ctrl.enqueue(deltaBuf())
        ctrl.close()
      }

      if (stoped) exit()
    },
    cancel () {
      clearInterval(timerId)
    }
  })

  return {
    stream,
    stop: () => {
      stoped = true
      exit?.()
    }
  }
}

example

const file = mp4box.createFIle()
const { stream, stop } = file2stream(file, 500)

await stream.pipeTo(/* FileSystemWritableFileStream */)