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

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 จึงไม่รองรับการเล่นโฆษณาแบบสแตนด์อโลน เราขอแนะนำให้ใช้ Google Mobile Ads SDK แทนสำหรับกรณีการใช้งานนี้

การใช้ 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

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

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

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