Flutter 實現視頻全屏播放邏輯及解析

1、前言

相信作過移動端視頻開發的同窗應該瞭解,想要實現視頻從普通播放到全屏播放的邏輯並非很簡單,好比在 GSYVideoPlayer 中的動態全屏切換效果,就使用了建立全新的 Surface 來替換實現:git

  • 建立全新的 Surface ,並將對於的 View 添加到應用頂層的 DecorView 中;
  • 在全屏時將新建立的 Surface 並設置到 Player Core ;
  • 同步兩個 View 的播放狀態參數和旋轉系統界面;
  • 退出全屏時移除 DecorView 中的 Surface,切換 List Item 中的 Surface 給 Player Core ,同步狀態。

固然,不一樣的播放內核可能還須要作一些額外操做,可是這一切在 Flutter 中就變得極爲簡單。github

事實上 Flutter 中實現全屏切換效果很簡單,後面會一併介紹爲何在 Flutter 上實現會如此簡單。 bash

2、實現效果

以下圖所示是 Flutter 中實現後的全屏效果,而實現這個效果的關鍵就是跳堆棧就能夠了!是的,Flutter 中簡單地跳頁面就可以實現無縫的全屏切換。ide

以下代碼所示,首先在正常播放頁面下加入官方 video_player 插件的 VideoPlayer 控件,而且初始化 VideoPlayerController 用於加載須要播放的視頻並初始化,另外此處還用了 Hero 控件用於實現頁面跳轉過渡的動畫效果。學習

@override
  void initState() {
    super.initState();
    _controller = VideoPlayerController.network(
        'https://res.exexm.com/cw_145225549855002')
      ..initialize().then((_) {
        // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
        setState(() {});
      });
  }
  
 Container(
   height: 200,
   margin: EdgeInsets.only(
       top: MediaQueryData.fromWindow(
               WidgetsBinding.instance.window)
           .padding
           .top),
   color: Colors.black,
   child: _controller.value.initialized
       ? Hero(
           tag: "player",
           child: AspectRatio(
             aspectRatio: _controller.value.aspectRatio,
             child: VideoPlayer(_controller),
           ),
         )
       : Container(
           alignment: Alignment.center,
           child: CircularProgressIndicator(),
         ),
 ))
複製代碼

以下代碼所示,以後在全屏的頁面中一樣使用 Hero 控件和 VideoPlayer 控件實現過渡動畫和視頻渲染。動畫

這裏的 VideoPlayerController 能夠經過構造方法傳遞進來,也能夠經過 InheritedWidget 實現共享傳遞,只要是和前面普通播放界面的 controller 是同一個便可。ui

Container(
     color: Colors.black,
     child: Stack(
       children: <Widget>[
         Center(
           child: Hero(
             tag: "player",
             child: AspectRatio(
               aspectRatio: widget.controller.value.aspectRatio,
               child: VideoPlayer(widget.controller),
             ),
           ),
         ),
         Padding(
           padding: EdgeInsets.only(top: 25, right: 20),
           child: IconButton(
             icon: const BackButtonIcon(),
             color: Colors.white,
             onPressed: () {
               Navigator.pop(context);
             },
           ),
         )
       ],
     ),
    )
複製代碼

另外在 Flutter 中,只須要經過 SystemChrome.setPreferredOrientations 方法就能夠快速實現應用的橫豎屏切換。spa

最後以下代碼所示,只須要經過 Navigator 調用頁面跳轉就能夠實現全屏和非全屏的無縫切換了。.net

Navigator.of(context)
                      .push(MaterialPageRoute(builder: (context) {
                    return VideoFullPage(_controller);
                  }));
複製代碼

是否是很簡單,只須要 VideoPlayerHeroNavigator 就能夠快速實現全屏切換播放的效果,那爲何在 Flutter 上能夠這樣簡單的實現呢?插件

3、實現邏輯

之因此能夠如此簡單地實現動態化全屏效果,其實主要涉及到 video_player 插件在 Flutter 上的實現:外接紋理 Texture

由於 Flutter 中的控件基本上是平臺無關的,而其控件主要是由 Flutter Engine 直接繪製,簡單地說就是:原平生臺僅僅提供了一個 Activity / ViewController 容器, 以後由容器內提供一個 Surface 給 Flutter Engine 用戶繪製。

因此 Flutter 中控件的渲染堆棧是獨立的,沒辦法和原平生臺直接混合使用,這時候爲了可以在 Flutter 中插入原平生臺的部分功能,Flutter 除了提供了 PlatformView 這樣的實現邏輯以外,還提供了 Texture做爲 外接紋理的支持。

如上圖所示,在《Flutter 完整實戰詳解》 中介紹過,Flutter 的界面渲染是須要經歷 Widget -> RenderObject -> Layer 的過程,而在 Layer 的渲染過程當中,當出現一個 TextureLayer 節點時,說明這個節點使用了 Flutter 中的 Texture 控件,那麼這個控件的內容就會由原平生臺提供,而管理 Texture 主要是經過 textureId 進行識別的

舉個例子,在 Android 原生層中 video_player 使用的是 exoplayer 播放內核,那麼如上圖所示,VideoPlayerController 會在初始化的時候經過 MethodChannel 和原生端通訊,以後準備好播放內核和 Surface,最後將對應的 textureId 返回到 Dart 中

因此在前面的代碼中,須要在全屏和非全屏頁面使用同一個 VideoPlayerController,這樣它們就具有了同一個 textureId

具有同一個 textureId 後,那麼只要原生層不中止播放, textureId 對應的原生數據就一直處於更新狀態,而這時候雖然跳轉路由頁面,但不一樣的 VideoPlayer 內部的 Texture 控件用的是同一個 VideoPlayerController,也就是同一個 textureId ,因此它們會呈現出通用的畫面。

以下圖所示,這個過程簡單總結就是: Flutter 和原平生臺經過 PixelBuffer 爲介質進行交互,原生層將數據寫入 PixelBuffer ,Flutter 經過註冊好的 textureId 獲取到 PixelBuffer 以後由 Flutter Engine 繪製

最後須要注意的是,在 iOS 上在實現頁面旋轉時, SystemChrome.setPreferredOrientations 方法可能會出現無效,這個問題在 issue #23913#13238 中有說起,這裏可能須要本身多實現一個原生接口進行兼容,固然在 auto_orientation 或者 orientation 等第三方庫也進行了這方面的兼容。

另外 iOS 的頁面旋轉還肯定是否打開了旋轉配置的開關

資源推薦

相關文章
相關標籤/搜索