JS截取视频第一帧

论坛 期权论坛 期权     
冰山工作室   2019-7-27 14:57   5099   0
接上篇 js图片/视频预览 - URL.createObjectURL()
[iframe]https://mp.weixin.qq.com/mp/readtemplate?t=pages/video_player_tmpl&action=mpvideo&auto=0&vid=wxv_913496302246428672[/iframe]
当视频能够预览并上传后,非要来一张视频第一帧的截图贴上,第一帧是黑的怎么办,下一帧。
[h2]一、文件上传[/h2]使用
  1. [/code]上传, [code]change
复制代码
事件作为预览
  1. video
复制代码
  1. src
复制代码
的触发条件
新鲜源码:
    1. [/code]
    2. [*][code]
    复制代码
    1. 点击上传视频
    复制代码
[h2]二、canvas截取图片[/h2]关于截取或者处理图片/视频/富文本编辑器,
  1. canvas
复制代码
是一个非常nice的选择。
1.创建画布
  1. canvas
复制代码
或在
  1. html
复制代码
中直接写入。
    1. var canvas = document.createElement('canvas');
    复制代码
2.创建基于
  1. canvas
复制代码
的绘图环境
    1. var ctx = canvas.getContext('2d');
    复制代码
附 Q&A:
  • 什么是绘图环境?
网络上的常规理解是“在准备画布后,需要一些‘染料、画笔、绘图工具’的准备工作。”
比较官方的说话是返回
  1. canvas
复制代码
的上下文环境,
说人话是'你能够更好的操作你的
  1. canvas
复制代码
'。
  • 关于
    1. getContext('2d')
    复制代码
    的参数
方法中的
  1. 2d
复制代码
参数目前可以理解为是
  1. 固定参数
复制代码
,表示想要一个
  1. 二维
复制代码
绘制环境。虽然大家都认为有
  1. 2d
复制代码
自然应该有
  1. 3d
复制代码
,然而实际上本身设计时也是这么考虑的,不过大家有点等不起了,所以都去选择
  1. webGL
复制代码
了。
  1. webGL
复制代码
是啥?浏览器端借助系统显卡进行 3D 绘图。这是另一个故事了(
  1. IE
复制代码
别想了)。
  • 关于
    1. canvas.getContext('2d')
    复制代码
    的返回值
返回一个
  1. CanvasRenderingContext2D
复制代码
对象,也就是上文所说的能够支持绝大多数对画布的操作。
3.在
  1. canvas
复制代码
上绘制图片
    1.     // ctx.drawImage(file,sx,sy,swidth,sheight,x,y,width,height);
    复制代码
    1.     ctx.drawImage(this, 0, 0, swidth, sheight);
    复制代码
在不需要剪裁的情况下,使用上述参数即截取操作
  1. file
复制代码
的全部,绘制到
  1. canvas
复制代码

关于参数(w3school)
参数描述file规定要使用的图像、画布或视频。sx可选。开始剪切的 x 坐标位置。sy可选。开始剪切的 y 坐标位置。swidth可选。被剪切图像的宽度。sheight可选。被剪切图像的高度。x在画布上放置图像的 x 坐标位置。y在画布上放置图像的 y 坐标位置。width可选。要使用的图像的宽度。(伸展或缩小图像)height可选。要使用的图像的高度。(伸展或缩小图像)4.将
  1. canvas
复制代码
导出成图片放入
  1. src
复制代码
    1. var src = canvas.toDataURL('image/jpeg');
    复制代码
关于
  1. toDataURL()
复制代码
方法。将
  1. canvas
复制代码
的内容导出
    1. canvas.toDataURL(type, encoderOptions);
    复制代码
  1. type
复制代码
: 图片格式,默认
  1. image/jpeg
复制代码
  1. encoderOptions
复制代码
:图片质量,取值范围为0到1,默认0.92。
  1. 返回值
复制代码
:包含
  1. data URI
复制代码
  1. DOMString
复制代码
,也就是
  1. base64
复制代码
格式。
[h2]三、截取视频第一帧[/h2]上传文件OK,用
  1. canvas
复制代码
截取OK,怎么找
  1. 第一帧
复制代码
呢?(啥时候开始截取呢?)
当然是多媒体的事件来触发。关于
  1. video
