├── .github └── FUNDING.yml ├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── android ├── .gitignore ├── build.gradle ├── consumer-proguard-rules.txt ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── libs │ ├── pldroid-player-2.1.9.jar │ └── pldroid-rtc-streaming-2.0.3.jar ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── top │ │ └── huic │ │ └── flutter_qiniucloud_live_plugin │ │ ├── FlutterQiniucloudLivePlugin.java │ │ ├── enums │ │ ├── PlayerCallBackNoticeEnum.java │ │ └── PushCallBackNoticeEnum.java │ │ ├── listener │ │ ├── QiniucloudPlayerListener.java │ │ └── QiniuicloudPushListener.java │ │ ├── util │ │ └── CommonUtil.java │ │ ├── view │ │ ├── QiniucloudConnectedPlayerPlatformView.java │ │ ├── QiniucloudPlayerPlatformView.java │ │ └── QiniucloudPushPlatformView.java │ │ └── widget │ │ ├── CameraPreviewFrameView.java │ │ └── MediaController.java │ └── jniLibs │ ├── arm64-v8a │ ├── libQPlayer.so │ ├── libcrypto.so │ ├── libpldroid_mmprocessing.so │ ├── libpldroid_rtc_streaming.so │ ├── libpldroid_streaming_aac_encoder.so │ ├── libpldroid_streaming_amix.so │ ├── libpldroid_streaming_core.so │ ├── libpldroid_streaming_h264_encoder.so │ ├── libpldroid_streaming_puic.so │ ├── libpldroid_streaming_srt.so │ ├── libqcOpenSSL.so │ └── libssl.so │ ├── armeabi-v7a │ ├── libQPlayer.so │ ├── libcrypto.so │ ├── libpldroid_mmprocessing.so │ ├── libpldroid_rtc_streaming.so │ ├── libpldroid_streaming_aac_encoder.so │ ├── libpldroid_streaming_amix.so │ ├── libpldroid_streaming_core.so │ ├── libpldroid_streaming_h264_encoder.so │ ├── libpldroid_streaming_puic.so │ ├── libpldroid_streaming_srt.so │ ├── libqcOpenSSL.so │ └── libssl.so │ ├── armeabi │ ├── libQPlayer.so │ ├── libcrypto.so │ ├── libpldroid_mmprocessing.so │ ├── libpldroid_rtc_streaming.so │ ├── libpldroid_streaming_aac_encoder.so │ ├── libpldroid_streaming_amix.so │ ├── libpldroid_streaming_core.so │ ├── libpldroid_streaming_h264_encoder.so │ ├── libpldroid_streaming_puic.so │ ├── libpldroid_streaming_srt.so │ ├── libqcOpenSSL.so │ └── libssl.so │ └── x86 │ ├── libQPlayer.so │ ├── libcrypto.so │ ├── libpldroid_mmprocessing.so │ ├── libpldroid_rtc_streaming.so │ ├── libpldroid_streaming_aac_encoder.so │ ├── libpldroid_streaming_amix.so │ ├── libpldroid_streaming_core.so │ ├── libpldroid_streaming_h264_encoder.so │ ├── libpldroid_streaming_puic.so │ ├── libpldroid_streaming_srt.so │ ├── libqcOpenSSL.so │ └── libssl.so ├── example ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── top │ │ │ │ │ └── huic │ │ │ │ │ └── flutter_qiniucloud_live_plugin_example │ │ │ │ │ └── MainActivity.java │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ ├── main.dart │ └── page │ │ ├── home.dart │ │ ├── player.dart │ │ └── push.dart ├── pubspec.lock └── pubspec.yaml ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── FlutterQiniucloudLivePlugin.h │ ├── FlutterQiniucloudLivePlugin.m │ ├── QiniuCloudDelegate.swift │ ├── SwiftFlutterQiniucloudLivePlugin.swift │ ├── enums │ │ ├── PlayerCallBackNoticeEnum.swift │ │ └── PushCallBackNoticeEnum.swift │ ├── utils │ │ ├── CommonUtils.swift │ │ └── JsonUtil.swift │ └── view │ │ ├── QiniucloudConnectedPlayerPlatformView.swift │ │ ├── QiniucloudPlayerPlatformView.swift │ │ └── QiniucloudPushPlatformView.swift └── flutter_qiniucloud_live_plugin.podspec ├── lib ├── controller │ ├── qiniucloud_connected_player_view_controller.dart │ ├── qiniucloud_player_view_controller.dart │ └── qiniucloud_push_view_controller.dart ├── entity │ ├── camera_streaming_setting_entity.dart │ ├── conference_options_entity.dart │ ├── face_beauty_setting_entity.dart │ ├── streaming_profile_entity.dart │ └── watermark_setting_entity.dart ├── enums │ ├── qiniucloud_audio_source_type_enum.dart │ ├── qiniucloud_camera_type_enum.dart │ ├── qiniucloud_encoding_orientation_enum.dart │ ├── qiniucloud_player_display_aspect_ratio_enum.dart │ ├── qiniucloud_player_listener_type_enum.dart │ ├── qiniucloud_push_audio_quality_enum.dart │ ├── qiniucloud_push_listener_type_enum.dart │ ├── qiniucloud_push_preview_size_level_enum.dart │ └── qiniucloud_push_video_quality_enum.dart ├── flutter_qiniucloud_live_plugin.dart └── view │ ├── qiniucloud_connected_player_view.dart │ ├── qiniucloud_player_view.dart │ └── qiniucloud_push_view.dart ├── pubspec.lock └── pubspec.yaml /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: https://www.yuque.com/jiangjuhong/default/aso8g5 # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | .idea 9 | *.iml -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 36e599eb5d1bc1c8d98d1c32f2149803fc7ff74e 8 | channel: master 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.1 2 | 3 | * TODO: Describe initial release. 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | https://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | Copyright 2013-2018 Docker, Inc. 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | https://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_qiniucloud_live_plugin 2 | 3 | Flutter 七牛云直播云插件,支持IOS、Android客户端 4 | 5 | **当前版本未发布正式版,请谨慎使用** 6 | **重构计划:打算重构为RTMP推流端和RTMP播放端,拆分插件** 7 | 8 | # 终章 9 | ## 致所有 flutter_qiniucloud_live_plugin 贡献者及用户 10 | ```` 11 | 由于此插件是基于七牛云直播云SDK进行Flutter封装,鉴于七牛云连麦相关内容需要用到RTC SDK,所以此项目不再维护,后续将开放七牛云RTC SDk for flutter,支持直播、连麦所有相关功能,具体请参考:https://developer.qiniu.com/rtc/8802/pd-overview。本项目将在一段时间后进行归档,不再进行维护。 12 | 13 | 至今此项目支持直播推流、拉流功能,此功能可继续使用,相关Issues不再维护。RTC SDK开源后大家可转至 RTC SDK进行使用。 14 | 15 | 一个项目的征途结束了,开源的步伐永远不滞。 16 | 欢迎加入Flutter讨论群,QQ群号: 850923396 17 | 18 | 2022-03-30 19 | ```` 20 | 21 | 22 | ## Getting Started 23 | 24 | 集成七牛云直播云推流、观看等功能 25 | 26 | ## 讨论群 27 | 30 | [点击加入群聊](https://jq.qq.com/?_wv=1027&k=QxCWMlUf) 31 | 32 | ## 功能清单 33 | ### 推流端 34 | [x]推流 35 | [x]推流时初始化自定义系统参数、预览参数等内容 36 | [x]美颜设置 37 | [x]自动对焦 38 | [ ]手动对焦 39 | [x]回调监听器 40 | [x]动态水印 41 | [ ]背景音乐 42 | 43 | ### 播放端 44 | [x]播放 45 | [x]监听器 46 | 47 | ### 连麦SDK 48 | [x]连麦 49 | [x]远端画面播放 50 | 51 | ## 集成 52 | 53 | ### Flutter 54 | ``` 55 | flutter_qiniucloud_live_plugin: 56 | git: 57 | url: https://github.com/JiangJuHong/FlutterQiniucloudLivePlugin.git 58 | ref: master 59 | ``` 60 | ### Android 61 | 不需要额外集成,已内部打入混淆 62 | 63 | ### IOS 64 | 配置权限信息,在Info.plist中增加 65 | ``` 66 | io.flutter.embedded_views_preview 67 | 68 | NSAppTransportSecurity 69 | 70 | NSAllowsArbitraryLoads 71 | 72 | 73 | NSCameraUsageDescription 74 | App需要您的同意,才能访问相机 75 | NSMicrophoneUsageDescription 76 | App需要您的同意,才能访问麦克风 77 | NSPhotoLibraryUsageDescription 78 | App需要您的同意,才能访问相册 79 | UIBackgroundModes 80 | 81 | audio 82 | 83 | ``` 84 | 修改 ``Podfile``,增加 ``config.build_settings['ENABLE_BITCODE'] = 'NO'`` 85 | ```` 86 | post_install do |installer| 87 | installer.pods_project.targets.each do |target| 88 | flutter_additional_ios_build_settings(target) 89 | target.build_configurations.each do |config| 90 | config.build_settings['ENABLE_BITCODE'] = 'NO' 91 | end 92 | end 93 | end 94 | 95 | ```` 96 | 97 | 98 | ## 注意事项 99 | 由于Android、IOS底层兼容不一致,导致以下内容会受影响: 100 | ### QiniucloudPlayerView 监听器 101 | 0. Error 回调参数:`Android:int 错误码` or `IOS:String 错误描述` 102 | 0. Info 状态码: `IOS` or `Android` 不一致 103 | * Android 104 | 105 | | 状态码 | 描述 | 106 | | ---- | ---- | 107 | | 1 | 未知消息 108 | | 3 | 第一帧视频已成功渲染 109 | | 200 | 连接成功 110 | | 340 | 读取到 metadata 信息 111 | | 701 | 开始缓冲 112 | | 702 | 停止缓冲 113 | | 802 | 硬解失败,自动切换软解 114 | | 901 | 预加载完成 115 | | 8088 | loop 中的一次播放完成 116 | | 10001 | 获取到视频的播放角度 117 | | 10002 | 第一帧音频已成功播放 118 | | 10003 | 获取视频的I帧间隔 119 | | 20001 | 视频的码率统计结果 120 | | 20002 | 视频的帧率统计结果 121 | | 20003 | 音频的帧率统计结果 122 | | 20004 | 音频的帧率统计结果 123 | | 10004 | 视频帧的时间戳 124 | | 10005 | 音频帧的时间戳 125 | | 1345 | 离线缓存的部分播放完成 126 | | 565 | 上一次 seekTo 操作尚未完成 127 | 128 | * IOS 129 | 130 | | 状态码 | 描述 | 131 | | ---- | ---- | 132 | | 0 | 未知状态,只会作为 init 后的初始状态,开始播放之后任何情况下都不会再回到此状态。 133 | | 1 | 正在准备播放所需组件,在调用 -play 方法时出现。 134 | | 2 | 播放组件准备完成,准备开始播放,在调用 -play 方法时出现。 135 | | 3 | 播放组件准备完成,准备开始连接 136 | | 4 | 缓存数据为空状态。(特别需要注意的是当推流端停止推流之后,PLPlayer 将出现 caching 状态直到 timeout 后抛出 timeout 的 error 而不是出现 PLPlayerStatusStopped 状态,因此在直播场景中,当流停止之后一般做法是使用 IM 服务告知播放器停止播放,以达到即时响应主播断流的目的。) 137 | | 5 | 正在播放状态。 138 | | 6 | 暂停状态。 139 | | 7 | 停止状态 (该状态仅会在回放时播放结束出现,RTMP 直播结束并不会出现此状态) 140 | | 8 | 错误状态,播放出现错误时会出现此状态。 141 | | 9 | 自动重连的状态 142 | | 10 | 播放完成(该状态只针对点播有效) 143 | 144 | 0. VideoSizeChanged 回调:`仅支持Android` 145 | 146 | ### QiniucloudPushView 监听器 147 | 0. StateChanged 状态码: `IOS` or `Android` 不一致 148 | 149 | | 状态码 | 描述 | Android | Ios 150 | | ---- | ---- | ---- | ---- | 151 | | PREPARING | - | √ | √ 152 | | READY | 相机准备就绪 | √ | √ 153 | | CONNECTING | 连接中 | √ | √ 154 | | STREAMING | 推流中 | √ | √ 155 | | SHUTDOWN | 直播中断 | √ | 156 | | IOERROR | 网络连接失败(连接rtmp推流失败) | √ | √ 157 | | OPEN_CAMERA_FAIL | 摄像头打开失败 | √ | 158 | | AUDIO_RECORDING_FAIL | 麦克风打开失败 | √ | 159 | | DISCONNECTED | 已经断开连接(直播断开) | √ | √ 160 | | TORCH_INFO | 开启闪光灯 | √ | 161 | 162 | 0. StreamStatusChanged 状态 163 | IOS 仅包含 `audioFps、videoFps、totalAVBitrate` 属性 164 | 165 | 0. ConferenceStateChanged IOS暂不支持(计划内) 166 | 167 | 0. UserJoinConference IOS暂不支持(计划内) 168 | 169 | 0. UserLeaveConference IOS暂不支持(计划内) 170 | 171 | 0. RecordAudioFailedHandled IOS暂不支持 172 | 173 | 0. RestartStreamingHandled IOS暂不支持 174 | 175 | 0. PreviewSizeSelected IOS暂不支持 176 | 177 | 0. PreviewFpsSelected IOS暂不支持 178 | 179 | 0. AudioSourceAvailable IOS暂不支持 180 | 181 | ## 使用 182 | 使用Demo时请主动更改推流地址和播放地址 183 | 184 | 185 | 186 | 187 | 188 | ### 组件列表 189 | QiniucloudPushView: 推流组件,通过该窗口Controller实现推流以及回显功能 190 | QiniucloudPlayerView: 播放器组件,通过Controller控制播放等内容 191 | QiniucloudConnectPlayerView:连麦推流预览组件 192 | 193 | ## 功能清单 194 | ### 连麦推流视图组件(QiniucloudPushView) 195 | #### 例子 196 | ```dart 197 | QiniucloudPushView( 198 | cameraStreamingSetting: CameraStreamingSettingEntity( 199 | faceBeauty: faceBeautySettingEntity, 200 | ), 201 | streamingProfile: StreamingProfileEntity( 202 | publishUrl: "rtmp://pili-publish.tianshitaiyuan.com/zuqulive/1576400046230A?e=1581756846&token=v740N_w0pHblR7KZMSPHhfdqjxrHEv5e_yBaiq0e:nlza8l7AsBDNkp47AD09ItfZSKA=", 203 | ), 204 | onViewCreated: (QiniucloudPushViewController controller) { 205 | controller.resume(); 206 | }, 207 | ) 208 | ``` 209 | #### 相关接口:(QiniucloudPushViewController调用方法) 210 | | 接口 | 说明 | 参数 | Android | IOS | 211 | | ---- | ---- | ---- | ---- | ---- | 212 | | resume | 打开摄像头和麦克风采集 | - | √ | √ 213 | | pause | 关闭摄像头和麦克风采集 | - | √ | √ 214 | | startConference | 开始连麦 | - | √ | 215 | | stopConference | 停止连麦 | - | √ | √ 216 | | startStreaming | 开始推流 | {publishUrl:'推流地址'} | √ | √ 217 | | stopStreaming | 停止推流 | - | √ | √ 218 | | destroy | 销毁 | - | √ | √ 219 | | isZoomSupported | 查询是否支持缩放(IOS始终返回true) | - | √ | √ 220 | | setZoomValue | 设置缩放比例 | {value:'比例值'} | √ | √ 221 | | getMaxZoom | 获得最大缩放比例 | - | √ | √ 222 | | getZoom | 获得缩放比例 | - | √ | √ 223 | | turnLightOn | 开启闪光灯 | - | √ | √ 224 | | turnLightOff | 关闭闪光灯 | - | √ | √ 225 | | switchCamera | 切换摄像头 | - | √ | √ 226 | | mute | 切换静音 | {mute:'是否静音',audioSource:'音频源'} | √ | √ 227 | | kickoutUser | 根据用户ID踢人 | - | √ | 228 | | setConferenceOptions | 设置连麦参数 | - | √ | 229 | | setStreamingProfile | 更新推流参数 | - | √ | 230 | | getParticipantsCount | 获取参与连麦的人数,不包括自己 | - | √ | 231 | | getParticipants | 获取参与连麦的用户ID列表,不包括自己 | - | √ | 232 | | setPreviewMirror | 设置预览镜像 | - | √ | 233 | | setEncodingMirror | 设置推流镜像 | - | √ | 234 | | startPlayback | 开启耳返 | - | √ | 235 | | stopPlayback | 关闭耳返 | - | √ | 236 | | updateWatermarkSetting | 更新动态水印 | - | √ | 237 | | updateFaceBeautySetting | 更新美颜设置 | - | √ | 238 | | addRemoteWindow | 添加远程视图 | - | √ | 239 | | getVideoEncodingSize | 获取编码器输出的画面的高宽 | - | √ | 240 | | setLocalWindowPosition | 自定义视频窗口位置(连麦推流模式下有效) | - | √ | 241 | 242 | *** 243 | 244 | ### 播放视图组件(QiniucloudPlayerView) 245 | #### 例子 246 | ```dart 247 | QiniucloudPlayerView( 248 | url: "rtmp://pili-live-rtmp.tianshitaiyuan.com/zuqulive/test", 249 | onViewCreated: (QiniucloudPlayerViewController controller){ 250 | controller.setVideoPath(url:"rtmp://pili-live-rtmp.tianshitaiyuan.com/zuqulive/1576400046230A") 251 | }, 252 | ), 253 | ``` 254 | #### 相关接口:(QiniucloudPlayerViewController调用方法) 255 | | 接口 | 说明 | 参数 | Android | IOS | 256 | | ---- | ---- | ---- | ---- | ---- | 257 | | setDisplayAspectRatio | 设置画面预览模式 | {mode:'模式,枚举'} | √ | √ 258 | | start | 开始播放 | {url:"播放的URL,通过该参数可以做到切换视频",sameSource:"是否是同种格式播放,同格式切换打开更快 @waring 当sameSource 为 YES 时,视频格式与切换前视频格式不同时,会导致视频打开失败【该属性仅IOS有效】"} | √ | √ 259 | | pause | 暂停播放 | - | √ | √ 260 | | stopPlayback | 停止播放 | - | √ | √ 261 | | getRtmpVideoTimestamp | 在RTMP消息中获取视频时间戳 | - | √ | √ 262 | | getRtmpAudioTimestamp | 在RTMP消息中获取音频时间戳 | - | √ | √ 263 | | setBufferingEnabled | 启用/关闭 播放器的预缓冲 | {enabled:'是否启用'} | √ | 264 | | getHttpBufferSize | 获取已经缓冲的长度 | - | √ | √ 265 | 266 | *** 267 | 268 | ### 连麦推流预览组件(QiniucloudConnectPlayerView) 269 | #### 例子 270 | ```dart 271 | QiniucloudConnectPlayerView( 272 | onViewCreated: (viewId, playerController) { 273 | this.playerController = playerController; 274 | controller.addRemoteWindow(id: viewId); 275 | }, 276 | ) 277 | ``` 278 | #### 相关接口:(QiniucloudPlayerViewController调用方法) 279 | | 接口 | 说明 | 参数 | Android | IOS | 280 | | ---- | ---- | ---- | ---- | ---- | 281 | | setAbsoluteMixOverlayRect | 配置连麦合流的参数(仅主播才配置,连麦观众不用)使用绝对值来配置该窗口在合流画面中的位置和大小 | - | √ | 282 | | setRelativeMixOverlayRect | 配置连麦合流的参数(仅主播才配置,连麦观众不用)使用相对值来配置该窗口在合流画面中的位置和大小 | - | √ | 283 | 284 | 285 | ## 其它插件 286 | ```` 287 | 我同时维护的还有以下插件,如果您感兴趣与我一起进行维护,请通过Github联系我,欢迎 issues 和 PR。 288 | ```` 289 | | 平台 | 插件 | 描述 | 版本 | 290 | |:--------|:------------------------------------------------------------------------------------------|:-------------------------|:------------------------------------------------------------------------------------------------------------------------------------| 291 | | Flutter | [FlutterVideoPlayerLibrary-Desc](https://github.com/JiangJuHong/FlutterVideoPlayerLibrary-Desc) | Flutter 最好用的播放器(UI库) | - | 292 | | Flutter | [FlutterPerfectVolumeControl](https://github.com/JiangJuHong/FlutterPerfectVolumeControl) | Flutter 完美的音量控制器插件 | [![pub package](https://img.shields.io/pub/v/perfect_volume_control.svg)](https://pub.dartlang.org/packages/perfect_volume_control) | 293 | | Flutter | [FlutterTencentImPlugin](https://github.com/JiangJuHong/FlutterTencentImPlugin) | 腾讯云IM插件 | [![pub package](https://img.shields.io/pub/v/tencent_im_plugin.svg)](https://pub.dartlang.org/packages/tencent_im_plugin) | 294 | | Flutter | [FlutterTencentRtcPlugin](https://github.com/JiangJuHong/FlutterTencentRtcPlugin) | 腾讯云Rtc插件 | [![pub package](https://img.shields.io/pub/v/tencent_rtc_plugin.svg)](https://pub.dartlang.org/packages/tencent_rtc_plugin) | 295 | | Flutter | [FlutterXiaoMiPushPlugin](https://github.com/JiangJuHong/FlutterXiaoMiPushPlugin) | 小米推送SDK插件 | [![pub package](https://img.shields.io/pub/v/xiao_mi_push_plugin.svg)](https://pub.dartlang.org/packages/xiao_mi_push_plugin) | 296 | | Flutter | [FlutterHuaWeiPushPlugin](https://github.com/JiangJuHong/FlutterHuaWeiPushPlugin) | 华为推送(HMS Push)插件 | [![pub package](https://img.shields.io/pub/v/hua_wei_push_plugin.svg)](https://pub.dartlang.org/packages/hua_wei_push_plugin) | 297 | | Flutter | [FlutterTextSpanField](https://github.com/JiangJuHong/FlutterTextSpanField) | 自定义文本样式输入框 | [![pub package](https://img.shields.io/pub/v/text_span_field.svg)](https://pub.dartlang.org/packages/text_span_field) | 298 | | Flutter | [FlutterClipboardListener](https://github.com/JiangJuHong/FlutterClipboardListener) | 粘贴板监听器 | [![pub package](https://img.shields.io/pub/v/clipboard_listener.svg)](https://pub.dartlang.org/packages/clipboard_listener) | 299 | | Flutter | [FlutterQiniucloudLivePlugin](https://github.com/JiangJuHong/FlutterQiniucloudLivePlugin) | Flutter 七牛云直播云插件 | 暂未发布,通过 git 集成 300 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'top.huic.flutter_qiniucloud_live_plugin' 2 | version '1.0' 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | } 8 | 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.5.3' 11 | } 12 | } 13 | rootProject.allprojects { 14 | repositories { 15 | google() 16 | jcenter() 17 | } 18 | } 19 | apply plugin: 'com.android.library' 20 | android { 21 | compileSdkVersion 28 22 | 23 | defaultConfig { 24 | minSdkVersion 16 25 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 26 | // library 混淆 -> 随 library 引用,自动添加到 apk 打包混淆 27 | consumerProguardFiles 'consumer-proguard-rules.txt' 28 | } 29 | lintOptions { 30 | disable 'InvalidPackage' 31 | } 32 | } 33 | 34 | dependencies { 35 | // happy-dns 依赖 36 | api 'com.qiniu:happy-dns:0.2.7' 37 | api 'com.android.support:appcompat-v7:28.0.0' 38 | // 7 39 | api 'com.alibaba:fastjson:1.2.55' 40 | // 从 libs 引入 41 | api fileTree(include: ['*.jar'], dir: 'libs') 42 | api files('libs/pldroid-rtc-streaming-2.0.3.jar') 43 | api files('libs/pldroid-player-2.1.9.jar') 44 | // 解决 Failed resolution of: Landroidx/lifecycle/ProcessLifecycleOwner; 问题 45 | implementation "androidx.lifecycle:lifecycle-runtime:2.0.0" 46 | implementation "androidx.lifecycle:lifecycle-extensions:2.0.0" 47 | annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.0.0" 48 | } -------------------------------------------------------------------------------- /android/consumer-proguard-rules.txt: -------------------------------------------------------------------------------- 1 | # 七牛云直播云播放端 2 | -keep class com.pili.pldroid.player.** { *; } 3 | -keep class com.qiniu.qplayer.mediaEngine.MediaPlayer{*;} 4 | # 七牛云连麦SDK v2 5 | -keep class com.youme.** {*;} 6 | -keep class org.webrtc.** {*;} 7 | -keep class com.qiniu.android.dns.** {*;} 8 | -keep class com.qiniu.pili.droid.streaming.** {*;} -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 6 | -------------------------------------------------------------------------------- /android/libs/pldroid-player-2.1.9.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/libs/pldroid-player-2.1.9.jar -------------------------------------------------------------------------------- /android/libs/pldroid-rtc-streaming-2.0.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/libs/pldroid-rtc-streaming-2.0.3.jar -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'flutter_qiniucloud_live_plugin' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /android/src/main/java/top/huic/flutter_qiniucloud_live_plugin/FlutterQiniucloudLivePlugin.java: -------------------------------------------------------------------------------- 1 | package top.huic.flutter_qiniucloud_live_plugin; 2 | 3 | import android.content.Context; 4 | 5 | import com.qiniu.pili.droid.rtcstreaming.RTCMediaStreamingManager; 6 | import com.qiniu.pili.droid.rtcstreaming.RTCServerRegion; 7 | import com.qiniu.pili.droid.streaming.StreamingEnv; 8 | 9 | import androidx.annotation.NonNull; 10 | 11 | import io.flutter.embedding.engine.plugins.FlutterPlugin; 12 | import io.flutter.plugin.common.BinaryMessenger; 13 | import io.flutter.plugin.common.MethodCall; 14 | import io.flutter.plugin.common.MethodChannel; 15 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler; 16 | import io.flutter.plugin.common.MethodChannel.Result; 17 | import io.flutter.plugin.common.PluginRegistry.Registrar; 18 | import io.flutter.plugin.platform.PlatformViewRegistry; 19 | import top.huic.flutter_qiniucloud_live_plugin.view.QiniucloudPushPlatformView; 20 | import top.huic.flutter_qiniucloud_live_plugin.view.QiniucloudPlayerPlatformView; 21 | 22 | /** 23 | * FlutterQiniucloudLivePlugin 24 | */ 25 | public class FlutterQiniucloudLivePlugin implements FlutterPlugin, MethodCallHandler { 26 | 27 | private final static String TAG = FlutterQiniucloudLivePlugin.class.getName(); 28 | 29 | /** 30 | * 全局上下文 31 | */ 32 | private Context context; 33 | 34 | public FlutterQiniucloudLivePlugin() { 35 | } 36 | 37 | private FlutterQiniucloudLivePlugin(BinaryMessenger messenger, Context context, MethodChannel channel, PlatformViewRegistry registry) { 38 | this.context = context; 39 | 40 | // 初始化七牛云 41 | RTCMediaStreamingManager.init(context, RTCServerRegion.RTC_CN_SERVER); 42 | StreamingEnv.init(context); 43 | 44 | // 注册View 45 | registry.registerViewFactory(QiniucloudPlayerPlatformView.SIGN, new QiniucloudPlayerPlatformView(context, messenger)); 46 | registry.registerViewFactory(QiniucloudPushPlatformView.SIGN, new QiniucloudPushPlatformView(context, messenger)); 47 | } 48 | 49 | @Override 50 | public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { 51 | final MethodChannel channel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "flutter_qiniucloud_live_plugin"); 52 | channel.setMethodCallHandler(new FlutterQiniucloudLivePlugin(flutterPluginBinding.getBinaryMessenger(), flutterPluginBinding.getApplicationContext(), channel, flutterPluginBinding.getPlatformViewRegistry())); 53 | } 54 | 55 | // This static function is optional and equivalent to onAttachedToEngine. It supports the old 56 | // pre-Flutter-1.12 Android projects. You are encouraged to continue supporting 57 | // plugin registration via this function while apps migrate to use the new Android APIs 58 | // post-flutter-1.12 via https://flutter.dev/go/android-project-migration. 59 | // 60 | // It is encouraged to share logic between onAttachedToEngine and registerWith to keep 61 | // them functionally equivalent. Only one of onAttachedToEngine or registerWith will be called 62 | // depending on the user's project. onAttachedToEngine or registerWith must both be defined 63 | // in the same class. 64 | public static void registerWith(Registrar registrar) { 65 | final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_qiniucloud_live_plugin"); 66 | channel.setMethodCallHandler(new FlutterQiniucloudLivePlugin(registrar.messenger(), registrar.context(), channel, registrar.platformViewRegistry())); 67 | } 68 | 69 | @Override 70 | public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { 71 | // if (call.method.equals("init")) { 72 | // result.success("Android " + android.os.Build.VERSION.RELEASE); 73 | // } else { 74 | // result.notImplemented(); 75 | // } 76 | } 77 | 78 | @Override 79 | public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /android/src/main/java/top/huic/flutter_qiniucloud_live_plugin/enums/PlayerCallBackNoticeEnum.java: -------------------------------------------------------------------------------- 1 | package top.huic.flutter_qiniucloud_live_plugin.enums; 2 | 3 | /** 4 | * 播放回调通知枚举 5 | * 6 | * @author 蒋具宏 7 | */ 8 | public enum PlayerCallBackNoticeEnum { 9 | /** 10 | * 播放结束 11 | */ 12 | Completion, 13 | /** 14 | * 错误事件 15 | */ 16 | Error, 17 | /** 18 | * 状态消息 19 | */ 20 | Info, 21 | /** 22 | * 已经准备好 23 | */ 24 | Prepared, 25 | /** 26 | * 该回调用于监听当前播放的视频流的尺寸信息,在 SDK 解析出视频的尺寸信息后,会触发该回调,开发者可以在该回调中调整 UI 的视图尺寸。 27 | */ 28 | VideoSizeChanged 29 | } 30 | -------------------------------------------------------------------------------- /android/src/main/java/top/huic/flutter_qiniucloud_live_plugin/enums/PushCallBackNoticeEnum.java: -------------------------------------------------------------------------------- 1 | package top.huic.flutter_qiniucloud_live_plugin.enums; 2 | 3 | /** 4 | * 连麦推流回调通知枚举 5 | * 6 | * @author 蒋具宏 7 | */ 8 | public enum PushCallBackNoticeEnum { 9 | /** 10 | * 回调音频采集 PCM 数据 11 | */ 12 | AudioSourceAvailable, 13 | /** 14 | * 根据StreamingProfile.StreamStatusConfig.getIntervalMs()调用 15 | */ 16 | StreamStatusChanged, 17 | /** 18 | * 录音失败时调用。 19 | */ 20 | RecordAudioFailedHandled, 21 | /** 22 | * 重新启动流式处理通知。 23 | */ 24 | RestartStreamingHandled, 25 | /** 26 | * 在构造相机对象后调用。 27 | */ 28 | PreviewSizeSelected, 29 | /** 30 | * 自定义预览fps,在构造相机对象后调用。 31 | */ 32 | PreviewFpsSelected, 33 | /** 34 | * 状态发生改变 35 | */ 36 | StateChanged, 37 | /** 38 | * 连麦状态发生改变 39 | */ 40 | ConferenceStateChanged, 41 | } 42 | -------------------------------------------------------------------------------- /android/src/main/java/top/huic/flutter_qiniucloud_live_plugin/listener/QiniucloudPlayerListener.java: -------------------------------------------------------------------------------- 1 | package top.huic.flutter_qiniucloud_live_plugin.listener; 2 | 3 | 4 | import android.content.Context; 5 | 6 | import com.alibaba.fastjson.JSON; 7 | import com.pili.pldroid.player.PLOnCompletionListener; 8 | import com.pili.pldroid.player.PLOnErrorListener; 9 | import com.pili.pldroid.player.PLOnInfoListener; 10 | import com.pili.pldroid.player.PLOnPreparedListener; 11 | import com.pili.pldroid.player.PLOnVideoSizeChangedListener; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | import io.flutter.plugin.common.MethodChannel; 17 | import top.huic.flutter_qiniucloud_live_plugin.enums.PlayerCallBackNoticeEnum; 18 | 19 | /** 20 | * 七牛云播放监听器 21 | * 22 | * @author 蒋具宏 23 | */ 24 | public class QiniucloudPlayerListener implements PLOnPreparedListener, PLOnInfoListener, PLOnCompletionListener, PLOnVideoSizeChangedListener, PLOnErrorListener { 25 | /** 26 | * 日志标签 27 | */ 28 | private static final String TAG = QiniucloudPlayerListener.class.getName(); 29 | 30 | /** 31 | * 监听器回调的方法名 32 | */ 33 | private final static String LISTENER_FUNC_NAME = "onPlayerListener"; 34 | 35 | /** 36 | * 全局上下文 37 | */ 38 | private Context context; 39 | 40 | /** 41 | * 通信管道 42 | */ 43 | private MethodChannel channel; 44 | 45 | public QiniucloudPlayerListener(Context context, MethodChannel channel) { 46 | this.context = context; 47 | this.channel = channel; 48 | } 49 | 50 | /** 51 | * 调用监听器 52 | * 53 | * @param type 类型 54 | * @param params 参数 55 | */ 56 | private void invokeListener(final PlayerCallBackNoticeEnum type, final Object params) { 57 | Map resultParams = new HashMap<>(2, 1); 58 | resultParams.put("type", type); 59 | resultParams.put("params", params == null ? null : JSON.toJSONString(params)); 60 | channel.invokeMethod(LISTENER_FUNC_NAME, JSON.toJSONString(resultParams)); 61 | } 62 | 63 | /** 64 | * 播放结束 65 | */ 66 | @Override 67 | public void onCompletion() { 68 | invokeListener(PlayerCallBackNoticeEnum.Completion, null); 69 | } 70 | 71 | /** 72 | * 错误事件 73 | */ 74 | @Override 75 | public boolean onError(int i) { 76 | invokeListener(PlayerCallBackNoticeEnum.Error, i); 77 | return false; 78 | } 79 | 80 | /** 81 | * 状态信息 82 | */ 83 | @Override 84 | public void onInfo(int i, int i1) { 85 | invokeListener(PlayerCallBackNoticeEnum.Info, i); 86 | } 87 | 88 | /** 89 | * 准备好 90 | */ 91 | @Override 92 | public void onPrepared(int i) { 93 | invokeListener(PlayerCallBackNoticeEnum.Prepared, i); 94 | } 95 | 96 | /** 97 | * 播放的视频流的尺寸信息,在 SDK 解析出视频的尺寸信息后,会触发该回调,开发者可以在该回调中调整 UI 的视图尺寸 98 | */ 99 | @Override 100 | public void onVideoSizeChanged(int i, int i1) { 101 | Map params = new HashMap<>(2, 1); 102 | params.put("width", i); 103 | params.put("height", i1); 104 | invokeListener(PlayerCallBackNoticeEnum.VideoSizeChanged, params); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /android/src/main/java/top/huic/flutter_qiniucloud_live_plugin/listener/QiniuicloudPushListener.java: -------------------------------------------------------------------------------- 1 | package top.huic.flutter_qiniucloud_live_plugin.listener; 2 | 3 | import android.content.Context; 4 | import android.hardware.Camera; 5 | import android.os.Handler; 6 | import android.os.Looper; 7 | 8 | import com.alibaba.fastjson.JSON; 9 | import com.qiniu.pili.droid.rtcstreaming.RTCAudioLevelCallback; 10 | import com.qiniu.pili.droid.rtcstreaming.RTCConferenceState; 11 | import com.qiniu.pili.droid.rtcstreaming.RTCConferenceStateChangedListener; 12 | import com.qiniu.pili.droid.streaming.AudioSourceCallback; 13 | import com.qiniu.pili.droid.streaming.StreamStatusCallback; 14 | import com.qiniu.pili.droid.streaming.StreamingProfile; 15 | import com.qiniu.pili.droid.streaming.StreamingSessionListener; 16 | import com.qiniu.pili.droid.streaming.StreamingState; 17 | import com.qiniu.pili.droid.streaming.StreamingStateChangedListener; 18 | import com.qiniu.pili.droid.streaming.SurfaceTextureCallback; 19 | 20 | import java.nio.ByteBuffer; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | import io.flutter.plugin.common.MethodChannel; 26 | import top.huic.flutter_qiniucloud_live_plugin.enums.PushCallBackNoticeEnum; 27 | 28 | /** 29 | * 七牛云连麦推流监听器 30 | * 31 | * @author 蒋具宏 32 | */ 33 | public class QiniuicloudPushListener implements StreamingSessionListener, StreamingStateChangedListener, StreamStatusCallback, AudioSourceCallback, SurfaceTextureCallback, RTCAudioLevelCallback, RTCConferenceStateChangedListener { 34 | 35 | /** 36 | * 日志标签 37 | */ 38 | private static final String TAG = QiniuicloudPushListener.class.getName(); 39 | 40 | /** 41 | * 监听器回调的方法名 42 | */ 43 | private final static String LISTENER_FUNC_NAME = "onPushListener"; 44 | 45 | /** 46 | * 回调监听器 47 | */ 48 | public static SurfaceTextureCallback callback; 49 | 50 | /** 51 | * 全局上下文 52 | */ 53 | private Context context; 54 | 55 | /** 56 | * 通信管道 57 | */ 58 | private MethodChannel channel; 59 | 60 | public QiniuicloudPushListener(Context context, MethodChannel channel) { 61 | this.context = context; 62 | this.channel = channel; 63 | } 64 | 65 | /** 66 | * 调用监听器 67 | * 68 | * @param type 类型 69 | * @param params 参数 70 | */ 71 | private void invokeListener(final PushCallBackNoticeEnum type, final Object params) { 72 | invokeListener(type, params, null, true); 73 | } 74 | 75 | 76 | /** 77 | * 调用监听器 78 | * 79 | * @param type 类型 80 | * @param params 参数 81 | * @param callback 回调 82 | * @param jsonParam 是否序列化参数 83 | */ 84 | private void invokeListener(final PushCallBackNoticeEnum type, final Object params, final MethodChannel.Result callback, final boolean jsonParam) { 85 | // 切换到主线程 86 | Handler mainHandler = new Handler(Looper.getMainLooper()); 87 | mainHandler.post(new Runnable() { 88 | @Override 89 | public void run() { 90 | Map resultParams = new HashMap<>(2, 1); 91 | resultParams.put("type", type); 92 | resultParams.put("params", params == null ? null : (jsonParam ? JSON.toJSONString(params) : params)); 93 | channel.invokeMethod(LISTENER_FUNC_NAME, JSON.toJSONString(resultParams), callback); 94 | } 95 | }); 96 | } 97 | 98 | @Override 99 | public boolean onRecordAudioFailedHandled(int i) { 100 | invokeListener(PushCallBackNoticeEnum.RecordAudioFailedHandled, i); 101 | return false; 102 | } 103 | 104 | @Override 105 | public boolean onRestartStreamingHandled(int i) { 106 | invokeListener(PushCallBackNoticeEnum.RestartStreamingHandled, i); 107 | return false; 108 | } 109 | 110 | @Override 111 | public Camera.Size onPreviewSizeSelected(List list) { 112 | invokeListener(PushCallBackNoticeEnum.PreviewSizeSelected, list); 113 | return null; 114 | } 115 | 116 | @Override 117 | public int onPreviewFpsSelected(List list) { 118 | invokeListener(PushCallBackNoticeEnum.PreviewFpsSelected, list); 119 | return 0; 120 | } 121 | 122 | @Override 123 | public void onStateChanged(StreamingState status, Object extra) { 124 | invokeListener(PushCallBackNoticeEnum.StateChanged, status, null, false); 125 | } 126 | 127 | @Override 128 | public void onAudioSourceAvailable(ByteBuffer srcBuffer, int size, long tsInNanoTime, boolean isEof) { 129 | Map params = new HashMap<>(4, 1); 130 | params.put("srcBuffer", srcBuffer.array()); 131 | params.put("size", size); 132 | params.put("tsInNanoTime", tsInNanoTime); 133 | params.put("isEof", isEof); 134 | invokeListener(PushCallBackNoticeEnum.AudioSourceAvailable, params); 135 | } 136 | 137 | @Override 138 | public void notifyStreamStatusChanged(StreamingProfile.StreamStatus streamStatus) { 139 | invokeListener(PushCallBackNoticeEnum.StreamStatusChanged, streamStatus); 140 | } 141 | 142 | @Override 143 | public void onSurfaceCreated() { 144 | 145 | } 146 | 147 | @Override 148 | public void onSurfaceChanged(int i, int i1) { 149 | 150 | } 151 | 152 | @Override 153 | public void onSurfaceDestroyed() { 154 | 155 | } 156 | 157 | @Override 158 | public int onDrawFrame(final int texId, int width, int height, float[] transformMatrix) { 159 | if (callback != null) { 160 | return callback.onDrawFrame(texId, width, height, transformMatrix); 161 | } 162 | return texId; 163 | } 164 | 165 | @Override 166 | public void onAudioLevelChanged(String userId, int level) { 167 | System.out.println("userId:" + userId + ",level:" + level); 168 | } 169 | 170 | @Override 171 | public void onConferenceStateChanged(RTCConferenceState rtcConferenceState, int i) { 172 | invokeListener(PushCallBackNoticeEnum.ConferenceStateChanged, rtcConferenceState.name()); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /android/src/main/java/top/huic/flutter_qiniucloud_live_plugin/util/CommonUtil.java: -------------------------------------------------------------------------------- 1 | package top.huic.flutter_qiniucloud_live_plugin.util; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | import android.util.Log; 6 | 7 | import io.flutter.plugin.common.MethodCall; 8 | import io.flutter.plugin.common.MethodChannel; 9 | import top.huic.flutter_qiniucloud_live_plugin.view.QiniucloudPushPlatformView; 10 | 11 | /** 12 | * 工具类 13 | */ 14 | public class CommonUtil { 15 | /** 16 | * 主线程处理器 17 | */ 18 | private final static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper()); 19 | 20 | /** 21 | * 通用方法,获得参数值,如未找到参数,则直接中断 22 | * 23 | * @param methodCall 方法调用对象 24 | * @param result 返回对象 25 | * @param param 参数名 26 | */ 27 | public static T getParam(MethodCall methodCall, MethodChannel.Result result, String param) { 28 | T par = methodCall.argument(param); 29 | if (par == null) { 30 | result.error("Missing parameter", "Cannot find parameter `" + param + "` or `" + param + "` is null!", 5); 31 | throw new RuntimeException("Cannot find parameter `" + param + "` or `" + param + "` is null!"); 32 | } 33 | return par; 34 | } 35 | 36 | /** 37 | * 运行主线程返回结果执行 38 | * 39 | * @param result 返回结果对象 40 | * @param param 返回参数 41 | */ 42 | public static void runMainThreadReturn(final MethodChannel.Result result, final Object param) { 43 | Log.d(QiniucloudPushPlatformView.class.getName(), "run: 准备进入连麦线程"); 44 | MAIN_HANDLER.post(new Runnable() { 45 | @Override 46 | public void run() { 47 | Log.d(QiniucloudPushPlatformView.class.getName(), "run: 进入连麦线程"); 48 | result.success(param); 49 | } 50 | }); 51 | } 52 | 53 | /** 54 | * 运行主线程返回错误结果执行 55 | * 56 | * @param result 返回结果对象 57 | * @param errorCode 错误码 58 | * @param errorMessage 错误信息 59 | * @param errorDetails 错误内容 60 | */ 61 | public static void runMainThreadReturnError(final MethodChannel.Result result, final String errorCode, final String errorMessage, final Object errorDetails) { 62 | MAIN_HANDLER.post(new Runnable() { 63 | @Override 64 | public void run() { 65 | result.error(errorCode, errorMessage, errorDetails); 66 | } 67 | }); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /android/src/main/java/top/huic/flutter_qiniucloud_live_plugin/view/QiniucloudConnectedPlayerPlatformView.java: -------------------------------------------------------------------------------- 1 | package top.huic.flutter_qiniucloud_live_plugin.view; 2 | 3 | 4 | import android.content.Context; 5 | import android.view.View; 6 | 7 | import com.qiniu.pili.droid.rtcstreaming.RTCSurfaceView; 8 | import com.qiniu.pili.droid.rtcstreaming.RTCVideoWindow; 9 | 10 | import java.util.Map; 11 | 12 | import io.flutter.plugin.common.BinaryMessenger; 13 | import io.flutter.plugin.common.MethodCall; 14 | import io.flutter.plugin.common.MethodChannel; 15 | import io.flutter.plugin.common.StandardMessageCodec; 16 | import io.flutter.plugin.platform.PlatformView; 17 | import io.flutter.plugin.platform.PlatformViewFactory; 18 | import top.huic.flutter_qiniucloud_live_plugin.util.CommonUtil; 19 | 20 | /** 21 | * 七牛云连麦播放视图 22 | */ 23 | public class QiniucloudConnectedPlayerPlatformView extends PlatformViewFactory implements PlatformView, MethodChannel.MethodCallHandler { 24 | 25 | /** 26 | * 日志标签 27 | */ 28 | private static final String TAG = QiniucloudConnectedPlayerPlatformView.class.getName(); 29 | 30 | /** 31 | * 全局上下文 32 | */ 33 | private Context context; 34 | 35 | /** 36 | * 消息器 37 | */ 38 | private BinaryMessenger messenger; 39 | 40 | /** 41 | * 全局标识 42 | */ 43 | public static final String SIGN = "plugins.huic.top/QiniucloudConnectedPlayer"; 44 | 45 | /** 46 | * 视图管理器 47 | */ 48 | private RTCVideoWindow window; 49 | 50 | /** 51 | * 视图ID 52 | */ 53 | private int viewId; 54 | 55 | /** 56 | * 预览视图 57 | */ 58 | private RTCSurfaceView view; 59 | 60 | /** 61 | * 初始化视图工厂,注册视图时调用 62 | */ 63 | public QiniucloudConnectedPlayerPlatformView(Context context, BinaryMessenger messenger) { 64 | super(StandardMessageCodec.INSTANCE); 65 | this.context = context; 66 | this.messenger = messenger; 67 | } 68 | 69 | /** 70 | * 初始化组件,同时也初始化七牛云推流 71 | * 每个组件被实例化时调用 72 | */ 73 | private QiniucloudConnectedPlayerPlatformView(Context context) { 74 | super(StandardMessageCodec.INSTANCE); 75 | this.context = context; 76 | } 77 | 78 | @Override 79 | public void onMethodCall(MethodCall call, MethodChannel.Result result) { 80 | switch (call.method) { 81 | case "setAbsoluteMixOverlayRect": 82 | this.setAbsoluteMixOverlayRect(call, result); 83 | break; 84 | case "setRelativeMixOverlayRect": 85 | this.setRelativeMixOverlayRect(call, result); 86 | break; 87 | default: 88 | result.notImplemented(); 89 | } 90 | } 91 | 92 | @Override 93 | public PlatformView create(Context context, int viewId, Object args) { 94 | this.viewId = viewId; 95 | Map params = (Map) args; 96 | QiniucloudConnectedPlayerPlatformView view = new QiniucloudConnectedPlayerPlatformView(context); 97 | // 绑定方法监听器 98 | MethodChannel methodChannel = new MethodChannel(messenger, SIGN + "_" + viewId); 99 | methodChannel.setMethodCallHandler(view); 100 | // 初始化 101 | view.init(params, methodChannel); 102 | return view; 103 | } 104 | 105 | @Override 106 | public void dispose() { 107 | } 108 | 109 | @Override 110 | public View getView() { 111 | return window.getRTCSurfaceView(); 112 | } 113 | 114 | /** 115 | * 初始化 116 | * 117 | * @param params 参数 118 | * @param methodChannel 方法通道 119 | */ 120 | private void init(Map params, MethodChannel methodChannel) { 121 | // 初始化视图 122 | view = new RTCSurfaceView(context); 123 | window = new RTCVideoWindow(view); 124 | } 125 | 126 | /** 127 | * 配置连麦合流的参数(仅主播才配置,连麦观众不用) 128 | * 使用绝对值来配置该窗口在合流画面中的位置和大小 129 | */ 130 | private void setAbsoluteMixOverlayRect(MethodCall call, MethodChannel.Result result) { 131 | int x = CommonUtil.getParam(call, result, "x"); 132 | int y = CommonUtil.getParam(call, result, "y"); 133 | int w = CommonUtil.getParam(call, result, "w"); 134 | int h = CommonUtil.getParam(call, result, "h"); 135 | window.setAbsoluteMixOverlayRect(x, y, w, h); 136 | result.success(null); 137 | } 138 | 139 | /** 140 | * 配置连麦合流的参数(仅主播才配置,连麦观众不用) 141 | * 使用相对值来配置该窗口在合流画面中的位置和大小 142 | */ 143 | private void setRelativeMixOverlayRect(MethodCall call, MethodChannel.Result result) { 144 | int x = CommonUtil.getParam(call, result, "x"); 145 | int y = CommonUtil.getParam(call, result, "y"); 146 | int w = CommonUtil.getParam(call, result, "w"); 147 | int h = CommonUtil.getParam(call, result, "h"); 148 | window.setRelativeMixOverlayRect(x, y, w, h); 149 | result.success(null); 150 | } 151 | 152 | public RTCVideoWindow getWindow() { 153 | return window; 154 | } 155 | } -------------------------------------------------------------------------------- /android/src/main/java/top/huic/flutter_qiniucloud_live_plugin/view/QiniucloudPlayerPlatformView.java: -------------------------------------------------------------------------------- 1 | package top.huic.flutter_qiniucloud_live_plugin.view; 2 | 3 | 4 | import android.content.Context; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.widget.TextView; 8 | 9 | import com.pili.pldroid.player.widget.PLVideoTextureView; 10 | import com.pili.pldroid.player.widget.PLVideoView; 11 | 12 | import java.util.Map; 13 | import java.util.Objects; 14 | 15 | import io.flutter.plugin.common.BinaryMessenger; 16 | import io.flutter.plugin.common.MethodCall; 17 | import io.flutter.plugin.common.MethodChannel; 18 | import io.flutter.plugin.common.StandardMessageCodec; 19 | import io.flutter.plugin.platform.PlatformView; 20 | import io.flutter.plugin.platform.PlatformViewFactory; 21 | import top.huic.flutter_qiniucloud_live_plugin.listener.QiniucloudPlayerListener; 22 | import top.huic.flutter_qiniucloud_live_plugin.util.CommonUtil; 23 | import top.huic.flutter_qiniucloud_live_plugin.widget.MediaController; 24 | 25 | /** 26 | * 七牛云播放器视图 27 | */ 28 | public class QiniucloudPlayerPlatformView extends PlatformViewFactory implements PlatformView, MethodChannel.MethodCallHandler { 29 | 30 | /** 31 | * 日志标签 32 | */ 33 | private static final String TAG = QiniucloudPlayerPlatformView.class.getName(); 34 | 35 | /** 36 | * 全局上下文 37 | */ 38 | private Context context; 39 | 40 | /** 41 | * 消息器 42 | */ 43 | private BinaryMessenger messenger; 44 | 45 | /** 46 | * 全局标识 47 | */ 48 | public static final String SIGN = "plugins.huic.top/QiniucloudPlayer"; 49 | 50 | /** 51 | * 播放器 52 | */ 53 | private PLVideoView view; 54 | 55 | /** 56 | * 初始化视图工厂,注册视图时调用 57 | */ 58 | public QiniucloudPlayerPlatformView(Context context, BinaryMessenger messenger) { 59 | super(StandardMessageCodec.INSTANCE); 60 | this.context = context; 61 | this.messenger = messenger; 62 | } 63 | 64 | /** 65 | * 初始化组件,同时也初始化七牛云推流 66 | * 每个组件被实例化时调用 67 | */ 68 | private QiniucloudPlayerPlatformView(Context context) { 69 | super(StandardMessageCodec.INSTANCE); 70 | this.context = context; 71 | } 72 | 73 | @Override 74 | public void onMethodCall(MethodCall call, MethodChannel.Result result) { 75 | switch (call.method) { 76 | case "setDisplayAspectRatio": 77 | this.setDisplayAspectRatio(call, result); 78 | break; 79 | case "start": 80 | this.start(call, result); 81 | break; 82 | case "pause": 83 | this.pause(call, result); 84 | break; 85 | case "stopPlayback": 86 | this.stopPlayback(call, result); 87 | break; 88 | case "getRtmpVideoTimestamp": 89 | this.getRtmpVideoTimestamp(call, result); 90 | break; 91 | case "getRtmpAudioTimestamp": 92 | this.getRtmpAudioTimestamp(call, result); 93 | break; 94 | case "setBufferingEnabled": 95 | this.setBufferingEnabled(call, result); 96 | break; 97 | case "getHttpBufferSize": 98 | this.getHttpBufferSize(call, result); 99 | break; 100 | default: 101 | result.notImplemented(); 102 | } 103 | } 104 | 105 | @Override 106 | public PlatformView create(Context context, int viewId, Object args) { 107 | Map params = (Map) args; 108 | QiniucloudPlayerPlatformView view = new QiniucloudPlayerPlatformView(context); 109 | // 绑定方法监听器 110 | MethodChannel methodChannel = new MethodChannel(messenger, SIGN + "_" + viewId); 111 | methodChannel.setMethodCallHandler(view); 112 | // 初始化 113 | view.init(params, methodChannel); 114 | return view; 115 | } 116 | 117 | @Override 118 | public void dispose() { 119 | 120 | } 121 | 122 | @Override 123 | public View getView() { 124 | return view; 125 | } 126 | 127 | /** 128 | * 初始化 129 | * 130 | * @param params 参数 131 | * @param methodChannel 方法通道 132 | */ 133 | private void init(Map params, MethodChannel methodChannel) { 134 | // 初始化视图 135 | view = new PLVideoView(context); 136 | view.setMediaController(new MediaController(context)); 137 | if (params.get("url") != null) { 138 | view.setVideoPath(params.get("url").toString()); 139 | } 140 | 141 | // 监听器 142 | QiniucloudPlayerListener listener = new QiniucloudPlayerListener(context, methodChannel); 143 | view.setOnPreparedListener(listener); 144 | view.setOnInfoListener(listener); 145 | view.setOnCompletionListener(listener); 146 | view.setOnVideoSizeChangedListener(listener); 147 | view.setOnErrorListener(listener); 148 | } 149 | 150 | /** 151 | * 设置画面预览模式 152 | */ 153 | private void setDisplayAspectRatio(MethodCall call, MethodChannel.Result result) { 154 | int mode = CommonUtil.getParam(call, result, "mode"); 155 | view.setDisplayAspectRatio(mode); 156 | result.success(null); 157 | } 158 | 159 | /** 160 | * 播放 161 | */ 162 | private void start(MethodCall call, MethodChannel.Result result) { 163 | String url = call.argument("url"); 164 | if (url != null) { 165 | view.setVideoPath(url); 166 | } 167 | view.start(); 168 | result.success(null); 169 | } 170 | 171 | /** 172 | * 暂停 173 | */ 174 | private void pause(MethodCall call, MethodChannel.Result result) { 175 | view.pause(); 176 | result.success(null); 177 | } 178 | 179 | /** 180 | * 停止播放 181 | */ 182 | private void stopPlayback(MethodCall call, MethodChannel.Result result) { 183 | view.stopPlayback(); 184 | result.success(null); 185 | } 186 | 187 | /** 188 | * 在RTMP消息中获取视频时间戳 189 | */ 190 | private void getRtmpVideoTimestamp(MethodCall call, MethodChannel.Result result) { 191 | result.success(view.getRtmpVideoTimestamp()); 192 | } 193 | 194 | /** 195 | * 在RTMP消息中获取音频时间戳 196 | */ 197 | private void getRtmpAudioTimestamp(MethodCall call, MethodChannel.Result result) { 198 | result.success(view.getRtmpAudioTimestamp()); 199 | } 200 | 201 | /** 202 | * 暂停/恢复播放器的预缓冲 203 | */ 204 | private void setBufferingEnabled(MethodCall call, MethodChannel.Result result) { 205 | boolean enabled = CommonUtil.getParam(call, result, "enabled"); 206 | view.setBufferingEnabled(enabled); 207 | result.success(null); 208 | } 209 | 210 | /** 211 | * 获取已经缓冲的长度 212 | */ 213 | private void getHttpBufferSize(MethodCall call, MethodChannel.Result result) { 214 | result.success(view.getHttpBufferSize().longValue()); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /android/src/main/java/top/huic/flutter_qiniucloud_live_plugin/widget/CameraPreviewFrameView.java: -------------------------------------------------------------------------------- 1 | package top.huic.flutter_qiniucloud_live_plugin.widget; 2 | 3 | import android.content.Context; 4 | import android.opengl.GLSurfaceView; 5 | import android.util.AttributeSet; 6 | import android.util.Log; 7 | import android.view.GestureDetector; 8 | import android.view.MotionEvent; 9 | import android.view.ScaleGestureDetector; 10 | 11 | public class CameraPreviewFrameView extends GLSurfaceView { 12 | private static final String TAG = "CameraPreviewFrameView"; 13 | 14 | public interface Listener { 15 | boolean onSingleTapUp(MotionEvent e); 16 | boolean onZoomValueChanged(float factor); 17 | } 18 | 19 | private Listener mListener; 20 | private ScaleGestureDetector mScaleDetector; 21 | private GestureDetector mGestureDetector; 22 | 23 | public CameraPreviewFrameView(Context context) { 24 | super(context); 25 | initialize(context); 26 | } 27 | 28 | public CameraPreviewFrameView(Context context, AttributeSet attrs) { 29 | super(context, attrs); 30 | initialize(context); 31 | } 32 | 33 | public void setListener(Listener listener) { 34 | mListener = listener; 35 | } 36 | 37 | @Override 38 | public boolean onTouchEvent(MotionEvent event) { 39 | if (!mGestureDetector.onTouchEvent(event)) { 40 | return mScaleDetector.onTouchEvent(event); 41 | } 42 | return false; 43 | } 44 | 45 | private GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() { 46 | @Override 47 | public boolean onSingleTapUp(MotionEvent e) { 48 | if (mListener != null) { 49 | mListener.onSingleTapUp(e); 50 | } 51 | return false; 52 | } 53 | }; 54 | 55 | private ScaleGestureDetector.SimpleOnScaleGestureListener mScaleListener = new ScaleGestureDetector.SimpleOnScaleGestureListener() { 56 | 57 | private float mScaleFactor = 1.0f; 58 | 59 | @Override 60 | public boolean onScaleBegin(ScaleGestureDetector detector) { 61 | return true; 62 | } 63 | 64 | @Override 65 | public boolean onScale(ScaleGestureDetector detector) { 66 | // factor > 1, zoom 67 | // factor < 1, pinch 68 | mScaleFactor *= detector.getScaleFactor(); 69 | 70 | // Don't let the object get too small or too large. 71 | mScaleFactor = Math.max(0.01f, Math.min(mScaleFactor, 1.0f)); 72 | 73 | return mListener != null && mListener.onZoomValueChanged(mScaleFactor); 74 | } 75 | }; 76 | 77 | private void initialize(Context context) { 78 | mScaleDetector = new ScaleGestureDetector(context, mScaleListener); 79 | mGestureDetector = new GestureDetector(context, mGestureListener); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /android/src/main/jniLibs/arm64-v8a/libQPlayer.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/arm64-v8a/libQPlayer.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/arm64-v8a/libcrypto.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/arm64-v8a/libcrypto.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/arm64-v8a/libpldroid_mmprocessing.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/arm64-v8a/libpldroid_mmprocessing.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/arm64-v8a/libpldroid_rtc_streaming.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/arm64-v8a/libpldroid_rtc_streaming.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/arm64-v8a/libpldroid_streaming_aac_encoder.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/arm64-v8a/libpldroid_streaming_aac_encoder.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/arm64-v8a/libpldroid_streaming_amix.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/arm64-v8a/libpldroid_streaming_amix.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/arm64-v8a/libpldroid_streaming_core.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/arm64-v8a/libpldroid_streaming_core.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/arm64-v8a/libpldroid_streaming_h264_encoder.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/arm64-v8a/libpldroid_streaming_h264_encoder.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/arm64-v8a/libpldroid_streaming_puic.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/arm64-v8a/libpldroid_streaming_puic.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/arm64-v8a/libpldroid_streaming_srt.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/arm64-v8a/libpldroid_streaming_srt.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/arm64-v8a/libqcOpenSSL.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/arm64-v8a/libqcOpenSSL.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/arm64-v8a/libssl.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/arm64-v8a/libssl.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi-v7a/libQPlayer.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi-v7a/libQPlayer.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi-v7a/libcrypto.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi-v7a/libcrypto.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi-v7a/libpldroid_mmprocessing.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi-v7a/libpldroid_mmprocessing.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi-v7a/libpldroid_rtc_streaming.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi-v7a/libpldroid_rtc_streaming.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_aac_encoder.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_aac_encoder.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_amix.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_amix.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_core.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_core.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_h264_encoder.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_h264_encoder.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_puic.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_puic.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_srt.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_srt.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi-v7a/libqcOpenSSL.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi-v7a/libqcOpenSSL.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi-v7a/libssl.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi-v7a/libssl.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi/libQPlayer.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi/libQPlayer.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi/libcrypto.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi/libcrypto.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi/libpldroid_mmprocessing.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi/libpldroid_mmprocessing.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi/libpldroid_rtc_streaming.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi/libpldroid_rtc_streaming.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi/libpldroid_streaming_aac_encoder.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi/libpldroid_streaming_aac_encoder.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi/libpldroid_streaming_amix.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi/libpldroid_streaming_amix.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi/libpldroid_streaming_core.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi/libpldroid_streaming_core.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi/libpldroid_streaming_h264_encoder.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi/libpldroid_streaming_h264_encoder.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi/libpldroid_streaming_puic.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi/libpldroid_streaming_puic.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi/libpldroid_streaming_srt.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi/libpldroid_streaming_srt.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi/libqcOpenSSL.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi/libqcOpenSSL.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/armeabi/libssl.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/armeabi/libssl.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/x86/libQPlayer.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/x86/libQPlayer.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/x86/libcrypto.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/x86/libcrypto.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/x86/libpldroid_mmprocessing.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/x86/libpldroid_mmprocessing.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/x86/libpldroid_rtc_streaming.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/x86/libpldroid_rtc_streaming.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/x86/libpldroid_streaming_aac_encoder.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/x86/libpldroid_streaming_aac_encoder.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/x86/libpldroid_streaming_amix.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/x86/libpldroid_streaming_amix.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/x86/libpldroid_streaming_core.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/x86/libpldroid_streaming_core.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/x86/libpldroid_streaming_h264_encoder.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/x86/libpldroid_streaming_h264_encoder.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/x86/libpldroid_streaming_puic.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/x86/libpldroid_streaming_puic.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/x86/libpldroid_streaming_srt.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/x86/libpldroid_streaming_srt.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/x86/libqcOpenSSL.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/x86/libqcOpenSSL.so -------------------------------------------------------------------------------- /android/src/main/jniLibs/x86/libssl.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/android/src/main/jniLibs/x86/libssl.so -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Exceptions to above rules. 37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 38 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 36e599eb5d1bc1c8d98d1c32f2149803fc7ff74e 8 | channel: master 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # flutter_qiniucloud_live_plugin_example 2 | 3 | Demonstrates how to use the flutter_qiniucloud_live_plugin plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 28 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "top.huic.flutter_qiniucloud_live_plugin_example" 37 | minSdkVersion 16 38 | targetSdkVersion 28 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | testImplementation 'junit:junit:4.12' 59 | androidTestImplementation 'androidx.test:runner:1.1.1' 60 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 61 | } 62 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 11 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/top/huic/flutter_qiniucloud_live_plugin_example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package top.huic.flutter_qiniucloud_live_plugin_example; 2 | 3 | import androidx.annotation.NonNull; 4 | import io.flutter.embedding.android.FlutterActivity; 5 | import io.flutter.embedding.engine.FlutterEngine; 6 | import io.flutter.plugins.GeneratedPluginRegistrant; 7 | 8 | public class MainActivity extends FlutterActivity { 9 | @Override 10 | public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { 11 | GeneratedPluginRegistrant.registerWith(flutterEngine); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.6.1' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '11.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | target.build_configurations.each do |config| 41 | config.build_settings['ENABLE_BITCODE'] = 'NO' 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - flutter_qiniucloud_live_plugin (0.0.1): 4 | - Flutter 5 | - PLPlayerKit (= 3.4.3) 6 | - PLRTCStreamingKit (= 3.2.3) 7 | - PLPlayerKit (3.4.3): 8 | - PLPlayerKit/iphoneos (= 3.4.3) 9 | - PLPlayerKit/iphoneos (3.4.3) 10 | - PLRTCStreamingKit (3.2.3): 11 | - PLRTCStreamingKit/iphoneos (= 3.2.3) 12 | - PLRTCStreamingKit/iphoneos (3.2.3) 13 | 14 | DEPENDENCIES: 15 | - Flutter (from `Flutter`) 16 | - flutter_qiniucloud_live_plugin (from `.symlinks/plugins/flutter_qiniucloud_live_plugin/ios`) 17 | 18 | SPEC REPOS: 19 | trunk: 20 | - PLPlayerKit 21 | - PLRTCStreamingKit 22 | 23 | EXTERNAL SOURCES: 24 | Flutter: 25 | :path: Flutter 26 | flutter_qiniucloud_live_plugin: 27 | :path: ".symlinks/plugins/flutter_qiniucloud_live_plugin/ios" 28 | 29 | SPEC CHECKSUMS: 30 | Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 31 | flutter_qiniucloud_live_plugin: e02fbafadb63df0f13fb56fefd7b84c4533bbc22 32 | PLPlayerKit: a44734dc78c1f8f9fb22c537fa1916612fa46b06 33 | PLRTCStreamingKit: 4c7d1470d56feda9a96796a7f72cf710119e7abe 34 | 35 | PODFILE CHECKSUM: 87585dfbd547da448f47d098e60e7673ff168ae0 36 | 37 | COCOAPODS: 1.12.1 38 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_qiniucloud_live_plugin_example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | NSCameraUsageDescription 31 | App需要您的同意,才能访问相机 32 | NSMicrophoneUsageDescription 33 | App需要您的同意,才能访问麦克风 34 | NSPhotoLibraryUsageDescription 35 | App需要您的同意,才能访问相册 36 | UIBackgroundModes 37 | 38 | audio 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIMainStoryboardFile 43 | Main 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UISupportedInterfaceOrientations~ipad 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationPortraitUpsideDown 54 | UIInterfaceOrientationLandscapeLeft 55 | UIInterfaceOrientationLandscapeRight 56 | 57 | UIViewControllerBasedStatusBarAppearance 58 | 59 | io.flutter.embedded_views_preview 60 | 61 | CADisableMinimumFrameDurationOnPhone 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_qiniucloud_live_plugin_example/page/home.dart'; 6 | 7 | void main() { 8 | runApp(MyApp()); 9 | // 实现Android完全沉浸式效果 10 | if (Platform.isAndroid) { 11 | SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(statusBarColor: Colors.transparent); 12 | SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); 13 | } 14 | } 15 | 16 | class MyApp extends StatefulWidget { 17 | @override 18 | _MyAppState createState() => _MyAppState(); 19 | } 20 | 21 | class _MyAppState extends State { 22 | @override 23 | Widget build(BuildContext context) { 24 | return MaterialApp( 25 | home: Container(), 26 | // home: HomePage(), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /example/lib/page/home.dart: -------------------------------------------------------------------------------- 1 | // import 'package:flutter/cupertino.dart'; 2 | // import 'package:flutter/material.dart'; 3 | // import 'package:flutter_qiniucloud_live_plugin_example/page/player.dart'; 4 | // import 'package:flutter_qiniucloud_live_plugin_example/page/push.dart'; 5 | 6 | // class HomePage extends StatefulWidget { 7 | // @override 8 | // State createState() => HomePageState(); 9 | // } 10 | 11 | // class HomePageState extends State { 12 | // TextEditingController controller = TextEditingController(); 13 | 14 | // @override 15 | // void initState() { 16 | // super.initState(); 17 | // } 18 | 19 | // /// 播放界面 20 | // onPlayer() { 21 | // // TODO 摄像头和麦克风权限请求 22 | // Navigator.push( 23 | // context, 24 | // new MaterialPageRoute(builder: (context) => new PlayerPage()), 25 | // ); 26 | // } 27 | 28 | // /// 连麦推流界面 29 | // onConnectedPush() { 30 | // // 摄像头和麦克风权限请求 31 | // PermissionHandler().requestPermissions([PermissionGroup.camera, PermissionGroup.microphone]).then((res) { 32 | // if (res[PermissionGroup.camera] == PermissionStatus.granted && res[PermissionGroup.microphone] == PermissionStatus.granted) { 33 | // Navigator.push( 34 | // context, 35 | // new MaterialPageRoute(builder: (context) => PushPage()), 36 | // ); 37 | // } 38 | // }); 39 | // } 40 | 41 | // @override 42 | // Widget build(BuildContext context) { 43 | // return Scaffold( 44 | // appBar: AppBar( 45 | // title: const Text('首页'), 46 | // ), 47 | // body: Center( 48 | // child: Column( 49 | // mainAxisAlignment: MainAxisAlignment.center, 50 | // children: [ 51 | // TextButton( 52 | // onPressed: onConnectedPush, 53 | // child: Text("开始推流"), 54 | // ), 55 | // TextButton( 56 | // onPressed: onPlayer, 57 | // child: Text("开始播放"), 58 | // ), 59 | // ], 60 | // ), 61 | // ), 62 | // ); 63 | // } 64 | // } 65 | -------------------------------------------------------------------------------- /example/lib/page/player.dart: -------------------------------------------------------------------------------- 1 | // import 'dart:convert'; 2 | // import 'dart:io'; 3 | 4 | // import 'package:flutter/cupertino.dart'; 5 | // import 'package:flutter/material.dart'; 6 | // import 'package:flutter_qiniucloud_live_plugin/view/qiniucloud_player_view.dart'; 7 | // import 'package:flutter_qiniucloud_live_plugin/controller/qiniucloud_player_view_controller.dart'; 8 | // import 'package:flutter_qiniucloud_live_plugin/enums/qiniucloud_player_listener_type_enum.dart'; 9 | // import 'package:flutter_qiniucloud_live_plugin/enums/qiniucloud_player_display_aspect_ratio_enum.dart'; 10 | 11 | // /// 播放界面 12 | // class PlayerPage extends StatefulWidget { 13 | // @override 14 | // State createState() => PlayerPageState(); 15 | // } 16 | 17 | // class PlayerPageState extends State { 18 | // /// 播放控制器 19 | // QiniucloudPlayerViewController controller; 20 | 21 | // /// 描述信息 22 | // String hint; 23 | 24 | // /// 状态 25 | // int status; 26 | 27 | // /// 错误信息 28 | // String error; 29 | 30 | // /// 视频宽度 31 | // int width = 0; 32 | 33 | // /// 视频高度 34 | // int height = 0; 35 | 36 | // /// 是否启用预缓存 37 | // bool bufferingEnabled = false; 38 | 39 | // @override 40 | // void initState() { 41 | // super.initState(); 42 | // } 43 | 44 | // @override 45 | // void dispose() { 46 | // super.dispose(); 47 | // if (controller != null) { 48 | // controller.removeListener(onListener); 49 | // } 50 | // } 51 | 52 | // /// 控制器初始化 53 | // onViewCreated(QiniucloudPlayerViewController controller) { 54 | // this.controller = controller; 55 | // controller.setDisplayAspectRatio( 56 | // mode: QiniucloudPlayerDisplayAspectRatioEnum.ASPECT_RATIO_PAVED_PARENT); 57 | // controller.addListener(onListener); 58 | // } 59 | 60 | // /// 监听器 61 | // onListener(type, params) { 62 | // // 错误 63 | // if (type == QiniucloudPlayerListenerTypeEnum.Error) { 64 | // this.setState(() => error = params.toString()); 65 | // } 66 | 67 | // // 状态改变 68 | // if (type == QiniucloudPlayerListenerTypeEnum.Info) { 69 | // this.setState(() => status = params); 70 | // } 71 | 72 | // // 大小改变 73 | // if (type == QiniucloudPlayerListenerTypeEnum.VideoSizeChanged) { 74 | // Map paramsObj = jsonDecode(params); 75 | // this.setState(() { 76 | // width = paramsObj["width"]; 77 | // height = paramsObj["height"]; 78 | // }); 79 | // } 80 | // } 81 | 82 | // /// 获得状态文本 83 | // getStatusText() { 84 | // if (status == null) { 85 | // return "等待中"; 86 | // } 87 | 88 | // if (Platform.isIOS) { 89 | // switch (status) { 90 | // case 0: 91 | // return "未知状态"; 92 | // case 1: 93 | // return "准备中"; 94 | // case 2: 95 | // return "准备完成"; 96 | // case 3: 97 | // return "开始连接"; 98 | // case 4: 99 | // return "正在缓存"; 100 | // case 5: 101 | // return "正在播放"; 102 | // case 6: 103 | // return "暂停"; 104 | // case 7: 105 | // return "播放结束或手动停止"; 106 | // case 8: 107 | // return "出现错误"; 108 | // case 9: 109 | // return "播放器开始自动重连"; 110 | // case 10: 111 | // return "点播播放完成"; 112 | // default: 113 | // return "未知状态"; 114 | // } 115 | // } 116 | 117 | // switch (status) { 118 | // case 1: 119 | // return "未知消息"; 120 | // case 3: 121 | // return "第一帧视频已成功渲染"; 122 | // case 200: 123 | // return "连接成功"; 124 | // case 340: 125 | // return "读取到 metadata 信息"; 126 | // case 701: 127 | // return "开始缓冲"; 128 | // case 702: 129 | // return "停止缓冲"; 130 | // case 802: 131 | // return "硬解失败,自动切换软解"; 132 | // case 901: 133 | // return "预加载完成"; 134 | // case 8088: 135 | // return "loop 中的一次播放完成"; 136 | // case 10001: 137 | // return "获取到视频的播放角度"; 138 | // case 10002: 139 | // return "第一帧音频已成功播放"; 140 | // case 10003: 141 | // return "获取视频的I帧间隔"; 142 | // case 20001: 143 | // return "视频的码率统计结果"; 144 | // case 20002: 145 | // return "视频的帧率统计结果"; 146 | // case 20003: 147 | // return "音频的帧率统计结果"; 148 | // case 20003: 149 | // return "音频的帧率统计结果"; 150 | // case 10004: 151 | // return "视频帧的时间戳"; 152 | // case 10005: 153 | // return "音频帧的时间戳"; 154 | // case 1345: 155 | // return "离线缓存的部分播放完成"; 156 | // case 565: 157 | // return "上一次 seekTo 操作尚未完成"; 158 | // default: 159 | // return "未知状态"; 160 | // } 161 | // } 162 | 163 | // /// 获得状态文本 164 | // getErrorText() { 165 | // if (Platform.isIOS) { 166 | // return error; 167 | // } 168 | 169 | // switch (error) { 170 | // case "-1": 171 | // return "未知错误"; 172 | // case "-2": 173 | // return "播放器打开失败"; 174 | // case "-3": 175 | // return "网络异常"; 176 | // case "-4": 177 | // return "拖动失败"; 178 | // case "-5": 179 | // return "预加载失败"; 180 | // case "-2003": 181 | // return "硬解失败"; 182 | // case "-2008": 183 | // return "播放器已被销毁,需要再次 setVideoURL 或 prepareAsync"; 184 | // case "-9527": 185 | // return "so 库版本不匹配,需要升级"; 186 | // case "-4410": 187 | // return "AudioTrack 初始化失败,可能无法播放音频"; 188 | // default: 189 | // return error; 190 | // } 191 | // } 192 | 193 | // /// 开始播放 194 | // onStart() async { 195 | // await controller.start( 196 | // url: 197 | // "rtmp://pili-live-rtmp.tianshitaiyuan.com/zuqulive/test", 198 | // ); 199 | // } 200 | 201 | // /// 暂停播放 202 | // onPause() async { 203 | // await controller.pause(); 204 | // } 205 | 206 | // /// 停止播放 207 | // onStopPlayback() async { 208 | // await controller.stopPlayback(); 209 | // } 210 | 211 | // /// 获得视频时间戳 212 | // onGetRtmpVideoTimestamp() async { 213 | // int time = await controller.getRtmpVideoTimestamp(); 214 | // this.setState(() => hint = "视频时间戳为:$time"); 215 | // } 216 | 217 | // /// 获得音频时间戳 218 | // onGetRtmpAudioTimestamp() async { 219 | // int time = await controller.getRtmpAudioTimestamp(); 220 | // this.setState(() => hint = "音频时间戳为:$time"); 221 | // } 222 | 223 | // /// 启用 / 关闭预缓存 224 | // onSetBufferingEnabled() async { 225 | // bufferingEnabled = !bufferingEnabled; 226 | // await controller.setBufferingEnabled(enabled: bufferingEnabled); 227 | // this.setState(() => hint = "已${bufferingEnabled ? "启用" : "关闭"}播放器预缓存"); 228 | // } 229 | 230 | // /// 获取已经缓冲的长度 231 | // onGetHttpBufferSize() async { 232 | // int size = await controller.getHttpBufferSize(); 233 | // this.setState(() => hint = "已经缓冲的长度:$size"); 234 | // } 235 | 236 | // @override 237 | // Widget build(BuildContext context) { 238 | // return Scaffold( 239 | // body: Stack( 240 | // children: [ 241 | // Container( 242 | // height: MediaQuery.of(context).size.height, 243 | // child: Stack( 244 | // children: [ 245 | // QiniucloudPlayerView( 246 | // onViewCreated: onViewCreated, 247 | // ), 248 | // Align( 249 | // alignment: new FractionalOffset(0.5, 0.95), 250 | // child: Container( 251 | // padding: EdgeInsets.all(5), 252 | // decoration: BoxDecoration( 253 | // borderRadius: BorderRadius.all(Radius.circular(20)), 254 | // border: Border.all(width: 1, color: Colors.red), 255 | // ), 256 | // child: Text( 257 | // "上滑查看功能栏", 258 | // style: TextStyle(color: Colors.red, fontSize: 10), 259 | // ), 260 | // ), 261 | // ), 262 | // ], 263 | // ), 264 | // ), 265 | // Container( 266 | // child: ListView( 267 | // padding: EdgeInsets.all(0), 268 | // children: [ 269 | // Container( 270 | // height: MediaQuery.of(context).size.height, 271 | // ), 272 | // Container( 273 | // color: Colors.white, 274 | // child: Column( 275 | // children: [ 276 | // Text( 277 | // error != null ? "发生错误,错误信息为:${getErrorText()}" : "", 278 | // style: TextStyle(color: Colors.red), 279 | // ), 280 | // Text(hint ?? ""), 281 | // Text("当前状态:${getStatusText()},视频高宽:$height,$width"), 282 | // Wrap( 283 | // children: [ 284 | // TextButton( 285 | // onPressed: onStart, 286 | // child: Text("开始播放"), 287 | // ), 288 | // TextButton( 289 | // onPressed: onPause, 290 | // child: Text("暂停"), 291 | // ), 292 | // TextButton( 293 | // onPressed: onStopPlayback, 294 | // child: Text("停止"), 295 | // ), 296 | // TextButton( 297 | // onPressed: onGetRtmpVideoTimestamp, 298 | // child: Text("获得视频时间戳"), 299 | // ), 300 | // TextButton( 301 | // onPressed: onGetRtmpVideoTimestamp, 302 | // child: Text("获得音频时间戳"), 303 | // ), 304 | // TextButton( 305 | // onPressed: onSetBufferingEnabled, 306 | // child: Text("启用/关闭 播放器预缓存"), 307 | // ), 308 | // TextButton( 309 | // onPressed: onGetHttpBufferSize, 310 | // child: Text("获得已缓冲长度"), 311 | // ), 312 | // ], 313 | // ), 314 | // ], 315 | // ), 316 | // ), 317 | // ], 318 | // ), 319 | // ), 320 | // ], 321 | // ), 322 | // ); 323 | // } 324 | // } 325 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.10.0" 12 | boolean_selector: 13 | dependency: transitive 14 | description: 15 | name: boolean_selector 16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.1.1" 20 | characters: 21 | dependency: transitive 22 | description: 23 | name: characters 24 | sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "1.2.1" 28 | clock: 29 | dependency: transitive 30 | description: 31 | name: clock 32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.1.1" 36 | collection: 37 | dependency: transitive 38 | description: 39 | name: collection 40 | sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.17.0" 44 | cupertino_icons: 45 | dependency: "direct main" 46 | description: 47 | name: cupertino_icons 48 | sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.0.5" 52 | fake_async: 53 | dependency: transitive 54 | description: 55 | name: fake_async 56 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "1.3.1" 60 | flutter: 61 | dependency: "direct main" 62 | description: flutter 63 | source: sdk 64 | version: "0.0.0" 65 | flutter_qiniucloud_live_plugin: 66 | dependency: "direct dev" 67 | description: 68 | path: ".." 69 | relative: true 70 | source: path 71 | version: "0.0.1" 72 | flutter_test: 73 | dependency: "direct dev" 74 | description: flutter 75 | source: sdk 76 | version: "0.0.0" 77 | js: 78 | dependency: transitive 79 | description: 80 | name: js 81 | sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" 82 | url: "https://pub.dev" 83 | source: hosted 84 | version: "0.6.5" 85 | matcher: 86 | dependency: transitive 87 | description: 88 | name: matcher 89 | sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" 90 | url: "https://pub.dev" 91 | source: hosted 92 | version: "0.12.13" 93 | material_color_utilities: 94 | dependency: transitive 95 | description: 96 | name: material_color_utilities 97 | sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 98 | url: "https://pub.dev" 99 | source: hosted 100 | version: "0.2.0" 101 | meta: 102 | dependency: transitive 103 | description: 104 | name: meta 105 | sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" 106 | url: "https://pub.dev" 107 | source: hosted 108 | version: "1.8.0" 109 | path: 110 | dependency: transitive 111 | description: 112 | name: path 113 | sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b 114 | url: "https://pub.dev" 115 | source: hosted 116 | version: "1.8.2" 117 | sky_engine: 118 | dependency: transitive 119 | description: flutter 120 | source: sdk 121 | version: "0.0.99" 122 | source_span: 123 | dependency: transitive 124 | description: 125 | name: source_span 126 | sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 127 | url: "https://pub.dev" 128 | source: hosted 129 | version: "1.9.1" 130 | stack_trace: 131 | dependency: transitive 132 | description: 133 | name: stack_trace 134 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 135 | url: "https://pub.dev" 136 | source: hosted 137 | version: "1.11.0" 138 | stream_channel: 139 | dependency: transitive 140 | description: 141 | name: stream_channel 142 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" 143 | url: "https://pub.dev" 144 | source: hosted 145 | version: "2.1.1" 146 | string_scanner: 147 | dependency: transitive 148 | description: 149 | name: string_scanner 150 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 151 | url: "https://pub.dev" 152 | source: hosted 153 | version: "1.2.0" 154 | term_glyph: 155 | dependency: transitive 156 | description: 157 | name: term_glyph 158 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 159 | url: "https://pub.dev" 160 | source: hosted 161 | version: "1.2.1" 162 | test_api: 163 | dependency: transitive 164 | description: 165 | name: test_api 166 | sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 167 | url: "https://pub.dev" 168 | source: hosted 169 | version: "0.4.16" 170 | vector_math: 171 | dependency: transitive 172 | description: 173 | name: vector_math 174 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 175 | url: "https://pub.dev" 176 | source: hosted 177 | version: "2.1.4" 178 | sdks: 179 | dart: ">=2.18.0 <3.0.0" 180 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_qiniucloud_live_plugin_example 2 | description: Demonstrates how to use the flutter_qiniucloud_live_plugin plugin. 3 | publish_to: 'none' 4 | 5 | environment: 6 | sdk: ">=2.12.0" 7 | 8 | dependencies: 9 | flutter: 10 | sdk: flutter 11 | 12 | # The following adds the Cupertino Icons font to your application. 13 | # Use with the CupertinoIcons class for iOS style icons. 14 | cupertino_icons: ^1.0.5 15 | # 权限请求(https://github.com/BaseflowIT/flutter-permission-handler) 16 | # permission_handler: ^10.2.0 17 | # file_picker: ^5.3.0 18 | 19 | 20 | dev_dependencies: 21 | flutter_test: 22 | sdk: flutter 23 | 24 | flutter_qiniucloud_live_plugin: 25 | path: ../ 26 | 27 | # For information on the generic Dart part of this file, see the 28 | # following page: https://dart.dev/tools/pub/pubspec 29 | 30 | # The following section is specific to Flutter. 31 | flutter: 32 | 33 | # The following line ensures that the Material Icons font is 34 | # included with your application, so that you can use the icons in 35 | # the material Icons class. 36 | uses-material-design: true 37 | 38 | # To add assets to your application, add an assets section, like this: 39 | # assets: 40 | # - images/a_dot_burr.jpeg 41 | # - images/a_dot_ham.jpeg 42 | 43 | # An image asset can refer to one or more resolution-specific "variants", see 44 | # https://flutter.dev/assets-and-images/#resolution-aware. 45 | 46 | # For details regarding adding assets from package dependencies, see 47 | # https://flutter.dev/assets-and-images/#from-packages 48 | 49 | # To add custom fonts to your application, add a fonts section here, 50 | # in this "flutter" section. Each entry in this list should have a 51 | # "family" key with the font family name, and a "fonts" key with a 52 | # list giving the asset and other descriptors for the font. For 53 | # example: 54 | # fonts: 55 | # - family: Schyler 56 | # fonts: 57 | # - asset: fonts/Schyler-Regular.ttf 58 | # - asset: fonts/Schyler-Italic.ttf 59 | # style: italic 60 | # - family: Trajan Pro 61 | # fonts: 62 | # - asset: fonts/TrajanPro.ttf 63 | # - asset: fonts/TrajanPro_Bold.ttf 64 | # weight: 700 65 | # 66 | # For details regarding fonts from package dependencies, 67 | # see https://flutter.dev/custom-fonts/#from-packages 68 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangJuHong/FlutterQiniucloudLivePlugin/2d9b022c2dd3cb23271e2d0f8a60e1478f40803f/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/FlutterQiniucloudLivePlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | @interface FlutterQiniucloudLivePlugin : NSObject 5 | @end 6 | -------------------------------------------------------------------------------- /ios/Classes/FlutterQiniucloudLivePlugin.m: -------------------------------------------------------------------------------- 1 | #import "FlutterQiniucloudLivePlugin.h" 2 | #if __has_include() 3 | #import 4 | #else 5 | // Support project import fallback if the generated compatibility header 6 | // is not copied when this plugin is created as a library. 7 | // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 8 | #import "flutter_qiniucloud_live_plugin-Swift.h" 9 | #endif 10 | 11 | @implementation FlutterQiniucloudLivePlugin 12 | + (void)registerWithRegistrar:(NSObject*)registrar { 13 | [SwiftFlutterQiniucloudLivePlugin registerWithRegistrar:registrar]; 14 | } 15 | @end 16 | -------------------------------------------------------------------------------- /ios/Classes/QiniuCloudDelegate.swift: -------------------------------------------------------------------------------- 1 | import PLRTCStreamingKit 2 | 3 | /// 七牛云委托代理 4 | public protocol QiniuCloudDelegate: NSObjectProtocol { 5 | 6 | /// 流处理器 7 | func mediaStreamingSession(_ session: PLMediaStreamingSession, cameraSourceDidGet pixelBuffer: CVPixelBuffer, timingInfo: CMSampleTimingInfo) -> Unmanaged 8 | } -------------------------------------------------------------------------------- /ios/Classes/SwiftFlutterQiniucloudLivePlugin.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import PLPlayerKit 4 | import PLRTCStreamingKit 5 | 6 | public class SwiftFlutterQiniucloudLivePlugin: NSObject, FlutterPlugin { 7 | public static func register(with registrar: FlutterPluginRegistrar) { 8 | // 初始化七牛云推流环境 9 | PLStreamingEnv.initEnv() 10 | 11 | let channel = FlutterMethodChannel(name: "flutter_qiniucloud_live_plugin", binaryMessenger: registrar.messenger()) 12 | let instance = SwiftFlutterQiniucloudLivePlugin() 13 | registrar.addMethodCallDelegate(instance, channel: channel) 14 | 15 | // 播放界面 16 | registrar.register( 17 | QiniucloudPlayerPlatformViewFactory(message: registrar.messenger()), 18 | withId: QiniucloudPlayerPlatformViewFactory.SIGN 19 | ); 20 | // 推流界面 21 | registrar.register( 22 | QiniucloudPushPlatformViewFactory(message: registrar.messenger()), 23 | withId: QiniucloudPushPlatformViewFactory.SIGN 24 | ); 25 | } 26 | 27 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ios/Classes/enums/PlayerCallBackNoticeEnum.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlayerCallBackNoticeEnum.swift 3 | // flutter_qiniucloud_live_plugin 4 | // 5 | // Created by 蒋具宏 on 2020/3/3. 6 | // 播放回调枚举 7 | public enum PlayerCallBackNoticeEnum{ 8 | /** 9 | * 播放结束 10 | */ 11 | case Completion 12 | /** 13 | * 错误事件 14 | */ 15 | case Error 16 | /** 17 | * 状态消息 18 | */ 19 | case Info 20 | /** 21 | * 已经准备好 22 | */ 23 | case Prepared 24 | /** 25 | * 该回调用于监听当前播放的视频流的尺寸信息,在 SDK 解析出视频的尺寸信息后,会触发该回调,开发者可以在该回调中调整 UI 的视图尺寸。 26 | */ 27 | case VideoSizeChanged 28 | } 29 | -------------------------------------------------------------------------------- /ios/Classes/enums/PushCallBackNoticeEnum.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PushCallBackNoticeEnum.swift 3 | // flutter_qiniucloud_live_plugin 4 | // 5 | // Created by 蒋具宏 on 2020/3/6. 6 | // 推流回调枚举 7 | public enum PushCallBackNoticeEnum{ 8 | /// 回调音频采集 PCM 数据 9 | case AudioSourceAvailable 10 | 11 | /// 根据StreamingProfile.StreamStatusConfig.getIntervalMs()调用 12 | case StreamStatusChanged 13 | 14 | /// 录音失败时调用。 15 | case RecordAudioFailedHandled 16 | 17 | /// 重新启动流式处理通知。 18 | case RestartStreamingHandled 19 | 20 | /// 在构造相机对象后调用。 21 | case PreviewSizeSelected 22 | 23 | /// 自定义预览fps,在构造相机对象后调用。 24 | case PreviewFpsSelected 25 | 26 | /// 状态发生改变 27 | case StateChanged 28 | 29 | /// 连麦状态改变 30 | case ConferenceStateChanged 31 | 32 | /// 用户加入连麦 33 | case UserJoinConference 34 | 35 | /// 用户离开连麦 36 | case UserLeaveConference 37 | } 38 | -------------------------------------------------------------------------------- /ios/Classes/utils/CommonUtils.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | 3 | // 通用工具类 4 | // Created by 蒋具宏 on 2020/2/10. 5 | public class CommonUtils{ 6 | /** 7 | * 通用方法,获得参数值,如未找到参数,则直接中断 8 | * 9 | * @param methodCall 方法调用对象 10 | * @param result 返回对象 11 | * @param param 参数名 12 | */ 13 | public static func getParam(call: FlutterMethodCall, result: @escaping FlutterResult, param : String)->Any? 14 | { 15 | let value = (call.arguments as! [String:Any])[param]; 16 | if value == nil{ 17 | result( 18 | FlutterError(code: "5", message: "Missing parameter",details: "Cannot find parameter `\(param)` or `\(param)` is null!") 19 | ); 20 | } 21 | return value 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Classes/utils/JsonUtil.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | // Json工具类 4 | // Created by 蒋具宏 on 2020/2/11. 5 | public class JsonUtil { 6 | 7 | /** 8 | * 字典转模型 9 | */ 10 | public static func toModel(_ type: T.Type, value: Any?) -> T? where T: Decodable { 11 | guard let value = value else { return nil } 12 | return toModel(type, value: value) 13 | } 14 | 15 | /** 16 | * 字典转模型 17 | */ 18 | public static func toModel(_ type: T.Type, value: Any) -> T? where T : Decodable { 19 | guard let data = try? JSONSerialization.data(withJSONObject: value) else { return nil } 20 | let decoder = JSONDecoder() 21 | decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "+Infinity", negativeInfinity: "-Infinity", nan: "NaN") 22 | return try? decoder.decode(type, from: data) 23 | } 24 | 25 | /** 26 | * 将json字符串转换为字典 27 | */ 28 | public static func getDictionaryFromJSONString(jsonString:String) ->[String:Any]{ 29 | 30 | let jsonData:Data = jsonString.data(using: .utf8)! 31 | 32 | let dict = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) 33 | if dict != nil { 34 | return (dict as! NSDictionary) as! [String : Any] 35 | } 36 | return NSDictionary() as! [String : Any] 37 | } 38 | 39 | /** 40 | * 将对象转换为JSON字符串(数组/对象) 41 | */ 42 | public static func toJson(_ object: Any) -> Any { 43 | // 解析数组 44 | if let array = object as? [Any] { 45 | let isStringArray = object is [String]; 46 | var result = "["; 47 | for item in array{ 48 | let data = isStringArray ? "\"\(toJsonByObj(item))\"" : toJsonByObj(item); 49 | result += "\(data),"; 50 | } 51 | // 删除末尾逗号 52 | if result.hasSuffix(","){ 53 | result = String(result.dropLast()); 54 | } 55 | return result + "]"; 56 | } 57 | 58 | // 解析单个对象 59 | return toJsonByObj(object); 60 | } 61 | 62 | /** 63 | * 将对象转换为JSON字符串(单个对象) 64 | */ 65 | private static func toJsonByObj(_ object: Any) -> Any{ 66 | 67 | if object is String{ 68 | return "\(object)"; 69 | } 70 | 71 | if object is Int32 || object is Int || object is UInt32 || object is UInt64 || object is Bool || object is Double || object is time_t || object is Date || object is Data || object is Dictionary 72 | { 73 | return vHandler(object); 74 | } 75 | 76 | var result = "{"; 77 | // 反射当前类及父类反射对象 78 | let morror = Mirror.init(reflecting: object) 79 | let superMorror = morror.superclassMirror 80 | // 键值对字典 81 | var dict : Dictionary = [:]; 82 | 83 | // 遍历父类和子类属性集合,添加到键值对字典 84 | if superMorror != nil{ 85 | for (name, value) in (superMorror?.children)! { 86 | dict[name!] = value; 87 | } 88 | } 89 | 90 | for (name, value) in morror.children { 91 | dict[name!] = value; 92 | } 93 | 94 | // 组装json对象 95 | for (name,value) in dict{ 96 | // 解码值,根据不同类型设置不同封装,nil不进行封装 97 | if let n = name{ 98 | let v = unwrap(value); 99 | // 未解码成功的值,则是nil 100 | if !("\(type(of:v))".hasPrefix("Optional")) { 101 | result += kv(n, v); 102 | result += ","; 103 | } 104 | } 105 | } 106 | 107 | // 删除末尾逗号 108 | if result.hasSuffix(","){ 109 | result = String(result.dropLast()); 110 | } 111 | 112 | return result + "}"; 113 | } 114 | 115 | /** 116 | * 解码值,optional 将会被自动解码 117 | */ 118 | private static func unwrap(_ any: T) -> Any{ 119 | let mirror = Mirror(reflecting: any) 120 | guard mirror.displayStyle == .optional, let first = mirror.children.first else { 121 | return any 122 | } 123 | return first.value 124 | } 125 | 126 | /** 127 | * 根据K和V拼装键值对 128 | */ 129 | private static func kv(_ k : Any, _ v : Any)->String{ 130 | return "\"\(k)\":\(vHandler(v))"; 131 | } 132 | 133 | /** 134 | * 值处理,根据不同类型的值,返回不同的结果 135 | */ 136 | private static func vHandler(_ v : Any)->Any{ 137 | // 根据类型赋值不同的值 138 | // 如果是字符串,将会进行转移 " to \" 139 | // 如果是Data,将会解析为字符串并且进行转移 140 | if v is String{ 141 | return "\"\(stringReplace(source: "\(v)"))\""; 142 | }else if v is Int32 || v is Int || v is UInt32 || v is UInt64 || v is Bool || v is Double || v is time_t{ 143 | return v; 144 | }else if v is Date{ 145 | return Int((v as! Date).timeIntervalSince1970); 146 | }else if v is Data{ 147 | return "\"\(stringReplace(source: String(data: v as! Data, encoding: String.Encoding.utf8)!))\""; 148 | }else if v is Dictionary{ 149 | var result = "{"; 150 | // 解析键值对 151 | for (key,value) in v as! Dictionary{ 152 | result += "\(kv(key, value)),"; 153 | } 154 | // 删除末尾逗号 155 | if result.hasSuffix(","){ 156 | result = String(result.dropLast()); 157 | } 158 | return result + "}"; 159 | }else if v is NSObject{ 160 | return toJson(v); 161 | }else { 162 | return "\"\(v)\""; 163 | } 164 | } 165 | 166 | /** 167 | * 字符串替换 168 | */ 169 | private static func stringReplace(source:String)->String{ 170 | var result = source; 171 | 172 | // 内容替换 173 | result = result.replacingOccurrences(of:"\\",with:"\\\\"); 174 | result = result.replacingOccurrences(of:"\"",with:"\\\""); 175 | result = result.replacingOccurrences(of:"/",with:"\\/"); 176 | result = result.replacingOccurrences(of:"\\\\b",with:"\\b"); 177 | result = result.replacingOccurrences(of:"\\\\f",with:"\\f"); 178 | result = result.replacingOccurrences(of:"\n",with:"\\n"); 179 | result = result.replacingOccurrences(of:"\r",with:"\\r"); 180 | result = result.replacingOccurrences(of:"\t",with:"\\t"); 181 | result = result.replacingOccurrences(of:"\0",with:""); 182 | 183 | return result; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /ios/Classes/view/QiniucloudConnectedPlayerPlatformView.swift: -------------------------------------------------------------------------------- 1 | // Created by 蒋具宏 on 2020/3/3. 2 | // 七牛云连麦播放视图工厂 3 | public class QiniucloudConnectedPlayerPlatformViewFactory: NSObject, FlutterPlatformViewFactory { 4 | /** 5 | * 全局标识 6 | */ 7 | public static let SIGN = "plugins.huic.top/QiniucloudConnectedPlayer"; 8 | 9 | /** 10 | * 消息器 11 | */ 12 | private var message: FlutterBinaryMessenger; 13 | 14 | 15 | init(message: FlutterBinaryMessenger) { 16 | self.message = message; 17 | } 18 | 19 | /** 20 | * 视图创建 21 | */ 22 | public func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView { 23 | 24 | let view = QiniucloudPushPlatformView(frame); 25 | 26 | // 绑定方法监听 27 | FlutterMethodChannel( 28 | name: "\(QiniucloudConnectedPlayerPlatformViewFactory.SIGN)_\(viewId)", 29 | binaryMessenger: message 30 | ).setMethodCallHandler(view.handle); 31 | 32 | return view; 33 | } 34 | } 35 | 36 | // 七牛云连麦播放器视图 37 | public class QiniucloudConnectedPlayerPlatformView: NSObject, FlutterPlatformView { 38 | /** 39 | * 窗体 40 | */ 41 | private var frame: CGRect; 42 | 43 | init(_ frame: CGRect) { 44 | self.frame = frame; 45 | } 46 | 47 | /** 48 | * 方法监听器 49 | */ 50 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 51 | switch call.method { 52 | case "startRemoteView": 53 | // self.startRemoteView(call: call, result: result); 54 | break; 55 | default: 56 | result(FlutterMethodNotImplemented); 57 | } 58 | } 59 | 60 | public func view() -> UIView { 61 | return UIView(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ios/Classes/view/QiniucloudPlayerPlatformView.swift: -------------------------------------------------------------------------------- 1 | import PLPlayerKit 2 | import UIKit 3 | 4 | // Created by 蒋具宏 on 2020/3/3. 5 | // 七牛云播放器视图工厂 6 | public class QiniucloudPlayerPlatformViewFactory : NSObject,FlutterPlatformViewFactory{ 7 | /** 8 | * 全局标识 9 | */ 10 | public static let SIGN = "plugins.huic.top/QiniucloudPlayer"; 11 | 12 | /** 13 | * 消息器 14 | */ 15 | private var message : FlutterBinaryMessenger; 16 | 17 | 18 | init(message : FlutterBinaryMessenger) { 19 | self.message = message; 20 | } 21 | 22 | /** 23 | * 视图创建 24 | */ 25 | public func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView { 26 | let view = QiniucloudPlayerPlatformView(frame); 27 | 28 | // 绑定方法监听 29 | let methodChannel = FlutterMethodChannel( 30 | name: "\(QiniucloudPlayerPlatformViewFactory.SIGN)_\(viewId)", 31 | binaryMessenger: message 32 | ); 33 | methodChannel.setMethodCallHandler(view.handle); 34 | 35 | // 初始化 36 | view.`init`(args,methodChannel); 37 | 38 | return view; 39 | } 40 | 41 | /** 42 | * 定义参数解码器 43 | */ 44 | public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol { 45 | return FlutterStandardMessageCodec.sharedInstance(); 46 | } 47 | } 48 | 49 | // 七牛云播放器视图 50 | public class QiniucloudPlayerPlatformView : NSObject,FlutterPlatformView,PLPlayerDelegate{ 51 | /** 52 | * 监听器回调的方法名 53 | */ 54 | private static let LISTENER_FUNC_NAME = "onPlayerListener"; 55 | 56 | /** 57 | * 通信管道 58 | */ 59 | private var channel : FlutterMethodChannel?; 60 | 61 | /** 62 | * 窗体 63 | */ 64 | private var frame : CGRect; 65 | 66 | /** 67 | * 播放对象 68 | */ 69 | private var player : PLPlayer?; 70 | 71 | init(_ frame : CGRect) { 72 | self.frame = frame; 73 | } 74 | 75 | public func view() -> UIView { 76 | return player!.playerView!; 77 | } 78 | 79 | /** 80 | * 方法监听器 81 | */ 82 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 83 | switch call.method { 84 | case "setDisplayAspectRatio": 85 | self.setDisplayAspectRatio(call: call, result: result); 86 | break; 87 | case "start": 88 | self.start(call: call, result: result); 89 | break; 90 | case "pause": 91 | self.pause(call: call, result: result); 92 | break; 93 | case "stopPlayback": 94 | self.stopPlayback(call: call, result: result); 95 | break; 96 | case "getRtmpVideoTimestamp": 97 | self.getRtmpVideoTimestamp(call: call, result: result); 98 | break; 99 | case "getRtmpAudioTimestamp": 100 | self.getRtmpAudioTimestamp(call: call, result: result); 101 | break; 102 | case "setBufferingEnabled": 103 | self.setBufferingEnabled(call: call, result: result); 104 | break; 105 | case "getHttpBufferSize": 106 | self.getHttpBufferSize(call: call, result: result); 107 | break; 108 | default: 109 | result(FlutterMethodNotImplemented); 110 | } 111 | } 112 | 113 | /** 114 | * 初始化 115 | */ 116 | public func `init`(_ args : Any?, _ methodChannel : FlutterMethodChannel){ 117 | // 参数转换为字典格式 118 | let dict = args as! Dictionary; 119 | 120 | // 初始化播放器 121 | self.player = PLPlayer( 122 | url: URL( 123 | string: dict["url"]! is NSNull ? "" : (dict["url"] as! String) 124 | ), 125 | option: PLPlayerOption.default() 126 | )!; 127 | self.player?.videoClipFrame = self.frame; 128 | 129 | // 绑定监听器并全局赋值 130 | self.player!.delegate = self; 131 | self.channel = methodChannel; 132 | } 133 | 134 | /** 135 | * 设置画面预览模式 136 | */ 137 | private func setDisplayAspectRatio(call: FlutterMethodCall, result: @escaping FlutterResult) { 138 | if let mode = CommonUtils.getParam(call: call, result: result, param: "mode") as? Int{ 139 | var rc = self.player!.playerView!.frame; 140 | switch mode { 141 | case 0: 142 | rc = CGRect.zero; 143 | break; 144 | case 2: 145 | break; 146 | case 3: 147 | var width : CGFloat = 0; 148 | var height : CGFloat = 0; 149 | if (rc.size.width / rc.size.height > 16.0 / 9.0) { 150 | height = rc.size.height; 151 | width = rc.size.height * 16.0 / 9.0; 152 | rc.origin.x = (rc.size.width - width ) / 2; 153 | } else { 154 | width = rc.size.width; 155 | height = rc.size.width * 9.0 / 16.0; 156 | rc.origin.y = (rc.size.height - height ) / 2; 157 | } 158 | rc.size.width = width; 159 | rc.size.height = height; 160 | break; 161 | case 4: 162 | var width : CGFloat = 0; 163 | var height : CGFloat = 0; 164 | if (rc.size.width / rc.size.height > 4.0 / 3.0) { 165 | height = rc.size.height; 166 | width = rc.size.height * 4.0 / 3.0; 167 | rc.origin.x = (rc.size.width - width ) / 2; 168 | } else { 169 | width = rc.size.width; 170 | height = rc.size.width * 3.0 / 4.0; 171 | rc.origin.y = (rc.size.height - height ) / 2; 172 | } 173 | rc.size.width = width; 174 | rc.size.height = height; 175 | break; 176 | default: 177 | rc = CGRect.zero; 178 | break; 179 | } 180 | player!.videoClipFrame = rc; 181 | result(nil); 182 | } 183 | } 184 | 185 | /** 186 | * 播放 187 | */ 188 | private func start(call: FlutterMethodCall, result: @escaping FlutterResult) { 189 | let url = ((call.arguments as! [String:Any])["url"]) as? String; 190 | if let sameSource = CommonUtils.getParam(call: call, result: result, param: "sameSource") as? Bool{ 191 | player!.play(with: url == nil ? nil : URL(string: url!), sameSource: sameSource); 192 | result(nil); 193 | } 194 | } 195 | 196 | /** 197 | * 暂停 198 | */ 199 | private func pause(call: FlutterMethodCall, result: @escaping FlutterResult) { 200 | player!.pause(); 201 | result(nil); 202 | } 203 | 204 | /** 205 | * 停止播放 206 | */ 207 | private func stopPlayback(call: FlutterMethodCall, result: @escaping FlutterResult) { 208 | player!.stop(); 209 | result(nil); 210 | } 211 | 212 | /** 213 | * 在RTMP消息中获取视频时间戳 214 | */ 215 | private func getRtmpVideoTimestamp(call: FlutterMethodCall, result: @escaping FlutterResult) { 216 | result(Int(CMTimeGetSeconds(player!.rtmpVideoTimeStamp) * 1000)); 217 | } 218 | 219 | /** 220 | * 在RTMP消息中获取音频时间戳 221 | */ 222 | private func getRtmpAudioTimestamp(call: FlutterMethodCall, result: @escaping FlutterResult) { 223 | result(Int(CMTimeGetSeconds(player!.rtmpAudioTimeStamp) * 1000)); 224 | } 225 | 226 | /** 227 | * 暂停/恢复播放器的预缓冲 228 | */ 229 | private func setBufferingEnabled(call: FlutterMethodCall, result: @escaping FlutterResult) { 230 | if let enabled = CommonUtils.getParam(call: call, result: result, param: "enabled") as? Bool{ 231 | player!.setBufferingEnabled(enabled); 232 | result(nil); 233 | } 234 | } 235 | 236 | /** 237 | * 获取已经缓冲的长度 238 | */ 239 | private func getHttpBufferSize(call: FlutterMethodCall, result: @escaping FlutterResult) { 240 | result(player!.getHttpBufferSize()); 241 | } 242 | 243 | /** 244 | * 调用监听器 245 | * 246 | * @param type 类型 247 | * @param params 参数 248 | */ 249 | private func invokeListener(type : PlayerCallBackNoticeEnum, params : Any?) { 250 | var resultParams : [String:Any] = [:]; 251 | resultParams["type"] = type; 252 | if let p = params{ 253 | resultParams["params"] = JsonUtil.toJson(p); 254 | } 255 | 256 | self.channel!.invokeMethod(QiniucloudPlayerPlatformView.LISTENER_FUNC_NAME, arguments:JsonUtil.toJson(resultParams)); 257 | } 258 | 259 | 260 | 261 | /** 262 | * 错误事件 263 | */ 264 | public func player(_ player: PLPlayer, stoppedWithError error: Error?) { 265 | self.invokeListener(type: PlayerCallBackNoticeEnum.Error, params:error?.localizedDescription); 266 | } 267 | 268 | /** 269 | * 播放状态改变事件 270 | */ 271 | public func player(_ player: PLPlayer, statusDidChange state: PLPlayerStatus) { 272 | // 准备好 273 | if state == PLPlayerStatus.statusReady{ 274 | self.invokeListener(type: PlayerCallBackNoticeEnum.Prepared, params:nil); 275 | } 276 | 277 | // 准备结束 278 | if state == PLPlayerStatus.statusCompleted{ 279 | self.invokeListener(type: PlayerCallBackNoticeEnum.Completion, params:nil); 280 | } 281 | 282 | 283 | self.invokeListener(type: PlayerCallBackNoticeEnum.Info, params: state.rawValue); 284 | } 285 | 286 | } 287 | -------------------------------------------------------------------------------- /ios/flutter_qiniucloud_live_plugin.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint flutter_qiniucloud_live_plugin.podspec' to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'flutter_qiniucloud_live_plugin' 7 | s.version = '0.0.1' 8 | s.summary = 'Flutter 七牛云直播云插件' 9 | s.description = <<-DESC 10 | Flutter 七牛云直播云插件 11 | DESC 12 | s.homepage = 'https://github.com/JiangJuHong/FlutterQiniucloudLivePlugin' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'JiangJuHong' => '690717394@qq.com' } 15 | s.source = { :path => '.' } 16 | s.source_files = 'Classes/**/*' 17 | s.dependency 'Flutter' 18 | s.platform = :ios, '8.0' 19 | 20 | # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported. 21 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } 22 | s.swift_version = '5.0' 23 | 24 | # 资源导入 25 | s.vendored_frameworks = '**/*.framework' 26 | 27 | # 七牛云直播云播放端依赖(https://developer.qiniu.com/pili/sdk/1211/ios-playback-end-the-sdk) 28 | s.dependency 'PLPlayerKit', '3.4.3' 29 | # 七牛云直播云连麦端依赖(https://developer.qiniu.com/pili/sdk/4311/PLRTCStreamingKit) 30 | s.dependency 'PLRTCStreamingKit', '3.2.3' 31 | 32 | end 33 | -------------------------------------------------------------------------------- /lib/controller/qiniucloud_connected_player_view_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_qiniucloud_live_plugin/view/qiniucloud_connected_player_view.dart'; 4 | 5 | /// 连麦视图控制器 6 | class QiniucloudConnectedPlayerViewController { 7 | QiniucloudConnectedPlayerViewController(int id) : _channel = new MethodChannel('${QiniucloudConnectPlayerViewState.type}_$id'); 8 | 9 | final MethodChannel _channel; 10 | 11 | /// 配置合流参数(仅主播端设置,连麦观众不设置) 12 | /// 使用绝对值来配置该窗口在合流画面中的位置和大小 13 | Future setAbsoluteMixOverlayRect({ 14 | @required int x, 15 | @required int y, 16 | @required int w, 17 | @required int h, 18 | }) async { 19 | return await _channel.invokeMethod('setAbsoluteMixOverlayRect', { 20 | "x": x, 21 | "y": y, 22 | "w": w, 23 | "h": h, 24 | }); 25 | } 26 | 27 | /// 配置合流参数(仅主播端设置,连麦观众不设置) 28 | /// 使用相对值来配置该窗口在合流画面中的位置和大小 29 | Future setRelativeMixOverlayRect({ 30 | @required int x, 31 | @required int y, 32 | @required int w, 33 | @required int h, 34 | }) async { 35 | return await _channel.invokeMethod('setRelativeMixOverlayRect', { 36 | "x": x, 37 | "y": y, 38 | "w": w, 39 | "h": h, 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/controller/qiniucloud_player_view_controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_qiniucloud_live_plugin/enums/qiniucloud_player_display_aspect_ratio_enum.dart'; 6 | import 'package:flutter_qiniucloud_live_plugin/enums/qiniucloud_player_listener_type_enum.dart'; 7 | import 'package:flutter_qiniucloud_live_plugin/view/qiniucloud_player_view.dart'; 8 | 9 | /// 视图控制器 10 | class QiniucloudPlayerViewController { 11 | QiniucloudPlayerViewController(int id) : _channel = new MethodChannel('${QiniucloudPlayerViewState.type}_$id'); 12 | 13 | final MethodChannel _channel; 14 | 15 | /// 监听器对象 16 | QiniucloudPlayerListener listener; 17 | 18 | /// 添加消息监听 19 | void addListener(QiniucloudPlayerListenerValue func) { 20 | if (listener == null) { 21 | listener = QiniucloudPlayerListener(_channel); 22 | } 23 | listener.addListener(func); 24 | } 25 | 26 | /// 移除消息监听 27 | void removeListener(QiniucloudPlayerListenerValue func) { 28 | if (listener == null) { 29 | listener = QiniucloudPlayerListener(_channel); 30 | } 31 | listener.removeListener(func); 32 | } 33 | 34 | /// 设置画面预览模式 35 | Future setDisplayAspectRatio({ 36 | QiniucloudPlayerDisplayAspectRatioEnum mode, 37 | }) async { 38 | return _channel.invokeMethod('setDisplayAspectRatio', { 39 | "mode": QiniucloudPlayerDisplayAspectRatioEnumTool.toInt(mode), 40 | }); 41 | } 42 | 43 | /// 开始播放 44 | Future start({ 45 | String url, // URL,如果该属性不为null,则会执行切换操作 46 | bool sameSource: false, // 是否是同种格式播放,同格式切换打开更快 @waring 当sameSource 为 YES 时,视频格式与切换前视频格式不同时,会导致视频打开失败【该属性仅IOS有效】 47 | }) async { 48 | return await _channel.invokeMethod('start', { 49 | "url": url, 50 | "sameSource": sameSource, 51 | }); 52 | } 53 | 54 | /// 暂停 55 | Future pause() async { 56 | return await _channel.invokeMethod('pause'); 57 | } 58 | 59 | /// 停止 60 | Future stopPlayback() async { 61 | return await _channel.invokeMethod('stopPlayback'); 62 | } 63 | 64 | /// 获得视频时间戳 65 | Future getRtmpVideoTimestamp() async { 66 | return await _channel.invokeMethod('getRtmpVideoTimestamp'); 67 | } 68 | 69 | /// 获得音频时间戳 70 | Future getRtmpAudioTimestamp() async { 71 | return await _channel.invokeMethod('getRtmpAudioTimestamp'); 72 | } 73 | 74 | /// 暂停/恢复播放器的预缓冲 75 | Future setBufferingEnabled({ 76 | bool enabled, 77 | }) async { 78 | return await _channel.invokeMethod('setBufferingEnabled', {"enabled": enabled}); 79 | } 80 | 81 | /// 获取已经缓冲的长度 82 | Future getHttpBufferSize() async { 83 | return await _channel.invokeMethod('getHttpBufferSize'); 84 | } 85 | } 86 | 87 | /// 七牛云播放监听器 88 | class QiniucloudPlayerListener { 89 | /// 监听器列表 90 | static Set listeners = Set(); 91 | 92 | QiniucloudPlayerListener(MethodChannel channel) { 93 | // 绑定监听器 94 | channel.setMethodCallHandler((methodCall) async { 95 | // 解析参数 96 | Map arguments = jsonDecode(methodCall.arguments); 97 | 98 | switch (methodCall.method) { 99 | case 'onPlayerListener': 100 | // 获得原始类型和参数 101 | String typeStr = arguments['type']; 102 | var params = arguments['params']; 103 | 104 | // 封装回调类型和参数 105 | QiniucloudPlayerListenerTypeEnum type; 106 | 107 | // 初始化类型 108 | for (var item in QiniucloudPlayerListenerTypeEnum.values) { 109 | if (item.toString().replaceFirst("QiniucloudPlayerListenerTypeEnum.", "") == typeStr) { 110 | type = item; 111 | break; 112 | } 113 | } 114 | 115 | // 没有找到类型就返回 116 | if (type == null) { 117 | throw MissingPluginException(); 118 | } 119 | 120 | // 回调触发 121 | for (var item in listeners) { 122 | item(type, params); 123 | } 124 | 125 | break; 126 | default: 127 | throw MissingPluginException(); 128 | } 129 | }); 130 | } 131 | 132 | /// 添加消息监听 133 | void addListener(QiniucloudPlayerListenerValue func) { 134 | listeners.add(func); 135 | } 136 | 137 | /// 移除消息监听 138 | void removeListener(QiniucloudPlayerListenerValue func) { 139 | listeners.remove(func); 140 | } 141 | } 142 | 143 | /// 推流监听器值模型 144 | typedef QiniucloudPlayerListenerValue

