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

为什么要用Flutter?

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

Flutter有哪些与众不同

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. 界面截图:

RTMP直播播放器功能介绍:

  • [支持播放协议]高稳定、超低延迟(一秒内,行业内几无效果接近的播放端)、业内首屈一指的RTMP直播播放器SDK;
  •  [多实例播放]支持多实例播放;
  •  [事件回调]支持网络状态、buffer状态等回调;
  •  [视频格式]支持RTMP扩展H.265,H.264;
  •  [音频格式]支持AAC/PCMA/PCMU/Speex;
  •  [H.264/H.265软解码]支持H.264/H.265软解;
  •  [H.264硬解码]Android/iOS支持H.264硬解;
  •  [H.265硬解]Android/iOS支持H.265硬解;
  •  [H.264/H.265硬解码]Android支持设置Surface模式硬解和普通模式硬解码;
  •  [缓冲时间设置]支持buffer time设置;
  •  [首屏秒开]支持首屏秒开模式;
  •  [低延迟模式]支持类似于线上娃娃机等直播方案的超低延迟模式设置(公网200~400ms);
  •  [复杂网络处理]支持断网重连等各种网络环境自动适配;
  •  [快速切换URL]支持播放过程中,快速切换其他URL,内容切换更快;
  •  [音视频多种render机制]Android平台,视频:surfaceview/OpenGL ES,音频:AudioTrack/OpenSL ES;
  •  [实时静音]支持播放过程中,实时静音/取消静音;
  •  [实时快照]支持播放过程中截取当前播放画面;
  •  [渲染角度]支持0°,90°,180°和270°四个视频画面渲染角度设置;
  •  [渲染镜像]支持水平反转、垂直反转模式设置;
  •  [实时下载速度更新]支持当前下载速度实时回调(支持设置回调时间间隔);
  •  [解码前视频数据回调]支持H.264/H.265数据回调;
  •  [解码后视频数据回调]支持解码后YUV/RGB数据回调;
  •  [解码前音频数据回调]支持AAC/PCMA/PCMU/SPEEX数据回调;
  •  [音视频自适应]支持播放过程中,音视频信息改变后自适应;
  •  [扩展录像功能]完美支持和录像SDK组合使用(支持RTMP扩展H.265流录制,支持PCMA/PCMU/Speex转AAC后录制,支持设置只录制音频或视频)

RTSP直播播放器功能介绍:

  •  [支持播放协议]高稳定、超低延迟、业内首屈一指的RTSP直播播放器SDK;
  •  [多实例播放]支持多实例播放;
  •  [事件回调]支持网络状态、buffer状态等回调;
  •  [视频格式]支持H.265、H.264,此外,Windows/Android平台还支持RTSP MJPEG播放;
  •  [音频格式]支持AAC/PCMA/PCMU;
  •  [H.264/H.265软解码]支持H.264/H.265软解;
  •  [H.264硬解码]Android/iOS支持H.264硬解;
  •  [H.265硬解]Android/iOS支持H.265硬解;
  •  [H.264/H.265硬解码]Android支持设置Surface模式硬解和普通模式硬解码;
  •  [RTSP模式设置]支持RTSP TCP/UDP模式设置;
  •  [RTSP TCP/UDP自动切换]支持RTSP TCP、UDP模式自动切换;
  •  [RTSP超时设置]支持RTSP超时时间设置,单位:秒;
  •  [RTSP 401认证处理]支持上报RTSP 401事件,如URL携带鉴权信息,会自动处理;
  •  [缓冲时间设置]支持buffer time设置;
  •  [首屏秒开]支持首屏秒开模式;
  •  [复杂网络处理]支持断网重连等各种网络环境自动适配;
  •  [快速切换URL]支持播放过程中,快速切换其他URL,内容切换更快;
  •  [音视频多种render机制]Android平台,视频:surfaceview/OpenGL ES,音频:AudioTrack/OpenSL ES;
  •  [实时静音]支持播放过程中,实时静音/取消静音;
  •  [实时快照]支持播放过程中截取当前播放画面;
  •  [渲染角度]支持0°,90°,180°和270°四个视频画面渲染角度设置;
  •  [渲染镜像]支持水平反转、垂直反转模式设置;
  •  [实时下载速度更新]支持当前下载速度实时回调(支持设置回调时间间隔);
  •  [解码前视频数据回调]支持H.264/H.265数据回调;
  •  [解码后视频数据回调]支持解码后YUV/RGB数据回调;
  •  [解码前音频数据回调]支持AAC/PCMA/PCMU/SPEEX数据回调;
  •  [音视频自适应]支持播放过程中,音视频信息改变后自适应;
  •  [扩展录像功能]完美支持和录像SDK组合使用(支持RTSP H.265流录制,支持PCMA/PCMU转AAC后录制,支持设置只录制音频或视频)

