m3u8 ビデオへの videojs アクセスを実装すると、いくつの落とし穴に遭遇しましたか?

vue3 から videojs を介して m3u8 ビデオにアクセスする実装には 1 日以上かかり、最終的に成功しました。導入プロセス中に私が陥った落とし穴を見てみましょう。

序文

最も一般的な mp4 タイプのビデオは、Vue のネイティブ ビデオを通じて直接導入できます。

<video :src="video" autoPlay controls muted loop />

ただし、アクセスしたビデオがライブ ビデオ ストリームの場合、ビデオはサポートされていないため、機能を実装する前に特定のプラグインを使用して支援し、いくつかの概念を導入する必要があります。

1.HLS、M3U8

需要が hls や m3u8 ビデオなどの Hikvision のビデオにアクセスすることだと聞いたとき、私は無知ですぐに Du Niang を探しに行きました。

HLS は http に基づくストリーミング メディア ネットワーク伝送プロトコルであり、伝送コンテンツは M3U8 記述ファイルと TS メディア ファイルの 2 つの部分で構成されます。

M3U8 ファイルは、UTF-8 エンコード形式の M3U ファイルを指します。M3U ファイルは、ビデオ ファイルのインデックスを記録するテキスト ファイルです。

要約すると、HLS プロトコルは、ストリーミング メディアを ts ファイルと m3u8 ファイルにスライスし、m3u8 インデックス ファイルを介して ts ファイルに順次アクセスし、クライアントがサーバーからビデオ ファイルを継続的に取得することで、オンライン オーディオとビデオの再生機能を実現します。

この記事では、videojs プラグインを介したビデオ アクセスを実装します。

機能実現

1. 依存関係をインストールしてインポートする

npm install --save video.js
npm install --save videojs-contrib-hls

Index.html で紹介されました

    <!-- 视频取流 -->
    <link href="https://cdn.bootcss.com/video.js/7.20.3/alt/video-js-cdn.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/video.js/7.20.3/video.js"></script>
    <script src="https://cdn.bootcss.com/videojs-contrib-hls/5.15.0/videojs-contrib-hls.min.js"></script>

注: これが最初の落とし穴です。多くのメソッドを読んだ後は、依存関係を直接インストールするだけで済みますが、そのような手順はありません。ただし、リクエストが導入されていない場合、リクエストは成功しますが、ビデオを読み込むことはできません。

ここに画像の説明を挿入します

追加後は正常に表示されるので、
ここに画像の説明を挿入します
ローカルにファイルをインポートします。

import videojs from 'video.js';
import 'videojs-contrib-hls';
import 'video.js/dist/video-js.css';

2. コードの実装

テンプレートコード

  <div id="video-box" class="video-item" v-show="showFlag">
    <video
      id="my-video"
      class="video-js vjs-default-skin"
      controls
      preload="auto"
      style="width: 100%;height:100%"
    >
      <source id="source" :src="xxxx视频请求网址xxxx" type="application/x-mpegURL" />
    </video>
  </div>

初期化

const myPlayer = ref(null);
const initVideo = () => {
//初始化配置
  myPlayer.value = videojs('my-video',{
    bigPlayButton: false,
    textTrackDisplay: false,
    posterImage: true,
    errorDisplay: false,
    controlBar: true,
  });
 // 播放
  myPlayer.value.play();
};

onMounted(() => {
  setTimeout(() => {
      initVideo();
  }, 300);
});

注: 2 番目のピットが来ています! ビデオ コンポーネントがポップアップ ウィンドウに配置されるときに、
TypeError: The element or ID provided is not valid. (videojs) というエラーが報告された場合

このとき、次の 2 つの点に注意する必要があります。

  1. コンポーネントの外側の div には v-if を使用できません。表示と非表示を制御するには v-show を使用する必要があります。
  2. videojs の初期化は、すべての dom がマウントされた後に行う必要があるため、setTimeout を使用して読み込みを遅らせることができます。

上記は固定アドレスの書き込み方法ですが、異なるビデオ アドレス ソースを切り替える必要があるため、さらに最適化する必要があります。

