@lookit/record 3.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -39
- package/dist/index.browser.js +96 -33
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.min.js +13 -13
- package/dist/index.browser.min.js.map +1 -1
- package/dist/index.cjs +95 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +42 -3
- package/dist/index.js +95 -32
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/consentVideo.spec.ts +38 -11
- package/src/consentVideo.ts +75 -18
- package/src/errors.ts +7 -27
- package/src/recorder.spec.ts +208 -1
- package/src/recorder.ts +36 -5
- package/src/videoConfig.spec.ts +115 -0
- package/src/videoConfig.ts +38 -1
package/src/videoConfig.spec.ts
CHANGED
|
@@ -132,6 +132,30 @@ beforeEach(() => {
|
|
|
132
132
|
cameras: [devicesObj.cam1, devicesObj.cam2],
|
|
133
133
|
mics: [devicesObj.mic1, devicesObj.mic2],
|
|
134
134
|
};
|
|
135
|
+
|
|
136
|
+
// Global MediaRecorder.isTypeSupported
|
|
137
|
+
Object.defineProperty(global, "MediaRecorder", {
|
|
138
|
+
writable: true,
|
|
139
|
+
value: jest.fn().mockImplementation(() => ({
|
|
140
|
+
start: jest.fn(),
|
|
141
|
+
ondataavailable: jest.fn(),
|
|
142
|
+
onerror: jest.fn(),
|
|
143
|
+
state: "",
|
|
144
|
+
stop: jest.fn(),
|
|
145
|
+
pause: jest.fn(),
|
|
146
|
+
resume: jest.fn(),
|
|
147
|
+
})),
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
Object.defineProperty(MediaRecorder, "isTypeSupported", {
|
|
151
|
+
writable: true,
|
|
152
|
+
/**
|
|
153
|
+
* Placeholder for value
|
|
154
|
+
*
|
|
155
|
+
* @returns True
|
|
156
|
+
*/
|
|
157
|
+
value: () => true,
|
|
158
|
+
});
|
|
135
159
|
});
|
|
136
160
|
|
|
137
161
|
afterEach(() => {
|
|
@@ -1096,3 +1120,94 @@ test("Video config onMicActivityLevel", () => {
|
|
|
1096
1120
|
expect(video_config["micChecked"]).toBe(true);
|
|
1097
1121
|
expect(event_pass.resolve).toHaveBeenCalled();
|
|
1098
1122
|
});
|
|
1123
|
+
|
|
1124
|
+
test("Video config initializeAndCreateRecorder uses supported mime type", () => {
|
|
1125
|
+
const getCompatibleMimeTypeSpy = jest.spyOn(
|
|
1126
|
+
video_config,
|
|
1127
|
+
"getCompatibleMimeType",
|
|
1128
|
+
);
|
|
1129
|
+
|
|
1130
|
+
// getCameraRecorder is just a convenient way of grabbing the mock stream.
|
|
1131
|
+
video_config["initializeAndCreateRecorder"](
|
|
1132
|
+
jsPsych.pluginAPI.getCameraRecorder().stream,
|
|
1133
|
+
);
|
|
1134
|
+
|
|
1135
|
+
expect(getCompatibleMimeTypeSpy).toHaveBeenCalled();
|
|
1136
|
+
// isTypeSupported is already mocked to return true, so this should use the first mime type value in the list
|
|
1137
|
+
expect(jsPsych.pluginAPI.initializeCameraRecorder).toHaveBeenCalledWith(
|
|
1138
|
+
jsPsych.pluginAPI.getCameraRecorder().stream,
|
|
1139
|
+
{ mimeType: "video/webm;codecs=vp9,opus" },
|
|
1140
|
+
);
|
|
1141
|
+
});
|
|
1142
|
+
|
|
1143
|
+
test("Video config initializeAndCreateRecorder uses default if no mime types are supported", () => {
|
|
1144
|
+
const getCompatibleMimeTypeSpy = jest.spyOn(
|
|
1145
|
+
video_config,
|
|
1146
|
+
"getCompatibleMimeType",
|
|
1147
|
+
);
|
|
1148
|
+
|
|
1149
|
+
// Override the isTypeSupported mock that is set in beforeEach
|
|
1150
|
+
// No type is supported
|
|
1151
|
+
jest.spyOn(MediaRecorder, "isTypeSupported").mockImplementation(() => {
|
|
1152
|
+
return false;
|
|
1153
|
+
});
|
|
1154
|
+
expect(MediaRecorder.isTypeSupported("video/webm;codecs=vp9,opus")).toBe(
|
|
1155
|
+
false,
|
|
1156
|
+
);
|
|
1157
|
+
|
|
1158
|
+
// getCameraRecorder is just a convenient way of grabbing the mock stream.
|
|
1159
|
+
video_config["initializeAndCreateRecorder"](
|
|
1160
|
+
jsPsych.pluginAPI.getCameraRecorder().stream,
|
|
1161
|
+
);
|
|
1162
|
+
|
|
1163
|
+
expect(getCompatibleMimeTypeSpy).toHaveBeenCalled();
|
|
1164
|
+
// If there are no compatible mime types, then it should use the default "video/webm" for initialization.
|
|
1165
|
+
expect(jsPsych.pluginAPI.initializeCameraRecorder).toHaveBeenCalledWith(
|
|
1166
|
+
jsPsych.pluginAPI.getCameraRecorder().stream,
|
|
1167
|
+
{ mimeType: "video/webm" },
|
|
1168
|
+
);
|
|
1169
|
+
});
|
|
1170
|
+
|
|
1171
|
+
test("Video config getCompatibleMimeType gets correct mime type or null", () => {
|
|
1172
|
+
// Note - don't use 'mockImplementationOnce' for the isTypeSupported mock because isTypeSupported can be called multiple times by getCompatibleMimeType.
|
|
1173
|
+
|
|
1174
|
+
// Override the isTypeSupported mock that is set in beforeEach
|
|
1175
|
+
// 1. only supports vp9,opus
|
|
1176
|
+
const isTypeSupportedSpy = jest
|
|
1177
|
+
.spyOn(MediaRecorder, "isTypeSupported")
|
|
1178
|
+
.mockImplementation((type) => {
|
|
1179
|
+
if (type == "video/webm;codecs=vp9,opus") {
|
|
1180
|
+
return true;
|
|
1181
|
+
} else {
|
|
1182
|
+
return false;
|
|
1183
|
+
}
|
|
1184
|
+
});
|
|
1185
|
+
const mime_type_1 = video_config["getCompatibleMimeType"]();
|
|
1186
|
+
expect(mime_type_1).toBe("video/webm;codecs=vp9,opus");
|
|
1187
|
+
|
|
1188
|
+
// 2. only supports vp8,opus
|
|
1189
|
+
isTypeSupportedSpy.mockImplementation((type) => {
|
|
1190
|
+
if (type == "video/webm;codecs=vp8,opus") {
|
|
1191
|
+
return true;
|
|
1192
|
+
} else {
|
|
1193
|
+
return false;
|
|
1194
|
+
}
|
|
1195
|
+
});
|
|
1196
|
+
const mime_type_2 = video_config["getCompatibleMimeType"]();
|
|
1197
|
+
expect(mime_type_2).toBe("video/webm;codecs=vp8,opus");
|
|
1198
|
+
|
|
1199
|
+
// 3. supports vp9,opus and vp8,opus, should use the former
|
|
1200
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1201
|
+
isTypeSupportedSpy.mockImplementation((type) => {
|
|
1202
|
+
return true;
|
|
1203
|
+
});
|
|
1204
|
+
const mime_type_3 = video_config["getCompatibleMimeType"]();
|
|
1205
|
+
expect(mime_type_3).toBe("video/webm;codecs=vp9,opus");
|
|
1206
|
+
|
|
1207
|
+
// 4. none supported, should return null
|
|
1208
|
+
isTypeSupportedSpy.mockImplementation(() => {
|
|
1209
|
+
return false;
|
|
1210
|
+
});
|
|
1211
|
+
const mime_type_4 = video_config["getCompatibleMimeType"]();
|
|
1212
|
+
expect(mime_type_4).toBeNull();
|
|
1213
|
+
});
|
package/src/videoConfig.ts
CHANGED
|
@@ -99,6 +99,7 @@ export default class VideoConfigPlugin implements JsPsychPlugin<Info> {
|
|
|
99
99
|
private minVolume: number = 0.1;
|
|
100
100
|
private micChecked: boolean = false;
|
|
101
101
|
private processorNode: AudioWorkletNode | null = null;
|
|
102
|
+
private mimeType = "video/webm";
|
|
102
103
|
|
|
103
104
|
/**
|
|
104
105
|
* Constructor for video config plugin.
|
|
@@ -404,12 +405,20 @@ export default class VideoConfigPlugin implements JsPsychPlugin<Info> {
|
|
|
404
405
|
* @param stream - Media stream returned from getUserMedia that should be used
|
|
405
406
|
* to set up the jsPsych recorder.
|
|
406
407
|
* @param opts - Media recorder options to use when setting up the recorder.
|
|
408
|
+
* This will include the mimeType property that is set via getMimeTypeCodec,
|
|
409
|
+
* as well as any other options that can passed via the calling context.
|
|
407
410
|
*/
|
|
408
411
|
public initializeAndCreateRecorder = (
|
|
409
412
|
stream: MediaStream,
|
|
410
413
|
opts?: MediaRecorderOptions,
|
|
411
414
|
) => {
|
|
412
|
-
|
|
415
|
+
// If no mime types from the list are supported (getCompatibleMimeType returns null) then use the default.
|
|
416
|
+
this.mimeType = this.getCompatibleMimeType() || this.mimeType;
|
|
417
|
+
const recorder_options: MediaRecorderOptions = {
|
|
418
|
+
...opts,
|
|
419
|
+
mimeType: this.mimeType,
|
|
420
|
+
};
|
|
421
|
+
this.jsPsych.pluginAPI.initializeCameraRecorder(stream, recorder_options);
|
|
413
422
|
this.recorder = new Recorder(this.jsPsych);
|
|
414
423
|
};
|
|
415
424
|
|
|
@@ -660,4 +669,32 @@ export default class VideoConfigPlugin implements JsPsychPlugin<Info> {
|
|
|
660
669
|
next_button_el.classList.remove(`${html_params.step_complete_class}`);
|
|
661
670
|
}
|
|
662
671
|
};
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Check support for recording containers/codecs, in order of preference, and
|
|
675
|
+
* get the first supported type. The first supported type found in the
|
|
676
|
+
* mime_types array is returned and will be passed to the "mimeType" property
|
|
677
|
+
* in the recorder options object that is passed to the recorder
|
|
678
|
+
* initialization function (jsPsych.pluginAPI.initializeCameraRecorder). If
|
|
679
|
+
* none of these types is supported, the function returns null.
|
|
680
|
+
*
|
|
681
|
+
* Note: we will likely need to continuously update the mime_types list as new
|
|
682
|
+
* formats become supported, we support other browsers/versions, etc.
|
|
683
|
+
*
|
|
684
|
+
* @returns Mime type string, or null (if none from the array are supported).
|
|
685
|
+
*/
|
|
686
|
+
private getCompatibleMimeType() {
|
|
687
|
+
const mime_types = [
|
|
688
|
+
"video/webm;codecs=vp9,opus",
|
|
689
|
+
"video/webm;codecs=vp8,opus",
|
|
690
|
+
];
|
|
691
|
+
let mime_type_index = 0;
|
|
692
|
+
while (mime_type_index < mime_types.length) {
|
|
693
|
+
if (MediaRecorder.isTypeSupported(mime_types[mime_type_index])) {
|
|
694
|
+
return mime_types[mime_type_index];
|
|
695
|
+
}
|
|
696
|
+
mime_type_index++;
|
|
697
|
+
}
|
|
698
|
+
return null;
|
|
699
|
+
}
|
|
663
700
|
}
|