Java tutorial
/* * Copyright (C) 2016 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 com.studyjams.mdvideo.PlayerModule.ExoPlayerV2; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.DefaultLoadControl; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.drm.FrameworkMediaDrm; import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; import com.google.android.exoplayer2.drm.StreamingDrmSessionManager; import com.google.android.exoplayer2.drm.UnsupportedDrmException; import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.DecoderInitializationException; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException; import com.google.android.exoplayer2.source.ConcatenatingMediaSource; import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MergingMediaSource; import com.google.android.exoplayer2.source.SingleSampleMediaSource; import com.google.android.exoplayer2.source.dash.DashMediaSource; import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource; import com.google.android.exoplayer2.source.hls.HlsMediaSource; import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource; import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; import com.google.android.exoplayer2.trackselection.AdaptiveVideoTrackSelection; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelections; import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.ui.SimpleExoPlayerView; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; import com.studyjams.mdvideo.Data.source.local.SamplesPersistenceContract; import com.studyjams.mdvideo.MainFrame.MainActivity; import com.studyjams.mdvideo.PlayerModule.EventBusMessage.ControllerMessage; import com.studyjams.mdvideo.PlayerModule.MenuDialog.VideoMenuDialog; import com.studyjams.mdvideo.PlayerModule.ui.MediaControlView; import com.studyjams.mdvideo.R; import com.studyjams.mdvideo.Util.D; import com.studyjams.mdvideo.Util.Tools; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import java.net.CookieHandler; import java.net.CookieManager; import java.net.CookiePolicy; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.UUID; import static com.google.android.exoplayer2.C.TYPE_SS; /** * An activity that plays media using {@link SimpleExoPlayer}. */ public class PlayerActivityV2 extends AppCompatActivity implements ExoPlayer.EventListener, TrackSelector.EventListener<MappedTrackInfo>, VideoMenuDialog.VideoSelected { private static final String TAG = "PlayerActivityV2"; public static final String DRM_SCHEME_UUID_EXTRA = "drm_scheme_uuid"; public static final String DRM_LICENSE_URL = "drm_license_url"; public static final String DRM_KEY_REQUEST_PROPERTIES = "drm_key_request_properties"; public static final String PREFER_EXTENSION_DECODERS = "prefer_extension_decoders"; public static final String ACTION_VIEW = "android.intent.action.VIEW"; public static final String EXTENSION_EXTRA = "extension"; public static final String ACTION_VIEW_LIST = "com.google.android.exoplayer.demo.action.VIEW_LIST"; public static final String URI_LIST_EXTRA = "uri_list"; public static final String EXTENSION_LIST_EXTRA = "extension_list"; /**?id**/ public static final String CONTENT_ID_EXTRA = "content_id"; /**?**/ public static final String CONTENT_TYPE_EXTRA = "content_type"; /**?**/ public static final String CONTENT_POSITION_EXTRA = "content_position"; /**??**/ public static final String CONTENT_SUBTITLE_EXTRA = "content_subtitle"; /**intent**/ public static final String CONTENT_TYPE_INTENT = "content_intent"; /**?**/ private Uri mContentUri; // private int mContentType; //?id private String mContentId; // private long mContentPosition; /**?**/ private Uri mSubtitleUri; private MediaControlView controller; private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter(); private static final CookieManager DEFAULT_COOKIE_MANAGER; static { DEFAULT_COOKIE_MANAGER = new CookieManager(); DEFAULT_COOKIE_MANAGER.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER); } private Handler mainHandler; private Timeline.Window window; private EventLogger eventLogger; private SimpleExoPlayerView simpleExoPlayerView; private DataSource.Factory mediaDataSourceFactory; private SimpleExoPlayer player; private MappingTrackSelector trackSelector; private TrackSelectionHelper trackSelectionHelper; // private DebugTextViewHelper debugViewHelper; private boolean playerNeedsSource; private boolean shouldAutoPlay; private boolean isTimelineStatic; private int playerWindow; private long playerPosition; // Activity lifecycle @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); shouldAutoPlay = true; mediaDataSourceFactory = buildDataSourceFactory(true); mainHandler = new Handler(); window = new Timeline.Window(); if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) { CookieHandler.setDefault(DEFAULT_COOKIE_MANAGER); } setContentView(R.layout.player_activity_v2); // View rootView = findViewById(root); simpleExoPlayerView = (SimpleExoPlayerView) findViewById(R.id.player_view); // simpleExoPlayerView.setControllerVisibilityListener(this); simpleExoPlayerView.requestFocus(); /**?ExoPlayermediaController**/ simpleExoPlayerView.setUseController(false); controller = (MediaControlView) findViewById(R.id.player_control); controller.setVisibilityListener(new MediaControlView.VisibilityListener() { @Override public void onVisibilityChange(int visibility) { } }); EventBus.getDefault().register(this); } //MenuDialog??? @Override public void onVideoSelected(Intent intent) { if (player != null) { mContentPosition = player.getCurrentPosition(); } onNewIntent(intent); initializePlayer(); if (!controller.isVisible()) { controller.show(); } } @Override public void onNewIntent(Intent intent) { releasePlayer(); isTimelineStatic = false; setIntent(intent); } @Override public void onStart() { super.onStart(); if (Util.SDK_INT > 23) { initializePlayer(); } } @Override public void onResume() { super.onResume(); if ((Util.SDK_INT <= 23 || player == null)) { initializePlayer(); } } @Override public void onPause() { super.onPause(); if (Util.SDK_INT <= 23) { releasePlayer(); } } @Override public void onStop() { super.onStop(); if (Util.SDK_INT > 23) { releasePlayer(); } } @Override public void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { initializePlayer(); } else { showToast(R.string.storage_permission_denied); finish(); } } /**debug?**/ // @Override // public void onClick(View view) { // if (view == retryButton) { // initializePlayer(); // } else if (view.getParent() == debugRootView) { // trackSelectionHelper.showSelectionDialog(this, ((Button) view).getText(), // trackSelector.getCurrentSelections().info, (int) view.getTag()); // } // } // PlaybackControlView.VisibilityListener implementation // // @Override // public void onVisibilityChange(int visibility) { // debugRootView.setVisibility(visibility); // } // Internal methods private void initializePlayer() { Intent intent = getIntent(); String action = intent.getAction(); String type = intent.getType(); String intentType = intent.getStringExtra(CONTENT_TYPE_INTENT); if (Intent.ACTION_SEND.equals(action) && type.equals("video/*")) { mContentUri = intent.getParcelableExtra(Intent.EXTRA_STREAM); mContentId = ""; mContentPosition = C.TIME_UNSET; mSubtitleUri = null; } else if (intentType.equals(D.TYPE_VIDEO)) { mContentUri = intent.getData(); mContentId = intent.getStringExtra(CONTENT_ID_EXTRA); mContentPosition = intent.getLongExtra(CONTENT_POSITION_EXTRA, 0); String subtitle = intent.getStringExtra(CONTENT_SUBTITLE_EXTRA); if (!TextUtils.isEmpty(subtitle)) { mSubtitleUri = Uri.parse(subtitle); } else { mSubtitleUri = null; } playerPosition = mContentPosition; } else if (intentType.equals(D.TYPE_SUBTITLE)) { mSubtitleUri = intent.getData(); } if (player == null) { boolean preferExtensionDecoders = intent.getBooleanExtra(PREFER_EXTENSION_DECODERS, false); UUID drmSchemeUuid = intent.hasExtra(DRM_SCHEME_UUID_EXTRA) ? UUID.fromString(intent.getStringExtra(DRM_SCHEME_UUID_EXTRA)) : null; DrmSessionManager<FrameworkMediaCrypto> drmSessionManager = null; if (drmSchemeUuid != null) { String drmLicenseUrl = intent.getStringExtra(DRM_LICENSE_URL); String[] keyRequestPropertiesArray = intent.getStringArrayExtra(DRM_KEY_REQUEST_PROPERTIES); Map<String, String> keyRequestProperties; if (keyRequestPropertiesArray == null || keyRequestPropertiesArray.length < 2) { keyRequestProperties = null; } else { keyRequestProperties = new HashMap<>(); for (int i = 0; i < keyRequestPropertiesArray.length - 1; i += 2) { keyRequestProperties.put(keyRequestPropertiesArray[i], keyRequestPropertiesArray[i + 1]); } } try { drmSessionManager = buildDrmSessionManager(drmSchemeUuid, drmLicenseUrl, keyRequestProperties); } catch (UnsupportedDrmException e) { int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported : (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown); showToast(errorStringId); return; } } eventLogger = new EventLogger(); TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveVideoTrackSelection.Factory( BANDWIDTH_METER); trackSelector = new DefaultTrackSelector(mainHandler, videoTrackSelectionFactory); trackSelector.addListener(this); trackSelector.addListener(eventLogger); trackSelectionHelper = new TrackSelectionHelper(trackSelector, videoTrackSelectionFactory); player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, new DefaultLoadControl(), drmSessionManager, preferExtensionDecoders); player.addListener(this); player.addListener(eventLogger); player.setAudioDebugListener(eventLogger); player.setVideoDebugListener(eventLogger); player.setId3Output(eventLogger); simpleExoPlayerView.setPlayer(player); //?mediaController controller.setPlayer(player); controller.setTitle(mContentUri.getLastPathSegment()); // if (isTimelineStatic) { if (playerPosition == C.TIME_UNSET) { player.seekToDefaultPosition(playerWindow); } else { player.seekTo(playerWindow, playerPosition); } // } player.setPlayWhenReady(shouldAutoPlay); /**? debugViewHelper = new DebugTextViewHelper(player, debugTextView); debugViewHelper.start();**/ playerNeedsSource = true; } if (playerNeedsSource) { // String action = intent.getAction(); Uri[] uris; String[] extensions; if (ACTION_VIEW.equals(action)) { // uris = new Uri[]{intent.getData()}; uris = new Uri[] { mContentUri }; extensions = new String[] { intent.getStringExtra(EXTENSION_EXTRA) }; } else if (ACTION_VIEW_LIST.equals(action)) { String[] uriStrings = intent.getStringArrayExtra(URI_LIST_EXTRA); uris = new Uri[uriStrings.length]; for (int i = 0; i < uriStrings.length; i++) { uris[i] = Uri.parse(uriStrings[i]); } extensions = intent.getStringArrayExtra(EXTENSION_LIST_EXTRA); if (extensions == null) { extensions = new String[uriStrings.length]; } } else { showToast(getString(R.string.unexpected_intent_action, action)); return; } if (Util.maybeRequestReadExternalStoragePermission(this, uris)) { // The player will be reinitialized if the permission is granted. return; } MediaSource[] mediaSources = new MediaSource[uris.length]; for (int i = 0; i < uris.length; i++) { mediaSources[i] = buildMediaSource(uris[i], extensions[i]); } MediaSource mediaSource = mediaSources.length == 1 ? mediaSources[0] : new ConcatenatingMediaSource(mediaSources); player.prepare(mediaSource, !isTimelineStatic, !isTimelineStatic); playerNeedsSource = false; // updateButtonVisibilities(); } } private MediaSource buildMediaSource(Uri uri, String overrideExtension) { int type = Util.inferContentType( !TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension : uri.getLastPathSegment()); switch (type) { case TYPE_SS: return new SsMediaSource(uri, buildDataSourceFactory(false), new DefaultSsChunkSource.Factory(mediaDataSourceFactory), mainHandler, eventLogger); case C.TYPE_DASH: return new DashMediaSource(uri, buildDataSourceFactory(false), new DefaultDashChunkSource.Factory(mediaDataSourceFactory), mainHandler, eventLogger); case C.TYPE_HLS: return new HlsMediaSource(uri, mediaDataSourceFactory, mainHandler, eventLogger); case C.TYPE_OTHER: MediaSource mediaSource = new ExtractorMediaSource(uri, mediaDataSourceFactory, new DefaultExtractorsFactory(), mainHandler, eventLogger); if (mSubtitleUri != null) { Log.d(TAG, "========buildMediaSource: "); return TextSubtitle(mSubtitleUri, mediaSource); } else { return mediaSource; } default: { throw new IllegalStateException("Unsupported type: " + type); } } } /** * * @param srtUri * @param mediaSource */ private MediaSource TextSubtitle(Uri srtUri, MediaSource mediaSource) { Format textFormat = Format.createTextSampleFormat(null, MimeTypes.APPLICATION_SUBRIP, null, Format.NO_VALUE, Format.NO_VALUE, Locale.getDefault().getLanguage(), null); MediaSource textMediaSource = new SingleSampleMediaSource(srtUri, mediaDataSourceFactory, textFormat, C.TIME_UNSET); return new MergingMediaSource(mediaSource, textMediaSource); } private DrmSessionManager<FrameworkMediaCrypto> buildDrmSessionManager(UUID uuid, String licenseUrl, Map<String, String> keyRequestProperties) throws UnsupportedDrmException { if (Util.SDK_INT < 18) { return null; } HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl, buildHttpDataSourceFactory(false), keyRequestProperties); return new StreamingDrmSessionManager<>(uuid, FrameworkMediaDrm.newInstance(uuid), drmCallback, null, mainHandler, eventLogger); } private void releasePlayer() { if (player != null) { // debugViewHelper.stop(); // debugViewHelper = null; upDateRecord(); shouldAutoPlay = player.getPlayWhenReady(); playerWindow = player.getCurrentWindowIndex(); // playerPosition = C.TIME_UNSET; playerPosition = mContentPosition; // Timeline timeline = player.getCurrentTimeline(); // if (timeline != null && timeline.getWindow(playerWindow, window).isSeekable) { // playerPosition = player.getCurrentPosition(); // } player.release(); player = null; trackSelector = null; trackSelectionHelper = null; eventLogger = null; } } /** * Returns a new DataSource factory. * * @param useBandwidthMeter Whether to set {@link #BANDWIDTH_METER} as a listener to the new * DataSource factory. * @return A new DataSource factory. */ private DataSource.Factory buildDataSourceFactory(boolean useBandwidthMeter) { return buildDataSourceFactory(useBandwidthMeter ? BANDWIDTH_METER : null); } /** * Returns a new HttpDataSource factory. * * @param useBandwidthMeter Whether to set {@link #BANDWIDTH_METER} as a listener to the new * DataSource factory. * @return A new HttpDataSource factory. */ private HttpDataSource.Factory buildHttpDataSourceFactory(boolean useBandwidthMeter) { return buildHttpDataSourceFactory(useBandwidthMeter ? BANDWIDTH_METER : null); } DataSource.Factory buildDataSourceFactory(DefaultBandwidthMeter bandwidthMeter) { return new DefaultDataSourceFactory(this, bandwidthMeter, buildHttpDataSourceFactory(bandwidthMeter)); } HttpDataSource.Factory buildHttpDataSourceFactory(DefaultBandwidthMeter bandwidthMeter) { return new DefaultHttpDataSourceFactory(Util.getUserAgent(this, "MDVideo"), bandwidthMeter); } // ExoPlayer.EventListener implementation @Override public void onLoadingChanged(boolean isLoading) { // Do nothing. } /**?**/ @Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { if (playbackState == ExoPlayer.STATE_ENDED) { // showControls(); } // updateButtonVisibilities(); } @Override public void onPositionDiscontinuity() { // Do nothing. } @Override public void onTimelineChanged(Timeline timeline, Object manifest) { isTimelineStatic = timeline != null && timeline.getWindowCount() > 0 && !timeline.getWindow(timeline.getWindowCount() - 1, window).isDynamic; } @Override public void onPlayerError(ExoPlaybackException e) { String errorString = null; if (e.type == ExoPlaybackException.TYPE_RENDERER) { Exception cause = e.getRendererException(); if (cause instanceof DecoderInitializationException) { // Special case for decoder initialization failures. DecoderInitializationException decoderInitializationException = (DecoderInitializationException) cause; if (decoderInitializationException.decoderName == null) { if (decoderInitializationException.getCause() instanceof DecoderQueryException) { errorString = getString(R.string.error_querying_decoders); } else if (decoderInitializationException.secureDecoderRequired) { errorString = getString(R.string.error_no_secure_decoder, decoderInitializationException.mimeType); } else { errorString = getString(R.string.error_no_decoder, decoderInitializationException.mimeType); } } else { errorString = getString(R.string.error_instantiating_decoder, decoderInitializationException.decoderName); } } } if (errorString != null) { showToast(errorString); } playerNeedsSource = true; // updateButtonVisibilities(); // showControls(); } // MappingTrackSelector.EventListener implementation @Override public void onTrackSelectionsChanged(TrackSelections<? extends MappedTrackInfo> trackSelections) { // updateButtonVisibilities(); MappedTrackInfo trackInfo = trackSelections.info; if (trackInfo.hasOnlyUnplayableTracks(C.TRACK_TYPE_VIDEO)) { showToast(R.string.error_unsupported_video); } if (trackInfo.hasOnlyUnplayableTracks(C.TRACK_TYPE_AUDIO)) { showToast(R.string.error_unsupported_audio); } } /**??**/ private void upDateRecord() { if (!mContentId.equals("")) { long playDuration = player.getCurrentPosition(); Intent intent = new Intent(MainActivity.PLAY_HISTORY_ACTION); intent.putExtra(SamplesPersistenceContract.VideoEntry.COLUMN_VIDEO_ENTRY_ID, mContentId); intent.putExtra(SamplesPersistenceContract.VideoEntry.COLUMN_VIDEO_PLAY_DURATION, String.valueOf(playDuration)); intent.putExtra(SamplesPersistenceContract.VideoEntry.COLUMN_VIDEO_CREATED_DATE, Tools.getCurrentTimeMillis()); intent.putExtra(SamplesPersistenceContract.VideoEntry.COLUMN_VIDEO_SUBTITLE_PATH, String.valueOf(mSubtitleUri)); LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } } /** * threadMode * priority * sticky ? * @param msg */ @Subscribe(threadMode = ThreadMode.MAIN, priority = 0, sticky = false) public void handleEvent(ControllerMessage msg) { switch (msg.getCode()) { case ControllerMessage.EXIT: finish(); break; case ControllerMessage.SUBTITLE: if (controller.isVisible()) { controller.hide(); } VideoMenuDialog subtitleMenuDialog = VideoMenuDialog.newInstance(VideoMenuDialog.SUBTITLE); subtitleMenuDialog.show(getSupportFragmentManager(), String.valueOf(mSubtitleUri)); break; case ControllerMessage.MENU: if (controller.isVisible()) { controller.hide(); } VideoMenuDialog videoMenuDialog = VideoMenuDialog.newInstance(VideoMenuDialog.VIDEO); videoMenuDialog.show(getSupportFragmentManager(), mContentId); break; default: break; } } // User controls // private void updateButtonVisibilities() { // debugRootView.removeAllViews(); // // retryButton.setVisibility(playerNeedsSource ? View.VISIBLE : View.GONE); // debugRootView.addView(retryButton); // // if (player == null) { // return; // } // // TrackSelections<MappedTrackInfo> trackSelections = trackSelector.getCurrentSelections(); // if (trackSelections == null) { // return; // } // // int rendererCount = trackSelections.length; // for (int i = 0; i < rendererCount; i++) { // TrackGroupArray trackGroups = trackSelections.info.getTrackGroups(i); // if (trackGroups.length != 0) { // Button button = new Button(this); // int label; // switch (player.getRendererType(i)) { // case C.TRACK_TYPE_AUDIO: // label = R.string.audio; // break; // case C.TRACK_TYPE_VIDEO: // label = R.string.video; // break; // case C.TRACK_TYPE_TEXT: // label = R.string.text; // break; // default: // continue; // } // button.setText(label); // button.setTag(i); // button.setOnClickListener(this); // debugRootView.addView(button, debugRootView.getChildCount() - 1); // } // } // } // private void showControls() { // debugRootView.setVisibility(View.VISIBLE); // } private void showToast(int messageId) { showToast(getString(messageId)); } private void showToast(String message) { Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show(); } }