diff --git a/examples/jspsych-html-video-response.html b/examples/jspsych-html-video-response.html index 77bc2849..6575843d 100644 --- a/examples/jspsych-html-video-response.html +++ b/examples/jspsych-html-video-response.html @@ -14,6 +14,7 @@ const init_camera = { type: jsPsychInitializeCamera, width: 320, + include_audio: true } const record = { diff --git a/packages/jspsych/src/modules/plugin-api/MediaAPI.ts b/packages/jspsych/src/modules/plugin-api/MediaAPI.ts index ad085524..15b6deb4 100644 --- a/packages/jspsych/src/modules/plugin-api/MediaAPI.ts +++ b/packages/jspsych/src/modules/plugin-api/MediaAPI.ts @@ -338,9 +338,9 @@ export class MediaAPI { private camera_stream: MediaStream = null; private camera_recorder: MediaRecorder = null; - initializeCameraRecorder(stream: MediaStream) { + initializeCameraRecorder(stream: MediaStream, opts?: MediaRecorderOptions) { this.camera_stream = stream; - const recorder = new MediaRecorder(stream, { mimeType: 'video/webm;codecs="vp9"' }); + const recorder = new MediaRecorder(stream, opts); this.camera_recorder = recorder; } diff --git a/packages/plugin-initialize-camera/src/index.ts b/packages/plugin-initialize-camera/src/index.ts index 099c8c1e..08c39a26 100644 --- a/packages/plugin-initialize-camera/src/index.ts +++ b/packages/plugin-initialize-camera/src/index.ts @@ -3,16 +3,19 @@ import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from "jspsych"; const info = { name: "initialize-camera", parameters: { - /** Function to call */ device_select_message: { type: ParameterType.HTML_STRING, default: `

Please select the camera you would like to use.

`, }, - /** Is the function call asynchronous? */ + /** */ button_label: { type: ParameterType.STRING, default: "Use this camera", }, + include_audio: { + type: ParameterType.BOOL, + default: false, + }, width: { type: ParameterType.INT, default: null, @@ -21,6 +24,10 @@ const info = { type: ParameterType.INT, default: null, }, + mime_type: { + type: ParameterType.STRING, + default: null, + }, }, }; @@ -49,7 +56,7 @@ class InitializeCameraPlugin implements JsPsychPlugin { } private async run_trial(display_element: HTMLElement, trial: TrialType) { - await this.askForPermission(); + await this.askForPermission(trial); this.showCameraSelection(display_element, trial); @@ -69,16 +76,27 @@ class InitializeCameraPlugin implements JsPsychPlugin { if (trial.height) { constraints.video.height = trial.height; } + if (trial.include_audio) { + constraints.audio = true; + } const stream = await navigator.mediaDevices.getUserMedia(constraints); - this.jsPsych.pluginAPI.initializeCameraRecorder(stream); + const recorder_options: MediaRecorderOptions = {}; + if (trial.mime_type) { + recorder_options.mimeType = trial.mime_type; + } + + this.jsPsych.pluginAPI.initializeCameraRecorder(stream, recorder_options); return camera_id; } - private async askForPermission() { - const stream = await navigator.mediaDevices.getUserMedia({ audio: false, video: true }); + private async askForPermission(trial: TrialType) { + const stream = await navigator.mediaDevices.getUserMedia({ + audio: trial.include_audio, + video: true, + }); return stream; } @@ -102,14 +120,14 @@ class InitializeCameraPlugin implements JsPsychPlugin { private updateDeviceList(display_element) { navigator.mediaDevices.enumerateDevices().then((devices) => { - const mics = devices.filter( + const cams = devices.filter( (d) => d.kind === "videoinput" && d.deviceId !== "default" && d.deviceId !== "communications" ); // remove entries with duplicate groupID - const unique_cameras = mics.filter( - (mic, index, arr) => arr.findIndex((v) => v.groupId == mic.groupId) == index + const unique_cameras = cams.filter( + (cam, index, arr) => arr.findIndex((v) => v.groupId == cam.groupId) == index ); // reset the list by clearing all current options