国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁 > news >正文

網(wǎng)站建設(shè) 上市公司杭州網(wǎng)站seo外包

網(wǎng)站建設(shè) 上市公司,杭州網(wǎng)站seo外包,農(nóng)業(yè)技術(shù)推廣網(wǎng)站,做期貨在哪個(gè)網(wǎng)站查資料前言由于Flutter項(xiàng)目中需要使用到播放器功能,因此對(duì)flutter中各種播放器解決方案進(jìn)行了一番研究和比對(duì),最后決定還是自己通過Plugin的方法去引用原生播放器符合自己的需求,本篇文章會(huì)對(duì)各種解決方案做一個(gè)簡單的比較,以及講解一下…

前言

由于Flutter項(xiàng)目中需要使用到播放器功能,因此對(duì)flutter中各種播放器解決方案進(jìn)行了一番研究和比對(duì),最后決定還是自己通過Plugin的方法去引用原生播放器符合自己的需求,本篇文章會(huì)對(duì)各種解決方案做一個(gè)簡單的比較,以及講解一下發(fā)Flutter3.0中ios引用原生view的步驟和邏輯,方便大家遇到相同問題時(shí),可以進(jìn)行一個(gè)參考。

video_player

video_player:flutter官方出品。

優(yōu)點(diǎn):集成速度快,易上手,使用簡單,同時(shí)支持Android,ios,web;
缺點(diǎn):ui丑,功能簡單,可定制化差,不支持rtmp等協(xié)議直播;
適用:對(duì)播放器要求不高,不需要直播,只要視頻能播放出來就可以的用戶;

better_player

better_player:國外大神在video_player的基礎(chǔ)之上二次開發(fā)而來,對(duì)video_player進(jìn)行了各種優(yōu)化,并添加了非常多的實(shí)用功能。

優(yōu)點(diǎn):同video_player,且比video_player更強(qiáng)大一點(diǎn);
缺點(diǎn):依然是可定制化差,不支持rtmp等直播格式;
適用:對(duì)視頻播放器稍微有要求比如視頻格式,播放速度,緩存等功能,又不想自己動(dòng)手去寫原生插件,對(duì)ui定制化要求也不高的用戶;

fijkplayer

fijkplayer:基于ijkplayer,是對(duì) ijkplayer 的 Flutter 封裝,支持安卓和ios;

優(yōu)缺點(diǎn):做過安卓和ios原生的,對(duì)ijk應(yīng)該都非常熟悉了,這里就不做過多說明了;
fijkplayer支持各種視頻格式包括rtmp等協(xié)議直播,支持各種常用功能,支持定制化UI,具體可以去它的官網(wǎng)查看文檔說明;
適用:對(duì)播放器要求稍高,需要簡單定制化播放器ui,需要支持直播的用戶;

這里說一些個(gè)人的使用體驗(yàn),因?yàn)槲业捻?xiàng)目需要支持rtmp直播,并且對(duì)ui定制化要求較高,所以放棄了官方的video_player,優(yōu)先選擇了fijkplayer,但實(shí)際體驗(yàn)上不太盡如人意。雖然支持ui定制,但想要達(dá)到自己產(chǎn)品的ui設(shè)計(jì),還需要費(fèi)上很大一番功夫。并且該項(xiàng)目作者有一年多未更新維護(hù)了,后續(xù)是否會(huì)繼續(xù)更新維護(hù)解決bug不得而知。另外,我在播放rtmp時(shí)播放失敗,不知何故,不過我并沒深究原因,因?yàn)榇藭r(shí),我已經(jīng)動(dòng)了自己動(dòng)手引用雙端原生播放器的念頭了!

IOS制作并引用原生View步驟

1)使用Xcode運(yùn)行項(xiàng)目:

雙擊flutter項(xiàng)目/ios目錄下的Runner.xcworkspace,Xcode會(huì)自動(dòng)打開項(xiàng)目;

2)檢驗(yàn)項(xiàng)目:

直接運(yùn)行,查看是否有報(bào)錯(cuò)信息;

如果你已經(jīng)在pubspec.yaml中使用了大量的第三方插件,此時(shí)運(yùn)行可能會(huì)報(bào)錯(cuò):xxx Module not Found!那么你需要在打開終端并cd到ios目錄,執(zhí)行 pod install

在這里插入圖片描述
一般情況下都可以解決問題!

