177 lines
4.7 KiB
Dart
177 lines
4.7 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
import 'dart:isolate';
|
|
|
|
import 'package:file_picker/file_picker.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_ffmpeg/stream_information.dart';
|
|
import 'package:flutter_opencv_example/native_opencv.dart';
|
|
import 'package:path_provider/path_provider.dart';
|
|
import 'package:flutter_ffmpeg/flutter_ffmpeg.dart';
|
|
|
|
const title = 'Native OpenCV Example';
|
|
|
|
late Directory tempDir;
|
|
|
|
String get dataPath => '${tempDir.path}/data.csv';
|
|
|
|
void main() {
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
getTemporaryDirectory().then((dir) => tempDir = dir);
|
|
|
|
runApp(MyApp());
|
|
}
|
|
|
|
class MyApp extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return MaterialApp(
|
|
title: title,
|
|
theme: ThemeData(
|
|
primarySwatch: Colors.blue,
|
|
),
|
|
home: MyHomePage(),
|
|
);
|
|
}
|
|
}
|
|
|
|
class MyHomePage extends StatefulWidget {
|
|
@override
|
|
_MyHomePageState createState() => _MyHomePageState();
|
|
}
|
|
|
|
class _MyHomePageState extends State<MyHomePage> {
|
|
bool _isProcessed = false;
|
|
bool _isWorking = false;
|
|
|
|
void showVersion() {
|
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
|
final snackbar = SnackBar(
|
|
content: Text('OpenCV version: ${opencvVersion()}'),
|
|
);
|
|
|
|
scaffoldMessenger
|
|
..removeCurrentSnackBar(reason: SnackBarClosedReason.dismiss)
|
|
..showSnackBar(snackbar);
|
|
}
|
|
|
|
Future<String?> pickAnImage() async {
|
|
if (Platform.isIOS || Platform.isAndroid) {
|
|
return FilePicker.platform
|
|
.pickFiles(
|
|
dialogTitle: 'Pick an image',
|
|
type: FileType.video,
|
|
allowMultiple: false,
|
|
)
|
|
.then((v) => v?.files.first.path);
|
|
} else {
|
|
return FilePicker.platform
|
|
.pickFiles(
|
|
dialogTitle: 'Pick an image',
|
|
type: FileType.image,
|
|
allowMultiple: false,
|
|
)
|
|
.then((v) => v?.files.first.path);
|
|
}
|
|
}
|
|
|
|
Future<void> takeImageAndProcess() async {
|
|
final imagePath = await pickAnImage();
|
|
final FlutterFFprobe _flutterFFmpeg = FlutterFFprobe();
|
|
|
|
if (imagePath == null) {
|
|
return;
|
|
}
|
|
|
|
double? frameRate;
|
|
var vinfo = await _flutterFFmpeg.getMediaInformation(imagePath);
|
|
List<StreamInformation>? streams = vinfo.getStreams();
|
|
if (streams!.length > 0) {
|
|
for (var stream in streams) {
|
|
if (stream.getAllProperties()['codec_type'] == "video") {
|
|
frameRate = int.parse(
|
|
stream.getAllProperties()['avg_frame_rate'].split("/")[0]) /
|
|
int.parse(
|
|
stream.getAllProperties()['avg_frame_rate'].split("/")[1]);
|
|
}
|
|
}
|
|
}
|
|
setState(() {
|
|
_isWorking = true;
|
|
});
|
|
|
|
// Creating a port for communication with isolate and arguments for entry point
|
|
final port = ReceivePort();
|
|
final args = ProcessImageArguments(imagePath, dataPath, frameRate ?? 0.0);
|
|
|
|
// Spawning an isolate
|
|
Isolate.spawn<ProcessImageArguments>(
|
|
processImage,
|
|
args,
|
|
onError: port.sendPort,
|
|
onExit: port.sendPort,
|
|
);
|
|
|
|
// Making a variable to store a subscription in
|
|
late StreamSubscription sub;
|
|
|
|
// Listening for messages on port
|
|
sub = port.listen((_) async {
|
|
// Cancel a subscription after message received called
|
|
await sub.cancel();
|
|
|
|
setState(() {
|
|
_isProcessed = true;
|
|
_isWorking = false;
|
|
});
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(title: Text(title)),
|
|
body: Stack(
|
|
children: <Widget>[
|
|
Center(
|
|
child: ListView(
|
|
shrinkWrap: true,
|
|
children: <Widget>[
|
|
if (_isProcessed && !_isWorking)
|
|
ConstrainedBox(
|
|
constraints: BoxConstraints(maxWidth: 3000, maxHeight: 300),
|
|
// child: Image.file(
|
|
// File(tempPath),
|
|
// alignment: Alignment.center,
|
|
// ),
|
|
),
|
|
Column(
|
|
children: [
|
|
ElevatedButton(
|
|
child: Text('Show version'),
|
|
onPressed: showVersion,
|
|
),
|
|
ElevatedButton(
|
|
child: Text('Process photo'),
|
|
onPressed: takeImageAndProcess,
|
|
),
|
|
],
|
|
)
|
|
],
|
|
),
|
|
),
|
|
if (_isWorking)
|
|
Positioned.fill(
|
|
child: Container(
|
|
color: Colors.black.withOpacity(.7),
|
|
child: Center(
|
|
child: CircularProgressIndicator(),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|