การแทรกโฆษณา

ExoPlayer ใช้ได้กับการแทรกโฆษณาทั้งฝั่งไคลเอ็นต์และฝั่งเซิร์ฟเวอร์

การแทรกโฆษณาฝั่งไคลเอ็นต์

ในการแทรกโฆษณาฝั่งไคลเอ็นต์ โปรแกรมเล่นจะสลับระหว่างการโหลดสื่อจาก URL ที่ต่างกันขณะที่มีการเปลี่ยนระหว่างการเล่นเนื้อหาและโฆษณา ข้อมูล เกี่ยวกับโฆษณาจะโหลดแยกต่างหากจากสื่อ เช่น จาก VAST แบบ XML หรือ แท็กโฆษณา VMAP ซึ่งอาจรวมถึงตำแหน่งคิวโฆษณาที่สัมพันธ์กับจุดเริ่มต้นของ URL ของสื่อโฆษณาจริง และข้อมูลเมตา เช่น โฆษณาที่ระบุ ข้ามได้

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

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

ในการตั้งค่านี้ โปรแกรมเล่นจะดูแลการสลับระหว่างโฆษณาและเนื้อหา หมายความว่าแอปไม่ต้องดูแลการควบคุม โปรแกรมเล่นเบื้องหลัง/ที่ทำงานอยู่เบื้องหน้าสำหรับโฆษณาและเนื้อหา

เมื่อเตรียมวิดีโอเนื้อหาและแท็กโฆษณาเพื่อใช้กับการแทรกโฆษณาฝั่งไคลเอ็นต์ โฆษณาควรวางอยู่ในตัวอย่างการซิงค์ (คีย์เฟรม) ใน วิดีโอเนื้อหาเพื่อให้โปรแกรมเล่นสามารถเล่นเนื้อหาต่อได้อย่างราบรื่น

การรองรับโฆษณาแบบประกาศ

สามารถระบุ URI แท็กโฆษณาเมื่อสร้าง MediaItem ดังนี้

Kotlin

val mediaItem =
  MediaItem.Builder()
    .setUri(videoUri)
    .setAdsConfiguration(MediaItem.AdsConfiguration.Builder(adTagUri).build())
    .build()

Java

MediaItem mediaItem =
    new MediaItem.Builder()
        .setUri(videoUri)
        .setAdsConfiguration(
            new MediaItem.AdsConfiguration.Builder(adTagUri).build())
        .build();

หากต้องการเปิดการสนับสนุนโปรแกรมเล่นสำหรับรายการสื่อที่ระบุแท็กโฆษณา คุณต้องดำเนินการต่อไปนี้ สร้างและแทรก DefaultMediaSourceFactory ที่กำหนดค่าไว้ด้วย AdsLoader.Provider และ AdViewProvider เมื่อสร้างโปรแกรมเล่น

Kotlin

val mediaSourceFactory: MediaSource.Factory =
  DefaultMediaSourceFactory(context).setLocalAdInsertionComponents(adsLoaderProvider, playerView)
val player = ExoPlayer.Builder(context).setMediaSourceFactory(mediaSourceFactory).build()

Java

MediaSource.Factory mediaSourceFactory =
    new DefaultMediaSourceFactory(context)
        .setLocalAdInsertionComponents(adsLoaderProvider, /* adViewProvider= */ playerView);
ExoPlayer player =
    new ExoPlayer.Builder(context).setMediaSourceFactory(mediaSourceFactory).build();

จากภายใน DefaultMediaSourceFactory จะรวมแหล่งที่มาของสื่อเนื้อหาไว้ในไฟล์ AdsMediaSource AdsMediaSource จะได้รับ AdsLoader จาก AdsLoader.Providerและใช้เพื่อแทรกโฆษณาตามที่กำหนดโดยโฆษณาของรายการสื่อ แท็ก

PlayerView ของ ExoPlayer จะใช้ AdViewProvider ExoPlayer IMA ไลบรารีมี AdsLoader ที่ใช้งานง่าย ตามที่อธิบายไว้ด้านล่าง

