Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,9 @@ private void addVideoView(View videoView, Context context) {
}

public void connect(Context context) {
setWebVideoEnabled(false);
// Phone mode streams video over the same local connection as web mode does
// over the signaling websocket, so it needs the video server enabled too.
setWebVideoEnabled(true);
ILocalConnection connection = connectionSelector.getConnection();

if (!connection.isConnected()) {
Expand Down Expand Up @@ -250,4 +252,4 @@ private PhoneController() {
"Use getInstance() method to get the single instance of this class.");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import androidx.core.content.ContextCompat;
import com.pedro.rtplibrary.view.OpenGlView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
import org.openbot.app.robot.utils.AndGate;
Expand All @@ -34,6 +36,7 @@
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.RtpReceiver;
import org.webrtc.RtpSender;
import org.webrtc.RtpTransceiver;
import org.webrtc.SessionDescription;
import org.webrtc.SurfaceTextureHelper;
Expand Down Expand Up @@ -81,6 +84,8 @@ public class WebRtcServer implements IVideoServer {
SurfaceTextureHelper surfaceTextureHelper;
private PeerConnection peerConnection;
MediaStream mediaStream;
private RtpSender videoSender;
private RtpSender audioSender;

private AndGate andGate;
private Context context;
Expand Down Expand Up @@ -242,7 +247,7 @@ public void onCameraSwitchError(String errorDescription) {
private void resumeLocalCamera() {
emitLocalCameraCommand(Constants.CMD_RESUME_LOCAL_CAMERA);
}

private void emitLocalCameraCommand(String command) {
try {
ControllerToBotEventBus.emitEvent(new JSONObject().put("command", command).toString());
Expand Down Expand Up @@ -274,15 +279,42 @@ public void onCreateSuccess(SessionDescription sessionDescription) {
new MediaConstraints());
}

// Logs whether the negotiated SDP actually contains a video m-line, so a
// logcat pull tells us immediately whether the break is in track
// negotiation (this log) vs. transport/rendering (later logs).
private void logSdp(String label, SessionDescription sdp) {
boolean hasVideo = sdp.description.contains("m=video");
boolean hasAudio = sdp.description.contains("m=audio");
Log.d(TAG, "SDP [" + label + "]: m=video present=" + hasVideo + " m=audio present=" + hasAudio);
}

private void startStreamingVideo() {
mediaStream = factory.createLocalMediaStream("ARDAMS");
mediaStream.addTrack(videoTrackFromCamera);
mediaStream.addTrack(localAudioTrack);
peerConnection.addStream(mediaStream);
// peerConnection.addStream() is a Plan-B-only API and is a no-op under the
// Unified Plan SDP semantics that the current WebRTC build defaults to, so
// tracks must be attached with addTrack() instead.
List<String> streamIds = Collections.singletonList(mediaStream.getId());
videoSender = peerConnection.addTrack(videoTrackFromCamera, streamIds);
audioSender = peerConnection.addTrack(localAudioTrack, streamIds);
Log.d(
TAG,
"startStreamingVideo: videoSender="
+ (videoSender != null)
+ " audioSender="
+ (audioSender != null)
+ " videoTrack.state="
+ videoTrackFromCamera.state());
}

private void stopStreamingVideo() {
peerConnection.removeStream(mediaStream);
if (videoSender != null) {
peerConnection.removeTrack(videoSender);
}
if (audioSender != null) {
peerConnection.removeTrack(audioSender);
}
}

private void stopServer() {
Expand Down Expand Up @@ -335,6 +367,7 @@ private PeerConnection createPeerConnection(PeerConnectionFactory factory) {
iceServers.add(stunServer);

PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);
rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
MediaConstraints pcConstraints = new MediaConstraints();

PeerConnection.Observer pcObserver =
Expand Down Expand Up @@ -433,6 +466,10 @@ private void sendMessage(JSONObject message) {
private void createVideoTrackFromCameraAndShowIt() {
audioConstraints = new MediaConstraints();
videoCapturer = createVideoCapturer();
if (videoCapturer == null) {
Log.e(TAG, "createVideoTrackFromCameraAndShowIt: no back-facing camera capturer found");
return;
}
VideoSource videoSource = factory.createVideoSource(videoCapturer.isScreencast());

surfaceTextureHelper =
Expand All @@ -454,15 +491,18 @@ private void createVideoTrackFromCameraAndShowIt() {
}

private void initializePeerConnectionFactory() {
// PeerConnectionFactory.initialize() loads the native WebRTC library; it must
// run before any class that calls into native code (e.g. DefaultVideoEncoderFactory's
// SoftwareVideoEncoderFactory) is constructed, or it fails with UnsatisfiedLinkError.
PeerConnectionFactory.InitializationOptions initializationOptions =
PeerConnectionFactory.InitializationOptions.builder(context).createInitializationOptions();
PeerConnectionFactory.initialize(initializationOptions);

VideoEncoderFactory encoderFactory =
new DefaultVideoEncoderFactory(rootEglBase.getEglBaseContext(), true, true);
VideoDecoderFactory decoderFactory =
new DefaultVideoDecoderFactory(rootEglBase.getEglBaseContext());

PeerConnectionFactory.InitializationOptions initializationOptions =
PeerConnectionFactory.InitializationOptions.builder(context).createInitializationOptions();
PeerConnectionFactory.initialize(initializationOptions);

PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
options.networkIgnoreMask = 16;
options.disableEncryption = false;
Expand Down Expand Up @@ -495,12 +535,45 @@ private boolean useCamera2() {
return Camera2Enumerator.isSupported(context);
}

private final CameraVideoCapturer.CameraEventsHandler cameraEventsHandler =
new CameraVideoCapturer.CameraEventsHandler() {
@Override
public void onCameraError(String errorDescription) {
Log.e(TAG, "Camera onCameraError: " + errorDescription);
}

@Override
public void onCameraDisconnected() {
Log.e(TAG, "Camera onCameraDisconnected");
}

@Override
public void onCameraFreezed(String errorDescription) {
Log.e(TAG, "Camera onCameraFreezed: " + errorDescription);
}

@Override
public void onCameraOpening(String cameraName) {
Log.d(TAG, "Camera onCameraOpening: " + cameraName);
}

@Override
public void onFirstFrameAvailable() {
Log.d(TAG, "Camera onFirstFrameAvailable");
}

@Override
public void onCameraClosed() {
Log.d(TAG, "Camera onCameraClosed");
}
};

private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) {
final String[] deviceNames = enumerator.getDeviceNames();

for (String deviceName : deviceNames) {
if (enumerator.isBackFacing(deviceName)) {
VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, cameraEventsHandler);
if (videoCapturer != null) {
return videoCapturer;
}
Expand Down
21 changes: 18 additions & 3 deletions controller/flutter/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@ plugins {
id "dev.flutter.flutter-gradle-plugin"
}

def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
namespace "org.openbot.flutter_controller"
namespace "org.openbot.app.controller"

// 1. Updated to match the new plugin requirements
compileSdk 36
Expand All @@ -14,7 +20,7 @@ android {
ndkVersion = "28.2.13676358"

defaultConfig {
applicationId "org.openbot.flutter_controller"
applicationId "org.openbot.app.controller"

// 3. Changed from 'flutter.minSdkVersion' to 24 to support modern WebRTC/Sensors
minSdkVersion 24
Expand All @@ -26,9 +32,18 @@ android {
versionName flutter.versionName
}

signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}

buildTypes {
release {
signingConfig signingConfigs.debug
signingConfig signingConfigs.release
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.openbot.flutter_controller">
package="org.openbot.app.controller">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
Expand Down
4 changes: 3 additions & 1 deletion controller/flutter/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.openbot.flutter_controller">
package="org.openbot.app.controller">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<application
android:label="OpenBot Controller"
android:name="${applicationName}"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.openbot.flutter_controller
package org.openbot.app.controller

import io.flutter.embedding.android.FlutterActivity

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.openbot.flutter_controller">
package="org.openbot.app.controller">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
Expand Down
23 changes: 15 additions & 8 deletions controller/flutter/ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
PODS:
- Flutter (1.0.0)
- flutter_webrtc (1.4.0):
- flutter_webrtc (1.6.0):
- Flutter
- WebRTC-SDK (= 144.7559.01)
- WebRTC-SDK (= 144.7559.09)
- fluttertoast (0.0.2):
- Flutter
- nsd_ios (0.0.1):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- sensors_plus (0.0.1):
- Flutter
- video_player_avfoundation (0.0.1):
- Flutter
- FlutterMacOS
- WebRTC-SDK (144.7559.01)
- WebRTC-SDK (144.7559.09)

DEPENDENCIES:
- Flutter (from `Flutter`)
- flutter_webrtc (from `.symlinks/plugins/flutter_webrtc/ios`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- nsd_ios (from `.symlinks/plugins/nsd_ios/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- sensors_plus (from `.symlinks/plugins/sensors_plus/ios`)
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)

Expand All @@ -35,19 +39,22 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/fluttertoast/ios"
nsd_ios:
:path: ".symlinks/plugins/nsd_ios/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
sensors_plus:
:path: ".symlinks/plugins/sensors_plus/ios"
video_player_avfoundation:
:path: ".symlinks/plugins/video_player_avfoundation/darwin"

SPEC CHECKSUMS:
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_webrtc: ec91d94b484ad49cf191ef93413f64a40ffd3b4c
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
flutter_webrtc: f952258e6fa67477d481cfbad2885a4f117922a7
fluttertoast: fe6790210fdba20801685be946e3a2124b72eef5
nsd_ios: 596ad79109ddd3e52d665f650f36428049e3653e
sensors_plus: 6a11ed0c2e1d0bd0b20b4029d3bad27d96e0c65b
video_player_avfoundation: dd410b52df6d2466a42d28550e33e4146928280a
WebRTC-SDK: ab9b5319e458c2bfebdc92b3600740da35d5630d
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
sensors_plus: b70a93b69dafdabd26ce4ac3f4d0529ab9acc4ca
video_player_avfoundation: 3453f792138786248960ca029747fcd9f318ef52
WebRTC-SDK: e6006119cd730d6315d875e4a421b6cc8bb88833

PODFILE CHECKSUM: 0dbd5a87e0ace00c9610d2037ac22083a01f861d

Expand Down
6 changes: 3 additions & 3 deletions controller/flutter/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.openbot.openbotController;
PRODUCT_BUNDLE_IDENTIFIER = org.openbot.app.controller;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
Expand Down Expand Up @@ -496,7 +496,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.openbot.openbotController;
PRODUCT_BUNDLE_IDENTIFIER = org.openbot.app.controller;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
Expand All @@ -520,7 +520,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.openbot.openbotController;
PRODUCT_BUNDLE_IDENTIFIER = org.openbot.app.controller;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading