선형 DAI 스트림의 시간이 지정된 메타데이터 처리

양방향 미디어 광고 (IMA) 동적 광고 삽입 (DAI) SDK는 스트림의 미디어 세그먼트 (인밴드 메타데이터) 또는 스트리밍 매니페스트 파일 (매니페스트 내 메타데이터)에 삽입된 메타데이터 정보를 사용하여 시청자 위치와 클라이언트 측 광고 이벤트를 추적합니다. 메타데이터는 재생 중인 스트림 유형에 따라 다양한 형식으로 전송됩니다.

동영상 플레이어는 시간 표시 메타데이터를 일괄적으로 수신합니다. 플레이어에 따라 메타데이터가 예약된 시간에 표시되거나 일괄적으로 표시될 수 있습니다. 각 메타데이터 문자열에는 트리거되어야 하는 시점에 연결된 프레젠테이션 타임스탬프 (PTS)가 있습니다.

앱은 메타데이터를 캡처하여 IMA DAI SDK로 전달해야 합니다. SDK는 다음과 같은 메서드를 제공하여 이 정보를 전달합니다.

onTimedMetadata

이 메서드는 처리할 준비가 된 메타데이터 문자열을 SDK에 전달합니다. 단일 인수를 사용합니다.

  • metadata: TXXX 키와 연결된 문자열 값(google_ 접두사 포함)이 포함된 객체입니다.
processMetadata

이 메서드는 지정된 PTS 후에 SDK에서 처리할 메타데이터 문자열을 예약합니다. 다음 인수를 사용합니다.

  • type: 처리 중인 이벤트 유형이 포함된 문자열입니다. 허용되는 값은 HLS의 경우 ID3, DASH의 경우 urn:google:dai:2018입니다.
  • data: 접두사가 google_인 문자열 값 또는 이 형식 ID3:u\0004u\000u\000u\0000TXXXu\0004u\000u\000u\0000google_xxxxxxxx을 따르는 바이트 배열입니다.
  • timestamp: 데이터를 처리해야 하는 타임스탬프(단위: 초)입니다.

IMA DAI SDK에서 지원하는 각 스트림 유형은 다음 섹션에 설명된 대로 고유한 형식의 시간 메타데이터를 사용합니다.

HLS MPEG2TS 스트림

MPEG2TS 세그먼트를 사용하는 선형 DAI HLS 스트림은 인밴드 ID3 태그를 통해 시간 표시 메타데이터를 동영상 플레이어에 전달합니다. 이러한 ID3 태그는 MPEG2TS 세그먼트 내에 삽입되며 맞춤 사용자 정의 텍스트 콘텐츠의 경우 TXXX 필드 이름이 지정됩니다.

Safari에서 재생

Safari는 ID3 태그를 숨겨진 트랙으로 자동으로 처리하므로 각 메타데이터를 처리하기 위해 적절한 시점에 cuechange 이벤트가 실행됩니다. 콘텐츠나 유형과 관계없이 모든 메타데이터를 IMA DAI SDK에 전달해도 됩니다. 관련 없는 메타데이터는 자동으로 필터링됩니다.

예를 들면 다음과 같습니다.

videoElement.textTracks.addEventListener('addtrack', (e) => {
  const track = e.track;
  if (track.kind === 'metadata') {
    track.mode = 'hidden';
    track.addEventListener('cuechange', () => {
      for (const cue of track.activeCues) {
        const metadata = {};
        metadata[cue.value.key] = cue.value.data;
        streamManager.onTimedMetadata(metadata);
      }
    });
  }
});
...

HLS.js

HLS.js는 FRAG_PARSING_METADATA 이벤트를 통해 ID3 태그를 샘플 배열로 일괄 제공합니다. HLS.js는 ID3 데이터를 바이트 배열에서 문자열로 변환하지 않으며 이벤트를 해당 PTS로 오프셋하지 않습니다. 샘플 데이터를 바이트 배열에서 문자열로 디코딩하거나 관련 없는 ID3 태그를 필터링할 필요가 없습니다. IMA DAI SDK가 이 디코딩 및 필터링을 자동으로 실행하기 때문입니다.

예를 들면 다음과 같습니다.

hls.on(Hls.Events.FRAG_PARSING_METADATA, (e, data) => {
  if (streamManager && data) {
    data.samples.forEach((sample) => {
      streamManager.processMetadata('ID3', sample.data, sample.pts);
    });
  }
});
...

HLS CMAF 스트림

'Common Media Application Framework'(CMAF)를 사용하는 선형 DAI HLS 스트림은 CMAF를 통한 ID3 표준에 따라 인밴드 eMSGv1 상자를 통해 시간 메타데이터를 전달합니다. 이러한 eMSG 상자는 각 미디어 세그먼트의 시작 부분에 삽입되며 각 ID3 eMSG에는 스트림의 마지막 불연속점에 관한 PTS가 포함됩니다.

