Android Sender App to cast videos to a Receiver App
Posted By : Sapna Sharma | 29-Jun-2015
A sender application or Google Cast application refers to an app running on a mobile device (the sender device).
Setup
-
Download the latest version of the Android SDK using the Android SDK Manager.
-
Install the Android Support Libraries through the Android SDK Manager. The support libraries need to be revision 19.0.1 or later.
-
Install the Google Play services SDK through the Android SDK Manager. The Google Play services SDK needs to be revision 4.2 or later.
Add Libraries
The following libraries are required :
-
android-support-v7-appcompat : path to this library is <SDK install location>/extras/android/support/v7/appcompat
-
android-support-v7-mediarouter : path to this library is <SDK install location>/extras/android/support/v7/mediarouter
-
google-play-services_lib : path to this library is <SDK install location>/extras/google/google_play_services/libproject/google-play-services_lib
Note
- Import these libraries as library projects for your IDE instead of including their JAR files
- Android-support-v7-mediarouter has a dependency on android-support-v7-appcompat, so add it by selecting the android-support-v7-mediarouter project Properties, then select Android, and in the Libraries list, add android-support-v7-appcompat.
- Ensure the build target for each of the imported libraries is same : select the library project Properties and then select Android. Select a different Project Build Target, select "Apply", then re-select the desired target (> API 17) and hit "Apply" again.
Development
Android manifest
Add following configuration in AndroidManifest.xml file to use the Cast SDK:
Internet permission
uses-sdk
The minimum Android SDK version that the Cast SDK supports is 9 (GingerBread).
meta-data
android:theme
The applicationís theme needs to be correctly set based on the minimum Android SDK version.
...
Adding the Cast Button
The MediaRouter framework provides a Cast button and a list selection dialog for selecting a route. There are three ways to support a Cast button:
-
Using the MediaRouter ActionBar provider: android.support.v7.app.MediaRouteActionProvider
-
Using the MediaRouter Cast button: android.support.v7.app.MediaRouteButton
-
Developing a custom UI with the MediaRouter APIís and MediaRouter.Callback
This blog describes the use of the MediaRouteActionProvider to add the Cast button to the ActionBar.This action bar button handles all of the state logic needed for hiding or displaying the casting button, as well as coloring it when the app has connected to a casting device.Add the media routing button to our menu.xml as follows :
Add some values to strings.xml, such as the video URL, receiver application ID and general UI strings.You'll need to replace app_id
with your application ID, and video_url with a video that you would want to play.
Chromecast App xxxxx xxxxx Play Video Pause Video Resume Video Your Video Title Here video/mp4
The UI for this activity consists of a single button that will start a video if the app is connected to a casting device, or can pause/resume a video once it has been started, and the media router button in the action bar.The action bar button will display only if you are connected with chromecast device on the same wifi network.
Add following code to your casting activity i.e, MainActivity.java
package com.example.chromecastapp; import android.os.Bundle; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.ActionBarActivity; import android.support.v7.app.MediaRouteActionProvider; import android.support.v7.media.MediaRouteSelector; import android.support.v7.media.MediaRouter; import android.support.v7.media.MediaRouter.RouteInfo; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import com.google.android.gms.cast.ApplicationMetadata; import com.google.android.gms.cast.Cast; import com.google.android.gms.cast.CastDevice; import com.google.android.gms.cast.CastMediaControlIntent; import com.google.android.gms.cast.MediaInfo; import com.google.android.gms.cast.MediaMetadata; import com.google.android.gms.cast.MediaStatus; import com.google.android.gms.cast.RemoteMediaPlayer; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.common.api.Status; import java.io.IOException; public class MainActivity extends ActionBarActivity { private Button mButton; private MediaRouter mMediaRouter; private MediaRouteSelector mMediaRouteSelector; private MediaRouter.Callback mMediaRouterCallback; private CastDevice mSelectedDevice; private GoogleApiClient mApiClient; private RemoteMediaPlayer mRemoteMediaPlayer; private Cast.Listener mCastClientListener; private boolean mWaitingForReconnect = false; private boolean mApplicationStarted = false; private boolean mVideoIsLoaded; private boolean mIsPlaying; @Override protected void onCreate( Bundle savedInstanceState ) { super.onCreate( savedInstanceState ); setContentView( R.layout.activity_main ); mButton = (Button) findViewById( R.id.button ); mButton.setOnClickListener( new OnClickListener() { @Override public void onClick( View v ) { if( !mVideoIsLoaded ) startVideo(); else controlVideo(); } }); initMediaRouter(); } private void initMediaRouter() { // Configure Cast device discovery mMediaRouter = MediaRouter.getInstance( getApplicationContext() ); mMediaRouteSelector = new MediaRouteSelector.Builder() .addControlCategory( CastMediaControlIntent.categoryForCast( getString( R.string.app_id ) ) ) .build(); mMediaRouterCallback = new MediaRouterCallback(); } private void initCastClientListener() { mCastClientListener = new Cast.Listener() { @Override public void onApplicationStatusChanged() { } @Override public void onVolumeChanged() { } @Override public void onApplicationDisconnected( int statusCode ) { teardown(); } }; } private void initRemoteMediaPlayer() { mRemoteMediaPlayer = new RemoteMediaPlayer(); mRemoteMediaPlayer.setOnStatusUpdatedListener( new RemoteMediaPlayer.OnStatusUpdatedListener() { @Override public void onStatusUpdated() { MediaStatus mediaStatus = mRemoteMediaPlayer.getMediaStatus(); mIsPlaying = mediaStatus.getPlayerState() == MediaStatus.PLAYER_STATE_PLAYING; } }); mRemoteMediaPlayer.setOnMetadataUpdatedListener( new RemoteMediaPlayer.OnMetadataUpdatedListener() { @Override public void onMetadataUpdated() { } }); } private void controlVideo() { if( mRemoteMediaPlayer == null || !mVideoIsLoaded ) return; if( mIsPlaying ) { mRemoteMediaPlayer.pause( mApiClient ); mButton.setText( getString( R.string.resume_video ) ); } else { mRemoteMediaPlayer.play( mApiClient ); mButton.setText( getString( R.string.pause_video ) ); } } private void startVideo() { MediaMetadata mediaMetadata = new MediaMetadata( MediaMetadata.MEDIA_TYPE_MOVIE ); mediaMetadata.putString( MediaMetadata.KEY_TITLE, getString( R.string.video_title ) ); MediaInfo mediaInfo = new MediaInfo.Builder( getString( R.string.video_url ) ) .setContentType( getString( R.string.content_type_mp4 ) ) .setStreamType( MediaInfo.STREAM_TYPE_BUFFERED ) .setMetadata( mediaMetadata ) .build(); try { mRemoteMediaPlayer.load( mApiClient, mediaInfo, true ) .setResultCallback( new ResultCallback() { @Override public void onResult( RemoteMediaPlayer.MediaChannelResult mediaChannelResult ) { if( mediaChannelResult.getStatus().isSuccess() ) { mVideoIsLoaded = true; mButton.setText( getString( R.string.pause_video ) ); } } } ); } catch( Exception e ) { } } @Override protected void onResume() { super.onResume(); // Start media router discovery mMediaRouter.addCallback( mMediaRouteSelector, mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN ); } @Override protected void onPause() { if ( isFinishing() ) { // End media router discovery mMediaRouter.removeCallback( mMediaRouterCallback ); } super.onPause(); } private class MediaRouterCallback extends MediaRouter.Callback { @Override public void onRouteSelected(MediaRouter router, RouteInfo info) { initCastClientListener(); initRemoteMediaPlayer(); mSelectedDevice = CastDevice.getFromBundle( info.getExtras() ); launchReceiver(); } @Override public void onRouteUnselected( MediaRouter router, RouteInfo info ) { teardown(); mSelectedDevice = null; mButton.setText( getString( R.string.play_video ) ); mVideoIsLoaded = false; } } private void launchReceiver() { Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions .builder( mSelectedDevice, mCastClientListener ); ConnectionCallbacks mConnectionCallbacks = new ConnectionCallbacks(); ConnectionFailedListener mConnectionFailedListener = new ConnectionFailedListener(); mApiClient = new GoogleApiClient.Builder( this ) .addApi( Cast.API, apiOptionsBuilder.build() ) .addConnectionCallbacks( mConnectionCallbacks ) .addOnConnectionFailedListener( mConnectionFailedListener ) .build(); mApiClient.connect(); } private class ConnectionCallbacks implements GoogleApiClient.ConnectionCallbacks { @Override public void onConnected( Bundle hint ) { if( mWaitingForReconnect ) { mWaitingForReconnect = false; reconnectChannels( hint ); } else { try { Cast.CastApi.launchApplication( mApiClient, getString( R.string.app_id ), false ) .setResultCallback( new ResultCallback () { @Override public void onResult(Cast.ApplicationConnectionResult applicationConnectionResult) { Status status = applicationConnectionResult.getStatus(); if( status.isSuccess() ) { //Values that can be useful for storing/logic ApplicationMetadata applicationMetadata = applicationConnectionResult.getApplicationMetadata(); String sessionId = applicationConnectionResult.getSessionId(); String applicationStatus = applicationConnectionResult.getApplicationStatus(); boolean wasLaunched = applicationConnectionResult.getWasLaunched(); mApplicationStarted = true; reconnectChannels( null ); } } } ); } catch ( Exception e ) { } } } @Override public void onConnectionSuspended(int i) { mWaitingForReconnect = true; } } private void reconnectChannels( Bundle hint ) { if( ( hint != null ) && hint.getBoolean( Cast.EXTRA_APP_NO_LONGER_RUNNING ) ) { //Log.e( TAG, "App is no longer running" ); teardown(); } else { try { Cast.CastApi.setMessageReceivedCallbacks( mApiClient, mRemoteMediaPlayer.getNamespace(), mRemoteMediaPlayer ); } catch( IOException e ) { //Log.e( TAG, "Exception while creating media channel ", e ); } catch( NullPointerException e ) { //Log.e( TAG, "Something wasn't reinitialized for reconnectChannels" ); } } } private class ConnectionFailedListener implements GoogleApiClient.OnConnectionFailedListener { @Override public void onConnectionFailed( ConnectionResult connectionResult ) { teardown(); } } @Override public boolean onCreateOptionsMenu( Menu menu ) { super.onCreateOptionsMenu( menu ); getMenuInflater().inflate( R.menu.main, menu ); MenuItem mediaRouteMenuItem = menu.findItem( R.id.media_route_menu_item ); MediaRouteActionProvider mediaRouteActionProvider = (MediaRouteActionProvider) MenuItemCompat.getActionProvider( mediaRouteMenuItem ); mediaRouteActionProvider.setRouteSelector( mMediaRouteSelector ); return true; } private void teardown() { if( mApiClient != null ) { if( mApplicationStarted ) { try { Cast.CastApi.stopApplication( mApiClient ); if( mRemoteMediaPlayer != null ) { Cast.CastApi.removeMessageReceivedCallbacks( mApiClient, mRemoteMediaPlayer.getNamespace() ); mRemoteMediaPlayer = null; } } catch( IOException e ) { //Log.e( TAG, "Exception while removing application " + e ); } mApplicationStarted = false; } if( mApiClient.isConnected() ) mApiClient.disconnect(); mApiClient = null; } mSelectedDevice = null; mVideoIsLoaded = false; } @Override public void onDestroy() { teardown(); super.onDestroy(); } }
Thanks
Cookies are important to the proper functioning of a site. To improve your experience, we use cookies to remember log-in details and provide secure log-in, collect statistics to optimize site functionality, and deliver content tailored to your interests. Click Agree and Proceed to accept cookies and go directly to the site or click on View Cookie Settings to see detailed descriptions of the types of cookies and choose whether to accept certain cookies while on the site.
About Author
Sapna Sharma
Sapna is a bright Android Apps developer using Titanium framework. Sapna likes music and helping needy people.