上接口:

//
//  smartplayer.dart
//  smartplayer
//
//  GitHub: https://github.com/daniulive/SmarterStreaming
//  website: https://www.daniulive.com
//
//  Created by daniulive on 2019/02/25.
//  Copyright © 2014~2019 daniulive. All rights reserved.
//import 'dart:async';
import 'dart:convert';import 'package:flutter/services.dart';class EVENTID {static const EVENT_DANIULIVE_COMMON_SDK = 0x00000000;static const EVENT_DANIULIVE_PLAYER_SDK = 0x01000000;static const EVENT_DANIULIVE_PUBLISHER_SDK = 0x02000000;static const EVENT_DANIULIVE_ERC_PLAYER_STARTED =EVENT_DANIULIVE_PLAYER_SDK | 0x1;static const EVENT_DANIULIVE_ERC_PLAYER_CONNECTING =EVENT_DANIULIVE_PLAYER_SDK | 0x2;static const EVENT_DANIULIVE_ERC_PLAYER_CONNECTION_FAILED =EVENT_DANIULIVE_PLAYER_SDK | 0x3;static const EVENT_DANIULIVE_ERC_PLAYER_CONNECTED =EVENT_DANIULIVE_PLAYER_SDK | 0x4;static const EVENT_DANIULIVE_ERC_PLAYER_DISCONNECTED =EVENT_DANIULIVE_PLAYER_SDK | 0x5;static const EVENT_DANIULIVE_ERC_PLAYER_STOP =EVENT_DANIULIVE_PLAYER_SDK | 0x6;static const EVENT_DANIULIVE_ERC_PLAYER_RESOLUTION_INFO =EVENT_DANIULIVE_PLAYER_SDK | 0x7;static const EVENT_DANIULIVE_ERC_PLAYER_NO_MEDIADATA_RECEIVED =EVENT_DANIULIVE_PLAYER_SDK | 0x8;static const EVENT_DANIULIVE_ERC_PLAYER_SWITCH_URL =EVENT_DANIULIVE_PLAYER_SDK | 0x9;static const EVENT_DANIULIVE_ERC_PLAYER_CAPTURE_IMAGE =EVENT_DANIULIVE_PLAYER_SDK | 0xA;static const EVENT_DANIULIVE_ERC_PLAYER_RECORDER_START_NEW_FILE =EVENT_DANIULIVE_PLAYER_SDK | 0x21; /*录像写入新文件*/static const EVENT_DANIULIVE_ERC_PLAYER_ONE_RECORDER_FILE_FINISHED =EVENT_DANIULIVE_PLAYER_SDK | 0x22; /*一个录像文件完成*/static const EVENT_DANIULIVE_ERC_PLAYER_START_BUFFERING =EVENT_DANIULIVE_PLAYER_SDK | 0x81;static const EVENT_DANIULIVE_ERC_PLAYER_BUFFERING =EVENT_DANIULIVE_PLAYER_SDK | 0x82;static const EVENT_DANIULIVE_ERC_PLAYER_STOP_BUFFERING =EVENT_DANIULIVE_PLAYER_SDK | 0x83;static const EVENT_DANIULIVE_ERC_PLAYER_DOWNLOAD_SPEED =EVENT_DANIULIVE_PLAYER_SDK | 0x91;
}typedef SmartEventCallback = void Function(int, String, String, String);class SmartPlayerController {MethodChannel _channel;EventChannel _eventChannel;SmartEventCallback _eventCallback;void init(int id) {_channel = MethodChannel('smartplayer_plugin_$id');_eventChannel = EventChannel('smartplayer_event_$id');_eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);}void setEventCallback(SmartEventCallback callback) {_eventCallback = callback;}void _onEvent(Object event) {if (event != null) {Map valueMap = json.decode(event);String param = valueMap['param'];onSmartEvent(param);}}void _onError(Object error) {// print('error:'+ error);}Future<dynamic> _smartPlayerCall(String funcName) async {var ret = await _channel.invokeMethod(funcName);return ret;}Future<dynamic> _smartPlayerCallInt(String funcName, int param) async {var ret = await _channel.invokeMethod(funcName, {'intParam': param,});return ret;}Future<dynamic> _smartPlayerCallIntInt(String funcName, int param1, int param2) async {var ret = await _channel.invokeMethod(funcName, {'intParam': param1,'intParam2': param2,});return ret;}Future<dynamic> _smartPlayerCallString(String funcName, String param) async {var ret = await _channel.invokeMethod(funcName, {'strParam': param,});return ret;}/// 设置解码方式 false 软解码 true 硬解码 默认为false/// </summary>/// <param name="isHwDecoder"></param>Future<dynamic> setVideoDecoderMode(int isHwDecoder) async {return _smartPlayerCallInt('setVideoDecoderMode', isHwDecoder);}/// <summary>/// 设置音频输出模式: if 0: 自动选择; if with 1: audiotrack模式, 此接口仅限于Android平台使用/// </summary>/// <param name="use_audiotrack"></param>Future<dynamic> setAudioOutputType(int useAudiotrack) async {return _smartPlayerCallInt('setAudioOutputType', useAudiotrack);}/// <summary>/// 设置播放端缓存大小, 默认200毫秒/// </summary>/// <param name="buffer"></param>Future<dynamic> setBuffer(int buffer) async {return _smartPlayerCallInt('setBuffer', buffer);}/// <summary>/// 接口可实时调用:设置是否实时静音,1:静音; 0: 取消静音/// </summary>/// <param name="is_mute"></param>Future<dynamic> setMute(int isMute) async {return _smartPlayerCallInt('setMute', isMute);}/// <summary>/// 设置RTSP TCP模式, 1: TCP; 0: UDP/// </summary>/// <param name="is_using_tcp"></param>Future<dynamic> setRTSPTcpMode(int isUsingTcp) async {return _smartPlayerCallInt('setRTSPTcpMode', isUsingTcp);}/// <summary>/// 设置RTSP超时时间, timeout单位为秒,必须大于0/// </summary>/// <param name="timeout"></param>Future<dynamic> setRTSPTimeout(int timeout) async {return _smartPlayerCallInt('setRTSPTimeout', timeout);}/// <summary>/// 设置RTSP TCP/UDP自动切换/// 对于RTSP来说,有些可能支持rtp over udp方式,有些可能支持使用rtp over tcp方式./// 为了方便使用,有些场景下可以开启自动尝试切换开关, 打开后如果udp无法播放,sdk会自动尝试tcp, 如果tcp方式播放不了,sdk会自动尝试udp./// </summary>/// <param name="is_auto_switch_tcp_udp"></param>Future<dynamic> setRTSPAutoSwitchTcpUdp(int is_auto_switch_tcp_udp) async {return _smartPlayerCallInt('setRTSPAutoSwitchTcpUdp', is_auto_switch_tcp_udp);}/// <summary>/// 设置快速启动该模式,/// </summary>/// <param name="is_fast_startup"></param>Future<dynamic> setFastStartup(int isFastStartup) async {return _smartPlayerCallInt('setFastStartup', isFastStartup);}/// <summary>/// 设置超低延迟模式 false不开启 true开启 默认false/// </summary>/// <param name="mode"></param>Future<dynamic> setPlayerLowLatencyMode(int mode) async {return _smartPlayerCallInt('setPlayerLowLatencyMode', mode);}/// <summary>/// 设置视频垂直反转/// </summary>/// <param name="is_flip"></param>Future<dynamic> setFlipVertical(int is_flip) async {return _smartPlayerCallInt('setFlipVertical', is_flip);}/// <summary>/// 设置视频水平反转/// </summary>/// <param name="is_flip"></param>Future<dynamic> setFlipHorizontal(int is_flip) async {return _smartPlayerCallInt('setFlipHorizontal', is_flip);}/// <summary>/// 设置顺时针旋转, 注意除了0度之外, 其他角度都会额外消耗性能/// degress: 当前支持 0度,90度, 180度, 270度 旋转/// </summary>/// <param name="degress"></param>Future<dynamic> setRotation(int degress) async {return _smartPlayerCallInt('setRotation', degress);}/// <summary>/// 设置是否回调下载速度/// is_report: if 1: 上报下载速度, 0: 不上报./// report_interval: 上报间隔,以秒为单位,>0./// </summary>/// <param name="is_report"></param>/// <param name="report_interval"></param>Future<dynamic> setReportDownloadSpeed(int isReport, int reportInterval) async {return _smartPlayerCallIntInt('setReportDownloadSpeed', isReport, reportInterval);}/// <summary>/// Set playback orientation(设置播放方向),此接口仅适用于Android平台/// </summary>/// <param name="surOrg"></param>/// surOrg: current orientation,  PORTRAIT 1, LANDSCAPE with 2Future<dynamic> setOrientation(int surOrg) async {return _smartPlayerCallInt('setOrientation', surOrg);}/// <summary>/// 设置是否需要在播放或录像过程中快照/// </summary>/// <param name="is_save_image"></param>Future<dynamic> setSaveImageFlag(int isSaveImage) async {return _smartPlayerCallInt('setSaveImageFlag', isSaveImage);}/// <summary>/// 播放或录像过程中快照/// </summary>/// <param name="imageName"></param>Future<dynamic> saveCurImage(String imageName) async {return _smartPlayerCallString('saveCurImage', imageName);}/// <summary>/// 播放或录像过程中,快速切换url/// </summary>/// <param name="uri"></param>Future<dynamic> switchPlaybackUrl(String uri) async {return _smartPlayerCallString('switchPlaybackUrl', uri);}/// <summary>/// 创建录像存储路径/// </summary>/// <param name="path"></param>Future<dynamic> createFileDirectory(String path) async {return _smartPlayerCallString('createFileDirectory', path);}/// <summary>/// 设置录像存储路径/// </summary>/// <param name="path"></param>Future<dynamic> setRecorderDirectory(String path) async {return _smartPlayerCallString('setRecorderDirectory', path);}/// <summary>/// 设置单个录像文件大小/// </summary>/// <param name="size"></param>Future<dynamic> setRecorderFileMaxSize(int size) async {return _smartPlayerCallInt('setRecorderFileMaxSize', size);}/// <summary>/// 设置录像时音频转AAC编码的开关/// aac比较通用,sdk增加其他音频编码(比如speex, pcmu, pcma等)转aac的功能./// </summary>/// <param name="is_transcode"></param>/// is_transcode: 设置为1的话,如果音频编码不是aac,则转成aac,如果是aac,则不做转换. 设置为0的话,则不做任何转换. 默认是0.Future<dynamic> setRecorderAudioTranscodeAAC(int is_transcode) async {return _smartPlayerCallInt('setRecorderAudioTranscodeAAC', is_transcode);}/// <summary>/// 设置播放路径/// </summary>Future<dynamic> setUrl(String url) async {return _smartPlayerCallString('setUrl', url);}/// <summary>/// 开始播放/// </summary>Future<dynamic> startPlay() async {return _smartPlayerCall('startPlay');}/// <summary>/// 停止播放/// </summary>Future<dynamic> stopPlay() async {return _smartPlayerCall('stopPlay');}/// <summary>/// 开始录像/// </summary>Future<dynamic> startRecorder() async {return _smartPlayerCall('startRecorder');}/// <summary>/// 停止录像/// </summary>Future<dynamic> stopRecorder() async {return _smartPlayerCall('stopRecorder');}/// <summary>/// 关闭播放/// </summary>Future<dynamic> dispose() async {return await _channel.invokeMethod('dispose');}void onSmartEvent(String param) {if (!param.contains(",")) {print("[onNTSmartEvent] android传递参数错误");return;}List<String> strs = param.split(',');String code = strs[1];String param1 = strs[2];String param2 = strs[3];String param3 = strs[4];String param4 = strs[5];int evCode = int.parse(code);var p1, p2, p3;switch (evCode) {case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STARTED:print("开始。。");break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTING:print("连接中。。");break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTION_FAILED:print("连接失败。。");break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTED:print("连接成功。。");break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_DISCONNECTED:print("连接断开。。");break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STOP:print("停止播放。。");break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_RESOLUTION_INFO:print("分辨率信息: width: " + param1 + ", height: " + param2);p1 = param1;p2 = param2;break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_NO_MEDIADATA_RECEIVED:print("收不到媒体数据,可能是url错误。。");break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_SWITCH_URL:print("切换播放URL。。");break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CAPTURE_IMAGE:print("快照: " + param1 + " 路径:" + param3);if (int.parse(param1) == 0) {print("截取快照成功。.");} else {print("截取快照失败。.");}p1 = param1;p2 = param3;break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_RECORDER_START_NEW_FILE:print("[record]开始一个新的录像文件 : " + param3);p3 = param3;break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_ONE_RECORDER_FILE_FINISHED:print("[record]已生成一个录像文件 : " + param3);p3 = param3;break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_START_BUFFERING:print("Start_Buffering");break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_BUFFERING:print("Buffering: " + param1 + "%");p1 = param1;break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STOP_BUFFERING:print("Stop_Buffering");break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_DOWNLOAD_SPEED:print("download_speed:" + (double.parse(param1) * 8 / 1000).toStringAsFixed(0) + "kbps" + ", " + (double.parse(param1) / 1024).toStringAsFixed(0) + "KB/s");p1 = param1;break;}if (_eventCallback != null) {_eventCallback(evCode, p1, p2, p3);}}
}

