【问题标题】:Trouble getting WebVTT captions/subtitles working on Chromecast无法在 Chromecast 上获取 WebVTT 字幕/字幕
【发布时间】:2014-08-17 20:46:17
【问题描述】:

我正在开发适用于 Android 的 Chromecast 应用,并试图让 WebVTT 字幕/字幕正常工作。

我按如下方式实例化一个 MediaTrack 对象:

            MediaTrack track = new MediaTrack.Builder(0, MediaTrack.TYPE_TEXT)
                .setContentId("http://www.example.com/example.vtt")
                .setSubtype(MediaTrack.SUBTYPE_SUBTITLES)
                .setContentType("ISO-8859-1")
                .setLanguage(Locale.ENGLISH)
                .setName("English")
                .build();

并将其添加到列表中。

我使用构建器实例化 MediaInfo,并使用 .setMediaTracks 方法引用轨道列表:

MediaInfo.Builder builder = new MediaInfo.Builder("http://www.example.com/example.m3u8")
                .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
                .setContentType("application/vnd.apple.mpegurl")
                .setMetadata(movieMetadata)
                .setMediaTracks(mediaTracks).build();

然后在我的应用中启动伴随库附带的 CastControllerActivity:

mCastManager.startCastControllerActivity(this, mSelectedMedia, position, autoPlay);

完成此操作后,我设置了活动媒体轨道(在我的情况下只有一个)和文本轨道样式:

long [] activeTracks = new long[1];
activeTracks[0] = 0l;
mCastManager.getRemoteMediaPlayer()
        .setActiveMediaTracks(mCastManager.getGoogleApiClient(), activeTracks)
        .setResultCallback(new MediaResultCallback());
mCastManager.getRemoteMediaPlayer().setTextTrackStyle(
        mCastManager.getGoogleApiClient(),
        TextTrackStyle.fromSystemSettings(getBaseContext()));

播放包含字幕的剪辑时,控制台会显示以下内容:

[775.223s] [cast.receiver.MediaManager] Too many track IDs cast_receiver.js:18
[775.232s] [cast.receiver.MediaManager] Invalid track IDs cast_receiver.js:18
[775.238s] [cast.receiver.MediaManager] Sending error: INVALID_REQUEST INVALID_PARAMS 

并且返回的 ResultCallback 中的状态代码是 SERVICE_MISSING - 即使我在开发时使用的两台 Android 4.4 设备上都安装了 Google Play Services 5.0.89。字幕使用 CORS 标头托管。

有什么想法或建议吗?

