一、简介

关于前端使用websocket播放音视频我倒是没想过,但是理论上确实是可行的,因为websocket是长连接,虽然知道web端的常见用法,但是作为c++开发人员我最疑惑的问题就是:

  • 使用js处理二进制?这个做法很不常见,恕我没太多了解,我一直以为js一般处理二进制不方便,所以脑海里一直自以为是
  • js编解码效率高吗?因为是基于浏览器的脚本语言的二不是直接基于系统api的独立进行,转行较多,效率应该不高,所以我也一直避讳用js去处理编解码

其实,我自以为的经过不断的探索发现也确实是对的,相对而言js确实能够处理二进制流(使用ArrayBuffer)但是操作不方便且效率相对较低,不过对于web端播放来讲,第一是播放量不太,一般不会出现9路以上主码流播放的情况(一般是一路并发),第二就是web端播放编解码其实并不是像通过c/C++那样将所有的数据一步一步解码出来,而是可以借助EMS转换工具将C库直接转换为js脚本,我们只需要用c或c++语言进行开发即可!

二、c/C++转js

我这里多说一句,我看到有将c语言直接转换为js语言的库的时候,我是一愣,这不说来所谓的前端js的音视频开发的开源库那么多,都可以使用c语言直接开发出来?因为开源的有如下问题:

  • 有些部分开源,不免费
  • 有些有讨厌的logo,无法定制
  • 有些有bug,并不完善
  • 有些没有bug但是无法定制(比如延迟较高、播放长时间会变慢等疑难杂症)

如果能够用c或cpp编程开发,对于熟悉擅长这块的我岂不乐哉!于是我搜索了一下c或cpp转js的库

  • ecmascripten
    Emscripten,基于LLVM可将C/C++代码编译为js的工具。
  • Asm.js
    Asm.js来自于JavaScript应用的一个新领域:编译成JavaScript的C/C++应用。它是JavaScript应用的一个全新流派,由Mozilla的Emscripten项目催生而来。

Emscripten项目提供了可以编译C和C++(或其他任何可转换为LLVM IR的语言)代码为asm.js的工具,如下c语言代码:

int (int i)
{return i + 1;
}

Emscripten将输出下列JavaScript代码:

function (i)
{i = i | 0; return (i + 1) | 0;
}

Emscripten目的就是将c/c++进程编译成js或者H5应用,asm.js的产生是为了提高Emscripten转换后的代码执行效率的。流程是C++ -> Emscripten -> asm.js -> 浏览器运行。

我们知道js的性能是无法跟C++这种高效语言相比的,但是Asm.js比较注意性能优化,一般情况下,对于复杂的应用Asm.js的性能仅仅比普通C++编译的慢两倍(可以和Java或者C#相媲美)。

Asm.js为了优化性能,做了一下几点:

  • 所有外部数据在一个称为堆的对象中存储并被引用。堆在本质上是一个大数组(应当是一个在性能上高度优化的类型化数组)。所有的数据在这个数组中存储——有效的替代了全局变量,结构体,闭包和其他形式的数据存储。
  • 能处理被挑出的几种不同的数值类型,而没有提供其他的数据类型(包括字符串,布尔型和对象)。
  • 当访问和赋值变量时,结果被统一的强制转换成一种特定类型。例如f = e | 0;给变量f赋值e,但它也确保了结果的类型是一个整数(|0把值转换成整数,确保了这点)。

通过以上几点,可以看出来虽然js语言是一门动态语言,在进程运行时变量的类型是不确定的,但是Asm.js没有这个问题,他确保进程运行时变量类型已知(可以转换),让js实现了静态语言的概念。
同时,在内存操作上,将变量存放在一大块内存上,相当于在栈上操作(实际上是堆)。

三、websocket播放H264流

我们知道了以上概念之后,我们前端播放视频裸流用的一个库及时wfs,它已经帮我们使用了通过websocket接收二进制H264数据并解码渲染的功能,这里我们就不用过多的去操心了,站在巨人的肩膀上再创新!

经过websocket后天编程,增加h264文件读取发送的功能终于出了效果,而且非常的流畅。

前端播放结束wfs的代码如下:

<!DOCTYPE html>
<html>
<head><title>h.264播放</title><meta charset="utf-8"><script type="text/javascript" src="wfs.js"></script>
</head>
<body><h2>播放H264裸流(h.264转fmp4)</h2><div class="wfsjs"><video id="video1" width="640" height="480" controls></video><div class="ratio"></div></div><button onclick="clickbtn()">开始播放</button><script type="text/javascript">function clickbtn() {if (Wfs.isSupported()) {// 创建WFS库wfs = new Wfs();// 获取元素var video1 = document.getElementById("video1");// 关联到通道ch1wfs.attachMedia(video1, 'ch1');}};</script>
</body>
</html>

后端websocket读取文件核心代码如下:

package com.easystudy.websocket;import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.CopyOnWriteArraySet;import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;import org.springframework.stereotype.Component;import com.easystudy.service.H264Reader;
import com.easystudy.service.H264Reader.Frame;
import com.easystudy.util.PoolHelper;/*** @ServerEndpoint 该注解可以将类定义成一个WebSocket服务器端,* @OnOpen      表示有浏览器链接过来的时候被调用* @OnClose     表示浏览器发出关闭请求的时候被调用* @OnMessage  表示浏览器发消息的时候被调用* @OnError   表示报错了* @欢迎加入群聊,一起分享,一起合作,一起进步* QQ交流群:961179337* 微信账号:lixiang6153* 微信公众号:IT技术快餐* 电子邮箱:lixx2048@163.com*/
@Component
@ServerEndpoint("/play2")
public class MessageEndPoint extends BaseWS{private H264Reader h264Reader = new H264Reader();// concurrent包下线程安全的Setprivate static final CopyOnWriteArraySet<MessageEndPoint> SESSIONS = new CopyOnWriteArraySet<>();// 当前连接会话信息private Session session;// 是否停止发送音视频private boolean stop = false;@OnOpenpublic void onOpen(Session session) {this.session = session;SESSIONS.add(this);System.out.println(String.format("成功建立连接~ 当前总连接数为:%s", SESSIONS.size()));stop = false;h264Reader.open("test.h264");PoolHelper.execute(new Runnable() {@Overridepublic void run() {System.out.println("开始发送");byte[] szStartCode = {0x00, 0x00, 0x00, 0x01};while (!stop) {Frame frame = h264Reader.readFrame(szStartCode);if (null == frame || frame.getLength() <= 0) {break;}sendMessage(frame.getData());System.out.println("发送数据帧:" + frame.getLength());try {// 这里模拟需要考虑读取文件的时间所以不按40ms(25fps),否则导致卡顿// Thread.sleep(40);Thread.sleep(30);} catch (InterruptedException e) {e.printStackTrace();}}if (!stop) {try {byte[] b = new byte[0];sendMessage(b, true);session.close();} catch (IOException e) {e.printStackTrace();}}System.out.println("结束发送");}});}@OnClosepublic void onClose() {SESSIONS.remove(this);System.out.println(String.format("成功关闭连接~ 当前总连接数为:%s", SESSIONS.size()));stop = true;h264Reader.close();}@OnMessagepublic void onMessage(String message, Session session) {System.out.println("收到客户端【" +session.getId()+ "】消息:" + message);}@OnErrorpublic void onError(Session session, Throwable error) {System.out.println("发生错误");error.printStackTrace();}/*** 指定用户发文本消息* @param message*/public void sendMessage(String message) {try {this.session.getBasicRemote().sendText(message);} catch (IOException e) {e.printStackTrace();}}/*** 指定用户发二进制消息并指定标志* @param message*/public void sendMessage(byte[] message, boolean end) {try {ByteBuffer bf = ByteBuffer.wrap(message);this.session.getBasicRemote().sendBinary(bf, end);} catch (IOException e) {e.printStackTrace();}}/*** @功能描述: 发送二进制数据-无结束标志* @版权信息:  www.easystudy.com* @编写作者:  lixx2048@163.com* @开发日期:  2020年9月21日* @备注信息:*/public void sendMessage(byte[] message) {try {ByteBuffer bf = ByteBuffer.wrap(message);this.session.getBasicRemote().sendBinary(bf);} catch (IOException e) {e.printStackTrace();}}/*** @功能描述: 群发消息: 静态方法* @版权信息:  www.easystudy.com* @编写作者:  lixx2048@163.com* @开发日期:  2020年9月21日* @备注信息:*/public static void fanoutMessage(String message) {SESSIONS.forEach(ws -> ws.sendMessage(message));}
}

这里的核心就是当websocket连接上来的时候,通过读取一帧H264数据然后发送给前端,默认帧率25帧,所以发送一帧的时候需要睡眠40ms,但是这里如果直接睡眠40ms,前端可能播放渲染不流畅,原因是因为读取H264文件的时候也需要时间的,所以我这里睡眠了30ms。

后台发送数据截图:

另外这块需要注意的是wfs.js库:

  • wfs库中连接的地址是ws://localhost:8080/,如果地址不对需要修改wfs.js的地址
    key: 'onMediaAttached',value: function onMediaAttached(data) {if (data.websocketName != undefined) {var client = new WebSocket('ws://' + '127.0.0.1:8080' + '/' + data.websocketName);this.wfs.attachWebsocket(client, data.channelName);console.log('websocket connect');} else {console.log('websocketName ERROE!!!');}}

这里可以根据需要修改域名或添加自己的项目名称定制。

  • 后台项目不需要项目上下文名称(也就是项目名称),websocket否则连接不上

经过测试google和360浏览器都可以直接播放H264数据,而不需要额外的插件或安全设置,堪称完美!

源码获取、合作、技术交流请获取如下联系方式:
QQ交流群:961179337

微信账号:lixiang6153
公众号:IT技术快餐
电子邮箱:lixx2048@163.com

H5播放H264之websocket相关推荐

  1. php获取h5视频直链,一种H5播放实时视频的方法与系统与流程

    本发明涉及播放实时视频,尤其涉及一种h5播放实时视频的方法与系统. 背景技术: h5是指第5代html,也指用h5语言制作的一切数字产品.所谓html是"超文本标记语言"的英文缩写 ...

  2. 免费视频直播、点播H5播放器SkeyeWebPlayer播放常见问题

    免费视频直播.点播H5播放器SkeyeWebPlayer使用常见问题 1.用常见问题--配置iframe允许自动播放和全屏 SkeyeWebPlayer播放器在PC上正常情况下单击播放器的全屏按钮是可 ...

  3. 免费视频直播、点播H5播放器SkeyeWebPlayer 结合百度地图sdk实现电子地图播放功能

    免费视频直播.点播H5播放器SkeyeWebPlayer 结合vue-baidu-map百度地图组件实现电子地图播放功能,最终效果如图所示: SkeyeWebPlayer播放器如何在vue-baidu ...

  4. 熊猫TV直播H5播放器架构探索

    本文来自熊猫TV音视频技术专家姜雨晴在LiveVideoStackCon 2017上的分享,并有LiveVideoStack整理成文.当下,打造一款播放器已经有比较好的开源实现,但熊猫TV为什么还要自 ...

  5. H5播放HLS之hls.min.js库

    H5播放HLS之hls.min.js库 视频推流 H播放HLS 视频推流 为了产生HLS视频,我们可以借助srs来实现rtmp推流并生成HLS流,具体详细使用可以参考我之前的文章,这里不再赘述. 我们 ...

  6. web无插件解码播放H264/H265(WebAssembly解码HTML5播放)

    我之前写过一篇<web无插件解码播放H264/H265(js解码HTML5播放)>,与本文的项目意义基本一致,不同的是实现方案有一定差异.之前介绍的是纯JS解码,本文介绍WebAssemb ...

  7. 关于H5播放视频文件的问题

    m3u8文件是指UTF-8编码格式的M3U文件.M3U文件是记录了一个索引纯文本文件,打开它时播放软件并不是播放它,而是根据它的索引找到对应的音视频文件的路径进行播放. 简单说,播放器通过m3u8文件 ...

  8. 搭建webassembly网页播放器(六)---websocket后台服务程序

    这里我们主要介绍后台搭建技术,前端搭建好后,需要后端配置展现,实现的效果就是 :网页连接上我们的 websocket服务后,我们就从H264文件中不断的提取出H264帧,然后传递给前端,前端调用web ...

  9. H5播放webrtc视频

    一.简介 WebRTC概念 WebRTC是由Google主导的,由一组标准.协议和JavaScript API组成,用于实现浏览器之间(端到端之间)的音频.视频及数据共享.WebRTC不需要安装任何插 ...

最新文章

  1. 谈谈机器学习模型的部署
  2. 命令行中创建和打开模Android拟器
  3. python try else_python try/except/else与递归
  4. 上位机和下位机的概念,理解如何实现PC从PLC中读取数据?
  5. linux 文件预读,一种基于Linux系统小文件预读功能的可靠性测试方法与流程
  6. 《纽约时报》:乔布斯最后的日子 与家人相伴
  7. avr单片机流水灯程序c语言,动手学AVR单片机流水灯实验电路和程序实现.doc
  8. 牛顿插值java_java实现牛顿插值法
  9. LTE网络架构 学习整理
  10. 设计一个三阶巴特沃斯滤波器_巴特沃斯低通滤波器设计分析.doc
  11. dota2服务器位置设置在哪里,《DOTA2》自走棋国服怎么进入 自走棋国服服务器进入方法...
  12. 达芬奇--艺工结合先驱
  13. IDEA查看历史记录
  14. android 自定义locale,Android Locale填坑
  15. 工业控制系统(ICS)部署图
  16. ih5连接mysql数据库_iH5高级教程:H5数据应用,数据库基础
  17. vlc web 登录账号_使用VLC Activex插件做网页版视频播放器
  18. 抖音直播带货怎么开通?最新政策及直播带货话术技巧!
  19. 证券公司信息化1-证券行业的本质是什么?什么是资本市场?什么又是一级市场和二级市场?
  20. [高数][高昆轮][高等数学上][第一章-函数与极限]07.无穷小的比较

热门文章

  1. 小岳岳吐槽房子隔音差:买房小心隔墙有耳
  2. The ADB binary found at XX is obsolete and has seriousperformance problems with the Android Emulator
  3. zabbix简单安装部署
  4. 解读 2s-AGCN 代码
  5. 余弦相似度 高维数据_从勾股定理到余弦相似度-程序员的数学基础
  6. ACM模板 | 学习笔记 数学相关
  7. CPU E3-1230v2 开盖
  8. 汇编语言--jmp指令
  9. 我的世界四大微软签约服务器,一年吸纳1.5亿用户,《我的世界》宣布开启“阴阳师”等四大IP联动 - 全文...
  10. 亲爱的老狼-绝对路径、相对路径