3)創(chuàng)建VideoViewPlugin實(shí)現(xiàn)FlutterPlugin協(xié)議:

import Flutter
import UIKitclass VideoViewPlugin:  NSObject, FlutterPlugin {@objc static func register(with registrar: FlutterPluginRegistrar) {registrar.register(VideoViewFactory(registrar: registrar), withId: "plugins.my_video_player/view")}}

由于FlutterPlugin是OC寫的,所以在Swift中實(shí)現(xiàn)OC協(xié)議,前面需要加上NSObject。通過FlutterPluginRegistrar的registrar注冊(cè)PlatformViewFactory。

plugins.my_video_player/view即為該插件的id,在flutter中引用原生view時(shí)需要寫入并且安卓,ios和flutter三方都要保持一致!

4)創(chuàng)建VideoViewFactory實(shí)現(xiàn)FlutterPlatformViewFactory協(xié)議:

import UIKit
import Flutterclass VideoViewFactory:NSObject, FlutterPlatformViewFactory {private var registrar:FlutterPluginRegistrarinit(registrar: FlutterPluginRegistrar) {self.registrar=registrarsuper.init()}//create方法是在flutter中該widget加載顯示出來時(shí)才執(zhí)行//所以自定flutter中使用原生View時(shí)傳遞的參數(shù)在create執(zhí)行后,且獲取到參數(shù)后,再創(chuàng)建channelfunc create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {let id=args as! Intreturn VideoViewPlayer(id: id, registrar: registrar)}//這個(gè)方法一定要寫,否則接受不到flutter的傳參func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {return FlutterStandardMessageCodec.sharedInstance()}
}

注意,createArgsCodec()一定要重寫,否則無法接收到flutter在使用view時(shí)傳過來的參數(shù);create方法中的arguments即為傳遞的參數(shù),create方法返回PlatformView(UIView);

5)創(chuàng)建VideoViewPlayer實(shí)現(xiàn)FlutterPlatformView協(xié)議:

import UIKit
import Flutterclass VideoViewPlayer: NSObject,FlutterPlatformView {//懶加載private var videoView:UIView={let videoView=UIView()return videoView}()//主要就是在這里,返回原生viewfunc view() -> UIView {return videoView}}

6)在AppDelegate.swift中注冊(cè)插件:

