Electron實現跨平臺全能視訊播放器

relaxrock發表於2018-12-02

最近在看英文字幕的電影,聽力水平一般,有些字幕對話想多回放幾遍。這個是一個比較小眾的需求,發現目前的播放器都不支援。於是就想自己實現個有字幕回放功能的播放器。跨平臺的開源播放器,比如VLC、MPV,開發的門檻都挺高的。如果能用Electron做播放器的話,新增一些個性的功能,應該會比較簡單,寫一些html、js就可以。使用Electron製作播放器碰到的最大問題是,H5 video標籤只支援部分的視訊格式。經過一段時間的研究,這個問題已經解決。目前基於Electron的跨平臺全能播放器已經實現,並加上了我最想要的字幕對話回放功能。

使用ffmpeg支援所有視訊格式

在Electron應用裡,H5 video標籤支援視訊的本地路徑。 H5 video標籤只支援部分的視訊格式(mp4、webm和ogg)。需要使用ffmpeg支援其他格式的視訊檔案(mkv、rmvb、flv...)。這裡可以使用ffmpeg的nodejs封裝庫 fluentffmpeg。 先使用ffmpeg檢查視訊檔案是否可以直接用H5 video標籤直接播放。

var videoSupport = function (videoPath) {
    let p = new Promise(function (resolve, reject) {
        let command = ffmpeg()
            .input(videoPath)
            .ffprobe(function (err, data) {
                if (err) {
                    reject(err);
                    return;
                }
                var streams = data.streams;
                var checkResult = {
                    videoCodecSupport: false,
                    audioCodecSupport: false,
                    duration: data.format.duration
                }
                if (streams) {
                    streams.map((value) => {
                        // mp4, webm, ogg
                        if (value.codec_type == 'video' && (value.codec_name == 'h264' || 
                        value.codec_name == 'vp8' || value.codec_name == 'theora')) {
                            checkResult.videoCodecSupport = true;
                        }
                        if (value.codec_type == 'audio' && (value.codec_name == 'aac' || 
                        value.codec_name == 'vorbis')) {
                            checkResult.audioCodecSupport = true;
                        }
                    })
                }
                resolve(checkResult)
            });
    });
    return p;
}
複製程式碼

對於H5 video標籤不支援的格式,需要ffmpeg轉碼。 Electron應用程式分為瀏覽器渲染程式,和nodejs主程式。nodejs可以啟動http server,這個http server使用ffmpeg實時轉碼,返回H5 video標籤可以識別的fragmeted mp4視訊流。

  this._videoServer = http.createServer((request, response) => {
                var startTime = parseInt(getParam(request.url, "startTime"));
                let videoCodec = this.videoSourceInfo.checkResult.videoCodecSupport ? 'copy' : 'libx264';
                let audioCodec = this.videoSourceInfo.checkResult.audioCodecSupport ? 'copy' : 'aac';
                this.killFfmpegCommand();
                this._ffmpegCommand = ffmpeg()
                    .input(this.videoSourceInfo.videoSourcePath)
                    // read input at native framerate
                    .nativeFramerate()
                    .videoCodec(videoCodec)
                    .audioCodec(audioCodec)
                    .format('mp4')
                    .seekInput(startTime)
                    // fragmeted mp4
                    .outputOptions('-movflags', 'frag_keyframe+empty_moov');
                let videoStream = this._ffmpegCommand.pipe();
                videoStream.pipe(response);
            }).listen(8888);
複製程式碼

前端H5 video標籤src屬性設定為nodejs視訊流的地址

            <video id="video" width="800" height="480" preload>
                <source src="http://127.0.0.1:8888?startTime=0" type='video/mp4'>
            </video>
複製程式碼

Fragmented mp4視訊流seek的實現

H5 video>標籤有預設的拖動控制條,支援普通mp4視訊流的seek,一般通過http range實現。但是對fragmented mp4視訊流,http range無法實現seek。這裡的fragmented mp4視訊流是實時的轉碼流,整個視訊檔案的size是未知的。 這裡我們去掉了H5 video標籤的預設控制條,使用自定義的拖動控制條。通過ffmpeg獲得視訊的總時長。拖動的時候在視訊流的請求地址裡面提交seek time。http server獲得seek time後,通過ffmpeg命令的seek引數將視訊的播放時間移動。

開源

github.com/relaxrock/r…

相關文章