复制代码
的事件非常多(全部事件),这里只讨论能够影响到截取到第一帧的各个事件。
    1.     video.addEventListener('loadeddata', consoleString.bind(video, 'loadeddata')) // 当前帧加载完毕
    复制代码
    1.     video.addEventListener('loadedmetadata', consoleString.bind(video, 'loadedmetadata')) // 视频元数据加载完毕
    复制代码
    1.     video.addEventListener('canplay', consoleString.bind(video, 'canplay')) // 视频缓冲能够开始播放
    复制代码
    1.     video.addEventListener('timeupdate', consoleString.bind(video, 'timeupdate')) // 播放位置发生改变时
    复制代码
    1.     video.addEventListener('play', consoleString.bind(video, 'play')) // 开始播放时
    复制代码
    1.     video.addEventListener('waiting', consoleString.bind(video, 'waiting')) // 要播放下一帧而需要缓冲时
    复制代码

    1.     function consoleString(string) {
    复制代码
    1.         console.log(string)
    复制代码
    1.     }
    复制代码
    1. // 执行结果
    复制代码
    1. // timeupdate
    复制代码
    1. // loadedmetadata
    复制代码
    1. // loadeddata
    复制代码
    1. // canplay
    复制代码
    1. // play(开始播放)
    复制代码
    1. // 没有waiting, 因为视频较小不需要缓冲
    复制代码
  • 根据顺序,第一个被触发的竟然是
    1. timeupdate
    复制代码
    事件,按设想来说,最先执行的应该是
    1. loadedmetadata
    复制代码
    ,元数据加载完毕。关于这一点,在MDN上没有明确的说明,但是可以推理一下:
  1. currentTime
复制代码
更新时会触发
  1. timeupdate
复制代码
事件
来源:MDN
  1. loadedmetadata
复制代码
的元数据恰好是指
  1. 时长、尺寸(仅视频)以及文本轨道
复制代码
,也就是说在
  1. video
复制代码
未定义的时候
  1. currentTime
复制代码
  1. NaN
复制代码
  1. NULL
复制代码
,当元数据中时长加载完毕后,
  1. currentTime
复制代码
更新至
  1. 0
复制代码
,因此触发。
结论:虽然最先触发,但是此时视频文件尚未加载,截取的是
  1. canvas
复制代码
的无内容本身。注:
  1. timeupdate
复制代码
事件根据使用的系统不同,每秒触发4-66次,且由于触发频率高,单位过小(毫秒级别),事件响应需要延迟等原因,无法完全精准的控制。

  • loadedmetadata
    上文提到,元数据加载完毕之后即触发,但数据中并不包括视频文件本身。结论:如果视频文件较大,加载时间较长,仍然无法截取到已加载的第一帧。补充:通过
    1. URL.createObjectURL()
    复制代码
    方法能够基本做到无察觉,但并不保险。


  • loadeddata
    当前帧数(第一帧)加载完毕触发,没毛病。结论:可用。补充:万一第一帧是黑屏想用下一帧怎么办,对不起,余下帧数加没加载完不在它的考虑范围之类,这个事件不管。


  • canplay
    视频能够开始播放时触发,也就是根据上传的视频帧数决定加载多少帧(24/25/30/60等等)后满足播放画面后触发。总结:因为加载相对于
    1. loadeddata
    复制代码
    的事件来说更多(多一丢丢),总体可行。补充:通过控制
    1. currentTime
    复制代码
    可以满足(但不可能是第二帧那么准确),可以看做“当前播放帧”。


  • play
    开始播放时才会触发,和上传快速截取的需求不是很符合。


  • waiting
    已播放但下一画面没缓冲好时触发,适合插播小广告。

文件、方法、事件都OK了。截就完事儿了。
    1.     video.addEventListener('loadeddata', function (e) {
    复制代码
    1.             canvas.width = this.videoWidth
    复制代码
    1.             canvas.height = this.videoHeight
    复制代码
    1.             width = this.videoWidth
    复制代码
    1.             height = this.videoHeight
    复制代码
    1.             ctx.drawImage(this, 0, 0, width, height);
    复制代码
    1.             var src = canvas.toDataURL('image/jpeg');
    复制代码
    1.             img.src = src;      
    复制代码
    1.             // var currentTime = this.currentTime
    复制代码
    1.             // duration = this.duration
    复制代码
    1.             // var fps = duration / 30
    复制代码

    1.     })
    复制代码
Dome:https://243341386.github.io/review_dome/
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:15
帖子:3
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP