跨平台的开源播放器,比如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参数将视频的播放时间移动。
播放器UI
播放器使用 videojs
实现UI,很不错。