【问题讨论】:

    标签: android chromecast


    【解决方案1】:

    这是因为 CCL 尚未更新以处理轨道(这是即将推出的功能)。 CCL 从 MediaInfo 构建一个 Bundle,并在触发 CastControllerActivity 时使用该 bundle 来传递 MediaInfo(MediaInfo 不可打包,因此必须使用包装器,而 Bundle 是一种解决方案)。在现有的 CCL 中,当它创建一个包时,它不考虑轨道,因此该包没有轨道信息。结果,您实际上正在加载一个没有任何轨道信息的媒体(因为它发生在 CastControllerActivity 中),因此您会看到错误。

    在将 Tracks 支持添加到 CCL 之前,您可以执行以下操作。在 Utils 类中,使用 CCL 本身中的以下代码更新 fromMediaInfo()toMediaInfo() 两个方法:

    private static final String KEY_TRACK_ID = "track-id";
    private static final String KEY_TRACK_CONTENT_ID = "track-custom-id";
    private static final String KEY_TRACK_NAME = "track-name";
    private static final String KEY_TRACK_TYPE = "track-type";
    private static final String KEY_TRACK_SUBTYPE = "track-subtype";
    private static final String KEY_TRACK_LANGUAGE = "track-language";
    private static final String KEY_TRACK_CUSTOM_DATA = "track-custom-data";
    private static final String KEY_TRACKS_DATA = "track-data";
    
    public static Bundle fromMediaInfo(MediaInfo info) {
        if (null == info) {
            return null;
        }
    
        MediaMetadata md = info.getMetadata();
        Bundle wrapper = new Bundle();
        wrapper.putString(MediaMetadata.KEY_TITLE, md.getString(MediaMetadata.KEY_TITLE));
        wrapper.putString(MediaMetadata.KEY_SUBTITLE, md.getString(MediaMetadata.KEY_SUBTITLE));
        wrapper.putString(KEY_URL, info.getContentId());
        wrapper.putString(MediaMetadata.KEY_STUDIO, md.getString(MediaMetadata.KEY_STUDIO));
        wrapper.putString(KEY_CONTENT_TYPE, info.getContentType());
        wrapper.putInt(KEY_STREAM_TYPE, info.getStreamType());
        if (!md.getImages().isEmpty()) {
            ArrayList<String> urls = new ArrayList<String>();
            for (WebImage img : md.getImages()) {
                urls.add(img.getUrl().toString());
            }
            wrapper.putStringArrayList(KEY_IMAGES, urls);
        }
        JSONObject customData = info.getCustomData();
        if (null != customData) {
            wrapper.putString(KEY_CUSTOM_DATA, customData.toString());
        }
    
        if (null != info.getMediaTracks() && !info.getMediaTracks().isEmpty()) {
            try {
                JSONArray jsonArray = new JSONArray();
                for (MediaTrack mt : info.getMediaTracks()) {
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put(KEY_TRACK_NAME, mt.getName());
                    jsonObject.put(KEY_TRACK_CONTENT_ID, mt.getContentId());
                    jsonObject.put(KEY_TRACK_ID, mt.getId());
                    jsonObject.put(KEY_TRACK_LANGUAGE, mt.getLanguage());
                    jsonObject.put(KEY_TRACK_TYPE, mt.getType());
                    jsonObject.put(KEY_TRACK_SUBTYPE, mt.getSubtype());
                    if (null != mt.getCustomData()) {
                        jsonObject.put(KEY_TRACK_CUSTOM_DATA, mt.getCustomData().toString());
                    }
                    jsonArray.put(jsonObject);
                }
                wrapper.putString(KEY_TRACKS_DATA, jsonArray.toString());
            } catch (JSONException e) {
                LOGE(TAG, "fromMediaInfo(): Failed to convert Tracks data to json", e);
            }
        }
    
        return wrapper;
    }
    
    public static MediaInfo toMediaInfo(Bundle wrapper) {
        if (null == wrapper) {
            return null;
        }
    
        MediaMetadata metaData = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);
    
        metaData.putString(MediaMetadata.KEY_SUBTITLE,
                wrapper.getString(MediaMetadata.KEY_SUBTITLE));
        metaData.putString(MediaMetadata.KEY_TITLE, wrapper.getString(MediaMetadata.KEY_TITLE));
        metaData.putString(MediaMetadata.KEY_STUDIO, wrapper.getString(MediaMetadata.KEY_STUDIO));
        ArrayList<String> images = wrapper.getStringArrayList(KEY_IMAGES);
        if (null != images && !images.isEmpty()) {
            for (String url : images) {
                Uri uri = Uri.parse(url);
                metaData.addImage(new WebImage(uri));
            }
        }
        String customDataStr = wrapper.getString(KEY_CUSTOM_DATA);
        JSONObject customData = null;
        if (!TextUtils.isEmpty(customDataStr)) {
            try {
                customData = new JSONObject(customDataStr);
            } catch (JSONException e) {
                LOGE(TAG, "Failed to deserialize the custom data string: custom data= "
                        + customDataStr, e);
            }
        }
        List<MediaTrack> mediaTracks = null;
        if (null != wrapper.getString(KEY_TRACKS_DATA)) {
            try {
                JSONArray jsonArray = new JSONArray(wrapper.getString(KEY_TRACKS_DATA));
                mediaTracks = new ArrayList<MediaTrack>();
                if (null != jsonArray && jsonArray.length() > 0) {
                    for (int i = 0; i < jsonArray.length(); i++) {
                        JSONObject jsonObj = (JSONObject) jsonArray.get(i);
                        MediaTrack.Builder builder = new MediaTrack.Builder(
                                jsonObj.getLong(KEY_TRACK_ID), jsonObj.getInt(KEY_TRACK_TYPE));
                        if (jsonObj.has(KEY_TRACK_NAME)) {
                            builder.setName(jsonObj.getString(KEY_TRACK_NAME));
                        }
                        if (jsonObj.has(KEY_TRACK_SUBTYPE)) {
                            builder.setSubtype(jsonObj.getInt(KEY_TRACK_SUBTYPE));
                        }
                        if (jsonObj.has(KEY_TRACK_CONTENT_ID)) {
                            builder.setContentId(jsonObj.getString(KEY_TRACK_CONTENT_ID));
                        }
                        if (jsonObj.has(KEY_TRACK_LANGUAGE)) {
                            builder.setLanguage(jsonObj.getString(KEY_TRACK_LANGUAGE));
                        }
                        if (jsonObj.has(KEY_TRACKS_DATA)) {
                            builder.setCustomData(
                                    new JSONObject(jsonObj.getString(KEY_TRACKS_DATA)));
                        }
                        mediaTracks.add(builder.build());
                    }
                }
            } catch (JSONException e) {
                LOGE(TAG, "Failed to build media tracks from the wrapper bundle", e);
            }
        }
        return new MediaInfo.Builder(wrapper.getString(KEY_URL))
                .setStreamType(wrapper.getInt(KEY_STREAM_TYPE))
                .setContentType(wrapper.getString(KEY_CONTENT_TYPE))
                .setMetadata(metaData)
                .setCustomData(customData)
                .setMediaTracks(mediaTracks)
                .build();
    }
    

    这有望解决您的问题。

    【讨论】:

    • 使用回调的方法现在返回成功,并且在开发者控制台中没有错误。但是我没有看到任何文字。在我的下一条评论中查看包装包的内容。您对如何进行有任何建议吗?有什么方法可以在(样式化的)接收器中启用更多调试信息?
    • Bundle 的内容(注意 StackOverflow 去除了 URL 的 http 部分):[{movie-urls=example.com/example.m3u8, images=[example.com/1.jpg, example.com/2.jpg], com.google。 android.gms.cast.metadata.TITLE=标题, com.google.android.gms.cast.metadata.SUBTITLE=, content-type=application/vnd.apple.mpegurl, track-data=[{"track-name" :"English","track-custom-id":"example.com/…}], stream-type=1}]
    • 我编写了一个基于 CastClosedCaptioning-sample 的简单自定义接收器,看起来数据按应有的方式传递到接收器。请参阅以下内容:pastebin.com/KpvByRD4
    • 您可以通过在调试控制台中调用它来启用样式/默认接收器(与您的相同)中的调试: cast.receiver.logger.setLevelValue(cast.receiver.LoggerLevel.DEBUG);这应该会给我们更多关于出了什么问题的线索。
    • 那里的日志显示您已加载媒体,但在您设置活动轨道的位置周围没有看到任何线条。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-31
    • 1970-01-01
    相关资源
    最近更新 更多