import UIKit
import Flutter@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {override func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {GeneratedPluginRegistrant.register(with: self)//插件名自定義if let register=self.registrar(forPlugin: "VideoViewPlugin"){VideoViewPlugin.register(with: register)}return super.application(application, didFinishLaunchingWithOptions: launchOptions)}
}

這里其實(shí)需要兩步:

let register=self.registrar(forPlugin: "VideoViewPlugin")
VideoViewPlugin.register(with: register)

注意registrar和register是不一樣的!

7)在flutter中引用VideoView:

  • 自定義Widget,MyVideoPlayer:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';class MyVideoPlayer extends StatefulWidget {final int id;const MyVideoPlayer({super.key,required this.id,});State<StatefulWidget> createState() => _VideoPlayerState();
}class _VideoPlayerState extends State<MyVideoPlayer> {void initState() {super.initState();}Widget build(BuildContext context) {if (defaultTargetPlatform == TargetPlatform.android) {return AndroidView(viewType: 'plugins.my_video_player/view',creationParams: widget.id, //傳遞到原生插件的參數(shù)(任意類型)creationParamsCodec: const StandardMessageCodec(),);} else {return UiKitView(viewType: 'plugins.my_video_player/view',creationParams: widget.id, //傳遞到原生插件的參數(shù)(任意類型)creationParamsCodec: const StandardMessageCodec(),);}}}

viewType即上面VideoViewPlugin類中注冊(cè)的插件id:plugins.my_video_player/view,安卓,ios和flutter保持一致!

  • 引用MyVideoPlayer:
SizedBox(height: 200,child: MyVideoPlayer(id: 0))

這里可以看到,我給原生View傳了一個(gè)id(需要唯一,我用的是時(shí)間戳)的參數(shù),這個(gè)是為了后面在創(chuàng)建MethodChannel時(shí)加以區(qū)分,在同時(shí)使用了多個(gè)MyVideoPlayer時(shí),不會(huì)相互影響!

此時(shí)我們就可以來測(cè)試一下,引用是否成功了,為了效果明顯,我們可以在VideoViewPlayer.swift中為UIView添加一個(gè)背景色:

 private var videoView:UIView={let videoView=UIView()videoView.backgroundColor=UIColor.redreturn videoView}()

運(yùn)行效果:

在這里插入圖片描述

如上圖,說明原生View已經(jīng)引用成功了!不過,本篇文章的目的,不只是講解如何引用原生View,我們的目標(biāo)是,如何引用原生播放器!

如果你本身是ios開發(fā)者,或者開發(fā)過ios項(xiàng)目,并且在所開發(fā)的項(xiàng)目中使用過第三方的播放器,那么你其實(shí)可以直接將你所使用的播放器庫,引入到本項(xiàng)目中直接使用,無非就是在VideoViewPlayer.swift中初始化并配置一些參數(shù),最后封裝進(jìn)返回的videoView中!

如果你未開發(fā)過ios項(xiàng)目,或者沒使用過第三方的播放器,那么你就可以在github上自行搜索star數(shù)量高的ios開源播放器項(xiàng)目了。由于我使用的是Swift語言,所以我優(yōu)先查找了用Swift寫的播放器,但查找后發(fā)現(xiàn)star量都太低了,而且還必須要支持rtmp播放,最后還是選擇了OC寫的star量最高的ZFPlayer!

引入開源播放器:ZFPlayer

1)安裝:Podfile中添加:

  pod 'ZFPlayer', '~> 4.0'pod 'ZFPlayer/ControlView', '~> 4.0'pod 'ZFPlayer/AVPlayer', '~> 4.0'pod 'ZFPlayer/ijkplayer', '~> 4.0'

由于AVPlayer本身不支持直播,所以還需要引入ZFPlayer/ijkplayer,來支持直播功能!
執(zhí)行pod intall!由于github總是間歇性無法訪問,如果提示SSL超時(shí)等問題,可以多試幾遍!

2)參考ZFPlayer文檔,將ZFPlayer封裝進(jìn)UIView(viewPlayer)返回給Flutter:

VideoViewPlayer中:

    //zfplayer控制器private var player:ZFPlayerController = ZFPlayerController()//播放器進(jìn)度控制條UIViewprivate var playerControlView=ZFPlayerControlView()//AVPlayer管理器private var avPlayerManager:ZFAVPlayerManager?//IjkPlayer管理器private var ijkPlayerManager:ZFIJKPlayerManager?init(id:Int,registrar:FlutterPluginRegistrar) {super.init()//為player設(shè)置進(jìn)度控制條player.controlView=playerControlView//為player設(shè)置containerViewplayer.containerView=videoView}

此時(shí)其實(shí)就可以播放視頻了,傳入視頻Url:

//可以根據(jù)視頻類型,選擇使用AVPlayer還是IjkPlayer播放:
//我這里點(diǎn)播使用AVPlayer,直播使用IjkPlayer,自己可以根據(jù)自身項(xiàng)目情況選擇
if url.starts(with: "http"){avPlayerManager=ZFAVPlayerManager()avPlayerManager!.assetURL=URL(string: url)player.currentPlayerManager=avPlayerManager!playerControlView.portraitControlView.slider.isHidden=falseplayerControlView.portraitControlView.currentTimeLabel.isHidden=falseplayerControlView.portraitControlView.totalTimeLabel.isHidden=false}if url.starts(with: "rtmp"){ijkPlayerManager=ZFIJKPlayerManager()ijkPlayerManager!.assetURL=URL(string: url)player.currentPlayerManager=ijkPlayerManager!playerControlView.portraitControlView.slider.isHidden=trueplayerControlView.portraitControlView.currentTimeLabel.isHidden=trueplayerControlView.portraitControlView.totalTimeLabel.isHidden=true}if avPlayerManager != nil {avPlayerManager!.play()}if ijkPlayerManager != nil {ijkPlayerManager!.play()}

但這樣,無法動(dòng)態(tài)從flutter傳入url啊?所以,我們還需要flutter和ios通信,即使用MethodChannel!

3)IOS端創(chuàng)建FlutterMethodChannel:

    //這里就用到了從flutter傳入的idfunc initMethodChannel(id:Int,registrar:FlutterPluginRegistrar) {let channel = FlutterMethodChannel(name: "my_video_player_\(id)", binaryMessenger: registrar.messenger())channel.setMethodCallHandler(handleMethod)}//接收flutter發(fā)來的消息func handleMethod(call: FlutterMethodCall, result: FlutterResult){switch call.method {case "setUrl":let url: String=call.arguments as! StringinitPlayer(url: url)breakcase "start":play()breakcase "pause":pause()breakcase "release":stop()breakdefault:result(FlutterMethodNotImplemented)break}}

VideoViewPlayer完整代碼如下:

import UIKit
import Flutter
import ZFPlayerclass VideoViewPlayer: NSObject,FlutterPlatformView {private var videoView:UIView={let videoView=UIView()return videoView}()private var player:ZFPlayerController = ZFPlayerController()private var playerControlView=ZFPlayerControlView()private var avPlayerManager:ZFAVPlayerManager?private var ijkPlayerManager:ZFIJKPlayerManager?init(id:Int,registrar:FlutterPluginRegistrar) {super.init()player.controlView=playerControlViewinitMethodChannel(id: id, registrar: registrar)}func view() -> UIView {return videoView}}// MARK:- 播放器初始化及控制
extension VideoViewPlayer{//初始化播放器func initPlayer(url:String){player.containerView=videoViewif avPlayerManager != nil {avPlayerManager!.pause()avPlayerManager=nil}if ijkPlayerManager != nil {ijkPlayerManager!.pause()ijkPlayerManager=nil}if url.starts(with: "http"){avPlayerManager=ZFAVPlayerManager()avPlayerManager!.assetURL=URL(string: url)player.currentPlayerManager=avPlayerManager!playerControlView.portraitControlView.slider.isHidden=falseplayerControlView.portraitControlView.currentTimeLabel.isHidden=falseplayerControlView.portraitControlView.totalTimeLabel.isHidden=false}if url.starts(with: "rtmp"){ijkPlayerManager=ZFIJKPlayerManager()ijkPlayerManager!.assetURL=URL(string: url)player.currentPlayerManager=ijkPlayerManager!playerControlView.portraitControlView.slider.isHidden=trueplayerControlView.portraitControlView.currentTimeLabel.isHidden=trueplayerControlView.portraitControlView.totalTimeLabel.isHidden=true}}func play(){if avPlayerManager != nil {avPlayerManager!.play()}if ijkPlayerManager != nil {ijkPlayerManager!.play()}}func pause(){if avPlayerManager != nil {avPlayerManager!.pause()}if ijkPlayerManager != nil {ijkPlayerManager!.pause()}}func stop(){pause()player.stop()}
}// MARK:- flutter消息通道處理
extension VideoViewPlayer{func initMethodChannel(id:Int,registrar:FlutterPluginRegistrar) {let channel = FlutterMethodChannel(name: "my_video_player_\(id)", binaryMessenger: registrar.messenger())channel.setMethodCallHandler(handleMethod)}//接收flutter發(fā)來的消息func handleMethod(call: FlutterMethodCall, result: FlutterResult){switch call.method {case "setUrl":let url: String=call.arguments as! StringinitPlayer(url: url)breakcase "start":play()breakcase "pause":pause()breakcase "release":stop()breakdefault:result(FlutterMethodNotImplemented)break}}
}

4)flutter端創(chuàng)建MethodChannel:

  init() {methodChannel = MethodChannel('my_video_player_$id');methodChannel.setMethodCallHandler((call) => flutterMethod(call));}Future<void> setUrl(String url) async {return methodChannel.invokeMethod('setUrl', url);}Future<void> start() async {return methodChannel.invokeMethod('start');}...

