กำหนดเอง

อินเทอร์เฟซ Player คือหัวใจหลักของไลบรารี ExoPlayer Player แสดงฟังก์ชันการทำงานระดับสูงของมีเดียเพลเยอร์แบบดั้งเดิม เช่น ความสามารถในการบัฟเฟอร์สื่อ เล่น หยุดชั่วคราว และกรอวิดีโอ การใช้งานเริ่มต้น ExoPlayer ได้รับการออกแบบมาเพื่อให้มีการคาดเดาเพียงเล็กน้อยเกี่ยวกับ (และด้วยเหตุนี้จึงมีการจํากัดเพียงเล็กน้อยเกี่ยวกับ) ประเภทของสื่อที่เล่น วิธีการและตําแหน่งที่จัดเก็บ และวิธีแสดงผล การใช้งาน ExoPlayer ไม่ได้ใช้การโหลดและการแสดงผลสื่อโดยตรง แต่มอบหมายงานนี้ให้กับคอมโพเนนต์ที่แทรกเมื่อสร้างโปรแกรมเล่นหรือเมื่อส่งแหล่งที่มาของสื่อใหม่ไปยังโปรแกรมเล่น คอมโพเนนต์ที่ใช้ร่วมกันในการติดตั้งใช้งาน ExoPlayer ทั้งหมดมีดังนี้

  • อินสแตนซ์ MediaSource ที่กําหนดสื่อที่จะเล่น โหลดสื่อ และอ่านสื่อที่โหลดได้ ระบบจะสร้างอินสแตนซ์ MediaSource จาก MediaItem โดย MediaSource.Factory ภายในโปรแกรมเล่น นอกจากนี้ คุณยังส่งเพลย์ลิสต์ไปยังโปรแกรมเล่นโดยตรงได้โดยใช้ Playlist API ตามแหล่งที่มาของสื่อ
  • อินสแตนซ์ MediaSource.Factory ที่แปลง MediaItem เป็น MediaSource ระบบจะแทรก MediaSource.Factory เมื่อสร้างเพลเยอร์
  • Renderer อินสแตนซ์ที่แสดงผลแต่ละคอมโพเนนต์ของสื่อ ระบบจะแทรกข้อมูลเหล่านี้เมื่อสร้างเพลเยอร์
  • TrackSelector ที่เลือกแทร็กจาก MediaSource เพื่อใช้กับ Renderer แต่ละรายการที่มี ระบบจะแทรก TrackSelector เมื่อสร้างเพลเยอร์
  • LoadControl ที่ควบคุมว่าMediaSourceจะบัฟเฟอร์สื่อเพิ่มเมื่อใด และบัฟเฟอร์สื่อเท่าใด ระบบจะแทรก LoadControl เมื่อสร้างผู้เล่น
  • LivePlaybackSpeedControl ที่ควบคุมความเร็วในการเล่นระหว่างการเล่นแบบสดเพื่อให้ผู้เล่นเล่นตามเวลาจริงได้ใกล้เคียงกับเวลาออฟเซ็ตแบบสดที่กำหนดไว้ ระบบจะแทรก LivePlaybackSpeedControl เมื่อสร้างเพลเยอร์

แนวคิดในการแทรกคอมโพเนนต์ที่ใช้ฟังก์ชันการทำงานของเพลเยอร์มีอยู่ในไลบรารี การใช้งานเริ่มต้นของคอมโพเนนต์บางอย่างจะมอบหมายงานให้กับคอมโพเนนต์ที่แทรกเข้ามาเพิ่มเติม วิธีนี้ช่วยให้คุณแทนที่คอมโพเนนต์ย่อยหลายรายการด้วยการติดตั้งใช้งานที่กำหนดค่าในลักษณะที่กำหนดเองได้ทีละรายการ

การปรับแต่งผู้เล่น

ตัวอย่างทั่วไปบางส่วนของการปรับแต่งโปรแกรมเล่นโดยการแทรกคอมโพเนนต์มีอธิบายไว้ด้านล่าง

การกำหนดค่าสแต็กเครือข่าย

เรามีหน้าเว็บเกี่ยวกับการปรับแต่งสแต็กเครือข่ายที่ ExoPlayer ใช้

การแคชข้อมูลที่โหลดจากเครือข่าย

