Skip to content

Commit

Permalink
feat: use new device API in RN SDK and move to @stream-io/react-nativ…
Browse files Browse the repository at this point in the history
…e-webrtc (#925)

Co-authored-by: Zita Szupera <[email protected]>
Co-authored-by: Santhosh Vaiyapuri <[email protected]>
Co-authored-by: Santhosh Vaiyapuri <[email protected]>
  • Loading branch information
4 people authored Aug 16, 2023
1 parent b6ff212 commit 8442d82
Show file tree
Hide file tree
Showing 53 changed files with 1,443 additions and 765 deletions.
30 changes: 11 additions & 19 deletions packages/client/src/rtc/Publisher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,11 @@ import {
import { CallState } from '../store';
import { PublishOptions } from '../types';
import { isReactNative } from '../helpers/platforms';
import {
removeCodec,
setPreferredCodec,
toggleDtx,
} from '../helpers/sdp-munging';
import { toggleDtx } from '../helpers/sdp-munging';
import { Logger } from '../coordinator/connection/types';
import { getLogger } from '../logger';
import { Dispatcher } from './Dispatcher';
import { getOSInfo } from '../client-details';

const logger: Logger = getLogger(['Publisher']);

Expand Down Expand Up @@ -239,9 +236,17 @@ export class Publisher {
? findOptimalVideoLayers(track, targetResolution)
: undefined;

let preferredCodec = opts.preferredCodec;
if (!preferredCodec && trackType === TrackType.VIDEO) {
const isRNAndroid =
isReactNative() && getOSInfo()?.name.toLowerCase() === 'android';
if (isRNAndroid) {
preferredCodec = 'VP8';
}
}
const codecPreferences = this.getCodecPreferences(
trackType,
opts.preferredCodec,
preferredCodec,
);

// listen for 'ended' event on the track as it might be ended abruptly
Expand Down Expand Up @@ -546,19 +551,6 @@ export class Publisher {
private mungeCodecs = (sdp?: string) => {
if (sdp) {
sdp = toggleDtx(sdp, this.isDtxEnabled);
if (isReactNative()) {
if (this.preferredVideoCodec) {
sdp = setPreferredCodec(sdp, 'video', this.preferredVideoCodec);
}
sdp = setPreferredCodec(
sdp,
'audio',
this.isRedEnabled ? 'red' : 'opus',
);
if (!this.isRedEnabled) {
sdp = removeCodec(sdp, 'audio', 'red');
}
}
}
return sdp;
};
Expand Down
9 changes: 7 additions & 2 deletions packages/client/src/rtc/videoLayers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { getOSInfo } from '../client-details';
import { TargetResolution } from '../gen/coordinator';
import { isReactNative } from '../helpers/platforms';

export type OptimalVideoLayer = RTCRtpEncodingParameters & {
width: number;
Expand Down Expand Up @@ -27,6 +29,8 @@ export const findOptimalVideoLayers = (
const settings = videoTrack.getSettings();
const { width: w = 0, height: h = 0 } = settings;

const isRNIos = isReactNative() && getOSInfo()?.name.toLowerCase() === 'ios';

const maxBitrate = getComputedMaxBitrate(targetResolution, w, h);
let downscaleFactor = 1;
['f', 'h', 'q'].forEach((rid) => {
Expand All @@ -40,10 +44,11 @@ export const findOptimalVideoLayers = (
height: Math.round(h / downscaleFactor),
maxBitrate: Math.round(maxBitrate / downscaleFactor),
scaleResolutionDownBy: downscaleFactor,
// Simulcast on iOS React-Native requires all encodings to share the same framerate
maxFramerate: {
f: 30,
h: 25,
q: 20,
h: isRNIos ? 30 : 25,
q: isRNIos ? 30 : 20,
}[rid],
});
downscaleFactor *= 2;
Expand Down
54 changes: 52 additions & 2 deletions packages/react-bindings/src/hooks/callStateHooks.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { useMemo } from 'react';
import {
Call,
CallState,
CameraManagerState,
Comparator,
MicrophoneManagerState,
CallIngressResponse,
CallSessionResponse,
CallSettingsResponse,
CallState,
CallStatsReport,
Comparator,
EgressResponse,
MemberResponse,
StreamVideoParticipant,
Expand Down Expand Up @@ -310,3 +313,50 @@ export const useAnonymousParticipantCount = () => {
const { anonymousParticipantCount$ } = useCallState();
return useObservableValue(anonymousParticipantCount$);
};

/**
* Returns the camera state of the current call.
*
* @category Camera Manager State
*
*/
export const useCameraState = () => {
const call = useCall();

const {
camera = {
state: new CameraManagerState(),
},
} = call as Call;

const status = useObservableValue(camera.state.status$);
const direction = useObservableValue(camera.state.direction$);

return {
status,
direction,
};
};

/**
* Returns the microphone state of the current call.
*
* @category Microphone Manager State
*/
export const useMicrophoneState = () => {
const call = useCall();

const {
microphone = {
state: new MicrophoneManagerState(),
},
} = call as Call;

const status = useObservableValue(microphone.state.status$);
const selectedDevice = useObservableValue(microphone.state.selectedDevice$);

return {
status,
selectedDevice,
};
};
4 changes: 2 additions & 2 deletions packages/react-bindings/src/hooks/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ import { useObservableValue } from './useObservableValue';
*/
export const useHasPermissions = (...permissions: OwnCapability[]): boolean => {
const capabilities = useOwnCapabilities();
return permissions.every((permission) => capabilities.includes(permission));
return permissions.every((permission) => capabilities?.includes(permission));
};

/**
* A hook which returns the local participant's own capabilities.
*
* @category Call State
*/
export const useOwnCapabilities = (): OwnCapability[] => {
export const useOwnCapabilities = (): OwnCapability[] | undefined => {
const { ownCapabilities$ } = useCallState();
return useObservableValue(ownCapabilities$);
};
2 changes: 1 addition & 1 deletion packages/react-bindings/src/wrappers/Restricted.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const Restricted = ({
const call = useCall();
const ownCapabilities = useOwnCapabilities();
const hasPermissions = requiredGrants[requireAll ? 'every' : 'some'](
(capability) => ownCapabilities.includes(capability),
(capability) => ownCapabilities?.includes(capability),
);

if (hasPermissionsOnly) return hasPermissions ? <>{children}</> : null;
Expand Down
135 changes: 135 additions & 0 deletions packages/react-native-sdk/android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
buildscript {
// Buildscript is evaluated before everything else so we can't use getExtOrDefault
def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['StreamVideoReactNative_kotlinVersion']

repositories {
google()
mavenCentral()
jcenter()
}

dependencies {
classpath("com.android.tools.build:gradle:7.3.1")
// noinspection DifferentKotlinGradleVersion
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

def getExtOrDefault(name) {
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['StreamVideoReactNative_' + name]
}

def getExtOrIntegerDefault(name) {
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['StreamVideoReactNative_' + name]).toInteger()
}

android {
compileSdkVersion getExtOrIntegerDefault('compileSdkVersion')
namespace "com.streamvideo.reactnative"
defaultConfig {
minSdkVersion 16
targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
versionCode 1
versionName "1.0"

}

buildTypes {
release {
minifyEnabled false
}
}
lintOptions {
disable 'GradleCompatible'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

repositories {
mavenCentral()
jcenter()
google()
maven { url 'https://jitpack.io' }

def found = false
def defaultDir = null
def androidSourcesName = 'React Native sources'

if (rootProject.ext.has('reactNativeAndroidRoot')) {
defaultDir = rootProject.ext.get('reactNativeAndroidRoot')
} else {
defaultDir = new File(
projectDir,
'/../../../node_modules/react-native/android'
)
}

if (defaultDir.exists()) {
maven {
url defaultDir.toString()
name androidSourcesName
}

logger.info(":${project.name}:reactNativeAndroidRoot ${defaultDir.canonicalPath}")
found = true
} else {
def parentDir = rootProject.projectDir

1.upto(5, {
if (found) return true
parentDir = parentDir.parentFile

def androidSourcesDir = new File(
parentDir,
'node_modules/react-native'
)

def androidPrebuiltBinaryDir = new File(
parentDir,
'node_modules/react-native/android'
)

if (androidPrebuiltBinaryDir.exists()) {
maven {
url androidPrebuiltBinaryDir.toString()
name androidSourcesName
}

logger.info(":${project.name}:reactNativeAndroidRoot ${androidPrebuiltBinaryDir.canonicalPath}")
found = true
} else if (androidSourcesDir.exists()) {
maven {
url androidSourcesDir.toString()
name androidSourcesName
}

logger.info(":${project.name}:reactNativeAndroidRoot ${androidSourcesDir.canonicalPath}")
found = true
}
})
}

if (!found) {
throw new GradleException(
"${project.name}: unable to locate React Native android sources. " +
"Ensure you have you installed React Native as a dependency in your project and try again."
)
}
}

def kotlin_version = getExtOrDefault('kotlinVersion')

dependencies {
// noinspection GradleDynamicVersion
api 'com.facebook.react:react-native:+'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
api 'io.github.webrtc-sdk:android:104.5112.10'
implementation project(':stream-io_react-native-webrtc')
implementation "androidx.annotation:annotation:1.4.0"
}
3 changes: 3 additions & 0 deletions packages/react-native-sdk/android/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
StreamVideoReactNative_kotlinVersion=1.6.10
StreamVideoReactNative_compileSdkVersion=29
StreamVideoReactNative_targetSdkVersion=29
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading

0 comments on commit 8442d82

Please sign in to comment.