当前位置:   article > 正文

Flutter下实现低延迟的跨平台RTSP/RTMP播放

flutter rtmp rtsp player

为什么要用Flutter?

Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。

Flutter有哪些与众不同

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

1. Beautiful - Flutter 允许你控制屏幕上的每一寸像素,这让「设计」不用再对「实现」妥协;

2. Fast - 一个应用不卡顿的标准是什么,你可能会说 16ms 抑或是 60fps,这对桌面端应用或者移动端应用来说已足够,但当面对广阔的 AR/VR 领域,60fps 仍然会成为使人脑产生眩晕的瓶颈,而 Flutter 的目标远不止 60fps;借助 Dart 支持的 AOT 编译以及 Skia 的绘制,Flutter 可以运行的很快;

3. Productive - 前端开发可能已经习惯的开发中 hot reload 模式,但这一特性在移动开发中还算是个新鲜事。Flutter 提供有状态的 hot reload 开发模式,并允许一套 codebase 运行于多端;其他的,再比如开发采用 JIT 编译与发布的 AOT 编译,都使得开发者在开发应用时可以更加高效;

4. Open - Dart / Skia / Flutter (Framework),这些都是开源的,Flutter 与 Dart 团队也对包括 Web 在内的多种技术持开放态度,只要是优秀的他们都愿意借鉴吸收。而在生态建设上,Flutter 回应 GitHub Issue 的速度更是让人惊叹,因为是真的快(closed 状态的 issue 平均解决时间为 0.29天);

除了支持APICloud, Unity3d, React Native外,为什么要做Flutter下的RTSP/RTMP播放器

首先,Flutter则是依靠Flutter Engine虚拟机在iOS和Android上运行,开发人员可以通过Flutter框架和API在内部进行交互。Flutter Engine使用C/C++编写,具有低延迟输入和高帧速率的特点,不像Unity3d一样,我们是回调YUV/RGB数据,在Unity3d里面绘制,Flutter直接调用native SDK,效率更高。

其次,客户和开发者驱动,Flutter发展至今,目前还没有个像样的RTSP或RTMP播放器,一个播放器,不是说,有个界面,有个开始、停止按钮就可以了,一个好用的直播播放器,对功能和性能属性要求很高,特别是稳定性和低延迟这块,不谦虚的说,可能是首款功能强大、真正好用的Flutter RTSP/RTMP直播播放SDK


Android和iOS手机上RTSP/RTMP播放效果:

1. 视频播放效果:

http://www.iqiyi.com/w_19s8dv6yht.html

2. 界面截图:

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

