Electron实现跨平台全能视频播放器

6,894 阅读2分钟

最近在看英文字幕的电影,听力水平一般,有些字幕对话想多回放几遍。这个是一个比较小众的需求,发现目前的播放器都不支持。于是就想自己实现个有字幕回放功能的播放器。跨平台的开源播放器,比如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…