flutter中調(diào)用setUrl后,再調(diào)用start方法即可播放了!

我這里給flutter端的MethodChanel封裝為了一個(gè)Controller:

import 'package:flutter/services.dart';
import 'package:kjjl_flutter/components/loading.dart';
import 'package:kjjl_flutter/utils/log_util.dart';class VideoViewController {late MethodChannel methodChannel;int id;VideoViewController({required this.id});init() {methodChannel = MethodChannel('my_video_player_$id');methodChannel.setMethodCallHandler((call) => flutterMethod(call));}Future<void> setUrl(String url) async {return methodChannel.invokeMethod('setUrl', url);}Future<void> start() async {return methodChannel.invokeMethod('start');}Future<void> pause() async {return methodChannel.invokeMethod('pause');}Future<void> release() async {return methodChannel.invokeMethod('release');}Future<void> stopFullScreen() async {return methodChannel.invokeMethod('stopFullScreen');}Future<dynamic> flutterMethod(MethodCall methodCall) async {switch (methodCall.method) {}}
}

在State中使用時(shí)的部分代碼:

SizedBox(height: videoHeight,child: currentVideo != null && videoViewController != null? MyVideoPlayer(id: videoViewController!.id): SizedBox(),)...VideoModel? currentVideo;
VideoViewController? videoViewController;//頁面銷毀時(shí)釋放播放器

