You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* feat: add MediaRecorder migration specification and outline new export workflows
* feat: implement RecordingService for video and audio processing
* feat: refactor VideoService to delegate audio and video conversion to RecordingService
* feat: add gifenc package
* feat: refactor VideoService to implement GIF encoding and improve error handling
* feat: migrate from VideoService to MediaExportService and remove FFmpeg dependencies
* feat: add setup script for Chromium installation for unit tests
* feat: refactor Animator and MediaExportService to streamline video creation and remove WebMWriter dependency
* chore: Remove FFmpeg WebAssembly core and worker script files to streamline the project and eliminate unused dependencies.
* fix: update comment in formatProgress method for clarity on progress capping
* refactor: enhance documentation in AnimatorService and MediaExportService for clarity and maintainability
* chore: remove unused .vscode entry from .gitignore and update mediarecorder migration spec to English
* feat: add production Docker Compose configuration for app service
* chore: remove tentative timeline section from mediarecorder migration spec
* refactor: improve file loading process and enhance error handling in Animator class
* feat: implement MediaImportService for handling media file imports and decoding
* chore: update packageManager version in package.json
* test: add unit tests for MediaImportService functionality
* feat: enhance MediaImportService to return video and audio blobs, refactor load method in Animator class
* refactor: update load method signatures in Animator and AnimatorService to use Blob type
* fix: improve error handling and refactor decodeFile method in Animator class
* fix: update handleFrame to accept blob and index parameters in Animator class
* feat: add ADR 0001 to enforce VP8 WebM exports for improved compatibility
* feat: implement frame manifest support in media import and animator services
All frames are converted to **WebP** format for internal processing:
30
-
31
-
```typescript
32
-
awaitthis.ffmpeg.exec([
33
-
"-i", inputPath,
34
-
"-c:v", "libwebp",
35
-
"-lossless", "0",
36
-
"-compression_level", "4",
37
-
"-quality", "65",
38
-
outputPath
39
-
]);
40
-
```
29
+
All frames are converted to **WebP** format for internal processing using the browser's Canvas APIs. Frames that arrive as JPEG are decoded via `createImageBitmap`, rendered into an offscreen canvas, and saved back as WebP blobs for consistent downstream handling.
41
30
42
31
**Rationale:**
43
32
- Better compression than JPEG (25-35% smaller files)
@@ -50,7 +39,7 @@ await this.ffmpeg.exec([
50
39
The application handles mixed JPEG/WebP frames intelligently:
51
40
52
41
1.**Capture:** Frames are captured as JPEG from canvas
53
-
2.**Storage:** JPEG frames are converted to WebP via FFmpeg for consistency
42
+
2.**Storage:** JPEG frames are converted to WebP via Canvas for consistency
54
43
3.**Export:** All frames are WebP when creating videos or GIFs
55
44
4.**Import:** Loaded frames are stored as WebP internally
56
45
@@ -69,7 +58,7 @@ This approach ensures:
69
58
All video outputs use the **WebM** container format:
70
59
71
60
```typescript
72
-
const result =awaitthis.videoService.createVideo(
61
+
const result =awaitthis.mediaExportService.createVideo(
73
62
this.animator.frameWebpsAndJpegs,
74
63
frameRate,
75
64
this.animator.audioBlob,
@@ -84,27 +73,18 @@ const result = await this.videoService.createVideo(
84
73
- Native HTML5 video element support
85
74
- Good compression ratios
86
75
87
-
### Video Codec: VP8 (libvpx)
76
+
### Video Codec: VP8 (MediaRecorder)
88
77
89
-
Video encoding uses **VP8** codec via FFmpeg:
90
-
91
-
```typescript
92
-
parameters.push(
93
-
"-vcodec", "libvpx",
94
-
"-vf", "scale=640:-2,format=yuv420p",
95
-
outputFileName
96
-
);
97
-
```
78
+
Video encoding uses **VP8** via the browser's native MediaRecorder implementation. The recording pipeline renders frames onto an offscreen canvas, captures a `MediaStream` with `canvas.captureStream(frameRate)`, optionally merges an audio stream, and records with a VP8-capable MIME type (preferring `video/webm;codecs=vp9,opus` and falling back to `video/webm;codecs=vp8,opus`).
- Royalty-free and hardware-accelerated in modern browsers
82
+
- Eliminates heavy WASM workers and virtual file systems
83
+
- Seamlessly combines audio/video tracks via `MediaStream`
104
84
105
85
**Video Processing Options:**
106
-
- Scale to max width of 640px (maintains aspect ratio)
107
-
- YUV 4:2:0 color space (standard for web video)
86
+
- Scale to max width of 640px before rendering
87
+
- YUV 4:2:0 color space is enforced by the browser implementation
108
88
- Configurable frame rate (default: 6 fps for stop motion)
109
89
110
90
### Future Consideration: VP9
@@ -137,38 +117,15 @@ this.audioRecorder = new MediaRecorder(this.audioStream, {
137
117
138
118
### Storage: WebM/Opus
139
119
140
-
Regardless of the recorder outcome, audio blobs are converted to **WebM/Opus**:
141
-
142
-
```typescript
143
-
awaitthis.ffmpeg.exec([
144
-
"-i", inputAudioPath,
145
-
"-vn",
146
-
"-c:a", "libopus",
147
-
outputPath// .webm
148
-
]);
149
-
```
150
-
151
-
**Rationale:**
152
-
- Aligns with the WebM-based video pipeline
153
-
- Opus is royalty-free and optimised for voice
120
+
Regardless of the recorder outcome, audio blobs are normalized to **WebM/Opus** via an `AudioContext`. The export service decodes the blob, replays it through a `MediaStreamAudioDestination`, and records the result so downstream consumers always receive an Opus/WebM payload.
154
121
155
122
## GIF Export
156
123
157
-
GIF creation uses FFmpeg with a two-pass palette approach:
GIF creation uses the lightweight **gifenc** library. Frames are decoded, resized to a maximum width of 480px, quantized to an indexed palette, and encoded via gifenc's `GifEncoder`. Progress callbacks fire between batches so the UI can stay responsive.
168
125
169
126
**Features:**
170
-
-Palette generation for optimal colors
171
-
-Lanczos scaling for quality
127
+
-Deterministic palette building and dithering for good quality
128
+
-Runs entirely on the client without WASM payloads
172
129
- Scaled to 480px width for reasonable file size
173
130
- Infinite loop by default
174
131
@@ -186,26 +143,9 @@ project.zip
186
143
187
144
**Implementation:**
188
145
- Uses @zip.js/zip.js library
189
-
-Frames are encoded to WebM using webm-writer library
146
+
-Video blobs originate from `MediaRecorder` (via MediaExportService)
190
147
- Audio is stored separately if present
191
148
192
-
### WebM Draft Format
193
-
194
-
Draft videos are created using the webm-writer library:
195
-
196
-
```typescript
197
-
const videoWriter =newWebMWriter({
198
-
quality: 0.95,
199
-
frameRate: frameRate,
200
-
transparent: false
201
-
});
202
-
```
203
-
204
-
**Rationale:**
205
-
- Fast encoding in browser
206
-
- Compatible with our import pipeline
207
-
- No server-side processing required
208
-
209
149
## Browser Compatibility Matrix
210
150
211
151
| Format | Chrome | Firefox | Safari | Edge |
@@ -238,7 +178,7 @@ The consistent use of WebM and WebP ensures:
238
178
### Memory Management
239
179
240
180
1.**Batch Processing:** JPEG→WebP conversion is batched to prevent memory issues
241
-
2.**Streaming:**FFmpeg processes frames in a temporary directory
181
+
2.**Streaming:**MediaRecorder writes directly to in-memory blobs
242
182
3.**Cleanup:** Working directories are cleaned up after processing
243
183
244
184
### Processing Time
@@ -256,15 +196,10 @@ Times vary based on:
256
196
257
197
## Dependencies
258
198
259
-
### FFmpeg.wasm
260
-
-**Purpose:** Video/audio encoding, format conversion
0 commit comments