Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Using Exoplayer

Effie Barak
July 29, 2016
37k

Using Exoplayer

Effie Barak

July 29, 2016
Tweet

Transcript

  1. @Override public boolean onError(MediaPlayer mp, int what, int extra) {

    mediaPlayerError = true; if(what == -38) { //This exception just Happens for varios unexplainable reasons UdemyMediaPlayer.this.prepareAsync(); } return false; }
  2. Solves these problems • Open source, written in Java •

    Built on top of Media Codec • Handles HLS correctly
  3. Extensible and/ or supports • Background playing • Subtitles •

    Variable playing speed • Variable resolutions
  4. Tips to get started: • ExoPlayer is written in Java.

    • The sample app is a good place to start. • The default implementaions are Good Implementaions.
  5. Adding state listeners public abstract class UdemyBaseExoplayer implements ExoPlayer.Listener, ChunkSampleSource.EventListener,

    HlsSampleSource.EventListener, DefaultBandwidthMeter.EventListener, MediaCodecVideoTrackRenderer.EventListener, MediaCodecAudioTrackRenderer.EventListener player.addListener(this);
  6. Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE); Handler mainHandler = player.getMainHandler(); DefaultBandwidthMeter

    bandwidthMeter = new DefaultBandwidthMeter(mainHandler, null); DataSource dataSource = new DefaultUriDataSource( context, bandwidthMeter, Util.getUserAgent(mContext, Constants.UDEMY_NAME)); ExtractorSampleSource sampleSource = new ExtractorSampleSource(uri, dataSource, allocator, BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE, mainHandler, player, 0); MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context, sampleSource, MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, mainHandler, player, 50); MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource, MediaCodecSelector.DEFAULT, null, true, mainHandler, player, AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC); TrackRenderer[] renderers = new TrackRenderer[PlayerConstants.RENDERER_COUNT]; renderers[PlayerConstants.TYPE_VIDEO] = videoRenderer; renderers[PlayerConstants.TYPE_AUDIO] = audioRenderer; player.onRenderers(renderers, bandwidthMeter);
  7. // Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE); // Handler mainHandler =

    player.getMainHandler(); // DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, null); // DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter, // Util.getUserAgent(mContext, Constants.UDEMY_NAME)); ExtractorSampleSource sampleSource = new ExtractorSampleSource(uri, dataSource, allocator, BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE, mainHandler, player, 0); MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context, sampleSource, MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, mainHandler, player, 50); MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource, MediaCodecSelector.DEFAULT, null, true, mainHandler, player, AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC); TrackRenderer[] renderers = new TrackRenderer[PlayerConstants.RENDERER_COUNT]; renderers[PlayerConstants.TYPE_VIDEO] = videoRenderer; renderers[PlayerConstants.TYPE_AUDIO] = audioRenderer; player.onRenderers(renderers, bandwidthMeter);
  8. Build extractors DefaultUriDataSource uriDataSource = new DefaultUriDataSource (context, bandwidthMeter, userAgent);

    ExtractorSampleSource sampleSource = new ExtractorSampleSource (uri, uriDataSource, allocator, PlayerConstants.BUFFER_SEGMENT_COUNT * PlayerConstants.BUFFER_SEGMENT_SIZE);
  9. Build renderers TrackRenderer[] renderers = new TrackRenderer[PlayerConstants.RENDERER_COUNT]; MediaCodecAudioTrackRenderer audioRenderer =

    new MediaCodecAudioTrackRenderer( sampleSource, MediaCodecSelector.DEFAULT, null, true, player.getMainHandler(), player, AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC); renderers[PlayerConstants.TYPE_AUDIO] = audioRenderer;
  10. Udemy customizations to basic structure • We decreased the buffer

    size after getting OOM exceptions on low end devices. public static final int BUFFER_SEGMENT_SIZE = 16 * 1024; // Original value was 64 * 1024 public static final int VIDEO_BUFFER_SEGMENTS = 50; // Original value was 200 public static final int AUDIO_BUFFER_SEGMENTS = 20; // Original value was 54 public static final int BUFFER_SEGMENT_COUNT = 64; // Original value was 256
  11. Udemy customizations to basic structure • We decreased the buffer

    size after getting OOM exceptions on low end devices. • The ExtractorSampleSource gets a list of possible extractors to work with (mp3 and mp4 only) mp4Extractor = new Mp4Extractor(); mp3Extractor = new Mp3Extractor(); sampleSource = new ExtractorSampleSource(..., mp4Extractor, mp3Extractor);
  12. !

  13. HLS

  14. DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); PtsTimestampAdjusterProvider timestampAdjusterProvider = new PtsTimestampAdjusterProvider();

    HlsChunkSource chunkSource = new HlsChunkSource(..., uriDataSource, url,..., bandwidthMeter, timestampAdjusterProvider, HlsChunkSource.ADAPTIVE_MODE_SPLICE);
  15. The bad news // The index in variants of the

    currently selected variant. private int selectedVariantIndex; public void getChunkOperation(...) { int nextVariantIndex; ... if (adaptiveMode == ADAPTIVE_MODE_NONE) { nextVariantIndex = selectedVariantIndex; switchingVariantSpliced = false; } else { ... } ...
  16. !

  17. Clear the surface by sending a message to ExoPlayer that

    sets the surface to null player.blockingSendMessage( videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, null);
  18. Create a service to keep the app alive. <service android:name="com.udemy.android.player.exoplayer.UdemyExoplayerService"

    android:exported="true" android:label="@string/app_name" android:enabled="true"> <intent-filter> <action android:name="ccom.udemy.android.player.exoplayer.UdemyExoplayerService"> </action> </intent-filter> </service> Both the view and the service control the same instance of the player, stored globally.
  19. When the app resumes set the surface again setPlayerSurface(surfaceView.getHolder().getSurface()); And

    in the player public void setSurface(Surface surface) { this.surface = surface; pushSurface(false); }
  20. Subtitles • Support .srt • Don't crash the video in

    case of error • Support multiple formats such as UTF-8
  21. public void displayExoplayerSubtitles( File file, final MediaController.MediaPlayerControl playerControl, final ViewGroup

    subtitleLayout, final Context context) { convertFileCaptionList(file, context); runnableCode = new Runnable() { @Override public void run() { displayForPosition(playerControl.getCurrentPosition(), subtitleLayout, context); handler.postDelayed(runnableCode, 200); } }; handler.post(runnableCode); }
  22. Method to override private byte[] sonicInputBuffer; private byte[] sonicOutputBuffer; @Override

    protected void onOutputFormatChanged(final MediaFormat format) {
  23. Method body // Two samples per frame * 2 to

    support audio speeds down to 0.5 final int bufferSizeBytes = SAMPLES_PER_CODEC_FRAME * 2 * 2 * channelCount; this.sonicInputBuffer = new byte[bufferSizeBytes]; this.sonicOutputBuffer = new byte[bufferSizeBytes]; this.sonic = new Sonic( format.getInteger(MediaFormat.KEY_SAMPLE_RATE), format.getInteger(MediaFormat.KEY_CHANNEL_COUNT)); this.lastInternalBuffer = ByteBuffer.wrap(sonicOutputBuffer, 0, 0); sonic.flushStream(); sonic.setSpeed(audioSpeed);
  24. To change the speed in the middle of consuming a

    buffer if (wasSpeedChanged) { sonic.flushStream(); sonic.setSpeed(audioSpeed); }