2

I created an app in which there is a list view with youtube links. Now I want to play these videos using chrome cast. I followed the official documentation and I am able to play other videos with direct mp4 link, but its not working for youtube links.

Of course the other links are directly connected to a video (ending *.mp4), but how can i add a media with a youtube link? Somehow I need to create a MediaInfo object with youtube link, but I don't know how to do that or is it even possible.

I have found this information

MimeData data = new MimeData("v=g1LsT1PVjUA", MimeData.TYPE_TEXT); mSession.startSession("YouTube", data);

Open Chromecast YouTube video from my Android app

But I am not sure how to get the session and load it with youtube session. I would appreciate if someone can help me with this.

Thanks for your help

Community
  • 1
  • 1
Anurag
  • 1,521
  • 2
  • 17
  • 34

4 Answers4

4

I wrote an Android app that plays YouTube/Local media on VLC attached to a big screen TV last year. It was playing very nice but I always want to replace the VLC laptop with something more elegant. I was really excited when Chromecast was finally released with an official SDK. I too ran into the same problem when trying to launch YouTube receiver app to play YouTube video so I decided to delve into how VLC was able to do that (Is open source great :)) I found out that the YouTube video ID is just a JavaScript page with lots of stuff embedded inside. The trick is to pull out the correct info from it. Unfortunately, the VLC script is written in Lua so I spent a couple weeks learning enough Lua to navigate my way around the Lua script. You can google youtube.lua for the source code of the script.

I rewrote the script in Java and was able to modify CastVideos Android to play YouTube video at ease. Note that since Chromecast can only play mp4 video container format, video in other format may not play.

Here is my test code if anyone is interested. You can test the URL using CastHelloVideo-chrome load custom media.

Enjoy,

Danh

    package com.dql.urlexplorer;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

public class UrlExplore {

    private static final String URL_ENCODED_STREAM_MAP     = "\"url_encoded_fmt_stream_map\":";
    private static final String VIDEO_TITLE_KEYWORD               = "<meta name=\"title\"";
    private static final String VIDEO_DESCRIPTION_KEYWORD  = "<meta name=\"description\"";
    private static final String VIDEO_THUMBNAIL_KEYWORD    = "<meta property=\"og:image\"" ;
    private static final String CONTENT_VALUE_KEYWORD        = "content=\"";

    //private static final String TEST_URL = "http://www.youtube.com/watch?v=JtyCM4BTbYo";
    private static final String YT_UTRL_PREFIX = "http://www.youtube.com/watch?v=";

    public static void main(String[] args) throws IOException {

        while (true) {
               String videoId = getVideoIdFromUser();

                String urlSite = YT_UTRL_PREFIX + videoId;
                System.out.println("URL =  " + urlSite);
                System.out.println("===============================================================");
                System.out.println("Getting video site content");
                System.out.println("===============================================================");

                CloseableHttpClient httpclient = HttpClients.createDefault();
                try {
                    HttpGet httpget = new HttpGet(urlSite);

                    System.out.println("Executing request " + httpget.getRequestLine());


                    // Create a custom response handler
                    ResponseHandler<String> responseHandler = new ResponseHandler<String>() {

                        public String handleResponse(
                                final HttpResponse response) throws ClientProtocolException, IOException {
                            int status = response.getStatusLine().getStatusCode();
                            if (status >= 200 && status < 300) {
                                HttpEntity entity = response.getEntity();
                                return entity != null ? EntityUtils.toString(entity) : null;
                            } else {
                                throw new ClientProtocolException("Unexpected response status: " + status);
                            }
                        }

                    };
                    String responseBody = httpclient.execute(httpget, responseHandler);

                    if (responseBody.contains(VIDEO_TITLE_KEYWORD)) {
                        // video title
                        int titleStart = responseBody.indexOf(VIDEO_TITLE_KEYWORD);
                        StringBuilder title = new StringBuilder();
                        char ch;
                        do {
                            ch = responseBody.charAt(titleStart++);
                            title.append(ch);
                        }
                        while (ch != '>');
                        String videoTitle = getKeyContentValue(title.toString());
                         System.out.println("Video Title =  " + videoTitle);
                    }
                    if (responseBody.contains(VIDEO_DESCRIPTION_KEYWORD)) {
                        // video description
                        int descStart = responseBody.indexOf(VIDEO_DESCRIPTION_KEYWORD);
                        StringBuilder desc = new StringBuilder();
                        char ch;
                        do {
                            ch = responseBody.charAt(descStart++);
                            desc.append(ch);
                        }
                        while (ch != '>');
                        String videoDesc = getKeyContentValue(desc.toString());
                         System.out.println("Video Description =  " + videoDesc);                       
                    }
                    if (responseBody.contains(VIDEO_THUMBNAIL_KEYWORD)) {
                        // video thumbnail
                        int thumbnailStart = responseBody.indexOf(VIDEO_THUMBNAIL_KEYWORD);
                        StringBuilder thumbnailURL = new StringBuilder();
                        char ch;
                        do {
                            ch = responseBody.charAt(thumbnailStart++);
                            thumbnailURL.append(ch);
                        }
                        while (ch != '>');
                        String videoThumbnail= getKeyContentValue(thumbnailURL.toString());
                         System.out.println("Video Thumbnail =  " + videoThumbnail);                        
                    }
                    if (responseBody.contains(URL_ENCODED_STREAM_MAP)) {
                        // find the string we are looking for
                        int start = responseBody.indexOf(URL_ENCODED_STREAM_MAP) + URL_ENCODED_STREAM_MAP.length() + 1;  // is the opening "
                        String urlMap = responseBody.substring(start);
                        int end = urlMap.indexOf("\"");
                        if (end > 0) {
                            urlMap = urlMap.substring(0, end);
                        }
                        String path = getURLEncodedStream(urlMap);
                        System.out.println("Video URL = " + path);
                    }

                }
                finally {
                    httpclient.close();
                }
                System.out.println( "===============================================================");
                System.out.println("Done: ");
                System.out.println("===============================================================");      
        }

    }

