Tencent EdgeOne Edge Functionsを使用してM3U8ファイルを動的に書き換える方法は?

EdgeOne-Product Team
Dec 25, 2024


現在、主要な主流メーカーはCloudFlare Workers、Vercel Edge Runtimeなど、自社のエッジサーバーレスサービスを展開しています。 Tencent EdgeOne エッジ機能は、エッジノードに展開されたサーバーレスコード実行環境を提供します。ビジネス機能コードを書いてトリガールールを設定するだけで、ユーザーに近いエッジノードでコードを弾力的かつ安全に実行できます。

1. M3U8ファイルとは

M3U8は、マルチメディアプレイリストを保存するために使用されるファイル形式です。これはM3U形式に基づいており、「8」はUTF-8文字エンコーディングを使用していることを示します。M3U8ファイルは主にHTTP Live Streaming(HLS)で使用され、Appleによって開発された人気のある適応ストリーミングプロトコルです。

M3U8ファイルには、オーディオ、ビデオ、またはその両方のメディアファイルのURLのリストが含まれています。また、各メディアセグメントの長さやセグメントの順序など、メタデータやその他の情報も含むことがあります。HLSの文脈では、M3U8ファイルは適応ストリーミングプレイリストを作成するために使用され、ビデオプレーヤーが視聴者のネットワーク条件やデバイスの能力に基づいて異なる品質レベルやビットレートに切り替えることを可能にします。  この記事では、M3U8の関連内容について詳細に紹介しています。

2. エッジ機能を使ったM3U8ファイルの書き換え方法

実際の使用において、開発者はM3U8ファイルを書き換えたり処理したりする必要がある場合があります。以下は一般的なシナリオです:

  1. カスタマイズされたプレイリスト: 開発者は、ユーザーのネットワーク状況、デバイス性能、地理的位置に基づいて異なるメディアストリームを選択する必要があります。
  2. コンテンツのセキュリティとアクセス制御: 著作権保護のために、開発者はM3U8ファイルを処理して暗号化を実装し、アクセス・トークンを追加する必要があります。
  3. 広告挿入: ストリーミングコンテンツに広告を挿入することは一般的なビジネスモデルです。開発者はM3U8ファイルを変更して、ビデオ再生中の特定のポイントに広告セグメントを挿入できます。

M3U8ファイルの書き換えや処理は、開発者がより豊かで柔軟なストリーミングメディアアプリケーションシナリオを実現するのに役立ちます。

EdgeOneエッジ機能のプログラム性のおかげで、開発者はエッジノードでM3U8メディアファイルを処理し、コンテンツを動的に修正および注入できます。

それでは、エッジ機能を使用してM3U8ファイルを動的に書き換える方法を理解するために、3つのシナリオを見てみましょう。

2.1 URL認証の追加

M3U8ファイルの内容を書き換えて、各TSファイルリクエストにURL認証パラメータを追加します。これは主にビデオコンテンツのセキュリティを保護するためです。以下はいくつかの可能なシナリオです:

  1. 有料コンテンツ: たとえば、一部の動画サイトでは、有料の映画、テレビ番組、またはライブストリーミングサービスを提供している場合があります。これらのサイトでは、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

クライアントのHTTPリクエストでM3U8ファイルを取得するとき、Type Aパラメータauth_keyが付加されます。その形式は次のとおりです: http://localhost:8080/xxx.m3u8?auth_key=1698752518-0-00001-ff87dbc0598...

このリクエストはエッジ機能をトリガーし、M3U8 HTTPリクエストのauth_keyパラメータからtimestamp、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ファイルを繰り返し書き換える必要はありません。合理的なキャッシュのためにキャッシュを利用できます。

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. 結論

Tencent EdgeOne エッジ機能を使用してエッジノードでM3U8ファイルを書き換えることで、CDNアクセラレーションを維持しながら便利で柔軟なストリーミングメディア処理が可能になります。ユーザーの実際の状況に応じてメディアストリームを動的に調整し、ビジネスプロセスを最適化し、維持コストを削減できます。現在、無料トライアルを開始しました。こちらをクリックまたはお問い合わせください。詳細情報をご覧ください。