Skip to content

Commit 9b8af5a

Browse files
committed
Merge branch 'develop'
2 parents 2483e64 + 2e4e656 commit 9b8af5a

23 files changed

+5997
-4323
lines changed

README.md

+58-44
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@ CameraKit Web as the name suggests, is our camera platform for websites. In addi
3434
- [CameraKit Android](https://github.com/CameraKit/camerakit-android)
3535
- [CameraKit iOS](https://github.com/CameraKit/camerakit-ios)
3636

37+
## Browser support
38+
39+
| Browser | Preview | Pictures | Recording |
40+
| -------------- | ------- | -------- | --------- |
41+
| Desktop Chrome ||||
42+
| Android Chrome ||||
43+
| Firefox ||||
44+
| Edge ||||
45+
| Desktop Safari ||||
46+
| Mobile Safari ||||
47+
3748
## Sponsored By
3849

3950
<a href="https://www.expensify.com/"><img alt="Expensify" src=".repo/gh-readme-expensify-logo.svg" height="45px" width="375px" align="center"></a>
@@ -43,18 +54,18 @@ CameraKit Web as the name suggests, is our camera platform for websites. In addi
4354

4455
## Setup
4556

46-
Install the `camerakit-web` package.
57+
Install the `camerakit` package.
4758

4859
```
49-
$ npm install camerakit-web
60+
$ npm install camerakit
5061
```
5162

5263
## Usage
5364

54-
Import and use `camerakit-web` in your project.
65+
Import and use `camerakit` in your project.
5566

5667
```js
57-
import camerakit from "camerakit-web";
68+
import camerakit from "camerakit";
5869
```
5970

6071
Or, alternatively, you can import via a script tag:
@@ -64,38 +75,38 @@ Or, alternatively, you can import via a script tag:
6475
<!-- You can now access `camerakit` from the global scope -->
6576
```
6677

67-
To properly support `webm` video recording and playback on Safari, you'll need to host the WebAssembly(wasm) and worker files packaged in `dist/browser/` on your webserver. The video recorder and player require these in order to function properly on Safari.
78+
For additional Safari requirements, see [Safari support details](#safari).
6879

69-
Example usage:
80+
### Example usage:
7081

7182
```js
7283
async function () {
7384
const devices = await camerakit.getDevices();
7485

75-
const myStream = await camerakit.createCaptureStream({
86+
const preview = await camerakit.createCaptureStream({
7687
audio: devices.audio[0],
7788
video: devices.video[0]
7889
});
7990

80-
myStream.setResolution({width: 1920, height: 1080});
81-
const myPicture = myStream.shutter.capture();
91+
preview.setResolution({width: 1920, height: 1080});
92+
const myPicture = preview.shutter.capture();
8293

83-
myStream.recorder.start();
94+
preview.recorder.start();
8495

8596
// Wait...
8697

8798
// Pause the recording & resume
88-
await myRecorder.pause();
89-
await myRecorder.start();
99+
await preview.recorder.pause();
100+
await preview.recorder.start();
90101

91102
// Wait some more...
92103

93-
const recordedVideo = await myRecorder.stop(); // Use the video yourself
104+
const recordedVideo = await preview.recorder.stop(); // Use the video yourself
94105

95-
myRecorder.downloadLatestRecording(); // Download the video direct from browser
106+
preview.recorder.downloadLatestRecording(); // Download the video direct from browser
96107

97108
// Stop using camera
98-
myStream.destroy();
109+
preview.destroy();
99110

100111
// Play video via camerakit player
101112
const player = new camerakit.Player();
@@ -107,14 +118,14 @@ async function () {
107118
}
108119
```
109120

110-
## Safari support details
121+
## Safari support details<a id="safari"></a>
111122

112-
**Safari audio recording and video seeking are not currently supported.**
123+
Currently, the WebAssembly and JS worker files used for video recording and playback on Safari must be hosted seperately on a webserver. The compiled files can be found in `dist/browser/`, ensure they're accessible via a public URL (e.g `https://myurl.com/myWorkerFile.js`).
113124

114-
If you'd like to host the wasm/worker files in a subdirectory, you'll need to update the `base` param on `camerakit.Loader` and as well as to `fallbackConfig` when calling `createCaptureStream`:
125+
If you'd like to host the wasm/worker files in a custom path, you'll need to update the `base` param on `camerakit.Loader` and as well as to `fallbackConfig` when calling `createCaptureStream`:
115126

116127
```js
117-
import camerakit from "camerakit-web";
128+
import camerakit from "camerakit";
118129

119130
async function () {
120131
// Point fallback video player to correct directory
@@ -143,6 +154,8 @@ async function () {
143154
| `camerakit.createCaptureStream` | `{audio?: MediaSource, video?: MediaSource, fallbackConfig?: Partial<FallbackConfig>}` | `Promise<CaptureStream>` | Creates new `CaptureStream` instance with provided media inputs |
144155
| `camerakit.enableStorage` | `{method?: "localStorage" \| "sessionStorage" \| null}` | `void` | Enables photo storage as a default |
145156
| `camerakit.disableStorage` | none | `void` | Disables photo storage as a default |
157+
| `camerakit.enableDebug` | none | `void` | Enables debug mode for logging output |
158+
| `camerakit.disableDebug` | none | `void` | Disables debug mode |
146159

147160
#### Properties
148161

@@ -155,13 +168,14 @@ async function () {
155168

156169
#### Instance methods
157170

158-
| Name | Parameters | Return | Description |
159-
| ----------------------- | -------------------------------------------------------------------------------------- | ---------------------- | -------------------------------------------------------- |
160-
| `stream.init` | none | `Promise<void>` | Initializes stream and requests permissions from browser |
161-
| `stream.setResolution` | `{width?: number, height?: number, aspect?: number, source?: "original" \| "preview"}` | `Promise<void>` | Sets the video resolution of the specified source |
162-
| `stream.setSource` | `{audio?: MediaSource, video?: MediaSource, source?: "original" \| "preview"}` | `Promise<void>` | Overrides original media inputs for specified source |
163-
| `stream.getMediaStream` | `{source?: "original" \| "preview"}` | `Promise<MediaStream>` | Returns raw `MediaStream` for use in video display |
164-
| `stream.destroy` | none | `void` | Closes all open streams and cancels capture |
171+
| Name | Parameters | Return | Description |
172+
| ----------------------- | -------------------------------------------------------------------------------------- | ---------------------- | --------------------------------------------------------------------- |
173+
| `stream.init` | none | `Promise<void>` | Initializes stream and requests permissions from browser |
174+
| `stream.setResolution` | `{width?: number, height?: number, aspect?: number, source?: "original" \| "preview"}` | `Promise<void>` | Sets the video resolution of the specified source |
175+
| `stream.setSource` | `{audio?: MediaSource, video?: MediaSource, source?: "original" \| "preview"}` | `Promise<void>` | Overrides original media inputs for specified source |
176+
| `stream.getPreview` | `{source?: "original" \| "preview"}` | `HTMLVideoElement` | Returns an video element with the appropriate internal event handlers |
177+
| `stream.getMediaStream` | `{source?: "original" \| "preview"}` | `Promise<MediaStream>` | Returns raw `MediaStream` for use in video display |
178+
| `stream.destroy` | none | `void` | Closes all open streams and cancels capture |
165179

166180
#### Properties
167181

@@ -176,26 +190,26 @@ Used for taking photos of the `CaptureStream`.
176190

177191
### Instance methods
178192

179-
| name | Parameters | Return | Description |
180-
| ------------------------------- | ------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------- |
181-
| `shutter.capture` | `{source?: "original" \| "preview", save?: "localStorage" | "sessionStorage" | null}` | `string` | Takes and returns picture from specified source |
182-
| `shutter.captureAndDownload` | `{source?: "original" \| "preview", filename?: string}` | `boolean` | Calls `capture` and creates file download from result |
183-
| `shutter.downloadLatestCapture` | `filename?: string` | `boolean` | Downloads the last picture taken |
193+
| name | Parameters | Return | Description |
194+
| ------------------------------- | --------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------- |
195+
| `shutter.capture` | `{source?: "original" \| "preview", save?: "localStorage" \| "sessionStorage" \| null}` | `string` | Takes and returns picture from specified source |
196+
| `shutter.captureAndDownload` | `{source?: "original" \| "preview", filename?: string}` | `boolean` | Calls `capture` and creates file download from result |
197+
| `shutter.downloadLatestCapture` | `filename?: string` | `boolean` | Downloads the last picture taken |
184198

185199
### `Recorder`
186200

187201
Used for recording video of the the `CaptureStream`.
188202

189203
### Instance methods
190204

191-
| name | Parameters | Return | Description |
192-
| ---------------------------------- | ------------------------------------ | ------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
193-
| `recorder.start` | `{source?: "original" \| "preview"}` | `Promise<void>` | Starts the recording from the specified source |
194-
| `recorder.stop` | none | `Promise<?[Blob, ?Blob]>` | Stops the recording and returns an array. First Blob is Video (and audio if available), the second is Audio on Safari. |
195-
| `recorder.pause` | none | `Promise<void>` | Pauses the recording until resumed with `recorder.start()` |
196-
| `recorder.getLatestRecording` | none | `?Blob` | Returns last recorded video file |
197-
| `recorder.downloadLatestRecording` | `filename?: string` | `boolean` | Creates file download from last video recording |
198-
| `recorder.setMimeType` | `mimeType: string` | `boolean` | Sets the video recording mime type for all sources |
205+
| name | Parameters | Return | Description |
206+
| ---------------------------------- | ------------------------------------ | ---------------- | ---------------------------------------------------------- |
207+
| `recorder.start` | `{source?: "original" \| "preview"}` | `Promise<void>` | Starts the recording from the specified source |
208+
| `recorder.stop` | none | `Promise<?Blob>` | Stops the recording and returns a completed video file |
209+
| `recorder.pause` | none | `Promise<void>` | Pauses the recording until resumed with `recorder.start()` |
210+
| `recorder.getLatestRecording` | none | `?Blob` | Returns last recorded video file |
211+
| `recorder.downloadLatestRecording` | `filename?: string` | `boolean` | Creates file download from last video recording |
212+
| `recorder.setMimeType` | `mimeType: string` | `boolean` | Sets the video recording mime type for all sources |
199213

200214
### `Player`
201215

@@ -206,7 +220,7 @@ Example:
206220
```js
207221
const player = new camerakit.Player();
208222

209-
player.src = window.URL.createObjectURL(...);
223+
player.src = window.URL.createObjectURL(/* Your video Blob */);
210224

211225
player.play();
212226
player.pause();
@@ -234,10 +248,10 @@ Exposed `OGVLoader`.
234248
{
235249
base: string; // Base directory for wasm/worker files
236250

237-
width: number; // Video width
238-
height: number; // Video height
239-
bitrate: number; // Video bitrate
240-
framerate: number; // Video framerate
251+
width: number;
252+
height: number;
253+
bitrate: number;
254+
framerate: number;
241255
}
242256
```
243257

example/pages/example.tsx

+22-29
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as React from "react";
22
import * as CameraKitWeb from "../../src";
3-
import { CaptureSource } from "../../src/types";
4-
import { CaptureStream } from "../../src";
3+
import { CaptureStream, CaptureSource } from "../../src";
54

65
type Props = {};
76

@@ -13,7 +12,6 @@ type State = {
1312
stream: CaptureStream | undefined;
1413
video: Blob | undefined;
1514
videoTaken: boolean;
16-
audio: Blob | null;
1715
recording: boolean;
1816
};
1917

@@ -22,7 +20,7 @@ class Example extends React.Component {
2220

2321
audioSource: HTMLSelectElement | null;
2422
videoSource: HTMLSelectElement | null;
25-
src: HTMLVideoElement | null;
23+
preview: HTMLDivElement | null;
2624
out: HTMLVideoElement | null;
2725
imageContainer: HTMLImageElement | null;
2826

@@ -36,10 +34,10 @@ class Example extends React.Component {
3634
stream: undefined,
3735
video: undefined,
3836
videoTaken: false,
39-
audio: null,
4037
recording: false
4138
};
4239

40+
CameraKitWeb.enableDebug();
4341
CameraKitWeb.Loader.base = "/ogv";
4442
const videoElem = new CameraKitWeb.Player();
4543
this.out = videoElem;
@@ -58,11 +56,12 @@ class Example extends React.Component {
5856
};
5957

6058
gotStream = (stream: CaptureStream) => {
61-
if (!this.src) return;
59+
if (!this.preview) return;
60+
const preview = stream.getPreview();
61+
preview.style.width = "200";
62+
63+
this.preview.appendChild(preview);
6264
this.setState({ stream });
63-
this.src.srcObject = stream.getMediaStream();
64-
this.src.play();
65-
this.src.muted = true;
6665
};
6766

6867
requestCamera = () => {
@@ -124,21 +123,18 @@ class Example extends React.Component {
124123
stopRecording = async () => {
125124
let { stream } = this.state;
126125
if (!stream) return;
127-
const [buffer, audioBuffer] = await stream.recorder.stop();
128-
this.setState(
129-
{ video: buffer, audio: audioBuffer, recording: false, videoTaken: true },
130-
() => {
131-
const { video } = this.state;
132-
if (!video || !this.out) return;
133-
this.out.src = "";
134-
this.out.srcObject = null;
135-
this.out.src = window.URL.createObjectURL(video);
136-
this.out.controls = true;
137-
this.out.width = 200;
138-
this.out.height = 150;
139-
this.out.play();
140-
}
141-
);
126+
const buffer = await stream.recorder.stop();
127+
this.setState({ video: buffer, recording: false, videoTaken: true }, () => {
128+
const { video } = this.state;
129+
if (!video || !this.out) return;
130+
this.out.src = "";
131+
this.out.srcObject = null;
132+
this.out.src = window.URL.createObjectURL(video);
133+
this.out.controls = true;
134+
this.out.width = 200;
135+
this.out.height = 150;
136+
this.out.play();
137+
});
142138
};
143139

144140
downloadVideo = () => {
@@ -189,12 +185,9 @@ class Example extends React.Component {
189185
Request Stream
190186
</button>
191187
<br />
192-
<video
193-
playsInline
194-
autoPlay
195-
width="200"
188+
<div
196189
ref={video => {
197-
this.src = video;
190+
this.preview = video;
198191
}}
199192
/>
200193
<br />

example/server.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ app.get("/ogv/*", (req, res) => {
3333
res.status(404).end();
3434
});
3535

36-
// Host `webm-wasm` wasm/worker files
37-
app.get("/webm/*", (req, res) => {
38-
const fileRegex = /^\/webm\/([a-zA-Z0-9\-]+\.(wasm|js|(js\.map)))$/;
36+
// Host `webm-media-recorder` wasm/worker files
37+
app.get("/webm/*.(js|wasm)", (req, res) => {
38+
const fileRegex = /^\/webm\/([a-zA-Z0-9\-]+\.(wasm|js|(js\.map)|(umd\.js)))$/;
3939
const match = req.path.match(fileRegex);
4040
if (match && match[1]) {
4141
const filePath = path.resolve(
4242
__dirname,
43-
"../node_modules/webm-wasm/dist/",
43+
"../node_modules/webm-media-recorder/",
4444
match[1]
4545
);
4646

0 commit comments

Comments
 (0)