광고 삽입

ExoPlayer는 클라이언트 측 광고 삽입과 서버 측 광고 삽입에 모두 사용할 수 있습니다.

클라이언트 측 광고 삽입

클라이언트 측 광고 삽입에서 플레이어는 콘텐츠 재생과 광고 재생 간에 전환할 때 여러 URL에서 미디어를 로드하는 방식으로 전환합니다. 광고에 관한 정보는 XML VAST 또는 VMAP 광고 태그처럼 미디어와 별도로 로드됩니다. 여기에는 콘텐츠 시작을 기준으로 한 광고 큐 위치, 실제 광고 미디어 URI, 메타데이터(예: 특정 광고의 건너뛸 수 있는지 여부)가 포함될 수 있습니다.

클라이언트 측 광고 삽입에 ExoPlayer의 AdsMediaSource를 사용하는 경우 플레이어에는 재생할 광고에 관한 정보가 있습니다. 이렇게 하면 다음과 같은 몇 가지 장점이 있습니다.

  • 플레이어는 API를 사용하여 광고와 관련된 메타데이터와 기능을 노출할 수 있습니다.
  • ExoPlayer UI 구성요소는 광고 위치에 대한 마커를 자동으로 표시할 수 있으며 광고 재생 여부에 따라 동작을 변경할 수 있습니다.
  • 내부적으로 플레이어는 광고와 콘텐츠 간의 전환 시 일관된 버퍼를 유지할 수 있습니다.

이 설정에서 플레이어는 광고와 콘텐츠 간의 전환을 처리합니다. 즉, 앱은 광고와 콘텐츠를 위해 별도의 여러 백그라운드/포그라운드 플레이어를 제어하지 않아도 됩니다.

클라이언트 측 광고 삽입과 함께 사용할 콘텐츠 동영상 및 광고 태그를 준비할 때는 플레이어가 콘텐츠 재생을 원활하게 재개할 수 있도록 콘텐츠 동영상의 동기화 샘플 (키프레임)에 광고를 배치하는 것이 이상적입니다.

선언적 광고 지원

광고 태그 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();

광고 태그를 지정하는 미디어 항목에서 플레이어 지원을 사용 설정하려면 플레이어를 만들 때 AdsLoader.ProviderAdViewProvider로 구성된 DefaultMediaSourceFactory를 빌드하고 삽입해야 합니다.

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에 래핑합니다. AdsMediaSourceAdsLoader.Provider에서 AdsLoader를 가져와서 미디어 항목의 광고 태그에 정의된 대로 광고를 삽입하는 데 사용합니다.

ExoPlayer의 PlayerViewAdViewProvider를 구현합니다. ExoPlayer IMA 라이브러리는 아래 설명과 같이 사용하기 쉬운 AdsLoader를 제공합니다.

광고가 있는 재생목록

여러 미디어 항목이 포함된 재생목록을 재생할 때 기본 동작은 각 미디어 ID, 콘텐츠 URI, 광고 태그 URI 조합에 대해 광고 태그를 요청하고 광고 재생 상태를 한 번씩 저장하는 것입니다. 즉, 광고 태그 URI가 일치하더라도 사용자는 미디어 ID 또는 콘텐츠 URI가 다른 광고가 포함된 모든 미디어 항목의 광고를 보게 됩니다. 미디어 항목이 반복되면 사용자는 상응하는 광고를 한 번만 보게 됩니다. 광고 재생 상태는 광고가 재생되었는지 여부를 저장하므로 광고가 처음 노출된 후에는 건너뜁니다.

객체 동등성에 따라 특정 미디어 항목의 광고 재생 상태가 연결되는 불투명한 광고 식별자를 전달하여 이 동작을 맞춤설정할 수 있습니다. 다음은 광고 재생 상태가 광고 태그 URI를 광고 식별자로 전달하여 미디어 ID와 광고 태그 URI의 조합이 아닌 광고 태그 URI에만 연결되는 예입니다. 그 결과 광고가 한 번만 로드되고 사용자가 재생목록을 처음부터 끝까지 재생할 때 두 번째 항목에 광고가 표시되지 않습니다.

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 광고 삽입을 지원합니다. 백그라운드 재생 및 재생 재개를 처리하는 방법을 비롯하여 라이브러리 사용 방법에 관한 안내는 리드미를 참고하세요.

데모 애플리케이션은 IMA 라이브러리를 사용하며 샘플 목록에 몇 가지 샘플 VAST/VMAP 광고 태그를 포함합니다.

UI 고려사항

PlayerView는 기본적으로 광고가 재생되는 동안 전송 컨트롤을 숨겨 주지만 앱은 setControllerHideDuringAds를 호출하여 이 동작을 전환할 수 있습니다. IMA SDK는 광고가 재생되는 동안 플레이어 상단에 추가 보기를 표시합니다(예: '추가 정보' 링크 및 건너뛰기 버튼(해당하는 경우)).

IMA SDK는 플레이어 위에 렌더링된 애플리케이션에서 제공하는 보기로 광고가 가려지는지 여부를 보고할 수 있습니다. 재생을 제어하는 데 필수적인 보기를 오버레이해야 하는 앱은 조회가능성 계산에서 생략할 수 있도록 IMA SDK에 보기를 등록해야 합니다. PlayerViewAdViewProvider로 사용하면 컨트롤 오버레이를 자동으로 등록합니다. 맞춤 플레이어 UI를 사용하는 앱은 AdViewProvider.getAdOverlayInfos에서 오버레이 뷰를 반환하여 등록해야 합니다.

오버레이 보기에 관한 자세한 내용은 IMA SDK에서 Open Measurement를 참고하세요.

컴패니언 광고

일부 광고 태그에는 앱 UI의 '슬롯'에 표시할 수 있는 추가 컴패니언 광고가 포함되어 있습니다. 이러한 슬롯은 ImaAdsLoader.Builder.setCompanionAdSlots(slots)를 통해 전달될 수 있습니다. 자세한 내용은 컴패니언 광고 추가를 참고하세요.

독립형 광고

IMA SDK는 독립형 광고 자체를 재생하는 것이 아니라 미디어 콘텐츠에 광고를 삽입하기 위해 설계되었습니다. 따라서 독립형 광고의 재생은 IMA 라이브러리에서 지원되지 않습니다. 이 사용 사례에는 Google 모바일 광고 SDK를 대신 사용하는 것이 좋습니다.

서드 파티 광고 SDK 사용

서드 파티 광고 SDK를 통해 광고를 로드해야 하는 경우 이미 ExoPlayer 통합을 제공하는지 확인하는 것이 좋습니다. 그렇지 않은 경우 서드 파티 광고 SDK를 래핑하는 맞춤 AdsLoader를 구현하는 것이 좋습니다. 위에서 설명한 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라고도 함)에서는 미디어 스트림에 광고와 콘텐츠가 모두 포함됩니다. DASH 매니페스트는 콘텐츠와 광고 세그먼트를 둘 다 가리킬 수 있으며, 서로 다른 기간에 있을 수도 있습니다. HLS의 경우 재생목록에 광고 통합에 관한 Apple 문서를 참고하세요.

서버 측 광고 삽입을 사용하는 경우 클라이언트는 병합된 스트림을 가져오기 위해 미디어 URL을 동적으로 확인해야 하거나 UI에 광고 오버레이를 표시하거나 광고 SDK 또는 광고 서버에 이벤트를 보고해야 할 수 있습니다.

ExoPlayer의 DefaultMediaSourceFactoryssai:// 스키마를 사용하여 URI의 서버 측 광고 삽입 MediaSource에 이러한 모든 작업을 위임할 수 있습니다.

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의 서버 측에서 삽입된 광고 스트림과 쉽게 통합할 수 있습니다. 이 라이브러리는 Android용 IMA DAI SDK의 기능을 래핑하고 제공된 광고 메타데이터를 플레이어에 완전히 통합합니다. 예를 들어 Player.isPlayingAd()와 같은 메서드를 사용하고, 콘텐츠 광고 전환을 수신 대기하고, 플레이어가 이미 재생된 광고 건너뛰기와 같은 광고 재생 로직을 처리하도록 할 수 있습니다.

이 클래스를 사용하려면 ImaServerSideAdInsertionMediaSource.AdsLoaderImaServerSideAdInsertionMediaSource.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);

ImaServerSideAdInsertionUriBuilder로 URL을 작성하여 IMA 애셋 키 또는 콘텐츠 소스 ID 및 동영상 ID를 로드합니다.

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 통합을 제공하는지 확인하는 것이 좋습니다. 그렇지 않은 경우 ImaServerSideAdInsertionMediaSourcessai:// 스키마가 있는 URI를 허용하는 커스텀 MediaSource를 제공하는 것이 좋습니다.

광고 구조를 만드는 실제 로직은 MediaSource 스트림을 래핑하고 사용자가 광고 메타데이터를 나타내는 AdPlaybackState를 설정 및 업데이트할 수 있게 해주는 범용 ServerSideAdInsertionMediaSource에 위임할 수 있습니다.

서버 측에서 삽입된 광고 스트림에는 플레이어에게 광고 메타데이터에 대해 알리는 시간 지정 이벤트가 포함되는 경우가 많습니다. ExoPlayer에서 지원되는 시점이 지정된 메타데이터 형식에 관한 자세한 내용은 지원되는 형식을 참고하세요. 맞춤 광고 SDK MediaSource 구현은 Player.Listener.onMetadata를 사용하여 플레이어에서 시간이 지정된 메타데이터 이벤트를 수신 대기할 수 있습니다.