上接口:

  1. //
  2. // smartplayer.dart
  3. // smartplayer
  4. //
  5. // GitHub: https://github.com/daniulive/SmarterStreaming
  6. // website: https://www.daniulive.com
  7. //
  8. // Created by daniulive on 2019/02/25.
  9. // Copyright © 2014~2019 daniulive. All rights reserved.
  10. //
  11. import 'dart:async';
  12. import 'dart:convert';
  13. import 'package:flutter/services.dart';
  14. class EVENTID {
  15. static const EVENT_DANIULIVE_COMMON_SDK = 0x00000000;
  16. static const EVENT_DANIULIVE_PLAYER_SDK = 0x01000000;
  17. static const EVENT_DANIULIVE_PUBLISHER_SDK = 0x02000000;
  18. static const EVENT_DANIULIVE_ERC_PLAYER_STARTED =
  19. EVENT_DANIULIVE_PLAYER_SDK | 0x1;
  20. static const EVENT_DANIULIVE_ERC_PLAYER_CONNECTING =
  21. EVENT_DANIULIVE_PLAYER_SDK | 0x2;
  22. static const EVENT_DANIULIVE_ERC_PLAYER_CONNECTION_FAILED =
  23. EVENT_DANIULIVE_PLAYER_SDK | 0x3;
  24. static const EVENT_DANIULIVE_ERC_PLAYER_CONNECTED =
  25. EVENT_DANIULIVE_PLAYER_SDK | 0x4;
  26. static const EVENT_DANIULIVE_ERC_PLAYER_DISCONNECTED =
  27. EVENT_DANIULIVE_PLAYER_SDK | 0x5;
  28. static const EVENT_DANIULIVE_ERC_PLAYER_STOP =
  29. EVENT_DANIULIVE_PLAYER_SDK | 0x6;
  30. static const EVENT_DANIULIVE_ERC_PLAYER_RESOLUTION_INFO =
  31. EVENT_DANIULIVE_PLAYER_SDK | 0x7;
  32. static const EVENT_DANIULIVE_ERC_PLAYER_NO_MEDIADATA_RECEIVED =
  33. EVENT_DANIULIVE_PLAYER_SDK | 0x8;
  34. static const EVENT_DANIULIVE_ERC_PLAYER_SWITCH_URL =
  35. EVENT_DANIULIVE_PLAYER_SDK | 0x9;
  36. static const EVENT_DANIULIVE_ERC_PLAYER_CAPTURE_IMAGE =
  37. EVENT_DANIULIVE_PLAYER_SDK | 0xA;
  38. static const EVENT_DANIULIVE_ERC_PLAYER_RECORDER_START_NEW_FILE =
  39. EVENT_DANIULIVE_PLAYER_SDK | 0x21; /*录像写入新文件*/
  40. static const EVENT_DANIULIVE_ERC_PLAYER_ONE_RECORDER_FILE_FINISHED =
  41. EVENT_DANIULIVE_PLAYER_SDK | 0x22; /*一个录像文件完成*/
  42. static const EVENT_DANIULIVE_ERC_PLAYER_START_BUFFERING =
  43. EVENT_DANIULIVE_PLAYER_SDK | 0x81;
  44. static const EVENT_DANIULIVE_ERC_PLAYER_BUFFERING =
  45. EVENT_DANIULIVE_PLAYER_SDK | 0x82;
  46. static const EVENT_DANIULIVE_ERC_PLAYER_STOP_BUFFERING =
  47. EVENT_DANIULIVE_PLAYER_SDK | 0x83;
  48. static const EVENT_DANIULIVE_ERC_PLAYER_DOWNLOAD_SPEED =
  49. EVENT_DANIULIVE_PLAYER_SDK | 0x91;
  50. }
  51. typedef SmartEventCallback = void Function(int, String, String, String);
  52. class SmartPlayerController {
  53. MethodChannel _channel;
  54. EventChannel _eventChannel;
  55. SmartEventCallback _eventCallback;
  56. void init(int id) {
  57. _channel = MethodChannel('smartplayer_plugin_$id');
  58. _eventChannel = EventChannel('smartplayer_event_$id');
  59. _eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
  60. }
  61. void setEventCallback(SmartEventCallback callback) {
  62. _eventCallback = callback;
  63. }
  64. void _onEvent(Object event) {
  65. if (event != null) {
  66. Map valueMap = json.decode(event);
  67. String param = valueMap['param'];
  68. onSmartEvent(param);
  69. }
  70. }
  71. void _onError(Object error) {
  72. // print('error:'+ error);
  73. }
  74. Future<dynamic> _smartPlayerCall(String funcName) async {
  75. var ret = await _channel.invokeMethod(funcName);
  76. return ret;
  77. }
  78. Future<dynamic> _smartPlayerCallInt(String funcName, int param) async {
  79. var ret = await _channel.invokeMethod(funcName, {
  80. 'intParam': param,
  81. });
  82. return ret;
  83. }
  84. Future<dynamic> _smartPlayerCallIntInt(
  85. String funcName, int param1, int param2) async {
  86. var ret = await _channel.invokeMethod(funcName, {
  87. 'intParam': param1,
  88. 'intParam2': param2,
  89. });
  90. return ret;
  91. }
  92. Future<dynamic> _smartPlayerCallString(String funcName, String param) async {
  93. var ret = await _channel.invokeMethod(funcName, {
  94. 'strParam': param,
  95. });
  96. return ret;
  97. }
  98. /// 设置解码方式 false 软解码 true 硬解码 默认为false
  99. /// </summary>
  100. /// <param name="isHwDecoder"></param>
  101. Future<dynamic> setVideoDecoderMode(int isHwDecoder) async {
  102. return _smartPlayerCallInt('setVideoDecoderMode', isHwDecoder);
  103. }
  104. /// <summary>
  105. /// 设置音频输出模式: if 0: 自动选择; if with 1: audiotrack模式, 此接口仅限于Android平台使用
  106. /// </summary>
  107. /// <param name="use_audiotrack"></param>
  108. Future<dynamic> setAudioOutputType(int useAudiotrack) async {
  109. return _smartPlayerCallInt('setAudioOutputType', useAudiotrack);
  110. }
  111. /// <summary>
  112. /// 设置播放端缓存大小, 默认200毫秒
  113. /// </summary>
  114. /// <param name="buffer"></param>
  115. Future<dynamic> setBuffer(int buffer) async {
  116. return _smartPlayerCallInt('setBuffer', buffer);
  117. }
  118. /// <summary>
  119. /// 接口可实时调用:设置是否实时静音,1:静音; 0: 取消静音
  120. /// </summary>
  121. /// <param name="is_mute"></param>
  122. Future<dynamic> setMute(int isMute) async {
  123. return _smartPlayerCallInt('setMute', isMute);
  124. }
  125. /// <summary>
  126. /// 设置RTSP TCP模式, 1: TCP; 0: UDP
  127. /// </summary>
  128. /// <param name="is_using_tcp"></param>
  129. Future<dynamic> setRTSPTcpMode(int isUsingTcp) async {
  130. return _smartPlayerCallInt('setRTSPTcpMode', isUsingTcp);
  131. }
  132. /// <summary>
  133. /// 设置RTSP超时时间, timeout单位为秒,必须大于0
  134. /// </summary>
  135. /// <param name="timeout"></param>
  136. Future<dynamic> setRTSPTimeout(int timeout) async {
  137. return _smartPlayerCallInt('setRTSPTimeout', timeout);
  138. }
  139. /// <summary>
  140. /// 设置RTSP TCP/UDP自动切换
  141. /// 对于RTSP来说,有些可能支持rtp over udp方式,有些可能支持使用rtp over tcp方式.
  142. /// 为了方便使用,有些场景下可以开启自动尝试切换开关, 打开后如果udp无法播放,sdk会自动尝试tcp, 如果tcp方式播放不了,sdk会自动尝试udp.
  143. /// </summary>
  144. /// <param name="is_auto_switch_tcp_udp"></param>
  145. Future<dynamic> setRTSPAutoSwitchTcpUdp(int is_auto_switch_tcp_udp) async {
  146. return _smartPlayerCallInt('setRTSPAutoSwitchTcpUdp', is_auto_switch_tcp_udp);
  147. }
  148. /// <summary>
  149. /// 设置快速启动该模式,
  150. /// </summary>
  151. /// <param name="is_fast_startup"></param>
  152. Future<dynamic> setFastStartup(int isFastStartup) async {
  153. return _smartPlayerCallInt('setFastStartup', isFastStartup);
  154. }
  155. /// <summary>
  156. /// 设置超低延迟模式 false不开启 true开启 默认false
  157. /// </summary>
  158. /// <param name="mode"></param>
  159. Future<dynamic> setPlayerLowLatencyMode(int mode) async {
  160. return _smartPlayerCallInt('setPlayerLowLatencyMode', mode);
  161. }
  162. /// <summary>
  163. /// 设置视频垂直反转
  164. /// </summary>
  165. /// <param name="is_flip"></param>
  166. Future<dynamic> setFlipVertical(int is_flip) async {
  167. return _smartPlayerCallInt('setFlipVertical', is_flip);
  168. }
  169. /// <summary>
  170. /// 设置视频水平反转
  171. /// </summary>
  172. /// <param name="is_flip"></param>
  173. Future<dynamic> setFlipHorizontal(int is_flip) async {
  174. return _smartPlayerCallInt('setFlipHorizontal', is_flip);
  175. }
  176. /// <summary>
  177. /// 设置顺时针旋转, 注意除了0度之外, 其他角度都会额外消耗性能
  178. /// degress: 当前支持 0度,90度, 180度, 270度 旋转
  179. /// </summary>
  180. /// <param name="degress"></param>
  181. Future<dynamic> setRotation(int degress) async {
  182. return _smartPlayerCallInt('setRotation', degress);
  183. }
  184. /// <summary>
  185. /// 设置是否回调下载速度
  186. /// is_report: if 1: 上报下载速度, 0: 不上报.
  187. /// report_interval: 上报间隔,以秒为单位,>0.
  188. /// </summary>
  189. /// <param name="is_report"></param>
  190. /// <param name="report_interval"></param>
  191. Future<dynamic> setReportDownloadSpeed(
  192. int isReport, int reportInterval) async {
  193. return _smartPlayerCallIntInt(
  194. 'setReportDownloadSpeed', isReport, reportInterval);
  195. }
  196. /// <summary>
  197. /// Set playback orientation(设置播放方向),此接口仅适用于Android平台
  198. /// </summary>
  199. /// <param name="surOrg"></param>
  200. /// surOrg: current orientation, PORTRAIT 1, LANDSCAPE with 2
  201. Future<dynamic> setOrientation(int surOrg) async {
  202. return _smartPlayerCallInt('setOrientation', surOrg);
  203. }
  204. /// <summary>
  205. /// 设置是否需要在播放或录像过程中快照
  206. /// </summary>
  207. /// <param name="is_save_image"></param>
  208. Future<dynamic> setSaveImageFlag(int isSaveImage) async {
  209. return _smartPlayerCallInt('setSaveImageFlag', isSaveImage);
  210. }
  211. /// <summary>
  212. /// 播放或录像过程中快照
  213. /// </summary>
  214. /// <param name="imageName"></param>
  215. Future<dynamic> saveCurImage(String imageName) async {
  216. return _smartPlayerCallString('saveCurImage', imageName);
  217. }
  218. /// <summary>
  219. /// 播放或录像过程中,快速切换url
  220. /// </summary>
  221. /// <param name="uri"></param>
  222. Future<dynamic> switchPlaybackUrl(String uri) async {
  223. return _smartPlayerCallString('switchPlaybackUrl', uri);
  224. }
  225. /// <summary>
  226. /// 创建录像存储路径
  227. /// </summary>
  228. /// <param name="path"></param>
  229. Future<dynamic> createFileDirectory(String path) async {
  230. return _smartPlayerCallString('createFileDirectory', path);
  231. }
  232. /// <summary>
  233. /// 设置录像存储路径
  234. /// </summary>
  235. /// <param name="path"></param>
  236. Future<dynamic> setRecorderDirectory(String path) async {
  237. return _smartPlayerCallString('setRecorderDirectory', path);
  238. }
  239. /// <summary>
  240. /// 设置单个录像文件大小
  241. /// </summary>
  242. /// <param name="size"></param>
  243. Future<dynamic> setRecorderFileMaxSize(int size) async {
  244. return _smartPlayerCallInt('setRecorderFileMaxSize', size);
  245. }
  246. /// <summary>
  247. /// 设置录像时音频转AAC编码的开关
  248. /// aac比较通用,sdk增加其他音频编码(比如speex, pcmu, pcma等)转aac的功能.
  249. /// </summary>
  250. /// <param name="is_transcode"></param>
  251. /// is_transcode: 设置为1的话,如果音频编码不是aac,则转成aac,如果是aac,则不做转换. 设置为0的话,则不做任何转换. 默认是0.
  252. Future<dynamic> setRecorderAudioTranscodeAAC(int is_transcode) async {
  253. return _smartPlayerCallInt('setRecorderAudioTranscodeAAC', is_transcode);
  254. }
  255. /// <summary>
  256. /// 设置播放路径
  257. /// </summary>
  258. Future<dynamic> setUrl(String url) async {
  259. return _smartPlayerCallString('setUrl', url);
  260. }
  261. /// <summary>
  262. /// 开始播放
  263. /// </summary>
  264. Future<dynamic> startPlay() async {
  265. return _smartPlayerCall('startPlay');
  266. }
  267. /// <summary>
  268. /// 停止播放
  269. /// </summary>
  270. Future<dynamic> stopPlay() async {
  271. return _smartPlayerCall('stopPlay');
  272. }
  273. /// <summary>
  274. /// 开始录像
  275. /// </summary>
  276. Future<dynamic> startRecorder() async {
  277. return _smartPlayerCall('startRecorder');
  278. }
  279. /// <summary>
  280. /// 停止录像
  281. /// </summary>
  282. Future<dynamic> stopRecorder() async {
  283. return _smartPlayerCall('stopRecorder');
  284. }
  285. /// <summary>
  286. /// 关闭播放
  287. /// </summary>
  288. Future<dynamic> dispose() async {
  289. return await _channel.invokeMethod('dispose');
  290. }
  291. void onSmartEvent(String param) {
  292. if (!param.contains(",")) {
  293. print("[onNTSmartEvent] android传递参数错误");
  294. return;
  295. }
  296. List<String> strs = param.split(',');
  297. String code = strs[1];
  298. String param1 = strs[2];
  299. String param2 = strs[3];
  300. String param3 = strs[4];
  301. String param4 = strs[5];
  302. int evCode = int.parse(code);
  303. var p1, p2, p3;
  304. switch (evCode) {
  305. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STARTED:
  306. print("开始。。");
  307. break;
  308. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTING:
  309. print("连接中。。");
  310. break;
  311. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTION_FAILED:
  312. print("连接失败。。");
  313. break;
  314. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTED:
  315. print("连接成功。。");
  316. break;
  317. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_DISCONNECTED:
  318. print("连接断开。。");
  319. break;
  320. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STOP:
  321. print("停止播放。。");
  322. break;
  323. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_RESOLUTION_INFO:
  324. print("分辨率信息: width: " + param1 + ", height: " + param2);
  325. p1 = param1;
  326. p2 = param2;
  327. break;
  328. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_NO_MEDIADATA_RECEIVED:
  329. print("收不到媒体数据,可能是url错误。。");
  330. break;
  331. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_SWITCH_URL:
  332. print("切换播放URL。。");
  333. break;
  334. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CAPTURE_IMAGE:
  335. print("快照: " + param1 + " 路径:" + param3);
  336. if (int.parse(param1) == 0) {
  337. print("截取快照成功。.");
  338. } else {
  339. print("截取快照失败。.");
  340. }
  341. p1 = param1;
  342. p2 = param3;
  343. break;
  344. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_RECORDER_START_NEW_FILE:
  345. print("[record]开始一个新的录像文件 : " + param3);
  346. p3 = param3;
  347. break;
  348. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_ONE_RECORDER_FILE_FINISHED:
  349. print("[record]已生成一个录像文件 : " + param3);
  350. p3 = param3;
  351. break;
  352. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_START_BUFFERING:
  353. print("Start_Buffering");
  354. break;
  355. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_BUFFERING:
  356. print("Buffering: " + param1 + "%");
  357. p1 = param1;
  358. break;
  359. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STOP_BUFFERING:
  360. print("Stop_Buffering");
  361. break;
  362. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_DOWNLOAD_SPEED:
  363. print("download_speed:" + (double.parse(param1) * 8 / 1000).toStringAsFixed(0) + "kbps" + ", " + (double.parse(param1) / 1024).toStringAsFixed(0) + "KB/s");
  364. p1 = param1;
  365. break;
  366. }
  367. if (_eventCallback != null) {
  368. _eventCallback(evCode, p1, p2, p3);
  369. }
  370. }
  371. }
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

