Seek improved v2

This commit is contained in:
killer069 2022-01-05 12:05:07 +05:30
parent 2aa28f2350
commit d93fd98822
3 changed files with 30 additions and 22 deletions

View File

@ -65,7 +65,6 @@ export class SeekStream {
this.stream = new WebmSeeker({ this.stream = new WebmSeeker({
highWaterMark: 5 * 1000 * 1000, highWaterMark: 5 * 1000 * 1000,
readableObjectMode: true, readableObjectMode: true,
mode: options.seekMode
}); });
this.url = url; this.url = url;
this.quality = options.quality as number; this.quality = options.quality as number;

View File

@ -23,13 +23,11 @@ const WEB_ELEMENT_KEYS = Object.keys(WebmElements);
export class WebmSeeker extends Duplex { export class WebmSeeker extends Duplex {
remaining?: Buffer; remaining?: Buffer;
state: WebmSeekerState; state: WebmSeekerState;
mode: 'precise' | 'granular';
chunk?: Buffer; chunk?: Buffer;
cursor: number; cursor: number;
header: WebmHeader; header: WebmHeader;
headfound: boolean; headfound: boolean;
headerparsed: boolean; headerparsed: boolean;
time_left: number;
seekfound: boolean; seekfound: boolean;
private data_size: number; private data_size: number;
private data_length: number; private data_length: number;
@ -40,11 +38,9 @@ export class WebmSeeker extends Duplex {
this.cursor = 0; this.cursor = 0;
this.header = new WebmHeader(); this.header = new WebmHeader();
this.headfound = false; this.headfound = false;
this.time_left = 0;
this.headerparsed = false; this.headerparsed = false;
this.seekfound = false; this.seekfound = false;
this.data_length = 0; this.data_length = 0;
this.mode = options.mode || 'granular';
this.data_size = 0; this.data_size = 0;
} }
@ -75,21 +71,22 @@ export class WebmSeeker extends Duplex {
_read() {} _read() {}
seek(sec: number): Error | number { seek(sec : number): Error | number{
let position = 0; let clusterlength = 0, position = 0;
const time = Math.floor(sec / 10) * 10; const time = Math.floor(sec / 10) * 10;
this.time_left = (sec - time) * 1000 || 0; let time_left = (sec - time) * 1000 || 0;
this.time_left = Math.round(this.time_left / 20) * 20; time_left = Math.round(time_left / 20) * 20;
if (!this.header.segment.cues) return new Error('Failed to Parse Cues'); if (!this.header.segment.cues) return new Error('Failed to Parse Cues');
for (const data of this.header.segment.cues) { for (let i = 0; i < this.header.segment.cues.length; i++) {
const data = this.header.segment.cues[i]
if (Math.floor((data.time as number) / 1000) === time) { if (Math.floor((data.time as number) / 1000) === time) {
position = data.position as number; position = data.position as number;
clusterlength = this.header.segment.cues[i + 1].position! - position - 1
break; break;
} else continue; } else continue;
} }
if (position === 0) return new Error('Failed to find Cluster Position'); return Math.round(position + (time_left / 20) * (clusterlength / 500));
else return position;
} }
_write(chunk: Buffer, _: BufferEncoding, callback: (error?: Error | null) => void): void { _write(chunk: Buffer, _: BufferEncoding, callback: (error?: Error | null) => void): void {
@ -101,7 +98,7 @@ export class WebmSeeker extends Duplex {
let err: Error | undefined; let err: Error | undefined;
if (this.state === WebmSeekerState.READING_HEAD) err = this.readHead(); if (this.state === WebmSeekerState.READING_HEAD) err = this.readHead();
else if (!this.seekfound) err = this.getClosestCluster(); else if (!this.seekfound) err = this.getClosestBlock();
else err = this.readTag(); else err = this.readTag();
if (err) callback(err); if (err) callback(err);
@ -196,10 +193,6 @@ export class WebmSeeker extends Duplex {
} else this.cursor += this.data_size + this.data_length; } else this.cursor += this.data_size + this.data_length;
if (ebmlID.name === 'simpleBlock') { if (ebmlID.name === 'simpleBlock') {
if (this.time_left !== 0 && this.mode === 'precise') {
if (data.readUInt16BE(1) === this.time_left) this.time_left = 0;
else continue;
}
const track = this.header.segment.tracks![this.header.audioTrack]; const track = this.header.segment.tracks![this.header.audioTrack];
if (!track || track.trackType !== 2) return new Error('No audio Track in this webm file.'); if (!track || track.trackType !== 2) return new Error('No audio Track in this webm file.');
if ((data[0] & 0xf) === track.trackNumber) this.push(data.slice(4)); if ((data[0] & 0xf) === track.trackNumber) this.push(data.slice(4));
@ -209,11 +202,28 @@ export class WebmSeeker extends Duplex {
this.cursor = 0; this.cursor = 0;
} }
private getClosestCluster(): Error | undefined { private getClosestBlock(): Error | undefined {
if (!this.chunk) return new Error('Chunk is missing'); if (!this.chunk) return new Error('Chunk is missing');
const count = this.chunk.indexOf('1f43b675', 0, 'hex'); this.cursor = 0
if (count === -1) throw new Error('Failed to find nearest Cluster.'); let positionFound = false;
else this.chunk = this.chunk.slice(count); while(!positionFound){
this.cursor = this.chunk.indexOf('a3', this.cursor, 'hex');
if (this.cursor === -1) return new Error('Failed to find nearest Block.');
this.cursor ++;
if(!this.vint_value()) return new Error("Failed to find correct simpleBlock in first chunk")
const data = this.chunk.slice(
this.cursor + this.data_size,
this.cursor + this.data_size + this.data_length
);
const track = this.header.segment.tracks![this.header.audioTrack];
if (!track || track.trackType !== 2) return new Error('No audio Track in this webm file.');
if ((data[0] & 0xf) === track.trackNumber) {
this.cursor += this.data_size + this.data_length;
this.push(data.slice(4))
positionFound = true;
}
else continue;
}
this.seekfound = true; this.seekfound = true;
return this.readTag(); return this.readTag();
} }

View File

@ -14,7 +14,6 @@ export enum StreamType {
} }
export interface StreamOptions { export interface StreamOptions {
seekMode?: 'precise' | 'granular';
seek?: number; seek?: number;
quality?: number; quality?: number;
language?: string; language?: string;