Resolves #370 Camera stream support

This commit is contained in:
Yegor Vialov 2020-02-20 14:33:03 +00:00
parent 2f4c06e9b5
commit 35d8607484
5 changed files with 113 additions and 26 deletions

View File

@ -3,12 +3,16 @@ part of '../../main.dart';
class CameraEntity extends Entity { class CameraEntity extends Entity {
static const SUPPORT_ON_OFF = 1; static const SUPPORT_ON_OFF = 1;
static const SUPPORT_STREAM = 2;
CameraEntity(Map rawData, String webHost) : super(rawData, webHost); CameraEntity(Map rawData, String webHost) : super(rawData, webHost);
bool get supportOnOff => ((supportedFeatures & bool get supportOnOff => ((supportedFeatures &
CameraEntity.SUPPORT_ON_OFF) == CameraEntity.SUPPORT_ON_OFF) ==
CameraEntity.SUPPORT_ON_OFF); CameraEntity.SUPPORT_ON_OFF);
bool get supportStream => ((supportedFeatures &
CameraEntity.SUPPORT_STREAM) ==
CameraEntity.SUPPORT_STREAM);
@override @override
Widget _buildAdditionalControlsForPage(BuildContext context) { Widget _buildAdditionalControlsForPage(BuildContext context) {

View File

@ -10,19 +10,73 @@ class CameraStreamView extends StatefulWidget {
class _CameraStreamViewState extends State<CameraStreamView> { class _CameraStreamViewState extends State<CameraStreamView> {
CameraEntity _entity;
String streamUrl = "";
WebViewController webViewController;
VideoPlayerController videoPlayerController;
Timer monitorTimer;
bool started = false;
double aspectRatio = 1.78;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
} }
CameraEntity _entity; void loadStreamUrl() {
String streamUrl = ""; setState((){
WebViewController webViewController; 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() { void createPlayer(data) {
Launcher.launchURLInCustomTab( videoPlayerController = VideoPlayerController.network("${ConnectionManager().httpWebHost}${data["url"]}");
context: context, videoPlayerController.initialize().then((_) {
url: streamUrl 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<CameraStreamView> {
.of(context) .of(context)
.entityWrapper .entityWrapper
.entity; .entity;
streamUrl = '${ConnectionManager().httpWebHost}/api/camera_proxy_stream/${_entity if (_entity.supportStream && !started) {
.entityId}?token=${_entity.attributes['access_token']}'; loadStreamUrl();
return AspectRatio( return buildLoading();
aspectRatio: 1.33, } else if (_entity.supportStream && started) {
child: WebView( if (videoPlayerController.value.initialized) {
initialUrl: streamUrl, return AspectRatio(
initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, aspectRatio: aspectRatio,
debuggingEnabled: Logger.isInDebugMode, child: VideoPlayer(videoPlayerController),
javascriptMode: JavascriptMode.unrestricted, );
onWebViewCreated: (WebViewController controller) { } else {
webViewController = controller; return buildLoading();
}, }
onPageStarted: (url) { } else {
rootBundle.loadString('assets/js/cameraImgViewHelper.js').then((js){ streamUrl = '${ConnectionManager().httpWebHost}/api/camera_proxy_stream/${_entity
webViewController.evaluateJavascript(js); .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 @override
void dispose() { void dispose() {
monitorTimer?.cancel();
videoPlayerController?.dispose();
super.dispose(); super.dispose();
} }
} }

View File

@ -119,6 +119,17 @@ class HomeAssistant {
return completer.future; 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 { Future _getUserInfo() async {
_userName = null; _userName = null;
await ConnectionManager().sendSocketMessage(type: "auth/current_user").then((data) { await ConnectionManager().sendSocketMessage(type: "auth/current_user").then((data) {

View File

@ -31,6 +31,7 @@ import 'package:battery/battery.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart' as standaloneWebview; import 'package:flutter_webview_plugin/flutter_webview_plugin.dart' as standaloneWebview;
import 'package:webview_flutter/webview_flutter.dart'; import 'package:webview_flutter/webview_flutter.dart';
import 'package:video_player/video_player.dart';
import 'utils/logger.dart'; import 'utils/logger.dart';

View File

@ -31,6 +31,7 @@ dependencies:
workmanager: ^0.2.0 workmanager: ^0.2.0
battery: ^0.3.1+7 battery: ^0.3.1+7
firebase_crashlytics: ^0.1.2+5 firebase_crashlytics: ^0.1.2+5
video_player: ^0.10.7
dev_dependencies: dev_dependencies: