Electron+React+WebRTC屏幕共享(2):获取视频流

书接上回,上次已经建立了可以使用的 Electron + React + WebRTC 开发环境,这次重点解决获取视频流的问题。

Mobx 管理状态

我使用 mobx 抽离 React 组件的内部状态数据,这样有两个好处,1 是方便状态共享,2 是可以把逻辑写在组件外面。

import { observable } from "mobx";

let rtcState = observable({
    sources:[],
    sourceId:-1,
    mediaStream:null,
    SetAllSources :function(sources){
        //梓喵出没博客(azimiao.com)设置所有源引用
        this.sources.replace(sources);
    },
    SetChoseId : function(index){
        //设置选择的 id zi喵出没博客(azimiao.com)
         try {
            this.sourceId = this.sources[index].id;
            return true;
        } catch (error) {
            // throw error;
            //梓miao出没·博客
            return false;
        }
    },
    GetMediaStream : function(){
        //获取视频流
    },
    Clear:function(){
        //清空信息
        this.sources = [];
        this.sourceId = -1;
        this.mediaStream = null;
    }
});
export {rtcState}

Electron 获取 WebRTC 窗口列表

我们知道,浏览器可以通过 navigator.mediaDevices.getUserMedia获取视频流,几行代码就可以搞定它:

navigator.mediaDevices.getUserMedia({
  audio:true,
  video:true
}).then((source)=>{
  console.log(source);
}).catch(e=>{
  alert(e);//梓喵出没博客(azimiao.com)
})

但是,执行上面的代码时,会有一个申请提示,如下图:

我们并不希望应用程序弹出这样的选择窗口,而是应该在应用程序中展示所有的源窗口供用户选择。

Electron 提供了desktopCapturer对象,通过它,我们可以获取DesktopCapturerSource数组,而DesktopCapturerSource拥有的属性如下:

{
    // 一个 window 或 screen 的标识符,
    //在调用 [navigator. webkitGetUserMedia] 时可【梓喵出没博客(azimiao.com)】作为 chromeMediaSourceId 约束。
    //标识符的格式是:window:XX或者 screen:XX,XX 是一个随机生成的数字
    id:"screen:0:0",
    //screen源将被命名为 Entire Screen 或 Screen<index> , 而window源的名称将与window标题匹配
    name:"Screen 0",
    //缩略图
    thumbnail:NativeImage,
    display_id:"0",
    appIcon:NativeImage
}

所以,通过desktopCapturer可以获取所有窗口的名称chromeMediaSourceId,缩略图,进而可以生成源窗口列表,相应代码如下:

desktopCapturer.getSources({ types: ["window", 'screen'] }).then((sources) => {
            rtcState.SetAllSources(sources);
        }).catch((err) => {
            alert("Error:" + err);
})

之后,点击对应的窗口时,在navigator.mediaDevices.getUserMedia使用chromeMediaSourceId即可获取媒体流。

//rtcState.jsx
GetMediaStream : function(){
    return  navigator.mediaDevices.getUserMedia({
        audio:{
            mandatory:{
                chromeMediaSource:"desktop"
            }
        },//梓Miao出没 a|z|m|i|a|o|.c-o-m
        video:{
            mandatory:{
                chromeMediaSource:"desktop",
                chromeMediaSourceId:rtcState.sourceId,
                minWidth:640,
                maxWidth:1920,
                minHeight:360,
                maxHeight:1080
            }
        }
    });
},

React 组件

通过 mobx 自动监听 rtcState.sources 的变化,渲染对应的列表:

render() {
    if (!Array.isArray(rtcState.sources) || rtcState.sources.length <= 0) {
        return (
            <div className="content">
            <h4><center>Now Fecth All Screen</center></h4>
            </div>
        );
    }
    return (<div className="content">{rtcState.sources.map((e, index) => {
        var thumb = e.thumbnail.toDataURL();
        if (thumb) {
            return <PrevScreen
            key={e.name}
            sourceId={index}
            imgUrl={thumb}
            title={e.name}
            onItemClick={this.onItemClick}
            />
        }
        })}
        </div>)
}

效果如下:

子组件PrevScreen通过onItemClick将点击时对应的sourceId传递回来。

onItemClick(index) {
    if(rtcState.SetChoseId(index)){
        console.log("try to jump start share");
        //最顶层组件中定义的状态机,用来切换不同子组件
        this.props.entryCallBack("testshow");
    }
}

最后,当rtcState.SetChoseId成功后,我们跳转至testshow界面。在testshow界面中,直接获取视频流并播放:

constructor(props){
    super(props);
    this.state = {};
    this.getSomething = this.getSomething.bind(this);
    this.getSomething();//梓喵出没博客(azimiao.com)
}
render(){
    return(
        <video style={{maxWidth:100 + "%",maxHeight:"auto"}} id="testId"></video>
    )
}
getSomething(){
    rtcState.GetMediaStream().then((stream)=>{
        document.getElementById("testId").srcObject = stream;
        document.getElementById("testId").play();
    }).catch(e=>{//梓喵出没博客(azimiao.com)
        console.log(e);
    })
}

效果如下图(本地测试时把获取流的 Audio 变为 false,不然会循环采集播放声音):

好了,到这里,获取视频流的核心内容已经全部讲完了。

梓喵出没博客(azimiao.com)版权所有,转载请注明链接:https://www.azimiao.com/6396.html
欢迎加入梓喵出没博客交流群:313732000

我来吐槽

*

*

0位绅士参与评论

  1. 坐等楼主更新