package tv.danmaku.ijk.media.exo2; import android.content.Context; import android.net.NetworkInfo; import android.net.Uri; import android.support.annotation.Nullable; import android.support.annotation.Size; import android.view.Surface; import android.view.SurfaceHolder; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.DefaultLoadControl; import com.google.android.exoplayer2.DefaultRenderersFactory; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.analytics.AnalyticsListener; import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.util.HashMap; import java.util.Map; import tv.danmaku.ijk.media.exo2.demo.EventLogger; import tv.danmaku.ijk.media.player.AbstractMediaPlayer; import tv.danmaku.ijk.media.player.IMediaPlayer; import tv.danmaku.ijk.media.player.MediaInfo; import tv.danmaku.ijk.media.player.misc.IjkTrackInfo; /** * Created by guoshuyu on 2018/1/10. * Exo */ public class IjkExo2MediaPlayer extends AbstractMediaPlayer implements Player.EventListener, AnalyticsListener { private static final String TAG = "IjkExo2MediaPlayer"; protected Context mAppContext; protected SimpleExoPlayer mInternalPlayer; protected EventLogger mEventLogger; protected DefaultRenderersFactory rendererFactory; protected MediaSource mMediaSource; protected DefaultTrackSelector mTrackSelector; protected String mDataSource; protected Surface mSurface; protected Map mHeaders = new HashMap<>(); protected PlaybackParameters mSpeedPlaybackParameters; protected int mVideoWidth; protected int mVideoHeight; protected int lastReportedPlaybackState; protected boolean isLastReportedPlayWhenReady; protected boolean isPreparing = true; protected boolean isBuffering = false; protected boolean isLooping = false; /** * 是否带上header */ protected boolean isPreview = false; /** * 是否开启缓存 */ protected boolean isCache = false; /** * dataSource等的帮组类 */ protected ExoSourceManager mExoHelper; /** * 缓存目录,可以为空 */ protected File mCacheDir; protected int audioSessionId = C.AUDIO_SESSION_ID_UNSET; public IjkExo2MediaPlayer(Context context) { mAppContext = context.getApplicationContext(); lastReportedPlaybackState = Player.STATE_IDLE; mExoHelper = ExoSourceManager.newInstance(context, mHeaders); } private int getVideoRendererIndex() { if (mInternalPlayer != null) { for (int i = 0; i < mInternalPlayer.getRendererCount(); i++) { if (mInternalPlayer.getRendererType(i) == C.TRACK_TYPE_VIDEO) { return i; } } } return 0; } @Override public void setDisplay(SurfaceHolder sh) { if (sh == null) setSurface(null); else setSurface(sh.getSurface()); } @Override public void setSurface(Surface surface) { mSurface = surface; if (mInternalPlayer != null) { if (surface != null && !surface.isValid()) { mSurface = null; } mInternalPlayer.setVideoSurface(surface); } } @Override public void setDataSource(Context context, Uri uri, Map headers) { if (headers != null) { mHeaders.clear(); mHeaders.putAll(headers); } setDataSource(context, uri); } @Override public void setDataSource(String path) { setDataSource(mAppContext, Uri.parse(path)); } @Override public void setDataSource(Context context, Uri uri) { mDataSource = uri.toString(); mMediaSource = mExoHelper.getMediaSource(mDataSource, isPreview, isCache, isLooping, mCacheDir); } @Override public void setDataSource(FileDescriptor fd) { throw new UnsupportedOperationException("no support"); } @Override public String getDataSource() { return mDataSource; } @Override public void prepareAsync() throws IllegalStateException { if (mInternalPlayer != null) throw new IllegalStateException("can't prepare a prepared player"); TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(new DefaultBandwidthMeter()); mTrackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); mEventLogger = new EventLogger(mTrackSelector); boolean preferExtensionDecoders = true; boolean useExtensionRenderers = true;//是否开启扩展 @DefaultRenderersFactory.ExtensionRendererMode int extensionRendererMode = useExtensionRenderers ? (preferExtensionDecoders ? DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER : DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON) : DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF; rendererFactory = new DefaultRenderersFactory(mAppContext, extensionRendererMode); DefaultLoadControl loadControl = new DefaultLoadControl(); mInternalPlayer = ExoPlayerFactory.newSimpleInstance(rendererFactory, mTrackSelector, loadControl, null); mInternalPlayer.addListener(this); mInternalPlayer.addAnalyticsListener(this); mInternalPlayer.addListener(mEventLogger); if (mSpeedPlaybackParameters != null) { mInternalPlayer.setPlaybackParameters(mSpeedPlaybackParameters); } if (mSurface != null) mInternalPlayer.setVideoSurface(mSurface); mInternalPlayer.prepare(mMediaSource); mInternalPlayer.setPlayWhenReady(false); } @Override public void start() throws IllegalStateException { if (mInternalPlayer == null) return; mInternalPlayer.setPlayWhenReady(true); } @Override public void stop() throws IllegalStateException { if (mInternalPlayer == null) return; mInternalPlayer.release(); } @Override public void pause() throws IllegalStateException { if (mInternalPlayer == null) return; mInternalPlayer.setPlayWhenReady(false); } @Override public void setWakeMode(Context context, int mode) { // FIXME: implement } @Override public void setScreenOnWhilePlaying(boolean screenOn) { // TODO: do nothing } @Override public IjkTrackInfo[] getTrackInfo() { // TODO: implement return null; } @Override public int getVideoWidth() { return mVideoWidth; } @Override public int getVideoHeight() { return mVideoHeight; } @Override public boolean isPlaying() { if (mInternalPlayer == null) return false; int state = mInternalPlayer.getPlaybackState(); switch (state) { case Player.STATE_BUFFERING: case Player.STATE_READY: return mInternalPlayer.getPlayWhenReady(); case Player.STATE_IDLE: case Player.STATE_ENDED: default: return false; } } @Override public void seekTo(long msec) throws IllegalStateException { if (mInternalPlayer == null) return; mInternalPlayer.seekTo(msec); } @Override public long getCurrentPosition() { if (mInternalPlayer == null) return 0; return mInternalPlayer.getCurrentPosition(); } @Override public long getDuration() { if (mInternalPlayer == null) return 0; return mInternalPlayer.getDuration(); } @Override public int getVideoSarNum() { return 1; } @Override public int getVideoSarDen() { return 1; } @Override public void reset() { if (mInternalPlayer != null) { mInternalPlayer.release(); mInternalPlayer = null; } if (mExoHelper != null) { mExoHelper.release(); } mSurface = null; mDataSource = null; mVideoWidth = 0; mVideoHeight = 0; } @Override public void setLooping(boolean looping) { isLooping = looping; } @Override public boolean isLooping() { return isLooping; } @Override public void setVolume(float leftVolume, float rightVolume) { if (mInternalPlayer != null) mInternalPlayer.setVolume((leftVolume + rightVolume) / 2); } @Override public int getAudioSessionId() { return audioSessionId; } @Override public MediaInfo getMediaInfo() { return null; } @Override public void setLogEnabled(boolean enable) { // do nothing } @Override public boolean isPlayable() { return true; } @Override public void setAudioStreamType(int streamtype) { // do nothing } @Override public void setKeepInBackground(boolean keepInBackground) { // do nothing } @Override public void release() { if (mInternalPlayer != null) { reset(); mEventLogger = null; } } public void stopPlayback() { mInternalPlayer.stop(); } /** * 是否需要带上header * setDataSource之前生效 * * @param preview */ public void setPreview(boolean preview) { isPreview = preview; } public boolean isPreview() { return isPreview; } public boolean isCache() { return isCache; } /** * 是否开启cache * setDataSource之前生效 * * @param cache */ public void setCache(boolean cache) { isCache = cache; } public File getCacheDir() { return mCacheDir; } /** * cache文件的目录 * setDataSource之前生效 * * @param cacheDir */ public void setCacheDir(File cacheDir) { this.mCacheDir = cacheDir; } public MediaSource getMediaSource() { return mMediaSource; } public void setMediaSource(MediaSource mediaSource) { this.mMediaSource = mediaSource; } public ExoSourceManager getExoHelper() { return mExoHelper; } /** * 倍速播放 * * @param speed 倍速播放,默认为1 * @param pitch 音量缩放,默认为1,修改会导致声音变调 */ public void setSpeed(@Size(min = 0) float speed, @Size(min = 0) float pitch) { PlaybackParameters playbackParameters = new PlaybackParameters(speed, pitch); mSpeedPlaybackParameters = playbackParameters; if (mInternalPlayer != null) { mInternalPlayer.setPlaybackParameters(playbackParameters); } } public float getSpeed() { return mInternalPlayer.getPlaybackParameters().speed; } public int getBufferedPercentage() { if (mInternalPlayer == null) return 0; return mInternalPlayer.getBufferedPercentage(); } @Override public void onTimelineChanged(Timeline timeline, Object manifest, int reason) { } @Override public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { } @Override public void onLoadingChanged(boolean isLoading) { } @Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { //重新播放状态顺序为:STATE_IDLE -》STATE_BUFFERING -》STATE_READY //缓冲时顺序为:STATE_BUFFERING -》STATE_READY //Log.e(TAG, "onPlayerStateChanged: playWhenReady = " + playWhenReady + ", playbackState = " + playbackState); if (isLastReportedPlayWhenReady != playWhenReady || lastReportedPlaybackState != playbackState) { if (isBuffering) { switch (playbackState) { case Player.STATE_ENDED: case Player.STATE_READY: notifyOnInfo(IMediaPlayer.MEDIA_INFO_BUFFERING_END, mInternalPlayer.getBufferedPercentage()); isBuffering = false; break; } } if (isPreparing) { switch (playbackState) { case Player.STATE_READY: notifyOnPrepared(); isPreparing = false; break; } } switch (playbackState) { case Player.STATE_BUFFERING: notifyOnInfo(IMediaPlayer.MEDIA_INFO_BUFFERING_START, mInternalPlayer.getBufferedPercentage()); isBuffering = true; break; case Player.STATE_READY: break; case Player.STATE_ENDED: notifyOnCompletion(); break; default: break; } } isLastReportedPlayWhenReady = playWhenReady; lastReportedPlaybackState = playbackState; } @Override public void onRepeatModeChanged(int repeatMode) { } @Override public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { } @Override public void onPlayerError(ExoPlaybackException error) { notifyOnError(IMediaPlayer.MEDIA_ERROR_UNKNOWN, IMediaPlayer.MEDIA_ERROR_UNKNOWN); } @Override public void onPositionDiscontinuity(int reason) { } @Override public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { } @Override public void onSeekProcessed() { } /////////////////////////////////////AudioRendererEventListener///////////////////////////////////////////// @Override public void onPlayerStateChanged(EventTime eventTime, boolean playWhenReady, int playbackState) { } @Override public void onTimelineChanged(EventTime eventTime, int reason) { } @Override public void onPositionDiscontinuity(EventTime eventTime, int reason) { } @Override public void onSeekStarted(EventTime eventTime) { } @Override public void onSeekProcessed(EventTime eventTime) { } @Override public void onPlaybackParametersChanged(EventTime eventTime, PlaybackParameters playbackParameters) { } @Override public void onRepeatModeChanged(EventTime eventTime, int repeatMode) { } @Override public void onShuffleModeChanged(EventTime eventTime, boolean shuffleModeEnabled) { } @Override public void onLoadingChanged(EventTime eventTime, boolean isLoading) { } @Override public void onPlayerError(EventTime eventTime, ExoPlaybackException error) { } @Override public void onTracksChanged(EventTime eventTime, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { } @Override public void onLoadStarted(EventTime eventTime, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData) { } @Override public void onLoadCompleted(EventTime eventTime, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData) { } @Override public void onLoadCanceled(EventTime eventTime, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData) { } @Override public void onLoadError(EventTime eventTime, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData, IOException error, boolean wasCanceled) { } @Override public void onDownstreamFormatChanged(EventTime eventTime, MediaSourceEventListener.MediaLoadData mediaLoadData) { } @Override public void onUpstreamDiscarded(EventTime eventTime, MediaSourceEventListener.MediaLoadData mediaLoadData) { } @Override public void onMediaPeriodCreated(EventTime eventTime) { } @Override public void onMediaPeriodReleased(EventTime eventTime) { } @Override public void onReadingStarted(EventTime eventTime) { } @Override public void onBandwidthEstimate(EventTime eventTime, int totalLoadTimeMs, long totalBytesLoaded, long bitrateEstimate) { } @Override public void onViewportSizeChange(EventTime eventTime, int width, int height) { } @Override public void onNetworkTypeChanged(EventTime eventTime, @Nullable NetworkInfo networkInfo) { } @Override public void onMetadata(EventTime eventTime, Metadata metadata) { } @Override public void onDecoderEnabled(EventTime eventTime, int trackType, DecoderCounters decoderCounters) { } @Override public void onDecoderInitialized(EventTime eventTime, int trackType, String decoderName, long initializationDurationMs) { } @Override public void onDecoderInputFormatChanged(EventTime eventTime, int trackType, Format format) { } @Override public void onDecoderDisabled(EventTime eventTime, int trackType, DecoderCounters decoderCounters) { audioSessionId = C.AUDIO_SESSION_ID_UNSET; } @Override public void onAudioSessionId(EventTime eventTime, int audioSessionId) { this.audioSessionId = audioSessionId; } @Override public void onAudioUnderrun(EventTime eventTime, int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { } @Override public void onDroppedVideoFrames(EventTime eventTime, int droppedFrames, long elapsedMs) { } @Override public void onVideoSizeChanged(EventTime eventTime, int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { mVideoWidth = width; mVideoHeight = height; notifyOnVideoSizeChanged(width, height, 1, 1); if (unappliedRotationDegrees > 0) notifyOnInfo(IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED, unappliedRotationDegrees); } @Override public void onRenderedFirstFrame(EventTime eventTime, Surface surface) { } @Override public void onDrmKeysLoaded(EventTime eventTime) { } @Override public void onDrmSessionManagerError(EventTime eventTime, Exception error) { } @Override public void onDrmKeysRestored(EventTime eventTime) { } @Override public void onDrmKeysRemoved(EventTime eventTime) { } }