【问题标题】:Open Chromecast youtube video from android application从 android 应用程序打开 Chromecast youtube 视频
【发布时间】:2014-06-20 10:30:39
【问题描述】:

我创建了一个应用,其中有一个带有 youtube 链接的列表视图。现在我想使用 chrome cast 播放这些视频。我遵循了官方文档,我可以通过直接 mp4 链接播放其他视频,但它不适用于 youtube 链接。

当然,其他链接直接连接到视频(以 *.mp4 结尾),但是如何添加带有 youtube 链接的媒体?不知何故,我需要使用 youtube 链接创建一个 MediaInfo 对象,但我不知道该怎么做,甚至可能。

我找到了这个信息

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

Open Chromecast YouTube video from my Android app

但我不确定如何获取会话并使用 youtube 会话加载它。如果有人可以帮助我,我将不胜感激。

感谢您的帮助

【问题讨论】:

    标签: android youtube chromecast


    【解决方案1】:

    使用官方 SDK 无法做到这一点。有些人使用了 iframe 方法,但有不同的成功报告。

    【讨论】:

    • 那么自定义应用没有办法直接投射 youtube 视频吗?
    • 目前没有。
    • @AliNaddaf,难道还不能使用官方的 Chromecast 方法吗?需要从我们的应用中投射 Youtube 视频/播放列表。
    • 不,不可能。
    【解决方案2】:

    去年,我编写了一个 Android 应用,可以在连接到大屏幕电视的 VLC 上播放 YouTube/本地媒体。它玩得非常好,但我总是想用更优雅的东西替换 VLC 笔记本电脑。当 Chromecast 终于与官方 SDK 一起发布时,我真的很兴奋。我在尝试启动 YouTube 接收器应用程序播放 YouTube 视频时也遇到了同样的问题,所以我决定深入研究 VLC 是如何做到这一点的(开源很棒:))我发现 YouTube 视频 ID 只是一个JavaScript 页面,里面嵌入了很多东西。诀窍是从中提取正确的信息。不幸的是,VLC 脚本是用 Lua 编写的,所以我花了几周时间学习足够的 Lua 来浏览 Lua 脚本。你可以google youtube.lua 获取脚本的源代码。

    我用 Java 重写了脚本,并且能够修改 CastVideos Android 以轻松播放 YouTube 视频。请注意,由于 Chromecast 只能播放 mp4 视频容器格式,其他格式的视频可能无法播放。

    如果有人感兴趣,这是我的测试代码。您可以使用 CastHelloVideo-chrome 加载自定义媒体来测试 URL。

    享受,

    丹赫

        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`
    

    【讨论】:

    • 请注意,如果您在 Google Play 商店中发布使用 mp4 版 YouTube 视频的应用程序,该应用程序将被从 Play 商店中删除。换句话说,如果您使用 CastVideos 或编写类似将 mp4 版本的 YT 视频投射到 chromecast 设备的内容,您的应用将被删除。
    • 现在这令人失望,因为 Google 以开放性着称。应该有一种方法可以在媒体播放器本身内播放 youtube 视频。而且它已经存在于预览版 SDK 中,我不明白它为什么首先被删除。
    • 实际上,Google Play 中充斥着诸如 PVStar+ 之类的应用程序,其中包括抓取 MP4 网址的应用程序。对于我自己的使用 YouTube Android API 的应用程序,我选择遵守规则,但说应用程序将被删除是不正确的,尽管 Google Play 可能意识到这一点,但由于某种原因并没有强制执行他们自己的 TOS。我不建议抓取 MP4 网址,但同样,许多应用程序多年来一直使用它,并且实际上宣传背景音频而不受惩罚。这对那些遵守规则的人来说是不公平的,因为它给 MP4 抓取工具带来了明显不公平的竞争优势
    • 我也没有去抓取 mp4 网址,最终也没有集成 chromecast 支持。我仍然希望以真正的方式直接从应用程序投射 youtube 视频。
    • Google 不希望您抓取 mp4 网址,原因很明显:他们无法放置广告。事实上,它们可能具有安全功能,例如要求抓取和 mp4 下载来自同一个 IP 地址。因此,上述解决方案可能仅适用于您的 Android 设备与 Chromecast 位于同一 wifi 网络上的情况。看起来他们不比较用户代理标头。如果他们这样做了,那么你的爬虫代码也需要伪装成 Chromecast。
    【解决方案3】:

    我们可以使用当前的 chromecast 设备加入实时会话:使用下面的回调并让我知道您是否想要与下面相同的内容,然后我将发送剩余的部分代码。

    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");
                }
            }
        };
    

    【讨论】:

    • 嗨@Android Dev,如果我理解正确,您是说如果youtube 应用程序已经在投射某些内容,那么可以从应用程序中加入该特定会话??
    • 是的。当我们从 YouTube 播放视频并完成投射时,我们可以通过我们的应用程序在上述逻辑的帮助下加入当前会话。在此之前,我们需要调用一些回调才能继续。
    • 好的,但是应用程序将取决于用户应该首先从 youtube 投射的条件。难道也没有办法启动会话吗?
    • 是的。我们可以在最初投放 youtube 时加入同一个会话。
    【解决方案4】:

    我最近写了一个库来解决这个问题。 chromecast-sender。它是 android-youtube-player 库的扩展库,可轻松将视频从 Android 应用投射到 Google Cast 设备。

    接收器使用 YouTube IFrame 播放器 API。发送方和接收方通过自定义通道进行通信。

    【讨论】:

    • 我喜欢你的 youtube 播放器。但我无法让 chromecast 功能正常工作。我需要自定义接收器吗?如果需要,我该如何托管它?欢呼朋友@Pierfrancesco Soffritti
    • @markharrop 您可以阅读文档github.com/PierfrancescoSoffritti/… 中的所有内容。在上周,我收到了一些其他用户的报告,这些报告无法正常工作。下周我会调查的。如果你想保持更新,请在 GitHub 上打开一个问题。
    • 我设法让它正常工作,这是我没有正确上传自定义接收器的事实。如果您不介意,请再问一个问题?我想在连接到 chrome cast 时使用自定义 ui。所以我可以播放暂停快进 n 的东西。但它是用 kotlin 写的。核心应用中用jave编写的自定义ui和chromecast sender基本一样吗?
    • @markharrop 在 GitHub 上打开一个问题来提问,我在那里更容易回答!手机上控制 chromecast 的 UI 只是一堆视图。您只需要创建视图、侦听器,然后调用库的方法。
    猜你喜欢
    • 2013-08-09
    • 2019-01-27
    • 2014-04-28
    • 1970-01-01
    • 2020-10-10
    • 1970-01-01
    • 2013-10-07
    • 1970-01-01
    • 2017-05-13
    相关资源
    最近更新 更多