= void Function(QiniucloudPlayerListenerTypeEnum type, P params); 145 | -------------------------------------------------------------------------------- /lib/controller/qiniucloud_push_view_controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_qiniucloud_live_plugin/entity/conference_options_entity.dart'; 6 | import 'package:flutter_qiniucloud_live_plugin/entity/face_beauty_setting_entity.dart'; 7 | import 'package:flutter_qiniucloud_live_plugin/entity/streaming_profile_entity.dart'; 8 | import 'package:flutter_qiniucloud_live_plugin/entity/watermark_setting_entity.dart'; 9 | import 'package:flutter_qiniucloud_live_plugin/enums/qiniucloud_audio_source_type_enum.dart'; 10 | import 'package:flutter_qiniucloud_live_plugin/enums/qiniucloud_push_listener_type_enum.dart'; 11 | import 'package:flutter_qiniucloud_live_plugin/view/qiniucloud_push_view.dart'; 12 | 13 | /// 推流视图控制器 14 | class QiniucloudPushViewController { 15 | QiniucloudPushViewController(int id) : _channel = new MethodChannel('${QiniucloudPushViewState.type}_$id'); 16 | 17 | final MethodChannel _channel; 18 | 19 | /// 监听器对象 20 | QiniucloudConnectedPushListener _listener; 21 | 22 | /// 添加消息监听 23 | void addListener(QiniucloudPushListenerValue func) { 24 | if (_listener == null) { 25 | _listener = QiniucloudConnectedPushListener(_channel); 26 | } 27 | _listener.addListener(func); 28 | } 29 | 30 | /// 移除消息监听 31 | void removeListener(QiniucloudPushListenerValue func) { 32 | if (_listener == null) { 33 | return; 34 | } 35 | _listener.removeListener(func); 36 | } 37 | 38 | /// 打开摄像头和麦克风采集 39 | Future resume() async { 40 | return await _channel.invokeMethod('resume'); 41 | } 42 | 43 | /// 关闭摄像头和麦克风采集s 44 | Future pause() async { 45 | return await _channel.invokeMethod('pause'); 46 | } 47 | 48 | /// 释放不紧要资源。 49 | Future destroy() async { 50 | return await _channel.invokeMethod('destroy'); 51 | } 52 | 53 | /// 开始连麦 54 | Future startConference({ 55 | @required userId, // 用户ID 56 | @required roomName, //房间名 57 | @required roomToken, //房间token 58 | ConferenceOptionsEntity conferenceOptions, // 连麦参数(仅ios有效) 59 | }) async { 60 | return await _channel.invokeMethod('startConference', { 61 | "userId": userId, 62 | "roomName": roomName, 63 | "roomToken": roomToken, 64 | "options": conferenceOptions == null ? null : conferenceOptions.toJson(), 65 | }); 66 | } 67 | 68 | /// 停止连麦 69 | Future stopConference() async { 70 | return await _channel.invokeMethod('stopConference'); 71 | } 72 | 73 | /// 开始推流 74 | Future startStreaming({ 75 | publishUrl, // 推流地址,为null时使用全局配置上的推流地址 76 | }) async { 77 | return await _channel.invokeMethod('startStreaming', { 78 | "publishUrl": publishUrl, 79 | }); 80 | } 81 | 82 | /// 停止推流 83 | Future stopStreaming() async { 84 | return await _channel.invokeMethod('stopStreaming'); 85 | } 86 | 87 | /// 是否支持缩放 88 | Future isZoomSupported() async { 89 | return await _channel.invokeMethod('isZoomSupported'); 90 | } 91 | 92 | /// 设置缩放比例 93 | Future setZoomValue({ 94 | int value, 95 | }) async { 96 | return await _channel.invokeMethod('setZoomValue', {"value": value}); 97 | } 98 | 99 | /// 获得最大缩放比例 100 | Future getMaxZoom() async { 101 | return await _channel.invokeMethod('getMaxZoom'); 102 | } 103 | 104 | /// 获得当前缩放比例 105 | Future getZoom() async { 106 | return await _channel.invokeMethod('getZoom'); 107 | } 108 | 109 | /// 开启闪光灯 110 | Future turnLightOn() async { 111 | return await _channel.invokeMethod('turnLightOn'); 112 | } 113 | 114 | /// 关闭闪光灯 115 | Future turnLightOff() async { 116 | return await _channel.invokeMethod('turnLightOff'); 117 | } 118 | 119 | /// 切换摄像头 120 | Future switchCamera() async { 121 | return await _channel.invokeMethod('switchCamera'); 122 | } 123 | 124 | /// 静音 125 | Future mute({ 126 | bool mute, 127 | QiniucloudAudioSourceTypeEnum audioSource, 128 | }) async { 129 | return await _channel.invokeMethod('mute', { 130 | "mute": mute, 131 | "audioSource": audioSource == null ? null : audioSource.toString().replaceAll("QiniucloudAudioSourceTypeEnum.", ""), 132 | }); 133 | } 134 | 135 | /// 更新水印信息 136 | Future updateWatermarkSetting(WatermarkSettingEntity data) async { 137 | return await _channel.invokeMethod('updateWatermarkSetting', data.toJson()); 138 | } 139 | 140 | /// 更新美颜信息 141 | Future updateFaceBeautySetting(FaceBeautySettingEntity data) async { 142 | return await _channel.invokeMethod('updateFaceBeautySetting', data.toJson()); 143 | } 144 | 145 | /// 改变预览镜像 146 | Future setPreviewMirror({ 147 | bool mirror, 148 | }) async { 149 | return await _channel.invokeMethod('setPreviewMirror', { 150 | "mirror": mirror, 151 | }); 152 | } 153 | 154 | /// 改变推流镜像 155 | Future setEncodingMirror({ 156 | bool mirror, 157 | }) async { 158 | return await _channel.invokeMethod('setEncodingMirror', { 159 | "mirror": mirror, 160 | }); 161 | } 162 | 163 | /// 开启耳返 164 | Future startPlayback() async { 165 | return await _channel.invokeMethod('startPlayback'); 166 | } 167 | 168 | /// 关闭耳返 169 | Future stopPlayback() async { 170 | return await _channel.invokeMethod('stopPlayback'); 171 | } 172 | 173 | /// 更新推流参数 174 | Future setStreamingProfile({ 175 | StreamingProfileEntity streamingProfile, 176 | }) async { 177 | return await _channel.invokeMethod('setStreamingProfile', { 178 | "streamingProfile": streamingProfile, 179 | }); 180 | } 181 | 182 | /// 获取编码器输出的画面的高宽 183 | Future getVideoEncodingSize() async { 184 | return jsonDecode(await _channel.invokeMethod('getVideoEncodingSize')); 185 | } 186 | 187 | /// 设置混音 - [path] 音频文件 188 | /// [loop] 是否循环播放 189 | Future setMix({String path, bool loop: false}) async => await _channel.invokeMethod('setMix', {"path": path, "loop": loop}); 190 | 191 | /// 设置混音音量 192 | /// [volume] 音量 193 | Future setMixVolume({double volume}) async => await _channel.invokeMethod('setMixVolume', {"volume": volume}); 194 | 195 | /// 释放当前音频资源 196 | Future closeCurrentAudio() async => await _channel.invokeMethod('closeCurrentAudio'); 197 | } 198 | 199 | /// 七牛云连麦监听器 200 | class QiniucloudConnectedPushListener { 201 | /// 监听器列表 202 | Set _listeners = Set(); 203 | 204 | QiniucloudConnectedPushListener(MethodChannel channel) { 205 | // 绑定监听器 206 | channel.setMethodCallHandler((methodCall) async { 207 | // 解析参数 208 | Map arguments = jsonDecode(methodCall.arguments); 209 | 210 | switch (methodCall.method) { 211 | case 'onPushListener': 212 | // 获得原始类型和参数 213 | String typeStr = arguments['type']; 214 | String paramsStr = arguments['params']; 215 | 216 | // 封装回调类型和参数 217 | QiniucloudPushListenerTypeEnum type; 218 | Object params; 219 | 220 | // 初始化类型 221 | for (var item in QiniucloudPushListenerTypeEnum.values) { 222 | if (item.toString().replaceFirst("QiniucloudPushListenerTypeEnum.", "") == typeStr) { 223 | type = item; 224 | break; 225 | } 226 | } 227 | 228 | // 没有找到类型就返回 229 | if (type == null) { 230 | throw MissingPluginException(); 231 | } 232 | 233 | // 回调触发,只接收最后一个返回值 234 | dynamic result; 235 | for (var item in _listeners) { 236 | result = item(type, params ?? paramsStr); 237 | } 238 | return result; 239 | default: 240 | throw MissingPluginException(); 241 | } 242 | }); 243 | } 244 | 245 | /// 添加消息监听 246 | void addListener(QiniucloudPushListenerValue func) { 247 | _listeners.add(func); 248 | } 249 | 250 | /// 移除消息监听 251 | void removeListener(QiniucloudPushListenerValue func) { 252 | _listeners.remove(func); 253 | } 254 | } 255 | 256 | /// 推流监听器值模型 257 | typedef QiniucloudPushListenerValue

= dynamic Function(QiniucloudPushListenerTypeEnum type, P params); 258 | -------------------------------------------------------------------------------- /lib/entity/camera_streaming_setting_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_qiniucloud_live_plugin/enums/qiniucloud_camera_type_enum.dart'; 2 | import 'package:flutter_qiniucloud_live_plugin/enums/qiniucloud_push_preview_size_level_enum.dart'; 3 | 4 | import 'face_beauty_setting_entity.dart'; 5 | 6 | /// 摄像头设置相关参数 7 | class CameraStreamingSettingEntity { 8 | // 启用美颜功能 9 | bool builtInFaceBeautyEnabled; 10 | 11 | // 美颜设置 12 | FaceBeautySettingEntity faceBeauty; 13 | 14 | // 摄像头 15 | QiniucloudCameraTypeEnum cameraFacingId; 16 | 17 | // 启用镜像翻转(预览) 18 | bool frontCameraPreviewMirror; 19 | 20 | // 启用镜像翻转(播放端) 21 | bool frontCameraMirror; 22 | 23 | // 启用自动对焦 24 | bool continuousFocusModeEnabled; 25 | 26 | // 预览大小级别 27 | QiniucloudPushPreviewSizeLevelEnum cameraPrvSizeLevel; 28 | 29 | CameraStreamingSettingEntity({ 30 | this.builtInFaceBeautyEnabled: true, 31 | this.cameraFacingId: QiniucloudCameraTypeEnum.CAMERA_FACING_BACK, 32 | this.frontCameraPreviewMirror: false, 33 | this.faceBeauty, 34 | this.frontCameraMirror: false, 35 | this.continuousFocusModeEnabled: true, 36 | this.cameraPrvSizeLevel: QiniucloudPushPreviewSizeLevelEnum.MEDIUM, 37 | }); 38 | 39 | CameraStreamingSettingEntity.fromJson(Map json) { 40 | builtInFaceBeautyEnabled = json['builtInFaceBeautyEnabled']; 41 | cameraFacingId = json['cameraFacingId']; 42 | frontCameraPreviewMirror = json['frontCameraPreviewMirror']; 43 | faceBeauty = json['faceBeautySetting']; 44 | frontCameraMirror = json['frontCameraMirror']; 45 | continuousFocusModeEnabled = json['continuousFocusModeEnabled']; 46 | cameraPrvSizeLevel = json['cameraPrvSizeLevel']; 47 | } 48 | 49 | Map toJson() { 50 | final Map data = new Map(); 51 | data['builtInFaceBeautyEnabled'] = this.builtInFaceBeautyEnabled; 52 | data['cameraFacingId'] = this.cameraFacingId != null 53 | ? this 54 | .cameraFacingId 55 | .toString() 56 | .replaceAll("QiniucloudCameraTypeEnum.", "") 57 | : null; 58 | data['frontCameraPreviewMirror'] = this.frontCameraPreviewMirror; 59 | data['faceBeauty'] = 60 | this.faceBeauty != null ? this.faceBeauty.toJson() : null; 61 | data['frontCameraMirror'] = this.frontCameraMirror; 62 | data['continuousFocusModeEnabled'] = this.continuousFocusModeEnabled; 63 | data['cameraPrvSizeLevel'] = this.cameraPrvSizeLevel != null 64 | ? this 65 | .cameraPrvSizeLevel 66 | .toString() 67 | .replaceAll("QiniucloudPushPreviewSizeLevelEnum.", "") 68 | : null; 69 | return data; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/entity/conference_options_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_qiniucloud_live_plugin/enums/qiniucloud_encoding_orientation_enum.dart'; 2 | 3 | /// 连麦参数实体 4 | class ConferenceOptionsEntity { 5 | /// 编码方向 6 | QiniucloudEncodingOrientationEnum videoEncodingOrientation; 7 | 8 | ConferenceOptionsEntity({ 9 | this.videoEncodingOrientation, 10 | }); 11 | 12 | ConferenceOptionsEntity.fromJson(Map json) { 13 | videoEncodingOrientation = json['videoEncodingOrientation']; 14 | } 15 | 16 | Map toJson() { 17 | final Map data = new Map(); 18 | data['videoEncodingOrientation'] = this.videoEncodingOrientation == null ? null : this.videoEncodingOrientation.toString().replaceAll("QiniucloudEncodingOrientationEnum.", ""); 19 | return data; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/entity/face_beauty_setting_entity.dart: -------------------------------------------------------------------------------- 1 | /// 美颜设置实体 2 | class FaceBeautySettingEntity { 3 | // 美颜等级 4 | double beautyLevel; 5 | // 红润 6 | double redden; 7 | // 美白 8 | double whiten; 9 | 10 | FaceBeautySettingEntity({this.beautyLevel : 0, this.redden : 0, this.whiten : 0}); 11 | 12 | FaceBeautySettingEntity.fromJson(Map json) { 13 | beautyLevel = json['beautyLevel']; 14 | redden = json['redden']; 15 | whiten = json['whiten']; 16 | } 17 | 18 | Map toJson() { 19 | final Map data = new Map(); 20 | data['beautyLevel'] = this.beautyLevel; 21 | data['redden'] = this.redden; 22 | data['whiten'] = this.whiten; 23 | return data; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/entity/streaming_profile_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_qiniucloud_live_plugin/enums/qiniucloud_push_audio_quality_enum.dart'; 2 | import 'package:flutter_qiniucloud_live_plugin/enums/qiniucloud_push_video_quality_enum.dart'; 3 | 4 | /// 系统参数 5 | class StreamingProfileEntity { 6 | /// 推流URL,格式为: rtmp://xxxx,如果在初始化时不设置,则需要在推流时进行设置 7 | String publishUrl; 8 | 9 | /// 视频质量 10 | QiniucloudPushVideoQualityEnum videoQuality; 11 | 12 | /// 音频质量 13 | QiniucloudPushAudioQualityEnum audioQuality; 14 | 15 | /// 是否启用 QUIC 推流 16 | bool quicEnable; 17 | 18 | StreamingProfileEntity({ 19 | this.publishUrl, 20 | this.videoQuality: QiniucloudPushVideoQualityEnum.VIDEO_QUALITY_HIGH3, 21 | this.audioQuality: QiniucloudPushAudioQualityEnum.AUDIO_QUALITY_96, 22 | this.quicEnable: true, 23 | }); 24 | 25 | StreamingProfileEntity.fromJson(Map json) { 26 | publishUrl = json["publishUrl"]; 27 | videoQuality = json["videoQuality"]; 28 | audioQuality = json["audioQuality"]; 29 | quicEnable = json["quicEnable"]; 30 | } 31 | 32 | Map toJson() { 33 | final Map data = new Map(); 34 | if(this.publishUrl != null){ 35 | data["publishUrl"] = this.publishUrl; 36 | } 37 | data["videoQuality"] = this.videoQuality == null 38 | ? null 39 | : QiniucloudPushVideoQualityEnumTool.toInt(this.videoQuality); 40 | data["audioQuality"] = this.audioQuality == null 41 | ? null 42 | : QiniucloudPushAudioQualityTool.toInt(this.audioQuality); 43 | data["quicEnable"] = this.quicEnable; 44 | return data; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/entity/watermark_setting_entity.dart: -------------------------------------------------------------------------------- 1 | /// 水印设置实体 2 | class WatermarkSettingEntity { 3 | // 水印大小 4 | WatermarkSizeEnum size; 5 | 6 | // 水印资源路径 7 | String resourcePath; 8 | 9 | // 10 | int alpha; 11 | 12 | // 水印位置 13 | WatermarkLocation location; 14 | 15 | WatermarkSettingEntity( 16 | {this.size, this.resourcePath, this.alpha, this.location}); 17 | 18 | WatermarkSettingEntity.fromJson(Map json) { 19 | if (json['size'] != null) { 20 | for (var item in WatermarkSizeEnum.values) { 21 | if (item.toString().replaceAll("WatermarkSizeEnum.", "") == 22 | json['size']) { 23 | size = item; 24 | break; 25 | } 26 | } 27 | } 28 | resourcePath = json['resourcePath']; 29 | alpha = json['alpha']; 30 | if (json['location'] != null) { 31 | for (var item in WatermarkLocation.values) { 32 | if (item.toString().replaceAll("WatermarkLocation.", "") == 33 | json['location']) { 34 | location = item; 35 | break; 36 | } 37 | } 38 | } 39 | } 40 | 41 | Map toJson() { 42 | final Map data = new Map(); 43 | data['size'] = this.size == null 44 | ? null 45 | : this.size.toString().replaceAll("WatermarkSizeEnum.", ""); 46 | data['resourcePath'] = this.resourcePath; 47 | data['alpha'] = this.alpha; 48 | data['location'] = this.location == null 49 | ? null 50 | : this.location.toString().replaceAll("WatermarkLocation.", ""); 51 | return data; 52 | } 53 | } 54 | 55 | /// 水印大小枚举 56 | enum WatermarkSizeEnum { 57 | LARGE, 58 | MEDIUM, 59 | SMALL, 60 | } 61 | 62 | /// 水印位置枚举 63 | enum WatermarkLocation { 64 | NORTH_WEST, 65 | NORTH_EAST, 66 | SOUTH_WEST, 67 | SOUTH_EAST, 68 | } 69 | -------------------------------------------------------------------------------- /lib/enums/qiniucloud_audio_source_type_enum.dart: -------------------------------------------------------------------------------- 1 | /// 静音设备 2 | enum QiniucloudAudioSourceTypeEnum { 3 | // 麦克风 4 | MIC, 5 | // 扬声器 6 | SPEAKER, 7 | } 8 | -------------------------------------------------------------------------------- /lib/enums/qiniucloud_camera_type_enum.dart: -------------------------------------------------------------------------------- 1 | /// 七牛云摄像头类型枚举 2 | enum QiniucloudCameraTypeEnum { 3 | CAMERA_FACING_BACK, 4 | CAMERA_FACING_FRONT, 5 | } 6 | -------------------------------------------------------------------------------- /lib/enums/qiniucloud_encoding_orientation_enum.dart: -------------------------------------------------------------------------------- 1 | /// 编码方向 2 | enum QiniucloudEncodingOrientationEnum { 3 | PORT, 4 | LAND, 5 | } 6 | -------------------------------------------------------------------------------- /lib/enums/qiniucloud_player_display_aspect_ratio_enum.dart: -------------------------------------------------------------------------------- 1 | /// 七牛云播放 画面预览模式 2 | enum QiniucloudPlayerDisplayAspectRatioEnum { 3 | ASPECT_RATIO_ORIGIN, 4 | ASPECT_RATIO_PAVED_PARENT, 5 | ASPECT_RATIO_16_9, 6 | ASPECT_RATIO_4_3, 7 | } 8 | 9 | /// 枚举工具类 10 | class QiniucloudPlayerDisplayAspectRatioEnumTool { 11 | // 将枚举转换为int类型 12 | static int toInt(QiniucloudPlayerDisplayAspectRatioEnum type) { 13 | switch (type) { 14 | case QiniucloudPlayerDisplayAspectRatioEnum.ASPECT_RATIO_ORIGIN: 15 | return 0; 16 | case QiniucloudPlayerDisplayAspectRatioEnum.ASPECT_RATIO_PAVED_PARENT: 17 | return 2; 18 | case QiniucloudPlayerDisplayAspectRatioEnum.ASPECT_RATIO_16_9: 19 | return 3; 20 | case QiniucloudPlayerDisplayAspectRatioEnum.ASPECT_RATIO_4_3: 21 | return 4; 22 | default: 23 | return null; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/enums/qiniucloud_player_listener_type_enum.dart: -------------------------------------------------------------------------------- 1 | /// 七牛云播放器监听类型枚举 2 | enum QiniucloudPlayerListenerTypeEnum { 3 | /// 播放结束 4 | Completion, 5 | 6 | /// 错误事件 7 | Error, 8 | 9 | /// 状态消息 10 | Info, 11 | 12 | /// 已经准备好 13 | Prepared, 14 | 15 | /// 该回调用于监听当前播放的视频流的尺寸信息,在 SDK 解析出视频的尺寸信息后,会触发该回调,开发者可以在该回调中调整 UI 的视图尺寸。 16 | VideoSizeChanged 17 | } 18 | -------------------------------------------------------------------------------- /lib/enums/qiniucloud_push_audio_quality_enum.dart: -------------------------------------------------------------------------------- 1 | /// 七牛云推流音频质量 2 | enum QiniucloudPushAudioQualityEnum { 3 | AUDIO_QUALITY_96, 4 | AUDIO_QUALITY_128, 5 | } 6 | 7 | /// 枚举工具类 8 | class QiniucloudPushAudioQualityTool { 9 | // 将枚举转换为int类型 10 | static int toInt(QiniucloudPushAudioQualityEnum type) { 11 | switch (type) { 12 | case QiniucloudPushAudioQualityEnum.AUDIO_QUALITY_96: 13 | return 20; 14 | case QiniucloudPushAudioQualityEnum.AUDIO_QUALITY_128: 15 | return 21; 16 | default: 17 | return null; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/enums/qiniucloud_push_listener_type_enum.dart: -------------------------------------------------------------------------------- 1 | /// 七牛云连麦推流监听类型枚举 2 | enum QiniucloudPushListenerTypeEnum { 3 | /// 回调音频采集 PCM 数据 4 | AudioSourceAvailable, 5 | 6 | /// 根据StreamingProfile.StreamStatusConfig.getIntervalMs()调用 7 | StreamStatusChanged, 8 | 9 | /// 录音失败时调用。 10 | RecordAudioFailedHandled, 11 | 12 | /// 重新启动流式处理通知。 13 | RestartStreamingHandled, 14 | 15 | /// 在构造相机对象后调用。 16 | PreviewSizeSelected, 17 | 18 | /// 自定义预览fps,在构造相机对象后调用。 19 | PreviewFpsSelected, 20 | 21 | /// 状态发生改变 22 | StateChanged, 23 | 24 | /// 连麦状态改变 25 | ConferenceStateChanged, 26 | 27 | /// 用户加入连麦 28 | UserJoinConference, 29 | 30 | /// 用户离开连麦 31 | UserLeaveConference, 32 | } 33 | -------------------------------------------------------------------------------- /lib/enums/qiniucloud_push_preview_size_level_enum.dart: -------------------------------------------------------------------------------- 1 | /// 预览大小级别 2 | enum QiniucloudPushPreviewSizeLevelEnum{ 3 | SMALL, 4 | MEDIUM, 5 | LARGE, 6 | } -------------------------------------------------------------------------------- /lib/enums/qiniucloud_push_video_quality_enum.dart: -------------------------------------------------------------------------------- 1 | /// 七牛云推流视频质量 2 | enum QiniucloudPushVideoQualityEnum { 3 | VIDEO_QUALITY_LOW1, 4 | VIDEO_QUALITY_LOW2, 5 | VIDEO_QUALITY_LOW3, 6 | VIDEO_QUALITY_MEDIUM1, 7 | VIDEO_QUALITY_MEDIUM2, 8 | VIDEO_QUALITY_MEDIUM3, 9 | VIDEO_QUALITY_HIGH1, 10 | VIDEO_QUALITY_HIGH2, 11 | VIDEO_QUALITY_HIGH3, 12 | } 13 | 14 | /// 枚举工具类 15 | class QiniucloudPushVideoQualityEnumTool { 16 | // 将枚举转换为int类型 17 | static int toInt(QiniucloudPushVideoQualityEnum type) { 18 | switch (type) { 19 | case QiniucloudPushVideoQualityEnum.VIDEO_QUALITY_LOW1: 20 | return 0; 21 | case QiniucloudPushVideoQualityEnum.VIDEO_QUALITY_LOW2: 22 | return 1; 23 | case QiniucloudPushVideoQualityEnum.VIDEO_QUALITY_LOW3: 24 | return 2; 25 | case QiniucloudPushVideoQualityEnum.VIDEO_QUALITY_MEDIUM1: 26 | return 10; 27 | case QiniucloudPushVideoQualityEnum.VIDEO_QUALITY_MEDIUM2: 28 | return 11; 29 | case QiniucloudPushVideoQualityEnum.VIDEO_QUALITY_MEDIUM3: 30 | return 12; 31 | case QiniucloudPushVideoQualityEnum.VIDEO_QUALITY_HIGH1: 32 | return 20; 33 | case QiniucloudPushVideoQualityEnum.VIDEO_QUALITY_HIGH2: 34 | return 21; 35 | case QiniucloudPushVideoQualityEnum.VIDEO_QUALITY_HIGH3: 36 | return 22; 37 | default: 38 | return null; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/flutter_qiniucloud_live_plugin.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/services.dart'; 4 | 5 | class FlutterQiniucloudLivePlugin { 6 | static const MethodChannel _channel = 7 | const MethodChannel('flutter_qiniucloud_live_plugin'); 8 | 9 | static Future get platformVersion async { 10 | return await _channel.invokeMethod('init'); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/view/qiniucloud_connected_player_view.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter_qiniucloud_live_plugin/controller/qiniucloud_connected_player_view_controller.dart'; 4 | 5 | /// 七牛云连麦推流预览窗口 6 | class QiniucloudConnectPlayerView extends StatefulWidget { 7 | /// 创建事件 8 | final ViewCreated onViewCreated; 9 | 10 | const QiniucloudConnectPlayerView({ 11 | Key key, 12 | this.onViewCreated, 13 | }) : super(key: key); 14 | 15 | @override 16 | State createState() => QiniucloudConnectPlayerViewState(); 17 | } 18 | 19 | class QiniucloudConnectPlayerViewState extends State { 20 | /// 唯一标识符,需要与PlatformView标识对应 21 | static const String type = "plugins.huic.top/QiniucloudConnectedPlayer"; 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | if (Platform.isAndroid) { 26 | return AndroidView( 27 | viewType: type, 28 | onPlatformViewCreated: _onPlatformViewCreated, 29 | ); 30 | } else if (Platform.isIOS) { 31 | return UiKitView( 32 | viewType: type, 33 | onPlatformViewCreated: _onPlatformViewCreated, 34 | ); 35 | } else { 36 | return Text("不支持的平台"); 37 | } 38 | } 39 | 40 | /// 创建事件 41 | void _onPlatformViewCreated(int id) { 42 | if (widget.onViewCreated != null) { 43 | widget.onViewCreated(id, QiniucloudConnectedPlayerViewController(id)); 44 | } 45 | } 46 | } 47 | 48 | typedef ViewCreated = void Function(int viewId, T value); 49 | -------------------------------------------------------------------------------- /lib/view/qiniucloud_player_view.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:flutter_qiniucloud_live_plugin/controller/qiniucloud_player_view_controller.dart'; 7 | 8 | /// 七牛云播放器窗口 9 | class QiniucloudPlayerView extends StatefulWidget { 10 | /// 创建事件 11 | final ValueChanged onViewCreated; 12 | 13 | /// 播放地址 14 | final String url; 15 | 16 | const QiniucloudPlayerView({ 17 | Key key, 18 | this.onViewCreated, 19 | this.url, 20 | }) : super(key: key); 21 | 22 | @override 23 | State createState() => QiniucloudPlayerViewState(); 24 | } 25 | 26 | class QiniucloudPlayerViewState extends State { 27 | /// 唯一标识符,需要与PlatformView标识对应 28 | static const String type = "plugins.huic.top/QiniucloudPlayer"; 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | // 请求参数 33 | Map params = { 34 | "url": widget.url, 35 | }; 36 | // 请求参数解码器 37 | var paramsCodec = StandardMessageCodec(); 38 | 39 | if (Platform.isAndroid) { 40 | return AndroidView( 41 | viewType: type, 42 | creationParams: params, 43 | onPlatformViewCreated: _onPlatformViewCreated, 44 | creationParamsCodec: paramsCodec, 45 | ); 46 | } else if (Platform.isIOS) { 47 | return UiKitView( 48 | viewType: type, 49 | creationParams: params, 50 | onPlatformViewCreated: _onPlatformViewCreated, 51 | creationParamsCodec: paramsCodec, 52 | ); 53 | } else { 54 | return Text("不支持的平台"); 55 | } 56 | } 57 | 58 | /// 创建事件 59 | void _onPlatformViewCreated(int id) { 60 | if (widget.onViewCreated != null) { 61 | widget.onViewCreated(QiniucloudPlayerViewController(id)); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/view/qiniucloud_push_view.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:flutter_qiniucloud_live_plugin/controller/qiniucloud_push_view_controller.dart'; 7 | import 'package:flutter_qiniucloud_live_plugin/entity/camera_streaming_setting_entity.dart'; 8 | import 'package:flutter_qiniucloud_live_plugin/entity/streaming_profile_entity.dart'; 9 | 10 | /// 七牛云连麦推流预览窗口 11 | class QiniucloudPushView extends StatefulWidget { 12 | /// 系统参数 13 | final StreamingProfileEntity streamingProfile; 14 | 15 | /// 相机设置 16 | final CameraStreamingSettingEntity cameraStreamingSetting; 17 | 18 | /// 创建事件 19 | final ValueChanged onViewCreated; 20 | 21 | /// 是否仅音频模式 22 | final bool onlyAudio; 23 | 24 | const QiniucloudPushView({Key key, this.onViewCreated, this.cameraStreamingSetting, this.streamingProfile, this.onlyAudio = false}) : super(key: key); 25 | 26 | @override 27 | State createState() => QiniucloudPushViewState(); 28 | } 29 | 30 | class QiniucloudPushViewState extends State { 31 | /// 唯一标识符,需要与PlatformView标识对应 32 | static const String type = "plugins.huic.top/QiniucloudPush"; 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | // 请求参数 37 | Map params = { 38 | "streamingProfile": widget.streamingProfile != null ? jsonEncode(widget.streamingProfile) : null, 39 | "cameraStreamingSetting": widget.cameraStreamingSetting != null ? jsonEncode(widget.cameraStreamingSetting) : null, 40 | "onlyAudio": widget.onlyAudio, 41 | }; 42 | // 请求参数解码器 43 | var paramsCodec = StandardMessageCodec(); 44 | 45 | if (Platform.isAndroid) { 46 | return AndroidView( 47 | viewType: type, 48 | creationParams: params, 49 | onPlatformViewCreated: _onPlatformViewCreated, 50 | creationParamsCodec: paramsCodec, 51 | ); 52 | } else if (Platform.isIOS) { 53 | return UiKitView( 54 | viewType: type, 55 | creationParams: params, 56 | onPlatformViewCreated: _onPlatformViewCreated, 57 | creationParamsCodec: paramsCodec, 58 | ); 59 | } else { 60 | return Text("不支持的平台"); 61 | } 62 | } 63 | 64 | /// 创建事件 65 | void _onPlatformViewCreated(int id) { 66 | if (widget.onViewCreated != null) { 67 | widget.onViewCreated(QiniucloudPushViewController(id)); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.10.0" 12 | boolean_selector: 13 | dependency: transitive 14 | description: 15 | name: boolean_selector 16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.1.1" 20 | characters: 21 | dependency: transitive 22 | description: 23 | name: characters 24 | sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "1.2.1" 28 | clock: 29 | dependency: transitive 30 | description: 31 | name: clock 32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.1.1" 36 | collection: 37 | dependency: transitive 38 | description: 39 | name: collection 40 | sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.17.0" 44 | fake_async: 45 | dependency: transitive 46 | description: 47 | name: fake_async 48 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.3.1" 52 | flutter: 53 | dependency: "direct main" 54 | description: flutter 55 | source: sdk 56 | version: "0.0.0" 57 | flutter_test: 58 | dependency: "direct dev" 59 | description: flutter 60 | source: sdk 61 | version: "0.0.0" 62 | js: 63 | dependency: transitive 64 | description: 65 | name: js 66 | sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" 67 | url: "https://pub.dev" 68 | source: hosted 69 | version: "0.6.5" 70 | matcher: 71 | dependency: transitive 72 | description: 73 | name: matcher 74 | sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" 75 | url: "https://pub.dev" 76 | source: hosted 77 | version: "0.12.13" 78 | material_color_utilities: 79 | dependency: transitive 80 | description: 81 | name: material_color_utilities 82 | sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 83 | url: "https://pub.dev" 84 | source: hosted 85 | version: "0.2.0" 86 | meta: 87 | dependency: transitive 88 | description: 89 | name: meta 90 | sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" 91 | url: "https://pub.dev" 92 | source: hosted 93 | version: "1.8.0" 94 | path: 95 | dependency: transitive 96 | description: 97 | name: path 98 | sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b 99 | url: "https://pub.dev" 100 | source: hosted 101 | version: "1.8.2" 102 | sky_engine: 103 | dependency: transitive 104 | description: flutter 105 | source: sdk 106 | version: "0.0.99" 107 | source_span: 108 | dependency: transitive 109 | description: 110 | name: source_span 111 | sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 112 | url: "https://pub.dev" 113 | source: hosted 114 | version: "1.9.1" 115 | stack_trace: 116 | dependency: transitive 117 | description: 118 | name: stack_trace 119 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 120 | url: "https://pub.dev" 121 | source: hosted 122 | version: "1.11.0" 123 | stream_channel: 124 | dependency: transitive 125 | description: 126 | name: stream_channel 127 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" 128 | url: "https://pub.dev" 129 | source: hosted 130 | version: "2.1.1" 131 | string_scanner: 132 | dependency: transitive 133 | description: 134 | name: string_scanner 135 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 136 | url: "https://pub.dev" 137 | source: hosted 138 | version: "1.2.0" 139 | term_glyph: 140 | dependency: transitive 141 | description: 142 | name: term_glyph 143 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 144 | url: "https://pub.dev" 145 | source: hosted 146 | version: "1.2.1" 147 | test_api: 148 | dependency: transitive 149 | description: 150 | name: test_api 151 | sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 152 | url: "https://pub.dev" 153 | source: hosted 154 | version: "0.4.16" 155 | vector_math: 156 | dependency: transitive 157 | description: 158 | name: vector_math 159 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 160 | url: "https://pub.dev" 161 | source: hosted 162 | version: "2.1.4" 163 | sdks: 164 | dart: ">=2.18.0 <3.0.0" 165 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_qiniucloud_live_plugin 2 | description: Flutter 七牛云直播云插件 3 | version: 0.0.1 4 | author: 5 | homepage: https://github.com/JiangJuHong/FlutterQiniucloudLivePlugin 6 | 7 | environment: 8 | sdk: ">=2.1.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | dev_dependencies: 15 | flutter_test: 16 | sdk: flutter 17 | 18 | # For information on the generic Dart part of this file, see the 19 | # following page: https://dart.dev/tools/pub/pubspec 20 | 21 | # The following section is specific to Flutter. 22 | flutter: 23 | # This section identifies this Flutter project as a plugin project. 24 | # The androidPackage and pluginClass identifiers should not ordinarily 25 | # be modified. They are used by the tooling to maintain consistency when 26 | # adding or updating assets for this project. 27 | plugin: 28 | platforms: 29 | android: 30 | package: top.huic.flutter_qiniucloud_live_plugin 31 | pluginClass: FlutterQiniucloudLivePlugin 32 | ios: 33 | pluginClass: FlutterQiniucloudLivePlugin 34 | 35 | # To add assets to your plugin package, add an assets section, like this: 36 | # assets: 37 | # - images/a_dot_burr.jpeg 38 | # - images/a_dot_ham.jpeg 39 | # 40 | # For details regarding assets in packages, see 41 | # https://flutter.dev/assets-and-images/#from-packages 42 | # 43 | # An image asset can refer to one or more resolution-specific "variants", see 44 | # https://flutter.dev/assets-and-images/#resolution-aware. 45 | 46 | # To add custom fonts to your plugin package, add a fonts section here, 47 | # in this "flutter" section. Each entry in this list should have a 48 | # "family" key with the font family name, and a "fonts" key with a 49 | # list giving the asset and other descriptors for the font. For 50 | # example: 51 | # fonts: 52 | # - family: Schyler 53 | # fonts: 54 | # - asset: fonts/Schyler-Regular.ttf 55 | # - asset: fonts/Schyler-Italic.ttf 56 | # style: italic 57 | # - family: Trajan Pro 58 | # fonts: 59 | # - asset: fonts/TrajanPro.ttf 60 | # - asset: fonts/TrajanPro_Bold.ttf 61 | # weight: 700 62 | # 63 | # For details regarding fonts in packages, see 64 | # https://flutter.dev/custom-fonts/#from-packages 65 | --------------------------------------------------------------------------------