關于vue2中對接海康攝像頭以及直播流rtsp或rtmp,后臺ffmpeg轉碼后通過ws實現

最近項目中需要對接攝像頭監控,海康攝像頭為rtsp流格式

有一個軟件VLC media player,可以在線進行rtsp或者rtmp流播放,可用來測試流地址是否可用

功能實現思路為后臺通過fmpeg把rtsp流進行轉碼,然后通過ws方式進行一幀一幀推送。(還有一種時后臺通過fmpeg轉為hls格式,但是這樣會在項目中生產一張張圖片,所以我們沒有考慮)

首先后臺我先使用node進行了一個測試dme,網上找了一個測試的rtmp地址:

rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid? //好像是個游戲直播

const { spawn } = require('child_process');
const WebSocket = require('ws');const wss = new WebSocket.Server({ port: 9999 });wss.on('connection', (ws) => {console.log('WebSocket 客戶端連接');// 關鍵修改1:使用完整FFmpeg路徑(避免環境變量問題)const ffmpegPath = 'D:\\rj\\ffmpeg\\bin\\ffmpeg.exe'; // 自己電腦的ffmpeg地址,自行更換確保路徑存在// 關鍵修改2:簡化參數(移除可能沖突的選項)const args = ['-i', 'rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid', //網上的游戲直播地址'-f', 'mpegts','-codec:v', 'mpeg1video','-'];//   // rtsp的配置,和rtp略有區別
//  const args= [
//     '-rtsp_transport', 'tcp',       // 強制TCP傳輸
//     '-timeout', '5000000',          // 5秒超時
//     '-re',                          // 按原始速率讀取
//     '-i', 'rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4',
//  '-f', 'mpegts',
//   '-codec:v', 'mpeg1video',       // JSMpeg兼容編碼
//   '-b:v', '800k',                 // 視頻比特率
//   '-r', '25',                     // 幀率
//   '-vf', 'scale=640:480',         // 分辨率縮放
//   '-preset', 'ultrafast',         // 最快編碼預設
//   '-fflags', 'nobuffer',          // 減少緩沖
//   '-'
//   ]// 關鍵修改3:顯式傳遞環境變量const env = { ...process.env, PATH: process.env.PATH }; // 繼承當前環境const ffmpeg = spawn(ffmpegPath, args, { env: env,stdio: ['ignore', 'pipe', 'pipe'] // 忽略stdin,捕獲stdout/stderr});// 打印FFmpeg日志(調試用)ffmpeg.stderr.on('data', (data) => {console.log('[FFmpeg]', data.toString());});// 轉發數據到WebSocketffmpeg.stdout.on('data', (data) => {if (ws.readyState === ws.OPEN) {ws.send(data, { binary: true });}});ws.on('close', () => {ffmpeg.kill('SIGTERM');});
});

代碼寫完后通過node運行

前端在vue中采用? ?@cycjimmy/jsmpeg-player 或者?vue-jsmpeg-player,關于這點,因為vue-jsmpeg-player必須要求vue?2.6.12 以上,版本地低的話有問題,我這邊采用@cycjimmy/jsmpeg-player

1.使用@cycjimmy/jsmpeg-player

npm install?@cycjimmy/jsmpeg-player

npm下載安裝后新建一個播放直播流的vue組件,代碼如下