เพลย์ลิสต์ที่มีโฆษณา

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

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

Kotlin

// Build the media items, passing the same ads identifier for both items,
// which means they share ad playback state so ads play only once.
val firstItem =
  MediaItem.Builder()
    .setUri(firstVideoUri)
    .setAdsConfiguration(MediaItem.AdsConfiguration.Builder(adTagUri).setAdsId(adTagUri).build())
    .build()
val secondItem =
  MediaItem.Builder()
    .setUri(secondVideoUri)
    .setAdsConfiguration(MediaItem.AdsConfiguration.Builder(adTagUri).setAdsId(adTagUri).build())
    .build()
player.addMediaItem(firstItem)
player.addMediaItem(secondItem)

Java

// Build the media items, passing the same ads identifier for both items,
// which means they share ad playback state so ads play only once.
MediaItem firstItem =
    new MediaItem.Builder()
        .setUri(firstVideoUri)
        .setAdsConfiguration(
            new MediaItem.AdsConfiguration.Builder(adTagUri).setAdsId(adTagUri).build())
        .build();
MediaItem secondItem =
    new MediaItem.Builder()
        .setUri(secondVideoUri)
        .setAdsConfiguration(
            new MediaItem.AdsConfiguration.Builder(adTagUri).setAdsId(adTagUri).build())
        .build();
player.addMediaItem(firstItem);
player.addMediaItem(secondItem);

ไลบรารี ExoPlayer IMA

ไลบรารี ExoPlayer IMA มี ImaAdsLoader ซึ่งทำให้ ผสานรวมการแทรกโฆษณาฝั่งไคลเอ็นต์ในแอปของคุณ ซึ่งรวมฟังก์ชันการทำงานของ IMA SDK ฝั่งไคลเอ็นต์เพื่อรองรับการแทรกโฆษณา VAST/VMAP สำหรับ วิธีการใช้ไลบรารี รวมถึงวิธีจัดการพื้นหลัง และเล่นต่อ โปรดดู README

แอปพลิเคชันสาธิตใช้ไลบรารี IMA และมีตัวอย่างหลายรายการ แท็กโฆษณา VAST/VMAP ในรายการตัวอย่าง

ข้อควรพิจารณาเกี่ยวกับ UI

PlayerView ซ่อนตัวควบคุมการขนส่งระหว่างการเล่นโฆษณาโดย แต่แอปสามารถเปิด/ปิดลักษณะการทำงานนี้ด้วยการเรียกใช้ setControllerHideDuringAds IMA SDK จะแสดงมุมมองเพิ่มเติมที่ด้านบนของ โปรแกรมเล่นขณะที่โฆษณากำลังเล่น (เช่น ลิงก์ "ข้อมูลเพิ่มเติม" และปุ่มข้าม หากมี)

IMA SDK อาจรายงานว่าโฆษณาถูกบดบังโดยการดูที่ระบุโดยแอปพลิเคชันหรือไม่ ที่แสดงที่ด้านบนของโปรแกรมเล่น แอปที่ต้องวางซ้อนมุมมองที่ สิ่งที่จำเป็นสำหรับการควบคุมการเล่นต้องลงทะเบียนภาพเหล่านั้นกับ IMA SDK เพื่อให้ สามารถละเว้นจากการคำนวณการมองเห็นโฆษณาได้ เมื่อใช้ PlayerView เป็น AdViewProvider ก็จะลงทะเบียนการวางซ้อนการควบคุมโดยอัตโนมัติ แอป ที่ใช้ UI โปรแกรมเล่นแบบกำหนดเองจะต้องลงทะเบียนมุมมองแบบวางซ้อนโดยการแสดงผลจาก AdViewProvider.getAdOverlayInfos

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับมุมมองซ้อนทับ โปรดดูที่ เปิด Measurement ใน IMA SDK

โฆษณาที่แสดงร่วม

