SSAI (Server Side Ads Insert) 적용하기 - 1 (AWS S3, MediaConvert, MediaPackage, MediaTailor, CloudFront)

예진욱·2025년 3월 2일

SSAI

목록 보기
1/2
post-thumbnail

개요

Youtube - 티빙의 AWS Elemental 서비스 활용기

위 영상을 보고 영감을 받아 영상 송출에 서버 사이드 영상을 삽입하는 과정을 직접 실습해보고자 한다.

사용되는 서비스는 아래와 같다.

  • AWS S3 (VOD일 경우, 1번 포스팅에서 진행)
  • AWS MediaConvert (VOD일 경우, 1번 포스팅에서 진행)
  • AWS MediaLive (실시간 스트리밍일 경우, 2번 포스팅에서 진행)
  • AWS MediaPackage
  • AWS MediaTailor
  • AWS CloudFront
  • ADS (자체구축 또는 Google Ad Service, 현 실습에서는 샘플만 사용)





사전지식


SSAI?

SSAI (Server-Side Ad Insertion)는 서버 측 광고 삽입 기술을 의미하고,
영상 스트리밍 시 서버에서 광고를 콘텐츠에 직접 삽입하여 재생하는 방식이다.

클라이언트 측에서 광고를 삽입하는 CSAI (Client-Side Ad Insertion)와 대비된다.

광고 차단에 강하며, 사용자 경험을 향상시키는 장점을 가진다.


영상을 재생하는 방식

기존의 영상 재생 방식은 클라이언트가 서버로부터 영상 콘텐츠를 다운로드하여 재생하는 방식이다.

SSAI 방식은 서버에서 영상 콘텐츠와 광고를 하나의 스트림으로 결합하여 클라이언트에게 제공한다.

클라이언트는 광고가 삽입된 통합 스트림을 끊김 없이 재생할 수 있다.


HLS?

HLS (HTTP Live Streaming)는 Apple에서 개발한 HTTP 기반의 영상 스트리밍 프로토콜이다.

영상을 작은 세그먼트 단위로 분할하여 전송하고, 클라이언트에서 이를 순차적으로 재생하는 방식이다.

다양한 기기 및 플랫폼에서 널리 사용되며, 적응형 비트레이트 스트리밍을 지원한다.


Bitrate?

비트레이트는 디지털 데이터의 전송 속도를 나타내는 단위이다.
영상 스트리밍에서는 초당 전송되는 데이터의 양 (bits per second, bps) 을 의미한다.

높은 비트레이트는 더 많은 데이터를 전송하므로 고화질 영상을 제공하지만, 네트워크 대역폭을 더 많이 사용한다.

HLS 스트리밍은 적응형 비트레이트 스트리밍을 지원해,
네트워크 환경에 따라 자동으로 비트레이트를 조절하여 최적의 재생 환경을 제공한다.


Manifest, Segment?

매니페스트 (Manifest):
영상 스트림에 대한 정보를 담고 있는 파일이다.
재생 가능한 세그먼트 목록, 비트레이트 정보, 광고 정보 등을 포함한다.
HLS에서는 .m3u8 형식을 사용한다.

세그먼트 (Segment):
영상을 작은 단위로 분할한 파일이다.
클라이언트에서 순차적으로 다운로드하여 재생하며,
HLS에서는 .ts (Transport Stream) 형식을 사용한다.


.m3u8 응답 예시

#EXTM3U

  • m3u8 파일의 시작을 알리는 태그

#EXT-X-VERSION:4

  • HLS 프로토콜 버전

#EXT-X-MEDIA-SEQUENCE:0

  • 첫 번째 세그먼트의 순번

#EXT-X-TARGETDURATION:10

  • 세그먼트의 최대 재생 시간

#EXTINF:10.0,

  • 세그먼트의 재생 시간

segment0.ts

  • 세그먼트 파일의 이름

#EXT-X-ENDLIST

  • 재생목록의 끝, VOD (Video On Demand) 에서 사용
#EXTM3U
#EXT-X-VERSION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-TARGETDURATION:10
#EXTINF:10.0,
segment0.ts
#EXTINF:10.0,
segment1.ts
#EXTINF:10.0,
segment2.ts
#EXT-X-ENDLIST

SSAI Manifest 예시

SSAI 방식은 서버에서 영상 콘텐츠와 광고를 결합하여 하나의 스트림으로 생성한다.
광고 삽입 시점에 광고 세그먼트를 콘텐츠 세그먼트 사이에 삽입하고, 매니페스트를 수정한다.

#EXTINF:5.0,:

  • ad_segment1.ts 의 재생 시간을 5초

ad_segment1.ts:

  • 광고 세그먼트 파일의 이름

#EXTINF:5.0,:

  • ad_segment2.ts 의 재생 시간을 5초

ad_segment2.ts:

  • 광고 세그먼트 파일의 이름

#EXTINF:10.0,:

  • segment1.ts 의 재생 시간을 10초

segment1.ts:

  • 실제 영상 데이터를 포함하는 세그먼트 파일의 이름
#EXTM3U
#EXT-X-VERSION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-TARGETDURATION:10
#EXTINF:10.0,
segment0.ts
#EXTINF:5.0,
ad_segment1.ts
#EXTINF:5.0,
ad_segment2.ts
#EXTINF:10.0,
segment1.ts
#EXT-X-ENDLIST

VAST?

VAST (Video Ad Serving Template)는 디지털 비디오 광고를 위한 표준 XML 기반 템플릿이다.
광고 서버와 비디오 플레이어 간의 통신을 표준화하여 다양한 플랫폼에서 일관된 광고 재생을 가능하게 한다.
IAB (Interactive Advertising Bureau)에서 개발 및 관리하며, 온라인 비디오 광고 업계의 핵심 기술 표준이다.

VAST 의 주요 기능

  • 광고 정보 전달
    광고 소재 (비디오 파일, 이미지 등)의 위치, 재생 시간, 추적 URL 등의 정보를 비디오 플레이어에 전달한다.
    다양한 광고 유형 (선형, 비선형, 컴패니언 등)을 지원한다.
  • 광고 추적
    광고 노출, 클릭, 완료 등 다양한 사용자 상호작용을 추적하기 위한 동작을 제공하고, 광고 효과 측정 및 분석을 가능하게 한다.
  • 광고 재생 제어
    비디오 플레이어가 광고를 어떻게 재생해야 하는지에 대한 정보를 제공한다.
    광고 스킵 가능 여부, 자동 재생 여부 등을 제어한다.

VAST 구성 요소

  • XML 템플릿
    광고 정보를 담고 있는 XML 형식의 파일
    비디오 플레이어가 해석하여 광고를 재생하는 데 사용된다.
  • Ad Element
    개별 광고를 정의하는 요소
    선형 광고, 비선형 광고, 컴패니언 광고 등 다양한 유형의 광고를 포함할 수 있다.
  • Linear Element
    선형 광고 (비디오 콘텐츠 전후 또는 중간에 재생되는 광고)를 정의하는 요소
    광고 소재, 재생 시간, 추적 URL 등을 포함한다.
  • NonLinearAds Element
    비선형 광고 (비디오 콘텐츠 위에 오버레이되는 광고)를 정의하는 요소
    광고 소재, 크기, 위치 등을 포함한다.
  • CompanionAds Element
    컴패니언 광고 (비디오 플레이어 주변에 표시되는 광고)를 정의하는 요소
    광고 소재, 크기, 위치 등을 포함한다.

VAST 응답 예시

<VAST version="4.2">: VAST 버전 4.2를 사용함
<Ad id="12345">: 광고의 고유 ID
<InLine>: 광고 정보를 직접 포함하는 인라인 광고
<AdSystem>: 광고를 제공하는 광고 시스템
<AdTitle>: 광고 제목
<Impression>: 광고 노출 추적 URL
<Creatives>: 광고 소재를 포함하는 컨테이너
<Creative id="67890">: 광고 소재의 고유 ID
<Linear>: 선형 광고
<Duration>: 광고 재생 시간
<MediaFiles>: 광고 소재 파일을 포함하는 컨테이너
<MediaFile>: 광고 소재 파일의 정보

  • delivery: 전송 방식 (progressive)
  • type: 파일 형식 (video/mp4)
  • width, height: 영상의 크기

