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

Using Exoplayer

Avatar for Effie Barak Effie Barak
July 29, 2016
37k

Using Exoplayer

Avatar for Effie Barak

Effie Barak

July 29, 2016
Tweet

More Decks by Effie Barak

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); }