Android java exoplayer2 проигрование с файловой системы и raw/

149
27 марта 2019, 14:20

package com.none.rnar.testplayer.service; 
 
import android.app.Notification; 
import android.app.NotificationChannel; 
import android.app.NotificationManager; 
import android.app.PendingIntent; 
import android.app.Service; 
import android.content.BroadcastReceiver; 
import android.content.Context; 
import android.content.Intent; 
import android.content.IntentFilter; 
import android.graphics.BitmapFactory; 
import android.media.AudioAttributes; 
import android.media.AudioFocusRequest; 
import android.media.AudioManager; 
import android.net.Uri; 
import android.os.Binder; 
import android.os.Build; 
import android.os.IBinder; 
import android.support.annotation.Nullable; 
import android.support.v4.app.NotificationManagerCompat; 
import android.support.v4.content.ContextCompat; 
import android.support.v4.media.MediaMetadataCompat; 
import android.support.v4.media.session.MediaButtonReceiver; 
import android.support.v4.media.session.MediaSessionCompat; 
import android.support.v4.media.session.PlaybackStateCompat; 
import android.support.v4.app.NotificationCompat; 
import android.support.v4.media.app.NotificationCompat.MediaStyle; 
 
import com.google.android.exoplayer2.DefaultLoadControl; 
import com.google.android.exoplayer2.DefaultRenderersFactory; 
import com.google.android.exoplayer2.ExoPlaybackException; 
import com.google.android.exoplayer2.ExoPlayer; 
import com.google.android.exoplayer2.ExoPlayerFactory; 
import com.google.android.exoplayer2.PlaybackParameters; 
import com.google.android.exoplayer2.SimpleExoPlayer; 
import com.google.android.exoplayer2.Timeline; 
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSourceFactory; 
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; 
import com.google.android.exoplayer2.extractor.ExtractorsFactory; 
import com.google.android.exoplayer2.source.ExtractorMediaSource; 
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.upstream.DataSource; 
import com.google.android.exoplayer2.upstream.cache.Cache; 
import com.google.android.exoplayer2.upstream.cache.CacheDataSource; 
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory; 
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor; 
import com.google.android.exoplayer2.upstream.cache.SimpleCache; 
import com.google.android.exoplayer2.util.Util; 
 
import okhttp3.OkHttpClient; 
 
import com.none.rnar.testplayer.R; 
import com.none.rnar.testplayer.ui.MainActivity; 
 
import java.io.File; 
 
 
 
final public class PlayerService extends Service { 
 
  private final int NOTIFICATION_ID = 404; 
  private final String NOTIFICATION_DEFAULT_CHANNEL_ID = "default_channel"; 
 
  private final MediaMetadataCompat.Builder metadataBuilder = new MediaMetadataCompat.Builder(); 
 
  private final PlaybackStateCompat.Builder stateBuilder = new PlaybackStateCompat.Builder().setActions( 
    PlaybackStateCompat.ACTION_PLAY | 
    PlaybackStateCompat.ACTION_STOP | 
    PlaybackStateCompat.ACTION_PAUSE | 
    PlaybackStateCompat.ACTION_PLAY_PAUSE | 
    PlaybackStateCompat.ACTION_SKIP_TO_NEXT | 
    PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS 
  ); 
 
  private MediaSessionCompat mediaSession; 
 
  private AudioManager audioManager; 
  private AudioFocusRequest audioFocusRequest; 
  private boolean audioFocusRequested = false; 
 
  private SimpleExoPlayer exoPlayer; 
  private ExtractorsFactory extractorsFactory; 
  private DataSource.Factory dataSourceFactory; 
 
  private final MusicRepository musicRepository = new MusicRepository(); 
 
  @Override 
  public void onCreate() { 
    super.onCreate(); 
 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 
      NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_DEFAULT_CHANNEL_ID, getString(R.string.notification_channel_name), NotificationManagerCompat.IMPORTANCE_DEFAULT); 
      NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 
      notificationManager.createNotificationChannel(notificationChannel); 
 
      AudioAttributes audioAttributes = new AudioAttributes.Builder() 
        .setUsage(AudioAttributes.USAGE_MEDIA) 
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) 
        .build(); 
      audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) 
        .setOnAudioFocusChangeListener(audioFocusChangeListener) 
        .setAcceptsDelayedFocusGain(false) 
        .setWillPauseWhenDucked(true) 
        .setAudioAttributes(audioAttributes) 
        .build(); 
    } 
 
    audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 
 
    mediaSession = new MediaSessionCompat(this, "PlayerService"); 
    mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); 
    mediaSession.setCallback(mediaSessionCallback); 
 
    Context appContext = getApplicationContext(); 
 
    Intent activityIntent = new Intent(appContext, MainActivity.class); 
    mediaSession.setSessionActivity(PendingIntent.getActivity(appContext, 0, activityIntent, 0)); 
 
    Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null, appContext, MediaButtonReceiver.class); 
    mediaSession.setMediaButtonReceiver(PendingIntent.getBroadcast(appContext, 0, mediaButtonIntent, 0)); 
 
    exoPlayer = ExoPlayerFactory.newSimpleInstance(new DefaultRenderersFactory(this), new DefaultTrackSelector(), new DefaultLoadControl()); 
    exoPlayer.addListener(exoPlayerListener); 
    DataSource.Factory httpDataSourceFactory = new OkHttpDataSourceFactory(new OkHttpClient(), Util.getUserAgent(this, getString(R.string.app_name)), null); 
    Cache cache = new SimpleCache(new File(this.getCacheDir().getAbsolutePath() + "/exoplayer"), new LeastRecentlyUsedCacheEvictor(1024 * 1024 * 100)); // 100 Mb max 
    this.dataSourceFactory = new CacheDataSourceFactory(cache, httpDataSourceFactory, CacheDataSource.FLAG_BLOCK_ON_CACHE | CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR); 
    this.extractorsFactory = new DefaultExtractorsFactory(); 
  } 
 
  @Override 
  public int onStartCommand(Intent intent, int flags, int startId) { 
    MediaButtonReceiver.handleIntent(mediaSession, intent); 
    return super.onStartCommand(intent, flags, startId); 
  } 
 
  @Override 
  public void onDestroy() { 
    super.onDestroy(); 
    mediaSession.release(); 
    exoPlayer.release(); 
  } 
 
  private MediaSessionCompat.Callback mediaSessionCallback = new MediaSessionCompat.Callback() { 
 
    private Uri currentUri; 
    int currentState = PlaybackStateCompat.STATE_STOPPED; 
 
    @Override 
    public void onPlay() { 
      if (!exoPlayer.getPlayWhenReady()) { 
        startService(new Intent(getApplicationContext(), PlayerService.class)); 
 
        MusicRepository.Track track = musicRepository.getCurrent(); 
        updateMetadataFromTrack(track); 
 
        prepareToPlay(track.getUri()); 
 
        if (!audioFocusRequested) { 
          audioFocusRequested = true; 
 
          int audioFocusResult; 
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 
            audioFocusResult = audioManager.requestAudioFocus(audioFocusRequest); 
          } else { 
            audioFocusResult = audioManager.requestAudioFocus(audioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); 
          } 
          if (audioFocusResult != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) 
            return; 
        } 
 
        mediaSession.setActive(true); // Сразу после получения фокуса 
 
        registerReceiver(becomingNoisyReceiver, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)); 
 
        exoPlayer.setPlayWhenReady(true); 
      } 
 
      mediaSession.setPlaybackState(stateBuilder.setState(PlaybackStateCompat.STATE_PLAYING, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1).build()); 
      currentState = PlaybackStateCompat.STATE_PLAYING; 
 
      refreshNotificationAndForegroundStatus(currentState); 
    } 
 
    @Override 
    public void onPause() { 
      if (exoPlayer.getPlayWhenReady()) { 
        exoPlayer.setPlayWhenReady(false); 
        unregisterReceiver(becomingNoisyReceiver); 
      } 
 
      mediaSession.setPlaybackState(stateBuilder.setState(PlaybackStateCompat.STATE_PAUSED, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1).build()); 
      currentState = PlaybackStateCompat.STATE_PAUSED; 
 
      refreshNotificationAndForegroundStatus(currentState); 
    } 
 
    @Override 
    public void onStop() { 
      if (exoPlayer.getPlayWhenReady()) { 
        exoPlayer.setPlayWhenReady(false); 
        unregisterReceiver(becomingNoisyReceiver); 
      } 
 
      if (audioFocusRequested) { 
        audioFocusRequested = false; 
 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 
          audioManager.abandonAudioFocusRequest(audioFocusRequest); 
        } else { 
          audioManager.abandonAudioFocus(audioFocusChangeListener); 
        } 
      } 
 
      mediaSession.setActive(false); 
 
      mediaSession.setPlaybackState(stateBuilder.setState(PlaybackStateCompat.STATE_STOPPED, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1).build()); 
      currentState = PlaybackStateCompat.STATE_STOPPED; 
 
      refreshNotificationAndForegroundStatus(currentState); 
 
      stopSelf(); 
    } 
 
    @Override 
    public void onSkipToNext() { 
      MusicRepository.Track track = musicRepository.getNext(); 
      updateMetadataFromTrack(track); 
 
      refreshNotificationAndForegroundStatus(currentState); 
 
      prepareToPlay(track.getUri()); 
    } 
 
    @Override 
    public void onSkipToPrevious() { 
      MusicRepository.Track track = musicRepository.getPrevious(); 
      updateMetadataFromTrack(track); 
 
      refreshNotificationAndForegroundStatus(currentState); 
 
      prepareToPlay(track.getUri()); 
    } 
 
    private void prepareToPlay(Uri uri) { 
      if (!uri.equals(currentUri)) { 
        currentUri = uri; 
        ExtractorMediaSource mediaSource = new ExtractorMediaSource(uri, dataSourceFactory, extractorsFactory, null, null); 
        exoPlayer.prepare(mediaSource); 
      } 
    } 
 
    private void updateMetadataFromTrack(MusicRepository.Track track) { 
      metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, BitmapFactory.decodeResource(getResources(), track.getBitmapResId())); 
      metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, track.getTitle()); 
      metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, track.getArtist()); 
      metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, track.getArtist()); 
      metadataBuilder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, track.getDuration()); 
      mediaSession.setMetadata(metadataBuilder.build()); 
    } 
  }; 
 
  private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() { 
    @Override 
    public void onAudioFocusChange(int focusChange) { 
      switch (focusChange) { 
        case AudioManager.AUDIOFOCUS_GAIN: 
          mediaSessionCallback.onPlay(); // Не очень красиво 
          break; 
        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 
          mediaSessionCallback.onPause(); 
          break; 
        default: 
          mediaSessionCallback.onPause(); 
          break; 
      } 
    } 
  }; 
 
  private final BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() { 
    @Override 
    public void onReceive(Context context, Intent intent) { 
      // Disconnecting headphones - stop playback 
      if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) { 
        mediaSessionCallback.onPause(); 
      } 
    } 
  }; 
 
  private ExoPlayer.EventListener exoPlayerListener = new ExoPlayer.EventListener() { 
    @Override 
    public void onTimelineChanged(Timeline timeline, Object manifest) {} 
 
    @Override 
    public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {} 
 
    @Override 
    public void onLoadingChanged(boolean isLoading) {} 
 
    @Override 
    public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { 
      if (playWhenReady && playbackState == ExoPlayer.STATE_ENDED) { 
        mediaSessionCallback.onSkipToNext(); 
      } 
    } 
 
    @Override 
    public void onPlayerError(ExoPlaybackException error) {} 
 
    @Override 
    public void onPositionDiscontinuity() {} 
 
    @Override 
    public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {} 
  }; 
 
  @Nullable 
  @Override 
  public IBinder onBind(Intent intent) { 
    return new PlayerServiceBinder(); 
  } 
 
  public class PlayerServiceBinder extends Binder { 
    public MediaSessionCompat.Token getMediaSessionToken() { 
      return mediaSession.getSessionToken(); 
    } 
  } 
 
  private void refreshNotificationAndForegroundStatus(int playbackState) { 
    switch (playbackState) { 
      case PlaybackStateCompat.STATE_PLAYING: 
        { 
          startForeground(NOTIFICATION_ID, getNotification(playbackState)); 
          break; 
        } 
      case PlaybackStateCompat.STATE_PAUSED: 
        { 
          NotificationManagerCompat.from(PlayerService.this).notify(NOTIFICATION_ID, getNotification(playbackState)); 
          stopForeground(false); 
          break; 
        } 
      default: 
        { 
          stopForeground(true); 
          break; 
        } 
    } 
  } 
 
  private Notification getNotification(int playbackState) { 
    NotificationCompat.Builder builder = MediaStyleHelper.from(this, mediaSession); 
    builder.addAction(new NotificationCompat.Action(android.R.drawable.ic_media_previous, getString(R.string.previous), MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS))); 
 
    if (playbackState == PlaybackStateCompat.STATE_PLAYING) 
      builder.addAction(new NotificationCompat.Action(android.R.drawable.ic_media_pause, getString(R.string.pause), MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_PLAY_PAUSE))); 
    else 
      builder.addAction(new NotificationCompat.Action(android.R.drawable.ic_media_play, getString(R.string.play), MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_PLAY_PAUSE))); 
 
    builder.addAction(new NotificationCompat.Action(android.R.drawable.ic_media_next, getString(R.string.next), MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_SKIP_TO_NEXT))); 
    builder.setStyle(new MediaStyle() 
      .setShowActionsInCompactView(1) 
      .setShowCancelButton(true) 
      .setCancelButtonIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_STOP)) 
      .setMediaSession(mediaSession.getSessionToken())); // setMediaSession требуется для Android Wear 
    builder.setSmallIcon(R.mipmap.ic_launcher); 
    builder.setColor(ContextCompat.getColor(this, R.color.colorPrimaryDark)); // The whole background (in MediaStyle), not just icon background 
    builder.setShowWhen(false); 
    builder.setPriority(NotificationCompat.PRIORITY_HIGH); 
    builder.setOnlyAlertOnce(true); 
    builder.setChannelId(NOTIFICATION_DEFAULT_CHANNEL_ID); 
 
    return builder.build(); 
  } 
}

package com.none.rnar.testplayer.service; 
 
import android.net.Uri; 
 
import com.none.rnar.testplayer.R; 
 
 
final class MusicRepository { 
 
  private final Track[] data = { 
    new Track("Triangle", "Jason Shaw", R.drawable.ic_launcher_foreground, Uri.parse("android.resource://com.none.rnar.testplayer/" + R.raw.us_big), (3 * 60 + 41) * 1000), 
    new Track("Rubix Cube", "Jason Shaw", R.drawable.ic_launcher_background, Uri.parse("android.resource://com.none.rnar.testplayer/" + R.raw.us_black), (3 * 60 + 44) * 1000), 
    new Track("MC Ballad S Early Eighties", "Frank Nora", R.drawable.ic_launcher_background, Uri.parse("android.resource://com.none.rnar.testplayer/" + R.raw.us_blue), (2 * 60 + 50) * 1000), 
    new Track("Folk Song", "Brian Boyko", R.drawable.ic_launcher_background, Uri.parse("android.resource://com.none.rnar.testplayer/" + R.raw.us_child), (3 * 60 + 5) * 1000), 
    new Track("Morning Snowflake", "Kevin MacLeod", R.drawable.ic_launcher_foreground, Uri.parse("android.resource://com.none.rnar.testplayer/" + R.raw.saga), (2 * 60 + 0) * 1000), 
  }; 
 
  private final int maxIndex = data.length - 1; 
  private int currentItemIndex = 0; 
 
  Track getNext() { 
    if (currentItemIndex == maxIndex) 
      currentItemIndex = 0; 
    else 
      currentItemIndex++; 
    return getCurrent(); 
  } 
 
  Track getPrevious() { 
    if (currentItemIndex == 0) 
      currentItemIndex = maxIndex; 
    else 
      currentItemIndex--; 
    return getCurrent(); 
  } 
 
  Track getCurrent() { 
    return data[currentItemIndex]; 
  } 
 
  static class Track { 
 
    private String title; 
    private String artist; 
    private int bitmapResId; 
    private Uri uri; 
    private long duration; // in ms 
 
    Track(String title, String artist, int bitmapResId, Uri uri, long duration) { 
      this.title = title; 
      this.artist = artist; 
      this.bitmapResId = bitmapResId; 
      this.uri = uri; 
      this.duration = duration; 
    } 
 
    String getTitle() { 
      return title; 
    } 
 
    String getArtist() { 
      return artist; 
    } 
 
    int getBitmapResId() { 
      return bitmapResId; 
    } 
 
    Uri getUri() { 
      return uri; 
    } 
 
    long getDuration() { 
      return duration; 
    } 
  } 
}

Привет. Подскажите плиз, как добавить сюда возможность проигровать файлы с файловой системы устройства, и с папки raw. (хотя-бы укажите направление куда копать)

Попробовал просто ссылку на аудио заменить на такого вида... Uri.parse("android.resource://com.none.rnar.testplayer/" + R.raw.us_big) . Но это, естественно не помогло, что и не странно. Т.к насколько я понимаю, наверное где-то okhttp3 загружает аудиозапись по сети и сохраняет его во временную память, затем exoplayer как-то это играет. К сожалению я пока не могу определить точно ту часть кода где это происходит, и как. Было бы здорово если бы указали на эти места. Так-же не могу понять вот чтоnew Track("Triangle", "Jason Shaw", R.drawable.ic_launcher_foreground, Uri.parse("android.resource://com.none.rnar.testplayer/" + R.raw.us_big), (3 * 60 + 41) * 1000)) последний параметр (duration), по названию можно догадаться что это продолжительность. Но почему она указывается в ручную, да ещё и способом умножения и сложения чисел. Зачем этот параметр здесь? В таком виде... p.s

И если можно, ссылочку на примеры реализации различных плюшек от гугла, с этими либами (которые на java и актуальны) ибо я чука потерялся (первый раз работю с android). Спасибо! И да, простите за глупый вопрос, просто уже почти день мучаюсь с этим примером, что весьма и весьма печально =) Вот где я взял этот пример >>тык<<

Answer 1

Вообщем нужно было всеголишь

DataSource.Factory httpDataSourceFactory = new OkHttpDataSourceFactory(new OkHttpClient(), Util.getUserAgent(this, getString(R.string.app_name)), null);

заменить на

DataSource.Factory dataSourceFactory = new FileDataSourceFactory();

ну и путь заменил на такого рода Uri.parse("file:///sdcard/MyData/sagаobegile") Если вдруг кому пригодится...

READ ALSO
Падает приложение [закрыт]

Падает приложение [закрыт]

Ребята, не могу понять, почему падает приложение, помогите разобраться В задумке: оно должно выполнять два разных сценария в зависимости...

154
Не вызывается контекстное меню

Не вызывается контекстное меню

Элемент Listview не реагирует на длительное нажатие и не вызывается контекстное меню

138
Вычисление производной [закрыт]

Вычисление производной [закрыт]

Для алгоритма требуется найти производнуюНе подскажете, какой библиотекой или каким-то иным способом можно это сделать? В скриншоте первый...

183
Сохранить содержимое консоли в файл. Java [закрыт]

Сохранить содержимое консоли в файл. Java [закрыт]

Возможно ли как-то это осуществить? Например, программа сработала, консоль отобразила результат и пользователь введя букву "s" переносит...

149