HLS.js의 1.2.0 출시부터 두 추천 플레이어 모두 ID3가 ID3 태그인 것처럼 CMAF를 통해 사용자에게 전달합니다. 따라서 다음 예시는 HLS MPEG2TS 스트림과 동일합니다. 그러나 일부 플레이어에서는 이 작업이 지원되지 않을 수 있으므로 HLS CMAF 스트림 지원을 구현하려면 eMSG를 통해 ID3를 파싱하는 고유한 코드가 필요할 수 있습니다.

Safari에서 재생

Safari는 eMSG 메타데이터를 통한 ID3를 가상 ID3 이벤트로 취급하여 숨겨진 트랙으로 자동으로 일괄 제공하므로 각 메타데이터를 처리할 때 적절한 시점에 cuechange 이벤트가 실행됩니다. 타이밍과 관련이 있든 없든 모든 메타데이터를 IMA DAI SDK에 전달해도 됩니다. DAI와 관련 없는 메타데이터는 자동으로 필터링됩니다.

예를 들면 다음과 같습니다.

videoElement.textTracks.addEventListener('addtrack', (e) => {
  const track = e.track;
  if (track.kind === 'metadata') {
    track.mode = 'hidden';
    track.addEventListener('cuechange', () => {
      for (const cue of track.activeCues) {
        const metadata = {};
        metadata[cue.value.key] = cue.value.data;
        streamManager.onTimedMetadata(metadata);
      }
    });
  }
});
...

HLS.js

버전 1.2.0부터 HLS.js는 eMSG 메타데이터를 통한 ID3를 가상 ID3 이벤트로 취급하여 FRAG_PARSING_METADATA 이벤트를 통해 일괄적으로 샘플 배열로 제공합니다. HLS.js는 ID3 데이터를 바이트 배열에서 문자열로 변환하지 않으며 이벤트를 해당 PTS로 오프셋하지 않습니다. 샘플 데이터를 바이트 배열에서 문자열로 디코딩할 필요가 없습니다. IMA DAI SDK가 이 디코딩을 자동으로 실행하기 때문입니다.

예를 들면 다음과 같습니다.

hls.on(Hls.Events.FRAG_PARSING_METADATA, (e, data) => {
  if (streamManager && data) {
    data.samples.forEach((sample) => {
      streamManager.processMetadata('ID3', sample.data, sample.pts);
    });
  }
});
...

DASH 스트림

선형 DAI DASH 스트림은 맞춤 schemeIdUriurn:google:dai:2018를 사용하여 이벤트 스트림에서 매니페스트 이벤트로 메타데이터를 전달합니다. 이러한 스트림의 각 이벤트에는 텍스트 페이로드와 PTS가 포함됩니다.

DASH.js

Dash.js는 각 이벤트 스트림의 schemeIdUri 값을 따서 이름이 지정된 맞춤 이벤트 핸들러를 제공합니다. 이러한 맞춤 핸들러는 일괄적으로 실행되므로 개발자가 PTS 값을 처리하여 이벤트 시간을 적절하게 지정해야 합니다. IMA DAI SDK는 streamManager 메서드 processMetadata()를 사용하여 이를 처리할 수 있습니다.

예를 들면 다음과 같습니다.

const dash = dashjs.MediaPlayer().create();
dash.on('urn:google:dai:2018', (payload) => {
  const mediaId = payload.event.messageData;
  const pts = payload.event.calculatedPresentationTime;
  streamManager.processMetadata('urn:google:dai:2018', mediaId, pts);
});
...

Shaka Player

Shaka Player는 timelineregionenter 이벤트의 일부로 이벤트를 표시합니다. Shaka Player와의 형식 불일치로 인해 메타데이터 값은 세부정보 속성 eventNode.attributes['messageData']을 통해 원시로 검색해야 합니다.

예를 들면 다음과 같습니다.

player.addEventListener('timelineregionenter', function(event) {
  const detail = event.detail;
  if ( detail.eventNode.attributes &&
       detail.eventNode.attributes['messageData']) {
    const mediaId = detail.eventNode.attributes['messageData'];
    const pts = detail.startTime;
    streamManager.processMetadata("urn:google:dai:2018", mediaId, pts);
  }
});
...

광고 모음 게재

포드 게재의 경우 다음 기준에 따라 시간 메타데이터를 전달하는 다양한 구성이 있습니다.

  • 라이브 또는 VOD 스트림 유형
  • HLS 또는 DASH 스트림 형식
  • 사용 중인 플레이어 유형
  • 사용 중인 DAI 백엔드 유형

HLS 스트림 형식 (라이브 및 VOD 스트림, HLS.js 플레이어)

HLS.js 플레이어를 사용하는 경우 HLS.js FRAG_PARSING_METADATA 이벤트를 리슨하여 ID3 메타데이터를 가져와 StreamManager.processMetadata()를 사용하여 SDK에 전달합니다.

모든 항목이 로드되고 준비되면 동영상을 자동으로 재생하려면 HLS.js MANIFEST_PARSED 이벤트를 수신 대기하여 재생을 트리거합니다.

function loadStream(streamID) {
  hls.loadSource(url);
  hls.attachMedia(videoElement);
  
  // Timed metadata is passed HLS stream events to the streamManager.
  hls.on(Hls.Events.FRAG_PARSING_METADATA, parseID3Events);
  hls.on(Hls.Events.MANIFEST_PARSED, startPlayback);
}

function parseID3Events(event, data) {
  if (streamManager && data) {
    // For each ID3 tag in the metadata, pass in the type - ID3, the
    // tag data (a byte array), and the presentation timestamp (PTS).
    data.samples.forEach((sample) => {
      streamManager.processMetadata('ID3', sample.data, sample.pts);
    });
  }
}

function startPlayback() {
  console.log('Video Play');
  videoElement.play();
}

DASH.js (DASH 스트림 형식, 라이브 및 VOD 스트림 유형)

DASH.js 플레이어를 사용하는 경우 라이브 또는 VOD 스트림의 ID3 메타데이터를 리슨하려면 다른 문자열을 사용해야 합니다.

  • 라이브 스트림: 'https://developer.apple.com/streaming/emsg-id3'
  • VOD 스트림: 'urn:google:dai:2018'

StreamManager.processMetadata()를 사용하여 ID3 메타데이터를 SDK에 전달합니다.

모든 항목이 로드되고 준비되면 동영상 컨트롤을 자동으로 표시하려면 DASH.js MANIFEST_LOADED 이벤트를 리슨합니다.

const googleLiveSchema = 'https://developer.apple.com/streaming/emsg-id3';
const googleVodSchema = 'urn:google:dai:2018';
dashPlayer.on(googleLiveSchema, processMetadata);
dashPlayer.on(googleVodSchema, processMetadata);
dashPlayer.on(dashjs.MediaPlayer.events.MANIFEST_LOADED, loadlistener);

function processMetadata(metadataEvent) {
  const messageData = metadataEvent.event.messageData;
  const timestamp = metadataEvent.event.calculatedPresentationTime;

  // Use StreamManager.processMetadata() if your video player provides raw
  // ID3 tags, as with dash.js.
  streamManager.processMetadata('ID3', messageData, timestamp);
}

function loadlistener() {
  showControls();

  // This listener must be removed, otherwise it triggers as addional
  // manifests are loaded. The manifest is loaded once for the content,
  // but additional manifests are loaded for upcoming ad breaks.
  dashPlayer.off(dashjs.MediaPlayer.events.MANIFEST_LOADED, loadlistener);
}

라이브 스트림이 포함된 Shaka Player (DASH 스트림 형식)

라이브 스트림 재생에 Shaka 플레이어를 사용하는 경우 'emsg' 문자열을 사용하여 메타데이터 이벤트를 수신 대기합니다. 그런 다음 StreamManager.onTimedMetadata() 호출에서 이벤트 메시지 데이터를 사용합니다.

shakaPlayer.addEventListener('emsg', (event) => onEmsgEvent(event));

function onEmsgEvent(metadataEvent) {
  // Use StreamManager.onTimedMetadata() if your video player provides
  // processed metadata, as with Shaka player livestreams.
  streamManager.onTimedMetadata({'TXXX': metadataEvent.detail.messageData});
}

VOD 스트림이 포함된 Shaka Player (DASH 스트림 형식)

VOD 스트림 재생에 Shaka 플레이어를 사용하는 경우 'timelineregionenter' 문자열을 사용하여 메타데이터 이벤트를 수신 대기합니다. 그런 다음 'urn:google:dai:2018' 문자열과 함께 StreamManager.processMetadata() 호출에서 이벤트 메시지 데이터를 사용합니다.

shakaPlayer.addEventListener('timelineregionenter', (event) => onTimelineEvent(event));

function onTimelineEvent(metadataEvent) {
  const detail = metadataEvent.detail;
  if ( detail.eventElement.attributes &&
       detail.eventElement.attributes['messageData'] &&
       detail.eventElement.attributes['messageData'].value ) {
        const mediaId = detail.eventElement.attributes['messageData'].value;
        const pts = detail.startTime;
        // Use StreamManager.processMetadata() if your video player provides raw
        // ID3 tags, as with Shaka player VOD streams.
        streamManager.processMetadata('urn:google:dai:2018', mediaId, pts);
       }
}