แท็กโฆษณาบางแท็กมีโฆษณาที่แสดงร่วมเพิ่มเติมที่แสดงใน "ช่อง" ได้ ใน UI ของแอป สล็อตเหล่านี้ส่งผ่านได้ ImaAdsLoader.Builder.setCompanionAdSlots(slots) สำหรับข้อมูลเพิ่มเติม โปรดดู การเพิ่มโฆษณาที่แสดงร่วม

โฆษณาแบบสแตนด์อโลน

IMA SDK ออกแบบมาเพื่อแทรกโฆษณาลงในเนื้อหาสื่อ ไม่ใช่เพื่อการเล่น โฆษณาแบบสแตนด์อโลนด้วยตัวเอง จึงไม่รองรับการเล่นโฆษณาแบบสแตนด์อโลน ด้วยไลบรารี IMA เราขอแนะนำให้ใช้ SDK โฆษณาในอุปกรณ์เคลื่อนที่ของ Google แทน สำหรับกรณีการใช้งานนี้

การใช้ SDK โฆษณาของบุคคลที่สาม

หากคุณต้องโหลดโฆษณาผ่าน SDK โฆษณาของบุคคลที่สาม คุณควรตรวจสอบว่า โมเดลนี้จะมีการผสานรวม ExoPlayer อยู่แล้ว หากไม่เป็นเช่นนั้น การใช้ AdsLoader ที่รวม SDK โฆษณาของบุคคลที่สามเป็นวิธีที่แนะนำ เนื่องจากมีประโยชน์ของ AdsMediaSource ที่อธิบายไว้ข้างต้น ImaAdsLoader ทำหน้าที่เป็นตัวอย่างการใช้งาน

หรือจะใช้การรองรับเพลย์ลิสต์ของ ExoPlayer เพื่อสร้างเป็นลำดับก็ได้ ของโฆษณาและคลิปเนื้อหา

Kotlin

// A pre-roll ad.
val preRollAd = MediaItem.fromUri(preRollAdUri)
// The start of the content.
val contentStart =
  MediaItem.Builder()
    .setUri(contentUri)
    .setClippingConfiguration(ClippingConfiguration.Builder().setEndPositionMs(120000).build())
    .build()
// A mid-roll ad.
val midRollAd = MediaItem.fromUri(midRollAdUri)
// The rest of the content
val contentEnd =
  MediaItem.Builder()
    .setUri(contentUri)
    .setClippingConfiguration(ClippingConfiguration.Builder().setStartPositionMs(120000).build())
    .build()

// Build the playlist.
player.addMediaItem(preRollAd)
player.addMediaItem(contentStart)
player.addMediaItem(midRollAd)
player.addMediaItem(contentEnd)

Java

// A pre-roll ad.
MediaItem preRollAd = MediaItem.fromUri(preRollAdUri);
// The start of the content.
MediaItem contentStart =
    new MediaItem.Builder()
        .setUri(contentUri)
        .setClippingConfiguration(
            new ClippingConfiguration.Builder().setEndPositionMs(120_000).build())
        .build();
// A mid-roll ad.
MediaItem midRollAd = MediaItem.fromUri(midRollAdUri);
// The rest of the content
MediaItem contentEnd =
    new MediaItem.Builder()
        .setUri(contentUri)
        .setClippingConfiguration(
            new ClippingConfiguration.Builder().setStartPositionMs(120_000).build())
        .build();

// Build the playlist.
player.addMediaItem(preRollAd);
player.addMediaItem(contentStart);
player.addMediaItem(midRollAd);
player.addMediaItem(contentEnd);

การแทรกโฆษณาฝั่งเซิร์ฟเวอร์

ในการแทรกโฆษณาฝั่งเซิร์ฟเวอร์ (หรือที่เรียกว่าการแทรกโฆษณาแบบไดนามิกหรือ DAI) แอตทริบิวต์ สตรีมสื่อมีทั้งโฆษณาและเนื้อหา ไฟล์ Manifest สำหรับ DASH อาจชี้ไปยัง เนื้อหาและกลุ่มโฆษณา อาจอยู่ในช่วงเวลาที่แตกต่างกัน สำหรับ HLS โปรดดูที่ Apple ซึ่งเป็นเอกสารเกี่ยวกับการรวมโฆษณาไว้ในเพลย์ลิสต์

