vue中好用的視頻插件推薦,video+canvas實(shí)現(xiàn)視頻截圖第一幀,ffmpeg實(shí)現(xiàn)視頻截圖第一幀

最近做視頻功能,看了一些視頻相關(guān)的插件,發(fā)現(xiàn)下面兩個(gè)還是比較好用,這里推薦一下:

videojs
Mui Player

videojs應(yīng)該很多人都知道,比較成熟的插件了這里是官網(wǎng)地址:

https://videojs.com/

然后Mui Player也是很不錯(cuò)跟videojs差不多都很好用,對(duì)比videojs優(yōu)勢(shì)應(yīng)該就是文檔更好閱讀點(diǎn)對(duì)于英語不好的是個(gè)好事。

https://muiplayer.js.org/zh/guide/

我自己用了videojs,
然后項(xiàng)目主要是做了視頻的一個(gè)上傳和視頻的展示功能
看看效果圖:


1
2

3

使用的話官方文檔自己翻

最后主要說一下我用了插件后踩的一些坑,特別是上傳部分,其它的都還好。

我項(xiàng)目需求上傳需要做封面圖,然后安卓用戶的插件可以支持自動(dòng)截取視頻第一張圖,ios手機(jī)上的因?yàn)橄拗撇恢С忠曨l自動(dòng)去加載元數(shù)據(jù)不會(huì)自動(dòng)截取視頻第一張圖,所以ios的需要特殊處理一下,先將視頻設(shè)置為運(yùn)行小窗口播放,然后視頻加載時(shí)執(zhí)行一下手動(dòng)播放然后馬上暫停,因?yàn)閕os不支持自動(dòng)播放要獲取第一幀必須點(diǎn)擊一下播放視頻,代碼如下:

videoScreenshot(file, key) {
            let tthis = this;
            try {
                var reader = new FileReader();
                reader.onload = function () {
                    var videoDom = document.createElement("video");
                    videoDom.setAttribute('muted', true);
                    videoDom.setAttribute('autoplay', true);
                    videoDom.setAttribute('x5-video-player-fullscreen', true);
                    videoDom.setAttribute('webkit-playsinline', true);
                    videoDom.setAttribute('playsinline', true);
                    videoDom.setAttribute('x5-playsinline', true);
                    videoDom.setAttribute('x-webkit-airplay', 'allow');
                    videoDom.onloadeddata = function () {
                        videoDom.play();
                        videoDom.pause();
                    }
                    videoDom.onplay = function() {
                        setTimeout(()=>{
                            var canvas = document.createElement("canvas");
                            canvas.width = this.videoWidth;
                            canvas.height = this.videoHeight;
                            canvas.getContext("2d").drawImage(this, 0, 0, canvas.width, canvas.height);
                            tthis[key].poster = canvas.toDataURL('image/png');
                            tthis[key].duration = this.duration;
                            tthis[key].coverImg = tthis.base64ToFile(canvas.toDataURL('image/png'));
                            tthis.compressorImg(tthis[key].coverImg, key);
                            tthis.upVideo();
                        }, 100)
                    }
                    videoDom.src = URL.createObjectURL(new Blob([file], { type: "video/mp4" }));
                }
                reader.readAsDataURL(file);
            } catch (e) {
                console.log(e)
            }
        },

這樣就能兼容到ios上的視頻格式了

對(duì)于mac還有其它設(shè)備測(cè)試后可以給一個(gè)與加載元數(shù)據(jù)屬性能夠自動(dòng)去加載視頻第一幀做為封面就不需要去截圖其實(shí):

preload: "metadata",

寫了一個(gè)視頻組件配置代碼:

<template>
    <div class="contentVideo" :id="'myVideoWrap_' + videoData.name" :class="{blur: videoData.blur}">
        <video :id="'myVideo_' + videoData.name" class="video-js">
            <source :src="videoData.url" type="video/mp4">
        </video>
    </div>
