/* * Copyright (C) 2016 Yaroslav Mytkalyk * * 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.doctoror.fuckoffmusicplayer.data.player; import android.content.Context; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.doctoror.commons.util.Log; import com.doctoror.fuckoffmusicplayer.domain.player.MediaPlayer; import com.doctoror.fuckoffmusicplayer.domain.player.MediaPlayerListener; 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.audio.AudioRendererEventListener; import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.util.Util; import java.util.Locale; import static com.doctoror.fuckoffmusicplayer.domain.player.MediaPlayerKt.SESSION_ID_NOT_SET; final class ExoMediaPlayer implements MediaPlayer { private static final String TAG = "ExoMediaPlayer"; private SimpleExoPlayer exoPlayer; private DataSource.Factory dataSourceFactory; private MediaPlayerListener mediaPlayerListener; private MediaSource mediaSource; private Uri loadingMediaUri; private Uri loadedMediaUri; ExoMediaPlayer() { } @Override public void setListener(@Nullable final MediaPlayerListener listener) { mediaPlayerListener = listener; } @Override public void init(@NonNull final Context context) { final TrackSelector trackSelector = new DefaultTrackSelector(); exoPlayer = ExoPlayerFactory.newSimpleInstance( new DefaultRenderersFactory(context), trackSelector, new DefaultLoadControl()); exoPlayer.addListener(mEventListener); exoPlayer.addAudioDebugListener(mAudioRendererEventListener); dataSourceFactory = new DefaultDataSourceFactory(context, Util.getUserAgent(context, "Painless Music Player")); } @Override public void load(@NonNull final Uri uri) { if (mediaSource != null) { mediaSource.releaseSource(); } if (mediaPlayerListener != null) { mediaPlayerListener.onLoading(); } mediaSource = new ExtractorMediaSource.Factory(dataSourceFactory).createMediaSource(uri); loadingMediaUri = uri; exoPlayer.prepare(mediaSource); } @Override public void play() { exoPlayer.setPlayWhenReady(true); } @Override public void pause() { exoPlayer.setPlayWhenReady(false); } @Override public void seekTo(final long millis) { exoPlayer.seekTo(millis); } @Override public long getCurrentPosition() { return exoPlayer.getCurrentPosition(); } @Nullable @Override public Uri getLoadedMediaUri() { return loadedMediaUri; } @Override public void stop() { exoPlayer.stop(); if (mediaSource != null) { mediaSource.releaseSource(); } } @Override public void release() { exoPlayer.release(); } private final AudioRendererEventListener mAudioRendererEventListener = new AudioRendererEventListener() { @Override public void onAudioEnabled(final DecoderCounters counters) { if (Log.logDEnabled()) { Log.d(TAG, "onAudioEnabled"); } } @Override public void onAudioSessionId(final int audioSessionId) { if (Log.logDEnabled()) { Log.d(TAG, "onAudioSessionId: " + audioSessionId); } if (mediaPlayerListener != null) { mediaPlayerListener.onAudioSessionId(audioSessionId); } } @Override public void onAudioDecoderInitialized(final String decoderName, final long initializedTimestampMs, final long initializationDurationMs) { if (Log.logDEnabled()) { Log.d(TAG, "onAudioDecoderInitialized: " + (decoderName == null ? "null" : decoderName)); } } @Override public void onAudioInputFormatChanged(final Format format) { if (Log.logDEnabled()) { Log.d(TAG, "onAudioInputFormatChanged: " + (format == null ? "null" : format)); } } @Override public void onAudioSinkUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { if (Log.logDEnabled()) { Log.d(TAG, String.format(Locale.US, "onAudioSinkUnderrun, bufferSize = '%d', bufferSizeMs = '%d', elapsedSinceLastFeedMs = '%d", bufferSize, bufferSizeMs, elapsedSinceLastFeedMs)); } } @Override public void onAudioDisabled(final DecoderCounters counters) { if (Log.logDEnabled()) { Log.d(TAG, "onAudioDisabled"); } if (mediaPlayerListener != null) { mediaPlayerListener.onAudioSessionId(SESSION_ID_NOT_SET); } } }; private final Player.EventListener mEventListener = new Player.EventListener() { @Override public void onTimelineChanged(Timeline timeline, Object manifest, int reason) { if (Log.logDEnabled()) { Log.d(TAG, "onTimelineChanged()"); } } @Override public void onTracksChanged(final TrackGroupArray trackGroups, final TrackSelectionArray trackSelections) { if (Log.logDEnabled()) { Log.d(TAG, "onTracksChanged()"); } } @Override public void onLoadingChanged(final boolean isLoading) { if (Log.logDEnabled()) { Log.d(TAG, "onLoadingChanged: " + isLoading); } } @Override public void onPlayerStateChanged(final boolean playWhenReady, final int playbackState) { if (Log.logDEnabled()) { Log.d(TAG, "onPlayerStateChanged: " + playbackState); } if (mediaPlayerListener != null) { switch (playbackState) { case Player.STATE_READY: loadedMediaUri = loadingMediaUri; if (playWhenReady) { mediaPlayerListener.onPlaybackStarted(); } else { mediaPlayerListener.onPlaybackPaused(); } break; case Player.STATE_ENDED: mediaPlayerListener.onPlaybackFinished(); break; } } } @Override public void onPlayerError(final ExoPlaybackException error) { if (Log.logDEnabled()) { Log.d(TAG, "onPlayerError: " + (error == null ? "null" : error)); } if (mediaPlayerListener != null) { mediaPlayerListener.onPlayerError(error); } } @Override public void onRepeatModeChanged(final int repeatMode) { if (Log.logDEnabled()) { Log.d(TAG, "onRepeatModeChanged: " + repeatMode); } } @Override public void onShuffleModeEnabledChanged(final boolean shuffleModeEnabled) { if (Log.logDEnabled()) { Log.d(TAG, "onShuffleModeEnabledChanged: " + shuffleModeEnabled); } } @Override public void onPositionDiscontinuity(final int reason) { if (Log.logDEnabled()) { Log.d(TAG, "onPositionDiscontinuity: " + reason); } } @Override public void onPlaybackParametersChanged(final PlaybackParameters playbackParameters) { if (Log.logDEnabled()) { Log.d(TAG, "onPlaybackParametersChanged: " + playbackParameters); } } @Override public void onSeekProcessed() { if (Log.logDEnabled()) { Log.d(TAG, "onSeekProcessed"); } } }; }