void dispose() {if (videoViewController != null) videoViewController!.release();super.dispose();
}//點(diǎn)擊播放時(shí)執(zhí)行
play(VideoModel video) {if (videoViewController != null) {if (currentVideo != null && currentVideo!.id == video.id) return;setState(() {currentVideo = video;videoViewController!.release();startPlay(video);});} else {setState(() {currentVideo = video;videoViewController = VideoViewController(id: DateTime.now().millisecondsSinceEpoch);videoViewController!.init();//延時(shí)500ms執(zhí)行,是為了防止播放器還未初始化完成,就調(diào)用了播放,導(dǎo)致首次播放失敗;Future.delayed(const Duration(milliseconds: 500), () {startPlay(video);});});}}startPlay(VideoModel video){videoViewController!.setUrl(video.mobileUrl);videoViewController!.start();
}

效果圖:

直播:

在這里插入圖片描述

錄播:

在這里插入圖片描述

下一篇:Flutter3引用原生播放器-Android篇

http://m.aloenet.com.cn/news/41846.html

相關(guān)文章:

  • 網(wǎng)站開發(fā)還找到工作嗎鏈網(wǎng)
  • 做網(wǎng)站建設(shè)要什么證小網(wǎng)站
  • 影樓網(wǎng)站推廣seo系統(tǒng)優(yōu)化
  • 網(wǎng)站建設(shè)方案模板高校引流軟件下載站
  • 做優(yōu)化網(wǎng)站是什么意思誰有推薦的網(wǎng)址
  • 有什么好看的網(wǎng)站seo系統(tǒng)培訓(xùn)
  • 如何侵入網(wǎng)站服務(wù)器免費(fèi)企業(yè)黃頁查詢官網(wǎng)
  • 宜興網(wǎng)站制作百度seo優(yōu)化及推廣
  • 政府網(wǎng)站建設(shè)運(yùn)行情況寧波seo免費(fèi)優(yōu)化軟件
  • 新網(wǎng)站優(yōu)化怎么做武漢seo優(yōu)化
  • 界面網(wǎng)站的風(fēng)格關(guān)鍵字c語言
  • 成都哪里有做網(wǎng)站建設(shè)的青島網(wǎng)站建設(shè)公司電話
  • 在小說網(wǎng)站做責(zé)編如何免費(fèi)發(fā)布廣告
  • 網(wǎng)站制作公司智能 樂云踐新做網(wǎng)站怎么優(yōu)化
  • 網(wǎng)站模板購買房地產(chǎn)估價(jià)師考試
  • 小程序開發(fā)平臺(tái)哪里做得好seo網(wǎng)站優(yōu)化培訓(xùn)怎么做
  • 可以免費(fèi)發(fā)帖的網(wǎng)站品牌廣告投放
  • asp如何做網(wǎng)站河北網(wǎng)站seo策劃
  • 網(wǎng)站瀏覽思路上海網(wǎng)絡(luò)推廣公司網(wǎng)站
  • 哪個(gè)網(wǎng)站推廣做的好引流獲客app下載
  • 關(guān)于藥品網(wǎng)站建設(shè)策劃書seo軟件哪個(gè)好
  • 做網(wǎng)站公司在深圳杭州疫情最新情況
  • 網(wǎng)站建設(shè)在線視頻上海百度推廣官網(wǎng)
  • java源代碼網(wǎng)站seo在線外鏈
  • 企業(yè)網(wǎng)站建設(shè)系統(tǒng)惠東seo公司
  • 邯鄲哪有做網(wǎng)站的公司邵陽網(wǎng)站seo
  • 網(wǎng)站開發(fā)原型濰坊今日頭條新聞
  • 濰坊市建設(shè)局網(wǎng)站上海關(guān)鍵詞自動(dòng)排名
  • 網(wǎng)站源碼上傳完后怎么做足球排名最新排名世界
  • 怎么給企業(yè)做網(wǎng)站網(wǎng)絡(luò)營銷企業(yè)是什么