diff --git a/lib/entities/camera/camera_entity.class.dart b/lib/entities/camera/camera_entity.class.dart index 810f04a..6836ab8 100644 --- a/lib/entities/camera/camera_entity.class.dart +++ b/lib/entities/camera/camera_entity.class.dart @@ -3,12 +3,16 @@ part of '../../main.dart'; class CameraEntity extends Entity { static const SUPPORT_ON_OFF = 1; + static const SUPPORT_STREAM = 2; CameraEntity(Map rawData, String webHost) : super(rawData, webHost); bool get supportOnOff => ((supportedFeatures & CameraEntity.SUPPORT_ON_OFF) == CameraEntity.SUPPORT_ON_OFF); + bool get supportStream => ((supportedFeatures & + CameraEntity.SUPPORT_STREAM) == + CameraEntity.SUPPORT_STREAM); @override Widget _buildAdditionalControlsForPage(BuildContext context) { diff --git a/lib/entities/camera/widgets/camera_stream_view.dart b/lib/entities/camera/widgets/camera_stream_view.dart index fe08134..2e51483 100644 --- a/lib/entities/camera/widgets/camera_stream_view.dart +++ b/lib/entities/camera/widgets/camera_stream_view.dart @@ -10,19 +10,73 @@ class CameraStreamView extends StatefulWidget { class _CameraStreamViewState extends State { + CameraEntity _entity; + String streamUrl = ""; + WebViewController webViewController; + VideoPlayerController videoPlayerController; + Timer monitorTimer; + bool started = false; + double aspectRatio = 1.78; + @override void initState() { super.initState(); } - CameraEntity _entity; - String streamUrl = ""; - WebViewController webViewController; + void loadStreamUrl() { + setState((){ + started = true; + }); + Logger.d("[Camera Player] Loading stream url"); + HomeAssistant().getCameraStream(_entity.entityId) + .then((data) { + Logger.d("[Camera Player] Stream url: ${ConnectionManager().httpWebHost}${data["url"]}"); + if (videoPlayerController != null) { + videoPlayerController.dispose().then((_) => createPlayer(data)); + } else { + createPlayer(data); + } + }) + .catchError((e) => Logger.e("[Camera Player] $e")); + } - launchStream() { - Launcher.launchURLInCustomTab( - context: context, - url: streamUrl + void createPlayer(data) { + videoPlayerController = VideoPlayerController.network("${ConnectionManager().httpWebHost}${data["url"]}"); + videoPlayerController.initialize().then((_) { + setState((){ + aspectRatio = videoPlayerController.value.aspectRatio; + }); + autoPlay(); + startMonitor(); + }).catchError((e) { + Logger.e("[Camera Player] Error player init. Retrying"); + loadStreamUrl(); + }); + } + + void autoPlay() { + if (!videoPlayerController.value.isPlaying) { + videoPlayerController.play(); + } + } + + void startMonitor() { + monitorTimer = Timer.periodic(Duration(milliseconds: 500), (timer) { + if (videoPlayerController.value.hasError) { + setState(() { + timer.cancel(); + started = false; + }); + } + }); + } + + Widget buildLoading() { + return AspectRatio( + aspectRatio: aspectRatio, + child: Center( + child: CircularProgressIndicator() + ) ); } @@ -32,29 +86,45 @@ class _CameraStreamViewState extends State { .of(context) .entityWrapper .entity; - streamUrl = '${ConnectionManager().httpWebHost}/api/camera_proxy_stream/${_entity - .entityId}?token=${_entity.attributes['access_token']}'; - return AspectRatio( - aspectRatio: 1.33, - child: WebView( - initialUrl: streamUrl, - initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, - debuggingEnabled: Logger.isInDebugMode, - javascriptMode: JavascriptMode.unrestricted, - onWebViewCreated: (WebViewController controller) { - webViewController = controller; - }, - onPageStarted: (url) { - rootBundle.loadString('assets/js/cameraImgViewHelper.js').then((js){ - webViewController.evaluateJavascript(js); - }); - }, - ), - ); + if (_entity.supportStream && !started) { + loadStreamUrl(); + return buildLoading(); + } else if (_entity.supportStream && started) { + if (videoPlayerController.value.initialized) { + return AspectRatio( + aspectRatio: aspectRatio, + child: VideoPlayer(videoPlayerController), + ); + } else { + return buildLoading(); + } + } else { + streamUrl = '${ConnectionManager().httpWebHost}/api/camera_proxy_stream/${_entity + .entityId}?token=${_entity.attributes['access_token']}'; + return AspectRatio( + aspectRatio: 1.33, + child: WebView( + initialUrl: streamUrl, + initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, + debuggingEnabled: Logger.isInDebugMode, + javascriptMode: JavascriptMode.unrestricted, + onWebViewCreated: (WebViewController controller) { + webViewController = controller; + }, + onPageStarted: (url) { + rootBundle.loadString('assets/js/cameraImgViewHelper.js').then((js){ + webViewController.evaluateJavascript(js); + }); + }, + ), + ); + } } @override void dispose() { + monitorTimer?.cancel(); + videoPlayerController?.dispose(); super.dispose(); } } \ No newline at end of file diff --git a/lib/home_assistant.class.dart b/lib/home_assistant.class.dart index 357856b..0d134a6 100644 --- a/lib/home_assistant.class.dart +++ b/lib/home_assistant.class.dart @@ -119,6 +119,17 @@ class HomeAssistant { return completer.future; } + Future getCameraStream(String entityId) { + Completer completer = Completer(); + + ConnectionManager().sendSocketMessage(type: "camera/stream", additionalData: {"entity_id": entityId}).then((data) { + completer.complete(data); + }).catchError((e) { + completer.completeError(e); + }); + return completer.future; + } + Future _getUserInfo() async { _userName = null; await ConnectionManager().sendSocketMessage(type: "auth/current_user").then((data) { diff --git a/lib/main.dart b/lib/main.dart index cb65dca..3e44c70 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -31,6 +31,7 @@ import 'package:battery/battery.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:flutter_webview_plugin/flutter_webview_plugin.dart' as standaloneWebview; import 'package:webview_flutter/webview_flutter.dart'; +import 'package:video_player/video_player.dart'; import 'utils/logger.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index e49bbd3..ec7c6a7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,6 +31,7 @@ dependencies: workmanager: ^0.2.0 battery: ^0.3.1+7 firebase_crashlytics: ^0.1.2+5 + video_player: ^0.10.7 dev_dependencies: