Video Creative Ingest API

借助 Video Creative Ingest API,第三方广告系统可以主动将广告素材提取到 YouTube 和 DAI 产品中。此流程可确保在从非 Google 广告服务器中做出广告投放决策时,返回的广告素材已准备就绪,可以投放到 YouTube 或 DAI 视频串流中。

从第三方广告系统主动提取广告素材的过程涉及两个主要部分。第 1 部分允许第三方广告系统向 Google 提供广告素材,并在广告素材提取到 Google 视频基础架构时监控广告素材的进度。成功提取广告素材后,系统会将标识符传回给广告系统。

第二步涉及广告系统获取这些标识符,并使用通用广告 ID (VAST 4.0+) 或类型为“UniversalAdId”的广告素材附加项将其添加到引用提交的广告素材的 VAST 文档中。当 DAI 或 YouTube 在决策时遇到来自广告系统的 VAST 文档时,广告素材系统会从 VAST 中提取这些标识符,并从 Google 视频基础架构中提取要插入以进行播放的合适广告素材。

媒体要求

提交给 API 的广告素材资源应满足以下条件,以确保系统能够成功处理。

媒体类型

Google 接受以下媒体类型。提交后,任何其他媒体类型都可能会被拒绝。

  • video/mp4
  • video/webm
  • video/quicktime
  • video/avi
  • video/mpeg
  • video/x-flv
  • flv-application/octet-stream
  • application/octet-stream
  • video/3gpp
  • video/ogg
  • audio/mp4
  • audio/mpeg

媒体属性

提交的媒体必须符合以下条件:

  • 媒体必须至少包含一个音轨或视频轨。
  • 媒体内容的时长必须合理。不超过几分钟。
  • 媒体必须包含时长超过 1 秒的音频或视频。
  • 如果媒体包含音频,则必须具有标准的音频声道配置,例如单声道、立体声或环绕声。

提交广告素材以供提取

提取流程的流程图

  1. 第三方广告系统向 Video Creative Ingest API 发送 HTTP 请求,其中包含与广告素材关联的媒体文件列表。
  2. Video Creative Ingest API 会从所提供的列表中选择质量最高的广告素材,并从 CDN 下载该广告素材。
  3. Video Creative Ingest API 会将下载的广告素材提取到 Google 视频基础架构中,以便 YouTube 和 DAI 产品使用。
  4. 系统会向第三方广告系统发送包含广告素材状态的 Pub/Sub 通知。该通知会指明该广告素材是否已成功同时加载到 YouTube 和 DAI。如果加载广告素材失败,通知中会指明失败原因。

提交广告素材时,请遵循以下准则。

身份验证

您必须使用 Google Cloud 服务账号访问该 API。在集成时,您必须向 Google 提供账号信息,以获得适当的授权。

通过 creativeingestion@google.com 向 Google 提供账号信息后,系统会授予对用于发布通知的 API 端点和 Cloud Pub/Sub 主题的访问权限。

如需开始身份验证流程,请按以下步骤操作:

  1. 创建 Google Cloud 账号。
  2. 使用该账号设置一个项目,以便向 DAI API 发送请求。
  3. 在项目中,前往左侧的 IAM 和管理标签页,然后创建一个新的服务账号;系统会使用该服务账号发送请求。
  4. 启用 PubSub API,并创建一个唯一的合作伙伴通知主题,供 API 发布所有提取成功和失败通知。

    如果您已实施网域限制组织政策,则需要将 Google 客户 ID 添加到允许的客户名单中。如需获取 ID,请与 creativeingestion@google.com 联系。完成后,您可以将允许的客户名单(API 的客户 ID)中的主账号(我们的机器人账号)添加到 IAM 政策(PubSub 发布方)。

    video-creative-library-discovery@system.gserviceaccount.com 添加为该主题的 Pub/Sub 发布方。

  5. creativeingestion@google.com 提供服务账号电子邮件地址和 Pub/Sub 主题,以便在该 API 中注册此信息。

  6. 将该账号添加为服务使用方后,启用 DAI API

  7. 如需设置用于访问该 API 的凭据,请执行以下操作:

    • 从服务账号下载凭据:点击 IAM 和管理标签页的服务账号部分中的服务账号,点击创建密钥,然后存储凭据。

    • 对 API 调用方进行身份验证。将 GOOGLE_APPLICATION_CREDENTIALS 设置为 JSON 文件的路径。如需使用常用语言对 gRPC 客户端进行身份验证,请参阅代码示例

广告素材提取

每次合作伙伴的广告系统中有新的广告素材可用时,合作伙伴的系统都应将该广告素材提交到 Creative Ingest API。

将新广告素材提交给 Google 以加载到视频基础架构中时,应提供与广告系统生成的 VAST 文档中包含的广告素材数据相同的数据。这样可确保主动将广告素材提取到 Google 中的行为与在广告响应中首次发现广告素材时加载广告素材的行为相同。

每提交一个广告素材,服务器上都会创建一个长时间运行的提取操作,您可以查询该操作的状态。操作成功完成后,系统中会生成一个广告素材资源。提交广告素材的响应包含相应请求所产生的操作的 ID。生成的操作 ID 包含在操作完成时发送的 Cloud Pub/Sub 通知中,以便映射回提交的原始广告素材。您还可以使用操作 ID 随时查看操作进度。

广告素材请求

数据会以协议缓冲区或 JSON 对象的形式发送到 API。建议使用 gRPC 发送协议缓冲区,因为它们对于使用 Pub/Sub 通知至关重要,并且由于响应中的 google.protobuf.Any 字段,解码 JSON 会增加复杂性。

富有创意的回答

成功时,服务器会返回 Protocol Buffer 响应或 JSON 响应,具体取决于请求类型。响应包含新创建的提取操作名称和操作详情。

首次提交时,与操作关联的部分元数据可能会为空,并且可能没有结果。操作完成后,结果会填充广告资源(或错误),并且元数据包含有关提取状态的信息。

请注意,必须将元数据从 Any proto 反序列化到 CreateCreativeOperationMetadata proto,并且必须将响应(如果有)反序列化到 Creative proto。

用于访问 API 的客户端库

请按照以下步骤与该 API 集成:

  1. 下载 protocol buffer 依赖项,并将整个 google 文件夹移至 src 目录。如果使用的是 Google Cloud App Engine,则可以跳过此步骤。
  2. 使用 protoc 插件从 proto 文件生成 gRPC 代码,并将生成的代码放入相关目录。
  3. 为 Pub/Sub 通知中返回的 proto 创建一个新的 proto 文件;重复上一步,从 proto 生成代码。
  4. 使用 gRPC 向 API 发出请求:

    1. 获取具有以下权限范围的默认凭据:

    https://www.googleapis.com/auth/video-ads

    https://www.googleapis.com/auth/pubsub

    1. 将请求发送到广告素材提取。
    • 使用默认凭据连接到 dai.googleapis.com:443
    • 使用 gRPC proto 生成的代码为广告素材提取服务创建客户端桩。
    • 使用客户端桩调用 CreateCreative

    (可选)使用长时间运行的操作 gRPC 代码(包含在第 2 步中下载的 googleapis 目录中)创建 Operations 服务的桩,该桩可用于获取给定操作 (GetOperation) 的状态。

  5. 检查 Pub/Sub 是否有通知:

    1. 使用 Pub/Sub 客户端库或 gRPC 连接到 Pub/Sub 服务,并轮询发布到所提供主题的 Pub/Sub 通知。

    2. 将 PubSub 消息反序列化为第 4 步中的 Proto。

    3. 每当从对 CreateCreativeGetOperation 的调用或 Pub/Sub 通知中返回长时间运行的操作时,请将元数据从 google.protobuf.Any proto 反序列化到 discovery.proto 中的 CreateCreativeOperationMetadata,并将任何响应反序列化到 discovery.proto 中的 Creative

Go 客户端代码示例

// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
  dai "google.golang.org/genproto/googleapis/ads/dai/v1"
  psnotif "protos/dai"
  "google.golang.org/genproto/googleapis/longrunning"
  "cloud.google.com/go/pubsub"
  "golang.org/x/net/context"
  "google.golang.org/grpc"
  "google.golang.org/grpc/credentials"
  "google.golang.org/grpc/credentials/oauth"
  "github.com/golang/protobuf/proto"
  anypb "github.com/golang/protobuf/ptypes/any"
)

var scopes = []string{"https://www.googleapis.com/auth/video-ads", "https://www.googleapis.com/auth/pubsub"}

const (
  subID = "sub-id" // The ID for the subscription to the topic you provided us (projects/<project ID>/subscriptions/<subscription ID>)
  project = "your-project-id" // Your Google Cloud Project ID
)

func main() {
  ctx := context.Background()
  crpc, err := oauth.NewApplicationDefault(ctx, scopes...)
  if err != nil {
    // TODO: Handle error
  }

  conn, err := grpc.Dial("dai.googleapis.com:443",
    grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")),
    grpc.WithPerRPCCredentials(crpc))
  if err != nil {
    // TODO: Handle error 
  }

  apiclient := dai.NewPartnerDiscoveryServiceClient(conn)
  opclient := longrunning.NewOperationsClient(conn)

  op, err := apiclient.CreateCreative(ctx, &dai.CreateCreativeRequest{
    // TODO: Add Creative with metadata and media files.
  })
  if err != nil {
    // TODO: Handle error
  }

  pubsubClient, err := pubsub.NewClient(ctx, project)
  if err != nil {
    // TODO: Handle error
  }
  sub := pubsubClient.Subscription(subID)

  // TODO: Pull from Pub/Sub subscription to receive messages, and acknowledge them
  var msg *pubsub.Message // Assume msg = a received Pub/Sub message

  // Unmarshal the Pub/Sub message into the proto created in step 9a of guide
  var n psnotif.CreateCreativeStatus
  if err := proto.Unmarshal(msg.Data, &n); err != nil {
    // TODO: Handle error
  }
  op = n.GetDetails()
  md, err = anyToMetadata(op.GetMetadata())
  if err != nil {
    // TODO: Handle error
  }
  // TODO: Check if ingest was successful and get Google Video ID from md.GetGvRegistryId()
  // TODO: Enable serving of creative using Google Video ID in VAST document

  // Optionally check the status of an ingest request using the status endpoint
  op, err = opclient.GetOperation(ctx, &longrunning.GetOperationRequest{Name:   op.Name})
  if err != nil {
    // TODO: Handle error
  }
}

// anyToMetadata converts the metadata Any field in an Operation message to
// CreateCreativeOperationMetadata.
func anyToMetadata(msg *anypb.Any) (*dai.CreateCreativeOperationMetadata, error) {
  var md dai.CreateCreativeOperationMetadata
  return &md, proto.Unmarshal(msg.Value, &md)
}

// anyToCreative converts the response Any field in an Operation message to
// Creative.
func anyToCreative(msg *anypb.Any) (*dai.Creative, error) {
  var cr dai.Creative
  return &cr, proto.Unmarshal(msg.Value, &cr)
}

播放期间的广告素材查询

广告素材查找流程的流程图

  1. 用户请求在 YouTube 或 DAI 中观看内容。
  2. YouTube / DAI 从第三方广告系统获取 VAST 响应,以便将广告插入内容中。
  3. 返回的广告会映射到之前提取的广告素材。这些广告素材已准备好播放,系统会从 Google 视频基础架构提取这些广告素材并为用户播放。

合作伙伴修改的 VAST 文档

当广告素材成功加载到 Google 视频基础架构后,如果广告系统返回 VAST 响应,该文档应包含对所处理广告素材的引用。VAST 文档应继续包含给定广告素材的媒体文件,但我们无法保证系统会使用这些媒体文件。

您可以通过以下两种方式在 VAST 文档中引用已处理的广告素材:向 VAST 4 及更高版本文档添加新的 UniversalAdId 元素,或添加自定义扩展程序。

