1.0.1 • Published 1 year ago

node-live-stream-jsmpeg v1.0.1

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

node-live-stream-jsmpeg

推荐转到项目 node-live-stream-h5-flv

  • 将任意的视频直播流转换成rtsp流, 并在浏览器中播放, 依赖于ffmpeg进行流转换.

  • 项目基于node-rtsp-stream进行二次修改,使用ffmpeg可以将任意的直播类型转换成rtsp流,之后可以在网页上进行播放。

  • 一般配合electron开发跨平台的看直播。
  • node-live-stream-jsmpeg

Usage

yarn add node-live-stream-jsmpeg
或者
npm install node-live-stream-jsmpeg
yarn add ffmpeg-static
或者
npm install ffmpeg-static

结合electron使用

import { app, BrowserWindow, shell, ipcMain } from 'electron'
/**
 * 需要加载ffmpeg-static
 */
import ffmpegPath from "ffmpeg-static";
import VideoStream from "node-live-stream-jsmpeg";
/**
 * electron 自带启动代码,参考默认的electron代码
 * @returns {Promise<void>}
 */
async function createWindow() {
    win = new BrowserWindow({
        title: 'Main window',
        icon: join(process.env.PUBLIC, 'favicon.ico'),
        webPreferences: {
            // Warning: Enable nodeIntegration and disable contextIsolation is not secure in production
            // Consider using contextBridge.exposeInMainWorld
            // Read more on https://www.electronjs.org/docs/latest/tutorial/context-isolation
            nodeIntegration: true,
            contextIsolation: false,
            webSecurity: false
        },
    })
    // ........ 省略代码,参考electron官方
}
app.on('window-all-closed', () => {
    for (const liveOpender in liveOpenders) {
        try {
            liveOpenders[liveOpender].stream.stop()
        }catch (e) {
            console.log(e)
        }
    }
    liveOpenders = {}
    win = null
    if (process.platform !== 'darwin') app.quit()
})

/**
 * 开启live stream
 * @param liveUrl {String} 直播流地址
 */
ipcMain.on('openLiveStream', (event, liveUrl) => {
    const wsPort = Math.floor(Math.random()*(1 - 100) + 100) + 64000;
    if (streamOpenders[liveUrl]) {
        event.returnValue = {
            code: 200,
            msg: '开启成功',
            ws: streamOpenders[liveUrl].ws,
            videoSize: streamOpenders[liveUrl].videoSize
        }
    } else {
        const stream = new VideoStream({
            streamUrl: liveUrl,
            wsPort: wsPort,
            ffmpegPath: app.isPackaged ? ffmpegPath.replace('app.asar', 'app.asar.unpacked') : ffmpegPath,
            // printFFmpegStdout: false
        })
            .on('getStreamInfoError' || "forwardStreamExit", () => {
                console.log("打开视频流失败,请检查网络和视频流地址.")
                stream.stop()
                delete streamOpenders[liveUrl]
                event.returnValue = {
                    code: 400,
                    msg: '开启失败'
                }
            })
            .once('forwardStreamSuccess', (data) => {
                console.log(`打开视频流成功,视频信息: ${JSON.stringify(data)}`)
                streamOpenders[liveUrl] = {
                    ws: `ws://localhost:${wsPort}`,
                    stream: stream,
                    videoSize: data
                }
                event.returnValue = {
                    code: 200,
                    msg: '开启成功',
                    ws: streamOpenders[liveUrl].ws,
                    videoSize: data
                }
            })
    }
})

/**
 * 关闭live stream
 * @param liveUrl {String} 直播流地址
 */
ipcMain.on('closeLiveStream', (event, liveUrl) => {
    if (streamOpenders[liveUrl]) {
        // 停止解析
        streamOpenders[liveUrl].stream.stop()
        // 删除该项
        delete streamOpenders[liveUrl]
        // 返回结果
        event.returnValue = {
            code: 200,
            msg: '关闭成功'
        }
    } else {
        event.returnValue = {
            code: 400,
            msg: `未找到该${liveUrl}`
        }
    }
})
  • 网页代码index.html或者app.vue

  • 在网页中播放,可以直接使用jsmpeg-player.

  • jsmpeg-player
<script setup lang="ts">
    import { ref } from 'vue'
    import JSMpeg from '@cycjimmy/jsmpeg-player';
    const { ipcRenderer } = require('electron')
    const rtspUrl = ref('')
    const mpegPlayer = ref()
    const msg = ref('')
    let player: any = null
    const vHeight = ref("700px")
    const vWidth = ref("1300")
    const open = () => {
        const res = ipcRenderer.sendSync('openLiveStream', rtspUrl.value)
        if (res.code === 200) {
            player = new JSMpeg.VideoElement(mpegPlayer.value, res.ws)
        }
        msg.value = res.msg
        vHeight.value = res.videoSize.height + "px"
        vWidth.value = res.videoSize.width + "px"
    }
    const close = () => {
        const res = ipcRenderer.sendSync('closeLiveStream', rtspUrl.value)
        msg.value = res.msg
    }
</script>

<template>
  <div class="flexBox">
    <input type="text" v-model="rtspUrl">
    <button @click="open">打开直播流</button>
    <button @click="close">关闭直播流</button>
  </div>
  <div class="mpegPlayer" ref="mpegPlayer"></div>
  <div>{{msg}}</div>
</template>

<style>
* {
  padding: 0;
  margin: 0;
}
.flexBox {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 50px;
}
.flexBox input  {
  height: 30px;
  width: 500px;
  box-sizing: border-box;
  padding-left: 8px;
}
.flexBox button {
  height: 30px;
  padding: 0 12px;
}
.mpegPlayer {
  width: v-bind(vWidth);
  height: v-bind(vHeight);
  background: #ccc;
}
</style>

单App ES6标准使用

  • 示例代码NodeApp
  • Node应用 wsplay.mjs
  • npm install && npm run dev
import http from 'http';
import VideoStream from 'node-live-stream-jsmpeg';
import ffmpegPath from "ffmpeg-static";
import c from 'child_process'

const wsPort = Math.floor(Math.random()*(1 - 100) + 100) + 64000;
const httpPort = Math.floor(Math.random()*(1 - 100) + 100) + 64000;
const liveUrl = process.argv.slice(2)[0]
if (!liveUrl) {
    console.log(`Error 请输入直播流地址. \nUsage: \nnode ${process.argv.slice(1)[0]} rtmp://ns8.indexforce.com/home/mystream`)
    process.exit()
}
console.log('开始测试...')
const stream = new VideoStream({
    ffmpegPath: ffmpegPath,
    streamUrl: liveUrl,
    wsPort: wsPort,
    printFFmpegStdout: false
});
console.log(`ws地址为: ws://127.0.0.1:${wsPort}, 正在为你打开视频流,请稍等...`)
stream.once('getStreamInfoError' || "forwardStreamExit", () => {
    console.log('直播流打开失败, 请检查网络和视频流地址是否正确.')
    process.exit()
});
stream.once('forwardStreamSuccess', (data) => {
    console.log(`视频流打开成功, 视屏信息为: ${JSON.stringify(data)}, 浏览器中打开地址"http://127.0.0.1:${httpPort}/play", 进行观看.`)
    startHttpServer(data)
    console.log('启动浏览器进行播放...')
    const cmd = (process.platform==='win32') ?  `start http://127.0.0.1:${httpPort}/play` : `open http://127.0.0.1:${httpPort}/play`
    c.exec(cmd)
});
function startHttpServer( stream ) {
    console.log('启动网页服务...')
    http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.write(`
    <script src="https://cdn.jsdelivr.net/npm/@cycjimmy/jsmpeg-player@6.0.5/dist/jsmpeg-player.umd.min.js"></script>
    <center>
    <div id="videoWrapper" ></div>
    </center>
    <script>
    height = ${stream.height}
    width = ${stream.width}
    videoWrapper = document.getElementById('videoWrapper')
    videoWrapper.style = "width:" + width + "px;height:" + height + "px;"
    var videoUrl = 'ws://127.0.0.1:${wsPort}/';
    new JSMpeg.VideoElement('#videoWrapper', videoUrl);
    </script>
    `);
    res.end();
    }).listen(httpPort, "127.0.0.1");
}