miloproductionsinc/cordova-plugin-chromecast

Cast audio

Closed this issue · 9 comments

Is there any example on how to integrate it with an audio player. I use the cordova media plugin on my app and I need to be able to cast my audio. I install the plugin but I'm a little lost on how to use it. Any help will be appreciate.

I am hoping to add a bit of proper documentation on it's use later (because I also found it confusing), but basically, you use it exactly as you would use the chromecast for chrome desktop.

Here is how you start the connection for chrome desktop, and how to start the connection with the plugin. After that, usage should be identical.

/**
 * Sets the initialization trigger for Chrome Desktop browsers.
 */
window['__onGCastApiAvailable'] = function(isAvailable, err) {
  // If error, it is probably because we are not on chrome, so just disregard
  if (isAvailable) {
    initializeCastApi(); // Function from example code
  }
};

/**
 * Sets the initialization trigger for Cordova.
 */
document.addEventListener("deviceready", function () {
    if (chrome && chrome.cast) {
        initializeCastApi(); // Function from example code
    }
});

I haven't tested casting pure audio, so if you find any bugs please report them here or to jellyfin!

Thanks for the example codes! They help me figure it out how to set up things. Now I'm working on send some metadata to devices what works from web version sending title, subtitle and image but not on android. It sends the title and subtitle but not image. Also trying to figure it out how to update song metadata when song changes.

I haven't used the metadata before.

Can you post your code that works on the web? Then I can use it to make a test and I should be able to add/fix that functionality.
(Please include what the expected output is as well!)

This is the code I use for test on web with metadata. It sends the audio, title, subtitle and image to my Chrome Cast attached to my tv. But the metadata on android just send title and subtitle not the image.

<!DOCTYPE html>
<html lang="en">

<head>

  <script type="text/javascript" src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js"></script>

</head>

<body>
  Hello World</br>
<google-cast-launcher></google-cast-launcher>

  <a onclick="castNow()" href="#">Cast!</a></br>
  <a onclick="loadNow()" href="#">LOAD!</a></br>
  <a onclick="stopCasting()" href="#">STOP!</a>
  <script>
    var cast = {}

    var castNow = function() {
      console.log('casting!');
      var sessionRequest = new chrome.cast.SessionRequest(chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID);
      chrome.cast.requestSession(function onRequestSessionSuccess(session) {
        console.log('Session success', session);
        cast.session = session;
        loadNow();
      }, function onLaunchError(er) {
        console.log('onLaunchError', er)
      }, sessionRequest);

      setTimeout(function() {
        var sessionRequest = new chrome.cast.SessionRequest(chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID);
        chrome.cast.requestSession(function onRequestSessionSuccess(session) {
          console.log('Session success', session);
          cast.session = session;
        }, function onLaunchError(er) {
          console.log('onLaunchError', er);
        }, sessionRequest);

      }, 1000)

    }


    var loadNow = function() {
      console.log('LoadNow, session:', cast.session)
      var mediaInfo = new chrome.cast.media.MediaInfo('http://unoredradio.com:9580/;', 'audio/mpeg');
      mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata();
      mediaInfo.metadata.title = "Metal Del Cielo";
      mediaInfo.metadata.subtitle = "De las alturas a tu corazón";
      mediaInfo.metadata.images = [new chrome.cast.Image('https://e.snmc.io/i/300/w/f7f10e3177a629a486c6bd964188d3d1/7490356')];
      var request = new chrome.cast.media.LoadRequest(mediaInfo);
      cast.session.loadMedia(request,
        onMediaDiscovered.bind(this, 'loadMedia'),
        function(er) {
          console.log('onMediaError', er)
        });

      function onMediaDiscovered(how, media) {
        console.log('got media!', media)
        cast.currentMedia = media;
      }
    }

var stopCasting = function(){
  cast.session.stop();
}

    initializeCastApi = function() {

      console.log('initializing cast api')
      var sessionRequest = new chrome.cast.SessionRequest(chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID);
      var apiConfig = new chrome.cast.ApiConfig(sessionRequest,
        function(session) {
          console.log('got session', session)
          cast.session = session
        },
        function receiverListener(e) {
          if (e === chrome.cast.ReceiverAvailability.AVAILABLE) {
            console.log('receiver is available :)')
          }
        })

      chrome.cast.initialize(apiConfig, function() {
          console.log('got initSuccess')
        },
        function(gotError) {
          console.log('gotError', gotError)
        });
    };

    window.onload = function() {
      window['__onGCastApiAvailable'] = function(loaded, errorInfo) {
        console.log('in __onGCastApiAvailable, loaded:', loaded)
        if (loaded) {
          initializeCastApi()
        }
      };
    }
  </script>
</body>

Also I would like it to make the metadata change by song. I manage to do it on my cordova ionic v1 app using $interval function inside the loadNow() function but then I have problems with audio because it keeps loading. An example of the code is:

var loadNow = function() {

     $interval(function() {
           var mediaInfo = new chrome.cast.media.MediaInfo('http://unoredradio.com:9580/;', 'audio/mpeg');
           mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata();
           mediaInfo.metadata.title = $rootScope.songtitle;
           mediaInfo.metadata.subtitle = $rootScope.artist;
           mediaInfo.metadata.images = [new chrome.cast.Image($rootScope.artwork];
           var request = new chrome.cast.media.LoadRequest(mediaInfo);
           cast.session.loadMedia(request,
           onMediaDiscovered.bind(this, 'loadMedia'),
               function(er) {
                   console.log('onMediaError', er)
               });
       },10000)

      function onMediaDiscovered(how, media) {
        console.log('got media!', media)
        cast.currentMedia = media;
      }
    }

This send the song title, artist name and song cover to the Chrome Cast and check it every 10 secs. for changes. This actually works sending the Song Title and Artist Name but not the image and of course no audio. I read something about RemoteMediaPlayerStatusUpdated() here but it is an old post so I don't know if it works.

Thanks I will take a look within the next 3 days!

Sorry this took so long. It took me much longer than expected to get some other things done.
My most recent commit on branch 1.0.0 should have the metadata fixed. (Let me know if it works as expected for you now!)

I will take a look at audio casting tomorrow I think. (edit: in a day or two, coming down with a cold)

Hi @Mabudigital,

I did some more work on the metadata. I think it should be working much better now. I have confirmed images (among quite a few other parameters work).

I also tested audio, I was able to confirm that an mp3 worked (after my recent changes). If your audio still doesn't work after updating the plugin, you could try the mp3 I am using. (Maybe it is your audio stream, I did find trying to load large pictures caused the chromecast to crash...).

https://ia600304.us.archive.org/20/items/OTRR_Gunsmoke_Singles/Gunsmoke_52-10-03_024_Cain.mp3

As for detecting the end of the audio stream, I'm not sure, I haven't tried that yet. But hopefully it is the same as detecting a video end. This is how I detected a video end:

media.addUpdateListener(function listener (isAlive) {
    if (media.playerState === chrome.cast.media.PlayerState.IDLE) {
        media.removeUpdateListener(listener);
        assert.equal(media.idleReason, chrome.cast.media.IdleReason.FINISHED);
        assert.isFalse(isAlive);
        // if FINISHED I think we can be pretty sure the playback finished naturally
    }
});

Then, when you load new media you should receive it in the loadMedia successCallback (it should have the updated metadata I believe).

Please let me know if it is working properly now or not!

Hi @Lindsay-Needs-Sleep. Thanks for take time for check it. Right now it is working as expected except for an error that make the app crash. When I select the cast device the app crash and the following error appear on my android studio logcat.

10-15 15:56:35.299 23719-23719/MY_APP E/AndroidRuntime: FATAL EXCEPTION: main
Process: MY_APP, PID: 23719
java.lang.NullPointerException: Attempt to invoke virtual method 'int com.google.android.gms.cast.TextTrackStyle.getBackgroundColor()' on a null object reference
at acidhax.cordova.chromecast.ChromecastUtilities.createTextTrackObject(ChromecastUtilities.java:192)
at acidhax.cordova.chromecast.ChromecastSession.createMediaInfoObject(ChromecastSession.java:540)
at acidhax.cordova.chromecast.ChromecastSession.createMediaObject(ChromecastSession.java:570)
at acidhax.cordova.chromecast.ChromecastSession.onStatusUpdated(ChromecastSession.java:687)
at com.google.android.gms.cast.RemoteMediaPlayer.onStatusUpdated(Unknown Source)
at com.google.android.gms.cast.RemoteMediaPlayer.zza(Unknown Source)
at com.google.android.gms.cast.zzax.onStatusUpdated(Unknown Source)
at com.google.android.gms.internal.cast.zzdx.onStatusUpdated(Unknown Source)
at com.google.android.gms.internal.cast.zzdx.zzo(Unknown Source)
at com.google.android.gms.cast.RemoteMediaPlayer.onMessageReceived(Unknown Source)
at com.google.android.gms.internal.cast.zzdj.run(Unknown Source)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at com.google.android.gms.internal.cast.zzez.dispatchMessage(Unknown Source)
at android.os.Looper.loop(Looper.java:179)
at android.app.ActivityThread.main(ActivityThread.java:5537)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:955)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:750)

Currently I don't have any reference to TextTracks on my code.

I think I have fixed this issue (on 1.0.0 branch). (Since I haven't encountered it before, I'm not 100% sure, but it should be okay now I think.)