Skip to content

Commit

Permalink
Merge pull request #22 from diffusionstudio/konstantin/fix/large-vide…
Browse files Browse the repository at this point in the history
…o-handling

Konstantin/fix/large video handling
  • Loading branch information
k9p5 authored Oct 20, 2024
2 parents 9a501fe + 65af040 commit 96eb5bf
Show file tree
Hide file tree
Showing 11 changed files with 44 additions and 30 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@diffusionstudio/core",
"private": false,
"version": "1.0.0-rc.8",
"version": "1.0.0",
"type": "module",
"description": "Build bleeding edge video processing applications",
"files": [
Expand Down
6 changes: 3 additions & 3 deletions src/clips/video/buffer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ describe('FrameBuffer', () => {

frameBuffer.enqueue(mockVideoFrame);

expect(frameBuffer['buffer'].length).toBe(1);
expect(frameBuffer['buffer'][0]).toBe(mockVideoFrame);
expect(frameBuffer.frames.length).toBe(1);
expect(frameBuffer.frames[0]).toBe(mockVideoFrame);
expect(mockOnEnqueue).toHaveBeenCalled();
});

Expand All @@ -44,7 +44,7 @@ describe('FrameBuffer', () => {

expect(dequeuedFrame1).toBe(frame1);
expect(dequeuedFrame2).toBe(frame2);
expect(frameBuffer['buffer'].length).toBe(0);
expect(frameBuffer.frames.length).toBe(0);
});

it('should wait for a frame to be enqueued if buffer is empty and state is active', async () => {
Expand Down
14 changes: 7 additions & 7 deletions src/clips/video/buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,27 @@
*/

export class FrameBuffer {
private buffer: Array<VideoFrame> = [];
private state: 'active' | 'closed' = 'active';
public frames: Array<VideoFrame> = [];
public state: 'active' | 'closed' = 'active';

public onenqueue?: () => void;
public onclose?: () => void;

public enqueue(data: VideoFrame) {
this.buffer.unshift(data);
this.frames.unshift(data);
this.onenqueue?.();
}

public async dequeue() {
if (this.buffer.length == 0 && this.state == 'active') {
if (this.frames.length == 0 && this.state == 'active') {
await this.waitFor(20e3);
}

if (this.buffer.length == 0 && this.state == 'closed') {
if (this.frames.length == 0 && this.state == 'closed') {
return;
}

return this.buffer.pop();
return this.frames.pop();
}

public close() {
Expand All @@ -35,7 +35,7 @@ export class FrameBuffer {
}

public terminate() {
for (const frame of this.buffer) {
for (const frame of this.frames) {
frame.close();
}
}
Expand Down
11 changes: 10 additions & 1 deletion src/clips/video/video.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ describe('The Video Clip', () => {
const composition = new Composition();
await composition.add(clip);

composition.computeFrame();

expect(clip.track?.view.children.length).toBe(1);

const buffer = new FrameBuffer();

Object.defineProperty(buffer, 'onenqueue', {
Expand All @@ -274,8 +278,13 @@ describe('The Video Clip', () => {
const decodeSpy = vi.spyOn(clip, 'decodeVideo').mockReturnValueOnce(buffer);
composition.state = 'RENDER';

await clip.seek(new Timestamp());
await composition.seek(0);

expect(clip.track?.view.children.length).toBe(0);

await composition.computeFrame();

expect(clip.track?.view.children.length).toBe(1);
expect(decodeSpy).toBeCalledTimes(1);
expect(seekFn.mock.calls[0][0]).toBe(0);
});
Expand Down
18 changes: 7 additions & 11 deletions src/clips/video/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ export class VideoClip extends VisualMixin(MediaClip<VideoClipProps>) {
await this.seek(Timestamp.fromFrames(frame));
}

public enter() {
super.enter();
if (this.track?.composition?.rendering && this.buffer?.state != 'active') {
this.decodeVideo();
}
}

@visualize
@textureSwap
public update(_: Timestamp): void | Promise<void> {
Expand Down Expand Up @@ -139,17 +146,6 @@ export class VideoClip extends VisualMixin(MediaClip<VideoClipProps>) {
return clip;
}

public async seek(time: Timestamp): Promise<void> {
if (this.track?.composition?.rendering) {
const buffer = await this.decodeVideo();
return new Promise<void>((resolve) => {
buffer.onenqueue = () => resolve();
});
}

return super.seek(time);
}

private async decodeVideo() {
this.buffer = new FrameBuffer();
this.worker = new DecodeWorker();
Expand Down
3 changes: 1 addition & 2 deletions src/encoders/encoder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ describe('The Encoder', () => {
await encoder.render();

expect(pauseSpy).toBeCalledTimes(1);
// before and after
expect(seekSpy).toBeCalledTimes(2);
expect(seekSpy).toBeCalledTimes(1);
});

it('should not render when the composition renderer is not defined', async () => {
Expand Down
1 change: 0 additions & 1 deletion src/encoders/encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ export class Encoder extends WebcodecsVideoEncoder {
this.composition.state = 'IDLE';
this.composition.renderer.resolution = 1;
this.composition.fps = FPS_DEFAULT;
this.composition.seek(0);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/models/transcript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type { Captions } from '../types';
import type { Serializer } from '../services';

export class Transcript implements Serializer {
public readonly id = crypto.randomUUID();
public id = crypto.randomUUID();
public language: Language = Language.en;
public groups: WordGroup[] = [];

Expand Down
2 changes: 1 addition & 1 deletion src/services/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export class Serializer {
/**
* Unique identifier of the object
*/
public readonly id = crypto.randomUUID();
public id = crypto.randomUUID();

toJSON(): any {
const obj: any = {};
Expand Down
11 changes: 11 additions & 0 deletions src/tracks/video/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,19 @@
*/

import { MediaTrack } from '../media';
import { Timestamp } from '../../models';

import type { VideoClip } from '../../clips';

export class VideoTrack extends MediaTrack<VideoClip> {
public readonly type = 'video';

public async seek(time: Timestamp): Promise<void> {
if (this.composition?.rendering) {
// ensures that 'enter' method will be called again
this.view.removeChildren();
} else {
super.seek(time);
}
}
}

0 comments on commit 96eb5bf

Please sign in to comment.