海康isc平台flutter版视频播放插件

995 阅读3分钟

背景介绍:

基于公司项目需求,需要在flutter项目中播放海康的 iSecure Center 平台视频监控画面,在网上找寻了好久,发现没有合适的视频播放插件,咨询海康官方也没有提供flutter版SDK的支持,所以封装了一下native端的SDK,开发了一个基于海康isc平台SDK的flutter版插件(支持Android和IOS),并发布到dart仓库 iscflutterplugin 有需要的童鞋可以自行使用,使用中有任何问题,可以QQ群交流856941179

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,还可以加交流群,群号 856941179。

最后:

好多小伙伴找不到demo的位置,在这里截图说明一下:

1,我们在yaml文件中添加插件依赖后,会在项目的如下目录中找到该插件 image.png 2,右击打开demo的文件路径 image.png 3,最后用AndroidStudio打开该项目就可以了 image.png