Skip to content

Commit

Permalink
Merge pull request #30 from CameraKit/v0.3.1
Browse files Browse the repository at this point in the history
v0.3.1
  • Loading branch information
austinkettner authored Apr 9, 2019
2 parents 0b1faaf + ee08884 commit 0cc99f8
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 83 deletions.
3 changes: 1 addition & 2 deletions example/pages/example.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from "react";
import * as CameraKitWeb from "../../src";
import { CaptureSource } from "../../src/types";
import { CaptureStream } from "../../src";
import { CaptureStream, CaptureSource } from "../../src";

type Props = {};

Expand Down
29 changes: 8 additions & 21 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"name": "camerakit-web",
"version": "0.2.0",
"version": "0.3.1",
"description": "CameraKit for web and javascript projects.",
"main": "./dist/compiled/index.js",
"types": "./dist/compiled/index.d.ts",
Expand All @@ -16,11 +16,10 @@
"author": "Alterac, Inc.",
"license": "MIT",
"dependencies": {
"@mattiasbuelens/web-streams-polyfill": "^0.2.1",
"@types/dom-mediacapture-record": "^1.0.0",
"ogv": "^1.6.0",
"ts-ebml": "^2.0.2",
"webm-media-recorder": "^0.8.0",
"webm-media-recorder": "^0.8.1",
"webrtc-adapter": "^7.1.1"
},
"devDependencies": {
Expand Down
9 changes: 9 additions & 0 deletions src/entity/CaptureSource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class CaptureSource {
device: MediaDeviceInfo;
label: string;

constructor({ device, label }: { device: MediaDeviceInfo; label: string }) {
this.device = device;
this.label = label || "Unnamed source";
}
}
52 changes: 25 additions & 27 deletions src/entity/CaptureStream.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { Shutter } from "./Shutter";
import { Recorder } from "./Recorder";
import { CaptureSource, FallbackMediaRecorderConfig } from "../types";
import { CaptureSource } from "./CaptureSource";
import { FallbackMediaRecorderConfig } from "../types";
import { toTrackConstraints, createVideoElement } from "../util";

export class CaptureStream {
private mediaStream: MediaStream;
private previewStream: MediaStream;

private videoSource: CaptureSource | undefined;
private audioSource: CaptureSource | undefined;
private videoSource:
| CaptureSource
| MediaTrackConstraints
| "front"
| "back"
| undefined;
private audioSource: CaptureSource | MediaTrackConstraints | undefined;
private previewVideoSource = this.videoSource;
private previewAudioSource = this.audioSource;

Expand All @@ -19,17 +26,17 @@ export class CaptureStream {
/**
* CaptureStream provides access to streaming related functions
* @param {Object} opts
* @param {CaptureSource} [opts.video] - Video source to create CaptureStream from
* @param {CaptureSource} [opts.audio] - Audio source to create CaptureStream from
* @param {CaptureSource | MediaTrackConstraints | "front" | "back"} [opts.video] - Video source to create CaptureStream from
* @param {CaptureSource | MediaTrackConstraints} [opts.audio] - Audio source to create CaptureStream from
* @param {Partial<FallbackMediaRecorderConfig>} [opts.fallbackConfig] - Optional config for FallbackMediaRecorder
*/
constructor({
video,
audio,
fallbackConfig
}: {
video?: CaptureSource;
audio?: CaptureSource;
video?: CaptureSource | MediaTrackConstraints | "front" | "back";
audio?: CaptureSource | MediaTrackConstraints;
fallbackConfig?: Partial<FallbackMediaRecorderConfig>;
}) {
this.previewVideoSource = this.videoSource = video;
Expand All @@ -50,19 +57,17 @@ export class CaptureStream {
private generateConstraints({
source
}: { source?: "original" | "preview" } = {}): MediaStreamConstraints {
const video = (source === "preview"
? this.previewVideoSource
: this.videoSource)!.device!.deviceId;
const audio = (source === "preview"
? this.previewAudioSource
: this.audioSource)!.device!.deviceId;
if (!video && !audio) {
const videoSource =
source === "preview" ? this.previewVideoSource : this.videoSource;
const audioSource =
source === "preview" ? this.previewAudioSource : this.audioSource;
if (videoSource && !audioSource) {
throw new Error("No compatible media sources");
}

return {
video: { deviceId: video ? { exact: video } : undefined },
audio: { deviceId: audio ? { exact: audio } : undefined }
video: toTrackConstraints(videoSource),
audio: toTrackConstraints(audioSource)
};
}

Expand Down Expand Up @@ -98,15 +103,8 @@ export class CaptureStream {
* @returns {Promise<Shutter>} Newly created shutter instance
*/
private async initalizeShutter(): Promise<Shutter> {
const original = document.createElement("video");
const preview = document.createElement("video");

original.srcObject = this.mediaStream;
original.play();
original.muted = true;
preview.srcObject = this.previewStream;
preview.play();
preview.muted = true;
const original = createVideoElement(this.mediaStream);
const preview = createVideoElement(this.previewStream);

this.shutter = new Shutter({ original, preview });
return this.shutter;
Expand Down Expand Up @@ -181,8 +179,8 @@ export class CaptureStream {
/**
* Re-sets the audio/video source for the specified stream
* @param {Object} [opts={}]
* @param {CaptureSource} [opts.video] - New video source
* @param {CaptureSource} [opts.audio] - New audio source
* @param {CaptureSource | MediaTrackConstraints | "front" | "back"} [opts.video] - New video source
* @param {CaptureSource | MediaTrackConstraints} [opts.audio] - New audio source
* @param {("original" | "preview")} [opts.source=original] - Which stream the to set the new sources
* @returns {Promise<MediaStream>} The new stream with updated source
*/
Expand Down
39 changes: 24 additions & 15 deletions src/entity/FallbackMediaRecorder.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getVideoSpecs, injectMetadata } from "../util";
import { getVideoSpecs, injectMetadata, closeStream } from "../util";
import { FallbackMediaRecorderConfig } from "../types";
import * as path from "path";
import FediaRecorder = require("webm-media-recorder");
Expand Down Expand Up @@ -33,7 +33,8 @@ export class FallbackMediaRecorder {
stream: MediaStream,
config?: Partial<FallbackMediaRecorderConfig>
) {
this.stream = stream;
// Stream must be cloned, otherwise there are audio recording issues
this.stream = stream.clone();
this.blobs = [];
this.mediaRecorder = null;

Expand All @@ -44,24 +45,32 @@ export class FallbackMediaRecorder {
};
}

private destroy() {
// TODO: any needed cleanup
private async destroy() {
if (this.stream) {
// Fallback recorder is responsible for closing the cloned stream
await closeStream(this.stream);
}
}

private createRecorder() {
this.mediaRecorder = null;

try {
this.mediaRecorder = new FediaRecorder(this.stream.clone(), {
mimeType: this.mimeType,
videoBitsPerSecond: this.config.bitrate,
width: this.config.width,
height: this.config.height,
framerate: this.config.framerate
}, {
encoderWorkerFactory: () => new Worker(path.join(this.config.base, WORKER_NAME)),
WebmOpusEncoderWasmPath: path.join(this.config.base, WASM_NAME)
});
this.mediaRecorder = new FediaRecorder(
this.stream.clone(),
{
mimeType: this.mimeType,
videoBitsPerSecond: this.config.bitrate,
width: this.config.width,
height: this.config.height,
framerate: this.config.framerate
},
{
encoderWorkerFactory: () =>
new Worker(path.join(this.config.base, WORKER_NAME)),
WebmOpusEncoderWasmPath: path.join(this.config.base, WASM_NAME)
}
);
} catch (e) {
console.error("Exception while creating MediaRecorder:", e);
return;
Expand All @@ -83,7 +92,7 @@ export class FallbackMediaRecorder {
private async stopAndAwait() {
return new Promise(resolve => {
if (this.mediaRecorder) {
this.mediaRecorder.addEventListener("stop", async (e: BlobEvent) => {
this.mediaRecorder.addEventListener("stop", async () => {
resolve();
});
this.mediaRecorder.stop();
Expand Down
1 change: 1 addition & 0 deletions src/entity/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./CaptureSource";
export * from "./CaptureStream";
export * from "./Shutter";
export * from "./Recorder";
Expand Down
40 changes: 30 additions & 10 deletions src/main/index.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,44 @@
import { CaptureStream } from "../entity";
import {
CaptureSource,
StorageMethod,
FallbackMediaRecorderConfig
} from "../types";
import { CaptureStream, CaptureSource } from "../entity";
import { StorageMethod, FallbackMediaRecorderConfig } from "../types";
import settings from "../main/settings";
import { requestAndCloseStream } from "../util";

/**
* Returns media devices available to browser
* @param {Object} [opts]
* @param {boolean} [opts.noRequest] - Return devices without requesting audio/video permissions
* @returns {Promise<{audio: Array<CaptureSource>, video: Array<CaptureSource>}>} Available audio and video sources
*/
export async function getDevices() {
export async function getDevices(
opts: {
noRequest?: boolean;
} = {}
) {
if (!opts.noRequest) {
await requestAndCloseStream();
}

const video: Array<CaptureSource> = [];
const audio: Array<CaptureSource> = [];

const devices = await navigator.mediaDevices.enumerateDevices();
devices.forEach(device => {
switch (device.kind) {
case "videoinput":
video.push({ device, label: device.label || "Unnamed video input" });
video.push(
new CaptureSource({
device,
label: device.label || "Unnamed video input"
})
);
break;
case "audioinput":
audio.push({ device, label: device.label || "Unnamed audio input" });
audio.push(
new CaptureSource({
device,
label: device.label || "Unnamed audio input"
})
);
break;
default:
console.log("Other input type detected:", device.kind);
Expand All @@ -33,14 +50,17 @@ export async function getDevices() {

/**
* Creates capture stream via chosen CaptureSource's
* @param {Object} [opts]
* @param {CaptureSource | "front" | "back"} [opts.video] - Video source to create CaptureStream from
* @param {CaptureSource} [opts.video] - Audio source to create CaptureStream from
* @returns {Promise<CaptureStream>} Freshly created CaptureStream from sources
*/
export async function createCaptureStream({
video,
audio,
fallbackConfig
}: {
video?: CaptureSource;
video?: CaptureSource | "front" | "back";
audio?: CaptureSource;
fallbackConfig?: Partial<FallbackMediaRecorderConfig>;
}) {
Expand Down
5 changes: 0 additions & 5 deletions src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
export type CaptureSource = {
device: MediaDeviceInfo;
label: string;
};

export type StorageMethod = "localStorage" | "sessionStorage" | null;

export type CKSettings = {
Expand Down
Loading

0 comments on commit 0cc99f8

Please sign in to comment.