ดูคำแนะนำสำหรับการแคชชั่วคราวขณะใช้งานและการดาวน์โหลดสื่อ

การปรับแต่งการโต้ตอบกับเซิร์ฟเวอร์

บางแอปอาจต้องการขัดขวางคำขอ HTTP และการตอบกลับ คุณอาจต้องแทรกส่วนหัวคำขอที่กำหนดเอง อ่านส่วนหัวการตอบกลับของเซิร์ฟเวอร์ แก้ไข URI ของคำขอ เป็นต้น เช่น แอปอาจตรวจสอบสิทธิ์ด้วยตนเองโดยการแทรกโทเค็นเป็นส่วนหัวเมื่อขอกลุ่มสื่อ

ตัวอย่างต่อไปนี้แสดงวิธีใช้ลักษณะการทํางานนี้ด้วยการแทรก DataSource.Factory ที่กําหนดเองลงใน DefaultMediaSourceFactory

Kotlin

val dataSourceFactory =
  DataSource.Factory {
    val dataSource = httpDataSourceFactory.createDataSource()
    // Set a custom authentication request header.
    dataSource.setRequestProperty("Header", "Value")
    dataSource
  }
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)
    )
    .build()

Java

DataSource.Factory dataSourceFactory =
    () -> {
      HttpDataSource dataSource = httpDataSourceFactory.createDataSource();
      // Set a custom authentication request header.
      dataSource.setRequestProperty("Header", "Value");
      return dataSource;
    };

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory))
        .build();

ในข้อมูลโค้ดด้านบน HttpDataSource ที่แทรกจะมีส่วนหัว "Header: Value" ในคำขอ HTTP ทุกรายการ ลักษณะการทำงานนี้จะคงที่สำหรับการโต้ตอบกับแหล่งที่มา HTTP ทุกครั้ง

หากต้องการใช้แนวทางที่ละเอียดยิ่งขึ้น คุณสามารถแทรกลักษณะการทำงานแบบทันท่วงทีได้โดยใช้ ResolvingDataSource ข้อมูลโค้ดต่อไปนี้แสดงวิธีแทรกส่วนหัวคำขอก่อนโต้ตอบกับแหล่งที่มา HTTP

Kotlin

val dataSourceFactory: DataSource.Factory =
  ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec ->
    // Provide just-in-time request headers.
    dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri))
  }

Java

    DataSource.Factory dataSourceFactory =
        new ResolvingDataSource.Factory(
            httpDataSourceFactory,
            // Provide just-in-time request headers.
            dataSpec -> dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)));

นอกจากนี้คุณยังใช้ ResolvingDataSource เพื่อทำการแก้ไข URI ได้ทันที ดังที่แสดงในข้อมูลโค้ดต่อไปนี้

Kotlin

val dataSourceFactory: DataSource.Factory =
  ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec ->
    // Provide just-in-time URI resolution logic.
    dataSpec.withUri(resolveUri(dataSpec.uri))
  }

Java

DataSource.Factory dataSourceFactory =
    new ResolvingDataSource.Factory(
        httpDataSourceFactory,
        // Provide just-in-time URI resolution logic.
        dataSpec -> dataSpec.withUri(resolveUri(dataSpec.uri)));

การปรับแต่งการจัดการข้อผิดพลาด

การใช้ LoadErrorHandlingPolicy ที่กําหนดเองช่วยให้แอปปรับแต่งวิธีตอบสนองของ ExoPlayer ต่อข้อผิดพลาดในการโหลดได้ ตัวอย่างเช่น แอปอาจต้องการแสดงข้อความ "ดำเนินการไม่สำเร็จ" อย่างรวดเร็วแทนที่จะลองใหม่หลายครั้ง หรืออาจต้องการปรับแต่งตรรกะการหยุดชั่วคราวซึ่งควบคุมระยะเวลาที่ผู้เล่นรอระหว่างการลองใหม่แต่ละครั้ง ข้อมูลโค้ดต่อไปนี้แสดงวิธีใช้ตรรกะการลดจำนวนที่กำหนดเอง

Kotlin

val loadErrorHandlingPolicy: LoadErrorHandlingPolicy =
  object : DefaultLoadErrorHandlingPolicy() {
    override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorInfo): Long {
      // Implement custom back-off logic here.
      return 0
    }
  }
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)
    )
    .build()

