精华内容
下载资源
问答
  • 前言: Flutter的插件分为两种:Package和Plugin。 Package是纯Dart的,主要用在组件展示。类似:日历、下拉刷新等。...项目地址:flutter_rtmp_plugin 1、创建Plugin工程 1、AS中new flutter proj

    前言:

    Flutter的插件分为两种:Package和Plugin。
    Package是纯Dart的,主要用在组件展示。类似:日历、下拉刷新等。
    Plugin是通过Flutter的channel调用原生,原生来实现功能;主要用在功能性上。类似:拍照、录音等。
    rtmp推流为功能型的,所以此文讲的推流插件是Plugin类型。

    环境:

    Mac、AS、Xcode、Flutter1.12

    项目地址:flutter_rtmp_plugin

    1、创建Plugin工程

    1、AS中new flutter project 选择plugin选项,按提示语一路next,此处不再赘述。

     

    1.png

    2、创建完毕,打开工程目录如下,主要修改lib、ios、android三个目录。

     

    2.png

    2、Flutter端代码书写

     

    class FlutterRtmpPlugin {
    //1、创建channel
      static const MethodChannel _channel =
          const MethodChannel('flutter_rtmp_plugin');
    //2、开始直播的api,参数为推流地址
      static startLive(String url) async {
        await _channel.invokeMethod('startLive', {"url" : url});
      }
    
    }
    

    在外部的使用方法:
    var url = "rtmp://192.168.101.240/rtmplive/test";
    FlutterRtmpPlugin.startLive(url);

    3、 iOS端代码书写

    1、ios目录如下

     

    image.png

    2、ios端的推流使用的是LFLiveKit
    ,首先打开ios目录下后缀为.podspec的文件。
    添加依赖

     

    s.dependency 'LFLiveKit'
    

    3、FlutterRtmpPlugin.m文件
    实现逻辑是:ios接收到名为‘ flutter_rtmp_plugin’的channel,回调‘ startLive’这个方法,参数为‘ url’;ios则调起推流页面,推流到url。

     

    @interface FlutterRtmpPlugin ()
    @property(strong, nonatomic) UIViewController *viewController;
    @end
    
    @implementation FlutterRtmpPlugin
    + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
      FlutterMethodChannel* channel = [FlutterMethodChannel
          methodChannelWithName:@"flutter_rtmp_plugin"
                binaryMessenger:[registrar messenger]];
    
      UIViewController *viewController =
        [UIApplication sharedApplication].delegate.window.rootViewController;
    
      FlutterRtmpPlugin* instance = [[FlutterRtmpPlugin alloc] initWithViewController:viewController];
      [registrar addMethodCallDelegate:instance channel:channel];
    }
    
    - (instancetype)initWithViewController:(UIViewController *)viewController {
      self = [super init];
      if (self) {
        self.viewController = viewController;
      }
      return self;
    }
    
    - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
      if ([call.method isEqualToString:@"startLive"]) {
          NSDictionary * dict = call.arguments;
          NSLog(@"流地址是 %@",dict[@"url"]);
    
          LFViewController *liveVC = [[LFViewController alloc] init];
          liveVC.liveUrl = dict[@"url"];
          liveVC.modalPresentationStyle = UIModalPresentationFullScreen;
          [self.viewController presentViewController:liveVC animated:YES completion:nil];
      }
      else {
        result(FlutterMethodNotImplemented);
      }
    }
    

    LFViewController就是纯ios代码了。你可以在Assets文件夹下添加资源文件、也可以使用xib搭建UI。

    4、 Android端代码书写

    1、android目录如下

    5.png


    2、android端的推流使用的是SopCastComponent
    在plugin模块的gradle中添加依赖

    6.png

     

     

    dependencies {
        implementation 'androidx.constraintlayout:constraintlayout:+'
        //LFLive
        implementation 'com.laifeng:sopcast-sdk:1.0.4'
    }
    

    3、FlutterRtmpPlugin类
    注意:flutter1.12版本的回调方法如下

     

      @Override
      public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
        FlutterRtmpPlugin plugin = new FlutterRtmpPlugin();
        plugin.context = flutterPluginBinding.getApplicationContext();
        final MethodChannel channel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "flutter_rtmp_plugin");
        channel.setMethodCallHandler(plugin);
      }
    

    flutter1.12版本之前则在registerWith方法中回调

     

      public static void registerWith(Registrar registrar) {
        final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_rtmp_plugin");
        channel.setMethodCallHandler(new FlutterRtmpPlugin());
      }
    

    写的时候因为这个,总是回调不到,走了不少弯路。

    拿到app的当前context或activity之后就可以进行页面跳转了,逻辑和ios一样

     

    @Override
      public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
        if(call.method.equals("startLive")){
          Intent intent = new Intent(context,LivingActivity.class);
          String url = call.argument("url");
          intent.putExtra("url",url);
          intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );
          context.startActivity(intent);
    
        } else {
          result.notImplemented();
        }
      }
    

    LivingActivity 就是纯android原生代码了,在这里注意下,flutter调起的页面自带一个导航栏,
    在LivingActivity中添加进行去除

     

    requestWindowFeature(Window.FEATURE_NO_TITLE);
    展开全文
  • 该插件是flutter 的扩展,可将rtmp流添加为系统的一部分。 它适用于android和iOS(但不适用于网络)。 这意味着API与摄像头完全相同,安装要求也相同。 不同之处在于,还有一个额外的API,即startStreaming(url)...
  • flutter_rtmp environment: sdk: ">=2.1.0 <3>=1.10.0" 使用方法 git安装使用时建议要按照tag/release版本号 引入 1.第一种方法 flutter_rtmp: git: https://github.com/MEnigma/flutter_rtmp.git ref: *** 2.第...
  • 跨平台(iOS + Andriod)多协议(RTMP / HTTP-FLV / HLS / WebRTC)实时播放器,Flutter + 。 Flutter + 实时流播放器,iOS + Android,RTMP / HTTP-FLV / HLS / WebRTC。 用法 从安装iOS / Android播放器应用,或 ...
  • flutter_rtmp environment: sdk: ">=2.1.0 <3.0.0" flutter: ">=1.10.0" 使用方法 git安装使用时建议要按照tag/release版本号 更新 0.1.6 引入 1.第一种方法 flutter_rtmp: git: ...

    flutter_rtmp

    environment:
      sdk: ">=2.1.0 <3.0.0"
      flutter: ">=1.10.0"
    

    使用方法

    git安装使用时建议要按照tag/release版本号

    更新 0.1.6

    引入

    1.第一种方法

    flutter_rtmp:
       git: https://github.com/MEnigma/flutter_rtmp.git
       ref: ***
    

    2.第二种

    flutter_rtmp: ^*.*.*
    *注: 由于pub提交时经常失败,所以并不是最新版,建议使用git方式
    

    关于iOS的配置

    需要在info.plist中添加 
    
    <key>io.flutter.embedded_views_preview</key>
    <true/>
    

    关于Android的配置

    暂无需配置
    

    示例

     /// 控制器
     void initState() {
         super.initState();
         _manager = RtmpManager();
     }
    
     /// 视图
    Widget build(BuildContext context) {
    
       return RtmpView(
             manager: _manager,
        );
    }
    

    引用

    预览

    android iOS

    进度

    • 推流
    • 切换摄像头
    • 美颜
    • 旋转

    LICENSE

    • 参与者
    • mark < https://github.com/MEnigma >
    • youshinki < https://github.com/youshinki >

    Versions

    更新记录
    展开全文
  • 为什么要用FlutterFlutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全...

    为什么要用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 2
      Future<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 url
      TextEditingController 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,
          );
        }
    
      @override
      void initState() {
         print("initState called..");
        super.initState();
      }
    
      @override
      void didChangeDependencies() {
        print('didChangeDependencies called..');
        super.didChangeDependencies();
      }
    
      @override
      void deactivate() {
        print('deactivate called..');
        super.deactivate();
      }
    
      @override
      void dispose() {
        print("dispose called..");
        player.dispose();
        super.dispose();
      }
    
      @override
      Widget 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);
    
        //第一次启动 为方便测试 设置个初始url
        playback_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一样优异的播放体验。

    展开全文
  • rtmp_tencent_live 腾讯直播flutter插件 承接各种前端项目,flutter App项目 有问题欢迎提issues,或者欢迎加入flutter腾讯直播,群聊号码:1128573542 安装 在工程 pubspec.yaml 中加入 dependencies dependencies:...
  • Android摄像头RTMP推流

    2018-12-31 22:11:11
    使用FFMPEG的RTMP推流修改而来,可以读取摄像头和音频推流至流媒体服务器,适合做视频直播的新手参考. 使用FFMPEG的RTMP推流修改而来,可以读取摄像头和音频推流至流媒体服务器,适合做视频直播的新手参考.
  • 为什么要用FlutterFlutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全...

    为什么要用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 2
      Future<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 url
      TextEditingController 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,
          );
        }
    
      @override
      void initState() {
         print("initState called..");
        super.initState();
      }
    
      @override
      void didChangeDependencies() {
        print('didChangeDependencies called..');
        super.didChangeDependencies();
      }
    
      @override
      void deactivate() {
        print('deactivate called..');
        super.deactivate();
      }
    
      @override
      void dispose() {
        print("dispose called..");
        player.dispose();
        super.dispose();
      }
    
      @override
      Widget 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);
    
        //第一次启动 为方便测试 设置个初始url
        playback_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一样优异的播放体验。

    展开全文
  • flutter进阶之封装ijkplayer使其支持rtsp播放

    千次阅读 热门讨论 2019-01-25 18:54:56
    【原创不易,转载请注明出处:... 【20190222更新】 flutter_ijk 是flutter端的ijkplayer播放器,在IOS和Android native端都使用的是bilibili的ijkplayer,由于GitHub大小限制,本项目的ijkplayer源码放在...
  • 线上项目应用运行效果: ...demo apk: ...0.Tip 必须真机 android打release包必须加–no-shrink: flutter build apk --release --no-shrink 打包混淆配置参考issue99 1.Setup flutter_tencentplayer
  • 不幸的是,我的前端(Flutter和所有的Web浏览器)不支持RTMP协议。因此,我构建了pxy来代理从此类客户端(通过websocket)到广播RTMP服务器的实时流。由于RTMP仍然被广泛的视频流媒体行业,我觉得业余爱好者喜欢自己...
  • 掌握RTMP协议理论 掌握HLS协议理论 掌握OBS推流工具使用 掌握播放器配置及使用 掌握流媒体Nginx-Rtmp安装配置及使用 掌握流媒体集群配置方法 理解连麦原理 理解并掌握源服务器朝边缘服务分发技术 掌握React实现IM...
  • Flutter插件汇总

    2020-06-16 08:00:38
    Github地址:https://github.com/jahnli/awesome-flutter-plugins 文章源地址: https://juejin.im/post/5edeee2e51882542f347030c 插件 依赖 描述 Fliggy Mobile 【阿里巴巴-飞猪-FliggyMobile 技术...
  • 基于公司项目需求,需要在flutter项目中播放海康的 iSecure Center 平台视频监控画面,在网上找寻了好久,发现没有合适的视频播放插件,咨询海康官方也没有提供flutter版SDK的支持,所以封装了一下native端的SDK,开发了一...
  • ExoPlayer RTMP支持H265扩展

    千次阅读 2019-02-25 11:43:01
    ExoPlayer RTMP支持H265扩展 RTMP是由Adobe公司提出的一种应用层的协议。RTMP和FLV格式友好的兼容性,主要体现在RTMP封装可播放的音视频流时,RTMP Packet中封装的音视频数据流时,其实和FLV/tag封装音频和视频数据...
  • rtmp handshake

    2021-11-19 11:38:47
    rtmp handshake RTMP协议规定,播放一个流媒体有两个前提步骤: 第一步,建立一个网络连接(NetConnection); 第二步,建立一个网络流(NetStream)。 其中,网络连接代表服务器端应用程序和客户端之间基础...
  • https://github.com/AweiLoveAndroid/Flutter-learning/blob/master/readme/Flutter%E7%9A%84%E9%9C%80%E8%A6%81%E4%B8%8E%E5%8E%9F%E7%94%9F%E4%BA%A4%E4%BA%92%E7%9A%84%E4%B8%80%E4%BA%9B%E5%B8%B8%E7%94%A8...
  • Flutter基础笔记

    千次阅读 热门讨论 2021-05-31 12:28:41
    目录 List里面常用的属性和方法: Set Map forEach,map, where,any,every extends抽象类 和 implements Flutter环境搭建 入口文件、入口方法 第一个 Demo Center 组件的 使用 把内容单独抽离成一个组件 给 Text 组件...
  • 前面有提到有视频的RTMP包和音频的RTMP包,分别是将单元H264和单元AAC封装成RTMP包,发到服务器。这些包之间有什么规律? 这些包之间是按时间顺序排列的,MediaCodec返回编码数据时,会返回编码数据的时间戳。但注意...
  • Android 原生SDK Flutter插件编写

    万次阅读 2017-03-16 17:55:06
    CBS,http://123.108.164.71/etv2sb/phd27/playlist.m3u8 CGNTV,rtmp://edge2.everyon.tv:1935/etv2/phd235 CTIME,http://123.108.164.71/etv2sb/pld21/lihattv.m3u8 CTS,rtmp://edge1.everyon.tv:1935/etv1/phd28
  • ijkplayer和ffplay在打开rtmp串流视频时,大多数都会遇到5~10秒的延迟,在ffplay播放时,如果加上-fflags nobuffer可以缩短播放的rtmp视频延迟在1s内,而在IjkMediaPlayer中加入 mediaPlayer.setOption(1, ...
  • 学习笔记:Flutter的资源整合

    千次阅读 2019-01-09 20:33:28
    这篇文章来自网络,属于各大资源整合,如有冒犯到大家,请见谅联系...在Flutter项目的根目录中,导航到.../android/app/src/main/res。各种位图资源文件夹(如mipmap-hdpi已包含占位符图像”ic_launcher.png”)。 ...
  • 应用:HTTP、HTTPS、RTMP、FTP、SMTP、POP3 三次握手: 1\. C->S:SYN,seq=x(你能听到吗?) 2\. S->C:SYN,seq=y,ack=x+1(我能听到,你能听到吗?) 3\. C->S:ACK,seq=x+1,ack=y+1(我能听到,开始吧) 两...
  • 前言 众所周知,Xamarin应该是.net下的跨平台开发工具。...以至于谷歌的Flutter火爆之后,Xamarin社区很多人便转移阵地,你在百度搜索Xamarin,第一个关键词就是xamarin还有人用么。 那么对于C#/.net

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 152
精华内容 60
关键字:

flutterrtmp