如何使用 Azure 媒体服务发送计时元数据信号

Media Services logo v3


警告

Azure 媒体服务将于 2024 年 6 月 30 日停用。 有关详细信息,请参阅 AMS 停用指南

计时元数据是插入到实时流中的自定义数据。 数据及其插入时间戳都将保存在媒体流本身中。 这样,播放视频流的客户端就可以在完全相同的时间获取与视频流相关的相同自定义元数据。

注意

计时元数据仅适用于使用 RTMP 和 RTMPS 引入创建的实时事件。

先决条件

  • 一个媒体服务帐户
  • 熟悉如何从本地编码器实时传送视频流。 如果你以前未执行过此操作,请先尝试参考 OBS 快速入门实时传送视频流。 完成设置并运行后,应该能够执行以下步骤。
  • 一个用于测试 HTTP POST 的工具。

查看示例

以下示例演示视频播放器如何捕获和显示视频流的计时元数据。 它使用 Shaka 播放器及其通过 EmsgEvent 提供的事件消息数据(“emsg”)内置支持。

媒体服务还支持 Shaka 播放器 ID3 MetadataEvent,即使用方案 ID URI https://aomedia.org/emsg/ID3 的“emsg”事件。

查看 Stackblitz 上的代码

我们在 Stackblitz 上提供了一个示例 Shaka 播放器供你使用。 使用此按钮创建 Stackblitz.com 上的示例代码的分支。

Open Fork in StackBlitz

查看 HTML 页

index.html 文档包含:

  • 一个 div 元素,发送消息后将在此元素中显示该消息。
  • 一个标准的 HTML5 video 元素。 请注意,video 元素设置为 autoplaystart muted
  • 一个流定位符 URL 输入字段。 输入字段中有一个可查看的占位符 URL,但它不是实时流。 你将替换此值。
<script type="module" src="./index.js"></script>
<link href="./style.css" type="text/css" rel="stylesheet">

<div class="grid-container">

  <div id="header">
    <a href="https://github.com/Azure-Samples/media-services-v3-node-tutorials/tree/main/Player/examples/shaka">
      <span class="microsoft"><svg aria-hidden="true" role="presentation" viewBox="0 0 26 25"
          xmlns="http://www.w3.org/2000/svg">
          <path d="M12.5708 0.981934H0.907471V12.3682H12.5708V0.981934Z" fill="#F25022"></path>
          <path d="M25.4625 0.981934H13.7992V12.3682H25.4625V0.981934Z" fill="#7FBA00"></path>
          <path d="M12.5708 13.5649H0.907471V24.9512H12.5708V13.5649Z" fill="#00A4EF"></path>
          <path d="M25.4629 13.5649H13.7996V24.9512H25.4629V13.5649Z" fill="#FFB900"></path>
        </svg></span>
      <span class="title">Azure Media Services LL-HLS with Timed Metadata Sample</span>
    </a>
  </div>
  <div id="videoArea">


    <div id="video-container" data-shaka-player-cast-receiver-id="07AEE832">
      <div id="metadata" class="metadata-hide"></div>
      <video autoplay muted playsinline id="video" style="width: 100%; height: 100%"></video>
    </div>
  </div>

  <div id="console">Waiting for timed metadata signals to arrive...</div>
  <div id="manifest">
    <label>Your Manifest (paste and hit enter):</label>
    <input id="manifestUrl" type="url" placeholder="place manifest URL here" size="80"
      value="//aka.ms/lowlatencydemo.m3u8" />
  </div>

  <div id="footer">
  <a href="http://media.azure">Azure Media Services</a>
  </div>
</div>

查看 JavaScript

index.js 文件用于创建和管理播放器与播放器事件。 已注册 onEventMessage 函数来处理来自 Shaka 播放器的 emsg 事件并显示从 POST 收到的消息。

player.addEventListener('emsg', onEventMessage);

function onEventMessage(event) {
    console.log('Timed Metadata Event Message');
    //console.log('emsg:', event)
    // emsg box information are in emsg.details
    const dataMsg = new TextDecoder().decode(event.detail.messageData);
    console.log('EMSG: Scheme = ' + event.detail.schemeIdUri);
    console.log('EMSG: StartTime = ' + event.detail.startTime);
    console.log(
        'video.currenttime=' + document.getElementById('video').currentTime
    );

    // The start time and the presentationTimeDelta are in seconds on the presentation timeline. Shaka player does this work for us. The value startTime-presentationTimeDelta will give you the exact time in the video player's timeline to display the event.
    console.log(
        'EMSG: startTime-presentationTimeDelta = ' +
        (event.detail.startTime - event.detail.presentationTimeDelta)
    );

    console.log(
        'EMSG: presentationTimeDelta = ' + event.detail.presentationTimeDelta
    );
    console.log('EMSG: endTime = ' + event.detail.endTime);
    console.log('EMSG: timescale = ' + event.detail.timescale);
    console.log('EMSG: duration = ' + event.detail.eventDuration);
    console.log('EMSG: message length = ' + event.detail.messageData.length);

    try {
        const frames = shaka.util.Id3Utils.getID3Frames(event.detail.messageData);

        if (frames.length > 0) {
            console.log('EMSG: message = ', frames[0]);
            console.log('EMSG: mimeType = ', frames[0].mimeType);

            if (frames[0].mimeType === 'application/json') {
                const jsonPayload = JSON.parse(frames[0].data);
                let message = jsonPayload.message;
                console.log('message=' + message);

                // Now do something with your custom JSON payload
                let metadataDiv = document.getElementById('metadata');
                metadataDiv.innerText = message;

                let logLine = document.createElement('p');
                logLine.innerText = 'timestamp:' + (event.detail.startTime - event.detail.presentationTimeDelta).toFixed(2) + ' ' + JSON.stringify(jsonPayload);
                document.getElementById('console').appendChild(logLine).scrollIntoView(false);

                metadataDiv.className = 'metadata-show';

                setTimeout(() => {
                    metadataDiv.className = 'metadata-hide';
                }, 5000); // clear the message

                console.log('JSON= ' + JSON.stringify(jsonPayload));
            }
        }
    } catch (err) {
        console.error(err.stack);
    }
}

使用流定位符创建实时事件

如果你尚未学习前面提到的 OBS 快速入门,现在请使用流定位符创建一个实时事件。

  1. 使用 Azure 门户、REST 或偏好的 SDK 创建实时事件。 复制引入 URL 并将其粘贴到文本编辑器中,因为稍后需要对其进行编辑,以通过 HTTP PUT 请求向播放器发送消息。
  2. 启动实时事件,并确保关联的流式处理终结点也已启动。

流式传输实时事件

将流定位符复制并粘贴到 Stackblitz 上的播放器的输入字段中,或者选择性地更新 index.html 文件中输入元素的值。 应会看到实时事件流式传输到播放器。

创建 POST URL

编辑引入 URL:

  1. RTMPS 更改为 HTTPS
  2. 删除端口号(包括冒号)。
  3. 从路径中删除 /live/
  4. ingest.isml/eventdata 追加到路径。

示例:

rtmps://mylivestream.channel.media.chinacloudapi.cn:2935/live/0251458ba5df44b2b807ea02f40fed76

将变为

https://mylivestream.channel.media.chinacloudapi.cn/0251458ba5df44b2b807ea02f40fed76/ingest.isml/eventdata

创建并发送请求

可以使用你偏好的任何工具或 SDK 向播放器发送正文中包含元数据的 HTTP POST。

标头和请求正文

提醒:HTTP Content-type 标头必须设置为 application/json。 然后添加要显示的信息,并将键设置为“message”。 下面是一条简单的示例消息:

POST https://mylivestream.channel.media.chinacloudapi.cn/0251458ba5df44b2b807ea02f40fed76/ingest.isml/eventdata
Content-Type: application/json

{

"message": "Hello world!"

}

发送请求时,应会看到 JSON 有效负载中的消息显示在 video 元素上浮动的 div 中。

替代请求

可以发送交互式叠加的附加信息。 本文不会介绍该方案的完整设置,不过提供了测验请求正文的大致外观。 可以迭代每个“问题”的答案(此处将“message”替换为键)并提供一个按钮供观看者选择。

POST https://mylivestream.channel.media.chinacloudapi.cn/0251458ba5df44b2b807ea02f40fed76/ingest.isml/eventdata
Content-Type: application/json


{
    "question": "What is the airspeed velocity of an unladen swallow?",
     "answers" : [
        {"a1": "A shrubbery!"},
        {"a2": "I am not a witch!"},
        {"a3":  "An African or European swallow?"},
        {"a4": "It's just a flesh wound."},
    ]
}

提示

在浏览器中打开“开发人员工具”,并观看触发的视频事件以及从请求 JSON 有效负载收到的消息。

使用 cURL 的示例 POST

使用 cURL 时,必须使用 -H "Content-Type: application/json" 设置标头。 使用 -d 标志在命令行中设置 JSON 数据(使用命令行时,请用反斜杠来转义 JSON 正文中的引号)。 (可选)可以使用 -d \@\<path-to-json-file\> 指向 JSON 文件。

发送数据时 POST 是隐式的,因此不需要使用 -X POST 标志。

示例 POST:

curl https://mylivestream.channel.media.chinacloudapi.cn/618377123f4c49b3937ade20204ca0b2/ingest.isml/eventdata -H "Content-Type: application/json" -d "{\\"message\\":\\"Hello from Seattle\\"}" -v

清理资源

请务必关闭实时事件和流式处理终结点,并删除你不打算继续使用的资源,否则会产生费用。