Java

LoadErrorHandlingPolicy loadErrorHandlingPolicy =
    new DefaultLoadErrorHandlingPolicy() {
      @Override
      public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) {
        // Implement custom back-off logic here.
        return 0;
      }
    };

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context)
                .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy))
        .build();

อาร์กิวเมนต์ LoadErrorInfo มีข้อมูลเพิ่มเติมเกี่ยวกับการโหลดที่ล้มเหลวเพื่อปรับแต่งตรรกะตามประเภทข้อผิดพลาดหรือคำขอที่ล้มเหลว

การปรับแต่ง Flag ของเครื่องมือแยก

แฟล็กตัวแยกใช้เพื่อปรับแต่งวิธีแยกรูปแบบแต่ละรายการจากสื่อแบบโปรเกรสซีฟได้ โดยสามารถตั้งค่าใน DefaultExtractorsFactory ที่ระบุให้กับ DefaultMediaSourceFactory ตัวอย่างต่อไปนี้ส่งผ่าน Flag ที่เปิดใช้การค้นหาตามดัชนีสำหรับสตรีม MP3

Kotlin

val extractorsFactory =
  DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING)
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(DefaultMediaSourceFactory(context, extractorsFactory))
    .build()

Java

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING);

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(new DefaultMediaSourceFactory(context, extractorsFactory))
        .build();

การเปิดใช้การกรอไปยังอัตราบิตคงที่

สำหรับสตรีม MP3, ADTS และ AMR คุณสามารถเปิดใช้การกรอวิดีโอโดยประมาณได้โดยใช้สมมติฐานเกี่ยวกับอัตราบิตคงที่ด้วย Flag FLAG_ENABLE_CONSTANT_BITRATE_SEEKING คุณตั้งค่า Flag เหล่านี้สําหรับเครื่องมือแยกแต่ละรายการได้โดยใช้เมธอด DefaultExtractorsFactory.setXyzExtractorFlags แต่ละรายการตามที่อธิบายไว้ข้างต้น หากต้องการเปิดใช้การกรอตามอัตราบิตคงที่สำหรับโปรแกรมแยกข้อมูลทั้งหมดที่รองรับ ให้ใช้ DefaultExtractorsFactory.setConstantBitrateSeekingEnabled

Kotlin

val extractorsFactory = DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)

Java

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);

จากนั้นจะสามารถแทรก ExtractorsFactory ผ่าน DefaultMediaSourceFactory ตามที่อธิบายไว้สำหรับการปรับแต่งแฟล็กเครื่องมือแยกข้อมูลด้านบน

การเปิดใช้การจัดคิวบัฟเฟอร์แบบไม่พร้อมกัน

การจัดคิวบัฟเฟอร์แบบอะซิงโครนัสเป็นการเพิ่มประสิทธิภาพในไปป์ไลน์การแสดงผลของ ExoPlayer ซึ่งจะทำงานกับอินสแตนซ์ MediaCodec ในโหมดอะซิงโครนัส และใช้เธรดเพิ่มเติมเพื่อกำหนดเวลาการถอดรหัสและการแสดงผลข้อมูล การเปิดใช้โปรแกรมนี้จะช่วยลดเฟรมที่กระตุกและเสียงที่ไม่สมบูรณ์ได้

อุปกรณ์ที่ใช้ Android 12 (API ระดับ 31) ขึ้นไปจะเปิดใช้การจัดคิวบัฟเฟอร์แบบแอซิงโครนัสโดยค่าเริ่มต้น และสามารถเปิดใช้ด้วยตนเองได้ตั้งแต่ Android 6.0 (API ระดับ 23) เป็นต้นไป ลองเปิดใช้ฟีเจอร์นี้สำหรับอุปกรณ์เฉพาะที่คุณสังเกตเห็นเฟรมที่ตกหล่นหรือเสียงที่ไม่ดี โดยเฉพาะอย่างยิ่งเมื่อเล่นเนื้อหาที่มีอัตราเฟรมสูงหรือได้รับการปกป้องด้วย DRM

ในกรณีที่ง่ายที่สุด คุณต้องแทรก DefaultRenderersFactory ลงในโปรแกรมเล่น ดังนี้

Kotlin

