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

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

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

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

เมื่อใช้ 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 ไลบรารี IMA ของ ExoPlayer มี 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 มาพร้อมกับ HlsInterstitialsAdsLoader ที่รองรับโฆษณาที่กำหนดไว้ใน เพลย์ลิสต์ HLS เพื่อแทรกในฝั่งไคลเอ็นต์โดยอัตโนมัติ ดูส่วนเกี่ยวกับ HlsInterstitialsAdsLoader ในหน้า HLS

ไลบรารี IMA ของ ExoPlayer

ไลบรารี 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

ดูข้อมูลเพิ่มเติมเกี่ยวกับมุมมองซ้อนทับได้ที่การวัดแบบเปิดใน IMA SDK

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

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

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

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

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

หากต้องการโหลดโฆษณาผ่าน 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(
      MediaItem.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(
      MediaItem.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 MediaItem.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 MediaItem.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();

ไลบรารี IMA ของ ExoPlayer

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

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

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