diff --git a/packages/av-cliper/package.json b/packages/av-cliper/package.json index 73fe84f7..e0552e44 100644 --- a/packages/av-cliper/package.json +++ b/packages/av-cliper/package.json @@ -41,6 +41,7 @@ "dependencies": { "@types/dom-webcodecs": "^0.1.7", "@webav/mp4box.js": "0.5.3-fenghen", + "opfs-tools": "^0.1.1", "wave-resampler": "^1.0.0" } } diff --git a/packages/av-cliper/src/mp4-utils/mp4-previewer.ts b/packages/av-cliper/src/mp4-utils/mp4-previewer.ts index 421899cf..ad964d75 100644 --- a/packages/av-cliper/src/mp4-utils/mp4-previewer.ts +++ b/packages/av-cliper/src/mp4-utils/mp4-previewer.ts @@ -2,7 +2,7 @@ import { MP4File, MP4Info, MP4Sample } from '@webav/mp4box.js' import { autoReadStream } from '../av-utils' import { Log } from '../log' import { extractFileConfig, sample2ChunkOpts } from './mp4box-utils' -import { OPFSFileWrap } from './opfs-file-wrap' +import { file } from 'opfs-tools' import { SampleTransform } from './sample-transform' export class MP4Previewer { @@ -14,7 +14,7 @@ export class MP4Previewer { data: null }> = [] - #opfsFile = new OPFSFileWrap(Math.random().toString()) + #opfsFile = file(Math.random().toString()) #wrapDecoder: ReturnType | null = null @@ -27,6 +27,7 @@ export class MP4Previewer { async #init(stream: ReadableStream) { let offset = 0 + const writer = await this.#opfsFile.createWriter() return new Promise((resolve, reject) => { let mp4Info: MP4Info | null = null let mp4boxFile: MP4File | null = null @@ -60,13 +61,14 @@ export class MP4Previewer { data: null }) offset += s.data.byteLength - await this.#opfsFile.write(s.data) + await writer.write(s.data) } } mp4boxFile?.releaseUsedSamples(data.id, data.samples.length) } }, onDone: async () => { + await writer.close() if (mp4Info == null) { reject('Parse failed') return @@ -109,13 +111,15 @@ export class MP4Previewer { } } + const reader = await this.#opfsFile.createReader() const chunks = await Promise.all( this.#videoSamples.slice(start, end + 1) .map(async s => new EncodedVideoChunk(sample2ChunkOpts({ ...s, - data: await this.#opfsFile.read(s.offset, s.size) + data: await reader.read(s.offset, s.size) }))) ) + await reader.close() if (chunks.length === 0) return Promise.resolve(null) return new Promise((resolve) => { diff --git a/packages/av-cliper/src/mp4-utils/opfs-file-wrap.ts b/packages/av-cliper/src/mp4-utils/opfs-file-wrap.ts deleted file mode 100644 index 79a54520..00000000 --- a/packages/av-cliper/src/mp4-utils/opfs-file-wrap.ts +++ /dev/null @@ -1,92 +0,0 @@ - -interface FileSystemSyncAccessHandle { - read: (container: ArrayBuffer, opts: { at: number }) => void - write: (data: ArrayBuffer) => number - flush: () => void - getSize: () => number -} - -export class OPFSFileWrap { - #worker: Worker - - #cbId = 0 - - #cbFns: Record = {} - #initReady: Promise - - constructor(fileName: string) { - const createWorker = (): Worker => { - const blob = new Blob([`(${opfsWorkerSetup.toString()})()`]) - const url = URL.createObjectURL(blob) - return new Worker(url) - } - this.#worker = createWorker() - this.#worker.onmessage = this.#onMsg - this.#initReady = this.#postMsg('create', { fileName }) - } - - async #postMsg(evtType: 'create' | 'write' | 'read', args: any) { - if (evtType !== 'create') await this.#initReady - const cbId = this.#cbId - this.#cbId += 1 - - const rsP = new Promise((resolve) => { - this.#cbFns[cbId] = resolve - }) - - this.#worker.postMessage({ - cbId, - evtType, - args - }) - return rsP - } - - #onMsg = ({ data }: { data: { cbId: number, returnVal: unknown, evtType: string } }) => { - if (data.evtType === 'callback') { - this.#cbFns[data.cbId]?.(data.returnVal) - } - } - - async write(data: ArrayBuffer) { - return await this.#postMsg('write', { data }) - } - - async read(offset: number, size: number) { - return await this.#postMsg('read', { offset, size }) as ArrayBuffer - } -} - -const opfsWorkerSetup = (): void => { - let accessHandle: FileSystemSyncAccessHandle - - async function createFile(fileName: string) { - const root = await navigator.storage.getDirectory(); - const draftHandle = await root.getFileHandle(fileName, { create: true }); - // @ts-expect-error - accessHandle = await draftHandle.createSyncAccessHandle() - } - - self.onmessage = async e => { - let returnVal - if (e.data.evtType === 'create') { - await createFile(e.data.args.fileName as string) - } else if (e.data.evtType === 'write') { - accessHandle.write(e.data.args.data) - accessHandle.flush() - } else if (e.data.evtType === 'read') { - const { offset, size } = e.data.args - const ab = new ArrayBuffer(size) - accessHandle.read(ab, { at: offset }) - returnVal = ab - } - const trans: Transferable[] = [] - if (returnVal instanceof ArrayBuffer) trans.push(returnVal) - self.postMessage({ - evtType: 'callback', - cbId: e.data.cbId, - returnVal - // @ts-expect-error - }, trans) - } -} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index f2b73018..90b94aff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5471,13 +5471,14 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: semver "^5.6.0" validate-npm-package-name "^3.0.0" -npm-packlist@1.1.12, npm-packlist@^1.4.4: - version "1.1.12" - resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.12.tgz#22bde2ebc12e72ca482abd67afc51eb49377243a" - integrity sha512-WJKFOVMeAlsU/pjXuqVdzU0WfgtIBCupkEVwn+1Y0ERAbUfWw8R4GjgVbaKnUjRoD2FoQbHOCbOyT5Mbs9Lw4g== +npm-packlist@^1.4.4: + version "1.4.8" + resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" + integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== dependencies: ignore-walk "^3.0.1" npm-bundled "^1.0.1" + npm-normalize-package-bin "^1.0.1" npm-pick-manifest@^3.0.0: version "3.0.2" @@ -5632,6 +5633,11 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +opfs-tools@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/opfs-tools/-/opfs-tools-0.1.1.tgz#25c42085cdc32fb9f83cdd602e9c41e0bf9aa0d0" + integrity sha512-F+UroKcPfU2/ZzT/4/hONIEbdFHkD/oWwpzH3rkaz+EiDm0W6b2vqNAp1LrWClGrERZ19X3ceRixYL0ZdXcpjA== + optionator@^0.8.1: version "0.8.3" resolved "https://registry.npmmirror.com/optionator/-/optionator-0.8.3.tgz"