微信小程序开发笔记[3]-编码并推流视频(个人版小程序不可行)
<h2 id="原理">原理</h2><p>如今的emscripten已经可以轻松的将c/c++代码转换成asm/wasm,通过emscripten的Module对象可以控制wasm代码的执行,实现数据的交互,函数调用。<br>
<br>
2012年,Mozilla 的工程师 Alon Zakai 在研究 LLVM 编译器时突发奇想:许多 3D 游戏都是用 C / C++ 语言写的,如果能将 C / C++ 语言编译成 JavaScript 代码,它们不就能在浏览器里运行了吗?众所周知,JavaScript 的基本语法与 C 语言高度相似。<br>
于是,他开始研究怎么才能实现这个目标,为此专门做了一个编译器项目 Emscripten。这个编译器可以将 C / C++ 代码编译成 JS 代码,但不是普通的 JS,而是一种叫做 asm.js 的 JavaScript 变体。<br>
asm.js 就是为了解决这两个问题而设计的:它的变量一律都是静态类型,并且取消垃圾回收机制。除了这两点,它与 JavaScript 并无差异,也就是说,asm.js 是 JavaScript 的一个严格的子集,只能使用后者的一部分语法。<br>
一旦 JavaScript 引擎发现运行的是 asm.js,就知道这是经过优化的代码,可以跳过语法分析这一步,直接转成汇编语言。另外,浏览器还会调用 WebGL 通过 GPU 执行 asm.js,即 asm.js 的执行引擎与普通的 JavaScript 脚本不同。这些都是 asm.js 运行较快的原因。据称,asm.js 在浏览器里的运行速度,大约是原生代码的50%左右。<br>
如果你对 JS 比较了解,可能知道还有一种叫做 WebAssembly 的技术,也能将 C / C++ 转成 JS 引擎可以运行的代码。那么它与 asm.js 有何区别呢?<br>
回答是,两者的功能基本一致,就是转出来的代码不一样:asm.js 是文本,WebAssembly 是二进制字节码,因此运行速度更快、体积更小。从长远来看,WebAssembly 的前景更光明。<br>
但是,这并不意味着 asm.js 肯定会被淘汰,因为它有两个优点:首先,它是文本,人类可读,比较直观;其次,所有浏览器都支持 asm.js,不会有兼容性问题。<br>
<br>
将ffmpeg编译为wasm库,然后通过js调用相关的接口可以实现web端音视频工具。</p>
<h2 id="编译wasm的两种方式">编译WASM的两种方式</h2>
<p>方式一:<br>
C/C++(或其他LLVM语言)->LLVM-IR->ASM.js->WASM<br>
方式二:<br>
C/C++(或其他LLVM语言)->LLVM-IR->Native Assembly(.s)->WASM</p>
<h2 id="配置docker编译环境">配置docker编译环境</h2>
<h3 id="方法一">方法一:</h3>
<pre><code>这个方法国内无法使用
cd /Users/workspace/Downloads/ffmpeg_wasm
git clone https://hub.fastgit.xyz/Kagami/ffmpeg.js.git --recurse-submodules
docker run --rm -it -p 8070:8070 -v /Users/workspace/Downloads/ffmpeg_wasm/ffmpeg.js:/mnt --privileged=true -w /opt kagamihi/ffmpeg.js
cp -a /mnt/{.git,build,Makefile} . && source /root/emsdk/emsdk_env.sh && make && cp ffmpeg*.js /mnt
</code></pre>
<h3 id="方法二">方法二:</h3>
<p><br>
docker pull abdulachik/ffmpeg.js:latest<br>
docker run -it -p 8090:8090 -v /Users/workspace/Downloads/ffmpeg_wasm:/tmp --privileged=true abdulachik/ffmpeg.js:latest /bin/bash<br>
进入后:<br>
<img src="https://img2022.cnblogs.com/blog/1048201/202208/1048201-20220805023049447-619017141.png"><br>
可以看到已经编译好的ffmpeg-mp4.js文件<br>
ffmpeg-mp4.js(10.4MB)</p>
<h2 id="微信小程序使用webassembly">微信小程序使用WebAssembly</h2>
<p><br>
WXWebAssembly.instantiate(wasmBinaryFile, info)<br>
JS 调 WebAssembly 分为 3 大步: 加载字节码 > 编译字节码 > 实例化 ,获取到 WebAssembly 实例后就可以通过 JS 去调用了.<br>
对于浏览器可以通过网络请求去加载字节码,对于 Nodejs 可以通过 fs 模块读取字节码文件;<br>
在获取到字节码后都需要转换成 ArrayBuffer 后才能被编译,通过 WebAssembly 通过的 JS API WebAssembly.compile 编译后会通过 Promise resolve 一个 WebAssembly.Module ,这个 module 是不能直接被调用的需要;<br>
在获取到 module 后需要通过 WebAssembly.Instance API 去实例化 module,获取到 Instance 后就可以像使用 JS 模块一个调用了。</p>
<h3 id="webassembly运行的要素">WebAssembly运行的要素</h3>
<p><br>
编译结果如果输出html文件的话默认会生成一个胶水js文件和一个html调试文件:<br>
emcc first.cpp -o index.html<br>
ps: 胶水文件连接了一些C++常用类库的实现比如控制台、网络等在浏览器端的实现,如果是纯计算类的wasm应用则不需要胶水文件。</p>
<hr>
<p>wasm文件:可执行二进制文件<br>
js文件:胶水js</p>
<hr>
<h2 id="web-bluetooth技术">web bluetooth技术</h2>
<p><br>
Safari on ios不支持<br>
Chrome56支持</p>
<h3 id="结论想单纯用浏览器实现业务是做不到的需要依托微信小程序安卓ios鸿蒙的app才能实现">结论:想单纯用浏览器实现业务是做不到的,需要依托微信小程序、安卓/IOS/鸿蒙的APP才能实现。</h3>
<h2 id="局域网通信">局域网通信</h2>
<p><br>
wx.request/wx.connectSocket/wx.uploadFile/wx.downloadFile 的 url 参数允许为 <span class="math inline">\({IP}:\)</span>{PORT}/${PATH} 的格式,当且仅当 IP 与手机 IP 处在同一网段且不与本机 IP 相同(一般来说,就是同一局域网,如连接在同一个 wifi 下)时,请求/连接才会成功。<br>
在这种情况下,不会进行安全域的校验,不要求必须使用 https/wss,也可以使用 http/ws。<br>
wx.request({<br>
url: 'http://10.9.176.40:828'<br>
// 省略其他参数<br>
})</p>
<p>wx.connectSocket({<br>
url: 'ws://10.9.176.42:828'<br>
// 省略其他参数<br>
//当socket连接成功之后执行function里面的代码.<br>
})</p>
<h3 id="websocket">WebSocket</h3>
<p>WebSocket 是独立的、创建在 TCP 上的协议.<br>
在实现websocket连线过程中,需要透过浏览器发出websocket连线请求,然后服务器发出回应,这个过程通常称为“握手”(handshaking)</p>
<h3 id="php实现socket">php实现socket</h3>
<p>socket 编程需要开启 php 的 socket 扩展.<br>
<br>
监听所有ip:0.0.0.0</p>
<h2 id="h264编码串流">H264编码串流</h2>
<p>额外内容:h264 分为几种,一种为baseline,一种为main,一种为pro。如果一定要使用这个库的话必须要将视频流转为baseline流。<br>
编码帧相对于非编码帧,通过编码器(如 H264/H265、VP8/VP9)压缩后的帧称为编码帧。这里我们以 H264 为例,经过 H264 编码的帧包括以下三种类型。</p>
<ul>
<li>I 帧:关键帧。压缩率低,可以单独解码成一幅完整的图像。</li>
<li>P 帧:参考帧。压缩率较高,解码时依赖于前面已解码的数据。</li>
<li>B 帧:前后参考帧。压缩率最高,解码时不光依赖前面已经解码的帧,而且还依赖它后面的 P 帧。换句话说就是,B 帧后面的 P 帧要优先于它进行解码,然后才能将 B 帧解码。</li>
</ul>
<h3 id="方案一">方案一</h3>
<p>wasm + FFmpeg方案(调用ffmpeg库,使用emcc编译c++为js文件)</p>
<h3 id="方案二">方案二</h3>
<p>使用现成的ffmpeg-mp4.js文件,使用ffmpeg推流RTP</p>
<pre><code>const ffmpeg = require("ffmpeg-mp4.js");
let stdout = "";
let stderr = "";
// Print FFmpeg's version.
ffmpeg({
arguments: ["-version"],
print: function(data) { stdout += data + "\n"; },
printErr: function(data) { stderr += data + "\n"; },
onExit: function(code) {
console.log("Process exited with code " + code);
console.log(stdout);
console.log(stderr);
},
});
</code></pre>
<p>假设获取到的浏览器视频流为MediaStream<br>
使用RTP协议,<br>
写成命令形式为:<br>
ffmpeg -f dshow -i video="MediaStream" -vcodec libx264 -preset:v ultrafast -tune:v zerolatency -s 320x240 -f rtp rtp://192.168.4.1 > cam_h264.sdp<br>
但是即使是分包,还是太大了<br>
<img src="https://img2022.cnblogs.com/blog/1048201/202208/1048201-20220815055347550-51200301.png"></p>
<h3 id="扫码连接">扫码连接</h3>
<p>嵌入式的wifi名为wireless_cam_四位唯一id,e.g. wireless_cam_6Ydt,密码为6Ydt,这些通过二维码扫码获取(扫码->微信小程序->连接wifi)<br>
用接口B生成小程序码:生成个数不受限,可以附加scene参数用于连接wifi(可以实现一物一码)<br>
scene="6Ydt"<br>
本接口应在服务器端调用,详细说明参见服务端API<br>
url带多个参数<br>
完整请求1:POST https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN&scene=6Ydt&page=pages/WirelessCam/WirelessCam<br>
access_token 是小程序全局唯一后台接口调用凭据,调用绝大多数后台接口时都需使用。开发者可以通过 getAccessToken 接口获取并进行妥善保存。<br>
//TODO:获取小程序二维码</p>
<h4 id="扫码后解析参数">扫码后,解析参数</h4>
<p>scene 字段的值会作为 query 参数传递给小程序/小游戏。用户扫描该码进入小程序/小游戏后,开发者可以获取到二维码中的 scene 值,再做处理逻辑。<br>
调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 encodeURIComponent.</p>
<pre><code>Page({
onLoad (query) {
// scene 需要使用 decodeURIComponent 才能获取到生成二维码时传入的 scene
const scene = decodeURIComponent(query.scene)
}
})
</code></pre>
<h4 id="配置带参数的小程序调试">配置带参数的小程序调试</h4>
<p>编译->添加编译模式->重点配置启动参数和启动页面<br>
启动参数:scene=6Ydt<br>
启动页面:pages/WirelessCam/WirelessCam</p>
<h4 id="嵌入式推流地址">嵌入式推流地址</h4>
<p><s>192.168.2.100:81</s><br>
当前的网关的IP地址就是服务器地址,默认是192.168.4.1这个是乐鑫在出厂时候写死的.</p>
<h2 id="rtp协议">RTP协议</h2>
<p>基于UDP协议,不保证传输质量</p>
<h2 id="小程序分包">小程序分包</h2>
<p>引用的js文件太大,超过小程序大小限制,需要使用分包<br>
<br>
<strong>app.json</strong></p>
<pre><code>"pages": [
"pages/more/more",
"pages/balcony/index",
"pages/settings/index",
"pages/index/index"
],
"subpackages":[
{
"root":"package_wireless_cam",
"pages":[
"pages/WirelessCam"
]
}
],
</code></pre>
<p>然而,小程序单个主包/分包大小2M内,所有分包总共大小20M以内.<br>
方案一:切片巨大的ffmpeg-mp4.js文件为6个文件,分拆到6个包中,使用时拼合为1个js文件。(不可行)<br>
方案二:加载url资源到缓存中(不可行)<br>
微信小程序使用自定义目录(文件路径)进行下载/保存 案例<br>
https://www.qsbye.cn/more/wireless-cam/ffmpeg-mp4.js</p><br><br>
来源:https://www.cnblogs.com/qsbye/p/16556379.html
頁:
[1]