【发布时间】:2017-12-11 12:12:21
【问题描述】:
如何使用 Exoplayer、ExoMedia 或其他播放器启用并选择嵌入在 HLS 格式的 Vimeo 视频中的不同字幕? 在 iOS 中,同样的视频已经原生提供了字幕选项,但在 Android 中,我找不到实现它的方法。
【问题讨论】:
标签: android vimeo http-live-streaming caption exoplayer
如何使用 Exoplayer、ExoMedia 或其他播放器启用并选择嵌入在 HLS 格式的 Vimeo 视频中的不同字幕? 在 iOS 中,同样的视频已经原生提供了字幕选项,但在 Android 中,我找不到实现它的方法。
【问题讨论】:
标签: android vimeo http-live-streaming caption exoplayer
我在这里的答案看起来很像this one,所以你可能想先检查一下。
ExoPlayer 是您想要的 Android 库。让字幕显示是一项不平凡的任务,但该库的demo app 拥有让他们处理 HLS 视频所需的所有代码。更具体地说,PlayerActivity 类。您可以在演示应用程序中转到 HLS ->“Apple 16x9 基本流”,该视频有大量字幕(又名“文本轨道”)。
只是为了简化他们的代码,使其不依赖于帮助程序(这样您就可以在隐藏字幕上看到它的工作原理只是)我在下面编写/记录了他们的一些代码.
private static class ClosedCaptionManager {
ClosedCaptionManager(MappingTrackSelector mappingTrackSelector, SimpleExoPlayer player) {
this.player = player;
this.trackSelector = mappingTrackSelector;
}
SimpleExoPlayer player;
MappingTrackSelector trackSelector;
// These two could be fields OR passed around
int textTrackIndex;
TrackGroupArray trackGroups;
ArrayList<Pair<Integer, Integer>> pairTrackList = new ArrayList<>();
private boolean checkAndSetClosedCaptions() {
// This is the body of the logic for see if there are even video tracks
// It also does some field setting
MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
if (mappedTrackInfo == null) {
return false;
}
for (int i = 0; i < mappedTrackInfo.length; i++) {
trackGroups = mappedTrackInfo.getTrackGroups(i);
if (trackGroups.length != 0) {
switch (player.getRendererType(i)) {
case C.TRACK_TYPE_TEXT:
textTrackIndex = i;
return true;
}
}
}
return false;
}
private void buildTrackList() {
// This next part is actually about getting the list.
// Below you'd be building up items in a list. This just does
// views directly, but you could just have a list of track names (with indexes)
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
TrackGroup group = trackGroups.get(groupIndex);
for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
if (trackIndex == 0) {
// Beginning of a new set, the demo app adds a divider
}
//CheckedTextView trackView = ...; // The TextView to show in the list
// The below points to a util which extracts the quality from the TrackGroup
//trackView.setText(DemoUtil.buildTrackName(group.getFormat(trackIndex)));
Log.e("Thing", DemoUtil.buildTrackName(group.getFormat(trackIndex)));
pairTrackList.add(new Pair<>(groupIndex, trackIndex));
}
}
}
private void onTrackViewClick(Pair<Integer, Integer> trackPair) {
// Assuming you tagged the view with the groupIndex and trackIndex, you
// can build your override with that info.
Pair<Integer, Integer> tag = trackPair;
int groupIndex = tag.first;
int trackIndex = tag.second;
// This is the override you'd use for something that isn't adaptive.
// `override = new SelectionOverride(FIXED_FACTORY, groupIndex, trackIndex);`
// Otherwise they call their helper for adaptives (HLS/DASH), which roughly does:
int[] tracks = getTracksAdding(new MappingTrackSelector.SelectionOverride(
new FixedTrackSelection.Factory(), groupIndex, trackIndex),
trackIndex
);
TrackSelection.Factory factory = tracks.length == 1
? new FixedTrackSelection.Factory()
: new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
MappingTrackSelector.SelectionOverride override =
new MappingTrackSelector.SelectionOverride(factory, groupIndex, tracks);
// Then we actually set our override on the selector to switch the text track
trackSelector.setSelectionOverride(textTrackIndex, trackGroups, override);
}
private static int[] getTracksAdding(MappingTrackSelector.SelectionOverride override, int addedTrack) {
int[] tracks = override.tracks;
tracks = Arrays.copyOf(tracks, tracks.length + 1);
tracks[tracks.length - 1] = addedTrack;
return tracks;
}
}
然后,如果您将以下代码发布到他们的 initializePlayer() 方法的末尾,您将了解这些部分是如何组合在一起的。
final ClosedCaptionManager closedCaptionManager = new ClosedCaptionManager(trackSelector, player);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
boolean hasTracks = closedCaptionManager.checkAndSetClosedCaptions();
if (hasTracks) {
closedCaptionManager.buildTrackList();
closedCaptionManager.onTrackViewClick(closedCaptionManager.pairTrackList.get(4));
}
}
}, 2000);
上面的代码非常草率,但希望它至少能让你朝着正确的方向开始。我不建议使用任何所写的内容——它应该主要是为了了解不同的部分是如何组合在一起的。他们在演示应用程序中的功能要好一些,因为他们的代码对于不同的轨道选择类型非常可重用(因为您可以拥有视频、音频和文本轨道)。
【讨论】:
这很好用!
TrackGroupArray trackGroups = mappedTrackInfo.getTrackGroups(rendererIndex);
TrackSelectionArray currentTrackGroups = player.getCurrentTrackSelections();
TrackSelection currentTrackSelection = currentTrackGroups.get(rendererIndex);
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
TrackGroup group = trackGroups.get(groupIndex);
for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
Format trackFormat = group.getFormat(trackIndex);
if(currentTrackSelection!=null && currentTrackSelection.getSelectedFormat()==trackFormat){
//THIS ONE IS SELECTED
}
}
}
rendererIndex 是 0 用于视频,1 用于音频,2 用于字幕/文本
【讨论】:
mappedInfoTrack MappedTrackInfo mappedInfoTrack = trackSelector.getCurrentMappedTrackInfo()