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