From 02f0822fa91dfff9980fa60bc473d3e53e03af3c Mon Sep 17 00:00:00 2001 From: Ryan Plauche Date: Mon, 31 Aug 2020 16:34:01 -0500 Subject: [PATCH 1/6] Adding optional deviceId prop --- src/index.ts | 9 ++++++++- yarn.lock | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 yarn.lock diff --git a/src/index.ts b/src/index.ts index b3d5ca1..3ff2922 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,6 +23,7 @@ type ReactMediaRecorderProps = { onStop?: (blobUrl: string, blob: Blob) => void; blobPropertyBag?: BlobPropertyBag; mediaRecorderOptions?: MediaRecorderOptions | null; + deviceId?: string | null; }; type StatusMessages = @@ -59,6 +60,7 @@ export const ReactMediaRecorder = ({ blobPropertyBag, screen = false, mediaRecorderOptions = null, + deviceId = null, }: ReactMediaRecorderProps) => { const mediaRecorder = useRef(null); const mediaChunks = useRef([]); @@ -70,10 +72,15 @@ export const ReactMediaRecorder = ({ const getMediaStream = useCallback(async () => { setStatus("acquiring_media"); - const requiredMedia: MediaStreamConstraints = { + let requiredMedia: MediaStreamConstraints = { audio: typeof audio === "boolean" ? !!audio : audio, video: typeof video === "boolean" ? !!video : video, }; + if (deviceId) { + requiredMedia.video = { + deviceId: deviceId, + }; + } try { if (screen) { //@ts-ignore diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..757d754 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,41 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/dom-mediacapture-record@^1.0.2": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@types/dom-mediacapture-record/-/dom-mediacapture-record-1.0.7.tgz#08bacca4296ef521d59049f43e65cf971bbf6be1" + integrity sha512-ddDIRTO1ajtbxaNo2o7fPJggpN54PZf1ZUJKOjto2ENMJE/9GKUvaw3ZRuQzlS/p0E+PnIcssxfoqYJ4yiXSBw== + +"@types/node@^12.12.11": + version "12.12.54" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.54.tgz#a4b58d8df3a4677b6c08bfbc94b7ad7a7a5f82d1" + integrity sha512-ge4xZ3vSBornVYlDnk7yZ0gK6ChHf/CHB7Gl1I0Jhah8DDnEQqBzgohYG4FX4p81TNirSETOiSyn+y1r9/IR6w== + +"@types/prop-types@*": + version "15.7.3" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" + integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== + +"@types/react@^16.9.11": + version "16.9.49" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.49.tgz#09db021cf8089aba0cdb12a49f8021a69cce4872" + integrity sha512-DtLFjSj0OYAdVLBbyjhuV9CdGVHCkHn2R+xr3XkBvK2rS1Y1tkc14XSGjYgm5Fjjr90AxH9tiSzc1pCFMGO06g== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + +csstype@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.3.tgz#2b410bbeba38ba9633353aff34b05d9755d065f8" + integrity sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag== + +jsmin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/jsmin/-/jsmin-1.0.1.tgz#e7bd0dcd6496c3bf4863235bf461a3d98aa3b98c" + integrity sha1-570NzWSWw79IYyNb9GGj2YqjuYw= + +typescript@^3.7.2: + version "3.9.7" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" + integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== From a4ee1d2f4d0557939eceed0bc507b23d4a94d096 Mon Sep 17 00:00:00 2001 From: Ryan Plauche Date: Tue, 1 Sep 2020 08:44:24 -0500 Subject: [PATCH 2/6] Including lib --- .gitignore | 2 +- lib/index.d.ts | 29 +++++++++++++++++++++++++++++ lib/index.js | 18 ++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 lib/index.d.ts create mode 100644 lib/index.js diff --git a/.gitignore b/.gitignore index d2f2174..f85065c 100644 --- a/.gitignore +++ b/.gitignore @@ -58,4 +58,4 @@ typings/ .env # build directories -lib \ No newline at end of file +# lib diff --git a/lib/index.d.ts b/lib/index.d.ts new file mode 100644 index 0000000..51b76cb --- /dev/null +++ b/lib/index.d.ts @@ -0,0 +1,29 @@ +/// +import { ReactElement } from "react"; +declare type ReactMediaRecorderRenderProps = { + error: string; + muteAudio: () => void; + unMuteAudio: () => void; + startRecording: () => void; + pauseRecording: () => void; + resumeRecording: () => void; + stopRecording: () => void; + mediaBlobUrl: null | string; + status: StatusMessages; + isAudioMuted: boolean; + previewStream: MediaStream | null; + clearBlobUrl: () => void; +}; +declare type ReactMediaRecorderProps = { + render: (props: ReactMediaRecorderRenderProps) => ReactElement; + audio?: boolean | MediaTrackConstraints; + video?: boolean | MediaTrackConstraints; + screen?: boolean; + onStop?: (blobUrl: string, blob: Blob) => void; + blobPropertyBag?: BlobPropertyBag; + mediaRecorderOptions?: MediaRecorderOptions | null; + deviceId?: string | null; +}; +declare type StatusMessages = "media_aborted" | "permission_denied" | "no_specified_media_found" | "media_in_use" | "invalid_media_constraints" | "no_constraints" | "recorder_error" | "idle" | "acquiring_media" | "delayed_start" | "recording" | "stopping" | "stopped"; +export declare const ReactMediaRecorder: ({ render, audio, video, onStop, blobPropertyBag, screen, mediaRecorderOptions, deviceId, }: ReactMediaRecorderProps) => ReactElement ReactElement import("react").Component)> | null) | (new (props: any) => import("react").Component)>; +export {}; diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..0292618 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,18 @@ +"use strict";var __awaiter=(this&&this.__awaiter)||function(thisArg,_arguments,P,generator){function adopt(value){return value instanceof P?value:new P(function(resolve){resolve(value);});} +return new(P||(P=Promise))(function(resolve,reject){function fulfilled(value){try{step(generator.next(value));}catch(e){reject(e);}} +function rejected(value){try{step(generator["throw"](value));}catch(e){reject(e);}} +function step(result){result.done?resolve(result.value):adopt(result.value).then(fulfilled,rejected);} +step((generator=generator.apply(thisArg,_arguments||[])).next());});};var __generator=(this&&this.__generator)||function(thisArg,body){var _={label:0,sent:function(){if(t[0]&1)throw t[1];return t[1];},trys:[],ops:[]},f,y,t,g;return g={next:verb(0),"throw":verb(1),"return":verb(2)},typeof Symbol==="function"&&(g[Symbol.iterator]=function(){return this;}),g;function verb(n){return function(v){return step([n,v]);};} +function step(op){if(f)throw new TypeError("Generator is already executing.");while(_)try{if(f=1,y&&(t=op[0]&2?y["return"]:op[0]?y["throw"]||((t=y["return"])&&t.call(y),0):y.next)&&!(t=t.call(y,op[1])).done)return t;if(y=0,t)op=[op[0]&2,t.value];switch(op[0]){case 0:case 1:t=op;break;case 4:_.label++;return{value:op[1],done:false};case 5:_.label++;y=op[1];op=[0];continue;case 7:op=_.ops.pop();_.trys.pop();continue;default:if(!(t=_.trys,t=t.length>0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue;} +if(op[0]===3&&(!t||(op[1]>t[0]&&op[1]0){console.error("The constraints "+unSupportedConstraints.join(",")+" doesn't support on this browser. Please check your ReactMediaRecorder component.");}};if(typeof audio==="object"){checkConstraints(audio);} +if(typeof video==="object"){checkConstraints(video);} +if(mediaRecorderOptions&&mediaRecorderOptions.mimeType){if(!MediaRecorder.isTypeSupported(mediaRecorderOptions.mimeType)){console.error("The specified MIME type you supplied for MediaRecorder doesn't support this browser");}} +if(!mediaStream.current){getMediaStream();}},[audio,screen,video,getMediaStream,mediaRecorderOptions]);var startRecording=function(){return __awaiter(void 0,void 0,void 0,function(){var isStreamEnded;return __generator(this,function(_a){switch(_a.label){case 0:setError("NONE");if(!!mediaStream.current)return[3,2];return[4,getMediaStream()];case 1:_a.sent();_a.label=2;case 2:if(!mediaStream.current)return[3,5];isStreamEnded=mediaStream.current.getTracks().some(function(track){return track.readyState==="ended";});if(!isStreamEnded)return[3,4];return[4,getMediaStream()];case 3:_a.sent();_a.label=4;case 4:mediaRecorder.current=new MediaRecorder(mediaStream.current);mediaRecorder.current.ondataavailable=onRecordingActive;mediaRecorder.current.onstop=onRecordingStop;mediaRecorder.current.onerror=function(){setError("NO_RECORDER");setStatus("idle");};mediaRecorder.current.start();setStatus("recording");_a.label=5;case 5:return[2];}});});};var onRecordingActive=function(_a){var data=_a.data;mediaChunks.current.push(data);};var onRecordingStop=function(){var chunk=mediaChunks.current[0];var blobProperty=Object.assign({type:chunk.type},blobPropertyBag||(video?{type:"video/mp4"}:{type:"audio/wav"}));var blob=new Blob(mediaChunks.current,blobProperty);var url=URL.createObjectURL(blob);setStatus("stopped");setMediaBlobUrl(url);onStop(url,blob);};var muteAudio=function(mute){setIsAudioMuted(mute);if(mediaStream.current){mediaStream.current.getAudioTracks().forEach(function(audioTrack){return(audioTrack.enabled=!mute);});}};var pauseRecording=function(){if(mediaRecorder.current&&mediaRecorder.current.state==="recording"){mediaRecorder.current.pause();}};var resumeRecording=function(){if(mediaRecorder.current&&mediaRecorder.current.state==="paused"){mediaRecorder.current.resume();}};var stopRecording=function(){if(mediaRecorder.current){if(mediaRecorder.current.state!=="inactive"){setStatus("stopping");mediaRecorder.current.stop();mediaStream.current&&mediaStream.current.getTracks().forEach(function(track){return track.stop();});mediaChunks.current=[];}}};return render({error:RecorderErrors[error],muteAudio:function(){return muteAudio(true);},unMuteAudio:function(){return muteAudio(false);},startRecording:startRecording,pauseRecording:pauseRecording,resumeRecording:resumeRecording,stopRecording:stopRecording,mediaBlobUrl:mediaBlobUrl,status:status,isAudioMuted:isAudioMuted,previewStream:mediaStream.current?new MediaStream(mediaStream.current.getVideoTracks()):null,clearBlobUrl:function(){return setMediaBlobUrl(null);},});}; \ No newline at end of file From 6504506444c84186676fc1e666c57ec934ee1148 Mon Sep 17 00:00:00 2001 From: Ryan Plauche Date: Tue, 1 Sep 2020 10:15:01 -0500 Subject: [PATCH 3/6] Adding more broad customMediaStream prop --- lib/index.d.ts | 3 ++- lib/index.js | 4 ++-- src/index.ts | 6 +++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index 51b76cb..e750efc 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -23,7 +23,8 @@ declare type ReactMediaRecorderProps = { blobPropertyBag?: BlobPropertyBag; mediaRecorderOptions?: MediaRecorderOptions | null; deviceId?: string | null; + customMediaStream?: MediaStream | null; }; declare type StatusMessages = "media_aborted" | "permission_denied" | "no_specified_media_found" | "media_in_use" | "invalid_media_constraints" | "no_constraints" | "recorder_error" | "idle" | "acquiring_media" | "delayed_start" | "recording" | "stopping" | "stopped"; -export declare const ReactMediaRecorder: ({ render, audio, video, onStop, blobPropertyBag, screen, mediaRecorderOptions, deviceId, }: ReactMediaRecorderProps) => ReactElement ReactElement import("react").Component)> | null) | (new (props: any) => import("react").Component)>; +export declare const ReactMediaRecorder: ({ render, audio, video, onStop, blobPropertyBag, screen, mediaRecorderOptions, deviceId, customMediaStream, }: ReactMediaRecorderProps) => ReactElement ReactElement import("react").Component)> | null) | (new (props: any) => import("react").Component)>; export {}; diff --git a/lib/index.js b/lib/index.js index 0292618..ae18741 100644 --- a/lib/index.js +++ b/lib/index.js @@ -9,8 +9,8 @@ if(op[0]===6&&_.label0){console.error("The constraints "+unSupportedConstraints.join(",")+" doesn't support on this browser. Please check your ReactMediaRecorder component.");}};if(typeof audio==="object"){checkConstraints(audio);} if(typeof video==="object"){checkConstraints(video);} diff --git a/src/index.ts b/src/index.ts index 3ff2922..398af8b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,6 +24,7 @@ type ReactMediaRecorderProps = { blobPropertyBag?: BlobPropertyBag; mediaRecorderOptions?: MediaRecorderOptions | null; deviceId?: string | null; + customMediaStream?: MediaStream | null; }; type StatusMessages = @@ -61,6 +62,7 @@ export const ReactMediaRecorder = ({ screen = false, mediaRecorderOptions = null, deviceId = null, + customMediaStream = null, }: ReactMediaRecorderProps) => { const mediaRecorder = useRef(null); const mediaChunks = useRef([]); @@ -82,7 +84,9 @@ export const ReactMediaRecorder = ({ }; } try { - if (screen) { + if (customMediaStream) { + mediaStream.current = customMediaStream; + } else if (screen) { //@ts-ignore const stream = (await window.navigator.mediaDevices.getDisplayMedia({ video: video || true, From 0d1c3538f3ce07f6a7d5e15626b198f8bdaa651c Mon Sep 17 00:00:00 2001 From: Ryan Plauche Date: Wed, 2 Sep 2020 14:22:05 -0500 Subject: [PATCH 4/6] Added stopStreamsOnStop prop to prevent stopping underlying streams when recording stops --- lib/index.d.ts | 3 ++- lib/index.js | 5 +++-- src/index.ts | 8 ++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index e750efc..8925a7f 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -24,7 +24,8 @@ declare type ReactMediaRecorderProps = { mediaRecorderOptions?: MediaRecorderOptions | null; deviceId?: string | null; customMediaStream?: MediaStream | null; + stopStreamsOnStop?: boolean; }; declare type StatusMessages = "media_aborted" | "permission_denied" | "no_specified_media_found" | "media_in_use" | "invalid_media_constraints" | "no_constraints" | "recorder_error" | "idle" | "acquiring_media" | "delayed_start" | "recording" | "stopping" | "stopped"; -export declare const ReactMediaRecorder: ({ render, audio, video, onStop, blobPropertyBag, screen, mediaRecorderOptions, deviceId, customMediaStream, }: ReactMediaRecorderProps) => ReactElement ReactElement import("react").Component)> | null) | (new (props: any) => import("react").Component)>; +export declare const ReactMediaRecorder: ({ render, audio, video, onStop, blobPropertyBag, screen, mediaRecorderOptions, deviceId, customMediaStream, stopStreamsOnStop, }: ReactMediaRecorderProps) => ReactElement ReactElement import("react").Component)> | null) | (new (props: any) => import("react").Component)>; export {}; diff --git a/lib/index.js b/lib/index.js index ae18741..a00066c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -9,10 +9,11 @@ if(op[0]===6&&_.label0){console.error("The constraints "+unSupportedConstraints.join(",")+" doesn't support on this browser. Please check your ReactMediaRecorder component.");}};if(typeof audio==="object"){checkConstraints(audio);} if(typeof video==="object"){checkConstraints(video);} if(mediaRecorderOptions&&mediaRecorderOptions.mimeType){if(!MediaRecorder.isTypeSupported(mediaRecorderOptions.mimeType)){console.error("The specified MIME type you supplied for MediaRecorder doesn't support this browser");}} -if(!mediaStream.current){getMediaStream();}},[audio,screen,video,getMediaStream,mediaRecorderOptions]);var startRecording=function(){return __awaiter(void 0,void 0,void 0,function(){var isStreamEnded;return __generator(this,function(_a){switch(_a.label){case 0:setError("NONE");if(!!mediaStream.current)return[3,2];return[4,getMediaStream()];case 1:_a.sent();_a.label=2;case 2:if(!mediaStream.current)return[3,5];isStreamEnded=mediaStream.current.getTracks().some(function(track){return track.readyState==="ended";});if(!isStreamEnded)return[3,4];return[4,getMediaStream()];case 3:_a.sent();_a.label=4;case 4:mediaRecorder.current=new MediaRecorder(mediaStream.current);mediaRecorder.current.ondataavailable=onRecordingActive;mediaRecorder.current.onstop=onRecordingStop;mediaRecorder.current.onerror=function(){setError("NO_RECORDER");setStatus("idle");};mediaRecorder.current.start();setStatus("recording");_a.label=5;case 5:return[2];}});});};var onRecordingActive=function(_a){var data=_a.data;mediaChunks.current.push(data);};var onRecordingStop=function(){var chunk=mediaChunks.current[0];var blobProperty=Object.assign({type:chunk.type},blobPropertyBag||(video?{type:"video/mp4"}:{type:"audio/wav"}));var blob=new Blob(mediaChunks.current,blobProperty);var url=URL.createObjectURL(blob);setStatus("stopped");setMediaBlobUrl(url);onStop(url,blob);};var muteAudio=function(mute){setIsAudioMuted(mute);if(mediaStream.current){mediaStream.current.getAudioTracks().forEach(function(audioTrack){return(audioTrack.enabled=!mute);});}};var pauseRecording=function(){if(mediaRecorder.current&&mediaRecorder.current.state==="recording"){mediaRecorder.current.pause();}};var resumeRecording=function(){if(mediaRecorder.current&&mediaRecorder.current.state==="paused"){mediaRecorder.current.resume();}};var stopRecording=function(){if(mediaRecorder.current){if(mediaRecorder.current.state!=="inactive"){setStatus("stopping");mediaRecorder.current.stop();mediaStream.current&&mediaStream.current.getTracks().forEach(function(track){return track.stop();});mediaChunks.current=[];}}};return render({error:RecorderErrors[error],muteAudio:function(){return muteAudio(true);},unMuteAudio:function(){return muteAudio(false);},startRecording:startRecording,pauseRecording:pauseRecording,resumeRecording:resumeRecording,stopRecording:stopRecording,mediaBlobUrl:mediaBlobUrl,status:status,isAudioMuted:isAudioMuted,previewStream:mediaStream.current?new MediaStream(mediaStream.current.getVideoTracks()):null,clearBlobUrl:function(){return setMediaBlobUrl(null);},});}; \ No newline at end of file +if(!mediaStream.current){getMediaStream();}},[audio,screen,video,getMediaStream,mediaRecorderOptions]);var startRecording=function(){return __awaiter(void 0,void 0,void 0,function(){var isStreamEnded;return __generator(this,function(_a){switch(_a.label){case 0:setError("NONE");if(!!mediaStream.current)return[3,2];return[4,getMediaStream()];case 1:_a.sent();_a.label=2;case 2:if(!mediaStream.current)return[3,5];isStreamEnded=mediaStream.current.getTracks().some(function(track){return track.readyState==="ended";});if(!isStreamEnded)return[3,4];return[4,getMediaStream()];case 3:_a.sent();_a.label=4;case 4:mediaRecorder.current=new MediaRecorder(mediaStream.current);mediaRecorder.current.ondataavailable=onRecordingActive;mediaRecorder.current.onstop=onRecordingStop;mediaRecorder.current.onerror=function(){setError("NO_RECORDER");setStatus("idle");};mediaRecorder.current.start();setStatus("recording");_a.label=5;case 5:return[2];}});});};var onRecordingActive=function(_a){var data=_a.data;mediaChunks.current.push(data);};var onRecordingStop=function(){var chunk=mediaChunks.current[0];var blobProperty=Object.assign({type:chunk.type},blobPropertyBag||(video?{type:"video/mp4"}:{type:"audio/wav"}));var blob=new Blob(mediaChunks.current,blobProperty);var url=URL.createObjectURL(blob);setStatus("stopped");setMediaBlobUrl(url);onStop(url,blob);};var muteAudio=function(mute){setIsAudioMuted(mute);if(mediaStream.current){mediaStream.current.getAudioTracks().forEach(function(audioTrack){return(audioTrack.enabled=!mute);});}};var pauseRecording=function(){if(mediaRecorder.current&&mediaRecorder.current.state==="recording"){mediaRecorder.current.pause();}};var resumeRecording=function(){if(mediaRecorder.current&&mediaRecorder.current.state==="paused"){mediaRecorder.current.resume();}};var stopRecording=function(){if(mediaRecorder.current){if(mediaRecorder.current.state!=="inactive"){setStatus("stopping");mediaRecorder.current.stop();if(stopStreamsOnStop){mediaStream.current&&mediaStream.current.getTracks().forEach(function(track){return track.stop();});} +mediaChunks.current=[];}}};return render({error:RecorderErrors[error],muteAudio:function(){return muteAudio(true);},unMuteAudio:function(){return muteAudio(false);},startRecording:startRecording,pauseRecording:pauseRecording,resumeRecording:resumeRecording,stopRecording:stopRecording,mediaBlobUrl:mediaBlobUrl,status:status,isAudioMuted:isAudioMuted,previewStream:mediaStream.current?new MediaStream(mediaStream.current.getVideoTracks()):null,clearBlobUrl:function(){return setMediaBlobUrl(null);},});}; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 398af8b..ef52867 100644 --- a/src/index.ts +++ b/src/index.ts @@ -25,6 +25,7 @@ type ReactMediaRecorderProps = { mediaRecorderOptions?: MediaRecorderOptions | null; deviceId?: string | null; customMediaStream?: MediaStream | null; + stopStreamsOnStop?: boolean; }; type StatusMessages = @@ -63,6 +64,7 @@ export const ReactMediaRecorder = ({ mediaRecorderOptions = null, deviceId = null, customMediaStream = null, + stopStreamsOnStop = true, }: ReactMediaRecorderProps) => { const mediaRecorder = useRef(null); const mediaChunks = useRef([]); @@ -230,8 +232,10 @@ export const ReactMediaRecorder = ({ if (mediaRecorder.current.state !== "inactive") { setStatus("stopping"); mediaRecorder.current.stop(); - mediaStream.current && - mediaStream.current.getTracks().forEach((track) => track.stop()); + if (stopStreamsOnStop) { + mediaStream.current && + mediaStream.current.getTracks().forEach((track) => track.stop()); + } mediaChunks.current = []; } } From c4d13d56d37a0eded0255cf1033415546f3eab15 Mon Sep 17 00:00:00 2001 From: Ryan Plauche Date: Fri, 4 Sep 2020 16:29:17 -0500 Subject: [PATCH 5/6] Added onStart hook --- lib/index.d.ts | 3 ++- lib/index.js | 4 ++-- src/index.ts | 7 +++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index 8925a7f..b6676b0 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -20,6 +20,7 @@ declare type ReactMediaRecorderProps = { video?: boolean | MediaTrackConstraints; screen?: boolean; onStop?: (blobUrl: string, blob: Blob) => void; + onStart?: () => void; blobPropertyBag?: BlobPropertyBag; mediaRecorderOptions?: MediaRecorderOptions | null; deviceId?: string | null; @@ -27,5 +28,5 @@ declare type ReactMediaRecorderProps = { stopStreamsOnStop?: boolean; }; declare type StatusMessages = "media_aborted" | "permission_denied" | "no_specified_media_found" | "media_in_use" | "invalid_media_constraints" | "no_constraints" | "recorder_error" | "idle" | "acquiring_media" | "delayed_start" | "recording" | "stopping" | "stopped"; -export declare const ReactMediaRecorder: ({ render, audio, video, onStop, blobPropertyBag, screen, mediaRecorderOptions, deviceId, customMediaStream, stopStreamsOnStop, }: ReactMediaRecorderProps) => ReactElement ReactElement import("react").Component)> | null) | (new (props: any) => import("react").Component)>; +export declare const ReactMediaRecorder: ({ render, audio, video, onStop, onStart, blobPropertyBag, screen, mediaRecorderOptions, deviceId, customMediaStream, stopStreamsOnStop, }: ReactMediaRecorderProps) => ReactElement ReactElement import("react").Component)> | null) | (new (props: any) => import("react").Component)>; export {}; diff --git a/lib/index.js b/lib/index.js index a00066c..042aba3 100644 --- a/lib/index.js +++ b/lib/index.js @@ -9,11 +9,11 @@ if(op[0]===6&&_.label0){console.error("The constraints "+unSupportedConstraints.join(",")+" doesn't support on this browser. Please check your ReactMediaRecorder component.");}};if(typeof audio==="object"){checkConstraints(audio);} if(typeof video==="object"){checkConstraints(video);} if(mediaRecorderOptions&&mediaRecorderOptions.mimeType){if(!MediaRecorder.isTypeSupported(mediaRecorderOptions.mimeType)){console.error("The specified MIME type you supplied for MediaRecorder doesn't support this browser");}} -if(!mediaStream.current){getMediaStream();}},[audio,screen,video,getMediaStream,mediaRecorderOptions]);var startRecording=function(){return __awaiter(void 0,void 0,void 0,function(){var isStreamEnded;return __generator(this,function(_a){switch(_a.label){case 0:setError("NONE");if(!!mediaStream.current)return[3,2];return[4,getMediaStream()];case 1:_a.sent();_a.label=2;case 2:if(!mediaStream.current)return[3,5];isStreamEnded=mediaStream.current.getTracks().some(function(track){return track.readyState==="ended";});if(!isStreamEnded)return[3,4];return[4,getMediaStream()];case 3:_a.sent();_a.label=4;case 4:mediaRecorder.current=new MediaRecorder(mediaStream.current);mediaRecorder.current.ondataavailable=onRecordingActive;mediaRecorder.current.onstop=onRecordingStop;mediaRecorder.current.onerror=function(){setError("NO_RECORDER");setStatus("idle");};mediaRecorder.current.start();setStatus("recording");_a.label=5;case 5:return[2];}});});};var onRecordingActive=function(_a){var data=_a.data;mediaChunks.current.push(data);};var onRecordingStop=function(){var chunk=mediaChunks.current[0];var blobProperty=Object.assign({type:chunk.type},blobPropertyBag||(video?{type:"video/mp4"}:{type:"audio/wav"}));var blob=new Blob(mediaChunks.current,blobProperty);var url=URL.createObjectURL(blob);setStatus("stopped");setMediaBlobUrl(url);onStop(url,blob);};var muteAudio=function(mute){setIsAudioMuted(mute);if(mediaStream.current){mediaStream.current.getAudioTracks().forEach(function(audioTrack){return(audioTrack.enabled=!mute);});}};var pauseRecording=function(){if(mediaRecorder.current&&mediaRecorder.current.state==="recording"){mediaRecorder.current.pause();}};var resumeRecording=function(){if(mediaRecorder.current&&mediaRecorder.current.state==="paused"){mediaRecorder.current.resume();}};var stopRecording=function(){if(mediaRecorder.current){if(mediaRecorder.current.state!=="inactive"){setStatus("stopping");mediaRecorder.current.stop();if(stopStreamsOnStop){mediaStream.current&&mediaStream.current.getTracks().forEach(function(track){return track.stop();});} +if(!mediaStream.current){getMediaStream();}},[audio,screen,video,getMediaStream,mediaRecorderOptions]);var startRecording=function(){return __awaiter(void 0,void 0,void 0,function(){var isStreamEnded;return __generator(this,function(_a){switch(_a.label){case 0:setError("NONE");if(!!mediaStream.current)return[3,2];return[4,getMediaStream()];case 1:_a.sent();_a.label=2;case 2:if(!mediaStream.current)return[3,5];isStreamEnded=mediaStream.current.getTracks().some(function(track){return track.readyState==="ended";});if(!isStreamEnded)return[3,4];return[4,getMediaStream()];case 3:_a.sent();_a.label=4;case 4:mediaRecorder.current=new MediaRecorder(mediaStream.current);mediaRecorder.current.ondataavailable=onRecordingActive;mediaRecorder.current.onstop=onRecordingStop;mediaRecorder.current.onstart=onRecordingStart;mediaRecorder.current.onerror=function(){setError("NO_RECORDER");setStatus("idle");};mediaRecorder.current.start();setStatus("recording");_a.label=5;case 5:return[2];}});});};var onRecordingActive=function(_a){var data=_a.data;mediaChunks.current.push(data);};var onRecordingStart=function(){onStart();};var onRecordingStop=function(){var chunk=mediaChunks.current[0];var blobProperty=Object.assign({type:chunk.type},blobPropertyBag||(video?{type:"video/mp4"}:{type:"audio/wav"}));var blob=new Blob(mediaChunks.current,blobProperty);var url=URL.createObjectURL(blob);setStatus("stopped");setMediaBlobUrl(url);onStop(url,blob);};var muteAudio=function(mute){setIsAudioMuted(mute);if(mediaStream.current){mediaStream.current.getAudioTracks().forEach(function(audioTrack){return(audioTrack.enabled=!mute);});}};var pauseRecording=function(){if(mediaRecorder.current&&mediaRecorder.current.state==="recording"){mediaRecorder.current.pause();}};var resumeRecording=function(){if(mediaRecorder.current&&mediaRecorder.current.state==="paused"){mediaRecorder.current.resume();}};var stopRecording=function(){if(mediaRecorder.current){if(mediaRecorder.current.state!=="inactive"){setStatus("stopping");mediaRecorder.current.stop();if(stopStreamsOnStop){mediaStream.current&&mediaStream.current.getTracks().forEach(function(track){return track.stop();});} mediaChunks.current=[];}}};return render({error:RecorderErrors[error],muteAudio:function(){return muteAudio(true);},unMuteAudio:function(){return muteAudio(false);},startRecording:startRecording,pauseRecording:pauseRecording,resumeRecording:resumeRecording,stopRecording:stopRecording,mediaBlobUrl:mediaBlobUrl,status:status,isAudioMuted:isAudioMuted,previewStream:mediaStream.current?new MediaStream(mediaStream.current.getVideoTracks()):null,clearBlobUrl:function(){return setMediaBlobUrl(null);},});}; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index ef52867..ea226e4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,6 +21,7 @@ type ReactMediaRecorderProps = { video?: boolean | MediaTrackConstraints; screen?: boolean; onStop?: (blobUrl: string, blob: Blob) => void; + onStart?: () => void; blobPropertyBag?: BlobPropertyBag; mediaRecorderOptions?: MediaRecorderOptions | null; deviceId?: string | null; @@ -59,6 +60,7 @@ export const ReactMediaRecorder = ({ audio = true, video = false, onStop = () => null, + onStart = () => null, blobPropertyBag, screen = false, mediaRecorderOptions = null, @@ -181,6 +183,7 @@ export const ReactMediaRecorder = ({ mediaRecorder.current = new MediaRecorder(mediaStream.current); mediaRecorder.current.ondataavailable = onRecordingActive; mediaRecorder.current.onstop = onRecordingStop; + mediaRecorder.current.onstart = onRecordingStart; mediaRecorder.current.onerror = () => { setError("NO_RECORDER"); setStatus("idle"); @@ -194,6 +197,10 @@ export const ReactMediaRecorder = ({ mediaChunks.current.push(data); }; + const onRecordingStart = () => { + onStart(); + }; + const onRecordingStop = () => { const [chunk] = mediaChunks.current; const blobProperty: BlobPropertyBag = Object.assign( From e1b6284d157b47edd470ef282943c2d265013add Mon Sep 17 00:00:00 2001 From: Ryan Plauche Date: Tue, 8 Sep 2020 15:02:41 -0500 Subject: [PATCH 6/6] Removing deviceId prop in favor of customMediaStream --- lib/index.d.ts | 3 +-- lib/index.js | 3 +-- src/index.ts | 9 +-------- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index b6676b0..b68e920 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -23,10 +23,9 @@ declare type ReactMediaRecorderProps = { onStart?: () => void; blobPropertyBag?: BlobPropertyBag; mediaRecorderOptions?: MediaRecorderOptions | null; - deviceId?: string | null; customMediaStream?: MediaStream | null; stopStreamsOnStop?: boolean; }; declare type StatusMessages = "media_aborted" | "permission_denied" | "no_specified_media_found" | "media_in_use" | "invalid_media_constraints" | "no_constraints" | "recorder_error" | "idle" | "acquiring_media" | "delayed_start" | "recording" | "stopping" | "stopped"; -export declare const ReactMediaRecorder: ({ render, audio, video, onStop, onStart, blobPropertyBag, screen, mediaRecorderOptions, deviceId, customMediaStream, stopStreamsOnStop, }: ReactMediaRecorderProps) => ReactElement ReactElement import("react").Component)> | null) | (new (props: any) => import("react").Component)>; +export declare const ReactMediaRecorder: ({ render, audio, video, onStop, onStart, blobPropertyBag, screen, mediaRecorderOptions, customMediaStream, stopStreamsOnStop, }: ReactMediaRecorderProps) => ReactElement ReactElement import("react").Component)> | null) | (new (props: any) => import("react").Component)>; export {}; diff --git a/lib/index.js b/lib/index.js index 042aba3..c4563dd 100644 --- a/lib/index.js +++ b/lib/index.js @@ -9,8 +9,7 @@ if(op[0]===6&&_.label0){console.error("The constraints "+unSupportedConstraints.join(",")+" doesn't support on this browser. Please check your ReactMediaRecorder component.");}};if(typeof audio==="object"){checkConstraints(audio);} if(typeof video==="object"){checkConstraints(video);} diff --git a/src/index.ts b/src/index.ts index ea226e4..f75daf0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,7 +24,6 @@ type ReactMediaRecorderProps = { onStart?: () => void; blobPropertyBag?: BlobPropertyBag; mediaRecorderOptions?: MediaRecorderOptions | null; - deviceId?: string | null; customMediaStream?: MediaStream | null; stopStreamsOnStop?: boolean; }; @@ -64,7 +63,6 @@ export const ReactMediaRecorder = ({ blobPropertyBag, screen = false, mediaRecorderOptions = null, - deviceId = null, customMediaStream = null, stopStreamsOnStop = true, }: ReactMediaRecorderProps) => { @@ -78,15 +76,10 @@ export const ReactMediaRecorder = ({ const getMediaStream = useCallback(async () => { setStatus("acquiring_media"); - let requiredMedia: MediaStreamConstraints = { + const requiredMedia: MediaStreamConstraints = { audio: typeof audio === "boolean" ? !!audio : audio, video: typeof video === "boolean" ? !!video : video, }; - if (deviceId) { - requiredMedia.video = { - deviceId: deviceId, - }; - } try { if (customMediaStream) { mediaStream.current = customMediaStream;