Merge pull request #48 from daily-demos/dev-1074-update-recording-examples-demo-with-new

DEV-1074: Additional recording type support
This commit is contained in:
Kimberlee Johnson 2021-12-01 12:30:17 -08:00 committed by GitHub
commit f735bcf250
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 86 additions and 28 deletions

View File

@ -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

View File

@ -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';
@ -92,7 +94,7 @@ export const RecordingModal = () => {
{!enableRecording ? (
<Well variant="error">
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.
</Well>
) : (
@ -105,7 +107,9 @@ export const RecordingModal = () => {
<p>Recording started: {recordingStartedDate.toString()}</p>
)}
{enableRecording === RECORDING_TYPE_CLOUD && (
{[RECORDING_TYPE_CLOUD, RECORDING_TYPE_CLOUD_BETA].includes(
enableRecording
) && (
<>
<hr />
@ -115,6 +119,24 @@ export const RecordingModal = () => {
</p>
</>
)}
{enableRecording === RECORDING_TYPE_RTP_TRACKS && (
<>
<hr />
<p>
rtp-tracks recordings can be accessed via the Daily API. See the{' '}
<a
href="https://docs.daily.co/guides/recording-calls-with-the-daily-api#retrieve-individual-tracks-from-rtp-tracks-recordings"
noreferrer
target="_blank"
rel="noreferrer"
>
Daily recording guide
</a>{' '}
for details.
</p>
</>
)}
</CardBody>
</Modal>
);

View File

@ -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,19 +92,27 @@ export const RecordingProvider = ({ children }) => {
}
};
const handleRecordingUploadCompleted = () => {
setRecordingState(RECORDING_SAVED);
// 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);
await window.writer.write(ev.data);
if (ev.finished) {
console.log('closing!');
window.writer.close();
}
} catch (e) {
console.error(e);
}
};
callObject.on('app-message', handleAppMessage);
callObject.on('recording-upload-completed', handleRecordingUploadCompleted);
callObject.on('recording-data', handleRecordingData);
return () => {
callObject.off('app-message', handleAppMessage);
callObject.off(
'recording-upload-completed',
handleRecordingUploadCompleted
);
callObject.off('recording-data', handleRecordingData);
};
}, [callObject, enableRecording]);
@ -155,13 +170,26 @@ 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 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) => {
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]
);
@ -179,7 +207,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);
@ -263,11 +292,15 @@ 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);
setRecordingState(RECORDING_SAVED);
break;
default:
break;

View File

@ -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();