调用实例:

  1. //
  2. // main.dart
  3. // main
  4. //
  5. // GitHub: https://github.com/daniulive/SmarterStreaming
  6. // website: https://www.daniulive.com
  7. //
  8. // Created by daniulive on 2019/02/25.
  9. // Copyright © 2014~2019 daniulive. All rights reserved.
  10. //
  11. import 'dart:io';
  12. import 'package:flutter/services.dart';
  13. import 'package:flutter/material.dart';
  14. import 'package:flutter/cupertino.dart';
  15. import 'package:flutter/foundation.dart';
  16. import 'package:smartplayer_native_view/smartplayer.dart';
  17. import 'package:smartplayer_native_view/smartplayer_plugin.dart';
  18. void main() {
  19. ///
  20. /// 强制竖屏
  21. ///
  22. SystemChrome.setPreferredOrientations(
  23. [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
  24. runApp(new MyApp());
  25. }
  26. class MyApp extends StatefulWidget {
  27. @override
  28. _MyAppState createState() => new _MyAppState();
  29. }
  30. class _MyAppState extends State<MyApp> {
  31. SmartPlayerController player;
  32. double aspectRatio = 4.0 / 3.0;
  33. //输入需要播放的RTMP/RTSP url
  34. TextEditingController playback_url_controller_ = TextEditingController();
  35. //Event事件回调显示
  36. TextEditingController event_controller_ = TextEditingController();
  37. bool is_playing_ = false;
  38. bool is_mute_ = false;
  39. var rotate_degrees_ = 0;
  40. Widget smartPlayerView() {
  41. return SmartPlayerWidget(
  42. onSmartPlayerCreated: onSmartPlayerCreated,
  43. );
  44. }
  45. @override
  46. void initState() {
  47. print("initState called..");
  48. super.initState();
  49. }
  50. @override
  51. void didChangeDependencies() {
  52. print('didChangeDependencies called..');
  53. super.didChangeDependencies();
  54. }
  55. @override
  56. void deactivate() {
  57. print('deactivate called..');
  58. super.deactivate();
  59. }
  60. @override
  61. void dispose() {
  62. print("dispose called..");
  63. player.dispose();
  64. super.dispose();
  65. }
  66. @override
  67. Widget build(BuildContext context) {
  68. return MaterialApp(
  69. home: Scaffold(
  70. appBar: AppBar(
  71. title: const Text('Flutter SmartPlayer Demo'),
  72. ),
  73. body: new SingleChildScrollView(
  74. child: new Column(
  75. children: <Widget>[
  76. new Container(
  77. color: Colors.black,
  78. child: AspectRatio(
  79. child: smartPlayerView(),
  80. aspectRatio: aspectRatio,
  81. ),
  82. ),
  83. new TextField(
  84. controller: playback_url_controller_,
  85. keyboardType: TextInputType.text,
  86. decoration: InputDecoration(
  87. contentPadding: EdgeInsets.all(10.0),
  88. icon: Icon(Icons.link),
  89. labelText: '请输入RTSP/RTMP url',
  90. ),
  91. autofocus: false,
  92. ),
  93. new Row(
  94. children: [
  95. new RaisedButton(
  96. onPressed: this.onSmartPlayerStartPlay,
  97. child: new Text("开始播放")),
  98. new Container(width: 20),
  99. new RaisedButton(
  100. onPressed: this.onSmartPlayerStopPlay,
  101. child: new Text("停止播放")),
  102. new Container(width: 20),
  103. new RaisedButton(
  104. onPressed: this.onSmartPlayerMute,
  105. child: new Text("实时静音")),
  106. ],
  107. ),
  108. new Row(
  109. children: [
  110. new RaisedButton(
  111. onPressed: this.onSmartPlayerSwitchUrl,
  112. child: new Text("实时切换URL")),
  113. new Container(width: 20),
  114. new RaisedButton(
  115. onPressed: this.onSmartPlayerSetRotation,
  116. child: new Text("实时旋转View")),
  117. ],
  118. ),
  119. new TextField(
  120. controller: event_controller_,
  121. keyboardType: TextInputType.text,
  122. decoration: InputDecoration(
  123. contentPadding: EdgeInsets.all(10.0),
  124. icon: Icon(Icons.event_note),
  125. labelText: 'Event状态回调',
  126. ),
  127. autofocus: false,
  128. ),
  129. ],
  130. ),
  131. )),
  132. );
  133. }
  134. void _eventCallback(int code, String param1, String param2, String param3) {
  135. String event_str;
  136. switch (code) {
  137. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STARTED:
  138. event_str = "开始..";
  139. break;
  140. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTING:
  141. event_str = "连接中..";
  142. break;
  143. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTION_FAILED:
  144. event_str = "连接失败..";
  145. break;
  146. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTED:
  147. event_str = "连接成功..";
  148. break;
  149. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_DISCONNECTED:
  150. event_str = "连接断开..";
  151. break;
  152. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STOP:
  153. event_str = "停止播放..";
  154. break;
  155. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_RESOLUTION_INFO:
  156. event_str = "分辨率信息: width: " + param1 + ", height: " + param2;
  157. setState(() {
  158. aspectRatio = double.parse(param1) / double.parse(param2);
  159. print('change aspectRatio:$aspectRatio');
  160. });
  161. break;
  162. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_NO_MEDIADATA_RECEIVED:
  163. event_str = "收不到媒体数据,可能是url错误..";
  164. break;
  165. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_SWITCH_URL:
  166. event_str = "切换播放URL..";
  167. break;
  168. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CAPTURE_IMAGE:
  169. event_str = "快照: " + param1 + " 路径: " + param3;
  170. if (int.parse(param1) == 0) {
  171. print("截取快照成功。.");
  172. } else {
  173. print("截取快照失败。.");
  174. }
  175. break;
  176. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_RECORDER_START_NEW_FILE:
  177. event_str = "[record] new file: " + param3;
  178. break;
  179. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_ONE_RECORDER_FILE_FINISHED:
  180. event_str = "[record] record finished: " + param3;
  181. break;
  182. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_START_BUFFERING:
  183. //event_str = "Start Buffering";
  184. break;
  185. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_BUFFERING:
  186. event_str = "Buffering: " + param1 + "%";
  187. break;
  188. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STOP_BUFFERING:
  189. //event_str = "Stop Buffering";
  190. break;
  191. case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_DOWNLOAD_SPEED:
  192. event_str = "download_speed:" +
  193. (double.parse(param1) * 8 / 1000).toStringAsFixed(0) +
  194. "kbps" +
  195. ", " +
  196. (double.parse(param1) / 1024).toStringAsFixed(0) +
  197. "KB/s";
  198. break;
  199. }
  200. event_controller_.text = event_str;
  201. }
  202. void onSmartPlayerCreated(SmartPlayerController controller) async {
  203. player = controller;
  204. player.setEventCallback(_eventCallback);
  205. var ret = -1;
  206. //设置video decoder模式
  207. var is_video_hw_decoder = 0;
  208. if (defaultTargetPlatform == TargetPlatform.android)
  209. {
  210. ret = await player.setVideoDecoderMode(is_video_hw_decoder);
  211. }
  212. else if(defaultTargetPlatform == TargetPlatform.iOS)
  213. {
  214. is_video_hw_decoder = 1;
  215. ret = await player.setVideoDecoderMode(is_video_hw_decoder);
  216. }
  217. //设置缓冲时间
  218. var play_buffer = 100;
  219. ret = await player.setBuffer(play_buffer);
  220. //设置快速启动
  221. var is_fast_startup = 1;
  222. ret = await player.setFastStartup(is_fast_startup);
  223. //是否开启低延迟模式
  224. var is_low_latency_mode = 0;
  225. ret = await player.setPlayerLowLatencyMode(is_low_latency_mode);
  226. //set report download speed(默认5秒一次回调 用户可自行调整report间隔)
  227. ret = await player.setReportDownloadSpeed(1, 2);
  228. //设置RTSP超时时间
  229. var rtsp_timeout = 10;
  230. ret = await player.setRTSPTimeout(rtsp_timeout);
  231. var is_auto_switch_tcp_udp = 1;
  232. ret = await player.setRTSPAutoSwitchTcpUdp(is_auto_switch_tcp_udp);
  233. // 设置RTSP TCP模式
  234. //ret = await player.setRTSPTcpMode(1);
  235. //第一次启动 为方便测试 设置个初始url
  236. playback_url_controller_.text = "rtmp://live.hkstv.hk.lxdns.com/live/hks2";
  237. }
  238. Future<void> onSmartPlayerStartPlay() async {
  239. var ret = -1;
  240. if (playback_url_controller_.text.length < 8) {
  241. playback_url_controller_.text =
  242. "rtmp://live.hkstv.hk.lxdns.com/live/hks1"; //给个初始url
  243. }
  244. //实时静音设置
  245. ret = await player.setMute(is_mute_ ? 1 : 0);
  246. if (!is_playing_) {
  247. ret = await player.setUrl(playback_url_controller_.text);
  248. ret = await player.startPlay();
  249. if (ret == 0) {
  250. is_playing_ = true;
  251. }
  252. }
  253. }
  254. Future<void> onSmartPlayerStopPlay() async {
  255. if (is_playing_) {
  256. await player.stopPlay();
  257. playback_url_controller_.clear();
  258. is_playing_ = false;
  259. is_mute_ = false;
  260. }
  261. }
  262. Future<void> onSmartPlayerMute() async {
  263. if (is_playing_) {
  264. is_mute_ = !is_mute_;
  265. await player.setMute(is_mute_ ? 1 : 0);
  266. }
  267. }
  268. Future<void> onSmartPlayerSwitchUrl() async {
  269. if (is_playing_) {
  270. if (playback_url_controller_.text.length < 8) {
  271. playback_url_controller_.text =
  272. "rtmp://live.hkstv.hk.lxdns.com/live/hks1";
  273. }
  274. await player.switchPlaybackUrl(playback_url_controller_.text);
  275. }
  276. }
  277. Future<void> onSmartPlayerSetRotation() async {
  278. if (is_playing_) {
  279. rotate_degrees_ += 90;
  280. rotate_degrees_ = rotate_degrees_ % 360;
  281. if (0 == rotate_degrees_) {
  282. print("旋转90度");
  283. } else if (90 == rotate_degrees_) {
  284. print("旋转180度");
  285. } else if (180 == rotate_degrees_) {
  286. print("旋转270度");
  287. } else if (270 == rotate_degrees_) {
  288. print("不旋转");
  289. }
  290. await player.setRotation(rotate_degrees_);
  291. }
  292. }
  293. }
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==


经测试,Flutter环境下,RTMP和RTSP播放,拥有Native SDK一样优异的播放体验。

相关资料:Github: https://github.com/daniulive/SmarterStreaming


 
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/194913
推荐阅读
相关标签
  

闽ICP备14008679号