เมื่อใช้การแทรกโฆษณาฝั่งเซิร์ฟเวอร์ ลูกค้าอาจต้องแก้ไขสื่อ URL แบบไดนามิกเพื่อรับสตรีมที่ต่อกัน อาจต้องแสดงโฆษณาซ้อนทับ ใน UI หรืออาจต้องรายงานเหตุการณ์ไปยัง SDK โฆษณาหรือเซิร์ฟเวอร์โฆษณา

DefaultMediaSourceFactoryของ ExoPlayer จะมอบสิทธิ์งานทั้งหมดนี้ให้กับ การแทรกโฆษณาฝั่งเซิร์ฟเวอร์ MediaSource สำหรับ URI ที่ใช้รูปแบบ ssai://

Kotlin

val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setServerSideAdInsertionMediaSourceFactory(ssaiFactory)
    )
    .build()

Java

Player player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context)
                .setServerSideAdInsertionMediaSourceFactory(ssaiFactory))
        .build();

ไลบรารี ExoPlayer IMA

ไลบรารี ExoPlayer IMA มีค่า ImaServerSideAdInsertionMediaSource ซึ่งช่วยให้ง่ายต่อการผสานรวมกับสตรีมโฆษณาที่แทรกฝั่งเซิร์ฟเวอร์ของ IMA ใน แอป ซึ่งรวมฟังก์ชันการทำงานของ IMA DAI SDK สำหรับ Android และ ผสานรวมข้อมูลเมตาของโฆษณาที่ให้ไว้ลงในโปรแกรมเล่น ตัวอย่างเช่น วิธีนี้ทำให้ คุณใช้วิธีการอย่างเช่น Player.isPlayingAd() ฟังการเปลี่ยนภาพแบบโฆษณาเนื้อหา และให้โปรแกรมเล่นจัดการตรรกะการเล่นโฆษณา เช่น ข้ามโฆษณาที่เล่นไปแล้ว

หากต้องการใช้ชั้นเรียนนี้ คุณต้องตั้งค่า ImaServerSideAdInsertionMediaSource.AdsLoader และ ImaServerSideAdInsertionMediaSource.Factoryและเชื่อมต่อนาฬิกากับโปรแกรมเล่น โดยทำดังนี้

Kotlin

// MediaSource.Factory to load the actual media stream.
val defaultMediaSourceFactory = DefaultMediaSourceFactory(context)
// AdsLoader that can be reused for multiple playbacks.
val adsLoader =
  ImaServerSideAdInsertionMediaSource.AdsLoader.Builder(context, adViewProvider).build()
// MediaSource.Factory to create the ad sources for the current player.
val adsMediaSourceFactory =
  ImaServerSideAdInsertionMediaSource.Factory(adsLoader, defaultMediaSourceFactory)
// Configure DefaultMediaSourceFactory to create both IMA DAI sources and
// regular media sources. If you just play IMA DAI streams, you can also use
// adsMediaSourceFactory directly.
defaultMediaSourceFactory.setServerSideAdInsertionMediaSourceFactory(adsMediaSourceFactory)
// Set the MediaSource.Factory on the Player.
val player = ExoPlayer.Builder(context).setMediaSourceFactory(defaultMediaSourceFactory).build()
// Set the player on the AdsLoader
adsLoader.setPlayer(player)

Java

// MediaSource.Factory to load the actual media stream.
DefaultMediaSourceFactory defaultMediaSourceFactory = new DefaultMediaSourceFactory(context);
// AdsLoader that can be reused for multiple playbacks.
ImaServerSideAdInsertionMediaSource.AdsLoader adsLoader =
    new ImaServerSideAdInsertionMediaSource.AdsLoader.Builder(context, adViewProvider).build();
// MediaSource.Factory to create the ad sources for the current player.
ImaServerSideAdInsertionMediaSource.Factory adsMediaSourceFactory =
    new ImaServerSideAdInsertionMediaSource.Factory(adsLoader, defaultMediaSourceFactory);
// Configure DefaultMediaSourceFactory to create both IMA DAI sources and
// regular media sources. If you just play IMA DAI streams, you can also use
// adsMediaSourceFactory directly.
defaultMediaSourceFactory.setServerSideAdInsertionMediaSourceFactory(adsMediaSourceFactory);
// Set the MediaSource.Factory on the Player.
Player player =
    new ExoPlayer.Builder(context).setMediaSourceFactory(defaultMediaSourceFactory).build();
// Set the player on the AdsLoader
adsLoader.setPlayer(player);

โหลดคีย์เนื้อหา IMA หรือรหัสแหล่งที่มาของเนื้อหาและรหัสวิดีโอโดยการสร้าง URL ด้วย ImaServerSideAdInsertionUriBuilder:

Kotlin

val ssaiUri =
  ImaServerSideAdInsertionUriBuilder()
    .setAssetKey(assetKey)
    .setFormat(C.CONTENT_TYPE_HLS)
    .build()
player.setMediaItem(MediaItem.fromUri(ssaiUri))

Java

Uri ssaiUri =
    new ImaServerSideAdInsertionUriBuilder()
        .setAssetKey(assetKey)
        .setFormat(C.CONTENT_TYPE_HLS)
        .build();
player.setMediaItem(MediaItem.fromUri(ssaiUri));

สุดท้าย ปล่อยตัวโหลดโฆษณาเมื่อไม่ได้ใช้งานแล้ว โดยทำดังนี้

Kotlin

adsLoader.release()

Java

adsLoader.release();

ข้อควรพิจารณาเกี่ยวกับ UI

ข้อควรพิจารณาเกี่ยวกับ UI แบบเดียวกับการแทรกโฆษณาฝั่งไคลเอ็นต์ ใช้บังคับกับ การแทรกโฆษณาฝั่งเซิร์ฟเวอร์ด้วย

โฆษณาที่แสดงร่วม

แท็กโฆษณาบางแท็กมีโฆษณาที่แสดงร่วมเพิ่มเติมที่แสดงใน "ช่อง" ได้ ใน UI ของแอป สล็อตเหล่านี้ส่งผ่านได้ ImaServerSideAdInsertionMediaSource.AdsLoader.Builder.setCompanionAdSlots(slots) ดูข้อมูลเพิ่มเติมได้ที่การเพิ่มโฆษณาที่แสดงร่วม

การใช้ SDK โฆษณาของบุคคลที่สาม

หากคุณต้องโหลดโฆษณาโดยใช้ SDK โฆษณาของบุคคลที่สาม คุณควรตรวจสอบว่า โมเดลนี้จะมีการผสานรวม ExoPlayer อยู่แล้ว หากไม่ ขอแนะนำให้ ระบุ MediaSource ที่กำหนดเองซึ่งยอมรับ URI ที่มีรูปแบบ ssai:// คล้ายกับ ImaServerSideAdInsertionMediaSource

ตรรกะที่แท้จริงของการสร้างโครงสร้างโฆษณาสามารถมอบให้กับ วัตถุประสงค์ ServerSideAdInsertionMediaSource ซึ่งรวมสตรีม MediaSource แล้วให้ผู้ใช้ตั้งค่าและอัปเดต AdPlaybackState ที่แสดงโฆษณา ข้อมูลเมตา

สตรีมโฆษณาที่แทรกฝั่งเซิร์ฟเวอร์มักมีกิจกรรมตามกำหนดเวลาเพื่อแจ้งเตือนโปรแกรมเล่น เกี่ยวกับข้อมูลเมตาของโฆษณา โปรดดูรูปแบบที่รองรับเพื่อดูข้อมูลเกี่ยวกับ ExoPlayer รองรับรูปแบบข้อมูลเมตาที่มีกำหนดเวลา SDK โฆษณาที่กำหนดเอง MediaSource การติดตั้งใช้งานจะเฝ้าติดตามเหตุการณ์ข้อมูลเมตาที่มีกำหนดเวลาจากโปรแกรมเล่นได้โดยใช้ Player.Listener.onMetadata