<template><div class="video-player"><!-- 視頻畫布容器 --><div class="video-container" ref="videoContainer"><canvas ref="videoCanvas" class="video-canvas"></canvas><!-- 加載狀態 --><div v-if="isLoading" class="loading-overlay"><div class="spinner"></div><p class="loading-text">加載中...</p></div><!-- 錯誤提示 --><div v-if="hasError" class="error-overlay"><p class="error-text">無法加載視頻流,請檢查連接</p><button @click="reloadPlayer" class="reload-btn">重新加載</button></div></div><!-- 控制欄 --><div class="controls-bar"><div class="controls-group"><!-- 播放/暫停按鈕 --><button class="control-btn" @click="togglePlay":title="isPlaying ? '暫停' : '播放'"><i class="fas" :class="isPlaying ? 'fa-pause' : 'fa-play'"></i></button><!-- 音量控制 --><div class="volume-control"><button class="control-btn volume-btn"@click="toggleMute":title="isMuted ? '取消靜音' : '靜音'"><i class="fas" :class="isMuted ? 'fa-volume-mute' : 'fa-volume-up'"></i></button><inputtype="range"min="0"max="100"v-model="volume"@input="setVolume"class="volume-slider":title="`音量: ${volume}%`"></div></div><div class="controls-group"><!-- 全屏按鈕 --><button class="control-btn" @click="toggleFullscreen":title="isFullscreen ? '退出全屏' : '進入全屏'"><i class="fas" :class="isFullscreen ? 'fa-compress' : 'fa-expand'"></i></button></div></div></div>
</template><script>
import JSMpeg from '@cycjimmy/jsmpeg-player';export default {name: 'VideoPlayer',props: {// 視頻流地址streamUrl: {type: String,required: true,default: 'ws://localhost:9999' //node后臺1的ws地址},// 封面圖poster: {type: String,default: ''}},data() {return {player: null,isPlaying: false,volume: 80,isMuted: false,isFullscreen: false,isLoading: true,hasError: false};},mounted() {this.initPlayer();this.addEventListeners();},beforeUnmount() {this.destroyPlayer();this.removeEventListeners();},methods: {// 初始化播放器initPlayer() {this.isLoading = true;this.hasError = false;try {this.player = new JSMpeg.Player(this.streamUrl, {canvas: this.$refs.videoCanvas,autoplay: false,loop: false,controls: false,poster: this.poster,decodeFirstFrame: true,volume: this.volume / 100,// 事件回調onPlay: () => {this.isPlaying = true;this.isLoading = false;},onPause: () => {this.isPlaying = false;},onEnded: () => {this.isPlaying = false;},onError: () => {this.hasError = true;this.isLoading = false;this.isPlaying = false;}});} catch (error) {console.error('播放器初始化失敗:', error);this.hasError = true;this.isLoading = false;}},// 銷毀播放器destroyPlayer() {if (this.player) {this.player.destroy();this.player = null;}},// 重新加載播放器reloadPlayer() {this.destroyPlayer();this.initPlayer();},// 切換播放/暫停togglePlay() {if (!this.player) return;if (this.isPlaying) {this.player.pause();} else {// 處理瀏覽器自動播放限制this.player.play().catch(err => {console.warn('自動播放失敗,需要用戶交互:', err);this.showPlayPrompt();});}},// 顯示播放提示(用于自動播放限制)showPlayPrompt() {const container = this.$refs.videoContainer;const prompt = document.createElement('div');prompt.className = 'play-prompt';prompt.innerHTML = '<i class="fas fa-play"></i><p>點擊播放</p>';container.appendChild(prompt);prompt.addEventListener('click', () => {this.player.play();container.removeChild(prompt);}, { once: true });},// 切換靜音toggleMute() {if (!this.player) return;this.isMuted = !this.isMuted;this.player.volume = this.isMuted ? 0 : this.volume / 100;},// 設置音量setVolume() {if (!this.player) return;this.isMuted = this.volume === 0;this.player.volume = this.volume / 100;},// 切換全屏toggleFullscreen() {const container = this.$refs.videoContainer;if (!document.fullscreenElement) {container.requestFullscreen().catch(err => {console.error(`全屏錯誤: ${err.message}`);});this.isFullscreen = true;} else {if (document.exitFullscreen) {document.exitFullscreen();this.isFullscreen = false;}}},// 監聽全屏狀態變化handleFullscreenChange() {this.isFullscreen = !!document.fullscreenElement;},// 添加事件監聽器addEventListeners() {document.addEventListener('fullscreenchange', this.handleFullscreenChange);},// 移除事件監聽器removeEventListeners() {document.removeEventListener('fullscreenchange', this.handleFullscreenChange);}}
};
</script><style scoped>
.video-player {position: relative;width: 100%;max-width: 1200px;margin: 0 auto;border-radius: 8px;overflow: hidden;box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}.video-container {position: relative;width: 100%;background-color: #000;aspect-ratio: 16 / 9; /* 保持視頻比例 */
}.video-canvas {width: 100%;height: 100%;object-fit: contain;
}/* 加載狀態 */
.loading-overlay {position: absolute;top: 0;left: 0;width: 100%;height: 100%;display: flex;flex-direction: column;justify-content: center;align-items: center;background-color: rgba(0, 0, 0, 0.5);color: white;z-index: 10;
}.spinner {width: 40px;height: 40px;border: 4px solid rgba(255, 255, 255, 0.3);border-radius: 50%;border-top: 4px solid white;animation: spin 1s linear infinite;margin-bottom: 16px;
}@keyframes spin {0% { transform: rotate(0deg); }100% { transform: rotate(360deg); }
}.loading-text {font-size: 16px;font-weight: 500;
}/* 錯誤提示 */
.error-overlay {position: absolute;top: 0;left: 0;width: 100%;height: 100%;display: flex;flex-direction: column;justify-content: center;align-items: center;background-color: rgba(0, 0, 0, 0.7);color: white;z-index: 10;padding: 20px;text-align: center;
}.error-text {font-size: 16px;margin-bottom: 20px;max-width: 400px;
}.reload-btn {background-color: #42b983;color: white;border: none;padding: 8px 16px;border-radius: 4px;cursor: pointer;font-size: 14px;transition: background-color 0.2s;
}.reload-btn:hover {background-color: #359e75;
}/* 播放提示 */
::v-deep .play-prompt {position: absolute;top: 0;left: 0;width: 100%;height: 100%;display: flex;flex-direction: column;justify-content: center;align-items: center;background-color: rgba(0, 0, 0, 0.5);color: white;z-index: 9;cursor: pointer;
}::v-deep .play-prompt i {font-size: 48px;margin-bottom: 16px;transition: transform 0.2s;
}::v-deep .play-prompt:hover i {transform: scale(1.1);
}/* 控制欄 */
.controls-bar {display: flex;justify-content: space-between;align-items: center;padding: 12px 20px;background-color: #1a1a1a;color: white;
}.controls-group {display: flex;align-items: center;gap: 16px;
}.control-btn {background: none;border: none;color: white;font-size: 18px;cursor: pointer;width: 36px;height: 36px;border-radius: 50%;display: flex;align-items: center;justify-content: center;transition: background-color 0.2s;
}.control-btn:hover {background-color: rgba(255, 255, 255, 0.15);
}/* 音量控制 */
.volume-control {display: flex;align-items: center;gap: 8px;
}.volume-slider {width: 100px;height: 4px;-webkit-appearance: none;background: rgba(255, 255, 255, 0.2);border-radius: 2px;outline: none;
}.volume-slider::-webkit-slider-thumb {-webkit-appearance: none;width: 14px;height: 14px;border-radius: 50%;background: white;cursor: pointer;transition: transform 0.1s;
}.volume-slider::-webkit-slider-thumb:hover {transform: scale(1.2);
}/* 響應式調整 */
@media (max-width: 768px) {.controls-bar {padding: 8px 12px;}.controls-group {gap: 8px;}.control-btn {font-size: 16px;width: 32px;height: 32px;}.volume-slider {width: 80px;}
}
</style>

然后在父組件中引入使用就可以了。

2.使用?vue-jsmpeg-player

npm i?vue-jsmpeg-player@1.1.0-beta

安裝后在main.js全局引入:


import JSMpegPlayer from 'vue-jsmpeg-player';
import 'vue-jsmpeg-player/dist/jsmpeg-player.css';Vue.use(JSMpegPlayer)

然后新建vue組件:

<template><div><jsmpeg-player :url="url" /></div>
</template><script>
export default {components: {},data() {return {//后臺轉發的ws地址url: "ws://10.10.10.113:9999", //后臺ws地址};},computed: {},mounted() {// jsmpeg-player組件的使用說明//     url	string	視頻流地址(推薦websocket,實際上jsmpeg.js原生也支持http方式,但沒有經過測試)// title	string	播放器標題// no-signal-text	string	無信號時的顯示文本// options	object	jsmpeg原生選項,直接透傳,詳見下表// closeable	boolean	是否可關閉(單擊關閉按鈕,僅拋出事件)// in-background	boolean	是否處于后臺,如el-tabs的切換,路由的切換等,支持.sync修飾符// show-duration	boolean	是否現實持續播放時間// default-mute	boolean	默認靜音// with-toolbar	boolean	是否需要工具欄// loading-text	boolean	加載時的文本,默認為:拼命加載中},beforeDestroy() {},methods: {},
};
</script><style lang="scss">
</style>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/92431.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/92431.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/92431.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Docker容器強制刪除及文件系統修復完整指南

Docker容器強制刪除及文件系統修復完整指南 故障現象與原因分析 ?故障表現?&#xff1a; ERROR: for c9ca40be974d_OpIsosMD_OB unable to remove filesystem unlinkat /data/docker/storage/containers/c9ca40be974d...: structure needs cleaning?根本原因?&#xff1a;…

Matplotlib 知識點總結

1. 基礎繪圖&#xff08;plot函數&#xff09;基本語法&#xff1a;plot([x], y, [fmt], [x2], y2, [fmt2], ..., **kwargs)功能特點&#xff1a;可繪制點、線和組合圖形自動生成x軸&#xff08;0-N-1&#xff09;當x未指定時示例&#xff1a;繪制兩點連線、多點不規則線等代碼…

高可用微服務架構實戰:Nacos集群+Nginx負載均衡,Spring Cloud無縫對接

"當你的注冊中心掛了&#xff0c;整個微服務就變成了無頭蒼蠅。" 這是我在生產環境踩坑后最痛的領悟。今天&#xff0c;我將分享如何用Nacos集群Nginx搭建堅如磐石的注冊中心&#xff0c;讓你的微服務永不迷路&#xff01; 在 Windows 環境下配置 Nacos 集群&#x…

Spark大數據處理實戰指南

Spark 簡介 Apache Spark 是一個開源的分布式計算框架,專為大規模數據處理而設計。它通過內存計算和優化的執行引擎顯著提升了數據處理速度,適用于批處理、實時流處理、機器學習和圖計算等場景。 核心特性 高性能:利用內存計算(In-Memory Processing)減少磁盤 I/O,比傳…

瀏覽器緩存機制全解析:強緩存與協商緩存

瀏覽器緩存是瀏覽器為提升頁面加載速度、減少服務器壓力和節省網絡帶寬&#xff0c;在本地存儲資源&#xff08;如 HTML、CSS、JS、圖片等&#xff09;的機制。其核心分為強緩存和協商緩存&#xff0c;并涉及多種 HTTP 頭字段和存儲位置。以下是詳細解析&#xff1a;?? 一、緩…

知識隨記-----Qt 實用技巧:自定義倒計時按鈕防止用戶頻繁點擊

Qt 技巧&#xff1a;實現自定義倒計時按鈕防止用戶頻繁點擊注冊 項目場景 在一個基于 Qt 開發的聊天應用中&#xff0c;用戶注冊時需要獲取驗證碼。為防止用戶頻繁點擊獲取驗證碼按鈕&#xff0c;需要實現一個倒計時功能&#xff0c;用戶點擊后按鈕進入倒計時狀態&#xff0c;倒…

Linux與Windows應急響應

本人首先進行了linux的應急響應&#xff0c;windows之后再進行 Linux與Windows應急響應初體驗1 linux應急響應1.1 賬戶&#xff1a;1.1.1 使用cat /etc/passwd命令查看passwd文件2.1.2 使用cat /etc/shadow命令查找shadow文件&#xff0c;該文件為密碼文件的存儲項1.2 入侵排查…

計算機網絡1-4:計算機網絡的定義和分類

目錄 計算機網絡的定義 計算機網絡的分類 計算機網絡的定義 計算機網絡的分類 按交換技術分類&#xff1a;電路交換網絡、報文交換網絡、分組交換網絡 按使用者分類&#xff1a;公用網、專用網 按傳輸介質分類&#xff1a;有線網絡、無線網絡 按覆蓋范圍分類&#xff1a;…

在QT中動態添加/刪除控件,伸縮因子該怎么處理

開發中遇到的問題[TOC](開發中遇到的問題)處理方式在我們的界面開發過程中&#xff0c;通常需要開發一些可以動態添加or刪除控件的容器&#xff0c;類似Tab頁一樣&#xff0c;為了美觀的話&#xff0c;我們通常使用伸縮因子將容器中的控件往一個方向擠&#xff0c;類似下面的控…

【設計模式精解】什么是代理模式?徹底理解靜態代理和動態代理

目錄 靜態代理 動態代理 JDK動態代理 CGLIB代理 JDK動態代理和CGLIB代理的區別 總結 代理模式簡單來說就是 我們使用代理對象來代替對真實對象(real object)的訪問&#xff0c;這樣就可以在不修改原目標對象的前提下&#xff0c;擴展目標對象的功能。 代理模式有靜態代理…

MCU AI/ML - 彌合智能和嵌入式系統之間的差距

作者&#xff1a;芯科科技產品營銷高級經理Gopinath Krishniah 人工智能&#xff08;AI&#xff09;和機器學習&#xff08;ML&#xff09;是使系統能夠從數據中學習、進行推理并隨著時間的推移提高性能的關鍵技術。這些技術通常用于大型數據中心和功能強大的GPU&#xff0c;但…

Redis中的sdshdr的len和alloc那塊的知識點詳解

文章目錄核心比喻&#xff1a;一個可以伸縮的水瓶場景一&#xff1a;創建一個新字符串場景二&#xff1a;追加字符串&#xff08;觸發“空間預分配”&#xff09;場景三&#xff1a;再次追加字符串&#xff08;利用空閑空間&#xff09;場景四&#xff1a;縮短字符串&#xff0…

在Linux下訪問MS SQL Server數據庫

Linux作為一個免費的Unix類操作系統&#xff0c;以其開放性源代碼、多任務、X window等特點為眾多的用戶所采用&#xff0c;并有很多企業采用Linux來作為其內部網的全功能服務器(WWW&#xff0c;FTP&#xff0c;Email、DNS)。企業的內部網不僅要提供文本信息的訪問&#xff0c;…

計算機視覺-OpenCV

一下載第三方庫opencv-python3.4.18.65opencv-contrib-python3.4.18.65import cv2 # 讀取的格式是BGR numpy import numpy as np# 讀取圖片 a cv2.imread(generated_image.jpg) # 讀取圖片 print(a) # NumPy數組&#xff0c;其中存儲了讀取的圖像文件的像素值。cv2.imshow…

解決GitHub無法打開

找到下圖文件&#xff0c;用記事本打開 在最下方粘貼如下代碼140.82.113.4 github.com 20.205.243.166 github.com 140.82.112.4 github.com 151.101.1.6 github.global.ssl.fastly.net 185.199.108.153 assets-cdn.github.com 185.199.109.153 assets-cdn.github.com 185.199.…

AWS VPC Transit Gateway 可觀測最佳實踐

AWS VPC Transit Gateway 介紹 Amazon VPC Transit Gateway 是一個網絡傳輸中心&#xff0c;用于互連虛擬私有云 (VPCs) 和本地網絡。隨著您的云基礎設施在全球擴展&#xff0c;區域間對等互連使用 AWS 全球基礎設施將中轉網關連接在一起。 AWS 數據中心之間的所有網絡流量都在…

WeakRef的作用和使用

文章目錄WeakRef的作用和使用使用 WeakRef 避免強引用&#xff1a;原理與實踐一、WeakRef 的核心特性二、WeakRef 與強引用的對比三、WeakRef 的使用場景與示例1. 非關鍵數據緩存&#xff08;避免緩存導致內存泄漏&#xff09;2. 跟蹤對象生命周期&#xff08;不干擾回收&#…

【華為機試】332. 重新安排行程

文章目錄332. 重新安排行程題目描述示例 1&#xff1a;示例 2&#xff1a;提示&#xff1a;解題思路核心思路算法流程圖歐拉路徑原理DFS回溯機制字典序優化策略復雜度分析算法實現要點完整題解代碼332. 重新安排行程 題目描述 給你一份航線列表 tickets &#xff0c;其中 tic…

通信算法之300:CRC表生成方式-CRC8、CRC16、CRC32-輸入字節

"CRC表的MATLAB生成代碼"生成的查找表可以用于快速計算 CRC 值&#xff0c;通過查表法可以顯著提高 CRC 計算效率&#xff0c;尤其適用于需要處理大量數據的場景。下面是一個生成 CRC 查找表&#xff08;CRC Table&#xff09;的 MATLAB 代碼&#xff0c;該代碼可以根…

國內使用 npm 時配置鏡像源

在國內使用 npm 時&#xff0c;由于網絡限制可能會遇到下載速度慢或連接超時的問題。通過設置國內鏡像源&#xff0c;可以顯著提升下載速度和穩定性。以下是常用的國內 npm 鏡像源及其配置方法。 查詢當前使用的鏡像源 npm get registry 設置為淘寶鏡像源 npm config set reg…