ExoPlayer: Time to say goodbye MediaPlayer

  • Đợt vừa rồi mình tham gia một dự án liên quan đến Android TV, mục đích chủ yếu của ứng dụng là trình diễn video từ các khóa học online. Khi nói đến video trên Android mình ngay lập tức nghĩ đến MediaPlayer mặc định vì nó khá đơn giản và với vài dòng code là có thể chơi video; tuy nhiên, sau khi phân tích yêu cầu từ phía khách hàng, mình nhận thấy MediaPlayer không thể đáp ứng tốt các yêu cầu ví dụ như custom UI, bắt các sự kiện khi hiện thực play-list video, tối ưu tốc độ trình chiếu video... Và hơn hết, MediaPlayer được Google tích hợp theo từng phiên bản Android nên việc hiểu và nắm bắt quy trình làm việc của nó rất nan giải và có thể xảy ra nhiều bug trong quá trình làm.

  • Và với ExoPlayer (https://github.com/google/ExoPlayer), các vấn đề của mình đã được giải quyết triệt để. ExoPlayer được Google khuyến cáo ứng dụng rộng rãi vì khả năng tương thích và mở rộng vượt trội so với MediaPlayer (https://developer.android.com/guide/topics/media/exoplayer.html). ExoPlayer dễ đọc và dễ debug, nó được xây dựng dựa trên MediaCodec và xử lý trực tiếp HLS, hỗ trợ chơi video ngầm, hiển thị phụ đề, tùy ý điều chỉnh độ phân giải và tốc độ chơi video… Với bài viết này mình sẽ chia sẻ một vài kinh nghiệm với ExoPlayer mà mình đã có được với ExoPlayer khi thực hiện dự án.

1> ExoPlayer là gì?

  • ExoPlayer là một thư viện open-source chuyên để xử lý video và audio trên Android. Các bạn có thể theo link để xem các ưu/nhược điểm của ExoPlayer. Bắt đầu với project mẫu của ExoPlayer trên Github, chúng ta có thể xây dựng ứng dụng chơi video/audio cơ bản theo cấu trúc chuẩn của ExoPlayer.

2> Hiện thực hóa ExoPlayer

  • Để hiện thực ứng dụng với ExoPlayer, chúng ta cần 2 bước cơ bản:
  1. Custom UI của player control với các action chính như Play/Pause, Next/ Previous, Rewind/Fast Forward (Nếu bạn sử dụng player control mặc định thì có thể bỏ qua bước này). Các bạn có thể tham khải thêm ở đây để biết rõ hơn về cách custom. Ví dụ như hình dưới là player control của ứng dụng đã được custom lại:

  1. Tiếp đến chúng ta sẽ cấu hình xử lý video cho ExoPlayer. ExoPlayer nhận video thông qua URL, nó sẽ dùng ExtractorSampleSource để phân tách dữ liệu (ví dụ với file MP4 thì Mp4Extractor sẽ được sử dụng). Sau đó nó sẽ mở file và giải mã audio/video thành dạng raw để Player có thể xử lý được.

    Tuy nhiên, ExoPlayer có thể xử lý theo một hướng khác: Player sẽ yêu cầu buffer từ bộ giải mã và ExtractorSampleSource sẽ tiến hành fetch dữ liệu từ URI. Theo ví dụ của ExoPlayer, chúng ta cần cấu hình như sau:

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

DefaultUriDataSource được sửa dụng bởi ExtractorSampleSource, chúng ta sẽ đưa vào một mảng để Player xử lý. Xây dựng Extractor:

DefaultUriDataSource uriDataSource = new DefaultUriDataSource
    (context, bandwidthMeter, userAgent);
ExtractorSampleSource sampleSource = new ExtractorSampleSource
    (uri, uriDataSource, allocator,
    PlayerConstants.BUFFER_SEGMENT_COUNT * PlayerConstants.BUFFER_SEGMENT_SIZE);

Xây dựng Renderer:

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;

Kết nối Renderer với Player:

player.prepare(renderers);

Hi vọng với những gì mình chia sẽ giúp ích cho các bạn trong quá trình xây dựng một ứng dụng xử lý video/audio cho Android. Để hiểu rõ thêm về ExoPlayer và tính năng của các phiên bản mới hơn, các bạn nên tham khảo từ trang Github của ExoPlayer. Chúc các bạn thành công!