From c6c8a3522a6c15bb30fdd23f7016171cb5e787db Mon Sep 17 00:00:00 2001 From: PipesNBottles <32112030+PipesNBottles@users.noreply.github.com> Date: Fri, 13 May 2022 21:49:17 -0700 Subject: [PATCH] feature(adds proper WAV codec support): (#97) * feature(adds proper WAV codec support): - uses the extendable media recorder to allow use of WAV and author mime types - fixes type issues where audio and video tags required undefined rather than null - fixes issue where if mediaRecorderOptions are supplied the recorder stops recording after first audio blob recording * linter issues * removes yarn lock and updates version number --- package.json | 8 ++++++-- src/index.ts | 55 ++++++++++++++++++++++++++++++---------------------- yarn.lock | 41 --------------------------------------- 3 files changed, 38 insertions(+), 66 deletions(-) delete mode 100644 yarn.lock diff --git a/package.json b/package.json index 6170cc9..73bdde3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-media-recorder", - "version": "1.6.5", + "version": "1.6.6", "description": "A React component based on MediaRecorder() API to record audio/video streams", "main": "index.js", "scripts": { @@ -38,5 +38,9 @@ "jsmin": "^1.0.1", "typescript": "^4.4.3" }, - "types": "./lib/index.d.ts" + "types": "./lib/index.d.ts", + "dependencies": { + "extendable-media-recorder": "^6.6.5", + "extendable-media-recorder-wav-encoder": "^7.0.68" + } } diff --git a/src/index.ts b/src/index.ts index 0d5d878..8aeed95 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,6 @@ +import { register, MediaRecorder as ExtendableMediaRecorder, IMediaRecorder } from "extendable-media-recorder"; import { ReactElement, useCallback, useEffect, useRef, useState } from "react"; +import { connect } from 'extendable-media-recorder-wav-encoder'; export type ReactMediaRecorderRenderProps = { error: string; @@ -8,7 +10,7 @@ export type ReactMediaRecorderRenderProps = { pauseRecording: () => void; resumeRecording: () => void; stopRecording: () => void; - mediaBlobUrl: null | string; + mediaBlobUrl: undefined | string; status: StatusMessages; isAudioMuted: boolean; previewStream: MediaStream | null; @@ -23,7 +25,7 @@ export type ReactMediaRecorderHookProps = { onStop?: (blobUrl: string, blob: Blob) => void; onStart?: () => void; blobPropertyBag?: BlobPropertyBag; - mediaRecorderOptions?: MediaRecorderOptions | null; + mediaRecorderOptions?: MediaRecorderOptions | undefined; customMediaStream?: MediaStream | null; stopStreamsOnStop?: boolean; askPermissionOnMount?: boolean; @@ -66,19 +68,26 @@ export function useReactMediaRecorder({ onStart = () => null, blobPropertyBag, screen = false, - mediaRecorderOptions = null, + mediaRecorderOptions = undefined, customMediaStream = null, stopStreamsOnStop = true, askPermissionOnMount = false, }: ReactMediaRecorderHookProps): ReactMediaRecorderRenderProps { - const mediaRecorder = useRef(null); + const mediaRecorder = useRef(null); const mediaChunks = useRef([]); const mediaStream = useRef(null); const [status, setStatus] = useState("idle"); const [isAudioMuted, setIsAudioMuted] = useState(false); - const [mediaBlobUrl, setMediaBlobUrl] = useState(null); + const [mediaBlobUrl, setMediaBlobUrl] = useState(undefined); const [error, setError] = useState("NONE"); + useEffect(() => { + const setup = async () => { + await register(await connect()); + }; + setup(); + }, []); + const getMediaStream = useCallback(async () => { setStatus("acquiring_media"); const requiredMedia: MediaStreamConstraints = { @@ -89,7 +98,6 @@ export function useReactMediaRecorder({ if (customMediaStream) { mediaStream.current = customMediaStream; } else if (screen) { - //@ts-ignore const stream = (await window.navigator.mediaDevices.getDisplayMedia({ video: video || true, })) as MediaStream; @@ -108,7 +116,7 @@ export function useReactMediaRecorder({ mediaStream.current = stream; } else { const stream = await window.navigator.mediaDevices.getUserMedia( - requiredMedia + requiredMedia, ); mediaStream.current = stream; } @@ -125,9 +133,8 @@ export function useReactMediaRecorder({ } if (screen) { - //@ts-ignore if (!window.navigator.mediaDevices.getDisplayMedia) { - throw new Error("This browser doesn't support screen capturing"); + throw new Error("This browser doesn\'t support screen capturing"); } } @@ -136,14 +143,14 @@ export function useReactMediaRecorder({ navigator.mediaDevices.getSupportedConstraints(); const unSupportedConstraints = Object.keys(mediaType).filter( (constraint) => - !(supportedMediaConstraints as { [key: string]: any })[constraint] + !(supportedMediaConstraints as { [key: string]: any })[constraint], ); if (unSupportedConstraints.length > 0) { console.error( `The constraints ${unSupportedConstraints.join( - "," - )} doesn't support on this browser. Please check your ReactMediaRecorder component.` + ",", + )} doesn't support on this browser. Please check your ReactMediaRecorder component.`, ); } }; @@ -158,7 +165,7 @@ export function useReactMediaRecorder({ if (mediaRecorderOptions && mediaRecorderOptions.mimeType) { if (!MediaRecorder.isTypeSupported(mediaRecorderOptions.mimeType)) { console.error( - `The specified MIME type you supplied for MediaRecorder doesn't support this browser` + `The specified MIME type you supplied for MediaRecorder doesn't support this browser`, ); } } @@ -170,7 +177,7 @@ export function useReactMediaRecorder({ return () => { if (mediaStream.current) { const tracks = mediaStream.current.getTracks(); - tracks.forEach((track) => track.stop()); + tracks.forEach((track) => track.clone().stop()); } }; }, [ @@ -201,7 +208,9 @@ export function useReactMediaRecorder({ if (!mediaStream.current.active) { return; } - mediaRecorder.current = new MediaRecorder(mediaStream.current, mediaRecorderOptions || undefined); + mediaRecorder.current = new ExtendableMediaRecorder( + mediaStream.current, mediaRecorderOptions || undefined, + ); mediaRecorder.current.ondataavailable = onRecordingActive; mediaRecorder.current.onstop = onRecordingStop; mediaRecorder.current.onstart = onRecordingStart; @@ -226,7 +235,7 @@ export function useReactMediaRecorder({ const [chunk] = mediaChunks.current; const blobProperty: BlobPropertyBag = Object.assign( { type: chunk.type }, - blobPropertyBag || (video ? { type: "video/mp4" } : { type: "audio/wav" }) + blobPropertyBag || (video ? { type: "video/mp4" } : { type: "audio/wav" }), ); const blob = new Blob(mediaChunks.current, blobProperty); const url = URL.createObjectURL(blob); @@ -282,17 +291,17 @@ export function useReactMediaRecorder({ mediaBlobUrl, status, isAudioMuted, - previewStream: mediaStream.current - ? new MediaStream(mediaStream.current.getVideoTracks()) - : null, - previewAudioStream: mediaStream.current - ? new MediaStream(mediaStream.current.getAudioTracks()) - : null, + previewStream: mediaStream.current ? + new MediaStream(mediaStream.current.getVideoTracks()) : + null, + previewAudioStream: mediaStream.current ? + new MediaStream(mediaStream.current.getAudioTracks()) : + null, clearBlobUrl: () => { if (mediaBlobUrl) { URL.revokeObjectURL(mediaBlobUrl); } - setMediaBlobUrl(null); + setMediaBlobUrl(undefined); setStatus("idle"); }, }; diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index 757d754..0000000 --- a/yarn.lock +++ /dev/null @@ -1,41 +0,0 @@ -# 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==