กำหนดเอง

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

  • MediaSource อินสแตนซ์ที่กำหนดสื่อที่จะเล่น โหลดสื่อ และ ที่สามารถอ่านสื่อที่โหลดได้ สร้างอินสแตนซ์ MediaSource แล้ว จาก MediaItem โดย MediaSource.Factory ภายในโปรแกรมเล่น นอกจากนี้ยังสามารถ จะส่งไปยังโปรแกรมเล่นโดยตรงโดยใช้ 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 ตอบสนองต่อข้อผิดพลาดในการโหลด เช่น แอปอาจต้องการล้มเหลวอย่างรวดเร็ว แทนที่จะต้องลองใหม่หลายๆ ครั้ง หรืออาจต้องปรับแต่งตรรกะการย้อนกลับ ควบคุมระยะเวลาที่โปรแกรมเล่นต้องรอระหว่างการลองใหม่แต่ละครั้ง ข้อมูลโค้ดต่อไปนี้ แสดงวิธีใช้งานตรรกะ Backoff ที่กำหนดเอง

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 มีข้อมูลเพิ่มเติมเกี่ยวกับการโหลดที่ล้มเหลวไปยัง ปรับแต่งตรรกะตามประเภทข้อผิดพลาดหรือคำขอที่ล้มเหลว

การปรับแต่งแฟล็กเครื่องมือแยก

แฟล็กเครื่องมือแยกข้อมูลสามารถใช้เพื่อปรับแต่งวิธีแยกไฟล์แต่ละรูปแบบได้ จากสื่อแบบโปรเกรสซีฟ สามารถตั้งค่าใน 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_ENABLE_CONSTANT_BITRATE_SEEKING ค่าสถานะเหล่านี้สามารถตั้งค่าสําหรับเครื่องมือแยกข้อมูลแต่ละรายการได้โดยใช้ 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
  • หากต้องการเปลี่ยนการกรอไปข้างหน้า คุณจะต้องลบล้างทั้ง 2 ส่วน ForwardingPlayer.seekForward เพื่อทำการค้นหาด้วยโฆษณาที่คุณกำหนดเอง เพิ่มขึ้น และ ForwardingPlayer.getSeekForwardIncrement เพื่อรายงาน ส่วนเพิ่มที่ปรับแต่งเองอย่างถูกต้องกลับไปยังผู้โทร
  • หากคุณต้องการควบคุมสิ่งที่ผู้เล่นโฆษณา Player.Commands ไว้ คุณต้องลบล้างทั้ง Player.getAvailableCommands() และ Player.isCommandAvailable()และยังฟัง 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));

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

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

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

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

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