まずビデオアドレスを変数として設定します

 <source id="source" :src="cameraURL" type="application/x-mpegURL" />
  
 const srcPath = ref<string>('');
 const initVideo = (url:string) => {
  myPlayer.value = videojs('my-video',{
    bigPlayButton: false,
    textTrackDisplay: false,
    posterImage: true,
    errorDisplay: false,
    controlBar: true,
  });
  // 设置url
  myPlayer.value.src(url);
  myPlayer.value.play();
};

注: 3 番目のピットがやって来ます! オンライン学習の過程では、テンプレートにソース部分を書かずに属性値を直接割り当てる以下のような書き方をする人もいます。この方法を使い始めましたが、うまくいかないことがわかりました。最終的に上記の書き方に変更しました。(原因がわかる方いらっしゃいましたらアドバイスをお願いします)

    myPlayer.src([
        {
            type: "application/x-mpegURL",
            src: url 
        }
    ]);

注: その後、問題が再び発生しました。URL を切り替えた後、ビデオをロードできませんでした。ネットワークを確認すると、URL を切り替えた後の最初のリクエストがキャンセルとして表示されることがわかりました。解決策は、このリクエストをフィルタリングすることです
ここに画像の説明を挿入します

  videojs.hook('beforeerror', (player, err) => {
    // Video.js 在切换/指定 source 后立即会触发一个 err=null 的错误,这里过滤一下
    if (err !== null) {
      myPlayer.value.src(url);
    }

    // 清除错误,避免 error 事件在控制台抛出错误
    return null;
  });

リクエストは正常ですが、ビデオはまだロードできず、
エラーが報告されます。解決策: ソースを切り替えるときに元のコンポーネントを破棄し、videojs を再度追加して値を割り当てることしかできません。

補足: コンソールはエラーを報告しますが、ビデオの通常の表示には影響しません。理由: video.js と videojs-contrib-hls のバージョンの間に競合があります。解決策: videojs バージョンを 7.20.3 から videojs 7.5.5\videojs-contrib-hls 5.15.0 にダウングレードしても、エラーは報告されません。

ここに画像の説明を挿入します

最後に、実装を成功させるための完全なコードが添付されています。

テンプレート部分

<template>
  <div id="video-box" class="video-item" v-show="showVideo">
  </div>
</template>

JSパート

const myPlayer = ref<any>(null);
const mountedFlag = ref<boolean>(false);
// 初始化视频组件
const initVideo = (url:string) => {
  if (myPlayer.value) {
    myPlayer.value.pause();
    myPlayer.value.dispose();
  }
  // 向Dom中写入视频组件
  document.getElementById('video-box').innerHTML = '';
  const html = `<video id="fire-video" class="video-js vjs-default-skin vjs-big-play-centered" controls preload="auto" style="width: 100%;height:100%">
        <source id="source" src="${url}" type="application/x-mpegURL">
    </video>`;

  document.getElementById('video-box').innerHTML = html;
  // 初始化声明
  myPlayer.value = videojs('fire-video',{
    autoplay: 'muted',
    bigPlayButton: false,
    textTrackDisplay: false,
    posterImage: true,
    errorDisplay: false,
    controlBar: true,
  });
  videojs.hook('beforeerror', (player, err) => {
    // Video.js 在切换/指定 source 后立即会触发一个 err=null 的错误,这里过滤一下
    if (err !== null) {
      myPlayer.value.src(url);
    }

    // 清除错误,避免 error 事件在控制台抛出错误
    return null;
  });

  myPlayer.value.play();
};

// 监听,需要视频url由其他方法赋值且组件挂载完成后,才能进行初始化
watch([cameraURL,mountedFlag,], ([url,flag,]): void => {
  if (url && flag) {
    initVideo(url);
  }
});

onMounted(() => {
  setTimeout(() => {
    mountedFlag.value = true;
  }, 500);
});
// 组件销毁
onUnmounted(() => {
  if (myPlayer.value) {
    mountedFlag.value = false;
  }
});

参考ドキュメント:

https://cloud.tencent.com/developer/article/1782402
https://blog.csdn.net/u012961419/article/details/120989905

おすすめ

転載: blog.csdn.net/layliangbo/article/details/127075607