Skip to content

Commit

Permalink
HOTFIX: Added fallback to MJPEG video streaming if the app is launche…
Browse files Browse the repository at this point in the history
…d while the car is in motion. MJPEG stream is not as smooth as h264 and has degraded visuals.

Tesla firmware 2022.12.3.2 does not allow the RTCVideoRenderer to start drawing while in motion, however it allows the video stream to flow for a long time after switching from Park to Drive/Reverse.
  • Loading branch information
mikegapinski committed May 7, 2022
1 parent c9ca3b2 commit 85263c3
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 27 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
admin/
pihole/
# Miscellaneous
*.class
*.log
Expand Down
1 change: 1 addition & 0 deletions lib/common/ui/constants/ta_timing.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
class TATiming {
static const splashPageTransitionDuration = Duration(seconds: 2);
static const webRtcTimeoutDuration = Duration(seconds: 5);
static const snackBarDuration = Duration(seconds: 30);
static const animationDuration = Duration(milliseconds: 250);
static const timeoutDuration = Duration(milliseconds: 250);
Expand Down
2 changes: 2 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:tesla_android/common/di/ta_locator.dart';
import 'package:tesla_android/common/navigation/ta_page_factory.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();
await configureTADependencies();
setUrlStrategy(const HashUrlStrategy());
runApp(const TeslaAndroid());
}

Expand Down
79 changes: 52 additions & 27 deletions lib/view/androidViewer/janus/widget/janus_video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import 'package:flavor/flavor.dart';
import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:janus_client/janus_client.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tesla_android/common/di/ta_locator.dart';
import 'package:tesla_android/common/ui/constants/ta_dimens.dart';
import 'package:tesla_android/common/ui/constants/ta_timing.dart';
import 'package:tesla_android/view/iframe/iframe_view.dart';

class JanusVideoPlayer extends StatefulWidget {
final Widget touchScreenView;
Expand All @@ -27,13 +30,18 @@ class _JanusVideoPlayerState extends State<JanusVideoPlayer> {

RTCVideoRenderer? _remoteVideoRenderer;
MediaStream? _remoteVideoStream;
double _videoAspectRatio = 0.75;
bool _isSpinnerVisible = true;
double _videoAspectRatio = 1184 / 922;

int _janusInitRetryCount = 0;
final int _janusMaxRetryCount = 15;
bool _shouldFallbackToMjpeg = false;
bool _didResizeWebRTCStream = false;

@override
void initState() {
super.initState();
_initJanusClient();
_checkIfVideoTagIsWorking();
}

@override
Expand All @@ -45,33 +53,40 @@ class _JanusVideoPlayerState extends State<JanusVideoPlayer> {
@override
Widget build(BuildContext context) {
return Center(
child: AnimatedSwitcher(
duration: TATiming.animationDuration,
child: _isSpinnerVisible
? const CircularProgressIndicator()
: AspectRatio(
aspectRatio: _videoAspectRatio,
child: Stack(
children: [
_remoteVideoRenderer != null
? RTCVideoView(
_remoteVideoRenderer!,
mirror: false,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
)
: const SizedBox(),
widget.touchScreenView,
],
),
),
child: AspectRatio(
aspectRatio: _videoAspectRatio,
child: Stack(
children: [
_remoteVideoRenderer != null && _shouldFallbackToMjpeg == false
? RTCVideoView(
_remoteVideoRenderer!,
mirror: false,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
)
: const SizedBox(),
if (_shouldFallbackToMjpeg) ...[
const IframeView(source: "low-quality-stream.html"),
Positioned(
left: 0,
bottom: 0,
child: Container(
padding: TADimens.halfBasePadding,
color: Colors.red,
child: const Text(
"Low quality mode forced by loading Tesla Android in motion, reload the page while in Park"))),
],
PointerInterceptor(child: widget.touchScreenView),
],
),
),
);
}

void _initJanusClient() async {
if (!mounted) {
if (!mounted || _janusInitRetryCount > _janusMaxRetryCount || _shouldFallbackToMjpeg) {
return;
}
_janusInitRetryCount++;
setState(() {
_webSocketJanusTransport = WebSocketJanusTransport(url: _flavor.getString("janusWebSocket"));
_janusClient = JanusClient(
Expand All @@ -93,6 +108,7 @@ class _JanusVideoPlayerState extends State<JanusVideoPlayer> {
_observeWebSocketState();
_observeJanusPluginVideoTrack();
_observeJanusPluginMessages();
_janusInitRetryCount = 0;
} catch (e) {
await Future.delayed(TATiming.timeoutDuration, _initJanusClient);
}
Expand Down Expand Up @@ -126,6 +142,7 @@ class _JanusVideoPlayerState extends State<JanusVideoPlayer> {
_observeVideoSize();
_remoteVideoStream = temp;
});

await _remoteVideoRenderer?.initialize();
await _remoteVideoStream?.addTrack(event.track!);
_remoteVideoRenderer?.srcObject = _remoteVideoStream;
Expand All @@ -147,25 +164,33 @@ class _JanusVideoPlayerState extends State<JanusVideoPlayer> {
audioRecv: false,
);
_janusPlugin?.send(data: {"request": "start"}, jsep: answer);
}
});
}

void _checkIfVideoTagIsWorking() async {
await Future.delayed(TATiming.webRtcTimeoutDuration, () async {
if (!_didResizeWebRTCStream) {
setState(() {
_isSpinnerVisible = false;
_shouldFallbackToMjpeg = true;
});
_destroyJanus();
}
});
}

void _restartVideoPlayer() {
setState(() {
_isSpinnerVisible = true;
});
_initJanusClient();
}

void _observeVideoSize() {
_remoteVideoRenderer?.onResize = () {
setState(() {
final aspect = _remoteVideoRenderer!.videoWidth / _remoteVideoRenderer!.videoHeight;
_videoAspectRatio = aspect > 0 ? aspect : 0.75;
if (aspect > 0) {
_didResizeWebRTCStream = true;
_videoAspectRatio = aspect;
}
});
};
}
Expand Down
20 changes: 20 additions & 0 deletions web/low-quality-stream.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Player</title>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
}
img {
width: 100%;
}
</style>
</head>
<body>
<img src="http://3.3.3.1:8001/stream"/>
</body>
</html>

0 comments on commit 85263c3

Please sign in to comment.