    static String getURLEncodedStream(String stream) throws UnsupportedEncodingException {
        // replace all the \u0026 with &
         String str = stream.replace("\\u0026", "&");
        //str = java.net.URLDecoder.decode(stream, "UTF-8");
        //System.out.println("Raw URL map = " + str);
        String urlMap = str.substring(str.indexOf("url=http") + 4);
        // search urlMap until we see either a & or ,
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < urlMap.length(); i++) {
            if ((urlMap.charAt(i) == '&') || (urlMap.charAt(i) == ','))
                break;
            else
                sb.append(urlMap.charAt(i));
        }
        //System.out.println(java.net.URLDecoder.decode(sb.toString(),"UTF-8"));
        return java.net.URLDecoder.decode(sb.toString(),"UTF-8");

    }

    static String getKeyContentValue(String str) {
        StringBuilder contentStr = new StringBuilder();
        int contentStart = str.indexOf(CONTENT_VALUE_KEYWORD) + CONTENT_VALUE_KEYWORD.length();
        if (contentStart > 0) {
            char ch;
            while (true) {
                ch = str.charAt(contentStart++);
                if (ch == '\"')
                    break;
                contentStr.append(ch);
            }
        }
        try {
            return java.net.URLDecoder.decode(contentStr.toString(),"UTF-8");
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    /*
     * Prompt the user to enter a video ID.
     */
    private static String getVideoIdFromUser() throws IOException {

        String videoId = "";

        System.out.print("Please enter a YouTube video Id: ");
        BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
        videoId = bReader.readLine();

        if (videoId.length() < 1) {
            // Exit if the user doesn't provide a value.
            System.out.print("Video Id can't be empty! Exiting program");
            System.exit(1);
        }

        return videoId;
    }

}`enter code here`
DanhKE6D
  • 91
  • 1
  • 2
    Note that if you publish an app on the Google Play store which use mp4 version of YouTube videos, that app will be subject to removal from the Play store. Put differently, if you use CastVideos or write something similar to cast mp4 version of YT videos to a chromecast device, your app will be removed. – Ali Naddaf Jun 17 '14 at 20:04
  • Now that's disappointing given the fact that Google is known for its open-ness. There should be a way to play youtube video within the media player itself. And it was already there in the preview SDK, I don't understand why it got removed in the first place. – Anurag Jun 30 '14 at 16:26
  • Actually Google Play is filled with apps like PVStar+ amongst others that scrape MP4 urls. For my own app which uses the YouTube Android API I have elected to play by the rules but it is not true to say apps will be removed although perhaps Google Play is aware of this but for some reason does not enforce their own TOS. I wouldn't recommend scraping MP4 urls but again, many apps do and have gotten away with it for years and actually advertise background audio with impunity. This is not fair to those who do play by the rules as it gives MP4 scrapers a decidely unfair competitive advantage – Krafty Sep 23 '14 at 00:34
  • I also didn't went with scraping mp4 urls and eventually didn't integrated chromecast support. I am still looking to cast youtube videos directly from app in a genuine way. – Anurag Nov 17 '14 at 06:58
  • Google don't want you scraping mp4 URLs for obvious reason: they can't place ads. In fact, they probably have security features like requiring the scrape and the mp4 download to come from the same IP address. So the above solution probably only works if your Android device is on the same wifi network as your Chromecast. It looks like they don't compare user agent headers. If they do, then your scraper code also needs to masquerade as the Chromecast. – Sarsaparilla Sep 05 '15 at 23:36
2

I recently wrote a library to solve this problem. chromecast-sender. It is an extension library for the android-youtube-player library and makes it easy to cast videos from an Android app to a Google Cast device.

The receiver uses the YouTube IFrame player API. Sender and Receiver communicate through a custom channel.

Pierfrancesco Soffritti
  • 1,678
  • 1
  • 18
  • 22
  • i love your youtube player.. but i cant seam to get the chromecast function to work.. do i need a custom receiver?if so where n how do i host it? cheers pal @Pierfrancesco Soffritti – markharrop Nov 04 '18 at 21:33
  • 1
    @markharrop you can read everything in the documentation https://github.com/PierfrancescoSoffritti/android-youtube-player#chromecast-extension-library During the last week i've been receiving some reports from other users that have troubles making it work. I'll look into it next week. Open an issue on GitHub if you want to stay updated. – Pierfrancesco Soffritti Nov 05 '18 at 04:49
  • ive managed to get it working pal, it was the fact i had not uploaded the custom receiver correctly. one more question though please if you dont mind? i want to use the custom ui when its connected to the chrome cast. so i can can play pause fastforward n stuff. But its written in kotlin. is the custom ui in the core app that is written in jave basicly the same as the chromecast sender? – markharrop Nov 05 '18 at 11:17
  • 1
    @markharrop open an issue on GitHub for questions, is easier for me to answer there! The UI on your phone that controls chromecast is just a bunch of views. You just need to create views, listeners and then call the library's methods. – Pierfrancesco Soffritti Nov 05 '18 at 17:00
0

You cannot do that with the official SDK. Some folks have used iframe approach but there is varying reports of success.

Ali Naddaf
  • 16,951
  • 2
  • 21
  • 28
0

We can join with live session usign current chromecast device: Use the below call back and let me know if you want the same as below then i will send remaining part of the code.

private final GoogleApiClient.ConnectionCallbacks connectionCallback = new GoogleApiClient.ConnectionCallbacks() {
        @Override
        public void onConnected(Bundle bundle) {
            // SDK confirms that GoogleApiClient is connected: GoogleApiClient.ConnectionCallbacks.onConnected
            Trace.d(TAG, "GoogleApiClient.ConnectionCallbacks # onConnected()");

            try {
                // Sender app launches or join the receiver app: Cast.CastApi.launchApplication
                Cast.CastApi.joinApplication(mApiClient).setResultCallback(connectionResultCallback);
            } catch (Exception e) {
                Trace.d(TAG, "Failed to join application");
            }
        }

        @Override
        public void onConnectionSuspended(int i) {
            Trace.d(TAG, "GoogleApiClient.ConnectionCallbacks # onConnectionSuspended()");
            try {
                Cast.CastApi.leaveApplication(mApiClient);
            } catch (Exception e) {
                Trace.d(TAG, "Failed to join application");
            }
        }
    };
  • Hi @Android Dev, if I understood correctly, you are saying that if the youtube app is already casting something then that particular session can be joined from within an application?? – Anurag Dec 10 '14 at 11:13
  • Yes. When we play a video from YouTube and casting is done then through our app we can join a current session with help of above logic. before that we need to call few callbacks in order to proceed. – Android Dev Dec 10 '14 at 12:06
  • ok, but then the application will be dependent on the condition that user should cast from youtube first. Isn't there a way to initiate the session as well? – Anurag Dec 10 '14 at 12:21
  • Yes. We can join in the same session when youtube is casted initially. – Android Dev Dec 17 '14 at 09:55