背景介绍:
基于公司项目需求,需要在flutter项目中播放海康的 iSecure Center 平台视频监控画面,在网上找寻了好久,发现没有合适的视频播放插件,咨询海康官方也没有提供flutter版SDK的支持,所以封装了一下native端的SDK,开发了一个基于海康isc平台SDK的flutter版插件(支持Android和IOS),并发布到dart仓库 iscflutterplugin 有需要的童鞋可以自行使用;
iscflutterplugin使用:
引入依赖:
在pubspec.yaml文件中增加依赖:
dependencies:
iscflutterplugin: (最新版见pub)
运行命令获取依赖:
flutter pub get
在Dart代码中导包:
import 'package:iscflutterplugin/iscflutterplugin.dart';
插件使用起来比较简单方便,大家可以直接参考demo,插件中注释写的非常详细
example:
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:iscflutterplugin/isc_http.dart';
import 'package:iscflutterplugin/isc_player.dart';
import 'package:iscflutterplugin/iscflutterplugin.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Iscflutterplugin _controller;
String _previewUrl;
var cameraCode;
_MyAppState() {
//初始化配置
ArtemisConfig.host = "xxx";
ArtemisConfig.appKey = "xxx";
ArtemisConfig.appSecret = "xxx";
cameraCode = 'xxx';
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('海康isc播放器插件,支持android/ios'),
),
body: Column(
children: <Widget>[
Container(
width: double.infinity,
height: 200,
color: Colors.black,
child: IscPlayerWidget(
onCreated: _onCreated,
),
),
Expanded(
child: SingleChildScrollView(
child: Container(
width: double.infinity,
child: Column(
children: <Widget>[
Container(
width: double.infinity,
child: RaisedButton(
child: Text("预览"), onPressed: _preview)),
Container(
width: double.infinity,
child: RaisedButton(
child: Text("停止预览"), onPressed: _stop)),
Container(
width: double.infinity,
child: RaisedButton(
child: Text("码流平滑切换(仅支持Android)"),
onPressed: _changeStream),
),
Container(
width: double.infinity,
child: RaisedButton(
child: Text("抓拍(需存储权限)"), onPressed: _capture),
),
Container(
width: double.infinity,
child: RaisedButton(
child: Text(
_isRecording ? "结束录像(需存储权限)" : "开始录像(需存储权限)"),
onPressed: _record),
),
Container(
width: double.infinity,
child: RaisedButton(
child: Text("云台"), onPressed: _ptzs)),
Container(
width: double.infinity,
child: RaisedButton(
child: Text("回放"), onPressed: _playBack),
),
Container(
width: double.infinity,
child: RaisedButton(
child: Text("回放指定开始时间位置"), onPressed: _seekTime),
),
],
),
),
),
)
],
),
),
);
}
///创建成功回调
void _onCreated(controller) {
_controller = controller;
}
///停止播放
void _stop() {
_controller.stopPlay();
}
///切换码流
void _changeStream() async {
var ret = await _controller.changeStream(_previewUrl);
print('码流切换 : $ret');
}
/// 回放
void _playBack() async {
String beginTime = '2020-06-01T00:00:00.000+08:00';
String endTime = '2020-06-05T00:00:00.000+08:00';
Map ret = await IscApi.getPlaybackUrl(
cameraIndexCode: cameraCode,
beginTime: beginTime,
endTime: endTime,
recordLocation: 1,
);
String url = ret['data']['url'];
print(ret.toString());
_controller.startPlayback(url, beginTime, endTime);
}
///指定回放开始位置
void _seekTime() async {
String seekTime = '2020-06-04T00:00:00.000+08:00';
_controller.seekAbsPlayback(seekTime);
}
///云台控制
void _ptzs() async {
//开始云台控制
var ret = await IscApi.ptzControl(cameraCode, 0, 'ZOOM_OUT');
print('开始云台控制 : $ret');
sleep(Duration(milliseconds: 100));
//停止云台控制
var ret1 = await IscApi.ptzControl(cameraCode, 1, 'ZOOM_OUT');
print('停止云台控制 : $ret1');
}
var _isRecording = false;
///录像
void _record() async {
String path;
if (defaultTargetPlatform == TargetPlatform.android) {
//申请权限
_requestPermission();
// //图片保存路径
path =
'${(await getExternalStorageDirectory()).path}/${DateTime.now().toString()}.mp4';
}
print('path = $path');
if (_isRecording) {
var ret = await _controller.stopRecord();
if (ret['ret']) {
_isRecording = false;
}
print('结束录像 : $ret');
} else {
var ret = await _controller.startRecord(path);
if (ret['ret']) {
_isRecording = true;
}
print('开始录像 : $ret');
}
setState(() {});
}
///抓拍
void _capture() async {
String path;
//抓拍
if (defaultTargetPlatform == TargetPlatform.android) {
//申请权限
_requestPermission();
//图片保存路径
path =
'${(await getExternalStorageDirectory()).path}/${DateTime.now().toString()}.jpg';
}
var ret = await _controller.capturePicture(path);
print('path = $path');
print('抓拍 : $ret');
}
///预览
void _preview() async {
//获取预览地址
Map ret =
await IscApi.getPreviewURL(cameraIndexCode: cameraCode, version: 1);
_previewUrl = ret['data']['url'];
print('预览地址 = $_previewUrl');
//设置播放器状态回调
_controller.setStatusCallback((status) {
print('播放器状态 = ${_controller.getStatusMessage(status)}');
});
//开始预览
_controller.startRealPlay(_previewUrl);
}
///申请权限
void _requestPermission() async {
if (await Permission.storage.request().isGranted) {
print('已授权');
}
}
}
常见问题:
1,IOS端海康的SDK仅支持真机,不支持模拟器
2,如遇到打包后Android端视频无法播放的情况,可以参照demo中,app/build.gradle文件中的步骤进行配置,主要包括:
- apk用命令行打包时用到的签名配置
- 开启混淆的,注意一定要添加海康SDK的反混淆
- so库过滤
3,还可以加交流群,群号在iscflutterplugin的Readme中。(画外音:简书不让发QQ号)
最后:
好多小伙伴找不到demo的位置,在这里截图说明一下:
1,我们在yaml文件中添加插件依赖后,会在项目的如下目录中找到该插件
2,右击打开demo的文件路径
3,最后用AndroidStudio打开该项目就可以了