中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

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

網(wǎng)站建設(shè) 上市公司深圳關(guān)鍵詞排名seo

網(wǎng)站建設(shè) 上市公司,深圳關(guān)鍵詞排名seo,濱州網(wǎng)站開(kāi)發(fā),中山網(wǎng)站建設(shè)半江紅前言由于Flutter項(xiàng)目中需要使用到播放器功能,因此對(duì)flutter中各種播放器解決方案進(jìn)行了一番研究和比對(duì),最后決定還是自己通過(guò)Plugin的方法去引用原生播放器符合自己的需求,本篇文章會(huì)對(duì)各種解決方案做一個(gè)簡(jiǎn)單的比較,以及講解一下…

前言

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

video_player

video_player:flutter官方出品。

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

better_player

better_player:國(guó)外大神在video_player的基礎(chǔ)之上二次開(kāi)發(fā)而來(lái),對(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):做過(guò)安卓和ios原生的,對(duì)ijk應(yīng)該都非常熟悉了,這里就不做過(guò)多說(shuō)明了;
fijkplayer支持各種視頻格式包括rtmp等協(xié)議直播,支持各種常用功能,支持定制化UI,具體可以去它的官網(wǎng)查看文檔說(shuō)明;
適用:對(duì)播放器要求稍高,需要簡(jiǎn)單定制化播放器ui,需要支持直播的用戶;

這里說(shuō)一些個(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í)播放失敗,不知何故,不過(guò)我并沒(méi)深究原因,因?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)打開(kāi)項(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!那么你需要在打開(kāi)終端并cd到ios目錄,執(zhí)行 pod install

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

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。通過(guò)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加載顯示出來(lái)時(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()一定要重寫,否則無(wú)法接收到flutter在使用view時(shí)傳過(guò)來(lái)的參數(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í)我們就可以來(lái)測(cè)試一下,引用是否成功了,為了效果明顯,我們可以在VideoViewPlayer.swift中為UIView添加一個(gè)背景色:

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

運(yùn)行效果:

在這里插入圖片描述

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

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

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

引入開(kāi)源播放器: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,來(lái)支持直播功能!
執(zhí)行pod intall!由于github總是間歇性無(wú)法訪問(wèn),如果提示SSL超時(shí)等問(wèn)題,可以多試幾遍!

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()}

但這樣,無(wú)法動(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)}//接收f(shuō)lutter發(fā)來(lái)的消息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)}//接收f(shuō)lutter發(fā)來(lái)的消息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;//頁(yè)面銷毀時(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.risenshineclean.com/news/62455.html

相關(guān)文章:

  • 湘潭網(wǎng)站建設(shè) 技精磐石網(wǎng)絡(luò)網(wǎng)站發(fā)布與推廣
  • 可以做宣傳海報(bào)的網(wǎng)站今天國(guó)內(nèi)新聞
  • 重慶網(wǎng)站設(shè)計(jì)好的公司百度業(yè)務(wù)員聯(lián)系電話
  • 政府網(wǎng)站集約化平臺(tái)推廣策略都有哪些
  • 產(chǎn)品眾籌網(wǎng)站開(kāi)發(fā)怎么自己做個(gè)網(wǎng)站
  • 怎么設(shè)置自己做的網(wǎng)站google網(wǎng)站
  • ps做圖賺錢網(wǎng)站trinseo公司
  • 門面設(shè)計(jì)裝修效果圖廣州seo優(yōu)化外包公司
  • 鹽山縣招聘網(wǎng)站建設(shè)seoheuni
  • 怎么在網(wǎng)站標(biāo)題做logo小程序
  • 紅包網(wǎng)站開(kāi)發(fā)百度點(diǎn)擊軟件找名風(fēng)
  • 鎮(zhèn)江專業(yè)網(wǎng)站制作最有效的網(wǎng)絡(luò)推廣方式和策略
  • 免費(fèi)加盟游戲代理搜索引擎優(yōu)化公司
  • 音樂(lè)在線制作網(wǎng)站網(wǎng)絡(luò)推廣產(chǎn)品公司
  • 免費(fèi)畫(huà)圖網(wǎng)站東莞seo網(wǎng)絡(luò)推廣專
  • 怎樣優(yōu)古網(wǎng)絡(luò)公司網(wǎng)站后臺(tái)中國(guó)最好的網(wǎng)絡(luò)營(yíng)銷公司
  • 奢侈品 網(wǎng)站建設(shè)方案上海最新事件
  • 何做百度推廣網(wǎng)站國(guó)內(nèi)優(yōu)秀網(wǎng)頁(yè)設(shè)計(jì)賞析
  • 鶴崗做網(wǎng)站公司每天4元代發(fā)廣告
  • 深圳網(wǎng)站建設(shè)ppchsj個(gè)人博客搭建
  • 網(wǎng)站源碼搭建教程河南疫情最新消息
  • 網(wǎng)站制作器軟件下載新品上市怎么推廣詞
  • 濟(jì)南校園兼職網(wǎng)站建設(shè)正規(guī)seo排名公司
  • 網(wǎng)站地圖做法做疫情排行榜最新消息
  • it公司怎么在國(guó)外網(wǎng)站做宣傳建設(shè)網(wǎng)站制作
  • 傳奇手游最新下載seo優(yōu)化工作內(nèi)容做什么
  • 服務(wù)器上的網(wǎng)站怎么做3012022百度指數(shù)排名
  • 服務(wù)好的企業(yè)做網(wǎng)站南昌seo數(shù)據(jù)監(jiān)控
  • 淄博 網(wǎng)站制作seo網(wǎng)站自動(dòng)發(fā)布外鏈工具
  • 微信做自己的網(wǎng)站濰坊seo培訓(xùn)