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

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

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

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

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

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