1 year ago

#278468

test-img

Lallawmzuala khawlhring

Google cast sender app not opening TV receiver app

I'm trying to implement video casting for my flutter app using the package cast

Ive registered my app on the cast console and able to cast my videos from the sender android mobile app made with flutter using the following code to the Android TV

session.value.stateStream.listen((state) async {
    log('session state->' + state.toString());
    if (state == CastSessionState.connecting) {
      log('Connecting...');
    }
    if (state == CastSessionState.connected) {
      var media = {
        "contentId": _purchaseController.purchasedMedia.value.mpdH264,
        'contentUrl': _purchaseController.purchasedMedia.value.mpdH264,
        "streamType": "BUFFERED", // or LIVE
        "contentType": "application/dash+xml",
        "metadata": {
          "metadataType": 1,
          "title": video.title,
          "studio": "Lersia",
          "releaseDate": video.releaseYear,
          "images": [
            {"url": video.lgLandscape}
          ]
        },
        "customData": {
          "token": _purchaseController.purchasedMedia.value.drmTokenH264,
        }
      };
      try {
        session.value.sendMessage(CastSession.kNamespaceMedia, {
          'type': 'LOAD',
          'autoPlay': true,
          'media': media,
        });
        Get.snackbar("Cast Success", "Video casted to $_deviceName");
        setupNotifications();
        MediaPlayerCentral.add(video);
        MediaPlayerCentral.playPause();
      } catch (e) {
        log('stateStreamOnConnectedError=>' + e.toString());
      }
    }
    if (state == CastSessionState.closed) {
      try {
        Get.snackbar("Cast Disconnected", "Disconnected from $_deviceName");
        connectedDevice.value = null;
        castingVideo.value = null;
        NotificationUtils.cancelNotification(100);
        MediaPlayerCentral.stop();
        connectedDevice.value = null;
        if (session.value != null) {
          await session.value.socket.flush();
          await session.value.socket.close();
          session.value = null;
        } else {
          session.value = null;
        }
      } catch (e) {
        log('stateStreamOnClosedError=>' + e.toString());
      }
    }
  });
  var index = 0;
  session.value.messageStream.listen((message) {
    log("messageStream " + message.toString());
    var _status = message['status'];
    if (_status != null && _status is List && _status.isNotEmpty) {
      var _returnedTime = _status.first['currentTime'];
      currentPlayInSeconds.value = (_returnedTime is int)
          ? _returnedTime
          : int.tryParse(_returnedTime.toString());
    }
    requestId.value = message['requestId'];
    index += 1;
    if (index == 4) {
      Future.delayed(Duration(seconds: 2)).then((x) {
        var media = {
          "contentId": _purchaseController.purchasedMedia.value.mpdH264,
          "streamType": "BUFFERED", // or LIVE
          "contentType": "application/dash+xml",
          "metadata": {
            "metadataType": 1,
            "title": video.title,
            "studio": "Lersia",
            "releaseDate": "30 Sep 2021",
            'posterUrl': video.lgLandscape,
            "images": [
              {"url": video.lgLandscape}
            ]
          },
          "customData": {
            "licenseUrl":
                _purchaseController.purchasedMedia.value.drmWidevine,
            "token": _purchaseController.purchasedMedia.value.drmTokenH264,
          }
        };

        try {
          session.value.sendMessage(CastSession.kNamespaceMedia, {
            'requestId': 1,
            'type': '',
            'media': media,
          });
        } catch (e) {
          log('messageStreamListenError=>' + e.toString());
        }
      });
    }
  });
  session.value.sendMessage(CastSession.kNamespaceReceiver, {
    'type': 'LAUNCH',
    'appId': '0E0BA573', // set the appId of your app here
  });

On the android TV, the cast message is received and the video is opened on the browser even when the TV app is installed. I've added the CastOptionsProvider class and specified it on the manifest like below

