Showing posts with label WebAssembly. Show all posts
Showing posts with label WebAssembly. Show all posts

Monday, October 21, 2019

WebAssembly ffmpeg.js 透過 stdio async 讀入檔案 (驗證?

在與來自烏克蘭github大大來信後,他推薦我用WebRTC方式,最後進行串流討論後得出以下結論

我覺得WebRTC預設編碼的x264,本身好像也是p2p??,雖然延遲性和安全性提高了但是在應付大規模場景下應該不適合,
别迷信 WebRtc,WebRtc只适合小范围(8人以内)音视频会议,不适合做直播:
1. 视频部分:vpx的编码器太弱,专利原因不能用264,做的好的都要自己改264/265代码才行。
2. 音频部分:音频只适合人声编码,对音乐和其他非人声的效果很糟糕。
3. 网络部分:对国内各种奇葩网络适应性太低,网络糟糕点或者人多点就卡。
4. 信号处理:同时用过 GIPS和 WebRTC 进行对比,可以肯定目前开源的代码是GIPS阉割过的。
5. 使用规模:10人以内使用,超过10人就挂了,WebEx方案支持的人数都比 RTC 强。
作者:韦易笑
链接:https://www.zhihu.com/question/25497090/answer/72397450
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
在我詢問他是否可以通過我在上回的方向去實作他說是可行的(基於商業客戶向他買解決方案)不能透漏,在分支
提到這個部分大概就可以解決我所說的問題,再來就是猜猜看怎麼處理了

在ffmpeg 轉成 ffmpeg.js 他在把 ffmpeg 裡面的 file 和 tcp 的函數裡面的意外中斷的片段程式碼
都給 remark掉 這樣在 運行 async stdio就不會意外中止 ( 或許就可以 向他說的 透過 getUserMedia 去 讀取資料這樣我們只要把 那些blob 轉成 file 跟原先一樣不用動,在結合上一個文章就可以 達到 用網路傳遞數據給後端 推送給 其他串流伺服器了前端編碼就完成囉!
再來就是 要再編譯的參數 EMTERPRETIFY_ASYNC=1 給打開
他fork出來額外新增的內容應該就是這些
提到他說可以從這方向找尋到我要的解答所以確定可以用getUesrMedia去實作還有以下要問題要解決
第一個就是blob跟輸出檔案問題一樣超過70mb可能會出現 記憶體崩潰或者溢位導致瀏覽器crash
可能要查閱 wasm fs 虛擬檔案映射那邊
第二個則是檔案的讀入方式
他提問我為什麼要用自定義ffmpeg 去做 影音上的串流,目前不是有 WebRTC可以達成目標了嗎
我當初想做是因為可以實現任意編碼的問題和可以隨意推送去任意 RTMP串流伺服器,再者就是 目前大部分主流是RTMP 又有豐富的 CDN 當然 WebRTC也可以實作CDN ,國外網友好像不怎麼贊成 好像不太怎麼穩定)
上述內容可能需要 重新compliter 整個 ffmpeg.js才能實證了,又是一個17小時?,有空再來玩 (話說 是不是該刷leetcode了

Wednesday, October 16, 2019

WebAssembly 修改 ffmpeg.js 在運行時輸出 檔案 ArrayBuffer (三)

今天要解決的問題是,之前我們遇到的,除非 wasm 權限越開越大,
不然就是 ffmpeg output 可以輸出至 其他 通訊 tcp/rtmp
那上次研究的結果還會卡到 既然是在 web worker 裡面那我們可能還會面臨 chrome 的
https/http 協議不能混用

這邊的話想了一下有沒有能在程式執行中 進行 output呢?
所以我覺得能不能在這兩個函數內找到一些結果,果然有發現
開始初始化前
Module[“preRun”]
結束後輸出
Module[“postRun”]

也就是大大們卡很久的檔案映射問題?
之前想為什麼輸出能掛載在虛擬機上,既然是虛擬機,我跑ffmpeg 的時候應該就可以讀到
掛載的檔案那麼是不是可以在ffmpeg run 的時候就可以取的檔案 buffer?

實驗結果是可以的就是在檔案輸出 stdout 的時候去呼叫原本我們在 postRun的那邊結束後呼叫的時候才會產生blob檔案只要修改這邊就可以了

再參考論壇等等有發現已經有可以從stdin 前端"解碼"的範例了,也可以讀 串流
https://cloud.tencent.com/developer/article/1453425
https://github.com/sonysuqin/WasmVideoPlayer
至於讀出呢,可以透過這些方式去 回傳 給 js 去做處理
接下來呢,後續可能會有什麼玩法?
目前的話可以,因為不同的電腦cpu呼叫 ffmpeg 編碼的話速率不同,一次解碼的量也會不同所以我們buffer的偏移量我覺得應該也會不同,至於一次要偏移有空下次研究,假設這個問題可以解決,返回函數到,js後我們可以或許我們可以做 ArrayBuffer 合併 串流 那我們就可以完成前端 "編碼"的問題,那我們之前要實作的,就只差通訊協定了 可以透過 websocket 進行傳輸,

我們目前就已經解決我之前做的範例透過 chrome mediarecoder 在傳輸給 socket.io 先進行 畫面上的錄製再傳送給後端 nodejs, nodejs 在 fork 很多個 ffmpeg 在進行推畫面上的重組送給其他串流伺服器。

現在我們是 先 透過  chrome mediarecoder 進行畫面上的 傳輸,再透過走 TCP 或 udp (謎樣方式串流)
輸入至位於webwork 裡面的 ffmpeg wasm 在這邊 執行 用戶端編碼再透過我的方式去把檔案的 ArrayBuffer ,看是要在前端進行js 編碼上的處理還是  還是透過 websocket傳輸至後端處理再推送給其他串流伺服器。

這邊可以看到我們已經大大的減少 伺服器端幫我們做的事情,這樣我們是不是更貼近 無插件進行 串流了呢(?


在2020 chrome 要徹底廢除掉 flash 的話,那我們就可以實現,在前端不加掛任何插件的方式下
透過chrome mediarecord 進行 攝像頭的讀入,再根據我們剛剛的方式,再配合偏移量的演算法寫作,應該就可以達到 無插件 進行 前端 推流,wasm 在繼續進步下去或許效能會越來越強,adobe flash 可能就要掰了哈哈

Sunday, July 14, 2019

WebAssembly 編譯ffmpeg 新增入口點和 web workweb work簡單設定 (二)

實現串流這部分看來是不行了,雖然後面有編譯成功 , emscripten file system

嘗試用pipe 流,去做處理也是失敗,最後只完成了 轉檔,部分指令可能失效?,

可能有關於配置的關係,SharedArrayBuffer,透過mediarecorder 去抓blob 再轉成

ArrayBuffer 去讓ffmpeg.js 讀也是失敗,嘖嘖,可能功力不太夠,可能要在研究一番 又要停止更新這主題了xd

https://github.com/x213212/build_ffmpeg.js

version

  • emcc v1.38.38
  • ffmpeg lest version

script

此版本編譯為
CPPFLAGS="-D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600" \
emconfigure ./configure --cc="emcc" \
--prefix=$(pwd)/../dist --enable-cross-compile --target-os=none --arch=x86_64 \
--cpu=generic --disable-ffplay --disable-ffprobe  \
--disable-asm --disable-doc --disable-devices --disable-pthreads \
--disable-w32threads  --disable-hwaccels \
--disable-parsers --disable-bsfs --disable-debug --disable-protocols \
--disable-indevs --disable-outdevs --enable-protocol=file --enable-protocol=rtmp --enable-protocol=pipe \
--enable-network --enable-protocol=tcp --enable-demuxer=rtsp --enable-decoder=h264 --enable-encoder=libx264 \
--enable-demuxer=flv 

emcc -s ASSERTIONS=1 -s VERBOSE=1 -s TOTAL_MEMORY=33554432 \
-s ALLOW_MEMORY_GROWTH=1 -s WASM=1 -O2 -v ffmpeg.bc \
-o ../ffmpeg.js --pre-js ./pre.js --post-js ./post.js

add some html index.html
<html>
<p>ffmpeg.js</p>
<input id="file-input" type="file" />
</html>
<script>

var worker = new Worker('worker.js');
var inputElement = document.getElementById("file-input");
inputElement.addEventListener("change", handleFiles, false);
function handleFiles() {
var fileList = this.files; 
console.log(fileList)
worker.postMessage(fileList);
}
</script>
worker.js
self.importScripts('ffmpeg.js');

onmessage = function(e) {
  console.log('ffmpeg_run', ffmpeg_run);
  var files = e.data;
  console.log(files);
  ffmpeg_run({
   // arguments: ['-i', 'https://gw.alicdn.com/bao/uploaded/LB1l2iXISzqK1RjSZFjXXblCFXa.mp4?file=LB1l2iXISzqK1RjSZFjXXblCFXa.mp4', '-b:v', '64k', '-bufsize', '64k', '-vf', 'showinfo', '-strict', '-2', 'out.mp4'],
  // arguments: ['-i', '/input/' + files[0].name  'out.mp4'],
   arguments: ['-version'],
    //files: files,
  }, function(results) {
    console.log('result',results);
   // self.postMessage(results[0].data, [results[0].data]);
  });

}
enjoy~

Saturday, July 13, 2019

WebAssembly 編譯ffmpeg 進行串流(構想與實現?


上一個系列要來暫停了,來進入回憶…
在進公司打算一開始做串流 sever 的時候,怕出糗本來想用wasm 的來做前端解碼,我則選擇了用 socket.io 傳送畫面至後端 再交給 ffmpeg 進行編碼,
真的不幸,高軟停電的時候把其中一台 串流 server 虛擬機毀損了…,
近期內又要再搭建一台 , 最近比較有空閒 ,大致上網頁的規畫已經完成差不多了
又要切換成 研究模式 來小研究一下技術,在串流這邊這部分還沒有看過有人這樣玩的,或許可以在 github上面刷星星了xd,說要追逐到目前的技術的化大概跟現在的技術差兩年吧…。
近期內我們來完成 以前想要做的事情吧。
其中的科普我就不多作介紹了
最近一直在猶豫 是否只要把相關連結 貼出來,然後 我們來將實作 所遇到的 bug 再詳細解決,是否 整體版面會比較乾淨?

WebAssembly 是什么

这里不阐述太多了,知乎上各种科普帖子都有,基本上理解成浏览器标准实现的一个web汇编格式,使得可以将llvm转成可以在web上运行的代码来加快运行速度,这里主要介绍从c/c++/rust等语言编译到wasm的一些工具

Emscripten & asm.js

Emscripten: An LLVM-to-JavaScript Compiler
Asm.js an extraordinarily optimizable, low-level subset of JavaScript
这是wasm过程中2个比较重要的工具,官网链接在上:

Emscripte :

wasm的编译器,根据上面的介绍,这是一个LLVM到js的编译器。咱对于编译原理这些底层的东西了解也不太多,简单理解的话LLVM就是一个编译的中间状态,使用llvm-gcc或者clang这些编译工具可以将c/c
编译到LLVM bitcode这样一个中间代码的状态, 同样类似像rust的语言也可以编译到LLVM。有了LLVM bitcode,就可以使用Emscripten这个工具来将其编译成更底层的汇编js代码。
那么更底层的汇编js代码是啥,早期在wasm还没有实现之前,汇编js的实现就是Asm.js(字如其名。。)。asm.js的核心功能就是模拟一个汇编语言的运行环境,通过一个大的UintArray数组来模拟机器内存,然后通过js实现各种汇编指令来对这个虚拟的内存(UintArray对象)进行操作。
通过上面这2个步骤,我们先是把c/c
通过编译器编译到LLVM,再利用emscripten将LLVM编译成asm.js,当我们在运行这段asm.js代码的时候,就会省去大量的解释器开销,相当于直接使用汇编代码一样来直接操作内存空间运行。
但是asm.js实现的汇编环境毕竟还是基于js的,在性能上还是不能完全满意,这个时候,WebAssebmly就应运而生了,由浏览器来实现汇编环境,规定好webassembly的汇编格式,使得性能进一步提示

編譯ffmpeg to wasm

git clone https://github.com/juj/emsdk && cd emsdk 
./emsdk install sdk-incoming-64bit binaryen-master-64bit 
./emsdk activate sdk-incoming-64bit binaryen-master-64bit
. ./emsdk_env.sh
需要一段時間...


就這一段 我們的 centos 很詭異,
我們遇到了這個 bug ,這個原因找了很久 ,是不是官方相關套件沒裝呢還是編譯器問題,
跑去官網看要怎樣編譯原生的ffmpeg
https://trac.ffmpeg.org/wiki/CompilationGuide/Centos
# 首先,从github等地方获取ffmpeg的源代码
git clone https://github.com/FFmpeg/FFmpeg
cd FFmpeg

# 开始configure
# 这里的参数参考自videoconverter.js,其中注意需要额外带上下面第一行的CPPFLAGS
# 否则不能在最新的emcripten下编译通过
# 这里通过--cc="emcc"来指定编译器为emcc,emcc会调用clang来将target设置成LLVM
CPPFLAGS="-D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600" \
emconfigure ./configure --cc="emcc" \
--prefix=$(pwd)/../dist --enable-cross-compile --target-os=none --arch=x86_64 \
--cpu=generic --disable-ffplay --disable-ffprobe --disable-ffserver \
--disable-asm --disable-doc --disable-devices --disable-pthreads \
--disable-w32threads --disable-network --disable-hwaccels \
--disable-parsers --disable-bsfs --disable-debug --disable-protocols \
--disable-indevs --disable-outdevs --enable-protocol=file
make # 記得開始編譯

centos 7 gcc 升級

我安裝gcc 要破半天?(冏,難怪預設不讓使用者去官方下載 xd
可能虛擬機 cpu 核心分配太少 還是記憶體不太夠

centos 7 版本切換 gcc

scl --list
scl enable devtoolset-7 bash
這樣大致上就可以完成編譯囉,太久沒交叉編譯可能會deubg到死。
[root@localhost lib64]# mv libstdc++.so.6 libstdc++.so.6bck
[root@localhost lib64]# ln -s libstdc++.so.6.0.24 libstdc++.so.6
[root@localhost lib64]# strings /usr/lib64/libstdc++.so.6 | grep GLIBC

ffmpeg.js

經過18小時第一個 編譯終於要開始了…
再撐一下 容量快報了…
今天先這樣 明天大概就看的到 ffmpeg.js 了
後面大致架構應該是, 從 其他分支下手 service worker 加掛一個
socket.io 再透過 io 進行 畫面傳輸,再配合 hmtl5 的函數
MediaRecorder ,恩恩(構思很好 ,希望可以編譯成功,交叉編譯真的會吐血。
隔天了,恩不錯
make
在下一個指令!!!

誕生…

Final Battle 編譯LLVM到WebAssmbly

这里使用的命令依旧是emcc,但是注意此时emcc的输入为LLVM bitcode,它将会调用emscriptem来将其编译到js (和第一步emcc的行为不同,因为输入格式不同,target也会不同)
# 这里的ffmpeg是上一步编译输出的LLVM bitcode
cp ffmpeg ffmpeg.bc

# 最终的输出是 -o 指定的,这些 -s 参数的意义可以从emcc的文档中找到
# 这里打开了ALLOW_MEMORY_GROWTH是因为在移动端测试下会遇到内存(wasm/asm.js的虚拟内存)
# 不够的情况,默认内存大小是TOTAL_MEMORY指定的
# 设置WASM=1就会编译到WebAssembly,默认编译到asm.js
emcc -s ASSERTIONS=1 -s VERBOSE=1 -s TOTAL_MEMORY=33554432 \
-s ALLOW_MEMORY_GROWTH=1 -s WASM=1 -O2 -v ffmpeg.bc \
-o ../ffmpeg.js --pre-js ../ffmpeg_pre.js --post-js ../ffmpeg_post.js
emcc ffmpeg.bc -o ffmpeg.html -s TOTAL_MEMORY=33554432 
隔天, 加大虛擬機 記憶體編譯就過了…
#開啟chrome 實驗wasm
然後呢再加點html
index.html
<html> <p>test</p> <button onclick="sayHI()">Say HI</button> </html> <script> var worker = new Worker('worker.js'); worker.postMessage("message"); </script>
worker.js
self.importScripts('ffmpeg.js'); onmessage = function(e) { console.log('ffmpeg_run', ffmpeg_run); var files = e.data; console.log(files); ffmpeg_run({ // arguments: ['-i', 'https://gw.alicdn.com/bao/uploaded/LB1l2iXISzqK1RjSZFjXXblCFXa.mp4?file=LB1l2iXISzqK1RjSZFjXXblCFXa.mp4', '-b:v', '64k', '-bufsize', '64k', '-vf', 'showinfo', '-strict', '-2', 'out.mp4'], // arguments: ['-i', '/input/' + files[0].name 'out.mp4'], arguments: ['-version'], //files: files, }, function(results) { console.log('result',results); // self.postMessage(results[0].data, [results[0].data]); }); }
下一篇來看看能不能串流...

參考