val renderersFactory = 
  DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing()
val exoPlayer = ExoPlayer.Builder(context, renderersFactory).build()

Java

DefaultRenderersFactory renderersFactory =
    new DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing();
ExoPlayer exoPlayer = new ExoPlayer.Builder(context, renderersFactory).build();

หากคุณสร้างอินสแตนซ์ของโปรแกรมแสดงผลโดยตรง ให้ส่ง AsynchronousMediaCodecAdapter.Factory ไปยังตัวสร้าง MediaCodecVideoRenderer และ MediaCodecAudioRenderer

การขัดจังหวะการเรียกใช้เมธอดด้วย ForwardingPlayer

คุณสามารถปรับแต่งลักษณะการทํางานบางอย่างของอินสแตนซ์ Player ได้โดยรวมไว้ในคลาสย่อยของ ForwardingPlayer และลบล้างเมธอดเพื่อทําสิ่งต่อไปนี้

  • เข้าถึงพารามิเตอร์ก่อนที่จะส่งไปยังผู้รับมอบสิทธิ์ Player
  • เข้าถึงค่าที่ส่งคืนจากผู้รับมอบสิทธิ์ Player ก่อนส่งคืน
  • นำวิธีการดังกล่าวมาใช้ใหม่โดยสมบูรณ์

เมื่อลบล้างเมธอด ForwardingPlayer คุณควรตรวจสอบว่าการใช้งานยังคงสอดคล้องกันและเป็นไปตามอินเทอร์เฟซ Player โดยเฉพาะเมื่อจัดการกับเมธอดที่มีไว้เพื่อให้มีการทำงานเหมือนกันหรือเกี่ยวข้องกัน เช่น

  • หากต้องการลบล้างการดำเนินการ "เล่น" ทั้งหมด คุณต้องลบล้างทั้ง ForwardingPlayer.play และ ForwardingPlayer.setPlayWhenReady เนื่องจากผู้เรียกจะคาดหวังว่าลักษณะการทำงานของเมธอดเหล่านี้จะเหมือนกันเมื่อ playWhenReady = true
  • หากต้องการเปลี่ยนการเพิ่มขึ้นของการกรอไปข้างหน้า คุณต้องลบล้างทั้ง ForwardingPlayer.seekForward เพื่อทำการกรอด้วยการเพิ่มที่กำหนดเอง และ ForwardingPlayer.getSeekForwardIncrement เพื่อรายงานการเพิ่มที่กำหนดเองที่ถูกต้องกลับไปยังผู้เรียก
  • หากต้องการควบคุมสิ่งที่ Player.Commands โฆษณาโดยอินสแตนซ์ผู้เล่น คุณต้องลบล้างทั้ง Player.getAvailableCommands() และ Player.isCommandAvailable() รวมถึงฟัง Callback ของ Player.Listener.onAvailableCommandsChanged() เพื่อรับการแจ้งเตือนการเปลี่ยนแปลงที่มาจากโปรแกรมเล่นที่สำคัญ

การปรับแต่ง MediaSource

ตัวอย่างด้านบนจะแทรกคอมโพเนนต์ที่กําหนดเองเพื่อใช้ระหว่างการเล่นออบเจ็กต์ MediaItem ทั้งหมดที่ส่งไปยังโปรแกรมเล่น หากจำเป็นต้องมีการปรับแต่งแบบละเอียด คุณก็สามารถแทรกคอมโพเนนต์ที่กำหนดเองลงในอินสแตนซ์ MediaSource แต่ละรายการได้ ซึ่งสามารถส่งไปยังโปรแกรมเล่นได้โดยตรง ตัวอย่างด้านล่างแสดงวิธีปรับแต่ง ProgressiveMediaSource ให้ใช้ DataSource.Factory, ExtractorsFactory และ LoadErrorHandlingPolicy ที่กำหนดเอง

Kotlin

val mediaSource =
  ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory)
    .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy)
    .createMediaSource(MediaItem.fromUri(streamUri))

Java

ProgressiveMediaSource mediaSource =
    new ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory)
        .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy)
        .createMediaSource(MediaItem.fromUri(streamUri));

การสร้างคอมโพเนนต์ที่กําหนดเอง

