M3U8 파일을 Tencent EdgeOne 엣지 기능으로 동적으로 재작성하는 방법은 무엇인가요?

EdgeOne-Product Team
Dec 25, 2024


현재 주요 주류 제조업체들은 CloudFlare Workers, Vercel Edge Runtime 등 자신만의 엣지 서버리스 서비스를 출시했습니다. 텐센트 엣지원 엣지 기능은 엣지 노드에 배포된 서버리스 코드 실행 환경을 제공합니다. 비즈니스 기능 코드를 작성하고 트리거 규칙을 설정하기만 하면 사용자와 가까운 엣지 노드에서 코드를 탄력적이고 안전하게 실행할 수 있습니다.

1. M3U8 파일이란?

M3U8은 멀티미디어 재생 목록을 저장하는 데 사용되는 파일 형식입니다. 이는 M3U 형식을 기반으로 하며, "8"은 UTF-8 문자 인코딩을 사용함을 나타냅니다. M3U8 파일은 주로 Apple이 개발한 인기 있는 적응형 스트리밍 프로토콜인 HTTP Live Streaming(HLS)에서 사용됩니다.

M3U8 파일은 오디오, 비디오 또는 두 가지 모두의 미디어 파일 URL 목록을 포함합니다. 또한 각 미디어 세그먼트의 지속 시간 및 세그먼트 순서와 같은 메타데이터 및 기타 정보를 포함할 수 있습니다. HLS의 맥락에서 M3U8 파일은 적응형 스트리밍 재생 목록을 생성하는 데 사용되며, 이를 통해 비디오 플레이어는 시청자의 네트워크 상태 및 장치 기능에 따라 서로 다른 품질 수준이나 비트 전환 간에 전환할 수 있습니다.  이 기사에서는 M3U8 관련 내용에 대한 자세한 소개를 제공합니다.

2. 엣지 기능으로 M3U8 파일을 재작성하는 방법

실제 사용에서 개발자는 M3U8 파일을 재작성하고 처리해야 할 필요가 있을 수 있습니다. 다음은 몇 가지 일반적인 시나리오입니다:

  1. 맞춤형 재생 목록: 개발자는 사용자의 네트워크 조건, 장치 성능 또는 지리적 위치에 따라 다양한 미디어 스트림을 선택해야 할 수 있습니다.
  2. 콘텐츠 보안 및 접근 제어: 저작권 보호를 위해 개발자는 M3U8 파일을 처리하여 암호화, 접근 토큰 추가 및 기타 기능을 구현해야 할 수 있습니다.
  3. 광고 삽입: 스트리밍 콘텐츠에 광고를 삽입하는 것은 일반적인 비즈니스 모델입니다. 개발자는 M3U8 파일을 수정하여 비디오 재생 중 특정 지점에서 광고 세그먼트를 삽입할 수 있습니다.

M3U8 파일을 재작성하고 처리하면 개발자가 더 풍부하고 유연한 스트리밍 미디어 애플리케이션 시나리오를 구현하는 데 도움이 됩니다.

EdgeOne 엣지 기능의 프로그래머블 덕분에 개발자는 엣지 노드에서 M3U8 미디어 파일을 처리하고 동적으로 콘텐츠를 수정하고 삽입할 수 있습니다.

이제 세 가지 시나리오를 살펴보면서 엣지 기능을 사용하여 M3U8 파일을 동적으로 재작성하는 방법을 이해해 봅시다.

2.1 URL 인증 추가

M3U8 파일 내용을 재작성하고 각 TS 파일 요청에 URL 인증 매개변수를 추가하여 비디오 콘텐츠의 보안을 보호합니다. 가능한 시나리오는 다음과 같습니다:

  1. 유료 콘텐츠: 예를 들어, 일부 비디오 웹사이트는 유료 영화, TV 프로그램 또는 라이브 스트리밍 서비스를 제공할 수 있습니다. 이러한 웹사이트는 URL 인증을 사용하여 유료 사용자만 콘텐츠에 접근할 수 있도록 할 수 있습니다;
  2. 회원 콘텐츠: 일부 웹사이트는 회원 전용 콘텐츠를 제공할 수 있습니다. 이러한 웹사이트는 URL 인증을 사용하여 이러한 콘텐츠에 대한 접근을 회원만 제한할 수 있습니다;

우리는 엣지 기능에서 동적 URL 인증 매개변수 추가를 구현하여 콘텐츠 보호 목적을 달성할 수 있습니다.

원래 M3U8 파일은 다음과 같습니다:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.000000,
8k60_-bulgaria_8k_hdr_60p_fuhd692899210.ts
#EXTINF:10.000000,
8k60_-bulgaria_8k_hdr_60p_fuhd692899211.ts
#EXTINF:10.000000,
8k60_-bulgaria_8k_hdr_60p_fuhd692899212.ts
...
#EXTINF:9.360422,
8k60_-bulgaria_8k_hdr_60p_fuhd6928992133.ts
#EXT-X-ENDLIST

