From 16d259ccc4c33a54241be9c3eb28bdbba4dbddbd Mon Sep 17 00:00:00 2001
From: Kimberlee Johnson
Date: Mon, 22 Nov 2021 16:57:27 -0800
Subject: [PATCH 1/5] First pass at more recording types, updated README
---
custom/recording/README.md | 6 ++--
custom/recording/components/RecordingModal.js | 24 ++++++++++++++-
.../recording/contexts/RecordingProvider.js | 29 +++++++++++++++++--
custom/shared/contexts/CallProvider.js | 27 +++++++++--------
4 files changed, 68 insertions(+), 18 deletions(-)
diff --git a/custom/recording/README.md b/custom/recording/README.md
index 523abe6..ebabce6 100644
--- a/custom/recording/README.md
+++ b/custom/recording/README.md
@@ -6,14 +6,14 @@
## What does this demo do?
-- Use [startRecording](https://docs.daily.co/reference#%EF%B8%8F-startrecording) to create a video and audio recording of your call. You can read more about Daily call recording (and the different modes and types) [here](https://docs.daily.co/reference#recordings)
-- Supports both `cloud` and `local` recording modes (specified when creating the room or managed using the Daily dashboard)
+- Use [startRecording](https://docs.daily.co/reference#%EF%B8%8F-startrecording) to create a video and audio recording of your call. You can read more about Daily call recording (and the different modes and types) [here](https://docs.daily.co/guides/recording-calls-with-the-daily-api)
+- Supports `cloud`, `cloud-beta`, `local`, and `rtp-tracks` recording modes (specified when creating the room or managed using the Daily dashboard). _Heads up: if recording on mobile devices or Safari, then `cloud-beta` must be the specified mode._
- Coming soon: support different recording layouts / composites
- Coming soon: use the Daily REST API to retrieve a list of cloud recordings for the currently active room
**To turn on recording, you need to be on the Scale plan. There is also a per minute recording fee for cloud recording.**
-Please note: this demo is not currently mobile optimised
+Please note: this demo is not currently mobile optimised.
### Getting started
diff --git a/custom/recording/components/RecordingModal.js b/custom/recording/components/RecordingModal.js
index e89efa4..1725f4d 100644
--- a/custom/recording/components/RecordingModal.js
+++ b/custom/recording/components/RecordingModal.js
@@ -13,6 +13,8 @@ import {
RECORDING_RECORDING,
RECORDING_SAVED,
RECORDING_TYPE_CLOUD,
+ RECORDING_TYPE_CLOUD_BETA,
+ RECORDING_TYPE_RTP_TRACKS,
RECORDING_UPLOADING,
useRecording,
} from '../contexts/RecordingProvider';
@@ -105,7 +107,9 @@ export const RecordingModal = () => {
Recording started: {recordingStartedDate.toString()}
)}
- {enableRecording === RECORDING_TYPE_CLOUD && (
+ {[RECORDING_TYPE_CLOUD, RECORDING_TYPE_CLOUD_BETA].includes(
+ enableRecording
+ ) && (
<>
@@ -115,6 +119,24 @@ export const RecordingModal = () => {
>
)}
+ {enableRecording === RECORDING_TYPE_RTP_TRACKS && (
+ <>
+
+
+
+ rtp-tracks recordings can be accessed via the Daily API. See the{' '}
+
+ Daily recording guide
+ {' '}
+ for details.
+
+ >
+ )}
);
diff --git a/custom/recording/contexts/RecordingProvider.js b/custom/recording/contexts/RecordingProvider.js
index 326bb37..a56a0e3 100644
--- a/custom/recording/contexts/RecordingProvider.js
+++ b/custom/recording/contexts/RecordingProvider.js
@@ -26,7 +26,10 @@ export const RECORDING_COUNTDOWN_3 = 'starting3';
export const RECORDING_IDLE = 'idle';
export const RECORDING_TYPE_CLOUD = 'cloud';
+export const RECORDING_TYPE_CLOUD_BETA = 'cloud-beta';
export const RECORDING_TYPE_LOCAL = 'local';
+export const RECORDING_TYPE_OUTPUT_BYTE_STREAM = 'output-byte-stream';
+export const RECORDING_TYPE_RTP_TRACKS = 'rtp-tracks';
const RecordingContext = createContext({
isRecordingLocally: false,
@@ -37,8 +40,12 @@ const RecordingContext = createContext({
});
export const RecordingProvider = ({ children }) => {
- const { callObject, enableRecording, startCloudRecording, state } =
- useCallState();
+ const {
+ callObject,
+ enableRecording,
+ startCloudRecording,
+ state,
+ } = useCallState();
const { participants } = useParticipants();
const [recordingStartedDate, setRecordingStartedDate] = useState(null);
const [recordingState, setRecordingState] = useState(RECORDING_IDLE);
@@ -85,11 +92,26 @@ export const RecordingProvider = ({ children }) => {
}
};
+ // The 'recording-data' event is emitted when an output-byte-stream recording has started
+ const handleRecordingData = async (ev) => {
+ try {
+ console.log('got data', ev);
+ await window.writer.write(ev.data);
+ if (ev.finished) {
+ console.log('closing!');
+ window.writer.close();
+ }
+ } catch (e) {
+ console.error(e);
+ }
+ };
+
const handleRecordingUploadCompleted = () => {
setRecordingState(RECORDING_SAVED);
};
callObject.on('app-message', handleAppMessage);
+ callObject.on('recording-data', handleRecordingData);
callObject.on('recording-upload-completed', handleRecordingUploadCompleted);
return () => {
@@ -263,10 +285,13 @@ export const RecordingProvider = ({ children }) => {
if (recordingState === RECORDING_RECORDING) {
switch (enableRecording) {
case RECORDING_TYPE_LOCAL:
+ case RECORDING_TYPE_OUTPUT_BYTE_STREAM:
setRecordingState(RECORDING_SAVED);
setIsRecordingLocally(false);
break;
case RECORDING_TYPE_CLOUD:
+ case RECORDING_TYPE_CLOUD_BETA:
+ case RECORDING_TYPE_RTP_TRACKS:
setRecordingState(RECORDING_UPLOADING);
break;
default:
diff --git a/custom/shared/contexts/CallProvider.js b/custom/shared/contexts/CallProvider.js
index eb133c8..62b6919 100644
--- a/custom/shared/contexts/CallProvider.js
+++ b/custom/shared/contexts/CallProvider.js
@@ -3,7 +3,7 @@
* ---
* Configures the general state of a Daily call, such as which features
* to enable, as well as instantiate the 'call machine' hook responsible
- * fir the overaching call loop (joining, leaving, etc)
+ * for the overaching call loop (joining, leaving, etc)
*/
import React, {
createContext,
@@ -60,19 +60,22 @@ export const CallProvider = ({
);
}
const browser = Bowser.parse(window.navigator.userAgent);
+ const recordingType =
+ roomConfig?.tokenConfig?.enable_recording ??
+ roomConfig?.config?.enable_recording;
+
+ // Mobile and Safari recordings are only supported under the 'cloud-beta' type
const supportsRecording =
- browser.platform.type === 'desktop' && browser.engine.name === 'Blink';
- // recording and screen sharing is hidden in owner_only_broadcast for non-owners
+ ((browser.platform.type !== 'desktop' ||
+ browser.engine.name !== 'Blink') &&
+ recordingType === 'cloud-beta') ||
+ (browser.platform.type === 'desktop' &&
+ browser.engine.name === 'Blink');
if (supportsRecording) {
- const recordingType =
- roomConfig?.tokenConfig?.enable_recording ??
- roomConfig?.config?.enable_recording;
- if (['local', 'cloud'].includes(recordingType)) {
- setEnableRecording(recordingType);
- setStartCloudRecording(
- roomConfig?.tokenConfig?.start_cloud_recording ?? false
- );
- }
+ setEnableRecording(recordingType);
+ setStartCloudRecording(
+ roomConfig?.tokenConfig?.start_cloud_recording ?? false
+ );
}
};
updateRoomConfigState();
From d270fd3d34bd5a7bf02302fba1e92a4be000f8c9 Mon Sep 17 00:00:00 2001
From: Kimberlee Johnson
Date: Mon, 29 Nov 2021 16:01:00 -0800
Subject: [PATCH 2/5] Updated handleRecordingStarted and fixed typo
---
custom/recording/components/RecordingModal.js | 2 +-
custom/recording/contexts/RecordingProvider.js | 14 +++++++++++++-
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/custom/recording/components/RecordingModal.js b/custom/recording/components/RecordingModal.js
index 1725f4d..11ae3fa 100644
--- a/custom/recording/components/RecordingModal.js
+++ b/custom/recording/components/RecordingModal.js
@@ -94,7 +94,7 @@ export const RecordingModal = () => {
{!enableRecording ? (
Recording is not enabled for this room (or your browser does not
- support it.) Please enabled recording when creating the room or via
+ support it.) Please enable recording when creating the room or via
the Daily dashboard.
) : (
diff --git a/custom/recording/contexts/RecordingProvider.js b/custom/recording/contexts/RecordingProvider.js
index a56a0e3..c34f56c 100644
--- a/custom/recording/contexts/RecordingProvider.js
+++ b/custom/recording/contexts/RecordingProvider.js
@@ -177,13 +177,25 @@ export const RecordingProvider = ({ children }) => {
*/
const handleRecordingStarted = useCallback(
(event) => {
+ console.log('RECORDING');
+ console.log(event);
+
if (recordingState === RECORDING_RECORDING) return;
+ setRecordingState(RECORDING_RECORDING);
if (event.local) {
// Recording started locally, either through UI or programmatically
setIsRecordingLocally(true);
if (!recordingStartedDate) setRecordingStartedDate(new Date());
+ if (event.type === 'output-byte-stream') {
+ const { readable, writable } = new TransformStream({
+ transform: (chunk, ctrl) => {
+ chunk.arrayBuffer().then((b) => ctrl.enqueue(new Uint8Array(b)));
+ },
+ });
+ window.writer = writable.getWriter();
+ readable.pipeTo(window.streamSaver.createWriteStream('test-vid.mp4'));
+ }
}
- setRecordingState(RECORDING_RECORDING);
},
[recordingState, recordingStartedDate]
);
From e03dcdaaef9894e3ddd3ad3a16cbeaa80e8ae906 Mon Sep 17 00:00:00 2001
From: Kimberlee Johnson
Date: Mon, 29 Nov 2021 16:21:51 -0800
Subject: [PATCH 3/5] Removed recording-upload-complete event handler in favor
of recording-stopped
---
custom/recording/contexts/RecordingProvider.js | 13 +++----------
1 file changed, 3 insertions(+), 10 deletions(-)
diff --git a/custom/recording/contexts/RecordingProvider.js b/custom/recording/contexts/RecordingProvider.js
index c34f56c..620568e 100644
--- a/custom/recording/contexts/RecordingProvider.js
+++ b/custom/recording/contexts/RecordingProvider.js
@@ -106,20 +106,11 @@ export const RecordingProvider = ({ children }) => {
}
};
- const handleRecordingUploadCompleted = () => {
- setRecordingState(RECORDING_SAVED);
- };
-
callObject.on('app-message', handleAppMessage);
callObject.on('recording-data', handleRecordingData);
- callObject.on('recording-upload-completed', handleRecordingUploadCompleted);
return () => {
callObject.off('app-message', handleAppMessage);
- callObject.off(
- 'recording-upload-completed',
- handleRecordingUploadCompleted
- );
};
}, [callObject, enableRecording]);
@@ -213,7 +204,8 @@ export const RecordingProvider = ({ children }) => {
useEffect(() => {
if (!callObject || !enableRecording) return false;
- const handleRecordingStopped = () => {
+ const handleRecordingStopped = (event) => {
+ console.log(event);
if (isRecordingLocally) return;
setRecordingState(RECORDING_IDLE);
setRecordingStartedDate(null);
@@ -305,6 +297,7 @@ export const RecordingProvider = ({ children }) => {
case RECORDING_TYPE_CLOUD_BETA:
case RECORDING_TYPE_RTP_TRACKS:
setRecordingState(RECORDING_UPLOADING);
+ setRecordingState(RECORDING_SAVED);
break;
default:
break;
From 4e17e71fa9abacce93e09a34517ae256a3d51d53 Mon Sep 17 00:00:00 2001
From: Kimberlee Johnson
Date: Tue, 30 Nov 2021 16:49:52 -0800
Subject: [PATCH 4/5] Added cleanup for handleRecordingData
---
custom/recording/contexts/RecordingProvider.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/custom/recording/contexts/RecordingProvider.js b/custom/recording/contexts/RecordingProvider.js
index 620568e..d7ad1f5 100644
--- a/custom/recording/contexts/RecordingProvider.js
+++ b/custom/recording/contexts/RecordingProvider.js
@@ -111,6 +111,7 @@ export const RecordingProvider = ({ children }) => {
return () => {
callObject.off('app-message', handleAppMessage);
+ callObject.off('recording-data', handleRecordingData);
};
}, [callObject, enableRecording]);
From d03b9ad6314b59b33e0ccad6adb38022821ea194 Mon Sep 17 00:00:00 2001
From: Kimberlee Johnson
Date: Wed, 1 Dec 2021 12:25:22 -0800
Subject: [PATCH 5/5] Added comments to clarify output-byte-stream
---
custom/recording/contexts/RecordingProvider.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/custom/recording/contexts/RecordingProvider.js b/custom/recording/contexts/RecordingProvider.js
index d7ad1f5..c63d0b8 100644
--- a/custom/recording/contexts/RecordingProvider.js
+++ b/custom/recording/contexts/RecordingProvider.js
@@ -93,6 +93,7 @@ export const RecordingProvider = ({ children }) => {
};
// The 'recording-data' event is emitted when an output-byte-stream recording has started
+ // When the event emits, start writing data to the stream created in handleRecordingStarted()
const handleRecordingData = async (ev) => {
try {
console.log('got data', ev);
@@ -178,6 +179,7 @@ export const RecordingProvider = ({ children }) => {
// Recording started locally, either through UI or programmatically
setIsRecordingLocally(true);
if (!recordingStartedDate) setRecordingStartedDate(new Date());
+ // If an output-byte-stream recording has started, create a new data stream that can be piped to a third-party (in this case a file)
if (event.type === 'output-byte-stream') {
const { readable, writable } = new TransformStream({
transform: (chunk, ctrl) => {