魔力的一页大屏flash插件直播

1,688 阅读3分钟
因为公司业务,需要配合flash插件做视频监控的直播。

flash插件只能在ie上跑,对于前端来说。呵呵

项目搭建

原有的项目是用的layui+jq搭建。现在将大屏部分独立出来;

开心到飞起,哈哈哈

现在大屏单独出来做,我采用了react + react-mobx;

选用react的原因是,对react比较熟练

选用mobx的原因是,项目内容比较简单,没有涉及到负责的数据处理

项目遇到的坑

<object id="plugin1" type="application/x-deepcam-p2p">
    <param name="onload" value="pluginLoaded" />
</object>

flash部分,完全脱离文档流,独立的窗口。不可以在上面定位任何的图标。这就导致很多功能无法去实现。

flash容易卡死在网页上,当用户频繁操作时。

flash视频音量的控制,实际上是控制整个计算机系统的音量,并不能像音乐播放器只控制单独的应用。

具体的实现

  • 一页大屏的适配

先来一个控制大屏占满全局的组件BaseComponent

class BaseComponent extends Component {
    constructor(props) {
        super(props);
        this.state = {
            ...super.state,
            screenWidth: props.width || -1,
            screenHeight: props.height || -1,
            topHeight: -1
        };
    }
    resizeScreen() {
        try {
            const parentDom = ReactDOM.findDOMNode(this).parentNode;
            let {
                width,
                height,
                topHeight
            } = this.props;
            if (!width) {
                width = parentDom.offsetWidth;
            }
            if (!height) {
                height = (width * global.settings.standardHeight) / global.settings.standardWidth;
            }
            if (!topHeight && height) {
                topHeight = (height * 120) / global.settings.standardHeight;
            }
            global.settings.screenHeight = height;
            global.settings.screenWidth = width;
            this.setState({
                screenWidth: width,
                screenHeight: height
            })
        } catch (ignore) {
            // debugger;
        }
    }
    
    scaleHeight(height, unit) {
        return (height * global.settings.screenHeight) / global.settings.standardHeight + (unit ? unit : 0);
    }

    scaleWidth(width, unit) {
        return (width * global.settings.screenWidth) / global.settings.standardWidth + (unit ? unit : 0);
    }
}

export default BaseComponent;
//config.js
global.settings = {
    screenWidth: 1408,
    screenHeight: 768,
    standardWidth: 1920, //设计图宽度
    standardHeight: 1080, //设计图宽度
}

这样就可以适配 浏览器了,在入口组件App.js中;

let timer;
let flag = false;
        
class App extends BaseComponent {
    constructor(props) {
        super(props);
        this.state = {
            width: this.state.screenWidth,
            height: this.state.screenHeight,
            showPage: false
        }
    }

    componentDidMount() {
        this.resizeScreen();
        window.addEventListener('resize',this.handleResize();
    }
    
    handleResize = () => {
        if (flag) {
            clearTimeout(timer);
            timer = setTimeout(() => {
                this.resizeScreen();
                // window.location.reload();
                flag = false;
            }, 100)
        } else {
            flag = true;
            timer = setTimeout(() => {
                this.resizeScreen();
                // window.location.reload();
                flag = false;
            }, 100)
        }
    }
    
    componentWillUnmount() {
        window.removeEventListener('resize',this.handleResize);
    }

    render() {
        const {store} = this.props;
        return (
            this.state.showPage ?
                <div style={{marginTop: this.returnMarginByScreenRatio(),position: 'relative'}}>
                    <Header/>
                    <div style={{
                        width: this.scaleWidth(1920, 'px'),
                        height: this.scaleHeight(686, 'px')
                    }}>
                </div> : ''
        );
    }
}

export default App;
  • 使用插件部分

直接调用flash的api播放,

let flag = true;

@inject('store')
@observer
class MyVideo extends React.Component {

    handleChangePlugin = ({ uuid, channel_id, pasword, username }) => {
        setTimeout(() => {
            const plugin = document.getElementById('plugin0');
            if (plugin.valid) {
                plugin.set_volume(0);
                let ret = plugin.connect_camera1(uuid, username, pasword, channel_id, 1);
                if (ret < 0) {
                    console.log("connect camera err");
                }
            } else {
                console.log("Plugin is not working :(");
            }
        })
    }

    handleStopPlugin = () => {
        const plugin = document.getElementById('plugin0');
        if (plugin.valid) {
            plugin.disconnect_all_camera();
        }
    }

    handleChangePluginVoice = (voice) => {
        const plugin = document.getElementById('plugin0');
        if (plugin.valid) {
            plugin.set_volume(voice);
        }
    }

    handleChangePluginFour = ([{ uuid, pasword, username }], nPage) => {
        setTimeout(() => {
            const plugin = document.getElementById('plugin0');
            if (plugin.valid) {
                let ret = plugin.connect_camera4(uuid, username, pasword, 2, nPage);
                if (ret < 0) {
                    console.log("connect camera err" + ret);
                }
            }
        });
    }

    componentDidMount() {
        const plugin = document.getElementById('plugin1');
        const flag1 = plugin && !plugin.valid;
        this.props.store.handleToClose(
            flag1
        );
    }

    shouldComponentUpdate(nextProps) {
        if (!nextProps.flag1) {
            if(!nextProps.isVideo) {
                this.props.store.handleChangeIsVideo();
                if (nextProps.channel_num === 1) {
                    this.handleChangePlugin(nextProps.data[0]);
                }
                else {
                    this.handleChangePluginFour(nextProps.data, nextProps.nPage);
                }
            }
            if (nextProps.data.length === 0) {
                this.handleStopPlugin();
            }
            if (nextProps.data.length > 0 && ((nextProps.data.length !== this.props.data.length && flag) ||
                (this.props.data[0] && this.props.data[0].name !== nextProps.data[0].name))) {
                flag = false;
                if (nextProps.channel_num === 1) {
                    this.handleChangePlugin(nextProps.data[0]);
                }
                else {
                    this.handleChangePluginFour(nextProps.data, nextProps.nPage);
                }
            }
            if (nextProps.voiceFlag !== this.props.voiceFlag || nextProps.voice !== this.props.voice) {
                this.handleChangePluginVoice(nextProps.voiceFlag ? nextProps.voice : 0);
            }
            if (nextProps.channel_num !== this.props.channel_num && nextProps.data.length > 0) {
                if (nextProps.channel_num === 1) {
                    this.handleChangePlugin(nextProps.data[0]);
                }
                else {
                    this.handleChangePluginFour(nextProps.data, nextProps.nPage);
                }
            }
            if (nextProps.nPage !== this.props.nPage && nextProps.channel_num === 4) {
                this.handleChangePluginFour(nextProps.data, nextProps.nPage);
            }
            return nextProps.flag1 !== this.props.flag1 || nextProps.nPage !== this.props.nPage || nextProps.uuiD !== this.props.uuiD || (nextProps.channel_num !== this.props.channel_num && nextProps.data.length > 0)
                || nextProps.voiceFlag !== this.props.voiceFlag || (nextProps.voice !== this.props.voice ||
                    !!(nextProps.data.length > 0 && ((nextProps.data.length !== this.props.data.length && flag) ||
                        (this.props.data[0] && this.props.data[0].name !== nextProps.data[0].name)))
                );
        } else {
            return false;
        }
    }

    render() {
        const { store, data, voice, voiceFlag, channel_num, uuiD, nPage, flag1 } = this.props;
        return <MyVideoBox>
            <object id="plugin1" type="application/x-deepcam-p2p" style={{
                width: 0,
                height: 0,
                display: 'none'
            }}>
                <param style={{
                    width: 0,
                    height: 0,
                    display: 'none'
                }} name="onload" value="pluginLoaded" />
            </object>
            <OneV id="plugin0" style={{ display: flag1 ? 'none' : 'block' }} type="application/x-deepcam-p2p">
                <param name="onload" value="pluginLoaded" />
            </OneV>
            {!flag1 ? '' :
                <XiaZhai>
                    <Header>
                        <div className="my-close" onClick={() => {
                            store.handleToClose(false)
                        }}></div>
                        <img src={require('./ic_tips.png')} alt='' />&nbsp;&nbsp;点击下载&nbsp;<span onClick={() => {
                            store.handleToClose(false)
                        }}><a download href="/static/templateFile/deepcam.exe">播放插件</a></span>,推荐使用IE浏览器播放
                    </Header>
                    <Content></Content>
                </XiaZhai>}
            <Process />
            <Onechannel onClick={() => { store.handleChangeChannelNum(1) }} src={channel_num === 4 ? OnechannelBg : OnechannelBgH} />
            <Fourchannel onClick={() => { store.handleChangeChannelNum(4) }} src={channel_num === 4 ? FourchannelBgH : FourchannelBg} />
            {data.length > 0 && channel_num === 1 ? <Title>{data[0].name}</Title> : ''}
            {channel_num === 1 ? <VoiceBox>
                <Slider tooltipPlacement='right' value={voice} onChange={(e) => { store.handleChangeVoice(e) }} />
            </VoiceBox> : ''}
            {channel_num === 1 ? <VoiceIcon src={voiceFlag ? VoiceBg : VoiceBgN} onClick={store.handleChangeVoiceFlag} /> : ''}
            <BeforeBtn src={channel_num === 1 ?
                store.devicePage === 0 ? BeforeBtnBg : BeforeBtnBgH :
                nPage === 0 ? BeforeBtnBg : BeforeBtnBgH
            } onClick={channel_num === 1 ? () => {
                store.handleChangeUuid('before');
            } : () => {
                store.handleChangeNPage('before');
            }} />
            <NextBtn src={channel_num === 1 ?
                store.allDevice.length === 0 || store.allDevice.length === 1
                    || store.devicePage + 1 >= store.allDevice.length ? NextBtnBgH : NextBtnBg :
                uuiD === 0 || uuiD === 1
                    || (nPage + 1) * 4 >= uuiD ? NextBtnBgH : NextBtnBg
            }
                onClick={channel_num === 1 ? () => {
                    store.handleChangeUuid('next');
                } : () => {
                    store.handleChangeNPage('next');
                }} />
        </MyVideoBox>
    }
};

export default MyVideo;

还用一个点,怎么判断有没有安装flash控件。直接判断object对象有没有valid即可。

总结

主要为了配合公司的硬件和现有的sdk的原因。如果有条件还是使用HTML5的 video 标签 比较 丝滑。

flash在 0202年 已经 显得还鸡肋了 用户体现也比较差 消化的性能也太大了

总而言之 给用户体验比较差