class CastOptionsProvider : OptionsProvider {

override fun getCastOptions(context: Context): CastOptions? {
    val notificationOptions = NotificationOptions.Builder()
        .build()
    val mediaOptions = CastMediaOptions.Builder()
        .setNotificationOptions(notificationOptions)
        .build()
    val credentialsData = CredentialsData.Builder()
        .setCredentials("{\"userId\": \"abc\"}")
        .build()
    val launchOptions: LaunchOptions = LaunchOptions.Builder()
        .setAndroidReceiverCompatible(true)
        .setCredentialsData(credentialsData)
        .build()
  return  CastOptions.Builder()
        .setLaunchOptions(launchOptions)
        .setReceiverApplicationId(context.getString(R.string.app_id))
        .setCastMediaOptions(mediaOptions)
        .build();
}

override fun getAdditionalSessionProviders(context: Context?): List<SessionProvider?>? {
    return null
}

}

And this is the manifest file

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lersia.mobile">
<uses-permission android:name="android.permission.INTERNET"/>
<application
    android:label="Lersia Play"
    android:icon="@mipmap/ic_launcher"
    android:roundIcon="@mipmap/ic_launcher_round">
    <activity
        android:name="com.lersia.mobile.MainActivity"
        android:launchMode="singleTop"
        android:theme="@style/LaunchTheme"
        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
        android:hardwareAccelerated="true"
        android:exported="true"
        android:windowSoftInputMode="adjustResize">
        <!-- Specifies an Android theme to apply to this Activity as soon as
             the Android process has started. This theme is visible to the user
             while the Flutter UI initializes. After that, this theme continues
             to determine the Window background behind the Flutter UI. -->
        <meta-data
          android:name="io.flutter.embedding.android.NormalTheme"
          android:resource="@style/NormalTheme"
          />
        <!-- Displays an Android View that continues showing the launch screen
             Drawable until Flutter paints its first frame, then this splash
             screen fades out. A splash screen is useful to avoid any visual
             gap between the end of Android's launch screen and the painting of
             Flutter's first frame. -->
        <meta-data
          android:name="io.flutter.embedding.android.SplashScreenDrawable"
          android:resource="@drawable/launch_background"
          />
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
    <meta-data
        android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
        android:value="com.lersia.mobile.CastOptionsProvider" />
    <!-- Don't delete the meta-data below.CastOptionsProvider
         This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
    <meta-data
        android:name="flutterEmbedding"
        android:value="2" />
</application>

I've also added the CastReceiverOptionsProvider on the TV app like below

class CastReceiverOptionsProvider : ReceiverOptionsProvider {
override fun getOptions(context: Context?): CastReceiverOptions? {
    return CastReceiverOptions.Builder(context)
        .setStatusText("Lersia tv status text")
        .build()
}

}

and specified it on the manifest file of the TV app as below

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lersia.tv">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-feature android:name="android.software.leanback" android:required="true" />
<uses-feature android:name="android.hardware.touchscreen"
      android:required="false" />
<application
    android:name="com.lersia.tv.MyApplication"
    android:label="Lersia Play"
    android:banner="@drawable/launch_banner"
    android:icon="@mipmap/ic_launcher">
    <activity
        android:name=".MainActivity"
        android:banner="@drawable/launch_banner"
        android:launchMode="singleTask"
        android:theme="@style/LaunchTheme"
        android:exported="true"
        android:label="Lersia Play"
        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
        android:hardwareAccelerated="true"
        android:windowSoftInputMode="adjustResize">
        <meta-data
          android:name="io.flutter.embedding.android.NormalTheme"
          android:resource="@style/NormalTheme"/>
        <meta-data
          android:name="io.flutter.embedding.android.SplashScreenDrawable"
          android:resource="@drawable/launch_background"/>
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
        <intent-filter>
            <action android:name="com.google.android.gms.cast.tv.action.LAUNCH" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>
    <activity
        android:name="com.lersia.tv.TestIntent"
        android:exported="false"
        android:launchMode="singleTask">
        <intent-filter>
            <action android:name="com.google.android.gms.cast.tv.action.LOAD"/>
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>
    <meta-data
        android:name="com.google.android.gms.cast.tv.RECEIVER_OPTIONS_PROVIDER_CLASS_NAME"
        android:value="com.lersia.tv.CastReceiverOptionsProvider" />
    <meta-data
        android:name="flutterEmbedding"
        android:value="2" />
</application>

Am I missing something on why the TV app is not opening? Would be glad if someone can help

android

flutter

google-cast

android-tv

google-cast-sdk

0 Answers

Your Answer

Accepted video resources