Submit Search
Upload
Cast SDK for Flutter
•
3 likes
•
1,547 views
K
KojiYamada22
Follow
Cast SDK for Flutter Meetup Slide
Read less
Read more
Engineering
Report
Share
Report
Share
1 of 31
Download now
Download to read offline
Recommended
ゲームアプリの数学@GREE GameDevelopers' Meetup
ゲームアプリの数学@GREE GameDevelopers' Meetup
gree_tech
Google cast開発入門
Google cast開発入門
しくみ製作所
Kotlinアンチパターン
Kotlinアンチパターン
Recruit Lifestyle Co., Ltd.
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
Unity Technologies Japan K.K.
大規模ゲーム開発における build 高速化と安定化
大規模ゲーム開発における build 高速化と安定化
DeNA
HoloLens 2 開発入門
HoloLens 2 開発入門
Akihiro Ueyama
オープンデータとオープンソースGisを用いたweb上でのインタラクティブ可視化手法について
オープンデータとオープンソースGisを用いたweb上でのインタラクティブ可視化手法について
Ryousuke Wayama
ドキュメンテーションを加速するストレスフリーの作図ツール『blockdiag』 jus2011年6月勉強会
ドキュメンテーションを加速するストレスフリーの作図ツール『blockdiag』 jus2011年6月勉強会
Takayuki Shimizukawa
Recommended
ゲームアプリの数学@GREE GameDevelopers' Meetup
ゲームアプリの数学@GREE GameDevelopers' Meetup
gree_tech
Google cast開発入門
Google cast開発入門
しくみ製作所
Kotlinアンチパターン
Kotlinアンチパターン
Recruit Lifestyle Co., Ltd.
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
Unity Technologies Japan K.K.
大規模ゲーム開発における build 高速化と安定化
大規模ゲーム開発における build 高速化と安定化
DeNA
HoloLens 2 開発入門
HoloLens 2 開発入門
Akihiro Ueyama
オープンデータとオープンソースGisを用いたweb上でのインタラクティブ可視化手法について
オープンデータとオープンソースGisを用いたweb上でのインタラクティブ可視化手法について
Ryousuke Wayama
ドキュメンテーションを加速するストレスフリーの作図ツール『blockdiag』 jus2011年6月勉強会
ドキュメンテーションを加速するストレスフリーの作図ツール『blockdiag』 jus2011年6月勉強会
Takayuki Shimizukawa
Windowsフォームで大丈夫か?一番良いのを頼む。
Windowsフォームで大丈夫か?一番良いのを頼む。
Yuya Yamaki
async/await のしくみ
async/await のしくみ
信之 岩永
eStargzイメージとlazy pullingによる高速なコンテナ起動
eStargzイメージとlazy pullingによる高速なコンテナ起動
Kohei Tokunaga
【Unity】 Behavior TreeでAIを作る
【Unity】 Behavior TreeでAIを作る
torisoup
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
Yoshifumi Kawai
Googleのオープンなビーコン規格「Eddystone」とはなんなのか?
Googleのオープンなビーコン規格「Eddystone」とはなんなのか?
Fumihiko Sato
MagicOnion入門
MagicOnion入門
torisoup
第1回ROS勉強会発表資料 ROS+Gazeboではじめるロボットシミュレーション
第1回ROS勉強会発表資料 ROS+Gazeboではじめるロボットシミュレーション
akio19937
次世代ゲームにおける自動生成技術
次世代ゲームにおける自動生成技術
Youichiro Miyake
Unityネイティブプラグインマニアクス #denatechcon
Unityネイティブプラグインマニアクス #denatechcon
DeNA
ブラウザ自動化ツール カオスマップ風 - STAC2018 LT
ブラウザ自動化ツール カオスマップ風 - STAC2018 LT
hnisiji
Scapyで作る・解析するパケット
Scapyで作る・解析するパケット
Takaaki Hoyo
Python製BDDツールで自動化してみた
Python製BDDツールで自動化してみた
KeijiUehata1
UnityとROSの連携について
UnityとROSの連携について
UnityTechnologiesJapan002
WebRTCの技術解説 第二版 公開版 完全版
WebRTCの技術解説 第二版 公開版 完全版
Contest Ntt-west
こわくない Git
こわくない Git
Kota Saito
ROS の活用による屋外の歩行者空間に適応した自律移動ロボットの開発
ROS の活用による屋外の歩行者空間に適応した自律移動ロボットの開発
Yoshitaka HARA
BuildKitの概要と最近の機能
BuildKitの概要と最近の機能
Kohei Tokunaga
グラフ構造のデータモデルをPower BIで可視化してみた
グラフ構造のデータモデルをPower BIで可視化してみた
CData Software Japan
T119_5年間の試行錯誤で進化したMVPVMパターン
T119_5年間の試行錯誤で進化したMVPVMパターン
伸男 伊藤
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
Jumpei Ogawa
Pf部2012年1月勉強会.androidsola
Pf部2012年1月勉強会.androidsola
android sola
More Related Content
What's hot
Windowsフォームで大丈夫か?一番良いのを頼む。
Windowsフォームで大丈夫か?一番良いのを頼む。
Yuya Yamaki
async/await のしくみ
async/await のしくみ
信之 岩永
eStargzイメージとlazy pullingによる高速なコンテナ起動
eStargzイメージとlazy pullingによる高速なコンテナ起動
Kohei Tokunaga
【Unity】 Behavior TreeでAIを作る
【Unity】 Behavior TreeでAIを作る
torisoup
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
Yoshifumi Kawai
Googleのオープンなビーコン規格「Eddystone」とはなんなのか?
Googleのオープンなビーコン規格「Eddystone」とはなんなのか?
Fumihiko Sato
MagicOnion入門
MagicOnion入門
torisoup
第1回ROS勉強会発表資料 ROS+Gazeboではじめるロボットシミュレーション
第1回ROS勉強会発表資料 ROS+Gazeboではじめるロボットシミュレーション
akio19937
次世代ゲームにおける自動生成技術
次世代ゲームにおける自動生成技術
Youichiro Miyake
Unityネイティブプラグインマニアクス #denatechcon
Unityネイティブプラグインマニアクス #denatechcon
DeNA
ブラウザ自動化ツール カオスマップ風 - STAC2018 LT
ブラウザ自動化ツール カオスマップ風 - STAC2018 LT
hnisiji
Scapyで作る・解析するパケット
Scapyで作る・解析するパケット
Takaaki Hoyo
Python製BDDツールで自動化してみた
Python製BDDツールで自動化してみた
KeijiUehata1
UnityとROSの連携について
UnityとROSの連携について
UnityTechnologiesJapan002
WebRTCの技術解説 第二版 公開版 完全版
WebRTCの技術解説 第二版 公開版 完全版
Contest Ntt-west
こわくない Git
こわくない Git
Kota Saito
ROS の活用による屋外の歩行者空間に適応した自律移動ロボットの開発
ROS の活用による屋外の歩行者空間に適応した自律移動ロボットの開発
Yoshitaka HARA
BuildKitの概要と最近の機能
BuildKitの概要と最近の機能
Kohei Tokunaga
グラフ構造のデータモデルをPower BIで可視化してみた
グラフ構造のデータモデルをPower BIで可視化してみた
CData Software Japan
T119_5年間の試行錯誤で進化したMVPVMパターン
T119_5年間の試行錯誤で進化したMVPVMパターン
伸男 伊藤
What's hot
(20)
Windowsフォームで大丈夫か?一番良いのを頼む。
Windowsフォームで大丈夫か?一番良いのを頼む。
async/await のしくみ
async/await のしくみ
eStargzイメージとlazy pullingによる高速なコンテナ起動
eStargzイメージとlazy pullingによる高速なコンテナ起動
【Unity】 Behavior TreeでAIを作る
【Unity】 Behavior TreeでAIを作る
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
Googleのオープンなビーコン規格「Eddystone」とはなんなのか?
Googleのオープンなビーコン規格「Eddystone」とはなんなのか?
MagicOnion入門
MagicOnion入門
第1回ROS勉強会発表資料 ROS+Gazeboではじめるロボットシミュレーション
第1回ROS勉強会発表資料 ROS+Gazeboではじめるロボットシミュレーション
次世代ゲームにおける自動生成技術
次世代ゲームにおける自動生成技術
Unityネイティブプラグインマニアクス #denatechcon
Unityネイティブプラグインマニアクス #denatechcon
ブラウザ自動化ツール カオスマップ風 - STAC2018 LT
ブラウザ自動化ツール カオスマップ風 - STAC2018 LT
Scapyで作る・解析するパケット
Scapyで作る・解析するパケット
Python製BDDツールで自動化してみた
Python製BDDツールで自動化してみた
UnityとROSの連携について
UnityとROSの連携について
WebRTCの技術解説 第二版 公開版 完全版
WebRTCの技術解説 第二版 公開版 完全版
こわくない Git
こわくない Git
ROS の活用による屋外の歩行者空間に適応した自律移動ロボットの開発
ROS の活用による屋外の歩行者空間に適応した自律移動ロボットの開発
BuildKitの概要と最近の機能
BuildKitの概要と最近の機能
グラフ構造のデータモデルをPower BIで可視化してみた
グラフ構造のデータモデルをPower BIで可視化してみた
T119_5年間の試行錯誤で進化したMVPVMパターン
T119_5年間の試行錯誤で進化したMVPVMパターン
Similar to Cast SDK for Flutter
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
Jumpei Ogawa
Pf部2012年1月勉強会.androidsola
Pf部2012年1月勉強会.androidsola
android sola
Titanium Mobile
Titanium Mobile
Naoya Ito
Windows azure mobile services による mobile + cloud アプリケーション超高速開発
Windows azure mobile services による mobile + cloud アプリケーション超高速開発
Shotaro Suzuki
Azure IoT Edge で Custom Vision
Azure IoT Edge で Custom Vision
Yoshitaka Seo
Redmineosaka 20 talk_crosspoints
Redmineosaka 20 talk_crosspoints
Shinji Tamura
WebRTCを始めよう! HTML5fun 第一回勉強会
WebRTCを始めよう! HTML5fun 第一回勉強会
Yusuke Naka
[AI08] 深層学習フレームワーク Chainer × Microsoft で広がる応用
[AI08] 深層学習フレームワーク Chainer × Microsoft で広がる応用
de:code 2017
Android bluetooth
Android bluetooth
Masahiro Hidaka
"Up" with vagrant and docker
"Up" with vagrant and docker
Hiroshi Miura
UnityとnodeとMMDと
UnityとnodeとMMDと
sters
FlutterをRenderObjectまで理解する
FlutterをRenderObjectまで理解する
KeisukeKiriyama
VRのマルチプラットフォーム開発について
VRのマルチプラットフォーム開発について
yashinut
"Up" with vagrant and docker
"Up" with vagrant and docker
Hiroshi Miura
MRTK-Unreal(UX Tools) を利用した HoloLens 2 アプリ開発 | UNREAL FEST EXTREME 2020 WINTER
MRTK-Unreal(UX Tools) を利用した HoloLens 2 アプリ開発 | UNREAL FEST EXTREME 2020 WINTER
エピック・ゲームズ・ジャパン Epic Games Japan
.NET Gadgeteer のハンズオン資料 (2014年3月版)
.NET Gadgeteer のハンズオン資料 (2014年3月版)
Yoshitaka Seo
Unreal Engine でアプリ開発~ MRTK UXTools for Unreal V0.9.0 ~
Unreal Engine でアプリ開発~ MRTK UXTools for Unreal V0.9.0 ~
Takahiro Miyaura
Let's begin WebRTC
Let's begin WebRTC
yoshikawa_t
Grafana Dashboards as Code
Grafana Dashboards as Code
Takuhiro Yoshida
20120118 titanium
20120118 titanium
Hiroshi Oyamada
Similar to Cast SDK for Flutter
(20)
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
Pf部2012年1月勉強会.androidsola
Pf部2012年1月勉強会.androidsola
Titanium Mobile
Titanium Mobile
Windows azure mobile services による mobile + cloud アプリケーション超高速開発
Windows azure mobile services による mobile + cloud アプリケーション超高速開発
Azure IoT Edge で Custom Vision
Azure IoT Edge で Custom Vision
Redmineosaka 20 talk_crosspoints
Redmineosaka 20 talk_crosspoints
WebRTCを始めよう! HTML5fun 第一回勉強会
WebRTCを始めよう! HTML5fun 第一回勉強会
[AI08] 深層学習フレームワーク Chainer × Microsoft で広がる応用
[AI08] 深層学習フレームワーク Chainer × Microsoft で広がる応用
Android bluetooth
Android bluetooth
"Up" with vagrant and docker
"Up" with vagrant and docker
UnityとnodeとMMDと
UnityとnodeとMMDと
FlutterをRenderObjectまで理解する
FlutterをRenderObjectまで理解する
VRのマルチプラットフォーム開発について
VRのマルチプラットフォーム開発について
"Up" with vagrant and docker
"Up" with vagrant and docker
MRTK-Unreal(UX Tools) を利用した HoloLens 2 アプリ開発 | UNREAL FEST EXTREME 2020 WINTER
MRTK-Unreal(UX Tools) を利用した HoloLens 2 アプリ開発 | UNREAL FEST EXTREME 2020 WINTER
.NET Gadgeteer のハンズオン資料 (2014年3月版)
.NET Gadgeteer のハンズオン資料 (2014年3月版)
Unreal Engine でアプリ開発~ MRTK UXTools for Unreal V0.9.0 ~
Unreal Engine でアプリ開発~ MRTK UXTools for Unreal V0.9.0 ~
Let's begin WebRTC
Let's begin WebRTC
Grafana Dashboards as Code
Grafana Dashboards as Code
20120118 titanium
20120118 titanium
Cast SDK for Flutter
1.
Cast SDK for
Flutter
2.
山田 幸司 koji.yamada@gree.net グリー株式会社 開発本部 /
インフラストラクチャ部 / ディベロップメント オペレーションズグループ / サービスディベロップメント チーム 所属 業務内容 VRアプリフロントエンド担当 最近のお仕事はFlutter / Unity / Unreal Engine 4など
3.
アジェンダ ● Chromecastについて ○ Chromecastとは? ○
Cast SDKについて ● Cast SDK for Flutterを作る ○ Flutterでプラグインを作る準備やインストール方法など ○ 構成から実装のおおまかな流れ ○ 実装方法などについて
4.
Chromecast ● Googleが開発・販売する小型のデバイス ● HDMI端子に接続/Wi-Fiを介してスマートフォンやタブレットなどで再生して いる動画、写真、ウェブサイトなどをディスプレイに表示出来るデバイス ●
ミラーリングとはやや違ってスマートフォンをコントローラとして扱い動画 の再生や停止などもできる ● 値段は5,000円程度(4K対応のUltraは10,000円程度) ● アプリで利用するにはCast SDKを組み込む必要がある ※↑アプリが対応しているかつ周辺にChromecast がある場合はこのアイコンが表示される
5.
Cast SDK ● Googleが提供するChromecast用のSDK ○
https://developers.google.com/cast/ ● Android/iOS/Chrome(ブラウザ)に対応 ○ Android → Java/Kotlin ○ iOS → Objective-C/Swift ○ Chrome → Javascript ● 公式のFlutter用Cast SDKはまだ存在しない ● Flutter版もいつか対応するかも ○ GitHubのissuesはあがっている ○ https://github.com/flutter/flutter/issues/18212
6.
Cast SDK for
Flutter ● 今日の話 ○ Flutter用のChromecast Pluginを作る話 ○ ChromecastのAndroid/iOS両方に対応するアプリをさく っと作れる ● 実装するもの ○ 送信側(Sender)と受信側(Receiver)を作る必要があ りますが、今回はSenderアプリのみについて ○ 動画コントロールパネルやキャストボタンなどのUI部分 はFlutterで実装 ○ 動画をChromecastにキャスト/再生と停止ができる
7.
準備 ● Plugin Packageプロジェクトの作成 ○
$ flutter create --template=plugin -i swift -a kotlin [プロジェクト名] 例) chrome_cast_plugin Android/build.gradle … dependencies { implementation 'com.google.android.gms:play-services-cast-framework:16.1.2' ... iOS/Podfile … target 'Runner' do pod 'google-cast-sdk', '~> 4.3' ... Sync Now pod install
8.
構成 app main.dart lib chrome_cast_ plugin.dart Flutter/Dart Android/Kotlin iOS/Swift CastOptionsProvider.kt ChromeCastPlugin.kt SwiftChromeCast Plugin.swift Cast SDK for
Android gms:play-services-cast Cast SDK for iOS google-cast-sdk ● main.dart ○ キャストボタンなどのUI表示 ● chrome_cast_plugin.dart ○ Kotlin/Swiftとのつなぎ プラグイン側
9.
Pluginで実装すること 1. Cast Contextの初期化 2.
Cast Dialogの表示 3. Cast Buttonの表示 4. リスナーの登録 5. 動画をキャストする 6. キャストした動画を制御する
10.
Cast Contextの初期化 ● Chromecast
の機能を使うために必要 ● Android ○ OptionsProviderインタフェースを実装したクラスの作成 ○ レシーバーIdのCastOptionsのインスタンスを作る ○ CastContext.getSharedInstance(Activity)で初期化 ● iOS ○ レシーバーIdのGCKCastOptionのインスタンスを作る ○ GCKCastContext.setSharedInstanceWith(GCKCastOption)で初期化 ● 初期化タイミング ○ pluginのregisterWith / register
11.
class ChromeCastPlugin(private val
pActivity: Activity, private val pChannel: MethodChannel): … companion object { @JvmStatic fun registerWith(registrar: Registrar) { CastContext.getSharedInstance(pActivity) … Android/Kotlin static const MethodChannel _channel = const MethodChannel('chrome_cast_plugin'); chrome_cast_plugin.dart public class SwiftChromeCastPlugin: NSObject, FlutterPlugin { … public static func register(with registrar: FlutterPluginRegistrar) { let tCriteria = GCKDiscoveryCriteria(applicationID: [レシーバーのId]) let tOption = GCKCastOptions(discoveryCriteria: tCriteria) GCKCastContext.setSharedInstanceWith(tOption) … iOS/Swift
12.
class CastOptionsProvider :
OptionsProvider { … override fun getCastOptions(context: Context): CastOptions? { val tAppId = context.resources.getIdentifier("chromecast_app_id", "string", context.packageName) … return CastOptions.Builder() .setReceiverApplicationId(context.getString(tAppId)) .build() } … } Android/Kotlin Cast Contextの初期化(Android CastOptionsProvider.kt) <?xml version="1.0" encoding="utf-8"?> <resources> <string name="chromecast_app_id">[レシーバーId]</string> </resources> app/res/values/string.xml
13.
Cast Dialogの表示 ● キャストしてないとき ○
キャスト可能なデバイス一覧を表示 ● キャストしてるとき ○ メディア情報、音量などを表示 ● Android(少々手間がかかる) ○ 現在キャスト中かどうかを判定して出し分けが必要 ○ 現在のキャストセッションがあれば MediaRouteControllerDialog ○ 無い場合はMediaRouteChooserDialogを呼ぶ ● iOS ○ presentCastDialogを呼ぶだけ
14.
private fun showDialog(pCall:
MethodCall, pResult: Result) { val tCastSession = CastContext.getSharedInstance()?.sessionManager?.currentCastSession if (tCastSession != null) { val tControllerDialog = MediaRouteControllerDialog(pActivity, R.style.CastControllerDialogTheme) tControllerDialog.show() } else { val tChooserDialog = MediaRouteChooserDialog(pActivity, R.style.CastMediaRouterTheme) tChooserDialog.routeSelector = CastContext.getSharedInstance()?.mergedSelector!! tChooserDialog.show() } } Android/Kotlin Future<void> showCastDialog() async { await _channel.invokeMethod('ShowCastDialog'); } chrome_cast_plugin.dart private func showDialog(_ pCall: FlutterMethodCall, _ pResult: @escaping FlutterResult) { let tContext:GCKCastContext = GCKCastContext.sharedInstance() tContext.presentCastDialog() } iOS/Swift
15.
動画をキャストする ● 動画をキャストするために必要な情報をFlutterからPlugin側に渡す ● 手順 ○
[1] キャストするための必要なメタデータ(Metadata)を作成するためのパ ラメータ ■ タイトル、キャストダイアログに出す画像など ○ [2] 作成したメタデータからメディア情報(MediaInfo)を作成するためのパ ラメータ ■ 動画のコンテンツタイプ(mp4など)、レシーバーに渡す独自のJsonデータなど ○ [3] 必要に応じて読み込み時のオプションを作成するためのパラメータ ■ 再生位置、自動再生するかどうかなど ○ [4] 動画をデバイスにキャスト
16.
Future<bool> startCast(Media media)
async { bool _result = await _channel.invokeMethod('StartCast', { 'Uri': media.url, 'Title': media.title, 'Subtitle': media.subTitle, 'Studio': media.studio, 'ContentType': media.contentType, 'Images': { 'Dialog': { 'Url': media.dialogThumbnail.url, 'Width': media.dialogThumbnail.width, 'Height': media.dialogThumbnail.height }, 'Notification': { 'Url': media.notificationThumbnail.url, } }, chrome_cast_plugin.dart 'IsAuto': media.isAuto, 'PlayPosition': media.position, 'IsLive': media.isLive, 'CustomData': media.customData }); return _result; } ... class Media { Media({ this.url, this.title, this.subTitle, this.studio, this.contentType:, this.customData: const {}, this.isAuto, this.position, this.isLive, this.dialogThumbnail, this.notificationThumbnail, }) : ...
17.
private fun startCast(pCall:
MethodCall, pResult: Result) { val tArguments = pCall.arguments as? Map<String, Any> … // [1] メタデータの設定 val tMovieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE) tMovieMetadata.putString(MediaMetadata.KEY_TITLE, tTitle) tMovieMetadata.addImage(WebImage(Uri.parse(tDialogUrl), tWidth, tHeight)) tMovieMetadata.addImage(WebImage(Uri.parse(tNotificationUrl))) … // [2] メディア情報の作成 val tMediaInfo = MediaInfo.Builder(tUri) .setStreamType(tStreamType) .setContentType(tContentType) .setMetadata(tMovieMetadata) .setCustomData(tJsonObject) .build() … Android/Kotlin // [3] 読み込み時のオプション val tMediaLoadOptions: MediaLoadOptions = MediaLoadOptions.Builder() .setAutoplay(tIsAuto) .setPlayPosition(tPlayPosition.toLong()) .build() … // [4] Chromecastにキャスト val tIsSuccess = (tCastSession?.remoteMediaClient?.load(tMediaInfo, tMediaLoadOptions) != null) pResult.success(tIsSuccess) }
18.
private func startCast(_
pCall: FlutterMethodCall, _ pResult: @escaping FlutterResult) { let tArguments = pCall.arguments as? Dictionary<String, Any> … // [1] メタデータの設定 let tMetadata:GCKMediaMetadata = GCKMediaMetadata(metadataType: GCKMediaMetadataType.movie) tMetadata.setString(tTitle, forKey: kGCKMetadataKeyTitle) tMetadata.addImage(GCKImage(url: URL(string: (tDialogUrl)), width: tWidth, height: tHeight)) … // [2] メディア情報の作成 let tBuilder = GCKMediaInformationBuilder.init(contentURL: tParseUrl) tBuilder.contentID = tUri tBuilder.streamType = tStreamType tBuilder.contentType = tContentType tBuilder.metadata = tMetadata tBuilder.customData = tJsonData … // [3] 読み込み時のオプション let tMediaLoadOption = GCKMediaLoadOptions(); tMediaLoadOption.autoplay = tIsAuto tMediaLoadOption.playPosition = tPlayPosition … // [4] Chromecastにキャスト var tIsSuccess = true; if (tCastSession?.remoteMediaClient? .loadMedia(tMediaInfo, with: tMediaLoadOption)) != nil { tIsSuccess = true; } else { tIsSuccess = false } pResult(tIsSuccess) } iOS/Swift
19.
実演(動画)
20.
● 仕様 ○ Android
/ iOSで動作するFlutterプラグイン及びアプリ ■ Flutter : 1.2.1 ■ Android : Kotlin 1.3.11 / gms-play-service : 16.1.2 ■ iOS : Swift 4.2.1 / google-cast-sdk : 4.3.5 ● 開発エディタ ○ VSCode ○ Android Studio ○ Xcode ● Google Cast Document ○ https://developers.google.com/cast/docs/developers ● Google Cast GitHub(Android/iOS) ○ https://github.com/googlecast/CastVideos-ios ○ https://github.com/googlecast/CastVideos-android ここから先のスライドは補足
21.
補足 iOS/Xcodeで開発する場合の注意 ● Xcode10以上、iOS
12以降をターゲットにしている場合 ● Access Wifi InformationをONにする
22.
Cast Buttonの表示 ● キャスト可能なデバイスがあるときはアイコンを表示、接続中は接続中のア イコン、キャスト可能なデバイスがない場合は非表示に切り替える ●
Cast StatusをAndroid/iOSのプラグイン側から取得 ● Cast Statusの状態をもとにFlutter側でアイコンの表示・非表示を切り替える キャストしてない キャスト中
23.
private fun getCastState(pCall:
MethodCall, pResult: Result) { val tCastContext = CastContext.getSharedInstance() val Status = tCastContext.castState pResult.success(tStatus) } Android/Kotlin enum CastStatus { NO_DEVICES_AVAILABLE, NOT_CONNECTED, CONNECTING, CONNECTED, } … Future<CastStatus> getCastStatus() async { CastStatus _castStatus; int _result = await _channel.invokeMethod('GetCastStatus'); _castStatus = _getCastStateByValue(_result); return _castStatus; } … CastStatus _getCastStateByValue(int result) { switch (result) { case 1: _castStatus = CastStatus.NO_DEVICES_AVAILABLE; … break; case 2: _castStatus = CastStatus.NOT_CONNECTED; … break; … chrome_cast_plugin.dart private func getCastStatus(_ pCall: FlutterMethodCall, _ pResult: @escaping FlutterResult) { let tCastContext = GCKCastContext.sharedInstance() let tStatus = tCastContext.castState.rawValue pResult(tStatus) } iOS/Swift
24.
リスナーの登録 ● SessionManager Listener ○
アプリ全体のキャストセッションを管理 ○ SessionManagerに登録する ■ アプリがセッションを開始したとき ■ アプリがセッションとの接続を停止したとき など… ● RemoteMediaClient Listener ○ アプリと現在キャストしているデバイス(Chromecast)のメディアのセッションを管理 ○ CastSessionに登録する ■ 動画などをキャストをしたとき ■ キャストした動画に変更があったとき など… ● Flutter側の実装は必要なし ○ ※リスナーの中身の実装は今回は省略
25.
class ChromeCastPlugin(private val
pActivity: Activity, private val pChannel: MethodChannel): … CastContext.getSharedInstance()?.sessionManager? .addSessionManagerListener(*fugafura, CastSession::class.java) ... CastContext.getSharedInstance()?.sessionManager?.currentCastSession? .remoteMediaClient?.registerCallback(*hogehoge) …. Android/Kotlin public class SwiftChromeCastPlugin: NSObject, FlutterPlugin, GCKSessionManagerListener, GCKRemoteMediaClientListener { … GCKCastContext.sharedInstance().sessionManager.add(*hogehoge) …. GCKCastContext.sharedInstance().sessionManager.currentSession?.remoteMediaClient?.add(*fugafuga) …. iOS/Swift
26.
キャストした動画を制御する ● キャストしている動画をFlutter側から制御(一時停 止やシークなど)する ● Flutter側 ○
動画コントロールパネル用のUIを作る ● Plugin側 ○ 現在のキャストセッションからリモートメディアクライアン ト(remoteMediaClient)を取得して再生(play)や一時停止 (pause)、シーク(seek)を呼ぶ シーク 停止 再生 ボリューム変更
27.
Android/Kotlin private fun setMediaStreamPosition(pCall:
MethodCall, pResult: Result) { … tCastSession.remoteMediaClient.seek(tSeekTime.toLong()) … } iOS/Swift private func setMediaStreamPosition(_ pCall: FlutterMethodCall, _ pResult: @escaping FlutterResult) { … let tOptions = GCKMediaSeekOptions() // remotMediaClient.seekに渡す引数のために、ミリ秒から秒へ変換 tOptions.interval = tTime! / 1000 tCastSession?.remoteMediaClient?.seek(with: tOptions) } chrome_cast_plugin.dart Future<bool> setMediaStreamPosition(var milliseconds) async { bool _result = await _channel.invokeMethod('SetMediaStreamPosition', milliseconds); return _result; } 例)シーク
28.
応用 キャストボタンの表示バグをなくす ● Cast
Statusの状態がNOT_CONNECTEDの状態からアプリがバックグラウンドに いくと、キャストデバイスはない状態(NO_DEVICES_AVALLABLE)になる ● アプリがフォアグラウンドに戻った際に、キャストボタンが表示・非表示をし て点滅する ○ Google Playムービーなどの一部アプリでも起きている ● アプリがフォアグラウンドに戻ってきた際、数ミリ秒まってからCast Stateの 状態を取得する
29.
chrome_cast_plugin.dart class ChromeCast extends
WidgetsBindingObserver { … WidgetsBinding.instance.addObserver(this); …. @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); switch (state) { case AppLifecycleState.paused: _isDelay = true; break; default: break; } } … abstract WidgetsBindingObserver アプリのライフサイクルを監視する didChangeAppLifecycleState関数を使うために継承 didChangeAppLifecycleState AppLifecycleState state ● resumed ● inactive ● paused ● suspending のいずれかの状態を返す pausedのときに遅延を起こす WidgetsBindingObserverに登録
30.
chrome_cast_plugin.dart ... typedef OnChangeCastState =
Function(CastStatus); … class ChromeCast extends WidgetsBindingObserver { static const MethodChannel _channel = const MethodChannel('chrome_cast_plugin'); OnChangeCastState onChangeCastState; bool _isDelay = false; bool _isWaiting = false; … ChromeCast._internal() { … if (call.method == "OnChangeCastState") { CastStatus _castStatus = _getCastStateByValue(call.arguments); if (_isDelay) { if (_isWaiting == false) { _delayGetCastStatus(); } } else { onChangeCastState(_castStatus); } } }); ... } … chrome_cast_plugin.dart OnChangeCastState キャストの状態が変化したときに呼ばれるようにデリ ゲートを用意 _delayGetCastStatus 数ミリ秒待機してからキャストの状態を取得する(自作)
31.
… _delayGetCastStatus() async { _isWaiting
= true; await Future.delayed(Duration(milliseconds: _waitSeconds)); getCastStatus().then((onStatus) { if (onStatus != CastStatus.NO_DEVICES_AVAILABLE) { onChangeCastState(onStatus); _isDelay = false; } _isWaiting = false; }); } … chrome_cast_plugin.dart 数秒待機してCastStatusを取得する
Download now