</template>
<script>
import Video from 'video.js'
import 'video.js/dist/video-js.css'
export default {

    name: 'videoTemplate',
    data() {
        return {
            myVideo: '',
        }
    },
    props: ['videoData', 'startFn', 'endFn'],
    mounted() {
        this.initVideo();
    },
    destroyed() {
        this.myVideo.dispose();
    },
    methods: {
        initVideo() {
            let tthis = this;
            let option = {
                controls: true,
                playbackRates: [0.7, 1.0, 1.5, 2.0],  // Set playback speed options
                loop: false,
                muted: true,// Default Mute Play or Not
                disablePictureInPicture: true,
                playsinline: true,
                //autoplay: "muted", // Mute Play or Not
                preload: "metadata",
                //poster: this.videoData.poster,
                ...this.videoData.plugins,//Replace the Default Configuration
            };
            this.myVideo = Video('myVideo_' + this.videoData.name, option,function onPlayerReady() {
                this.on('play', function() {
                    _.forEach(document.getElementsByTagName('video'), (val) => {
                        if (val != this.el_.children[0]) {
                            val.pause()
                        }
                    });
                    tthis.startFn ? tthis.startFn(tthis.videoData) : '';
                });
                this.on('ended', function() {
                    tthis.endFn ? tthis.endFn(tthis.videoData) : '';
                });
          });
        }
    },
}

</script>
<style lang="less" scoped>
.contentVideo {
    width: 100%;
    height: 100%;
    overflow: hidden;
    /deep/ .video-js .vjs-big-play-button {
        width: 40px;
        height: 40px;
        line-height: 40px;
        border-radius: 50%;
        font-size: 18px;
        top: 50%;
        left: 50%;
        margin-top: -20px;
        margin-left: -20px;
        border: 1px solid #FFFFFF;
    }
    /deep/ .video-js {
        width: 100%;
        height: 100%;
    }
    /deep/ .video-js .vjs-picture-in-picture-control {
        display: none;
    }
    /deep/ .video-js .vjs-play-progress:before {
        top: -0.45em;
    }
    /deep/ .vjs-slider-horizontal .vjs-volume-level:before {
        top: -0.45em;
    }
}
.contentVideo.blur /deep/ video {
    -webkit-filter: blur(6px);
    filter: blur(6px);
    transform: scale(1.1);
}
</style>

調(diào)用時(shí):

<videoTemplate1 :videoData="videoDataFormat(item)" :startFn="hideVideoDuration" :endFn="videoEndFn"/>
videoDataFormat(item) {
            let videoData = {
                id: item.attachments[0].video.id,
                name: 'list_' + item.attachments[0].video.id + Date.now(),
                canWatch: item.canWatch,
                url: item.attachments[0].video.url,
                profId: item.userId,
                poster: item.attachments[0].cover.url,
                blur: !parseInt(item.canWatch) && item.userId != this.myUserInfo.userId,
            }
            return videoData;
},

屬性和方法根據(jù)自己的業(yè)務(wù)需求自己去添加

然后關(guān)于截圖也有另外的方式,那就是使用ffmpeg的方式
這個(gè)npm安裝@ffmpeg包就可以了,然后運(yùn)行ffmpeg命令就可以做截圖很簡(jiǎn)單,但是可能包比較大有20多M,對(duì)于項(xiàng)目可能才幾十M來說這個(gè)包實(shí)在可能太冗余了。

如果覺得包太大可以自己做wasm包,將多余的ffmpeg的功能剔除掉生成wasm包然后壓縮,如果只是截圖功能估計(jì)能壓縮到3-4M左右,完全能符合上線的要求。
對(duì)于如何生成ffmpeg 的wasm包這個(gè)要用到 WebAssembl, 感興趣的可以去了解下運(yùn)行起來也是很絲滑就是上手難度較高。

ffmpeg

用ffmpeg截圖,那幾乎兼容所有視頻格式的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容