OnTimedTextMethod медиаплеера не вызывается
Я пишу медиаплеер для Android TV. Я загрузил .srt
файл субтитров из папки сырых ресурсов и добавил его с помощью addTimedTextSource
как показано ниже, но onTimedText
метод не вызывается.
Я использую библиотеку Leanback v23.0.1 на Android 7.1.1.
Ниже мой код:
public abstract class MediaPlayerGlue extends PlaybackControlGlue
implements OnItemViewSelectedListener,MediaPlayer.OnTimedTextListener {
public static final int FAST_FORWARD_REWIND_STEP = 10 * 1000; // in ms
public static final int FAST_FORWARD_REWIND_REPEAT_DELAY = 200; // in ms
private static final String TAG = "MediaPlayerGlue";
protected final PlaybackControlsRow.ThumbsDownAction mThumbsDownAction;
protected final PlaybackControlsRow.ThumbsUpAction mThumbsUpAction;
private final Context mContext;
private final MediaPlayer mPlayer = new MediaPlayer();
private final PlaybackControlsRow.RepeatAction mRepeatAction;
private final PlaybackControlsRow.ShuffleAction mShuffleAction;
private PlaybackControlsRow mControlsRow;
private Runnable mRunnable;
private Handler mHandler = new Handler();
private boolean mPaused = false;
private boolean mInitialized = false; // true when the MediaPlayer is prepared/initialized
private OnMediaFileFinishedPlayingListener mMediaFileFinishedPlayingListener;
private OnEveryHalfMinute onEveryHalfMinute;
private OnShowSubtitle onShowSubtitle;
private Action mSelectedAction; // the action which is currently selected by the user
private long mLastKeyDownEvent = 0L; // timestamp when the last DPAD_CENTER KEY_DOWN occurred
private MetaData mMetaData;
private Uri mMediaSourceUri = null;
private String mMediaSourcePath = null;
private String mMediaSubTitlePath = null;
private String sub_sub = "1\n" +
"00:00:00,220 --> 00:00:01,215\n" +
"<b>COMPAÑÍA DE\n" +
"PUBLICIDAD EBBING</b>";
public MediaPlayerGlue(Context context, PlaybackOverlayFragment fragment) {
super(context, fragment, new int[]{1});
mContext = context;
// Instantiate secondary actions
mShuffleAction = new PlaybackControlsRow.ShuffleAction(mContext);
mRepeatAction = new PlaybackControlsRow.RepeatAction(mContext);
mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(mContext);
mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(mContext);
mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
// Setup controls and notify UI about change.
setFadingEnabled(false);
onStateChanged();
// Register selected listener such that we know what action the user currently has focused.
fragment.setOnItemViewSelectedListener(this);
}
/**
* Will reset the {@link MediaPlayer} and the glue such that a new file can
be played. You are
* not required to call this method before playing the first file. However
you have to call it
* before playing a second one.
*/
public void reset() {
mPaused = mInitialized = false;
mPlayer.reset();
}
public void
setOnMediaFileFinishedPlayingListener(OnMediaFileFinishedPlayingListener
listener) {
mMediaFileFinishedPlayingListener = listener;
}
public void setOnHalfMinute(OnEveryHalfMinute onEveryHalfMinute) {
this.onEveryHalfMinute = onEveryHalfMinute;
}
public void setOnShowSubtitle(OnShowSubtitle onShowSubtitle) {
this.onShowSubtitle = onShowSubtitle;
}
/**
* Override this method in case you need to add different secondary actions.
*
* @param secondaryActionsAdapter The adapter you need to add the {@link Action}s to.
*/
protected void addSecondaryActions(ArrayObjectAdapter secondaryActionsAdapter) {
secondaryActionsAdapter.add(mShuffleAction);
secondaryActionsAdapter.add(mRepeatAction);
secondaryActionsAdapter.add(mThumbsDownAction);
secondaryActionsAdapter.add(mThumbsUpAction);
}
/**
* @see MediaPlayer#setDisplay(SurfaceHolder)
*/
public void setDisplay(SurfaceHolder surfaceHolder) {
mPlayer.setDisplay(surfaceHolder);
}
/**
* Use this method to setup the {@link PlaybackControlsRowPresenter}. It'll be called
* <u>after</u> the {@link PlaybackControlsRowPresenter} has been created and the primary and
* secondary actions have been added.
*
* @param presenter The PlaybackControlsRowPresenter used to display the controls.
*/
public void setupControlsRowPresenter(PlaybackControlsRowPresenter presenter) {
// TODO: hahnr@ move into resources
presenter.setProgressColor(Color.BLUE);
presenter.setBackgroundColor(Color.BLACK);
}
@Override
public PlaybackControlsRowPresenter createControlsRowAndPresenter() {
PlaybackControlsRowPresenter presenter = super.createControlsRowAndPresenter();
mControlsRow = getControlsRow();
// Add secondary actions and change the control row color.
ArrayObjectAdapter secondaryActions = new ArrayObjectAdapter(
new ControlButtonPresenterSelector());
mControlsRow.setSecondaryActionsAdapter(secondaryActions);
addSecondaryActions(secondaryActions);
setupControlsRowPresenter(presenter);
return presenter;
}
int recentRequestTime = 0;
@Override
public void enableProgressUpdating(final boolean enabled) {
if (!enabled) {
if (mRunnable != null) mHandler.removeCallbacks(mRunnable);
return;
}
mRunnable = new Runnable() {
@Override
public void run() {
updateProgress();
Log.d(TAG, "enableProgressUpdating(boolean)");
if (onEveryHalfMinute != null) {
int currentTime = getCurrentPosition();
int currentTimeInSec = currentTime / 1000;
if (currentTimeInSec != 0) {
if (currentTimeInSec % 15 == 0) {
// if (recentRequestTime != currentTime) {
onEveryHalfMinute.onHalfMinute(currentTimeInSec);
recentRequestTime = currentTimeInSec / 15;
// }
}
}
}
mHandler.postDelayed(this, 1000);
}
};
mHandler.postDelayed(mRunnable, 1000);
}
@Override
public void onActionClicked(Action action) {
// If either 'Shuffle' or 'Repeat' has been clicked we need to make sure the acitons index
// is incremented and the UI updated such that we can display the new state.
super.onActionClicked(action);
if (action instanceof PlaybackControlsRow.ShuffleAction) {
mShuffleAction.nextIndex();
} else if (action instanceof PlaybackControlsRow.RepeatAction) {
mRepeatAction.nextIndex();
} else if (action instanceof PlaybackControlsRow.ThumbsUpAction) {
if (mThumbsUpAction.getIndex() == PlaybackControlsRow.ThumbsAction.SOLID) {
mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
} else {
mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.SOLID);
mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
}
} else if (action instanceof PlaybackControlsRow.ThumbsDownAction) {
if (mThumbsDownAction.getIndex() == PlaybackControlsRow.ThumbsAction.SOLID) {
mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
} else {
mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.SOLID);
mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
}
}
onMetadataChanged();
}
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// This method is overridden in order to make implement fast forwarding and rewinding when
// the user keeps the corresponding action pressed.
// We only consume DPAD_CENTER Action_DOWN events on the Fast-Forward and Rewind action and
// only if it has not been pressed in the last X milliseconds.
boolean consume = mSelectedAction instanceof PlaybackControlsRow.RewindAction;
consume = consume || mSelectedAction instanceof PlaybackControlsRow.FastForwardAction;
consume = consume && mInitialized;
consume = consume && event.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER;
consume = consume && event.getAction() == KeyEvent.ACTION_DOWN;
consume = consume && System
.currentTimeMillis() - mLastKeyDownEvent > FAST_FORWARD_REWIND_REPEAT_DELAY;
if (consume) {
mLastKeyDownEvent = System.currentTimeMillis();
int newPosition = getCurrentPosition() + FAST_FORWARD_REWIND_STEP;
if (mSelectedAction instanceof PlaybackControlsRow.RewindAction) {
newPosition = getCurrentPosition() - FAST_FORWARD_REWIND_STEP;
}
// Make sure the new calculated duration is in the range 0 >= X >= MediaDuration
if (newPosition < 0) newPosition = 0;
if (newPosition > getMediaDuration()) newPosition = getMediaDuration();
seekTo(newPosition);
return true;
}
return super.onKey(v, keyCode, event);
}
@Override
public boolean hasValidMedia() {
return mMetaData != null;
}
@Override
public boolean isMediaPlaying() {
return mPlayer.isPlaying();
}
@Override
public CharSequence getMediaTitle() {
return hasValidMedia() ? mMetaData.getTitle() : "N/a";
}
@Override
public CharSequence getMediaSubtitle() {
return hasValidMedia() ? mMetaData.getArtist() : "N/a";
}
@Override
public int getMediaDuration() {
return mInitialized ? mPlayer.getDuration() : 0;
}
@Override
public Drawable getMediaArt() {
return hasValidMedia() ? mMetaData.getCover() : null;
}
@Override
public long getSupportedActions() {
return PlaybackControlGlue.ACTION_PLAY_PAUSE |
PlaybackControlGlue.ACTION_FAST_FORWARD |
PlaybackControlGlue.ACTION_REWIND;
}
@Override
public int getCurrentSpeedId() {
// 0 = Pause, 1 = Normal Playback Speed
return mPlayer.isPlaying() ? 1 : 0;
}
@Override
public int getCurrentPosition() {
return mInitialized ? mPlayer.getCurrentPosition() : 0;
}
@Override
protected void startPlayback(int speed) throws IllegalStateException {
if (mPaused) {
mPlayer.start();
} else if (!isMediaPlaying()) {
reset();
try {
if (mMediaSourceUri != null) {
mPlayer.setDataSource(getContext(), mMediaSourceUri);
} else {
mPlayer.setDataSource(mMediaSourcePath);
}
} catch (Exception e) {
UtilMethods.LogMethod("downloadSubtitle123_ooooo", String.valueOf(e));
//throw new RuntimeException(e);
}
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
recentRequestTime = 0;
mInitialized = true;
mPaused = false;
if (mMediaSubTitlePath != null) {
// String mimeType = UtilMethods.getMimeType(mMediaSubTitlePath);
UtilMethods.LogMethod("downloadSubtitle123_mMediaSubTitlePath444",getSubtitleFile(R.raw.sub));
try {
mPlayer.addTimedTextSource(getSubtitleFile(R.raw.sub),
MediaPlayer.MEDIA_MIMETYPE_TEXT_SUBRIP);
int textTrackIndex = findTrackIndexFor(
MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT, mPlayer.getTrackInfo());
if (textTrackIndex >= 0) {
UtilMethods.LogMethod("downloadSubtitle123_trackInfo", "iffff");
mPlayer.selectTrack(textTrackIndex);
} else {
UtilMethods.LogMethod("downloadSubtitle123_trackInfo", "elseee");
}
} catch (IOException e) {
UtilMethods.LogMethod("downloadSubtitle123_aaaaa_qweerrwqq", String.valueOf(e));
e.printStackTrace();
}
}
//mPlayer.setOnTimedTextListener(MediaPlayerGlue.this);
mPlayer.start();
onMetadataChanged();
onStateChanged();
updateProgress();
}
});
mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
if (mInitialized && mMediaFileFinishedPlayingListener != null)
mMediaFileFinishedPlayingListener.onMediaFileFinishedPlaying(mMetaData);
}
});
mPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
mControlsRow.setBufferedProgress((int) (mp.getDuration() * (percent / 100f)));
}
});
mPlayer.setOnTimedTextListener(new MediaPlayer.OnTimedTextListener() {
@Override
public void onTimedText(MediaPlayer mp, TimedText text) {
UtilMethods.LogMethod("downloadSubtitle123_onTimedText_2222222", "22222222");
if(onShowSubtitle!=null){
onShowSubtitle.showSubtitle(mp,text);
}
}
});
mPlayer.prepareAsync();
onStateChanged();
}
}
@Override
protected void pausePlayback() {
if (mPlayer.isPlaying()) {
mPlayer.pause();
mPaused = true;
}
}
@Override
protected void skipToNext() {
// Not supported.
}
@Override
protected void skipToPrevious() {
// Not supported.
}
/**
* Called whenever the user presses fast-forward/rewind or when the user keeps the corresponding
* action pressed.
*
* @param newPosition The new position of the media track in milliseconds.
*/
protected void seekTo(int newPosition) {
mPlayer.seekTo(newPosition);
}
/**
* @see MediaPlayer#setDataSource(Context, Uri)
*/
public void setMediaSource(Uri uri) {
mMediaSourceUri = uri;
}
/**
* @see MediaPlayer#setDataSource(String)
*/
public void setMediaSource(String path) {
mMediaSourcePath = path;
}
public void setmMediaSubTitlePath(String path) {
mMediaSubTitlePath = path;
}
/**
* Call to <code>startPlayback(1)</code>.
*
* @throws IllegalStateException See {@link MediaPlayer} for further information about it's
* different states when setting a data source and preparing it to be played.
*/
public void startPlayback() throws IllegalStateException {
startPlayback(1);
}
/**
* @return Returns <code>true</code> iff 'Shuffle' is <code>ON</code>.
*/
public boolean useShuffle() {
return mShuffleAction.getIndex() == PlaybackControlsRow.ShuffleAction.ON;
}
/**
* @return Returns <code>true</code> iff 'Repeat-One' is <code>ON</code>.
*/
public boolean repeatOne() {
return mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.ONE;
}
/**
* @return Returns <code>true</code> iff 'Repeat-All' is <code>ON</code>.
*/
public boolean repeatAll() {
return mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.ALL;
}
public void setMetaData(MetaData metaData) {
mMetaData = metaData;
onMetadataChanged();
}
/**
* This is a listener implementation for the {@link OnItemViewSelectedListener} of the {@link
* PlaybackOverlayFragment}. This implementation is required in order to detect KEY_DOWN events
* on the {@link PlaybackControlsRow.FastForwardAction} and
* {@link PlaybackControlsRow.RewindAction}. Thus you should
* <u>NOT</u> set another {@link OnItemViewSelectedListener} on your {@link
* PlaybackOverlayFragment}. Instead, override this method and call its super (this)
* implementation.
*
* @see OnItemViewSelectedListener#( Presenter.ViewHolder, Object,
* RowPresenter.ViewHolder, Row )
*/
@Override
public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
if (item instanceof Action) {
mSelectedAction = (Action) item;
} else {
mSelectedAction = null;
}
}
/**
* A listener which will be called whenever a track is finished playing.
*/
public interface OnMediaFileFinishedPlayingListener {
/**
* Called when a track is finished playing.
*
* @param metaData The track's {@link MetaData} which just finished playing.
*/
void onMediaFileFinishedPlaying(MetaData metaData);
}
public interface OnEveryHalfMinute {
void onHalfMinute(int currentTime);
}
public interface OnShowSubtitle {
void showSubtitle(MediaPlayer mp, TimedText text);
}
/**
* Holds the meta data such as track title, artist and cover art. It'll be used by the {@link
* MediaPlayerGlue}.
*/
public static class MetaData {
private String mTitle;
private String mArtist;
private Drawable mCover;
public String getTitle() {
return mTitle;
}
public void setTitle(String title) {
this.mTitle = title;
}
public String getArtist() {
return mArtist;
}
public void setArtist(String artist) {
this.mArtist = artist;
}
public Drawable getCover() {
return mCover;
}
public void setCover(Drawable cover) {
this.mCover = cover;
}
}
private int findTrackIndexFor(int mediaTrackType, MediaPlayer.TrackInfo[] trackInfo) {
int index = -1;
UtilMethods.LogMethod("downloadSubtitle123_length", String.valueOf(trackInfo.length));
for (int i = 0; i < trackInfo.length; i++) {
UtilMethods.LogMethod("downloadSubtitle123_type", String.valueOf(trackInfo[i].getTrackType()));
if (trackInfo[i].getTrackType() == mediaTrackType) {
return i;
}
}
return index;
}
private String getSubtitleFile(int resId) {
String fileName = mContext.getResources().getResourceEntryName(resId);
File subtitleFile = mContext.getFileStreamPath(fileName);
Log.d(TAG, "Subtitle does not exists, copy it from res/raw");
// Copy the file from the res/raw folder to your app folder on the
// device
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = mContext.getResources().openRawResource(resId);
outputStream = new FileOutputStream(subtitleFile, false);
copyFile(inputStream, outputStream);
return subtitleFile.getAbsolutePath();
} catch (Exception e) {
e.printStackTrace();
} finally {
closeStreams(inputStream, outputStream);
}
return "";
}
private void copyFile(InputStream inputStream, OutputStream outputStream)
throws IOException {
final int BUFFER_SIZE = 1024;
byte[] buffer = new byte[BUFFER_SIZE];
int length = -1;
while ((length = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, length);
}
}
// A handy method I use to close all the streams
private void closeStreams(Closeable... closeables) {
if (closeables != null) {
for (Closeable stream : closeables) {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}