浅析浏览器 Web 视频播放器

音视频前置知识

视频,是一系列连续播放的图片。

码率,也叫比特率,是每秒视频的 bit 数,决定了视频是否清晰。比特率越高,压缩比越小,视频越清晰。

100min/1080P/1GB的视频的比特率:

// 帧率为 (数据量/时间)
8589934592/6000 = 1.4Mbit/s

每秒播放的图片数就是视频的帧率。

如果视频(图片)的的尺寸是 1920*1080,即一张图片的尺寸是 1920*1080*3 bytes,乘3是因为一个像素点3个比特,分别存放R,G,B,那么帧率60的1920 * 1080 视频,1 min 所需要的存储空间:

1920 * 1080 * 3 * 60 * 60 = 20.85GB

如此之大,所以视频需要编码(codec)压缩。不同的编码格式压缩率不同,常见的编码格式有 h264,mpeg4,vp8等。

视频和图片均具有以下特点:

  • 空间冗余:图像的相邻像素之间有较强的相关性,一张图片相邻像素往往是渐变的,不是突变的,没必要每个像素都完整地保存,可以隔几个保存一个,中间的用算法计算出来。
  • 时间冗余:视频序列的相邻图像之间内容相似。一个视频中连续出现的图片也不是突变的,可以根据已有的图片进行预测和推断。
  • 视觉冗余:人的视觉系统对某些细节不敏感,因此不会每一个细节都注意到,可以允许丢失一些数据。
  • 编码冗余:不同像素值出现的概率不同,概率高的用的字节少,概率低的用的字节多,类似霍夫曼编码(Huffman Coding)的思路。

帧率和分辨率都可以影响视频体积,帧率是主要因素,一般来说主流视频平台的帧率在1Mbit/s左右。

由于编码格式是有版权问题的,所以不同的浏览器支持的编码格式不同,就会出现有些编码格式的视频在某些浏览器播放不了,或者只有声音没有画面。主流浏览器支持的视频编码格式是h264

一个视频文件内会包含视频流和音频流,还有一些元数据,例如分辨率信息,标题等,这个文件的格式我们称为封装格式,可以理解为打包格式,常见的 mp4,webp,mov,mpeg。封装格式往往是与视频编码无关的,所以会出现同样都是 mp4文件,某些浏览器播放不了。

音频标签是 audio、视频标签是 video。

<video controls poster="1.jpg" src="1.mp4" loop muted></video>
<audio controls src="1.mp3"></audio>

其中,src指定资源地址,poster为视频指定一张封面图,controls表示浏览器应该显示UI控件(每个浏览器样式不同)。

video 和 audio 通用事件 :

video 的常用方法: 

  • play() 控制视频开始播放;
  • pause() 控制视频暂停播放。

操作音视频必备工具-FFMPEG

FFMPE是音视频处理最常用的开源软件,安装FFMPEG:

使用文档参考官网:FFmpeg

播放器实现

使用原生组件进行实现,重点关注业务逻辑。思路如下:

  • 该组件接收一个视频的src作为参数;
  • 监听 onLoadedMetadata事件,获取视频时长(duration)和真实宽(videoWidth)高 (videoHeight);
  • 点击播放/暂停时,调用视频元素的 play/pause 方法;
  • 播放时,监听 onTimeUpdate,获取当前播放时间(currentTime),计算出进度条的进度;
  • 拖动进度条,设置视频当前播放时间,与步骤 4 相反;
  • 视频初始加载时,显示loading。即组件初次渲染时,loading 属性默认为 true,即显示加载效果,当视频元数据加载时,取消 loading;
  • 视频卡顿时,显示 loading,监听的 onWaiting事件;不卡顿时,取消 loading,监听的是 onCanPlay事件。
.video-wrapper{
    width:800px;
}
.video-wrapper>video{
    width: 100%;
}

.video-controls{
    margin-top: 20px;
}

function formatDuration(duration) {
    var sec_num = parseInt(duration, 10);
    var hours   = Math.floor(sec_num / 3600);
    var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
    var seconds = sec_num - (hours * 3600) - (minutes * 60);

    if (hours   < 10) {hours   = "0"+hours;}
    if (minutes < 10) {minutes = "0"+minutes;}
    if (seconds < 10) {seconds = "0"+seconds;}
    return hours+':'+minutes+':'+seconds;
}

import React, { createRef, useState } from 'react'
import './VideoPlayer.css'
function VideoPlayer({src}){
    const videoDom=createRef()
    // 视频当前播放时间
    const [curTime,setCurTime]=useState(0)
    // 视频时长
    const [duration,setDuration]=useState(0)
    // 视频状态,是否暂停
    const [isPause,setPause]=useState(true)
    // 视频真是尺寸
    const [size,setSize]=useState({width:1920,height:1080})
    // 视频加载中
    const [waiting,setWaiting]=useState(true)

    // 视频元数据加载成功
    const onLoad=(e)=>{
        const {duration,videoWidth,videoHeight}=e.target
        setDuration(duration)
        setSize({width:videoWidth,height:videoHeight})
        setWaiting(false)
    }
    // 控制播放暂停
    const handlePlay=(play)=>{
        const v=videoDom.current
        if(play){
            setPause(false)
            v.play()
        }else{
            setPause(true)
            v.pause()
        }

    }
    // 拖动slider时改变视频currentTime
    const onSliderChange=(e)=>{
        setCurTime(e.target.value)
        videoDom.current.currentTime=e.target.value

    }
    // 监听video timeupdate
    const onTimeUpdate=()=>{
        const v=videoDom.current
        setCurTime(v.currentTime)
        if(v.ended){
            handlePlay(false)
            v.currentTime=0
        }
    }
    // 卡顿时,显示加载中提示
    const onWaiting=()=>{
        setWaiting(true)
    }
    // 可以播放时,隐藏加载中提示
    const onCanPlay=()=>{
        setWaiting(false)
    }
    return <div className="video-wrapper">
            <video ref={videoDom}  src={src} onLoadedMetadata={onLoad} onTimeUpdate={onTimeUpdate} onWaiting={onWaiting} onCanPlay={onCanPlay}  ></video>
            {/* 视频加载时显示loading */}
            {waiting && <div className="waiting">loading...</div>}
            <div className="video-controls">
                {/* 播放按钮 */}
                {isPause? <button onClick={()=>{handlePlay(true)}}>播放</button>: <button onClick={()=>{handlePlay(false)}}>暂停</button>}
                {/* 进度条 */}
                <input type="range" min="0" max={duration}  value={curTime} onChange={onSliderChange}/>
                {/* 时间信息和分辨率信息 */}
                <span>{formatDuration(curTime)}/{formatDuration(duration)}</span>
                <span>分辨率:{size.width}x{size.height}</span>
            </div>
        </div>
}
export default VideoPlayer;
autoscroll(true,false):当播放列表过长的时候,默认会自动显示滚动条。当该值设置为’true’的时候,会自动根据鼠标滚动播放列表。 displaywidth(number of pixes):设置显示区域的宽度,当设置的比较小的时候,播放列表会显示在显示区域的右侧而不是底部。 kenburns* (true,false): 用以实现在运动的时候实现kb效果(Ken Burns effect),注意,当图比较大,而且网速比较快的时候,建议打开,否则关闭。建议设置transition为"slowfade"来配合。 largecontrols (true,false): 设置该值为true用来放大控制区域的按钮。建议为视力不好的用户打开 logo* (url): 设置一个图片用来作为右上角的水印,支持所有图片格式,支持通明图层的png效果最佳。 overstretch* (true,false,fit,none): 设置图片/影片在显示区域的缩放。"true"等比例拉伸用来符合显示区域。"false"仅拉伸合显示区域。"fit"全屏显示。"none"显示原始大小。JW Media Player等默认为"fit",JW Image Rotator默认为"false" showdigits (true,false,total): 设置为"false"隐藏播放时间等信息用来节省控制区域的空间。设置为"total"用来显示全部时间。 showdownload (true,false):设置该值用来在控制区域显示下载按钮。链接到link所设置的地址。 showeq (true,false): 用来显示一个假的音频波动效果。当播放mp3的时候打设置该值可以得到很好的效果 showicons* (true,false): 用来显示或者隐藏显示区域中间的图片,JW Media Player等默认为true。JW Image Rotator默认为false; showvolume (true,false): 用来设置是否显示音量控制按钮 thumbsinplaylist (true,false): 设置列表中是否显示预览图 播放参数 autostart (true,false,muted): 设置为ture,页面加载完后会自动播放。设置为muted,会在静音模式下自动播放,并且显示区域中间有静音图标。bufferlength (number): 设置flv的缓存时间。默认为3秒 repeat* (true,false,list): 默认为flase,从当前播放位置播放到列表尾部后停止。设置为list会播放列表中所有的项目,设置为true会循环播放。 rotatetime* (number): 设置图片的显示时间。JW Media Player等默认为10秒,JW Image Rotator默认为5秒 shuffle* (true,false): 设置为false顺序播放,设置为true无序播 smoothing (true,false): 设置为false关闭视频平滑处理,推荐设置true用以得到更好效果。但对于大屏幕或者配置低的机器设置false是有好处的 start* (second): 在使用RTMP 或 HTTP 流媒体的时候(非常规的flv/mp3),使用该变量准确的定位开始位置。该参数设置在XSPE格式的列表中以便准且的设置文件的章节。 volume* (number): 设置音量,默认为80. 互动参数 audio* (url):用这个参数来添加一个mp3文件作为单独的音频,可以作为图片的背景音乐解说等。 bwfile (url):用以带宽检测的文件的地址,可以放一个图片,或者rtmp流媒体。可以在右键菜单中查看到贷款数值。 bwstreams (comma-separated list of bitrates): 和bwfile配合使用,根据带宽值来选择不同的文件。如:你要播放video.flv并且设置该项的值为100,250,500,1000,当播放器发现带宽为349kbps的时候,将会播放video_250.flv。所以他有一套有效的命名设置,他将会自动切换,哪怕是在采用播放列表的情况下。 callback (url):设置这个参数为服务端程序(php/asp)地址用来回传数据。在每个项播放和停止的时候会发送数据到服务器,以便在服务器端保存播放统计。 captions (url): 设置该值用以载入一个文本格式的文本作为字幕。播放器至支持SMIL格式和DVD的SRT格式的字幕。如果你的flv文件内置字体你可以设置该值为"captionate".如果你有多频道字幕,可以设置这个值为"captionate0", "captionate3"等。可以在列表中设置每一个项的值。 enablejs* (true,false): 设置为true打开对javascript的支持。仅支持在线使用。javascript可以控制播放,加载媒体,获得当前播放项的详尽信息。 fsbuttonlink (url):如果用户的flashplayer版本高于(9.0.28)播放器会自动的显示一个全屏按钮。通过设置该值,你可以链接到另外的页面用以全屏显示。服务端程可以设定将要播放的文件。 id (string): 播放器的唯一标识。将会被回传到服务器端。 javascriptid* (string):如果你的页面上有多个播放器,你可以设置这个参数给每个播放器不同的id,这样就可以方便的用javascript来控制。他将回传到getUpdate()事件中。 link (url): 通过这个参数用来设置一个可现在的版本,或者强制用户通过该地址下载当前项。可以在播放列表中为每一项设置该值。 linkfromdisplay* (true,false):设置显示区域被点击时要访问的页面。默认点击显示区域时会进行播放/暂停操作。 linktarget* (frame): 设置链接目标,"_self"在当前页打开。"_blank"在新页面中打开。 streamscript (url):设置这个参数为了兼容‘伪流媒体’FLV文件。 type (mp3,flv,rtmp,jpg,png,gif,swf,rbs,3gp,mp4,m4v): 播放器会根据文件名的最后三个字符来判断类型。在你使用服务器端语言进行重定向时,这种方法将不会再有效。所以你可以设置这个参数来告诉播放器文件类型。你也可以在播放列表中对每一项进行设置。如果播放器找不到文件类型将会被识别为播放列表。 useaudio (true,false): 设置为false用来改变为静音状态。 usecaptions (true,false):设置为false隐藏字幕. usefullscreen (true,false):如果你不希望用flashplayer9的全屏模式。可以设置"fsbuttonlink"参数用来替换全屏按钮的事件。 usekeys (true,false): 设置为"false"用来取消键盘操作(SPACE,UP,DOWN,LEFT,RIGHT) 注意:你必须对?=&进行字符转移。? → %3F, = → %3D, & → %26. 你的"file" 变量为getplaylist.php?id=123的时候必须设置为 getplaylist.php%3Fid%3D123.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛定谔的猫96

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值