클라이언트의 M3U8 파일을 얻기 위한 HTTP 요청은 auth_key라는 Type A 매개변수를 포함하며, 형태는 다음과 같습니다: http://localhost:8080/xxx.m3u8?auth_key=1698752518-0-00001-ff87dbc0598...

이 요청은 엣지 기능을 트리거하여 M3U8 HTTP 요청의 auth_key 매개변수에서 타임스탬프, rand 및 uid와 같은 주요 정보를 검색합니다. 이러한 매개변치를 기반으로 각 TS 요청에 대한 Type A 인증 매개변수 auth_key를 계산하고 M3U8 파일의 각 TS 요청 URL을 수정합니다:

...
async function rewriteLine({ line, querySign }) {
  if (/^\s*$/.test(line)) {
    return line;
  }

  if (line.charAt(0) === "#") {
    if (line.startsWith("#EXT-X-MAP")) {
      const key = await createSign(querySign, line);

      line = line.replace(/URI="([^"]+)"/, (matched, p1) => {
        return p1 ? matched.replace(p1, `${p1}?key=${key}`) : matched;
      });
    }
    return line;
  }

  const key = await createSign(querySign, line);

  return `${line}?${AUTH_KEY_NAME}=${key}`;
}

async function createSign(querySign, line) {
  const { basePath, ts, rand, uid } = querySign;
  const pathname = `${basePath}/${line}`;

  const md5hash = await md5([pathname, ts, rand, uid, SECRET_KEY].join("-"));
  const key = [ts, rand, uid, md5hash].join("-");

  return key;
}
... 

위의 처리 후 원래 M3U8 파일의 TS 요청이 재작성되었습니다:

클라이언트 플레이어는 M3U8 파일을 파싱하고 Type A 인증 매개변수 auth_key가 포함된 TS 요청을 다음과 같은 형태로 시작합니다: http://localhost:8080/xxx.ts?auth_key=1698752518-0-00001-88cbab261a...

TS 리소스를 얻기 위한 HTTP 요청은 여전히 엣지 기능의 실행을 트리거하며, 이때 auth_key를 확인합니다:

...
async function checkTypeA(urlInfo) {
  const sign = urlInfo.searchParams.get(AUTH_KEY_NAME) || "";
  const elements = sign.split("-");

  if (elements.length !== 4) {
    return {
      flag: false,
      message: "잘못된 서명 형식",
    };
  }

  const [ts, rand, uid, md5hash] = elements;
  if (!ts || !rand || !uid || !md5hash) {
    return {
      flag: false,
      message: "잘못된 서명 형식",
    };
  }

  if (!isNumber(ts)) {
    return {
      flag: false,
      message: "서명 만료",
    };
  }

  const now = Math.floor(Date.now() / 1000);
  if (now > Number(ts)) {
    return {
      flag: false,
      message: "서명 만료",
    };
  }

  const hash = await md5(
    [urlInfo.pathname, ts, rand, uid, SECRET_KEY].join("-")
  );
  if (hash !== md5hash) {
    return {
      flag: false,
      message: "서명 검증 실패",
    };
  }
  return {
    flag: true,
    message: "성공",
  };
}
...

TS URL에 포함된 auth_key가 성공적으로 검증되면 리소스가 정상적으로 반환되어 클라이언트가 비디오를 제대로 재생할 수 있습니다:

TS URL에 포함된 auth_key가 유효하지 않거나 포함되지 않은 경우, 엣지 기능은 403을 반환하며 비디오는 제대로 재생되지 않습니다:

2.2 광고 삽입

M3U8 파일에 TS 파일을 추가하여 광고를 삽입하는 것은 온라인 비디오 광고를 제공하는 일반적인 방법입니다. 이 방법은 비디오 스트림에 광고를 매끄럽게 삽입할 수 있게 해줍니다. 주요 적용 시나리오는 다음과 같습니다:

  1. 온라인 비디오 플랫폼: 예를 들어, YouTube, Netflix 등은 비디오의 시작, 중간 또는 끝에 광고를 삽입할 수 있습니다;
  2. 라이브 스트리밍 플랫폼: 예를 들어, Twitch, Douyu 등은 라이브 스트리밍 중 특정 순간에 광고를 삽입할 수 있습니다;

이 방법은 다른 광고 삽입 방법에 비해 다음과 같은 장점이 있습니다:

  1. 유연성: 필요에 따라 비디오의 시작, 중간 또는 끝에 광고를 삽입할 수 있습니다;
  2. 호환성: 이 방법은 특정 플레이어에 의존하지 않으며, HLS를 지원하는 장치라면 광고를 재생할 수 있습니다;
  3. 개인화: 사용자 행동, 관심사 또는 지리적 위치에 따라 맞춤형 광고를 제공할 수 있습니다;

이 아이디어를 사용하여 엣지 노드에서 M3U8 파일의 TS 파일 목록을 동적으로 수정할 수 있습니다.

원래 M3U8 파일은 다음과 같습니다:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.000000,
8k60_-bulgaria_8k_hdr_60p_fuhd692899210.ts
#EXTINF:10.000000,
8k60_-bulgaria_8k_hdr_60p_fuhd692899211.ts
#EXTINF:10.000000,
8k60_-bulgaria_8k_hdr_60p_fuhd692899212.ts
...
#EXTINF:9.360422,
8k60_-bulgaria_8k_hdr_60p_fuhd6928992133.ts
#EXT-X-ENDLIST

엣지 기능을 사용하여 미리 준비된 광고 TS 세그먼트를 삽입합니다:

const AD_TS = `#EXTINF:10.000000,
http://m3u8-1251557890.cos.ap-guangzhou.myqcloud.com/apple_watch_apple_watch_hermes184006680.ts
#EXTINF:10.000000,
http://m3u8-1251557890.cos.ap-guangzhou.myqcloud.com/apple_watch_apple_watch_hermes184006681.ts
#EXTINF:10.000000,
http://m3u8-1251557890.cos.ap-guangzhou.myqcloud.com/apple_watch_apple_watch_hermes184006682.ts
#EXT-X-DISCONTINUITY`;

...
async function handleM3U8(request) {
  ...
  const response = await fetchOrigin(request);
  if (response.status !== 200) {
    return response;
  }

  const originalM3U8 = await response.text();

  const modifiedM3U8 = originalM3U8.replace(
    "#EXT-X-MEDIA-SEQUENCE:0",
    `#EXT-X-MEDIA-SEQUENCE:0\n${AD_TS}`
  );

  return new Response(modifiedM3U8, response);
}
...

위의 처리 후 광고 TS 세그먼트가 원래 M3U8 파일에 삽입되었습니다:

클라이언트 플레이어가 재작성된 M3U8 파일을 가져오면 먼저 광고를 재생합니다:

광고가 재생된 후 매끄럽게 전환되어 비디오 콘텐츠를 재생합니다:

광고 콘텐츠는 일정 기간 동안 상대적으로 고정되어 있으므로 M3U8 파일을 반복적으로 재작성할 필요가 없습니다. 합리적인 캐싱을 위해 Cache를 사용할 수 있습니다.

2.3 지리적 타겟팅

지리적 위치에 따라 광고를 전달하면 광고 목표를 보다 효과적으로 달성하고 광고 효과를 개선하며 개인화된 경험을 제공할 수 있습니다. 시나리오 2를 기반으로 코드를 추가로 수정하여 엣지 기능 Request API의 request.eo.geo 매개변수를 사용하여 클라이언트의 지리적 위치를 얻고, 다양한 광고 콘텐츠를 주입하여 정교한 광고 전달을 달성할 수 있습니다.

const AD_TS_1 = `...AD_TS_1...`;
const AD_TS_2 = `...AD_TS_2...`;

const ADS = {
  CN: AD_TS_1,
  US: AD_TS_2,
};

...
async function handleM3U8(request) {
  ...
  const response = await fetchOrigin(request);
  if (response.status !== 200) {
    return response;
  }

  const originalM3U8 = await response.text();
  const alpha2code = request.eo.geo.countryCodeAlpha2;
  const AD_TS = ADS?.[alpha2code];
  let modifiedM3U8 = originalM3U8;
  
  if (AD_TS) {
    modifiedM3U8 = originalM3U8.replace(
      "#EXT-X-MEDIA-SEQUENCE:0",
      `#EXT-X-MEDIA-SEQUENCE:0\n${AD_TS}`
    );
  }

  return new Response(modifiedM3U8, response);
}
...

request.eo.geo를 기반으로 광고 TS 세그먼트를 사용자 지정하는 것과 유사하게, 개발자는 비디오의 TS의 다른 부분을 사용자 지정할 수 있습니다. 기사 길이 관계상 여기서는 시연하지 않습니다.

3. 결론

텐센트 엣지원 엣지 기능을 사용하여 엣지 노드에서 M3U8 파일을 재작성하면 편리하고 유연한 스트리밍 미디어 처리가 가능하며 CDN 가속을 보장합니다. 실제 사용자의 상황에 따라 미디어 스트림을 동적으로 조정할 수 있어 비즈니스 프로세스를 최적화하고 유지 관리 비용을 줄일 수 있습니다. 현재 무료 체험을 진행 중이며, 여기를 클릭하거나 문의하세요 더 많은 정보를 얻으세요.

Deliver fastest and most secure performance
14-Day Free TrialFree 100 GB