webm-duration-fix-electron
Version:
based on ts-ebml and support large file(than 2GB) and optimize memory usage during repair
143 lines (127 loc) • 4.19 kB
text/typescript
import { tools, Decoder, Reader } from './ebml';
const mimeType = 'video/webm\;';
/**
* based on ts-ebml and support large file,optimize memory usage during repair
*
* @param blob the blob you need to fix
* @returns the blob that has been fixed
*
*/
export async function fixWebmDuration(blob: Blob): Promise<Blob> {
if (!blob) {
throw Error('call to fixWebmDuration requires a blob');
}
const decoder = new Decoder();
const reader = new Reader();
const readstream = blob.stream() as any;
const readerBlob = readstream.getReader();
while (true) {
let { done, value } = await readerBlob.read();
if (done) {
reader.stop();
break;
}
let elms = decoder.decode(value);
// As browser upgrade webm meta attributes are gradually added,
// so filter unknown type to bypass this issue.
elms = elms?.filter(elm => elm.type !== 'unknown')
elms.forEach(elm => {
reader.read(elm)
});
value = null;
}
const refinedMetadataBuf = tools.makeMetadataSeekable(reader.metadatas, reader.duration, reader.cues);
const refinedMetadataBlob = new Blob([refinedMetadataBuf], { type: mimeType });
const firstPartBlobWithoutMetadata = blob.slice(reader.metadataSize);
const finalBlob = new Blob([refinedMetadataBlob, firstPartBlobWithoutMetadata], { type: mimeType });
return finalBlob;
}
interface Callback {
(size : number , blobWithHeader : Blob) : void;
}
export class FixWebmProcess {
private _processBlob : Blob;
private _nextBlob : Blob[];
private _end : boolean;
private _decoder : Decoder;
private _reader : Reader;
private _finish : boolean;
private _callback : Callback;
private _enableLog : boolean;
constructor() {
this._end = true;
this._decoder = new Decoder();
this._reader = new Reader();
this._processBlob = new Blob();
this._nextBlob = [];
this._finish = false;
this._callback = (size : number , blobWithHeader : Blob) => {};
this._enableLog = true;
// this._MyBlob = new Blob();
}
public processBlob(blob : Blob) : void {
if (this._end) {
this._processBlob = new Blob([blob], {type : mimeType});
this._end = false;
this.fixWebmDuration()
} else {
this._nextBlob[this._nextBlob.length] = blob;
}
}
private async fixWebmDuration() : Promise<void> {
if (!this._processBlob) {
throw Error('call to fixWebmDuration requires a blob');
}
let readstream = this._processBlob.stream() as any;
let readerBlob = readstream.getReader();
while (true) {
let { done, value } = await readerBlob.read();
if (done) {
if (this._nextBlob.length == 0) {
this._end = true;
if (this._finish) {
this.LOG('<FixWebmProcess> finish from fixWebmDuration')
this.finish(this._callback);
}
break;
} else {
this._processBlob = this._nextBlob.shift() as Blob;
readstream = this._processBlob.stream() as any;
readerBlob = readstream.getReader();
continue;
}
}
let elms = this._decoder.decode(value);
// As browser upgrade webm meta attributes are gradually added,
// so filter unknown type to bypass this issue.
elms = elms?.filter(elm => elm.type !== 'unknown')
elms.forEach(elm => {
this._reader.read(elm)
});
value = null;
}
}
private LOG(log : String) {
if (this._enableLog) {
console.log(log);
}
}
public finish(callback : Callback) {
this._finish = true;
this._callback = callback;
this.LOG('<FixWebmProcess> call finish')
if (this._end) {
this.LOG('<FixWebmProcess> finish from finish function')
this._reader.stop();
this.fixBlob();
}
}
public setEnableLog(enable : boolean) {
this._enableLog = enable;
}
private fixBlob() {
const refinedMetadataBuf = tools.makeMetadataSeekable(this._reader.metadatas, this._reader.duration, this._reader.cues);
const refinedMetadataBlob = new Blob([refinedMetadataBuf], { type: mimeType });
this._callback(this._reader.metadataSize, refinedMetadataBlob);
}
}