Live video on the AT Protocol
1import RNWebRTCPlugin from "@config-plugins/react-native-webrtc";
2import { ExpoConfig } from "expo/config";
3import {
4 ConfigPlugin,
5 IOSConfig,
6 withAppDelegate,
7 withMainApplication,
8 withXcodeProject,
9} from "expo/config-plugins";
10import { resolve } from "path";
11
12// https://github.com/react-native-webrtc/react-native-webrtc/blob/19ca31d4b77d149a659ee037fae54861a2d90a73/Documentation/AndroidInstallation.md#set-audio-category-output-to-media
13// look, i'm as upset about this as you are
14const androidApplicationReplacements = [
15 {
16 from: "class MainApplication : Application(), ReactApplication {",
17 to: `
18import com.oney.WebRTCModule.WebRTCModuleOptions
19import android.media.AudioAttributes
20import org.webrtc.audio.JavaAudioDeviceModule
21
22class MainApplication : Application(), ReactApplication {`,
23 },
24 {
25 from: "override fun onCreate() {",
26 to: `
27 override fun onCreate() {
28 // append this before WebRTCModule initializes
29 val options = WebRTCModuleOptions.getInstance()
30 val audioAttributes = AudioAttributes.Builder()
31 .setUsage(AudioAttributes.USAGE_MEDIA)
32 .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
33 .build()
34 options.audioDeviceModule = JavaAudioDeviceModule.builder(this)
35 .setAudioAttributes(audioAttributes)
36 .createAudioDeviceModule()
37`,
38 },
39];
40
41export const withWorkingAndroidWebRTCAudio: ConfigPlugin = (configOuter) => {
42 return withMainApplication(configOuter, (config) => {
43 let stringContents: string = config.modResults.contents;
44
45 for (const { from, to } of androidApplicationReplacements) {
46 stringContents = stringContents.replace(from, to);
47 }
48
49 config.modResults.contents = stringContents;
50
51 return config;
52 });
53};
54
55const iosDelegateReplacements = [
56 {
57 from: "#import <React/RCTLinkingManager.h>",
58 to: (config) => `
59#import <React/RCTLinkingManager.h>
60#import <WebRTC/WebRTC.h>
61#import "CaptureController.h"
62#import "CapturerEventsDelegate.h"
63#import "DataChannelWrapper.h"
64#import "RCTConvert+WebRTC.h"
65#import "RTCMediaStreamTrack+React.h"
66#import "RTCVideoViewManager.h"
67#import "ScreenCaptureController.h"
68#import "ScreenCapturePickerViewManager.h"
69#import "ScreenCapturer.h"
70#import "SerializeUtils.h"
71#import "SocketConnection.h"
72#import "TrackCapturerEventsEmitter.h"
73#import "VideoCaptureController.h"
74#import "WebRTCModule+RTCDataChannel.h"
75#import "WebRTCModule+RTCMediaStream.h"
76#import "WebRTCModule+RTCPeerConnection.h"
77#import "WebRTCModule+VideoTrackAdapter.h"
78#import "WebRTCModule.h"
79#import "WebRTCModuleOptions.h"
80#import "ExpoModulesCore-Swift.h"
81#import "${config.name.replaceAll(" ", "")}-Swift.h"
82`,
83 },
84 {
85 from: " self.initialProps = @{};",
86 to: () => `
87 self.initialProps = @{};
88 ////RTC PATCH////
89 RTCAudioSessionConfiguration* config = [RTCAudioSessionConfiguration webRTCConfiguration];
90
91 AVAudioSession * session = [AVAudioSession sharedInstance];
92 // Set audio to use phone speaker instead of headset speaker
93 [session setCategory:AVAudioSessionCategoryPlayAndRecord
94 withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionAllowBluetooth
95 error:nil];
96 [session setActive:YES error:nil];
97
98 id<RTCAudioDevice> device;
99 device = [[AUAudioUnitRTCAudioDevice alloc] init];
100
101 WebRTCModuleOptions *options = [WebRTCModuleOptions sharedInstance];
102 options.loggingSeverity = RTCLoggingSeverityWarning;
103 options.audioDevice = device;
104 ////END RTC PATCH////
105 `,
106 },
107];
108
109const withWorkingIOSWebRTCAudio: ConfigPlugin = (config) => {
110 const files = [
111 "AUAudioUnitRTCAudioDevice.swift",
112 "AudioSessionHandler.swift",
113 "SimpleAudioConverter.swift",
114 "Utils.swift",
115 ];
116
117 // modify the app delegate to make use of the CustomRTCAudioDevice
118 config = withAppDelegate(config, (config) => {
119 let stringContents: string = config.modResults.contents;
120
121 for (const { from, to } of iosDelegateReplacements) {
122 stringContents = stringContents.replace(from, to(config));
123 }
124
125 config.modResults.contents = stringContents;
126
127 return config;
128 });
129
130 // add the CustomRTCAudioDevice files to the xcode project
131 config = withXcodeProject(config, (config) => {
132 const rtc = require.resolve("rtcaudiodevice");
133 for (const file of files) {
134 IOSConfig.XcodeUtils.addBuildSourceFileToGroup({
135 filepath: resolve(rtc, "..", "CustomRTCAudioDevice", file),
136 groupName: config.name,
137 project: config.modResults,
138 });
139 }
140
141 return config;
142 });
143
144 return config;
145};
146
147export default function withStreamplaceReactNativeWebRTC(config: ExpoConfig) {
148 config = RNWebRTCPlugin(config);
149 config = withWorkingAndroidWebRTCAudio(config);
150 config = withWorkingIOSWebRTCAudio(config);
151 return config;
152}