ไลบรารีนี้มีการใช้งานเริ่มต้นของคอมโพเนนต์ที่แสดงอยู่ที่ด้านบนของหน้านี้สำหรับกรณีการใช้งานทั่วไป ExoPlayer สามารถใช้คอมโพเนนต์เหล่านี้ได้ แต่ก็อาจสร้างขึ้นเพื่อใช้การติดตั้งใช้งานที่กําหนดเองได้หากต้องการลักษณะการทํางานที่ไม่เป็นไปตามมาตรฐาน กรณีการใช้งานบางส่วนสําหรับการติดตั้งใช้งานที่กําหนดเองมีดังนี้

  • Renderer – คุณอาจต้องติดตั้งใช้งาน Renderer ที่กําหนดเองเพื่อจัดการประเภทสื่อที่การติดตั้งใช้งานเริ่มต้นจากไลบรารีไม่รองรับ
  • TrackSelector – การใช้ TrackSelector ที่กำหนดเองจะช่วยให้นักพัฒนาแอปเปลี่ยนวิธีเลือกแทร็กที่ MediaSource แสดงเพื่อใช้งานโดย Renderer แต่ละรายการที่มีอยู่ได้
  • LoadControl – การใช้ LoadControl ที่กําหนดเองช่วยให้นักพัฒนาแอปเปลี่ยนนโยบายการบัฟเฟอร์ของโปรแกรมเล่นได้
  • Extractor – หากต้องการรองรับรูปแบบคอนเทนเนอร์ที่ไลบรารีไม่รองรับในขณะนี้ ให้ลองใช้คลาส Extractor ที่กําหนดเอง
  • MediaSource – การใช้คลาส MediaSource ที่กําหนดเองอาจเหมาะสมหากคุณต้องการรับตัวอย่างสื่อเพื่อส่งไปยังโปรแกรมแสดงผลด้วยวิธีที่กำหนดเอง หรือหากต้องการใช้ลักษณะการคอมโพส MediaSource ที่กําหนดเอง
  • MediaSource.Factory – การใช้ MediaSource.Factory ที่กําหนดเองช่วยให้แอปพลิเคชันปรับแต่งวิธีสร้าง MediaSource จาก MediaItem ได้
  • DataSource – แพ็กเกจ Upstream ของ ExoPlayer มีการใช้งาน DataSource หลายรายการสําหรับกรณีการใช้งานที่แตกต่างกันอยู่แล้ว คุณอาจต้องติดตั้งใช้งานคลาส DataSource ของคุณเองเพื่อโหลดข้อมูลด้วยวิธีอื่น เช่น ผ่านโปรโตคอลที่กำหนดเอง โดยใช้สแต็ก HTTP ที่กําหนดเอง หรือจากแคชถาวรที่กําหนดเอง

เมื่อสร้างคอมโพเนนต์ที่กำหนดเอง เราขอแนะนำดังนี้

  • หากคอมโพเนนต์ที่กําหนดเองต้องรายงานเหตุการณ์กลับไปที่แอป เราขอแนะนําให้ทําโดยใช้รูปแบบเดียวกับคอมโพเนนต์ ExoPlayer ที่มีอยู่ เช่น ใช้คลาส EventDispatcher หรือส่ง Handler พร้อมกับตัวฟังไปยังคอนสตรคเตอร์ของคอมโพเนนต์
  • เราขอแนะนำให้คอมโพเนนต์ที่กำหนดเองใช้โมเดลเดียวกันกับคอมโพเนนต์ ExoPlayer ที่มีอยู่เพื่อให้แอปกำหนดค่าใหม่ได้ในระหว่างการเล่น โดยคอมโพเนนต์ที่กำหนดเองควรใช้ PlayerMessage.Target และรับการเปลี่ยนแปลงการกำหนดค่าในเมธอด handleMessage โค้ดแอปพลิเคชันควรส่งการเปลี่ยนแปลงการกําหนดค่าโดยเรียกใช้เมธอด createMessage ของ ExoPlayer และกําหนดค่าข้อความ แล้วส่งไปยังคอมโพเนนต์โดยใช้ PlayerMessage.send การส่งข้อความที่จะส่งในเธรดการเล่นจะช่วยให้มั่นใจได้ว่าข้อความจะดำเนินการตามลำดับพร้อมกับการดำเนินการอื่นๆ ในโปรแกรมเล่น