/* * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package androidx.media.widget; import android.content.Context; import android.media.MediaPlayer; import android.media.MediaPlayer.OnSubtitleDataListener; import android.media.SubtitleData; import android.net.Uri; import android.os.Bundle; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; import android.view.View; import android.view.ViewGroup.LayoutParams; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.media.subtitle.ClosedCaptionRenderer; import androidx.media.subtitle.SubtitleController; import androidx.media.subtitle.SubtitleTrack; import java.io.IOException; import java.util.ArrayList; import java.util.Map; /** * Base implementation of VideoView2. */ @RequiresApi(28) class VideoView2ImplApi28WithMp1 extends VideoView2ImplBaseWithMp1 { private static final String TAG = "VideoView2ImplApi28_1"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final int INVALID_TRACK_INDEX = -1; private ArrayList> mSubtitleTrackIndices; private SubtitleController mSubtitleController; // selected video/audio/subtitle track index as MediaPlayer returns private int mSelectedSubtitleTrackIndex; private SubtitleView mSubtitleView; private boolean mSubtitleEnabled; @Override public void initialize( VideoView2 instance, Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super.initialize(instance, context, attrs, defStyleAttr); mSelectedSubtitleTrackIndex = INVALID_TRACK_INDEX; LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); mSubtitleView = new SubtitleView(context); mSubtitleView.setLayoutParams(params); mSubtitleView.setBackgroundColor(0); mInstance.addView(mSubtitleView); mSubtitleEnabled = (attrs == null) || attrs.getAttributeBooleanValue( "http://schemas.android.com/apk/res/android", "enableSubtitle", false); } /** * Shows or hides closed caption or subtitles if there is any. * The first subtitle track will be chosen if there multiple subtitle tracks exist. * Default behavior of VideoView2 is not showing subtitle. * @param enable shows closed caption or subtitles if this value is true, or hides. */ @Override public void setSubtitleEnabled(boolean enable) { if (enable != mSubtitleEnabled) { selectOrDeselectSubtitle(enable); } mSubtitleEnabled = enable; } /** * Returns true if showing subtitle feature is enabled or returns false. * Although there is no subtitle track or closed caption, it can return true, if the feature * has been enabled by {@link #setSubtitleEnabled}. */ @Override public boolean isSubtitleEnabled() { return mSubtitleEnabled; } /////////////////////////////////////////////////// // Protected or private methods /////////////////////////////////////////////////// /** * Used in openVideo(). Setup MediaPlayer and related objects before calling prepare. */ @Override protected void setupMediaPlayer(Context context, Uri uri, Map headers) throws IOException { super.setupMediaPlayer(context, uri, headers); mSubtitleController = new SubtitleController(context); mSubtitleController.registerRenderer(new ClosedCaptionRenderer(context)); mSubtitleController.setAnchor((SubtitleController.Anchor) mSubtitleView); mMediaPlayer.setOnSubtitleDataListener(mSubtitleListener); } private void selectOrDeselectSubtitle(boolean select) { if (!isInPlaybackState()) { return; } if (select) { if (mSubtitleTrackIndices.size() > 0) { mSelectedSubtitleTrackIndex = mSubtitleTrackIndices.get(0).first; mSubtitleController.selectTrack(mSubtitleTrackIndices.get(0).second); mMediaPlayer.selectTrack(mSelectedSubtitleTrackIndex); mSubtitleView.setVisibility(View.VISIBLE); } } else { if (mSelectedSubtitleTrackIndex != INVALID_TRACK_INDEX) { mMediaPlayer.deselectTrack(mSelectedSubtitleTrackIndex); mSelectedSubtitleTrackIndex = INVALID_TRACK_INDEX; mSubtitleView.setVisibility(View.GONE); } } } @Override protected void extractTracks() { MediaPlayer.TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo(); mVideoTrackIndices = new ArrayList<>(); mAudioTrackIndices = new ArrayList<>(); mSubtitleTrackIndices = new ArrayList<>(); mSubtitleController.reset(); for (int i = 0; i < trackInfos.length; ++i) { int trackType = trackInfos[i].getTrackType(); if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_VIDEO) { mVideoTrackIndices.add(i); } else if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_AUDIO) { mAudioTrackIndices.add(i); } else if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) { SubtitleTrack track = mSubtitleController.addTrack(trackInfos[i].getFormat()); if (track != null) { mSubtitleTrackIndices.add(new Pair<>(i, track)); } } } // Select first tracks as default if (mVideoTrackIndices.size() > 0) { mSelectedVideoTrackIndex = 0; } if (mAudioTrackIndices.size() > 0) { mSelectedAudioTrackIndex = 0; } Bundle data = new Bundle(); data.putInt(MediaControlView2.KEY_VIDEO_TRACK_COUNT, mVideoTrackIndices.size()); data.putInt(MediaControlView2.KEY_AUDIO_TRACK_COUNT, mAudioTrackIndices.size()); data.putInt(MediaControlView2.KEY_SUBTITLE_TRACK_COUNT, mSubtitleTrackIndices.size()); if (mSubtitleTrackIndices.size() > 0) { selectOrDeselectSubtitle(mSubtitleEnabled); } mMediaSession.sendSessionEvent(MediaControlView2.EVENT_UPDATE_TRACK_STATUS, data); } private OnSubtitleDataListener mSubtitleListener = new OnSubtitleDataListener() { @Override public void onSubtitleData(MediaPlayer mp, SubtitleData data) { if (DEBUG) { Log.d(TAG, "onSubtitleData(): getTrackIndex: " + data.getTrackIndex() + ", getCurrentPosition: " + mp.getCurrentPosition() + ", getStartTimeUs(): " + data.getStartTimeUs() + ", diff: " + (data.getStartTimeUs() / 1000 - mp.getCurrentPosition()) + "ms, getDurationUs(): " + data.getDurationUs()); } final int index = data.getTrackIndex(); if (index != mSelectedSubtitleTrackIndex) { Log.d(TAG, "onSubtitleData(): getTrackIndex: " + data.getTrackIndex() + ", selected track index: " + mSelectedSubtitleTrackIndex); return; } for (Pair p : mSubtitleTrackIndices) { if (p.first == index) { SubtitleTrack track = p.second; track.onData(data); } } } }; @Override protected void doShowSubtitleCommand(Bundle args) { int subtitleIndex = args.getInt( MediaControlView2.KEY_SELECTED_SUBTITLE_INDEX, INVALID_TRACK_INDEX); if (subtitleIndex != INVALID_TRACK_INDEX) { int subtitleTrackIndex = mSubtitleTrackIndices.get(subtitleIndex).first; if (subtitleTrackIndex != mSelectedSubtitleTrackIndex) { mSelectedSubtitleTrackIndex = subtitleTrackIndex; mInstance.setSubtitleEnabled(true); } } } @Override protected void doHideSubtitleCommand() { mInstance.setSubtitleEnabled(false); } }