diff --git a/js/app/app.config.ts b/js/app/app.config.ts index 95aba4cfc..3c202c587 100644 --- a/js/app/app.config.ts +++ b/js/app/app.config.ts @@ -1,12 +1,9 @@ import { ConfigPlugin, - IOSConfig, - withAppDelegate, withEntitlementsPlist, - withMainApplication, withXcodeProject, } from "expo/config-plugins"; -import path from "path"; +import streamplaceReactNativeWebRTC from "../config-react-native-webrtc"; export const withNotificationsIOS: ConfigPlugin = (config) => { config = withEntitlementsPlist(config, (config) => { config.modResults["aps-environment"] = "production"; @@ -45,123 +42,6 @@ const withConsistentVersionNumber = ( return config; }; -// https://github.com/react-native-webrtc/react-native-webrtc/blob/19ca31d4b77d149a659ee037fae54861a2d90a73/Documentation/AndroidInstallation.md#set-audio-category-output-to-media -// look, i'm as upset about this as you are -const androidApplicationReplacements = [ - { - from: "class MainApplication : Application(), ReactApplication {", - to: ` -import com.oney.WebRTCModule.WebRTCModuleOptions -import android.media.AudioAttributes -import org.webrtc.audio.JavaAudioDeviceModule - -class MainApplication : Application(), ReactApplication {`, - }, - { - from: "override fun onCreate() {", - to: ` - override fun onCreate() { - // append this before WebRTCModule initializes - val options = WebRTCModuleOptions.getInstance() - val audioAttributes = AudioAttributes.Builder() - .setUsage(AudioAttributes.USAGE_MEDIA) - .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) - .build() - options.audioDeviceModule = JavaAudioDeviceModule.builder(this) - .setAudioAttributes(audioAttributes) - .createAudioDeviceModule() -`, - }, -]; - -export const withWorkingAndroidWebRTCAudio: ConfigPlugin = (configOuter) => { - return withMainApplication(configOuter, (config) => { - let stringContents: string = config.modResults.contents; - - for (const { from, to } of androidApplicationReplacements) { - stringContents = stringContents.replace(from, to); - } - - config.modResults.contents = stringContents; - - return config; - }); -}; - -const iosDelegateReplacements = [ - { - from: "#import ", - to: (config) => ` -#import -#import -#import -#import "ExpoModulesCore-Swift.h" -#import "${config.name}-Swift.h" -`, - }, - { - from: " self.initialProps = @{};", - to: () => ` - self.initialProps = @{}; - ////RTC PATCH//// - RTCAudioSessionConfiguration* config = [RTCAudioSessionConfiguration webRTCConfiguration]; - - AVAudioSession * session = [AVAudioSession sharedInstance]; - // Set audio to use phone speaker instead of headset speaker - [session setCategory:AVAudioSessionCategoryPlayAndRecord - withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionAllowBluetooth - error:nil]; - [session setActive:YES error:nil]; - - id device; - device = [[AUAudioUnitRTCAudioDevice alloc] init]; - - WebRTCModuleOptions *options = [WebRTCModuleOptions sharedInstance]; - options.loggingSeverity = RTCLoggingSeverityWarning; - options.audioDevice = device; - ////END RTC PATCH//// - `, - }, -]; - -const withWorkingIOSWebRTCAudio: ConfigPlugin = (config) => { - const files = [ - "AUAudioUnitRTCAudioDevice.swift", - "AudioSessionHandler.swift", - "SimpleAudioConverter.swift", - "Utils.swift", - ]; - - // modify the app delegate to make use of the CustomRTCAudioDevice - config = withAppDelegate(config, (config) => { - let stringContents: string = config.modResults.contents; - - for (const { from, to } of iosDelegateReplacements) { - stringContents = stringContents.replace(from, to(config)); - } - - config.modResults.contents = stringContents; - - return config; - }); - - // add the CustomRTCAudioDevice files to the xcode project - config = withXcodeProject(config, (config) => { - const rtc = require.resolve("rtcaudiodevice"); - for (const file of files) { - IOSConfig.XcodeUtils.addBuildSourceFileToGroup({ - filepath: path.resolve(rtc, "..", "CustomRTCAudioDevice", file), - groupName: config.name, - project: config.modResults, - }); - } - - return config; - }); - - return config; -}; - // turn a semver string into a always-increasing integer for google export const versionCode = (verStr: string) => { const [major, minor, patch] = verStr.split(".").map((x) => parseInt(x)); @@ -231,9 +111,7 @@ export default function () { favicon: "./assets/images/favicon.png", }, plugins: [ - withWorkingIOSWebRTCAudio, - withWorkingAndroidWebRTCAudio, - "@config-plugins/react-native-webrtc", + streamplaceReactNativeWebRTC, ["expo-sqlite", { useSQLCipher: true }], "expo-file-system", [ diff --git a/js/config-react-native-webrtc/.gitignore b/js/config-react-native-webrtc/.gitignore new file mode 100644 index 000000000..7bd11f8a3 --- /dev/null +++ b/js/config-react-native-webrtc/.gitignore @@ -0,0 +1,2 @@ +dist +tsconfig.tsbuildinfo diff --git a/js/config-react-native-webrtc/README.md b/js/config-react-native-webrtc/README.md new file mode 100644 index 000000000..3a8720df6 --- /dev/null +++ b/js/config-react-native-webrtc/README.md @@ -0,0 +1,17 @@ +# @streamplace/config-react-native-webrtc + +This npm module contains code to install the Streamplace-optimized version of +react-native-webrtc. + +To use: +`[yarn/pnpm/npm] add @streamplace/config-react-native-webrtc react-native-webrtc` + +And add to the `plugins` array in your `app.config.ts`: + +```tsx +export default { + expo: { + plugins: ["@streamplace/config-react-native-webrtc"], + }, +}; +``` diff --git a/js/config-react-native-webrtc/package.json b/js/config-react-native-webrtc/package.json new file mode 100644 index 000000000..37cd44efb --- /dev/null +++ b/js/config-react-native-webrtc/package.json @@ -0,0 +1,24 @@ +{ + "name": "@streamplace/config-react-native-webrtc", + "version": "0.0.1", + "description": "react-native-webrtc config mod with some changes to make it useful for streamplace", + "packageManager": "yarn@4.3.0", + "scripts": { + "postinstall": "tsc -p tsconfig.json" + }, + "dependencies": { + "@config-plugins/react-native-webrtc": "10.0.0", + "react-native-webrtc": "124.0.4", + "rtcaudiodevice": "git+https://github.com/streamplace/RTCAudioDevice.git#7b4659fe845545d366623cbc813936987144b76f" + }, + "devDependencies": { + "typescript": "~5.3.3" + }, + "main": "./dist/config-react-native-webrtc.js", + "src": "./src/config-react-native-webrtc.ts", + "types": "./dist/config-react-native-webrtc.d.ts", + "files": [ + "dist", + "src" + ] +} diff --git a/js/config-react-native-webrtc/src/config-react-native-webrtc.ts b/js/config-react-native-webrtc/src/config-react-native-webrtc.ts new file mode 100644 index 000000000..f461b1c27 --- /dev/null +++ b/js/config-react-native-webrtc/src/config-react-native-webrtc.ts @@ -0,0 +1,152 @@ +import RNWebRTCPlugin from "@config-plugins/react-native-webrtc"; +import { ExpoConfig } from "expo/config"; +import { + ConfigPlugin, + IOSConfig, + withAppDelegate, + withMainApplication, + withXcodeProject, +} from "expo/config-plugins"; +import { resolve } from "path"; + +// https://github.com/react-native-webrtc/react-native-webrtc/blob/19ca31d4b77d149a659ee037fae54861a2d90a73/Documentation/AndroidInstallation.md#set-audio-category-output-to-media +// look, i'm as upset about this as you are +const androidApplicationReplacements = [ + { + from: "class MainApplication : Application(), ReactApplication {", + to: ` +import com.oney.WebRTCModule.WebRTCModuleOptions +import android.media.AudioAttributes +import org.webrtc.audio.JavaAudioDeviceModule + +class MainApplication : Application(), ReactApplication {`, + }, + { + from: "override fun onCreate() {", + to: ` + override fun onCreate() { + // append this before WebRTCModule initializes + val options = WebRTCModuleOptions.getInstance() + val audioAttributes = AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_MEDIA) + .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) + .build() + options.audioDeviceModule = JavaAudioDeviceModule.builder(this) + .setAudioAttributes(audioAttributes) + .createAudioDeviceModule() +`, + }, +]; + +export const withWorkingAndroidWebRTCAudio: ConfigPlugin = (configOuter) => { + return withMainApplication(configOuter, (config) => { + let stringContents: string = config.modResults.contents; + + for (const { from, to } of androidApplicationReplacements) { + stringContents = stringContents.replace(from, to); + } + + config.modResults.contents = stringContents; + + return config; + }); +}; + +const iosDelegateReplacements = [ + { + from: "#import ", + to: (config) => ` +#import +#import +#import "CaptureController.h" +#import "CapturerEventsDelegate.h" +#import "DataChannelWrapper.h" +#import "RCTConvert+WebRTC.h" +#import "RTCMediaStreamTrack+React.h" +#import "RTCVideoViewManager.h" +#import "ScreenCaptureController.h" +#import "ScreenCapturePickerViewManager.h" +#import "ScreenCapturer.h" +#import "SerializeUtils.h" +#import "SocketConnection.h" +#import "TrackCapturerEventsEmitter.h" +#import "VideoCaptureController.h" +#import "WebRTCModule+RTCDataChannel.h" +#import "WebRTCModule+RTCMediaStream.h" +#import "WebRTCModule+RTCPeerConnection.h" +#import "WebRTCModule+VideoTrackAdapter.h" +#import "WebRTCModule.h" +#import "WebRTCModuleOptions.h" +#import "ExpoModulesCore-Swift.h" +#import "${config.name.replaceAll(" ", "")}-Swift.h" +`, + }, + { + from: " self.initialProps = @{};", + to: () => ` + self.initialProps = @{}; + ////RTC PATCH//// + RTCAudioSessionConfiguration* config = [RTCAudioSessionConfiguration webRTCConfiguration]; + + AVAudioSession * session = [AVAudioSession sharedInstance]; + // Set audio to use phone speaker instead of headset speaker + [session setCategory:AVAudioSessionCategoryPlayAndRecord + withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionAllowBluetooth + error:nil]; + [session setActive:YES error:nil]; + + id device; + device = [[AUAudioUnitRTCAudioDevice alloc] init]; + + WebRTCModuleOptions *options = [WebRTCModuleOptions sharedInstance]; + options.loggingSeverity = RTCLoggingSeverityWarning; + options.audioDevice = device; + ////END RTC PATCH//// + `, + }, +]; + +const withWorkingIOSWebRTCAudio: ConfigPlugin = (config) => { + const files = [ + "AUAudioUnitRTCAudioDevice.swift", + "AudioSessionHandler.swift", + "SimpleAudioConverter.swift", + "Utils.swift", + ]; + + // modify the app delegate to make use of the CustomRTCAudioDevice + config = withAppDelegate(config, (config) => { + let stringContents: string = config.modResults.contents; + + for (const { from, to } of iosDelegateReplacements) { + stringContents = stringContents.replace(from, to(config)); + } + + config.modResults.contents = stringContents; + + return config; + }); + + // add the CustomRTCAudioDevice files to the xcode project + config = withXcodeProject(config, (config) => { + const rtc = require.resolve("rtcaudiodevice"); + for (const file of files) { + IOSConfig.XcodeUtils.addBuildSourceFileToGroup({ + filepath: resolve(rtc, "..", "CustomRTCAudioDevice", file), + groupName: config.name, + project: config.modResults, + }); + } + + return config; + }); + + return config; +}; + +export default function withStreamplaceReactNativeWebRTC(config: ExpoConfig) { + config = RNWebRTCPlugin(config); + config = withWorkingAndroidWebRTCAudio(config); + config = withWorkingIOSWebRTCAudio(config); + return config; +} diff --git a/js/config-react-native-webrtc/tsconfig.json b/js/config-react-native-webrtc/tsconfig.json new file mode 100644 index 000000000..d30da8ce6 --- /dev/null +++ b/js/config-react-native-webrtc/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../app/tsconfig.base", + "compilerOptions": { + "strict": true, + "outDir": "./dist", + "rootDir": "./src", + "declaration": true, + "module": "CommonJS" + }, + "include": ["src/**/*.ts"] +} diff --git a/yarn.lock b/yarn.lock index 2f7ee8539..5e27901c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2935,7 +2935,7 @@ __metadata: languageName: node linkType: hard -"@config-plugins/react-native-webrtc@npm:^10.0.0": +"@config-plugins/react-native-webrtc@npm:10.0.0, @config-plugins/react-native-webrtc@npm:^10.0.0": version: 10.0.0 resolution: "@config-plugins/react-native-webrtc@npm:10.0.0" peerDependencies: @@ -7911,6 +7911,17 @@ __metadata: languageName: node linkType: hard +"@streamplace/config-react-native-webrtc@workspace:js/config-react-native-webrtc": + version: 0.0.0-use.local + resolution: "@streamplace/config-react-native-webrtc@workspace:js/config-react-native-webrtc" + dependencies: + "@config-plugins/react-native-webrtc": "npm:10.0.0" + react-native-webrtc: "npm:124.0.4" + rtcaudiodevice: "git+https://github.com/streamplace/RTCAudioDevice.git#7b4659fe845545d366623cbc813936987144b76f" + typescript: "npm:~5.3.3" + languageName: unknown + linkType: soft + "@swc/core-darwin-arm64@npm:1.6.13": version: 1.6.13 resolution: "@swc/core-darwin-arm64@npm:1.6.13" @@ -23939,7 +23950,7 @@ __metadata: languageName: node linkType: hard -"react-native-webrtc@npm:^124.0.4": +"react-native-webrtc@npm:124.0.4, react-native-webrtc@npm:^124.0.4": version: 124.0.4 resolution: "react-native-webrtc@npm:124.0.4" dependencies: