1

这是我在 Stack Overflow 上的第一篇文章,如果您不明白或者我忘记了什么,请告诉我。

我正在开发一个旨在广播视频内容的网站。该应用程序使用 Next.js (SSR) + Typescript 编码,并托管在 AWS 上。对于视频播放器,我使用谷歌库:videojs-ima。

目标是使用 VAST 代码分发广告。

几乎所有的操作系​​统和浏览器都可以,只有 iOS 有问题。事实上,在 iOS 上,当点击视频时,广告不会显示,就好像它被忽略了一样。查看 Safari 浏览器日志,出现此错误:

AdError 1009:VAST 响应文档为空。

如前所述,VAST 标记对我来说似乎是正确的,因为它适用于其他操作系统和浏览器。此外,我使用此工具对其进行了测试,并且出现了广告:

https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/vastinspector

我的另一个问题是我无法在本地测试,因为我总是遇到同样的错误:

AdError 1010:广告响应不被理解,无法解析。

我怀疑带有 SSR 的 Next.js 会导致这种情况。目前,我正在测试演示环境的更改。这真的不实用......

正如您在下面的代码中看到的,我已经测试了iOS 的自动播放和静音解决方案。但没有成功。

我提前感谢你的时间和你给我的帮助。

import { makeStyles } from '@material-ui/core';
import { Video } from 'api/entities';
import { useFetch } from 'api/hooks';
import classNames from 'classnames';
import { ASSET_URL } from 'common/constants';
import { useUtils } from 'hooks';
import useCanPlay from 'hooks/useCanPlay';
import { uniqueId } from 'lodash';
import { useRouter } from 'next/router';
import { FC, memo, useEffect, useRef, useState } from 'react';
import videojs from 'video.js';
import 'video.js/dist/video-js.min.css';
import 'videojs-contrib-ads';
import 'videojs-ima';
import { NewAdsRequest } from '.';

export type PlayerProps = {
  className?: string;
  ima?: string;
  play?: boolean;
  controls?: boolean;
  autoplay?: boolean;
  width: number | string;
  height: number | string;
  video: Partial<Video>;
  nextVideo?: Partial<Video>;
  onDuration?: (duration: number) => void;
  showTitle?: boolean;
  showPlayButton?: boolean;
  handlePlayClick?: () => void;
  pictoSize?: number;
  isLoading?: boolean;
  isPreview?: boolean;
  muted?: boolean;
};

export type State = {
  player?: any;
  iOS?: boolean;
  isAdContainerInitialized?: boolean;
  isImaInitialized?: boolean;
  isImaSdkLoaded?: boolean;
  autoplayAllowed?: boolean;
  autoplayRequiresMute?: boolean;
};

const useStyles = makeStyles({
  video: {
    width: '100%',
    height: '100%',
    position: 'absolute',
    top: 0,
    bottom: 0,
    left: 0,
    right: 0,
    objectFit: 'cover',
  },
});

const Player: FC<PlayerProps> = ({
  play,
  width,
  video: {
    id,
    duration,
    video: { key: src },
  },
  onDuration,
  nextVideo,
  className,
}) => {
  const router = useRouter();
  const { asPath } = router;

  const classes = useStyles();

  const { addScript, getAdTagUrl, isiOS } = useUtils();
  const { checkUnmutedAutoplaySupport } = useCanPlay();
  const videoRef = useRef<HTMLVideoElement>();
  const [state, setState] = useState<State>({
    autoplayAllowed: false,
    autoplayRequiresMute: false,
    isAdContainerInitialized: false,
    isImaInitialized: false,
    isImaSdkLoaded: false,
    iOS: false,
  });
  const {
    player,
    isImaInitialized,
    isImaSdkLoaded,
    iOS,
    isAdContainerInitialized,
    autoplayAllowed,
    autoplayRequiresMute,
  } = state;
  const playerId = uniqueId('player_');

  const dispatch = ({ ...payload }: State) => setState({ ...state, ...payload });

  useEffect(() => {
    addScript({
      src: '//imasdk.googleapis.com/js/sdkloader/ima3.js',
      onLoad: async () => {
        dispatch({ isImaSdkLoaded: true, iOS: isiOS(), ...(await checkUnmutedAutoplaySupport()) });
      },
    });
  }, []);

  useEffect(() => {
    if (!isImaSdkLoaded) return;

    const player = videojs(videoRef.current, {
      autoplay: autoplayAllowed,
      muted: autoplayRequiresMute,
    }) as any;

    player.on('ready', () => {
      player.src(ASSET_URL ? src : `/${src}`);

      const adTagUrl = getAdTagUrl({ asPath, playerId });
      if (adTagUrl) {
        player.ima({
          id: playerId,
          adTagUrl,
          disableCustomPlaybackForIOS10Plus: iOS,
        });
      }

      dispatch({
        player,
        isImaInitialized: true,
      });
    });
  }, [isImaSdkLoaded]);

  useEffect(() => {
    if (!isImaInitialized) return;

    player.src(ASSET_URL ? src : `/${src}`);

    const adTagUrl = getAdTagUrl({ asPath, playerId });
    if (!adTagUrl) return;

    var adsRequest = NewAdsRequest();
    adsRequest.adTagUrl = adTagUrl;
    const adSize = (window.innerWidth * parseInt(width.toString().replace('vw', ''))) / 100;
    adsRequest.linearAdSlotWidth = adSize;
    adsRequest.linearAdSlotHeight = adSize;
    adsRequest.nonLinearAdSlotWidth = adSize;
    adsRequest.nonLinearAdSlotHeight = adSize;
    player.ima.requestAds(adsRequest);
  }, [id]);

  useEffect(() => {
    if (play) {
      if (!isAdContainerInitialized) {
        player?.ima?.initializeAdDisplayContainer();
        dispatch({ isAdContainerInitialized: true });
      }
      player?.play();
    } else {
      player?.pause();
    }
  }, [play]);

  return (
    <div className={className}>
      <div data-vjs-player>
        <video
          id={playerId}
          ref={videoRef}
          controls
          playsInline
          preload={duration ? 'none' : 'metadata'}
          className={classNames('video-js', classes.video)}
          onDurationChange={(e) => {
            if (!duration) {
              useFetch({
                name: `video/${id}/duration`,
                body: { duration: e.currentTarget.duration },
                method: 'PUT',
              });
              onDuration(e.currentTarget.duration);
            }
          }}
          onEnded={() => {
            if (nextVideo) {
              const { id, title } = nextVideo;
              router.push(`/video/${id}/${title}`);
            }
          }}
        />
      </div>
    </div>
  );
};

export default memo(Player);
4

0 回答 0