|
| 1 | +--- |
| 2 | +outline: deep |
| 3 | +--- |
| 4 | + |
| 5 | +# Best Practice |
| 6 | + |
| 7 | +## Lifecycle |
| 8 | + |
| 9 | +The `useMediaRecorder` composable is a wrapper around the `MediaRecorder` API. It provides a simple interface to start, |
| 10 | +pause, resume, and stop recording. The composable is designed to be as close as possible to the native API. |
| 11 | + |
| 12 | +Although `stop()`, `pause()` and `resume()` are synchronous operations, they just trigger some delayed events. The |
| 13 | +actual lifecycle of the `MediaRecorder` is "asynchronous". |
| 14 | + |
| 15 | +:::info |
| 16 | +You should always wait for the actual event to be triggered before you access the data. |
| 17 | +::: |
| 18 | + |
| 19 | +### `start()` without timeslice and `stop()` |
| 20 | + |
| 21 | +If you don't pass a `timeslice` to `start()`, the `MediaRecorder` will record the stream until you call `stop()`. |
| 22 | +This does mean that you need to wait for the `onStop` callback to be called before you can access the recorded data. |
| 23 | + |
| 24 | +```ts |
| 25 | +const { start, stop } = useMediaRecorder({ |
| 26 | + onStart: () => console.log('started'), |
| 27 | + onStop: () => console.log('stopped') |
| 28 | +}) |
| 29 | + |
| 30 | +await start() |
| 31 | +stop() |
| 32 | +``` |
| 33 | + |
| 34 | +```mermaid |
| 35 | +stateDiagram-v2 |
| 36 | + [*] --> initial |
| 37 | + initial --> starting: start() |
| 38 | + starting --> recording: onStart |
| 39 | + recording --> ondataavailable: stop() |
| 40 | + ondataavailable --> inactive: onStop |
| 41 | + inactive --> starting: start() |
| 42 | + inactive --> [*] |
| 43 | + note right of initial |
| 44 | + state: undefined |
| 45 | + end note |
| 46 | + note right of starting |
| 47 | + set data to [] |
| 48 | + set stream to MediaStream |
| 49 | + start MediaRecorder |
| 50 | + end note |
| 51 | + note right of recording |
| 52 | + state: recording |
| 53 | + set mime Type |
| 54 | + end note |
| 55 | + note right of ondataavailable |
| 56 | + write recorded chunk to data |
| 57 | + end note |
| 58 | + note right of inactive |
| 59 | + state: inactive |
| 60 | + stop all stream tracks |
| 61 | + end note |
| 62 | +``` |
| 63 | + |
| 64 | +### `start()` with timeslice and `stop()` |
| 65 | + |
| 66 | +If you pass a `timeslice` to `start()`, the `MediaRecorder` will record the stream and write the recorded data to the |
| 67 | +`data` array every `timeslice` milliseconds. After you call `stop()`, it will call one last `ondataavailable` event and |
| 68 | +then call `onStop`. This means you should wait for the `onStop` callback to be called before you can access **all** the |
| 69 | +recorded |
| 70 | + |
| 71 | +```ts |
| 72 | +const { start, stop } = useMediaRecorder({ |
| 73 | + onStart: () => console.log('started'), |
| 74 | + onStop: () => console.log('stopped') |
| 75 | +}) |
| 76 | + |
| 77 | +await start(10) |
| 78 | +stop() |
| 79 | +``` |
| 80 | + |
| 81 | +```mermaid |
| 82 | +stateDiagram-v2 |
| 83 | + state "ondataavailable" as ondataavailable2 |
| 84 | + [*] --> initial |
| 85 | + initial --> starting: start() |
| 86 | + starting --> recording: onStart |
| 87 | + recording --> ondataavailable: timeslice passed |
| 88 | + ondataavailable --> recording |
| 89 | + recording --> ondataavailable2: stop() |
| 90 | + ondataavailable2 --> inactive: onStop |
| 91 | + inactive --> starting: start() |
| 92 | + inactive --> [*] |
| 93 | + note right of initial |
| 94 | + state: undefined |
| 95 | + end note |
| 96 | + note right of starting |
| 97 | + set data to [] |
| 98 | + set stream to MediaStream |
| 99 | + start MediaRecorder |
| 100 | + end note |
| 101 | + note right of recording |
| 102 | + state: recording |
| 103 | + set mime Type |
| 104 | + end note |
| 105 | + note right of ondataavailable |
| 106 | + write recorded chunk to data |
| 107 | + end note |
| 108 | + note right of ondataavailable2 |
| 109 | + write recorded chunk to data |
| 110 | + end note |
| 111 | + note right of inactive |
| 112 | + state: inactive |
| 113 | + stop all stream tracks |
| 114 | + end note |
| 115 | +``` |
| 116 | + |
| 117 | +## Limitations |
| 118 | + |
| 119 | +### Reactive `constraints` and `mediaRecorderOptions` |
| 120 | + |
| 121 | +The `constraints` and `mediaRecorderOptions` accept reactive values. **However**, changing these values will not |
| 122 | +automatically update the `MediaRecorder`. You need to call `stop()` and `start()` again to apply the new values. |
| 123 | + |
| 124 | +### `mimeType` and `isMimeTypeSupported` |
| 125 | + |
| 126 | +The `mimeType` is set when the `MediaRecorder` is initialized and the stream is available (start has to be called once). |
| 127 | + |
| 128 | +If you set the `mimeType` manually, make sure that the `mimeType` is supported by the browser. You can |
| 129 | +check if the `mimeType` is supported via `isMimeTypeSupported`. |
| 130 | + |
| 131 | +```ts |
| 132 | +const { mimeType, isMimeTypeSupported } = useMediaRecorder({ |
| 133 | + mediaRecorderOptions: { |
| 134 | + mimeType: 'video/webm;codecs=vp9' |
| 135 | + } |
| 136 | +}) |
| 137 | + |
| 138 | +console.log(mimeType.value, isMimeTypeSupported.value) |
| 139 | +``` |
| 140 | + |
| 141 | +::: warning |
| 142 | +`isMimeTypeSupported` will check your `mediaRecorderOptions.mimeType` and not the actual `mimeType` of the |
| 143 | +`MediaRecorder`. **But** `mimeType` will be set to the actual `mimeType` of the `MediaRecorder`. |
| 144 | + |
| 145 | +So it's possible to have `isMimeTypeSupported.value === true` and `mimeType.value === undefined`. |
| 146 | +::: |
0 commit comments