Skip to content

Commit 8cb7279

Browse files
committed
added thumbnail generator and meta function
1 parent 84d9179 commit 8cb7279

File tree

14 files changed

+378
-5
lines changed

14 files changed

+378
-5
lines changed

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1+
<p align="center">
2+
<img src="./public/icon.png" alt="Library Icon" width="164" height="164" />
3+
</p>
4+
5+
[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/diffusion-studio/ffmpeg-js/graphs/commit-activity)
6+
[![Website shields.io](https://img.shields.io/website-up-down-green-red/http/shields.io.svg)](https://ffmpeg-js-preview.vercel.app)
7+
[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/n3mpzfejAb)
8+
[![GitHub license](https://badgen.net/github/license/Naereen/Strapdown.js)](https://github.com/diffusion-studio/ffmpeg-js/blob/main/LICENSE)
9+
[![TypeScript](https://badgen.net/badge/icon/typescript?icon=typescript&label)](https://typescriptlang.org)
10+
111
# 🎥 FFmpeg.js: A WebAssembly-powered FFmpeg Interface for Browsers
212

313
Welcome to FFmpeg.js, an innovative library that offers a WebAssembly-powered interface for utilizing FFmpeg in the browser. 🌐💡
414

5-
### [👥Join our Discord](https://discord.gg/n3mpzfejAb)
6-
715
## Demo
816

917
![GIF Converter Demo](./public/preview.gif)
@@ -58,6 +66,7 @@ server: {
5866
### △Next.js
5967

6068
Here is an example `next.config.js` that supports the SharedArrayBuffer:
69+
6170
```
6271
module.exports = {
6372
async headers() {

package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@diffusion-studio/ffmpeg-js",
33
"private": false,
4-
"version": "0.1.0",
4+
"version": "0.2.0",
55
"description": "FFmpeg.js - Use FFmpeg in the browser powered by WebAssembly",
66
"type": "module",
77
"files": [
@@ -28,6 +28,7 @@
2828
"keywords": [
2929
"ffmpeg",
3030
"webassembly",
31+
"emscripten",
3132
"audio",
3233
"browser",
3334
"video",
@@ -42,9 +43,14 @@
4243
"mp3",
4344
"wav",
4445
"flac",
46+
"mkv",
47+
"mov",
4548
"ogg",
4649
"hevc",
4750
"h264",
51+
"h265",
52+
"quicktime",
53+
"matroska",
4854
"editing",
4955
"cutting"
5056
],

public/icon.png

36.1 KB
Loading

public/samples/video.mkv

560 KB
Binary file not shown.

public/samples/video_xl.mp4

-17 MB
Binary file not shown.

src/ffmpeg-base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ export class FFmpegBase {
222222
* have been written to the memfs memory
223223
*/
224224
public clearMemory(): void {
225-
for (const path of this._memory) {
225+
for (const path of [...new Set(this._memory)]) {
226226
this.deleteFile(path);
227227
}
228228
this._memory = [];

src/ffmpeg.ts

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { IFFmpegConfiguration } from './interfaces';
22
import { FFmpegBase } from './ffmpeg-base';
33
import * as types from './types';
44
import configs from './ffmpeg-config';
5-
import { noop } from './utils';
5+
import { noop, parseMetadata } from './utils';
66

77
export class FFmpeg<
88
Config extends IFFmpegConfiguration<
@@ -220,6 +220,82 @@ export class FFmpeg<
220220
return file;
221221
}
222222

223+
/**
224+
* Get the meta data of a the specified file.
225+
* Returns information such as codecs, fps, bitrate etc.
226+
*/
227+
public async meta(source: string | Blob): Promise<types.Metadata> {
228+
await this.writeFile('probe', source);
229+
const meta: types.Metadata = {
230+
streams: { audio: [], video: [] },
231+
};
232+
const callback = parseMetadata(meta);
233+
ffmpeg.onMessage(callback);
234+
await this.exec(['-i', 'probe']);
235+
ffmpeg.removeOnMessage(callback);
236+
this.clearMemory();
237+
return meta;
238+
}
239+
240+
/**
241+
* Generate a series of thumbnails
242+
* @param source Your input file
243+
* @param count The number of thumbnails to generate
244+
* @param start Lower time limit in seconds
245+
* @param stop Upper time limit in seconds
246+
* @example
247+
* // type AsyncGenerator<Blob, void, void>
248+
* const generator = ffmpeg.thumbnails('/samples/video.mp4');
249+
*
250+
* for await (const image of generator) {
251+
* const img = document.createElement('img');
252+
* img.src = URL.createObjectURL(image);
253+
* document.body.appendChild(img);
254+
* }
255+
*/
256+
public async *thumbnails(
257+
source: string | Blob,
258+
count: number = 5,
259+
start: number = 0,
260+
stop?: number
261+
): AsyncGenerator<Blob, void, void> {
262+
// make sure start and stop are defined
263+
if (!stop) {
264+
const { duration } = await this.meta(source);
265+
266+
// make sure the duration is defined
267+
if (duration) stop = duration;
268+
else {
269+
console.warn(
270+
'Could not extract duration from meta data please provide a stop argument. Falling back to 1sec otherwise.'
271+
);
272+
stop = 1;
273+
}
274+
}
275+
276+
// get the time increase for each iteration
277+
const step = (stop - start) / count;
278+
279+
await this.writeFile('input', source);
280+
281+
for (let i = start; i < stop; i += step) {
282+
await ffmpeg.exec([
283+
'-ss',
284+
i.toString(),
285+
'-i',
286+
'input',
287+
'-frames:v',
288+
'1',
289+
'image.jpg',
290+
]);
291+
try {
292+
const res = await ffmpeg.readFile('image.jpg');
293+
yield new Blob([res], { type: 'image/jpeg' });
294+
} catch (e) {}
295+
}
296+
this.clearMemory();
297+
}
298+
223299
private parseOutputOptions(): string[] {
224300
if (!this._output) {
225301
throw new Error('Please define the output first');

src/types/common.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,86 @@ export type WasmModuleURIs = {
108108
*/
109109
worker: string;
110110
};
111+
112+
/**
113+
* Defines the metadata of an audio stream
114+
*/
115+
export type AudioStream = {
116+
/**
117+
* String containing the id given
118+
* by ffmpeg, e.g. 0:1
119+
*/
120+
id?: string;
121+
/**
122+
* String containing the audio codec
123+
*/
124+
codec?: string;
125+
/**
126+
* Number containing the audio sample rate
127+
*/
128+
sampleRate?: number;
129+
};
130+
131+
/**
132+
* Defines the metadata of a video stream
133+
*/
134+
export type VideoStream = {
135+
/**
136+
* String containing the id given
137+
* by ffmpeg, e.g. 0:0
138+
*/
139+
id?: string;
140+
/**
141+
* String containing the video codec
142+
*/
143+
codec?: string;
144+
/**
145+
* Number containing the video width
146+
*/
147+
width?: number;
148+
/**
149+
* Number containing the video height
150+
*/
151+
height?: number;
152+
/**
153+
* Number containing the fps
154+
*/
155+
fps?: number;
156+
};
157+
158+
/**
159+
* Defines the metadata of a ffmpeg input log.
160+
* These information will be extracted from
161+
* the -i command.
162+
*/
163+
export type Metadata = {
164+
/**
165+
* Number containing the duration of the
166+
* input in seconds
167+
*/
168+
duration?: number;
169+
/**
170+
* String containing the bitrate of the file.
171+
* E.g 16 kb/s
172+
*/
173+
bitrate?: string;
174+
/**
175+
* Array of strings containing the applicable
176+
* container formats. E.g. mov, mp4, m4a,
177+
* 3gp, 3g2, mj2
178+
*/
179+
formats?: string[];
180+
/**
181+
* Separation in audio and video streams
182+
*/
183+
streams: {
184+
/**
185+
* Array of audio streams
186+
*/
187+
audio: AudioStream[];
188+
/**
189+
* Array of video streams
190+
*/
191+
video: VideoStream[];
192+
};
193+
};

src/types/gpl-extended.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export type ExtensionGPLExtended =
7171
| 'mp4'
7272
| 'mpg'
7373
| 'mpeg'
74+
| 'mkv'
7475
| 'mov'
7576
| 'ts'
7677
| 'm2t'

src/types/lgpl-base.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export type ExtensionBase =
7272
| 'mpg'
7373
| 'mpeg'
7474
| 'mov'
75+
| 'mkv'
7576
| 'ts'
7677
| 'm2t'
7778
| 'm2ts'

0 commit comments

Comments
 (0)