调用实例:

//
//  main.dart
//  main
//
//  GitHub: https://github.com/daniulive/SmarterStreaming
//  website: https://www.daniulive.com
//
//  Created by daniulive on 2019/02/25.
//  Copyright © 2014~2019 daniulive. All rights reserved.
//import 'dart:io';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:smartplayer_native_view/smartplayer.dart';
import 'package:smartplayer_native_view/smartplayer_plugin.dart';void main() {////// 强制竖屏///SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);runApp(new MyApp());
}class MyApp extends StatefulWidget {@override_MyAppState createState() => new _MyAppState();
}class _MyAppState extends State<MyApp> {SmartPlayerController player;double aspectRatio = 4.0 / 3.0;//输入需要播放的RTMP/RTSP urlTextEditingController playback_url_controller_ = TextEditingController();//Event事件回调显示TextEditingController event_controller_ = TextEditingController();bool is_playing_ = false;bool is_mute_ = false;var rotate_degrees_ = 0;Widget smartPlayerView() {return SmartPlayerWidget(onSmartPlayerCreated: onSmartPlayerCreated,);}@overridevoid initState() {print("initState called..");super.initState();}@overridevoid didChangeDependencies() {print('didChangeDependencies called..');super.didChangeDependencies();}@overridevoid deactivate() {print('deactivate called..');super.deactivate();}@overridevoid dispose() {print("dispose called..");player.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: const Text('Flutter SmartPlayer Demo'),),body: new SingleChildScrollView(child: new Column(children: <Widget>[new Container(color: Colors.black,child: AspectRatio(child: smartPlayerView(),aspectRatio: aspectRatio,),),new TextField(controller: playback_url_controller_,keyboardType: TextInputType.text,decoration: InputDecoration(contentPadding: EdgeInsets.all(10.0),icon: Icon(Icons.link),labelText: '请输入RTSP/RTMP url',),autofocus: false,),new Row(children: [new RaisedButton(onPressed: this.onSmartPlayerStartPlay,child: new Text("开始播放")),new Container(width: 20),new RaisedButton(onPressed: this.onSmartPlayerStopPlay,child: new Text("停止播放")),new Container(width: 20),new RaisedButton(onPressed: this.onSmartPlayerMute,child: new Text("实时静音")),],),new Row(children: [new RaisedButton(onPressed: this.onSmartPlayerSwitchUrl,child: new Text("实时切换URL")),new Container(width: 20),new RaisedButton(onPressed: this.onSmartPlayerSetRotation,child: new Text("实时旋转View")),],),new TextField(controller: event_controller_,keyboardType: TextInputType.text,decoration: InputDecoration(contentPadding: EdgeInsets.all(10.0),icon: Icon(Icons.event_note),labelText: 'Event状态回调',),autofocus: false,),],),)),);}void _eventCallback(int code, String param1, String param2, String param3) {String event_str;switch (code) {case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STARTED:event_str = "开始..";break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTING:event_str = "连接中..";break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTION_FAILED:event_str = "连接失败..";break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTED:event_str = "连接成功..";break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_DISCONNECTED:event_str = "连接断开..";break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STOP:event_str = "停止播放..";break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_RESOLUTION_INFO:event_str = "分辨率信息: width: " + param1 + ", height: " + param2;setState(() {aspectRatio = double.parse(param1) / double.parse(param2);print('change aspectRatio:$aspectRatio');});break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_NO_MEDIADATA_RECEIVED:event_str = "收不到媒体数据,可能是url错误..";break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_SWITCH_URL:event_str = "切换播放URL..";break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CAPTURE_IMAGE:event_str = "快照: " + param1 + " 路径: " + param3;if (int.parse(param1) == 0) {print("截取快照成功。.");} else {print("截取快照失败。.");}break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_RECORDER_START_NEW_FILE:event_str = "[record] new file: " + param3;break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_ONE_RECORDER_FILE_FINISHED:event_str = "[record] record finished: " + param3;break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_START_BUFFERING://event_str = "Start Buffering";break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_BUFFERING:event_str = "Buffering: " + param1 + "%";break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STOP_BUFFERING://event_str = "Stop Buffering";break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_DOWNLOAD_SPEED:event_str = "download_speed:" +(double.parse(param1) * 8 / 1000).toStringAsFixed(0) +"kbps" +", " +(double.parse(param1) / 1024).toStringAsFixed(0) +"KB/s";break;}event_controller_.text = event_str;}void onSmartPlayerCreated(SmartPlayerController controller) async {player = controller;player.setEventCallback(_eventCallback);var ret = -1;//设置video decoder模式var is_video_hw_decoder = 0;if (defaultTargetPlatform == TargetPlatform.android){ret = await player.setVideoDecoderMode(is_video_hw_decoder);}else if(defaultTargetPlatform == TargetPlatform.iOS){is_video_hw_decoder = 1;ret = await player.setVideoDecoderMode(is_video_hw_decoder);}//设置缓冲时间var play_buffer = 100;ret = await player.setBuffer(play_buffer);//设置快速启动var is_fast_startup = 1;ret = await player.setFastStartup(is_fast_startup);//是否开启低延迟模式var is_low_latency_mode = 0;ret = await player.setPlayerLowLatencyMode(is_low_latency_mode);//set report download speed(默认5秒一次回调 用户可自行调整report间隔)ret = await player.setReportDownloadSpeed(1, 2);//设置RTSP超时时间var rtsp_timeout = 10;ret = await player.setRTSPTimeout(rtsp_timeout);var is_auto_switch_tcp_udp = 1;ret = await player.setRTSPAutoSwitchTcpUdp(is_auto_switch_tcp_udp);// 设置RTSP TCP模式//ret = await player.setRTSPTcpMode(1);//第一次启动 为方便测试 设置个初始urlplayback_url_controller_.text = "rtmp://live.hkstv.hk.lxdns.com/live/hks2";}Future<void> onSmartPlayerStartPlay() async {var ret = -1;if (playback_url_controller_.text.length < 8) {playback_url_controller_.text ="rtmp://live.hkstv.hk.lxdns.com/live/hks1"; //给个初始url}//实时静音设置 ret = await player.setMute(is_mute_ ? 1 : 0);if (!is_playing_) {ret = await player.setUrl(playback_url_controller_.text);ret = await player.startPlay();if (ret == 0) {is_playing_ = true;}}}Future<void> onSmartPlayerStopPlay() async {if (is_playing_) {await player.stopPlay();playback_url_controller_.clear();is_playing_ = false;is_mute_ = false;}}Future<void> onSmartPlayerMute() async {if (is_playing_) {is_mute_ = !is_mute_;await player.setMute(is_mute_ ? 1 : 0);}}Future<void> onSmartPlayerSwitchUrl() async {if (is_playing_) {if (playback_url_controller_.text.length < 8) {playback_url_controller_.text ="rtmp://live.hkstv.hk.lxdns.com/live/hks1";}await player.switchPlaybackUrl(playback_url_controller_.text);}}Future<void> onSmartPlayerSetRotation() async {if (is_playing_) {rotate_degrees_ += 90;rotate_degrees_ = rotate_degrees_ % 360;if (0 == rotate_degrees_) {print("旋转90度");} else if (90 == rotate_degrees_) {print("旋转180度");} else if (180 == rotate_degrees_) {print("旋转270度");} else if (270 == rotate_degrees_) {print("不旋转");}await player.setRotation(rotate_degrees_);}}
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/553193.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何推送和播放RTMP H265流 (RTMP HEVC)

rtmp 播放h265 首先要扩展flv协议&#xff0c;国内常用扩展方式是给flv的videotag.codecid增加一个新类型(12)来表示h265(hevc),其他和h264规则差不多&#xff0c;另外和h264不同的地方是要解析HEVCDecoderConfigurationRecord&#xff0c;从HEVCDecoderConfigurationRecord中解…

5张图看懂如何实现Windows RTMP实时导播功能

一直以来&#xff0c;好多开发者苦于如何实现RTMP导播数据源实时切换&#xff0c;以下是大牛直播SDK导播切换说明&#xff0c;支持只切换数据源模式&#xff0c;或音视频混音合成输出模式&#xff1a; 数据源&#xff1a; 1. rtmp/rtsp音视频流&#xff1b; 2. 本地屏幕/摄像…

如何实现rtsp h265 转 rtmp (rtsp hevc 转 rtmp)并转发到CDN或自建服务器

rtsp h265 转 rtmp&#xff0c;首先要对rtmp协议做扩展&#xff0c;目前国内已有相应扩展标准&#xff0c;国内开发者基本上都按这个扩展协议做的。协议层问题已解决。剩下关键问题是要实现rtmp h265推送模块&#xff0c; 支持rtmp h265 的server, rtmp h265播放模块。 rtmp h…

如何优雅的实现一个靠谱的RTSP播放器?

二话不说&#xff0c;NO 图 NO BB&#xff08;以大牛直播SDK播放海康摄像机RTSP H.265流为例&#xff09;&#xff1a; 行业的浮躁&#xff0c;好多开发者甚至连rtsp、rtp细节不了解&#xff0c;依葫芦画瓢调用了ffmpeg的一些接口&#xff0c;就实现了个简单版的播放器&#xf…

如何对RTSP播放器做功能和性能评估

好多开发者在做产品竞品分析的时候&#xff0c;不知道如何界定一个RTSP播放器&#xff0c;大牛直播SDK认为&#xff0c;一个RTSP播放器&#xff0c;不是说有几个类似于Open/Close接口就够了&#xff0c;好的RTSP播放器需要具备以下功能和性能属性&#xff1a; 1. 低延迟&#…

如何实现RTSP/RTMP流接入到RTSP网关

好多企业或开发者给我们反映&#xff0c;他们期望能把外网的rtsp或rtmp流&#xff0c;直接拉取注入到内网流媒体服务器&#xff0c;保证内网用户&#xff0c;无需访问&#xff0c;直接链接到内网服务器就可以观看到公网rtmp/rtsp流。 优势如下&#xff1a; 1. 内网用户无需访…

如何在IE浏览器播放RTSP或RTMP流

好多开发者一直苦恼于如何在IE浏览器环境下&#xff0c;构建低延迟的RTSP或RTMP播放&#xff0c;对于RTSP流来说&#xff0c;好多公司通常的做法是把RTSP转RTMP&#xff0c;然后分发到RTMP服务器&#xff0c;然后服务器转http-flv出来&#xff0c;浏览器直接播放http-flv流&…

跨平台RTSP/RTMP转RTMP转发SDK

一个好的转发模块&#xff0c;首先要低延迟&#xff01;其次足够稳定、灵活、有状态反馈机制、资源占用低&#xff0c;如果可以跨平台&#xff0c;还能以SDK形式提供&#xff0c;会给开发者提供更大的便利&#xff01; 大牛直播SDK(Github: https://github.com/daniulive/Smar…

GitHub上排名前100的Android开源库介绍

GitHub上排名前100的Android开源库介绍 文章来源&#xff1a; http://www.open-open.com/news/view/1587067#6734290-qzone-1-31660-bf8335a56eb142042e6dc893bd988125 摘要&#xff1a; 本项目主要对目前 GitHub 上排名前 100 的 Android 开源库进行简单的介绍&#xff0c…

一张图了解大牛直播SDK

来源&#xff1a;https://github.com/daniulive/SmarterStreaming

轻量级RTSP服务SDK

为满足内网无纸化/电子教室等内网超低延迟需求&#xff0c;避免让用户配置单独的服务器&#xff0c;大牛直播SDK在推送端发布了轻量级RTSP服务SDK&#xff1a; 简单来说&#xff0c;之前推送端SDK支持的功能&#xff0c;内置轻量级RTSP服务SDK后&#xff0c;功能继续支持。 一…

多路RTSP-RTMP转RTMP定制版

大牛直播SDK多路RTMP/RTSP转RTMP转发软件&#xff0c;系原有转发SDK基础上&#xff0c;官方推出的Windows平台定制版。在秉承低延迟、灵活稳定、低资源占用的前提下&#xff0c;客户无需关注开发细节&#xff0c;只需图形化配置转发等各类参数&#xff0c;实现产品快速上线目的…

基于智慧教室|无纸化会议的新选择:RTMP解决方案

基于智慧教室或是会议的技术方案&#xff0c;一般主要是涉及到屏幕采集和推送&#xff0c;整体技术方案这块&#xff0c;一般建议走RTMP&#xff0c;说到这里&#xff0c;好人开发者提到&#xff0c;市面上也有RTSP的技术方案&#xff0c;甚至RTSP组播方案&#xff0c;这块&…

轻量级RTSP服务模块和RTSP推流模块适用场景区别

好多开发者一直搞不清轻量级RTSP服务SDK和RTSP推流SDK的区别&#xff08;Github下载地址&#xff09;&#xff0c;以下是相关区别&#xff1a; 1. 轻量级RTSP服务模块&#xff1a;轻量级RTSP服务解决的核心痛点是避免用户或者开发者单独部署RTSP或者RTMP服务&#xff0c;实现本…

如何实现多路海康大华等RTSP数据转RTMP推送

一个好的转发模块&#xff0c;首先要低延迟&#xff01;其次足够稳定、灵活、有状态反馈机制、资源占用低&#xff0c;跨平台&#xff0c;最好以接口形式提供&#xff0c;便于第三方系统集成。 以Windows平台为例&#xff0c;我们的考虑的点如下 1. 拉流&#xff1a;通过RTSP…

如何在RTSP/RTMP直播过程中加入SEI扩展数据发送和接收解析

在直播系统中&#xff0c;除了直播音视频之外&#xff0c;有时候还想从主播端发布文本信息等&#xff0c;这些信息可以不通过视频传输通道发送给用户播放端&#xff0c;但如果传输的数据想和视频保持精准同步&#xff0c;那最好的办法就是这些信息和视频数据打包在一起传输&…

如何拉取公网RTSP/RTMP流在内网多客户端播放

好多情况下&#xff0c;一路RTSP或RTMP网络流过来后&#xff0c;想共享给更多局域网内的客户端播放&#xff0c;一般来说&#xff0c;有两种设计方案&#xff1a; 1. 拉取的RTSP或RTMP流&#xff0c;回调后的数据&#xff0c;转推RTMP服务器&#xff0c;内网部署一台RTMP服务器…

轻量级RTSP服务存在的意义

为什么要设计轻量级RTSP服务 轻量级RTSP服务解决的核心痛点是避免用户或者开发者单独部署RTSP或者RTMP服务。 轻量级RTSP服务可满足内网无纸化/电子教室等内网超低延迟的低并发需求&#xff0c;避免让用户配置单独的服务器&#xff0c;大牛直播SDK在推送端发布了轻量级RTSP服…

Windows平台RTMP/RTSP直播推送模块设计和使用说明

开发背景 好多开发者一直反馈&#xff0c;Windows平台&#xff0c;做个推屏或者推摄像头&#xff0c;推RTMP或者RTSP出去&#xff0c;不知道哪些功能是必须的&#xff0c;哪些设计是可有可无的&#xff0c;还有就是&#xff0c;不知道如何选技术方案&#xff0c;以下是基于我们…

跨平台低延迟的RTMP/RTSP直播播放器设计实现

开发背景 2015年&#xff0c;当我们试图在市面上找一款专供直播播放使用的低延迟播放器&#xff0c;来配合测试我们的RTMP推送模块使用时&#xff0c;居然发现没有一款好用的&#xff0c;市面上的&#xff0c;如VLC或Vitamio&#xff0c;说白了都是基于FFMPEG&#xff0c;在点…