diff --git a/app/src/main/java/com/github/tvbox/osc/player/ExoMediaPlayerFactory.java b/app/src/main/java/com/github/tvbox/osc/player/ExoMediaPlayerFactory.java new file mode 100644 index 00000000..a98ec8d7 --- /dev/null +++ b/app/src/main/java/com/github/tvbox/osc/player/ExoMediaPlayerFactory.java @@ -0,0 +1,17 @@ +package com.github.tvbox.osc.player; + +import android.content.Context; + +import xyz.doikki.videoplayer.player.PlayerFactory; + +public class ExoMediaPlayerFactory extends PlayerFactory { + + public static ExoMediaPlayerFactory create() { + return new ExoMediaPlayerFactory(); + } + + @Override + public ExoPlayer createPlayer(Context context) { + return new ExoPlayer(context); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/tvbox/osc/player/ExoPlayer.java b/app/src/main/java/com/github/tvbox/osc/player/ExoPlayer.java new file mode 100644 index 00000000..c7e7d6ba --- /dev/null +++ b/app/src/main/java/com/github/tvbox/osc/player/ExoPlayer.java @@ -0,0 +1,174 @@ +package com.github.tvbox.osc.player; + +import android.content.Context; +import android.util.Pair; + +import com.github.tvbox.osc.util.LOG; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.source.TrackGroup; +import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; +import com.google.android.exoplayer2.trackselection.MappingTrackSelector; + +import xyz.doikki.videoplayer.exo.ExoMediaPlayer; + +import com.google.android.exoplayer2.C; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ExoPlayer extends ExoMediaPlayer { + + public ExoPlayer(Context context) { + super(context); + } + // 3. 获取所有轨道信息 + public TrackInfo getTrackInfo() { + TrackInfo data = new TrackInfo(); + MappingTrackSelector.MappedTrackInfo mappedInfo = getMappingTrackSelector().getCurrentMappedTrackInfo(); + if (mappedInfo == null) return data; + DefaultTrackSelector.Parameters params = ((DefaultTrackSelector) getMappingTrackSelector()).getParameters(); + for (int rendererIndex = 0; rendererIndex < mappedInfo.getRendererCount(); rendererIndex++) { + int type = mappedInfo.getRendererType(rendererIndex); + TrackGroupArray groups = mappedInfo.getTrackGroups(rendererIndex); + DefaultTrackSelector.SelectionOverride override = params.getSelectionOverride(rendererIndex, groups); + boolean hasSelected = false; + for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) { + TrackGroup group = groups.get(groupIndex); + for (int trackIndex = 0; trackIndex < group.length; trackIndex++) { + Format fmt = group.getFormat(trackIndex); + TrackInfoBean bean = new TrackInfoBean(); + bean.language = getLanguage(fmt); + bean.name = getName(fmt); + bean.groupIndex = groupIndex; + bean.index = trackIndex; + boolean selected = false; + if (override != null) { + if(override.groupIndex == groupIndex){ + for (int t : override.tracks) { + if (t == trackIndex) { + selected = true; + hasSelected = true; + break; + } + } + } + }else if (type == C.TRACK_TYPE_AUDIO && !hasSelected) { + selected = true; + hasSelected = true; + } + bean.selected = selected; + if (type == C.TRACK_TYPE_AUDIO) { + data.addAudio(bean); + } else if (type == C.TRACK_TYPE_TEXT) { + data.addSubtitle(bean); + } + } + } + } + return data; + } + + protected MappingTrackSelector getMappingTrackSelector() { + if (mTrackSelector instanceof MappingTrackSelector) { + return (MappingTrackSelector) mTrackSelector; + } + throw new IllegalStateException("trackSelector 必须是 MappingTrackSelector 类型"); + } + + /** + * 设置当前播放的音轨 + * @param groupIndex 音轨组的索引 + * @param trackIndex 音轨在组内的索引 + */ + public void setTrack(int groupIndex, int trackIndex) { + try { + DefaultTrackSelector trackSelector = (DefaultTrackSelector) getMappingTrackSelector(); + MappingTrackSelector.MappedTrackInfo mappedInfo = trackSelector.getCurrentMappedTrackInfo(); + if (mappedInfo == null) { + LOG.i("echo-setTrack: MappedTrackInfo is null"); + return; + } + int audioRendererIndex = findAudioRendererIndex(mappedInfo); + if (audioRendererIndex == C.INDEX_UNSET) { + LOG.i("echo-setTrack: No audio renderer found"); + return; + } + TrackGroupArray audioGroups = mappedInfo.getTrackGroups(audioRendererIndex); + if (!isTrackIndexValid(audioGroups, groupIndex, trackIndex)) { + LOG.i("echo-setTrack: Invalid track index - group:" + groupIndex + ", track:" + trackIndex); + return; + } + DefaultTrackSelector.SelectionOverride newOverride = new DefaultTrackSelector.SelectionOverride(groupIndex, trackIndex); + DefaultTrackSelector.ParametersBuilder builder = trackSelector.buildUponParameters(); + builder.clearSelectionOverrides(audioRendererIndex); + builder.setSelectionOverride(audioRendererIndex, audioGroups, newOverride); + trackSelector.setParameters(builder.build()); + } catch (Exception e) { + LOG.i("echo-setTrack error: " + e.getMessage()); + } + } + + /** + * 查找音频渲染器索引 + */ + private int findAudioRendererIndex(MappingTrackSelector.MappedTrackInfo mappedInfo) { + for (int i = 0; i < mappedInfo.getRendererCount(); i++) { + if (mappedInfo.getRendererType(i) == C.TRACK_TYPE_AUDIO) { + return i; + } + } + return C.INDEX_UNSET; + } + + /** + * 验证音轨索引是否有效 + */ + private boolean isTrackIndexValid(TrackGroupArray groups, int groupIndex, int trackIndex) { + if (groupIndex < 0 || groupIndex >= groups.length) { + return false; + } + + TrackGroup group = groups.get(groupIndex); + return trackIndex >= 0 && trackIndex < group.length; + } + + private static final Map LANG_MAP = new HashMap<>(); + static { + LANG_MAP.put("zh", "中文"); + LANG_MAP.put("zh-cn", "中文"); + LANG_MAP.put("en", "英语"); + LANG_MAP.put("en-us", "英语"); + } + + private String getLanguage(Format fmt){ + String lang = fmt.language; + if (lang == null || lang.isEmpty() || "und".equalsIgnoreCase(lang)) { + return "未知"; + } + String name = LANG_MAP.get(lang.toLowerCase()); + return name != null ? name : lang; + } + + private String getName(Format fmt){ + String channelLabel; + if (fmt.channelCount <= 0) { + channelLabel = ""; + } else if (fmt.channelCount == 1) { + channelLabel = "单声道"; + } else if (fmt.channelCount == 2) { + channelLabel = "立体声"; + } else { + channelLabel = fmt.channelCount + " 声道"; + } + String codec = ""; + if (fmt.sampleMimeType != null) { + String mime = fmt.sampleMimeType.substring(fmt.sampleMimeType.indexOf('/') + 1); + codec = mime.toUpperCase(); + } + return String.join(", ", channelLabel, codec); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/tvbox/osc/player/TrackInfoBean.java b/app/src/main/java/com/github/tvbox/osc/player/TrackInfoBean.java index 8a23262f..8521d977 100644 --- a/app/src/main/java/com/github/tvbox/osc/player/TrackInfoBean.java +++ b/app/src/main/java/com/github/tvbox/osc/player/TrackInfoBean.java @@ -3,6 +3,7 @@ package com.github.tvbox.osc.player; public class TrackInfoBean { public String name; public String language; + public int groupIndex; public int index; public boolean selected; } diff --git a/app/src/main/java/com/github/tvbox/osc/player/controller/VodController.java b/app/src/main/java/com/github/tvbox/osc/player/controller/VodController.java index d131171a..6862cfaf 100644 --- a/app/src/main/java/com/github/tvbox/osc/player/controller/VodController.java +++ b/app/src/main/java/com/github/tvbox/osc/player/controller/VodController.java @@ -764,7 +764,7 @@ public class VodController extends BaseController { mPlayerSpeedBtn.setText("x" + mPlayerConfig.getDouble("sp")); mPlayerTimeStartBtn.setText(stringForTime(mPlayerConfig.getInt("st") * 1000)); mPlayerTimeSkipBtn.setText(stringForTime(mPlayerConfig.getInt("et") * 1000)); - mAudioTrackBtn.setVisibility((playerType == 1) ? VISIBLE : GONE); + mAudioTrackBtn.setVisibility((playerType == 1 || playerType == 2) ? VISIBLE : GONE); } catch (JSONException e) { e.printStackTrace(); } diff --git a/app/src/main/java/com/github/tvbox/osc/ui/activity/PlayActivity.java b/app/src/main/java/com/github/tvbox/osc/ui/activity/PlayActivity.java index 87f536ce..8470d156 100644 --- a/app/src/main/java/com/github/tvbox/osc/ui/activity/PlayActivity.java +++ b/app/src/main/java/com/github/tvbox/osc/ui/activity/PlayActivity.java @@ -50,6 +50,7 @@ import com.github.tvbox.osc.bean.Subtitle; import com.github.tvbox.osc.bean.VodInfo; import com.github.tvbox.osc.cache.CacheManager; import com.github.tvbox.osc.event.RefreshEvent; +import com.github.tvbox.osc.player.ExoPlayer; import com.github.tvbox.osc.player.IjkMediaPlayer; import com.github.tvbox.osc.player.MyVideoView; import com.github.tvbox.osc.player.TrackInfo; @@ -356,11 +357,13 @@ public class PlayActivity extends BaseActivity { void selectMyAudioTrack() { AbstractPlayer mediaPlayer = mVideoView.getMediaPlayer(); - if (!(mediaPlayer instanceof IjkMediaPlayer)) { - return; - } TrackInfo trackInfo = null; - trackInfo = ((IjkMediaPlayer) mediaPlayer).getTrackInfo(); + if (mediaPlayer instanceof IjkMediaPlayer) { + trackInfo = ((IjkMediaPlayer)mediaPlayer).getTrackInfo(); + } + if (mediaPlayer instanceof ExoPlayer) { + trackInfo = ((ExoPlayer)mediaPlayer).getTrackInfo(); + } if (trackInfo == null) { Toast.makeText(mContext, "没有音轨", Toast.LENGTH_SHORT).show(); return; @@ -378,14 +381,15 @@ public class PlayActivity extends BaseActivity { } mediaPlayer.pause(); long progress = mediaPlayer.getCurrentPosition();//保存当前进度,ijk 切换轨道 会有快进几秒 - ((IjkMediaPlayer) mediaPlayer).setTrack(value.index); + if (mediaPlayer instanceof IjkMediaPlayer)((IjkMediaPlayer)mediaPlayer).setTrack(value.index); + if (mediaPlayer instanceof ExoPlayer)((ExoPlayer)mediaPlayer).setTrack(value.groupIndex,value.index); new Handler().postDelayed(new Runnable() { @Override public void run() { - mediaPlayer.seekTo(progress); + if(mediaPlayer instanceof IjkMediaPlayer)mediaPlayer.seekTo(progress); mediaPlayer.start(); } - }, 800); + }, 200); dialog.dismiss(); } catch (Exception e) { LOG.e("切换音轨出错"); @@ -394,7 +398,7 @@ public class PlayActivity extends BaseActivity { @Override public String getDisplay(TrackInfoBean val) { - return val.index + " . " + val.language + " : " + val.name; + return val.groupIndex + val.index + " . " + val.language + " : " + val.name; } }, new DiffUtil.ItemCallback() { @Override diff --git a/app/src/main/java/com/github/tvbox/osc/ui/fragment/PlayFragment.java b/app/src/main/java/com/github/tvbox/osc/ui/fragment/PlayFragment.java index d5cb1820..217d338f 100644 --- a/app/src/main/java/com/github/tvbox/osc/ui/fragment/PlayFragment.java +++ b/app/src/main/java/com/github/tvbox/osc/ui/fragment/PlayFragment.java @@ -50,6 +50,7 @@ import com.github.tvbox.osc.bean.Subtitle; import com.github.tvbox.osc.bean.VodInfo; import com.github.tvbox.osc.cache.CacheManager; import com.github.tvbox.osc.event.RefreshEvent; +import com.github.tvbox.osc.player.ExoPlayer; import com.github.tvbox.osc.player.IjkMediaPlayer; import com.github.tvbox.osc.player.MyVideoView; import com.github.tvbox.osc.player.TrackInfo; @@ -374,11 +375,13 @@ public class PlayFragment extends BaseLazyFragment { void selectMyAudioTrack() { AbstractPlayer mediaPlayer = mVideoView.getMediaPlayer(); - if (!(mediaPlayer instanceof IjkMediaPlayer)) { - return; - } TrackInfo trackInfo = null; - trackInfo = ((IjkMediaPlayer)mediaPlayer).getTrackInfo(); + if (mediaPlayer instanceof IjkMediaPlayer) { + trackInfo = ((IjkMediaPlayer)mediaPlayer).getTrackInfo(); + } + if (mediaPlayer instanceof ExoPlayer) { + trackInfo = ((ExoPlayer)mediaPlayer).getTrackInfo(); + } if (trackInfo == null) { Toast.makeText(mContext, "没有音轨", Toast.LENGTH_SHORT).show(); return; @@ -396,14 +399,15 @@ public class PlayFragment extends BaseLazyFragment { } mediaPlayer.pause(); long progress = mediaPlayer.getCurrentPosition();//保存当前进度,ijk 切换轨道 会有快进几秒 - ((IjkMediaPlayer)mediaPlayer).setTrack(value.index); + if (mediaPlayer instanceof IjkMediaPlayer)((IjkMediaPlayer)mediaPlayer).setTrack(value.index); + if (mediaPlayer instanceof ExoPlayer)((ExoPlayer)mediaPlayer).setTrack(value.groupIndex,value.index); new Handler().postDelayed(new Runnable() { @Override public void run() { - mediaPlayer.seekTo(progress); + if(mediaPlayer instanceof IjkMediaPlayer)mediaPlayer.seekTo(progress); mediaPlayer.start(); } - }, 800); + }, 200); dialog.dismiss(); } catch (Exception e) { LOG.e("切换音轨出错"); @@ -412,7 +416,7 @@ public class PlayFragment extends BaseLazyFragment { @Override public String getDisplay(TrackInfoBean val) { - return val.index + " . " + val.language + " : " + val.name; + return val.groupIndex + val.index + " . " + val.language + " : " + val.name; } }, new DiffUtil.ItemCallback() { @Override diff --git a/app/src/main/java/com/github/tvbox/osc/util/PlayerHelper.java b/app/src/main/java/com/github/tvbox/osc/util/PlayerHelper.java index a7c2b552..4438c29a 100644 --- a/app/src/main/java/com/github/tvbox/osc/util/PlayerHelper.java +++ b/app/src/main/java/com/github/tvbox/osc/util/PlayerHelper.java @@ -5,6 +5,7 @@ import android.content.Context; import com.github.tvbox.osc.api.ApiConfig; import com.github.tvbox.osc.bean.IJKCode; +import com.github.tvbox.osc.player.ExoMediaPlayerFactory; import com.github.tvbox.osc.player.IjkMediaPlayer; import com.github.tvbox.osc.player.render.SurfaceRenderViewFactory; import com.github.tvbox.osc.player.thirdparty.Kodi; @@ -22,7 +23,6 @@ import java.util.ArrayList; import java.util.HashMap; import tv.danmaku.ijk.media.player.IjkLibLoader; -import xyz.doikki.videoplayer.exo.ExoMediaPlayerFactory; import xyz.doikki.videoplayer.player.AndroidMediaPlayerFactory; import xyz.doikki.videoplayer.player.PlayerFactory; import xyz.doikki.videoplayer.player.VideoView; diff --git a/player/src/main/java/xyz/doikki/videoplayer/exo/ExoMediaPlayer.java b/player/src/main/java/xyz/doikki/videoplayer/exo/ExoMediaPlayer.java index cc99e174..076b3685 100644 --- a/player/src/main/java/xyz/doikki/videoplayer/exo/ExoMediaPlayer.java +++ b/player/src/main/java/xyz/doikki/videoplayer/exo/ExoMediaPlayer.java @@ -51,7 +51,7 @@ public class ExoMediaPlayer extends AbstractPlayer implements Player.Listener { private LoadControl mLoadControl; private RenderersFactory mRenderersFactory; - private TrackSelector mTrackSelector; + protected TrackSelector mTrackSelector; public ExoMediaPlayer(Context context) { mAppContext = context.getApplicationContext();