WebRTC音视频开发:React+Flutter+Go实战
上QQ阅读APP看书,第一时间看更新

5.3 分辨率设置

在WebRTC中,分辨率的设置是通过获取视频时传递约束条件来控制的,如下面的代码所示,表示要设置成一个高清720P的分辨率。这样当调用getUserMedia()时会采用1280×720的尺寸采集。


//高清约束条件
const hdConstraints = {
    //视频
    video: { 
    //宽
    width: { exact: 1280 }, 
    //高
    height: { exact: 720 } 
    }
};

接下来通过一个示例来详细阐述如何设置不同的分辨率。具体步骤如下。

步骤1 打开h5-samples工程下的src目录,添加Resolution.jsx文件。添加QVGA、VGA、720P、1080P、2K、4K以及8K的约束条件。以QVGA为例,代码如下所示。


//QVGA 320*240
const qvgaConstraints = {
    video: { width: { exact: 320 }, height: { exact: 240 } }
};

步骤2 编写根据不同约束条件获取视频流的方法,这里的约束代表了不同的分辨率。在重新获取视频之前,需要把当前stream里的所有流都停掉。大致处理如下所示。


//根据约束获取视频
getMedia = (constraints) => {
    ...
    //迭代并停止所有轨道
    stream.getTracks().forEach(track => {
        track.stop();
    });

    //重新获取视频
    navigator.mediaDevices.getUserMedia(constraints)
    ...
}

流中通常是有多个轨道的,可以使用forEach将其所有轨道迭代出来,然后调用stop方法停止。

步骤3 当获取到流后,将流传递给video对象渲染出来即可。代码如下所示。


gotStream = (mediaStream) => {
    stream = window.stream = mediaStream;
    //将video视频源指定为mediaStream
    video.srcObject = mediaStream;
}

步骤4 编写下拉列表框回调方法,根据选中的项传递不同的约束,代码大致如下所示。


//传递QVGA约束
getMedia(qvgaConstraints);
//传递VGA约束
getMedia(vgaConstraints);
...
//传递高清约束
getMedia(hdConstraints);

步骤5 上述方法每次改变分辨率时都需要重新调用getUserMedia()方法,这样需要重新请求访问摄像头。还有另一种方法可以动态改变分辨率。关键代码如下所示。


dynamicChange = (e) => {
    //获取当前的视频流中的视频轨道
    const track = window.stream.getVideoTracks()[0];
    ...
    //改变轨道的约束条件
    track.applyConstraints(constraints)
    ...
}

这里使用了MediaStreamTrack的applyConstraints()方法,此方法可以动态改变约束条件。打开https://developer.mozilla.org/zh-CN/docs/Web/API/MediaStreamTrack网址可以查看详细使用说明。

步骤6 在页面渲染部分添加video标签、分辨率下拉列表框等界面元素,然后在src目录下的App.jsx及Samples.jsx里加上链接及路由绑定,这可参考第3章。完整的代码如下所示。


import React from "react";
import { Button, Select } from "antd";

const { Option } = Select;

//QVGA 320*240
const qvgaConstraints = {
    video: { width: { exact: 320 }, height: { exact: 240 } }
};

//VGA 640*480
const vgaConstraints = {
    video: { width: { exact: 640 }, height: { exact: 480 } }
};

//高清 1280*720
const hdConstraints = {
    video: { width: { exact: 1280 }, height: { exact: 720 } }
};

//超清 1920*1080
const fullHdConstraints = {
    video: { width: { exact: 1920 }, height: { exact: 1080 } }
};

//2K 2560*1440
const twoKConstraints = {
    video: { width: { exact: 2560 }, height: { exact: 1440 } }
};

//4K 4096*2160
const fourKConstraints = {
    video: { width: { exact: 4096 }, height: { exact: 2160 } }
};

//8K 7680*4320
const eightKConstraints = {
    video: { width: { exact: 7680 }, height: { exact: 4320 } }
};

//视频流
let stream;
//视频对象
let video;

/**
 * 分辨率示例
 */
class Resolution extends React.Component {

    componentDidMount() {
        //获取video对象引用
        video = this.refs['video'];
    }

    //根据约束获取视频
    getMedia = (constraints) => {
        //判断流对象是否为空
        if (stream) {
            //迭代并停止所有轨道
            stream.getTracks().forEach(track => {
                track.stop();
            });
        }
        //重新获取视频
        navigator.mediaDevices.getUserMedia(constraints)
            //成功获取
            .then(this.gotStream)
            //错误
            .catch(e => {
                this.handleError(e);
            });
    }

    //得到视频流处理
    gotStream = (mediaStream) => {
        stream = window.stream = mediaStream;
        //将video视频源指定为mediaStream
        video.srcObject = mediaStream;
        const track = mediaStream.getVideoTracks()[0];
        const constraints = track.getConstraints();
        console.log('约束条件为:' + JSON.stringify(constraints));
    }

    //错误处理
    handleError(error) {
        console.log(`getUserMedia错误: ${error.name}`, error);
    }

    //下拉列表框中的选项改变
    handleChange = (value) => {
        console.log(`selected ${value}`);
        //根据下拉列表框的值获取不同分辨率的视频
        switch (value) {
            case 'qvga':
                this.getMedia(qvgaConstraints);
                break;
            case 'vga':
                this.getMedia(vgaConstraints);
                break;
            case 'hd':
                this.getMedia(hdConstraints);
                break;
            case 'fullhd':
                this.getMedia(fullHdConstraints);
                break;
            case '2k':
                this.getMedia(twoKConstraints);
                break;
            case '4k':
                this.getMedia(fourKConstraints);
                break;
            case '8k':
                this.getMedia(eightKConstraints);
                break;
            default:
                this.getMedia(vgaConstraints);
                break;
        }
    }

    //动态改变分辨率
    dynamicChange = (e) => {
        //获取当前视频流中的视频轨道
        const track = window.stream.getVideoTracks()[0];
        //使用超清约束作为测试条件
        console.log('应用高清效果:' + JSON.stringify(hdConstraints));
        track.applyConstraints(constraints)
            .then(() => {
              console.log('动态改变分辨率成功...');
            })
            .catch(err => {
              console.log('动态改变分辨率错误:', err.name);
            });
      }

    render() {
        return (
            <div className="container">
                <h1>
                    <span>视频分辨率示例</span>
                </h1>
                {/* 视频渲染 */}
                <video ref='video' playsInline autoPlay></video>
                {/* 清晰度选择 */}
                <Select defaultValue="vga" style={{ width: '100px',marginLeft:'20px' }} onChange={this.handleChange}>
                    <Option value="qvga">QVGA</Option>
                    <Option value="vga">VGA</Option>
                    <Option value="hd">高清</Option>
                    <Option value="fullhd">超清</Option>
                    <Option value="2k">2K</Option>
                    <Option value="4k">4K</Option>
                    <Option value="8k">8K</Option>
                </Select>
                <Button onClick={this.dynamicChange} style={{ marginLeft:'20px' }}>动态设置</Button>
            </div>
        );
    }
}
//导出组件
export default Resolution;

运行程序后,打开视频分辨率示例,点击下拉列表框可以选择不同的分辨率,运行效果如图5-1所示。

图5-1 视频分辨率切换

如果你的显示器或摄像头不支持某分辨率,则会报错。以笔者的笔记本为例,2K、4K以及8K是不支持的,这样就会报OverconstrainedError错误,控制台输出的内容如下所示。


getUserMedia错误: OverconstrainedError OverconstrainedError {name: "OverconstrainedError", message: null, constraint: "width"}

当控制台输出OverconstrainedError错误时,表示硬件无法满足约束条件,此时可以通过升级硬件或降低约束条件来解决这个问题。

当点击“动态设置”按钮时,也会切换用户分辨率,输出内容如下。


应用高清效果:{"video":{"width":{"exact":1280},"height":{"exact":720}}}
Resolution.jsx:130 动态改变分辨率成功...