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