Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
559 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'dart:io'; | ||
import 'dart:async'; | ||
import 'package:path_provider/path_provider.dart'; | ||
import 'package:camera/camera.dart'; | ||
|
||
const SRV_ADDR = '192.168.43.245:4040'; | ||
|
||
class VideoClientPage extends StatelessWidget { | ||
@override build(context) => Scaffold( | ||
appBar: AppBar(title: Text('Camera')), | ||
body: VideoClientWidget(), | ||
); | ||
} | ||
|
||
class VideoClientWidget extends StatefulWidget { | ||
final String srvAddr; | ||
VideoClientWidget([this.srvAddr = SRV_ADDR]); | ||
@override createState() => _State(); | ||
} | ||
|
||
class _State extends State<VideoClientWidget> { | ||
TextEditingController _srvAddr; | ||
CameraController _camera; | ||
WebSocket _socket; | ||
String _adir; | ||
Timer _timer; | ||
Exception _err; | ||
bool _recording = false; | ||
int _recorded = 0; | ||
|
||
@override initState() { | ||
super.initState(); | ||
() async { | ||
_adir = (await getApplicationDocumentsDirectory()).path; | ||
_srvAddr = TextEditingController(text: widget.srvAddr); | ||
final cams = await availableCameras(); | ||
print('-------\n $cams \n-------\n'); | ||
_camera = CameraController(cams[0], ResolutionPreset.medium); | ||
await _camera.initialize(); | ||
setState((){}); | ||
}(); | ||
} | ||
|
||
Future<void> _startRec() async { | ||
try { | ||
_recorded = _recorded == 1 ? 2 : 1; | ||
await _camera.startVideoRecording('$_adir/0$_recorded.mp4'); | ||
setState((){}); | ||
} catch(e) { setState(() => _err = e); } | ||
} | ||
|
||
Future<void> _stopRec(bool again) async { | ||
try { | ||
await _camera.stopVideoRecording(); | ||
final prevfile = '$_adir/0$_recorded.mp4'; | ||
if (again) await _startRec(); | ||
_socket.add(await File(prevfile).readAsBytes()); | ||
await File(prevfile).delete(); | ||
} catch(e) { setState(() => _err = e); } | ||
} | ||
|
||
Future<void> _startStopRec() async { | ||
_recording = !_recording; | ||
setState(() =>_err = null); | ||
if (_recording) { | ||
try { | ||
_err = null; | ||
_socket = await WebSocket.connect('ws://' + _srvAddr.text); | ||
_fdel('01.mp4'); | ||
_fdel('02.mp4'); | ||
await _startRec(); | ||
_timer = Timer.periodic(Duration(seconds: 3), (_) => _stopRec(true)); | ||
} catch(e) { setState(() => _err = e); } | ||
} else { | ||
_timer.cancel(); | ||
await _stopRec(false); | ||
await _socket.close(); | ||
} | ||
} | ||
|
||
void _fdel(fname) { | ||
try { File('$_adir/$fname').deleteSync(); } catch(_) {} | ||
} | ||
|
||
@override dispose() { | ||
_timer.cancel(); | ||
_socket.close(); | ||
_camera.dispose(); | ||
super.dispose(); | ||
} | ||
|
||
@override build(context) { | ||
if (_err != null) return Center(child: Text(_err.toString())); | ||
if (_camera?.value == null) return Center(child: Text('Activating camera...')); | ||
return Scaffold( | ||
body: Column(children: [ | ||
Row(children: [ | ||
Text(' Server: '), | ||
Expanded(child: TextFormField( | ||
controller: _srvAddr, | ||
decoration: InputDecoration(hintText: 'IP:port'), | ||
//autofocus: true, | ||
)), | ||
_recording ? Text('0$_recorded.mp4') : Container(), | ||
]), | ||
Expanded(child: AspectRatio( | ||
aspectRatio: _camera.value.aspectRatio, | ||
child: CameraPreview(_camera) | ||
)), | ||
]), | ||
floatingActionButton: FloatingActionButton.extended( | ||
label: Text(_recording ? 'STOP' : 'START'), | ||
backgroundColor: _recording ? Colors.red : null, | ||
onPressed: _startStopRec, | ||
), | ||
); | ||
} | ||
} | ||
|
||
/* | ||
child: Padding( | ||
padding: const EdgeInsets.all(8.0), | ||
child: Column( | ||
children: <Widget>[ | ||
TextFormField( | ||
controller: _taskController, | ||
key: ArchSampleKeys.taskField, | ||
style: Theme.of(context).textTheme.headline, | ||
decoration: InputDecoration( | ||
hintText: ArchSampleLocalizations.of(context).newTodoHint, | ||
), | ||
validator: (val) { | ||
return val.trim().isEmpty | ||
? ArchSampleLocalizations.of(context).emptyTodoError | ||
: null; | ||
}, | ||
), | ||
TextFormField( | ||
controller: _noteController, | ||
key: ArchSampleKeys.noteField, | ||
decoration: InputDecoration( | ||
hintText: ArchSampleLocalizations.of(context).notesHint, | ||
), | ||
maxLines: 10, | ||
) | ||
], | ||
), | ||
), | ||
), | ||
floatingActionButton: FloatingActionButton( | ||
key: ArchSampleKeys.saveTodoFab, | ||
tooltip: ArchSampleLocalizations.of(context).saveChanges, | ||
onPressed: () { | ||
if (_formKey.currentState.validate()) { | ||
_formKey.currentState.save(); | ||
widget.onEdit(_taskController.text, _noteController.text); | ||
} | ||
}, | ||
child: const Icon(Icons.check), | ||
), | ||
); | ||
} | ||
} | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'dart:io'; | ||
import 'dart:async'; | ||
import 'package:path_provider/path_provider.dart'; | ||
import 'package:video_player/video_player.dart'; | ||
|
||
const SRV_PORT = 4040; | ||
|
||
class VideoServerPage extends StatelessWidget { | ||
@override build(context) => Scaffold( | ||
appBar: AppBar(title: Text('Recorder')), | ||
body: VideoServerWidget(), | ||
); | ||
} | ||
|
||
class VideoServerWidget extends StatefulWidget { | ||
final int srvPort; | ||
VideoServerWidget([this.srvPort = SRV_PORT]); | ||
@override createState() => _State(); | ||
} | ||
|
||
class _State extends State<VideoServerWidget> { | ||
HttpServer _server; | ||
WebSocket _socket; | ||
VideoPlayerController _player; | ||
String _adir; | ||
Exception _err; | ||
bool _playing = false; | ||
int _received = 0; | ||
int _played = 0; | ||
|
||
@override initState() { | ||
super.initState(); | ||
() async { | ||
try { | ||
_adir = (await getApplicationDocumentsDirectory()).path; | ||
_server = await HttpServer.bind('127.0.0.1', SRV_PORT); | ||
_server.listen((req) async { | ||
try { | ||
_socket = await WebSocketTransformer.upgrade(req); | ||
_socket.listen((msg) async { | ||
try { | ||
await File('$_adir/${_received+1}.mp4').writeAsBytes(msg); | ||
_received++; | ||
if (!_playing) { | ||
if (_played == 0) { | ||
Timer(Duration(seconds: 3), _play); | ||
} else if (_received > _played) { | ||
_play(); | ||
} | ||
} | ||
} catch(e) { setState(() => _err = e); } | ||
}); | ||
} catch(e) { setState(() => _err = e); } | ||
}); | ||
} catch(e) { setState(() => _err = e); } | ||
}(); | ||
} | ||
|
||
Future<void> _play() async { | ||
try { | ||
_played++; | ||
_player = VideoPlayerController.file(File('$_adir/$_played.mp4')); | ||
await _player.initialize(); | ||
await _player.play(); | ||
_player.addListener(() { | ||
if (_player.value.position == _player.value.duration) { | ||
_playing = false; | ||
if (_received > _played) { | ||
_play(); | ||
} else { | ||
setState((){}); | ||
} | ||
} | ||
}); | ||
_playing = true; | ||
setState((){}); | ||
} catch(e) { setState(() => _err = e); } | ||
} | ||
|
||
@override dispose() { | ||
_server.close(force: true); | ||
_player.dispose(); | ||
super.dispose(); | ||
} | ||
|
||
@override build(context) { | ||
if (_err != null) return Center(child: Text(_err.toString())); | ||
if (!_playing) return Center(child: Text('Lisening port $SRV_PORT...')); | ||
return Column(children: [ | ||
Text('$_played.mp4'), | ||
Expanded(child: AspectRatio( | ||
aspectRatio: _player.value.aspectRatio, | ||
child: VideoPlayer(_player), | ||
)) | ||
]); | ||
} | ||
} | ||
|
||
/* | ||
child: Padding( | ||
padding: const EdgeInsets.all(8.0), | ||
child: Column( | ||
children: <Widget>[ | ||
TextFormField( | ||
controller: _taskController, | ||
key: ArchSampleKeys.taskField, | ||
style: Theme.of(context).textTheme.headline, | ||
decoration: InputDecoration( | ||
hintText: ArchSampleLocalizations.of(context).newTodoHint, | ||
), | ||
validator: (val) { | ||
return val.trim().isEmpty | ||
? ArchSampleLocalizations.of(context).emptyTodoError | ||
: null; | ||
}, | ||
), | ||
TextFormField( | ||
controller: _noteController, | ||
key: ArchSampleKeys.noteField, | ||
decoration: InputDecoration( | ||
hintText: ArchSampleLocalizations.of(context).notesHint, | ||
), | ||
maxLines: 10, | ||
) | ||
], | ||
), | ||
), | ||
), | ||
floatingActionButton: FloatingActionButton( | ||
key: ArchSampleKeys.saveTodoFab, | ||
tooltip: ArchSampleLocalizations.of(context).saveChanges, | ||
onPressed: () { | ||
if (_formKey.currentState.validate()) { | ||
_formKey.currentState.save(); | ||
widget.onEdit(_taskController.text, _noteController.text); | ||
} | ||
}, | ||
child: const Icon(Icons.check), | ||
), | ||
); | ||
} | ||
} | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import 'package:flutter/material.dart'; | ||
import './VideoClient.dart'; | ||
import './VideoServer.dart'; | ||
|
||
class VideoTestPage extends StatelessWidget { | ||
@override build(context) => Scaffold( | ||
appBar: AppBar(title: Text('Local test')), | ||
body: Column( | ||
mainAxisAlignment : MainAxisAlignment.spaceEvenly, | ||
children: [ | ||
Expanded(child: VideoClientWidget('127.0.0.1:4040')), | ||
Container(height: 2, color: Colors.blue), | ||
Expanded(child: VideoServerWidget(4040)), | ||
] | ||
), | ||
); | ||
} |
16 changes: 16 additions & 0 deletions
16
zdev/cctv_multicam_flutter/lib/generated_plugin_registrant.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// | ||
// Generated file. Do not edit. | ||
// | ||
|
||
// ignore: unused_import | ||
import 'dart:ui'; | ||
|
||
import 'package:video_player_web/video_player_web.dart'; | ||
|
||
import 'package:flutter_web_plugins/flutter_web_plugins.dart'; | ||
|
||
// ignore: public_member_api_docs | ||
void registerPlugins(PluginRegistry registry) { | ||
VideoPlayerPlugin.registerWith(registry.registrarFor(VideoPlayerPlugin)); | ||
registry.registerMessageHandler(); | ||
} |
Oops, something went wrong.