微信小程序开发笔记[3]-编码并推流视频(个人版小程序不可行)

原理

如今的emscripten已经可以轻松的将c/c++代码转换成asm/wasm,通过emscripten的Module对象可以控制wasm代码的执行,实现数据的交互,函数调用。
[https://blog.csdn.net/weixin_39722917/article/details/110839058]
2012年,Mozilla 的工程师 Alon Zakai 在研究 LLVM 编译器时突发奇想:许多 3D 游戏都是用 C / C++ 语言写的,如果能将 C / C++ 语言编译成 JavaScript 代码,它们不就能在浏览器里运行了吗?众所周知,JavaScript 的基本语法与 C 语言高度相似。
于是,他开始研究怎么才能实现这个目标,为此专门做了一个编译器项目 Emscripten。这个编译器可以将 C / C++ 代码编译成 JS 代码,但不是普通的 JS,而是一种叫做 asm.js 的 JavaScript 变体。
asm.js 就是为了解决这两个问题而设计的:它的变量一律都是静态类型,并且取消垃圾回收机制。除了这两点,它与 JavaScript 并无差异,也就是说,asm.js 是 JavaScript 的一个严格的子集,只能使用后者的一部分语法。
一旦 JavaScript 引擎发现运行的是 asm.js,就知道这是经过优化的代码,可以跳过语法分析这一步,直接转成汇编语言。另外,浏览器还会调用 WebGL 通过 GPU 执行 asm.js,即 asm.js 的执行引擎与普通的 JavaScript 脚本不同。这些都是 asm.js 运行较快的原因。据称,asm.js 在浏览器里的运行速度,大约是原生代码的50%左右。
如果你对 JS 比较了解,可能知道还有一种叫做 WebAssembly 的技术,也能将 C / C++ 转成 JS 引擎可以运行的代码。那么它与 asm.js 有何区别呢?
回答是,两者的功能基本一致,就是转出来的代码不一样:asm.js 是文本,WebAssembly 是二进制字节码,因此运行速度更快、体积更小。从长远来看,WebAssembly 的前景更光明。
但是,这并不意味着 asm.js 肯定会被淘汰,因为它有两个优点:首先,它是文本,人类可读,比较直观;其次,所有浏览器都支持 asm.js,不会有兼容性问题。
[https://blog.csdn.net/yelin042/article/details/77929480?locationNum=2&fps=1]
将ffmpeg编译为wasm库,然后通过js调用相关的接口可以实现web端音视频工具。

编译WASM的两种方式

方式一:
C/C++(或其他LLVM语言)->LLVM-IR->ASM.js->WASM
方式二:
C/C++(或其他LLVM语言)->LLVM-IR->Native Assembly(.s)->WASM

配置docker编译环境

方法一:

这个方法国内无法使用
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

方法二:

[https://hub.docker.com/r/abdulachik/ffmpeg.js/tags]
docker pull abdulachik/ffmpeg.js:latest
docker run -it -p 8090:8090 -v /Users/workspace/Downloads/ffmpeg_wasm:/tmp --privileged=true abdulachik/ffmpeg.js:latest /bin/bash
进入后:

可以看到已经编译好的ffmpeg-mp4.js文件
ffmpeg-mp4.js(10.4MB)

微信小程序使用WebAssembly

[https://zhuanlan.zhihu.com/p/441600357]
WXWebAssembly.instantiate(wasmBinaryFile, info)
JS 调 WebAssembly 分为 3 大步: 加载字节码 > 编译字节码 > 实例化 ,获取到 WebAssembly 实例后就可以通过 JS 去调用了.
对于浏览器可以通过网络请求去加载字节码,对于 Nodejs 可以通过 fs 模块读取字节码文件;
在获取到字节码后都需要转换成 ArrayBuffer 后才能被编译,通过 WebAssembly 通过的 JS API WebAssembly.compile 编译后会通过 Promise resolve 一个 WebAssembly.Module ,这个 module 是不能直接被调用的需要;
在获取到 module 后需要通过 WebAssembly.Instance API 去实例化 module,获取到 Instance 后就可以像使用 JS 模块一个调用了。

WebAssembly运行的要素

[https://zhuanlan.zhihu.com/p/114542645]
编译结果如果输出html文件的话默认会生成一个胶水js文件和一个html调试文件:
emcc first.cpp -o index.html
ps: 胶水文件连接了一些C++常用类库的实现比如控制台、网络等在浏览器端的实现,如果是纯计算类的wasm应用则不需要胶水文件。


wasm文件:可执行二进制文件
js文件:胶水js


web bluetooth技术

[https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API]
Safari on ios不支持
Chrome56支持

结论:想单纯用浏览器实现业务是做不到的,需要依托微信小程序、安卓/IOS/鸿蒙的APP才能实现。

局域网通信

[https://developers.weixin.qq.com/miniprogram/dev/framework/ability/mDNS.html]
wx.request/wx.connectSocket/wx.uploadFile/wx.downloadFile 的 url 参数允许为 \({IP}:\){PORT}/${PATH} 的格式,当且仅当 IP 与手机 IP 处在同一网段且不与本机 IP 相同(一般来说,就是同一局域网,如连接在同一个 wifi 下)时,请求/连接才会成功。
在这种情况下,不会进行安全域的校验,不要求必须使用 https/wss,也可以使用 http/ws。
wx.request({
url: 'http://10.9.176.40:828'
// 省略其他参数
})

wx.connectSocket({
url: 'ws://10.9.176.42:828'
// 省略其他参数
//当socket连接成功之后执行function里面的代码.
})

WebSocket

WebSocket 是独立的、创建在 TCP 上的协议.
在实现websocket连线过程中,需要透过浏览器发出websocket连线请求,然后服务器发出回应,这个过程通常称为“握手”(handshaking)

php实现socket

socket 编程需要开启 php 的 socket 扩展.
[https://www.php.net/manual/en/book.sockets.php]
监听所有ip:0.0.0.0

H264编码串流

额外内容:h264 分为几种,一种为baseline,一种为main,一种为pro。如果一定要使用这个库的话必须要将视频流转为baseline流。
编码帧相对于非编码帧,通过编码器(如 H264/H265、VP8/VP9)压缩后的帧称为编码帧。这里我们以 H264 为例,经过 H264 编码的帧包括以下三种类型。

  • I 帧:关键帧。压缩率低,可以单独解码成一幅完整的图像。
  • P 帧:参考帧。压缩率较高,解码时依赖于前面已解码的数据。
  • B 帧:前后参考帧。压缩率最高,解码时不光依赖前面已经解码的帧,而且还依赖它后面的 P 帧。换句话说就是,B 帧后面的 P 帧要优先于它进行解码,然后才能将 B 帧解码。

方案一

wasm + FFmpeg方案(调用ffmpeg库,使用emcc编译c++为js文件)

方案二

使用现成的ffmpeg-mp4.js文件,使用ffmpeg推流RTP

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);
  },
});

假设获取到的浏览器视频流为MediaStream
使用RTP协议,
写成命令形式为:
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
但是即使是分包,还是太大了

扫码连接

嵌入式的wifi名为wireless_cam_四位唯一id,e.g. wireless_cam_6Ydt,密码为6Ydt,这些通过二维码扫码获取(扫码->微信小程序->连接wifi)
用接口B生成小程序码:生成个数不受限,可以附加scene参数用于连接wifi(可以实现一物一码)
scene="6Ydt"
本接口应在服务器端调用,详细说明参见服务端API
url带多个参数
完整请求1:POST https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN&scene=6Ydt&page=pages/WirelessCam/WirelessCam
access_token 是小程序全局唯一后台接口调用凭据,调用绝大多数后台接口时都需使用。开发者可以通过 getAccessToken 接口获取并进行妥善保存。
//TODO:获取小程序二维码

扫码后,解析参数

scene 字段的值会作为 query 参数传递给小程序/小游戏。用户扫描该码进入小程序/小游戏后,开发者可以获取到二维码中的 scene 值,再做处理逻辑。
调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 encodeURIComponent.

Page({
  onLoad (query) {
    // scene 需要使用 decodeURIComponent 才能获取到生成二维码时传入的 scene
    const scene = decodeURIComponent(query.scene)
  }
})

配置带参数的小程序调试

编译->添加编译模式->重点配置启动参数和启动页面
启动参数:scene=6Ydt
启动页面:pages/WirelessCam/WirelessCam

嵌入式推流地址

192.168.2.100:81
当前的网关的IP地址就是服务器地址,默认是192.168.4.1这个是乐鑫在出厂时候写死的.

RTP协议

基于UDP协议,不保证传输质量

小程序分包

引用的js文件太大,超过小程序大小限制,需要使用分包
[https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/basic.html]
app.json

"pages": [
    "pages/more/more",
    "pages/balcony/index",
    "pages/settings/index",
    "pages/index/index"
  ],
  "subpackages":[
    {
      "root":"package_wireless_cam",
      "pages":[
        "pages/WirelessCam"
      ]
    }
  ],

然而,小程序单个主包/分包大小2M内,所有分包总共大小20M以内.
方案一:切片巨大的ffmpeg-mp4.js文件为6个文件,分拆到6个包中,使用时拼合为1个js文件。(不可行)
方案二:加载url资源到缓存中(不可行)
微信小程序使用自定义目录(文件路径)进行下载/保存 案例
https://www.qsbye.cn/more/wireless-cam/ffmpeg-mp4.js

posted @ 2022-08-14 21:45  qsBye  阅读(1652)  评论(0编辑  收藏  举报