<VideoClicks>: 비디오 클릭 관련 정보를 포함하는 컨테이너
<ClickThrough>: 클릭 시 이동할 랜딩 페이지 URL
<ClickTracking>: 클릭 추적 URL
<TrackingEvents>: 광고 재생 중 발생하는 이벤트를 추적하는 컨테이너
<Tracking event="...">: 특정 이벤트 발생 시 호출될 추적 URL

  • start: 광고 시작 시
  • firstQuartile: 25% 재생 시
  • midpoint: 50% 재생 시
  • thirdQuartile: 75% 재생 시
  • complete: 광고 완료 시
<VAST version="4.2">
  <Ad id="12345">
    <InLine>
      <AdSystem>Example Ad Server</AdSystem>
      <AdTitle>Example Linear Ad</AdTitle>
      <Impression><![CDATA[https://example-ad-server.com/impression/12345]]></Impression>
      <Creatives>
        <Creative id="67890">
          <Linear>
            <Duration>00:00:30</Duration>
            <MediaFiles>
              <MediaFile id="1" delivery="progressive" type="video/mp4" width="1280" height="720">
                <![CDATA[https://example-ad-server.com/video/12345.mp4]]>
              </MediaFile>
            </MediaFiles>
            <VideoClicks>
              <ClickThrough><![CDATA[https://example-advertiser.com/landing?ad=12345]]></ClickThrough>
              <ClickTracking><![CDATA[https://example-ad-server.com/click/12345]]></ClickTracking>
            </VideoClicks>
            <TrackingEvents>
              <Tracking event="start"><![CDATA[https://example-ad-server.com/tracking/start/12345]]></Tracking>
              <Tracking event="firstQuartile"><![CDATA[https://example-ad-server.com/tracking/firstQuartile/12345]]></Tracking>
              <Tracking event="midpoint"><![CDATA[https://example-ad-server.com/tracking/midpoint/12345]]></Tracking>
              <Tracking event="thirdQuartile"><![CDATA[https://example-ad-server.com/tracking/thirdQuartile/12345]]></Tracking>
              <Tracking event="complete"><![CDATA[https://example-ad-server.com/tracking/complete/12345]]></Tracking>
            </TrackingEvents>
          </Linear>
        </Creative>
      </Creatives>
    </InLine>
  </Ad>
</VAST>


AWS S3 (Simple Storage Service)

객체 스토리지 서비스.
다양한 유형의 비정형 데이터를 저장 및 관리한다.
99.999999999%의 높은 내구성 및 무제한 확장성 제공한다.
미디어 파일 저장 및 배포에 최적화된 스토리지 솔루션이고 AWS 의 대표적인 서비스 중 하나이다.


AWS MediaLive

방송 수준의 라이브 비디오 인코딩 서비스.
실시간 방송 스트리밍 생성 및 전송이 주 사용 목적이고,
다양한 입력 소스(SDI, RTP, HLS 등) 및 출력 형식(HLS, DASH 등) 지원한다.
안정적인 라이브 스트리밍 제공 및 방송 품질의 출력 생성한다고 한다.


AWS MediaConvert

방송 품질의 파일 기반 비디오 트랜스코딩 서비스.
다양한 해상도, 비트레이트 및 형식으로 비디오 파일을 변환한다.
고품질 비디오 출력 생성 및 다양한 기기 호환성을 확보하며,
VOD 콘텐츠 제작, 편집 및 배포에 활용한다.


AWS MediaPackage

비디오 패키징 및 원본 서버 서비스.
다양한 스트리밍 프로토콜(HLS, DASH, CMAF 등)로 비디오 콘텐츠 패키징.
적응형 비트레이트 스트리밍(ABR) 지원 및 DRM 암호화를 제공해 S3 보다 안정적으로 사용이 가능하다.
안정적인 비디오 스트리밍 및 다양한 기기 호환성을 제공한다.


AWS MediaTailor

서버 측 광고 삽입(SSAI, Server Side Ad Insert) 서비스.
비디오 스트림에 개인화된 광고 삽입 및 광고 추적 기능을 제공한다.
ADS 와 연동해 광고 시청률 및 수익 증대, 광고 차단 방지 및 사용자 경험을 향상시킨다.


AWS CloudFront

글로벌 콘텐츠 전송 네트워크(CDN) 서비스.
전 세계에 분산된 엣지 로케이션에 콘텐츠 캐싱 및 전송한다.
빠른 콘텐츠 전송 및 지연 시간 감소, 글로벌 사용자에게 안정적인 스트리밍을 제공한다.
또한 DDoS 공격 방어 및 보안 기능을 제공해 무리없이 사용 가능하다.


ADS

광고 결정 및 관리 서비스.
사용자 및 콘텐츠에 맞는 광고 선택 및 광고 수익 극대화를 목적으로 사용한다.
일반적으로 다양한 광고 플랫폼과 연동 및 광고 캠페인 관리 기능 제공하고, 광고 시청률 및 수익 분석 기능 제공한다.

대표적인 서비스로는 Google AD Manager 가 있고, 물론 자체 구축도 가능하다.






구성도


티빙이 소개한 인프라 그대로를 따라하려면 실시간 스트리밍 환경을 구축해야 하는데,
그러면 환경 구축에 힘을 너무 많이 들여야 하니 우선 VOD (저장된 영상)을 기반으로 구성해보자.

위는 영상을 저장하는 방식이다.
VOD 를 저장하기 위해 AWS S3 를 사용한다.
그리고 .mp4 와 같은 일반 영상 형식이 아닌 HLS 변환을 시킬 것이기 때문에, AWS MediaConvert 를 이용한다.

위는 클라이언트가 영상 및 광고+영상을 요청하는 과정이다.

원본 영상만을 요청할 경우 아래와 같은 과정을 거친다.

  • CloudFront 가 MediaPackage 에게 영상을 요청
  • MediaPackage 는 S3 에게 HLS segment 를 요청
  • MediaPackage 가 manifest 를 생성 및 응답받은 HLS segment 과 함께 응답
  • CloudFront 는 이 응답을 캐싱하며 클라이언트에게 응답한다

원본 + 광고 영상을 요청할 경우 아래와 같은 과정을 거친다.

  • CloudFront 는 MediaTailor 에게 영상 및 광고 요청
  • MediaTailor 는 MediaPackage 에게 manifest 및 segment 를 요청하며, ADS 에게는 광고 VAST 를 요청
  • MediaTailor 는 응답받은 manifest, segment 와 VAST 를 조합해 원본 + 광고 manifest 를 생성해 응답
  • CloudFront 는 정책에 따라, 광고 또는 원본 영상의 segment 를 캐싱하며 클라이언트에게 응답한다.





테스트용 영상 만들기


용량이 큰 영상으로 진행하면 그만큼 요금도 많이 나올테니,
최대한 작은 영상으로 (1MB 이하) 로 진행해보자.

https://ffmpeg.org/download.html

에서 자신의 OS 에 맞는 FFmpeg 설치파일을 다운로드한다.

압축을 풀고 C:\Program Files\ffmpeg 의 경로에 디렉토리를 생성하고

  • bin
  • doc
  • presets

디렉토리를 해당 디렉토리로 이관 후
C:\Program Files\ffmpeg\bin 디렉토리를 PATH 환경변수에 등록한다.

이후 powershell 명령어를 출력

# PowerShell
# 설치 확인
ffmpeg -version

>> ffmpeg version 7.1-essentials_build-www.gyan.dev Copyright (c) 2000-2024 the FFmpeg developers
built with gcc 14.2.0 (Rev1, Built by MSYS2 project)
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-zlib --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-sdl2 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libaom --enable-libopenjpeg --enable-libvpx --enable-mediafoundation --enable-libass --enable-libfreetype --enable-libfribidi --enable-libharfbuzz --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-dxva2 --enable-d3d11va --enable-d3d12va --enable-ffnvcodec --enable-libvpl --enable-nvdec --enable-nvenc --enable-vaapi --enable-libgme --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libtheora --enable-libvo-amrwbenc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-librubberband
libavutil      59. 39.100 / 59. 39.100
libavcodec     61. 19.100 / 61. 19.100
libavformat    61.  7.100 / 61.  7.100
libavdevice    61.  3.100 / 61.  3.100
libavfilter    10.  4.100 / 10.  4.100
libswscale      8.  3.100 /  8.  3.100
libswresample   5.  3.100 /  5.  3.100
libpostproc    58.  3.100 / 58.  3.100

이제 아무거나 녹화해서 영상을 만든다.
구글에 떠돌아다니는 거 말고 그냥 Window + G 로 녹화가 가능하다.

간단히 스톱워치를 녹화했는데, 8초 짜리 영상에 1.5MB 용량을 차지해, ffmpeg 를 통해 이를 줄여보자.

# PowerShell
# 사전에 해당 영상 파일의 이름을 input.mp4 로 변경한다.
# 아래는 상대경로로 실행하므로 input.mp4 가 위치한 디렉토리에서 실행한다.
ffmpeg -i ./input.mp4 -vf "scale=426:240" -r 15 -b:v 300k -c:v libx264 -preset fast -crf 30 -b:a 64k -c:a aac output.mp4

이제 23KB 으로 줄어든 모습을 볼 수 있다.






AWS S3 버킷 생성 및 MediaConvert 로 HLS 변환

저장된 영상을 가져와 송출하려면, S3 에 송출할 수 있는 영상 형태로 저장이 되어야 한다.
즉, .mp4 와 같은 형식이 아닌 HLS/DASH 로 변환된 파일로 저장이 되어야 한다는 뜻이다.

S3 에 영상파일을 업로드하고, 이를 AWS MediaConavert 로 변환하는 과정을 거쳐보자.


AWS S3 버킷 생성 과정은 https://celdan.tistory.com/36
을 참고. 버킷 정책 JSON 은 위를 참고하지 말고 아래와 같이 따라해야 한다.

Bucket 정책 JSON 을 위 포스팅과 다르게 해야 하는 이유

  1. MediaConvert 의 접근을 위해 GetBucketLocation, GetBucketRequestPayment, ListBucket 정책도 허용해야 함.
  2. AWS Policy Generator 에 오류가 있어 Resources 의 arn 값 뒤에 /* 를 입력해야 한다.

결론적으로는 Bucket 정책을 아래와 같이 입력해야 한다.

{
    "Version": "2012-10-17",
    "Id": "Policy1740884171507",
    "Statement": [
        {
            "Sid": "StmtAllowObjectActions",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "버킷arn입력/*"
        },
        {
            "Sid": "StmtAllowBucketActions",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetBucketLocation",
                "s3:GetBucketRequestPayment",
                "s3:ListBucket"
            ],
            "Resource": "버킷arn입력"
        }
    ]
}

추가적으로, 보안 상 퍼블릭 액세스 차단도 설정해주는 것이 좋다.
여기선 S3 를 외부에서 직접 접근하는 것이 아닌 MediaConvert, CloudFront 로부터 접근하므로 외부에 공개할 필요가 없다.

S3 버킷이 만들어졌으면 위에서 만든 테스트 영상을 업로드한다.


이후 AWS MediaConvert 는 AWS MediaConvert 에서 변환한다.

입력 창에 어떤 파일을 변환할지 선택한다.

출력 형식을 지정하기 위해 출력 그룹을 추가한다.

HLS 로 변환 선택 후,

우선 어디에 저장할 지 선택한다.

이후엔 출력 형식을 아래와 같이 설정한다.

  • 이름 한정자 (Name Modifier, 필수값) : _$dt$
    ex)
    input_360p_20250302T033716.m3u8
  • 최대 비트레이트 (필수값)
    최소 1000 이상을 설정해야 해 1000으로 설정했더니 영상이 다 깨지는 불상사가 발생했다. 어차피 영상 자체가 엄청 크지 않으므로 10000000 과 같이 적당히 크게 잡아보자.

이후 S3 버킷의 출력 디렉토리를 들어가보면 아래와 같이 나온다.
여기서는 해상도를 별도로 지정하지 않았으므로, 해상도에 따른 파일이 분리되지 않았음에 참고하자.

  • output.m3u8 : 마스터 플레이리스트 (다양한 해상도 관리)
  • output$해상도$dt.m3u8 : 개별 해상도의 변형 플레이리스트
  • output$해상도$dt_0000n.ts : 실제 비디오 세그먼트 파일 (설정에서 세그먼트를 10초 단위로 생성했으므로, 10초가 안되는 이 영상은 1개의 세그먼트만 생성되었다.






AWS MediaPackage 연동


HLS 변환된 파일을 CloudFront 에서 가져가기 위해선 패키징을 해야 한다.
패키징한 것을 저장하는 개념이 아닌,
HLS/DASH 변환된 세그먼트를 관리하고 manifest 파일과 같이 실시간으로 패키징하는 개념이다.

이를 AWS S3 와 연동해보자.



MediaPackage Role 설정

우선 MediaPackage 의 Role 을 설정해야 한다.

IAM Role(역할) 설정

역할 생성 -> 사용자 지정 신뢰 정책 (MediaPackage 는 AWS 서비스 탭에서 노출되지 않음..)

사용자 지정 신뢰 정책에는 아래와 같이 mediapackage.amazonaws.com 에 대한 접근 권한을 설정해주어야 한다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "mediapackage.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

이후 AWSElementalMediaPackageV2ReadOnly 로 읽기 권한만을 선택한다.


역할 이름 (Role Name) 만 설정하고 넘어가자.



S3 Bucket CORS 설정


MediaConvert S3 접근을 위해서는 CORS 설정도 해주어야한다.

AWS S3 에 진입해 영상을 업로드한 S3 Bucket 에 진입한다.

권한 탭의 CORS 를 아래와 같이 지정한다.

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "HEAD"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": []
    }
]


MediaPackage 생성 및 S3 연동

AWS MediaPackage

여기서 좌측 탭에서 설정 시 주의할 점이 있다.

  • Live : AWS MediaLive 와 연동
  • Video on demand : S3 와 같은 미리 HLS 변환된 영상 파일과 연동

이 과정에서는 S3 와 연동할 것이므로, Video on demand 로 설정한다.

우선 패키징할 그룹 생성을 한다.
(Packging groups -> Create Group)

간단히 테스트만 할 것이므로 ID 만 지정하고 Create 한다.

이제는 실제 S3 의 HLS 변환된 파일과 연동하기 위해 좌측 탭의
Video on Demand -> Assets -> Ingest assets 에 진입한다.

  • S3 Bucket name : 어느 S3 Bucket 을 지정할 것인지
  • Use existing role : MediaPackage 에 접근 가능한 Role 을 선택
  • Filename : S3 Bucket 에서 어떠한 파일을 가져오게 할 것인지 (.m3u8 인 매니페스트 파일 선택)
  • Packaging group : 바로 위에서 생성한 Package group 명 선택

이제 정상적으로 잘 가져오는지 확인해보자.
Preview 로 이동하면 HLS 변환된 파일을 재생할 수 있는 테스트 웹뷰를 제공한다.

정상적으로 잘 가져오는 것을 확인 가능하다.

여기서 아래와 오류가 발생할 수 있다.

CORS 오류 : S3 의 CORS 설정 누락

영상이 깨짐 : MediaConvert 시 비트레이트 설정값이 낮았을 확률이 높음

영상을 아예 가져오지 못하는 경우 : Video on demand 가 아닌 Live 로 설정되지 않았는지, Assets 에 유효한 S3 의 영상을 가져오는지 확인






AWS CloudFront 연동


AWS CloudFront 는 CDN 서비스이다.

CDN은 물리적 거리가 먼 전세계 유저들의 접근 시 대용량의 파일을 일일이 원본 서버에서 전송하면 굉장히 느려질 수 있으므로,
물리적으로 가까운 CDN 서버에서 컨텐츠를 제공할 수 있도록 캐싱하여 응답하게 한다.

이를 MediaConvert 와 연동하고, PC 에서 직접 접근해보자.

AWS CloudFront 에서 배포 생성 을 진입하자.

Origin domain 선택 시 MediaPackage 가 노출되지 않는다.
CloudFront 에서 직접적으로 제공하는 것은 MediaPackage 의 Live 만 제공하고 on demand 는 제공하지 않는 것으로 보이므로.. 직접 도메인을 입력해야 한다.

AWS MediaPackage 의 Packaging에 다시 진입해 위에서 만든 패키지의 도메인을 복사 후,
위의 CloudFront 의 Origin domain 에 넣는다.

프로토콜은 HTTPS 로 설정한다.
MediaConvert 는 HTTP 를 지원하지 않으므로 HTTPS 로만 지정해야 됨을 기억하자.

이후 Origin path 에는 /out/v1 을 넣는다.
이게 MediaConvert 에서 자동으로 기본 도메인 뒤에 /out/v1 엔드포인트를 넣는데, 이를 설정하지 않으면 나중에 CloudFront 에 요청할 시 /out/v1 을 수동으로 넣어도 동작하지 않는다..

추가적으로 Origin Sheid 도 설정해주자.
서울 리전에 캐싱 계층을 두어 영상을 빠르게 가져올 수 있게 해준다.

이후 WAF 만 활성화 하고 마무리하자.
딸깍 한 번 + 적은 비용으로 혹시 모를 공격에 방어를 쉽게 적용해준다.

이제 배포가 활성화 되었는지 확인하자.
요즘은 AWS 도 최적화가 많이 됐는지 서비스가 금방금방 띄워진다.
체감 상 설정 완료 후 5초도 되지 않아 띄워지는 것 같다.

활성화가 확인되었으면 해당 서비스의 도메인을 복사해두자.

이제는 https://CloudFront도메인/MediaPackage_파일의_Endpoint 로 접근이 가능하다.

MediaPackage_파일의_Endpoint 는 위에서 Preview 할 수 있던 화면에서 확인 가능하다.
AWS MediaPackage 에서 설정한 Assets 에 진입해,
해당 파일에 접근할 수 있는 URL 을 복사하자.

이를 아래와 같이 조합한다.

영상이 제대로 나오는지는 아래에서 확인 가능하다.
https://hlsjs.video-dev.org/demo/?src=위에서조합한URL






AWS MediaTailor, AWS CloudFront 연동


AWS MediaTailor 는 기존 원본 영상에서 광고를 삽입해 원본 + 광고 영상 자체를 응답하는 SSAI (Server Side Ads Insert) 방식으로 구현되어 있다.

이 포스팅에서는 아래 구조를 구현하려고 한다.

  • MediaPackage 로부터 원본 영상을 가져오고,
  • ADS (Ad Decision Server, 샘플용) 으로부터 광고 영상을 가져오고
  • CloudFront 는 MediaTailor 와 연동해 원본 + 광고 영상을 응답

참고할 점은 광고 결정 서버에서 응답해줄 때에는, VAST 규격으로 응답해야 됨을 알고 있어야 한다.


MediaTailor 생성

AWS Elemental MediaTailor 으로 진입해 구성 생성 에 진입한다.

그러면 아래와 같이 3개의 값을 필수 입력해야 한다.

하나씩 알아보자.

이름 : MediaTailor 서비스 식별을 위한 ID
콘텐츠 소스 (Content Source) : 원본 영상을 가져올 위치
예시인 placeholder 에는 domain/out/v1 까지만 작성되어 디렉토리 위치까지는 지정하지 않았지만, 이러면 나중에 요청할 때마다 디렉토리 위치까지 전부 지정해주어야 하는 귀찮음이 발생한다.

AWS MediaPackage Assets 의 원본영상의 URL 을 복사 후 파일명만 제거하자.

ex) https://abc.egress.mediapackage-vod.ap-northeast-2.amazonaws.com/out/v1/abc/abc/abc/index.m3u8
에서 실제 파일명인 index.m3u8 만 제외해
https://abc.egress.mediapackage-vod.ap-northeast-2.amazonaws.com/out/v1/abc/abc/abc 까지만 복사해 붙여넣자.

이러면 /abc/abc/abc 디렉토리에 있는 모든 원본 영상들을 가져올 때 endpoint 에 디렉토리명 없이 파일명으로만 호출할 수 있게 된다.

광고 결정 서버 (ADS) : 어떠한 광고를 어떻게 넣을건지 응답해주는 서버의 URL 을 입력한다.
우선 테스트용이므로 Google 에서 제공하는 VAST 규격의 서버 URL 을 넣어보자.
VAST 규격 Google Media Sample 에서 가져올 수 있다.

이 값 중 하나인 'Single Inline Linear' 의 값을 넣어보자.

https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/single_ad_samples&sz=640x480&cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&impl=s&correlator=

이 URL 로 HTTP API 요청을 날려보면 아래와 같이 응답한다.
(너무 많이 요청하면 Google 에서 빈 영상을 응답하므로 주의하자)

<?xml version="1.0" encoding="UTF-8"?>
<VAST xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="vast.xsd" version="3.0">
    <Ad id="5925573263">
        <InLine>
            <AdSystem>GDFP</AdSystem>
            <AdTitle>External - Single Inline Linear</AdTitle>
            <Description>
                <![CDATA[External - Single Inline Linear ad]]>
            </Description>
            <Error>
                <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=videoplayfailed[ERRORCODE]]]>
            </Error>
            <Impression>
                <![CDATA[https://securepubads.g.doubleclick.net/pcs/view?xai=AKAOjstBkogPC4a6Cvw-LYvr4_Br0eB49GtbTgPHdOFo9Vh2HDkz4biCJsXEJ2aYuqkFPq4XAIq1S9tTRs0BGeqbzBYhTvTjmaXJF0gZEbY7j2DuzL9JmmCXNVKRTtkIwCu5mhjEIpeiX8iIhhe66-Pyrva1bIovc_QL0XPX5aOlGhpEChlU9H83jykdYUoWl0wNiCVMTfhvChkIP19_27GnI23rwjJ2O44vEr9ujzk4CAGZoo1QV0BJ_xHrNHDFk0mmYxO_NY2LLFLRF9jVIyzVGMCkOkh16vEMnKbK7q39tFLZgyWDUBg0aqIxaHxNDC52EM8rfih4e-WAS0lYPAKcI_rdDPvBJb1Ui7Szumzj5myjyOWxv3Fohpsw2yWYiLCB4uKVQQ&sai=AMfl-YSnCixqbGIazNzHj5p5rwDLsHzAycoSVU_LT_q6cta9glv6KfIrNREwetCTpBZjOaKpd0iqv6f5ygdm&sig=Cg0ArKJSzIa90Sa1CGTzEAE&uach_m=%5BUACH%5D&adurl=]]>
            </Impression>
            <Creatives>
                <Creative id="138381721867" AdID="H0Hrk8zCNZI" sequence="1">
                    <CreativeExtensions>
                        <CreativeExtension type="UniversalAdId">
                            <UniversalAdId idRegistry="googlevideo">H0Hrk8zCNZI</UniversalAdId>
                        </CreativeExtension>
                    </CreativeExtensions>
                    <Linear>
                        <Duration>00:00:10</Duration>
                        <TrackingEvents>
                            <Tracking event="start">
                                <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=part2viewed&ad_mt=%5BAD_MT%5D]]>
                            </Tracking>
                            <Tracking event="firstQuartile">
                                <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=videoplaytime25&ad_mt=%5BAD_MT%5D]]>
                            </Tracking>
                            <Tracking event="midpoint">
                                <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=videoplaytime50&ad_mt=%5BAD_MT%5D]]>
                            </Tracking>
                            <Tracking event="thirdQuartile">
                                <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=videoplaytime75&ad_mt=%5BAD_MT%5D]]>
                            </Tracking>
                            <Tracking event="complete">
                                <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=videoplaytime100&ad_mt=%5BAD_MT%5D]]>
                            </Tracking>
                            <Tracking event="mute">
                                <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=admute&ad_mt=%5BAD_MT%5D]]>
                            </Tracking>
                            <Tracking event="unmute">
                                <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=adunmute&ad_mt=%5BAD_MT%5D]]>
                            </Tracking>
                            <Tracking event="rewind">
                                <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=adrewind&ad_mt=%5BAD_MT%5D]]>
                            </Tracking>
                            <Tracking event="pause">
                                <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=adpause&ad_mt=%5BAD_MT%5D]]>
                            </Tracking>
                            <Tracking event="resume">
                                <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=adresume&ad_mt=%5BAD_MT%5D]]>
                            </Tracking>
                            <Tracking event="creativeView">
                                <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=vast_creativeview&ad_mt=%5BAD_MT%5D]]>
                            </Tracking>
                            <Tracking event="fullscreen">
                                <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=adfullscreen&ad_mt=%5BAD_MT%5D]]>
                            </Tracking>
                            <Tracking event="acceptInvitationLinear">
                                <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=acceptinvitation&ad_mt=%5BAD_MT%5D]]>
                            </Tracking>
                            <Tracking event="closeLinear">
                                <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=adclose&ad_mt=%5BAD_MT%5D]]>
                            </Tracking>
                            <Tracking event="exitFullscreen">
                                <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=vast_exit_fullscreen&ad_mt=%5BAD_MT%5D]]>
                            </Tracking>
                        </TrackingEvents>
                        <VideoClicks>
                            <ClickThrough id="GDFP">
                                <![CDATA[https://pubads.g.doubleclick.net/pcs/click?xai=AKAOjsuvXg8WDEg_3jM5BrCMMZixX6S1SMryJkxVW4hCCGEHygXFdnbvWRt0i-IKY1Efh-z7BYLu66_mWgtbXdS1xUMelT08DmaiqEOp1dVct2nldzeUuEHKVfs0gM1OkzUXxSKWzd1juVflo2sa3H_77M-YaLiS6CfvtlyMVtV93qLRZvmsnIccO0VcbosvfD9KTzoF1cxSww8Dw_UCtZib-h5VDRRi0lAbjbZTdxBgsYPc0rsvp2jmwxWhmlnvB-R6X8o4xwZFq7GxExWl3M6OeVoEq2KPuVhKzmG9jCd7jJyrrIDPt8oDr9alDOP_DDS40drpplUBoIMBDRK5-xpIzdu4tF6gQcFO4FJKJibZuIyKoTtf7hvm&sai=AMfl-YRKowplk9KfrWmQwyIYrMNTnCXsZaZCdLIqbvfWLU7S3i-IpKnn_9wG2AzQIwvhKFyWTe5vJHSLWDJC&sig=Cg0ArKJSzLmhCch-llwR&fbs_aeid=%5Bgw_fbsaeid%5D&adurl=https://googleads.github.io/googleads-ima-html5/vsi/]]>
                            </ClickThrough>
                        </VideoClicks>
                        <Icons>
                            <Icon program="GoogleWhyThisAd" width="18" height="18" xPosition="right" yPosition="bottom">
                                <StaticResource creativeType="image/png">
                                    <![CDATA[https://imasdk.googleapis.com/formats/wta/help_outline_white_24dp_with_3px_trbl_padding.png?wp=ca-pub-9939518381636264]]>
                                </StaticResource>
                                <IconClicks>
                                    <IconClickThrough>
                                        <![CDATA[https://adssettings.google.com/whythisad?source=display&reasons=AU2hxZ3KLLLfmYW-RqIDEg2gvvtQnwx5efWb8poBoms0nttpGolOV8OgQl3wxLC-TFcXZCMgTUzulFAQK56wXiN53waGG4dxg_Sk-U3DdvuSOp0WgXRzo5-zBnDv-F-W0DFHKFQszH6X5h_cD9wIOd_0ZeogY_9rS7EK_VdEwRvWGk5TPmIh91pHfq_6TtW08bf2CQAbdwybjuH4lI7fmhv3XuqgnzVj9FWlpA7pV-DJvw-CeKIxJJgTrfWwW0sxhutq4eaBjmMF7-Ax323T1_pa9bRnQKl7BCuRAWqzabLnrQwc_hKw3Fhk50VuEe3nOmcc6SLQu5UH22YXywEeFLSrPmnzluuxBqbUoGvzdtRkjuNaVQ7OE6a5zxzsebFuNH5RfG8ajdrFbgClFtUE___M2fIrIrScbmaW8mvx_5WFa1dTz_vz6GE6KW2f8Ec6TNl5QnEBu8HTgPjTmlfeq59KuWzeehkJlai0kFguOnURXBvXPEx14rsDD4naoCAi0U-efM1IwQtrgk4hS7-ChVpApwGupD8W77D-SSrGWEjBkbTtpKw0DuRx_Ru26MSiifFYraIL2SbOmawbGp6Hgx_t6IBflieYqMyqCNQ0Uvdixh6ENobOJgryF42jbzJ2cVeTCO0vjlWR9joLMMgwR2ziCxOVzOivbOwO7Zzml0j2q17kwU4C5B7BaQ2Ymueg6jkQXKJP7J0HLzTQBWlIufSq8Eh3OUm74kduskOrb41CscLSyDYk-IBo-2V7KhJAwJc-p73AzMZ3sTAqKfZVZ1Q15ooql9Qs5i9N3BigOlGUSA5OJMDOjK_0yu3_OlUjEnLGmoiS1fkFtOCAUfDaElzZdiN3oynQH7D6eWS5gFo_-avg2ics3Wtmi0c6zSGObmZ2GJAMvx2H_OSjS0gfYjT5pjKAL05WC2JJvjZoU9Af&opi=122715837]]>
                                    </IconClickThrough>
                                </IconClicks>
                            </Icon>
                        </Icons>
                        <MediaFiles>
                            <MediaFile id="GDFP" delivery="progressive" width="640" height="360" type="video/mp4" bitrate="140" scalable="true" maintainAspectRatio="true">
                                <![CDATA[https://redirector.gvt1.com/videoplayback/id/f1be9c477e89fd68/itag/18/source/dclk_video_ads/acao/yes/cpn/mlIQ5E2Gs3QA6tpy/ctier/L/ei/vEfFZ_XUEOjzxtYP6cPR-Ag/ip/0.0.0.0/requiressl/yes/rqh/1/susc/dvc/xpc/Eghovf3BOnoBAQ%3D%3D/expire/1772518204/sparams/expire,ei,ip,itag,requiressl,acao,ctier,source,id,rqh,susc,xpc/sig/AJfQdSswRAIgVZXpNhr-oDO0Hg4FaUnzWdwyG13FHuDrl_jP6OOKiBcCIEjLlNFCld4L7mNf23LW1BW4yarAZjDv82YRbDoL85s2/file/file.mp4]]>
                            </MediaFile>
                            <MediaFile id="GDFP" delivery="progressive" width="1280" height="720" type="video/mp4" bitrate="237" scalable="true" maintainAspectRatio="true">
                                <![CDATA[https://redirector.gvt1.com/videoplayback/id/f1be9c477e89fd68/itag/22/source/dclk_video_ads/acao/yes/cpn/mlIQ5E2Gs3QA6tpy/ctier/L/ei/vEfFZ_XUEOjzxtYP6cPR-Ag/ip/0.0.0.0/requiressl/yes/rqh/1/susc/dvc/xpc/Eghovf3BOnoBAQ%3D%3D/expire/1772518204/sparams/expire,ei,ip,requiressl,acao,ctier,source,id,itag,rqh,susc,xpc/sig/AJfQdSswRQIgXINod-i43FCu3jLycyuzwcye9S7VcBxW3YIdO2xpEroCIQDiXzVpqsgvFvr03XMSfAQ8Z4yWeoxidPzcU0ZJtRmPPA%3D%3D/file/file.mp4]]>
                            </MediaFile>
                            <MediaFile id="GDFP" delivery="progressive" width="1280" height="720" type="video/mp4" bitrate="259" scalable="true" maintainAspectRatio="true">
                                <![CDATA[https://redirector.gvt1.com/videoplayback/id/f1be9c477e89fd68/itag/106/source/dclk_video_ads/acao/yes/cpn/mlIQ5E2Gs3QA6tpy/ctier/L/ei/vEfFZ_XUEOjzxtYP6cPR-Ag/ip/0.0.0.0/requiressl/yes/rqh/1/susc/dvc/xpc/Eghovf3BOnoBAQ%3D%3D/expire/1772518204/sparams/expire,ei,ip,requiressl,acao,ctier,source,id,itag,rqh,susc,xpc/sig/AJfQdSswRgIhAKesKpoPdYBMzDKuVOnfyCkETqwf65t8Tv78JHa9pdbEAiEAzXoOoNAZHuMfsqtyI2ncAC7CZpPhvcuLmx5a3gAMAMY%3D/file/file.mp4]]>
                            </MediaFile>
                            <MediaFile id="GDFP" delivery="progressive" width="854" height="480" type="video/mp4" bitrate="183" scalable="true" maintainAspectRatio="true">
                                <![CDATA[https://redirector.gvt1.com/videoplayback/id/f1be9c477e89fd68/itag/109/source/dclk_video_ads/acao/yes/cpn/mlIQ5E2Gs3QA6tpy/ctier/L/ei/vEfFZ_XUEOjzxtYP6cPR-Ag/ip/0.0.0.0/requiressl/yes/rqh/1/susc/dvc/xpc/Eghovf3BOnoBAQ%3D%3D/expire/1772518204/sparams/expire,ei,ip,requiressl,acao,ctier,source,id,itag,rqh,susc,xpc/sig/AJfQdSswRAIgMamPM2fgblyQTG-X7hTMo4AZCvqC0maaZ3I7zAorfP4CIAj8SYeGatLyA4q8E7YfYRmP4HQ8caOhM8Paj3PBzubd/file/file.mp4]]>
                            </MediaFile>
                            <MediaFile id="GDFP" delivery="streaming" width="256" height="144" type="application/x-mpegURL" minBitrate="96" maxBitrate="494" scalable="true" maintainAspectRatio="true">
                                <![CDATA[https://redirector.gvt1.com/api/manifest/hls_variant/id/f1be9c477e89fd68/itag/0/source/dclk_video_ads/acao/yes/cpn/mlIQ5E2Gs3QA6tpy/ctier/L/ei/vEfFZ_XUEOjzxtYP6cPR-Ag/hfr/all/ip/0.0.0.0/keepalive/yes/playlist_type/DVR/requiressl/yes/rqh/5/susc/dvc/xpc/Eghovf3BOnoBAQ%3D%3D/expire/1772518204/sparams/expire,ei,ip,itag,playlist_type,hfr,source,id,requiressl,acao,ctier,rqh,susc,xpc/sig/AJfQdSswRQIgRoPEStFPhNnq4Aj8y6xW0NJgPGXrhF5WPzwLjCOZFV0CIQCIHggtKC_bBprLGrCYyfX8zOND61o-oVklxhriyBQa6w%3D%3D/file/index.m3u8]]>
                            </MediaFile>
                            <MediaFile id="GDFP" delivery="streaming" width="0" height="0" type="application/dash+xml" scalable="true" maintainAspectRatio="true">
                                <![CDATA[https://redirector.gvt1.com/api/manifest/dash/id/f1be9c477e89fd68/itag/0/source/dclk_video_ads/acao/yes/cpn/mlIQ5E2Gs3QA6tpy/ctier/L/ei/vEfFZ_XUEOjzxtYP6cPR-Ag/hfr/all/ip/0.0.0.0/keepalive/yes/playlist_type/DVR/requiressl/yes/rqh/2/susc/dvc/xpc/Eghovf3BOnoBAQ%3D%3D/expire/1772518204/sparams/expire,ei,ip,source,id,requiressl,acao,ctier,itag,playlist_type,hfr,rqh,susc,xpc/sig/AJfQdSswRgIhAMmcYYFxxLXTrM5zNdEwWmm6WA-_0Bfma9nP5cvW4g5IAiEAnEiMYVW1aH7a2FwakxhLZMc1h3xjcylbEVMkrU2Ld6A%3D/file/index.mpd]]>
                            </MediaFile>
                        </MediaFiles>
                    </Linear>
                </Creative>
                <Creative id="138381056765" sequence="1">
                    <CompanionAds>
                        <Companion id="138381056765" width="300" height="250">
                            <StaticResource creativeType="image/png">
                                <![CDATA[https://pagead2.googlesyndication.com/simgad/4446644594546952943]]>
                            </StaticResource>
                            <TrackingEvents>
                                <Tracking event="creativeView">
                                    <![CDATA[https://securepubads.g.doubleclick.net/pcs/view?xai=AKAOjsuao5ekbjM2zLiSAPRtwIO4r28ebLhK0oHQCeq8PRC6ilykfhoQwjN_mBKKxv3GESoiMAyWuyBMUu9J-n7oKgj0T_PfnUNk9_MpJNQzk8fjbTNjDHpn3W_Z6w_OqTA6SP8aUfLh7xsNQqT-lTftox6-UbtyaHEZAiVR5wjhyIXMJdK-zGYI-rZfxSJXvMSHcIjt_sEbqhKii7bF6bFqcSNkjr6Zw0gIhUlRbmJKHVtGF03Va2065Gq3ycmQGrzHVzVR5TowoEZdSAkVn6yqO3q8q5ij8W5mg-JJ0NhMv57N6p32T5_Nl0O4eEsiaZdetmv5t9q9OfjY-GINI0B_gtgN8Zxta5znzglRppKvuB1i7h9Oh7DpcEQ9hy-dlltmMeFCMQ&sai=AMfl-YRpODJPWdGPrheAk7xTPQej35ghTOVQk5hzOEFuU8ORmfFzFGkC-lbNYn0Kagri0HjoOA0pINntaB7s&sig=Cg0ArKJSzJlhrTbsnoZXEAE&uach_m=%5BUACH%5D&adurl=]]>
                                </Tracking>
                            </TrackingEvents>
                            <CompanionClickThrough>
                                <![CDATA[https://pubads.g.doubleclick.net/pcs/click?xai=AKAOjsuthEWAe1Smef6QzZ-POU5Aqep9HBow0hTvm3vVOX2VEFDILdMlR5CGySEGDIZ6jsGSWJCSaDEyZ_8xOOB_OEqQ_QKbI0ubzpAeEdlSn8xOnzQ7L99JwmISLtyTjyf3pSBjPXe_vu0-EtAl6wEOMZfb_yDWftiWyMMmuhI_1P4kXzqDn_C5na6KIUMjl6VROjK8Bk8ri00GjYaktRN_8sDUUXh41lQl5M-TwVn-kOK_s2xkLTQoM82RcdIwjNciv_DDTkhWa6eOx4eYCkDHS9gG1XqWiL5Zq2kgNSSwiQjmNn2jMq_-DoCAyxbH5zYJHBAtGdoEermj6YpA9GYDaaUzjzDYmupOE6_X-z4m8IGdXF0I9fLH&sai=AMfl-YQb7ed-zHyN5148ytSYYIAX4D-_1ohPo-9rL9TkbCHt3QZmnHjFkv0AMizN5sRJyVOMTtFtFAjjU6It&sig=Cg0ArKJSzAg-MAtSKXKr&fbs_aeid=%5Bgw_fbsaeid%5D&adurl=https://googleads.github.io/googleads-ima-html5/vsi/]]>
                            </CompanionClickThrough>
                        </Companion>
                        <Companion id="138381188849" width="728" height="90">
                            <StaticResource creativeType="image/png">
                                <![CDATA[https://pagead2.googlesyndication.com/simgad/7802555171787573026]]>
                            </StaticResource>
                            <TrackingEvents>
                                <Tracking event="creativeView">
                                    <![CDATA[https://securepubads.g.doubleclick.net/pcs/view?xai=AKAOjstEckACAaplbe5PcG9vWvuyXN3C-8NTLzOmF3j1HUqnt4wDzuLwHYgA42L6hVtGnwUCh39vNRABvKQDGMbcoGhuCzwmZufu9OI9_iBBPfv_NxzXSWa0uuVqK2qw8jAivB9JuNM8FQ8UZfCMU8Zp2HRUc4uVlqljlNVkRykrK4vyY6i9QcsPjDV6vvFSbIklfHtsxTP5hy38U0K-MGWkdK0-DzbSSF13ZmOOGEo9ywiwODbcGhiktZ6QoAOSNRcyIU5PcuEcP7Y1U4d5h8wHons5nCIHGMcSMBfxm-wiTe3O5f0gXBCNkBz5FrPWqFSbmH28pAf5mZwqv22DC0OvzuhWk0xjsBxU8wzRkJ3cXxBvQg1ARl5PgCLmvcAi2T35sUTKrg&sai=AMfl-YTqJhmeL5ZtNuYq_zKrpWARMyaQUMK_ySpT7_df3ujvUP0RCml7iJT3rnuu0xLY4lHfKLypGjKj0aSJ&sig=Cg0ArKJSzNSDocVDG5c0EAE&uach_m=%5BUACH%5D&adurl=]]>
                                </Tracking>
                            </TrackingEvents>
                            <CompanionClickThrough>
                                <![CDATA[https://pubads.g.doubleclick.net/pcs/click?xai=AKAOjssj2sihfOu9Y6h7w_AHywQZkWpKokDBJYd_BstVSFq_RF4ENyB7PeOz0VLsNAhIWgsDgkk-4koyB52jujPR0ENVfBEPTc1QJfmXOI8ajR_QO7wy8w52DUPf9gIbKXQ9qgJpv59J6HFDcYsqmF_ETRs2KlxgVmnLfpsCtGl2mD-3bU6aTcGhPVg3NemVQuD-NVEEDDprN9xdStnI9OG3SoNhp5we9rfuj-eUqZpat1jvO9_AZWt-QzIN5mt8uT80PL5PiarfOLXmOeSbi8UrZm3vx0hlQGE6ETWixssS7NdBb5aMn5CSj-L0InhUCaCfojE8uq0KXSnL-VEmmbc0VN4XQcd-rHGIXyGRGXbEhLhLgKSjQHz1&sai=AMfl-YR67u2tNJ5uKV9l2j7FFPo3h6mS5JAmfAAGIstUGkhVlNzL8czuN20OgKJBefCzQGS-YtXfIjSvuizx&sig=Cg0ArKJSzKBxstETBEhd&fbs_aeid=%5Bgw_fbsaeid%5D&adurl=https://googleads.github.io/googleads-ima-html5/vsi/]]>
                            </CompanionClickThrough>
                        </Companion>
                    </CompanionAds>
                </Creative>
            </Creatives>
            <Extensions>
                <Extension type="waterfall" fallback_index="0"/>
                <Extension type="geo">
                    <Country>US</Country>
                    <Bandwidth>0</Bandwidth>
                </Extension>
                <Extension type="metrics">
                    <FeEventId>vEfFZ5OaD82TqMwPksOHiAI</FeEventId>
                    <AdEventId>CIqy4Y6g7YsDFfkzigMdSTAVjw</AdEventId>
                </Extension>
                <Extension type="ShowAdTracking">
                    <CustomTracking>
                        <Tracking event="show_ad">
                            <![CDATA[https://securepubads.g.doubleclick.net/pcs/view?xai=AKAOjssAiSzjze4MIXJv4LFIsYoKSZWRavWIPBGH8wn2se_JCg7hu--K2fkUWGhl4vRlbWJ8c9CPrju_HbtKSwvg2foGwkXYxlxTcud2ZgpUDD6McdzqB7lfQNsgiPQl6mhjmw0rs270kGAEvBQ7RyVhm75QzNqS9Vk5gSPyp-VRyycyKXAbOtW55hStsWWuVLvP3kOz_d_O1T5DBRUDpn73h2CQktZN3yu1Iao6vssr-YNQECE1Jjw2t-DChn606gOEwJ_6T9KkhUyiqhCaK0fioKZtwDUxmAMsqqS-gWf1IdpVCKS4oLCYCsHb58KM1kW7Mf0YXWi8Km9IBup17fyp_GtVi6dU4MPT2S0TGPRJ6JkNW1sHL_NGSxF9iJF4DAqnsQxNKTRy&sai=AMfl-YSvhgIqRD3UPHZtxKiIQpeQsGsqNORrDrJ8jHCxOgXa66fXnfldNc4eqEy1iitA2Q99b9VXos86ylnz&sig=Cg0ArKJSzDb8aOfyJ3hzEAE&uach_m=%5BUACH%5D&adurl=]]>
                        </Tracking>
                    </CustomTracking>
                </Extension>
                <Extension type="video_ad_loaded">
                    <CustomTracking>
                        <Tracking event="loaded">
                            <![CDATA[https://pubads.g.doubleclick.net/pagead/interaction/?ai=Bb1xrvEfFZ4rkD_nnqMwPyeDU-AiD6qWVRgAAABABII64hW84AViLgsbBgwRgya6ZjeykgBC6AQo3Mjh4OTBfeG1syAEFwAIC4AIA6gInLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3NpbmdsZV9hZF9zYW1wbGVz-ALw0R6AAwGQA6QDmAOkA6gDAeAEAdIFBhCPpcSJFpAGAaAGI6gHuL6xAqgHmgaoB5fFsQKoB_PRG6gHltgbqAeqm7ECqAfgvbECqAf_nrECqAffn7ECqAf4wrECqAf7wrEC2AcB4AcB0ggnCIBhEAEYnQEyAooCOguAQIDAgICAgKiAAki9_cE6WLKK4Y6g7YsD2AgCgAoFmAsBqg0CVVPqDRMI49nhjqDtiwMV-TOKAx1JMBWP0BUB-BYBgBcB&sigh=lglNGT79knY&label=video_ad_loaded]]>
                        </Tracking>
                    </CustomTracking>
                </Extension>
                <Extension type="esp">
                    <EspLibrary LibraryName="uidapi.com" LibraryUrl=""/>
                    <EspLibrary LibraryName="euid.eu" LibraryUrl=""/>
                    <EspLibrary LibraryName="liveramp.com" LibraryUrl=""/>
                    <EspLibrary LibraryName="esp.criteo.com" LibraryUrl=""/>
                    <EspLibrary LibraryName="liveintent.com" LibraryUrl=""/>
                    <EspLibrary LibraryName="liveintent.triplelift.com" LibraryUrl=""/>
                </Extension>
                <Extension type="IconClickFallbackImages">
                    <IconClickFallbackImages program="GoogleWhyThisAd">
                        <IconClickFallbackImage width="640" height="226">
                            <AltText>Why this ad? This ad is based on: * General factors like the app you&#39;re using, the time of day, or your approximate location. You can update your options for ads in this device&#39;s settings.</AltText>
                            <StaticResource creativeType="image/png">
                                <![CDATA[https://imasdk.googleapis.com/formats/wta/contextual_bks.png?wp=ca-pub-9939518381636264]]>
                            </StaticResource>
                        </IconClickFallbackImage>
                    </IconClickFallbackImages>
                </Extension>
                <Extension type="companion_about_this_ad">
                    <Icon program="GoogleWhyThisAd" width="18" height="18" xPosition="right" yPosition="bottom">
                        <StaticResource creativeType="image/png">
                            <![CDATA[https://imasdk.googleapis.com/formats/wta/help_outline_white_24dp_with_3px_trbl_padding.png?wp=ca-pub-9939518381636264]]>
                        </StaticResource>
                        <IconClicks>
                            <IconClickThrough>
                                <![CDATA[https://adssettings.google.com/whythisad?source=display&reasons=AU2hxZ1eaRIVJcuysAZTqzQCx99ssRbh6h1XZwHFS6AWxyEXS-IvQerLka-DOWAc02QeJbTASw-tMNnijtvfuRE4Vr_-GG_K-t1XbdWn7tgjdPoskoPbGEiWClDXdxuZAGMEAmVxCHGDEim692Kd9Lc1-zXKB6ie0D7wbJsBMLOmMS-9hF18kwSAcb3D_6FesNI3cceqrP7sQI75qAL7vuPmzLfbsMRKQNGyq-jVY72QrIotgydJ_tjmKwSJhSU9rCbRQJgTDOBhK-SKPEcgFuJUil03SXlQPhiqWB73qpA13iBPrLvHPhK82C7rD_Z99CQrpMo4Rj9e9RdZG7tzqIOUMxjLoNpNe_4IUClOnGqpwa68g88FWAVMiT66NQ2iqJO0QaHBxDEzPgGY9OeyvAHMu0SZqbgiIISW1hjKQPp2etvvyh4bf5rZZC95F2OtFG7hQm1XQ7JFdb68wdz0cQrnK9_uNjFT836Q848TUUsE6-77bGjDuxvznftLDMrkw7fCyqhZwmKvWs2913cAaaSe_z8ulRHi_Oc-5jov68qYRo472B42Wf4AN8jE6WjkC5696pXg8PGqPTh0v4dIazcwJ5Ib1yCSpsrKx0UXZoSNwGEHGNBs-zVAnFn93BrAe2PfzzRT7HJTVmNbzZ6RHACn-Pde4vOsA5rXG00WV2PwjnKZVJ80kCJuMvazSHNa9-kalng8H-IQjhmJSjAx0n9BxFieKV4S4Tq2vbHdnomAxICwgaumq77xIFMO-TWQ-J-ZT-HWefia2MrIwWYeFCuGUemo6ispgPn_gSKD9WffKMash0ZV1f1aTqkv3G_C6NXJdMJ0mic9V9Rj-QU4eGS5cpyupWzCQOTY8x5znizJCN1ISOYsCVzx61KEtMS3eaytZeZgxKRQvpr0B27Zl9R14lGO7cP8EHiXKB29Y7HO&opi=122715837]]>
                            </IconClickThrough>
                            <IconClickFallbackImages>
                                <IconClickFallbackImage width="640" height="226">
                                    <AltText>Why this ad? This ad is based on: * General factors like the app you're using, the time of day, or your approximate location. You can update your options for ads in this device's settings.</AltText>
                                    <StaticResource creativeType="image/png">
                                        <![CDATA[https://imasdk.googleapis.com/formats/wta/contextual_bks.png?wp=ca-pub-9939518381636264]]>
                                    </StaticResource>
                                </IconClickFallbackImage>
                            </IconClickFallbackImages>
                        </IconClicks>
                    </Icon>
                </Extension>
            </Extensions>
        </InLine>
    </Ad>
</VAST>

원본 영상 + 광고 응답되는지 확인

AWS MediaTailor 에서 방금 생성한 구성에 진입해보자.

아래에서 HLS 재생 접두사를 복사하고,

MediaPackage 를 테스트한 곳인
https://hlsjs.video-dev.org/demo/?src=
에서 뒤에 복사한 URL 과 파일명을 넣어서 웹으로 진입해보자.

ex)
https://hlsjs.video-dev.org/demo/?src=https://abc.mediatailor.ap-northeast-2.amazonaws.com/v1/master/abc/AdCampaign1/index.m3u8

내 원본 영상은 8초짜리였는데, 구글 광고 결정 서버에서 준 10초짜리 Preroll 광고가 붙어 18초짜리 영상이 됐음을 확인 가능하다.


MediaTailor + CloudFront 연동

위에서 원본 영상 응답용 CloudFront 를 재활용해서
원본 + 광고 영상도 응답할 수 있게 해보자.

과정을 먼저 요약하자면 아래와 같다.

  • 원본 영상은 MediaPackage 에서 가져오도록 한다.
  • 원본 + 광고 영상은 MediaTailor 에서 가져오도록 한다.
  • 두 요청의 API endpoint 패턴이 달라, 해당 패턴에 따라 다른 영상 도메인으로 요청

AWS CloudFront -> 위에서 만든 CloudFront 서비스 진입 -> Origin(원본) 탭 -> 원본 생성 진입

Origin Domain :
원본 영상 응답용 CloudFront 에서는 Orgin Domain 을 MediaPackage 의 domain 을 넣었지만,
여기서는 MediaTailor 의 도메인을 넣어야 한다.

AWS MediaTailor 로 돌아가서 만든 구성의 도메인을 가져오자. https://abc.mediatailor.ap-northeast-2.amazonaws.com 과 같은 값을 가져와서 넣으면 된다.

이름 : 광고용 CloudFront 라는 것을 식별할 수 있게 적절하게 설정


Origin(원본)이 추가되었으면, API endpoint 패턴에 따른 Origin 분기를 쳐야한다.
동작(Behavior) 탭에 진입해 동작 생성을 해보자.

경로 패턴 : /v1/*
MediaTailor 는 /v1/master/... 와 같은 endpoint 를 가지는 것을 위에서 확인했다. 그래서 도메인 바로 뒤에오는 /v1 을 인식하게 한다.
원본 및 원본 그룹 : 위에서 생성한 MediaTailor 선택
MediaPackage 가 아님에 주의하자.
캐시 정책 : CachingOptimized
나중에 고도화해 개인에 따라 광고를 다르게 하려면 캐싱 정책을 다르게 할 수 있지만, 여기서는 우선 광고 1개만 요청할 것이기 때문에 기본 캐싱 정책을 사용한다.

동작(Behavior)이 만들어졌으면, 일반 탭에 진입해 도메인을 복사한다.

도메인만 가지고는 뭘 할 수 없으므로, 아래 세 가지를 조합해서 호출하면 된다.

  • 광고용 CloudFront 도메인
  • MediaTailor Endpoint
  • 파일명

MediaTailor 의 Endpoint 는 방금 위에서 HLS 재생 접두사의 도메인만 제거한 부분을 넣으면 되고,
파일명은 MediaPackage 의 가장 마지막 파일명을 참고하자.

ex) 최종적으로 광고 + 원본 영상을 요청할 URL :
https://abc.cloudfront.net/v1/master/abc/AdCampaign1/index.m3u8

이 역시 정상 송출 확인을 위해 https://hlsjs.video-dev.org/demo/?src= 뒤에 위 URL 을 넣어 테스트해보자.

이제 MediaTailor 에게 직접 요청할 필요 없이 컨텐츠가 캐싱되는 CloudFront 으로부터도 영상을 송출할 수 있게 되었다.






Network 및 Manifest 분석


이 과정을 거쳤지만 실제 hls.js 클라이언트에서 어떻게 요청을 보내고,
응답받는지 분석해보자.

1. 최초 CloudFront manifest 요청

https://000.cloudfront.net/v1/master/111/AdCampaign1/index.m3u8 으로 요청해 최초 CloudFront 가 index.m3u8 manifest 파일을 응답한다.
manifest 내부 구조는 아래와 같다.

영상의 메타데이터 정보와
./../../manifest/111/AdCampaign1/222/0.m3u8 이라는 상대경로의 manifest 파일 위치를 가리킨다.

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-STREAM-INF:CODECS="avc1.640032,mp4a.40.2",AVERAGE-BANDWIDTH=197170,RESOLUTION=426x240,FRAME-RATE=15.0,BANDWIDTH=105684480
../../../manifest/111/AdCampaign1/222/0.m3u8

2. 1번 응답의 상대 경로의 manifest 재요청

https://000.cloudfront.net/v1/manifest/111/AdCampaign1/222/0.m3u8
으로 클라이언트는 manifest 파일을 다시 요청한다.

이번 응답은 아래와 같다.
위와는 달리 여러 정보들이 포함되어있다.

  • #EXT-X-PLAYLIST-TYPE:VOD -> VOD 라고 명시
  • #EXT-X-TARGETDURATION:10 -> 각 segment 의 최대 재생시간이 10초
  • #EXT-X-MEDIA-SEQUENCE:0 -> 첫 번째 segment의 순번
  • #EXT-X-DISCONTINUITY-SEQUENCE:0 -> 불연속성 시퀀스.
    일반적으로 해상도 또는 프레임 변경과 같은 스트림 속성의 변경을 나타냄.
    여기서는 광고 삽입 전후에 불연속성을 나타냄.
  • #EXTINF:2.0, -> 다음 세그먼트의 재생 시간을 2초 단위로 지정
  • ../../../../segment/111/AdCampaign1/222/0/0 -> segment 파일의 상대 경로. .m3u8 파일의 위치를 기준으로 해석.
  • 반복..
  • #EXT-X-DISCONTINUITY -> 다음 segment에 불연속성이 있음을 나타냄. 여기선 광고 이후 콘텐츠 재생으로 전환될 때 불연속성을 나타냄.
  • #EXTINF:8.533, -> 다음 segment의 재생 시간이 8.533초
  • https://333.egress.mediapackage-vod.ap-northeast-2.amazonaws.com/out/v1/444/555/666/777/index_1_0.ts -> segment 파일의 절대경로
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-DISCONTINUITY-SEQUENCE:0
#EXT-X-DISCONTINUITY
#EXTINF:2.0,
../../../../segment/111/AdCampaign1/222/0/0
#EXTINF:2.0,
../../../../segment/111/AdCampaign1/222/0/1
#EXTINF:2.0,
../../../../segment/111/AdCampaign1/222/0/2
#EXTINF:2.0,
../../../../segment/111/AdCampaign1/222/0/3
#EXTINF:2.0,
../../../../segment/111/AdCampaign1/222/0/4
#EXT-X-DISCONTINUITY
#EXTINF:8.533,
https://333.egress.mediapackage-vod.ap-northeast-2.amazonaws.com/out/v1/444/555/666/777/index_1_0.ts
#EXT-X-ENDLIST

정리하자면 이 manifest 는 광고 먼저 송출 후 콘텐츠가 송출되며,
광고는 5개의 2초 단위 segment 로 나뉘어지고,
콘텐츠는 1개의 8.533초 단위 segment 가 있다는 것을 보여준다.


3. 광고 segment 요청 (5회 반복)

https://000.cloudfront.net/v1/segment/111/AdCampaign1/222/0/0
으로 요청을 보냈으나 301 응답을 받아, 아래로 redirect 된다.

https://segments.mediatailor.ap-northeast-2.amazonaws.com/tm/111/888/asset_240_105_0_00001.ts

브라우저에 segment 가 load 되어 이제 광고를 송출할 수 있게 된다.


4. 콘텐츠 segment 요청

https://333.egress.mediapackage-vod.ap-northeast-2.amazonaws.com/out/v1/444/555/666/777/index_1_0.ts
으로 요청해 원본 영상의 segment 가 load 되어 콘텐츠를 송출할 수 있게 된다.

profile
Spring 백엔드 개발자

0개의 댓글