Skip to content

Commit fd419bb

Browse files
Merge pull request #15 from apivideo/add-mimetype-option
Add mimeType option
2 parents e774856 + 526088b commit fd419bb

File tree

4 files changed

+49
-27
lines changed

4 files changed

+49
-27
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Changelog
22
All changes to this project will be documented in this file.
33

4+
## [1.0.9] - 2023-02-07
5+
- Add `mimeType` and `generateFileOnStop` options
6+
47
## [1.0.8] - 2023-01-23
58
- Add "videoPlayable" event
69

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@api.video/media-recorder",
3-
"version": "1.0.8",
3+
"version": "1.0.9",
44
"description": "api.video media recorder - upload video from your webcam with ease",
55
"repository": {
66
"type": "git",

src/index.ts

+43-24
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ export { VideoUploadError } from "@api.video/video-uploader/dist/src/abstract-up
66

77
export interface Options {
88
onError?: (error: VideoUploadError) => void;
9+
generateFileOnStop?: boolean;
10+
mimeType?: string;
911
}
1012

1113
let PACKAGE_VERSION = "";
@@ -25,15 +27,31 @@ export class ApiVideoMediaRecorder {
2527
private onVideoAvailable?: (video: VideoUploadResponse) => void;
2628
private onStopError?: (error: VideoUploadError) => void;
2729
private eventTarget: EventTarget;
30+
private debugChunks: Blob[] = [];
31+
private generateFileOnStop: boolean;
32+
private mimeType: string;
2833

2934
constructor(mediaStream: MediaStream, options: Options & (ProgressiveUploaderOptionsWithUploadToken | ProgressiveUploaderOptionsWithAccessToken)) {
3035
this.eventTarget = new EventTarget();
31-
const supportedTypes = this.getSupportedMimeTypes();
32-
if (supportedTypes.length === 0) {
33-
throw new Error("No compatible supported video mime type");
36+
this.generateFileOnStop = options.generateFileOnStop || false;
37+
38+
const findBestMimeType = () => {
39+
const supportedTypes = this.getSupportedMimeTypes();
40+
if (supportedTypes.length === 0) {
41+
throw new Error("No compatible supported video mime type");
42+
}
43+
return supportedTypes[0];
3444
}
45+
46+
this.mimeType = options.mimeType || findBestMimeType();
47+
3548
this.mediaRecorder = new MediaRecorder(mediaStream, {
36-
mimeType: supportedTypes[0],
49+
mimeType: this.mimeType,
50+
});
51+
52+
this.mediaRecorder.addEventListener("stop", () => {
53+
const stopEventPayload = this.generateFileOnStop ? { file: new Blob(this.debugChunks, { type: this.mimeType }) } : {};
54+
this.dispatch("recordingStopped", stopEventPayload);
3755
});
3856

3957
this.streamUpload = new ProgressiveUploader({
@@ -53,7 +71,7 @@ export class ApiVideoMediaRecorder {
5371
}
5472

5573
public addEventListener(type: EventType, callback: EventListenerOrEventListenerObject | null, options?: boolean | AddEventListenerOptions | undefined): void {
56-
if(type === "videoPlayable") {
74+
if (type === "videoPlayable") {
5775
this.streamUpload.onPlayable((video) => this.dispatch("videoPlayable", video));
5876
}
5977
this.eventTarget.addEventListener(type, callback, options);
@@ -62,6 +80,9 @@ export class ApiVideoMediaRecorder {
6280
private async onDataAvailable(ev: BlobEvent) {
6381
const isLast = (ev as any).currentTarget.state === "inactive";
6482
try {
83+
if (this.generateFileOnStop) {
84+
this.debugChunks.push(ev.data);
85+
}
6586
if (isLast) {
6687
const video = await this.streamUpload.uploadLastPart(ev.data);
6788

@@ -72,18 +93,18 @@ export class ApiVideoMediaRecorder {
7293
await this.streamUpload.uploadPart(ev.data);
7394
}
7495
} catch (error) {
75-
if (!isLast) this.stopMediaRecorder();
96+
if (!isLast) this.mediaRecorder.stop();
7697
this.dispatch("error", error);
7798
if (this.onStopError) this.onStopError(error as VideoUploadError);
7899
}
79100
}
80101

81102
private dispatch(type: EventType, data: any): boolean {
82-
return this.eventTarget.dispatchEvent(Object.assign(new Event(type), {data}));
103+
return this.eventTarget.dispatchEvent(Object.assign(new Event(type), { data }));
83104
}
84105

85106
public start(options?: { timeslice?: number }) {
86-
if(this.getMediaRecorderState() === "recording") {
107+
if (this.getMediaRecorderState() === "recording") {
87108
throw new Error("MediaRecorder is already recording");
88109
}
89110
this.mediaRecorder.start(options?.timeslice || 5000);
@@ -95,46 +116,42 @@ export class ApiVideoMediaRecorder {
95116

96117
public stop(): Promise<VideoUploadResponse> {
97118
return new Promise((resolve, reject) => {
98-
if(this.getMediaRecorderState() === "inactive") {
119+
if (this.getMediaRecorderState() === "inactive") {
99120
reject(new Error("MediaRecorder is already inactive"));
100121
}
101-
this.stopMediaRecorder();
122+
this.mediaRecorder.stop();
102123
this.onVideoAvailable = (v) => resolve(v);
103124
this.onStopError = (e) => reject(e);
104125
})
105126
}
106127

107128
public pause() {
108-
if(this.getMediaRecorderState() !== "recording") {
129+
if (this.getMediaRecorderState() !== "recording") {
109130
throw new Error("MediaRecorder is not recording");
110131
}
111132
this.mediaRecorder.pause();
112133
}
113134

114-
private stopMediaRecorder() {
115-
this.mediaRecorder.stop();
116-
this.dispatch("recordingStopped", {});
117-
}
118-
119-
private getSupportedMimeTypes() {
135+
public getSupportedMimeTypes() {
120136
const VIDEO_TYPES = [
137+
"mp4",
121138
"webm",
122139
"ogg",
123-
"mp4",
124140
"x-matroska"
125141
];
126142
const VIDEO_CODECS = [
127-
"h264",
128-
"h.264",
143+
"vp9,opus",
144+
"vp8,opus",
129145
"vp9",
130146
"vp9.0",
131147
"vp8",
132148
"vp8.0",
149+
"h264",
150+
"h.264",
133151
"avc1",
134152
"av1",
135153
"h265",
136154
"h.265",
137-
"opus",
138155
];
139156

140157
const supportedTypes: string[] = [];
@@ -148,10 +165,12 @@ export class ApiVideoMediaRecorder {
148165
`${type};codecs:${codec.toUpperCase()}`,
149166
`${type}`
150167
]
151-
variations.forEach(variation => {
152-
if (MediaRecorder.isTypeSupported(variation))
168+
for (const variation of variations) {
169+
if (MediaRecorder.isTypeSupported(variation)) {
153170
supportedTypes.push(variation);
154-
})
171+
break;
172+
}
173+
}
155174
});
156175
});
157176
return supportedTypes;

0 commit comments

Comments
 (0)