通用广告 ID

Video Creative Ingest API 返回的 gvRegistryID 是一个通用的面向公众的 ID,用于在多个广告系统中唯一标识视频广告素材。此 ID 应添加到广告系统的 VAST 4 版文档中,以便 YouTube 或 DAI 收到 VAST 文档时,可以使用此 ID 映射回最初提交的广告素材。

广告系统应在其 VAST 4 版文档的“Creative”节点下添加新的 UniversalAdId 元素。idRegistry 属性应设置为“googlevideo”,值应为成功处理的广告素材通知状态中提供的值。

以下是添加了新通用广告 ID 的 VAST 4 文档示例:

<VAST xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="vast.xsd" version="4.0">
  <Ad id="318230556">
    <InLine>
      <AdSystem>DCM</AdSystem>
      <AdTitle>In-Stream Video</AdTitle>
      <Creatives>
        <Creative id="79534759" AdID="" sequence="1">
          <UniversalAdId idRegistry="googlevideo">dQw4w9WgXcQ</UniversalAdId>
          <Linear>
            <Duration>00:00:15</Duration>
            <MediaFiles>
              <Mezzanine>
                <![CDATA[https://cdn.com/media.mp4]]>
              </Mezzanine>
              <MediaFile delivery="progressive" width="640" height="360" type="video/mp4" bitrate="313">
                <![CDATA[https://cdn.com/low-res-media.mp4]]>
              </MediaFile>
            </MediaFiles>
          </Linear>
        </Creative>
      </Creatives>
    </InLine>
  </Ad>
</VAST>

CreativeExtension

由于通用广告 ID 元素仅在 VAST 4.0 中引入,因此相同的数据需要使用自定义扩展程序才能在版本低于 4.0 的 VAST 文档中显示。为此,您可以在 VAST 规范的 Creative 元素下使用 CreativeExtension 元素。

引入的新扩展程序是一种通用扩展程序,可用于填充任何通用广告 ID 值。在这种特殊情况下,通用广告 ID 将属于 googlevideo 注册库,并且 ID 将是 google video ID。

<VAST version="3.0">
  <Ad id="318230556">
    <InLine>
      <AdSystem>My Ad Server</AdSystem>
      <AdTitle>Car Company</AdTitle>
      <Creatives>
        <Creative id="79534759" AdID="" sequence="1">
          <Linear>...</Linear>
          <CreativeExtensions>
            <CreativeExtension type="UniversalAdId">
              <UniversalAdId idRegistry="googlevideo">dQw4w9WgXcQ</UniversalAdId>
            </CreativeExtension>
          </CreativeExtensions>
        </Creative>
      </Creative>
    </InLine>
  </Ad>
</VAST>

如果 UniversalAdId 已填充了其他 UniversalAdId 注册表的 ID,则也可以在 VAST 4 及更高版本中使用 CreativeExtension 方法:

<VAST xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="vast.xsd" version="4.0">
  <Ad id="318230556">
    <InLine>
      <AdSystem>DCM</AdSystem>
      <AdTitle>In-Stream Video</AdTitle>
      <Creatives>
        <Creative id="79534759" AdID="" sequence="1">
          <UniversalAdId idRegistry="other-registry">other-id</UniversalAdId>
          <Linear>
            <Duration>00:00:15</Duration>
            <MediaFiles>
              <Mezzanine>
                <![CDATA[https://cdn.com/media.mp4]]>
              </Mezzanine>
              <MediaFile delivery="progressive" width="640" height="360" type="video/mp4" bitrate="313">
                <![CDATA[https://cdn.com/low-res-media.mp4]]>
              </MediaFile>
            </MediaFiles>
          </Linear>
          <CreativeExtensions>
            <CreativeExtension type="UniversalAdId">
              <UniversalAdId idRegistry="googlevideo">dQw4w9WgXcQ</UniversalAdId>
            </CreativeExtension>
          </CreativeExtensions>
        </Creative>
      </Creatives>
    </InLine>
  </Ad>
</VAST>