Blog | 音乐播放器

概述

今天给我的个人博客加了个暗模式,这样可以在晚上阅读博客的时候不伤害眼睛。突然想到如果在阅读博客的时候可以听听音乐就更好了。 于是在github上找到了一个开源的项目MetingJS,看最后一次commit信息是3年前,但是看着README.md对网站的描述信息,是我要的音乐播放器无疑了。

MetingJS快速使用

把下面这段代码放到html页面中。

……

阅读全文

Openresty三 | 上传文件

上传文件接口

nginx.conf 的server 增加一个location:

location /upload
{
   resolver 8.8.8.8 ipv6=off;
   content_by_lua_file lua/upload.lua;
}

upload.lua

lua
-- upload.lua
--==========================================
-- 文件上传
--==========================================
local chunk_size = 4096
local form, err = upload:new(chunk_size)
if not form then
    ngx.log(ngx.ERR, "failed to new upload: ", err)
    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
form:set_timeout(1000)

-- 文件保存的根路径
local saveRootPath = "./download/"
-- 保存的文件对象
local fileToSave
--文件是否成功保存
local ret_save = false
local filename = ""
while true do
    local typ, res, err = form:read()
    if not typ then
        ngx.say("failed to read: ", err)
        return
    end
    if typ == "header" then
        -- 开始读取 http header
        -- 解析出本次上传的文件名
        local key = res[1]
        local value = res[2]
        if key == "Content-Disposition" then
            -- 解析出本次上传的文件名
            -- form-data; name="testFileName"; filename="testfile.txt"
            local kvlist = string.split(value, ';')
            for _, kv in ipairs(kvlist) do
                local seg = string.trim(kv)
                if seg:find("filename") then
                    local kvfile = string.split(seg, "=")
                    filename = string.sub(kvfile[2], 2, -2)
                    if filename then
                        fileToSave = io.open(saveRootPath .. filename, "w+")
                        if not fileToSave then
                            ngx.say("failed to open file ", filename)
                            return
                        end
                        break
                    end
                end
            end
        end
    elseif typ == "body" then
        -- 开始读取 http body
        if fileToSave then
            fileToSave:write(res)
        end
    elseif typ == "part_end" then
        -- 文件写结束,关闭文件
        if fileToSave then
            fileToSave:close()
            fileToSave = nil
        end

        ret_save = true
    elseif typ == "eof" then
        -- 文件读取结束
        break
    else
        ngx.log(ngx.INFO, "do other things")
    end
end
if not ret_save then
    ngx.say("save file error")
    return
end

filename = saveRootPath..filename
ngx.say("save file success filename: " .. filename)

curl测试

$ curl -X POST -F 'data=@foo.txt' http://127.0.0.1:9001/upload
  • foo.txt: 本地文件名字

更改上传目录权限

更改目录所有者命令

……

阅读全文

Webrtc二 | 共享桌面

环境

  • Ubuntu18.04
  • webrtc M84

改造peerconnection例子,用来实现桌面共享

改造类CapturerTrackSource

  • 增加桌面的数据源类RcrtcDesktopCapturerTrackSource
  • 继承webrtc::DesktopCapturer::Callback, 用于桌面数据捕获与回调
  • 继承类rtc::VideoSourceInterface<webrtc::VideoFrame>, 用于rendered_track_->AddOrUpdateSink(this, rtc::VideoSinkWants()); 添加class VideoRenderer : public rtc::VideoSinkInterface<webrtc::VideoFrame> 的回调

详细代码:

……

阅读全文

Webrtc一 | peerconnection

环境

  • Ubuntu18.04
  • webrtc M84

国内镜像:https://webrtc.org.cn/mirror/

webrtc交互过程

通过peerconnection的例子来分析webrtc的交互流程

peer_connection 解析

  1. 为了便于管理,如编码,回显等,需要在应用层和底层一帧数据做一个桥接层,以隔离上下层。
  2. 桥接层的代码主要位于media/basemedia/engine,WebRTC中抽象了VideoSourceInterfaceVideoSinkInterface这两个模板类分别表示生产和吸收视频流。
  3. 如果能提供视频数据,需要实现 VideoSourceInterface,此接口类暴露了 AddOrUpdateSink 函数,可以将 Sink 注册给 Source
  4. 如果想要接收视频数据,需要实现 VideoSinkInterface,此接口类暴露了 OnFrame 函数。只要将 Sink 通过 AddOrUpdateSink 函数注册给 Source, 那么 Source 就会通过 OnFrame 接口将数据传给 Sink
  5. 对于WebRTC的PeerConnection例子,其VcmCapturer类继承了VideoSinkInterfaceTestVideoCapturer类继承类VideoSourceInterface<VideoFrame>

信令交互主要是PeerConnectionClient这个类 初始化socket连接跟 绑定回调

……

阅读全文

Go-libp2p四 | 穿透NAT

本文目的

上文Go-libp2p三 | relay例子已经分析过relay例子,并解释了两个节点如果通过中继服务进行通信。本文改造relay例子 并让这个服务做如下两件事:

  1. 交换两个节点的在交换机上的信息,帮助两个节点进行P2P打洞。
  2. 在打洞不成功的情况下能提供中继服务,转发两节点发送的数据。

我是看到源码中有hole punching协议,我是看了测试用例后才明白的。 利用libp2p的hole punching协议,NAT穿透打洞。打洞成功后进行p2p传输

……

阅读全文

Go-libp2p三 | relay例子

我想做什么

chat和echo两个例子,说明了两个节点之间的通信需要两节点在同一个局域网内,或者有一个节点有公网。 如果我两个节点分别在两个NAT下面, 都在各自的局域网内,则需要有一个中继服务,这个服务做如下两件事:

……

阅读全文

Go-libp2p二 | echo例子

go-libp2p的echo例子分析

进入目录go-libp2p/examples/echo/, 编译 go build

先看README.md, 可以了解到使用echo的方式: 终端B

$ ./echo -l 3001
2021/10/28 11:25:33 I am /ip4/127.0.0.1/tcp/3001/p2p/QmTu8sxDi3fSjz9ZMvkAj3qA9YAoGskroEnyLz2ZvfTBhX
2021/10/28 11:25:33 listening for connections
2021/10/28 11:25:33 Now run "./echo -l 3002 -d /ip4/127.0.0.1/tcp/3001/p2p/QmTu8sxDi3fSjz9ZMvkAj3qA9YAoGskroEnyLz2ZvfTBhX" on a different terminal

2021/10/28 11:25:59 listener received new stream
2021/10/28 11:25:59 read: Hello, world!

在另外一个终端输A入:

……

阅读全文

Go-libp2p一 | chat例子

代码下载

git clone https://github.com/libp2p/go-libp2p.git

chat实例分析

进入目录go-libp2p/examples/chat/, 编译 go build 先看README.md, 可以了解到使用chat的方式

  1. 在节点 B
$ ./chat -sp 3001
2021/10/28 11:01:25 Run './chat -d /ip4/127.0.0.1/tcp/3001/p2p/QmXgEXt4CiqmVmDhPPQnyVkoj3Bsx3Nggq57PPsk5tLNTs' on another console.
2021/10/28 11:01:25 You can replace 127.0.0.1 with public IP as well.
2021/10/28 11:01:25 Waiting for incoming connection
2021/10/28 11:01:25 
2021/10/28 11:03:03 Got a new stream!
> hello
  1. 在节点 A, 如果B有公网IP,则把127.0.0.1替换成B的公网IP
$ ./chat -d /ip4/127.0.0.1/tcp/3001/p2p/QmXgEXt4CiqmVmDhPPQnyVkoj3Bsx3Nggq57PPsk5tLNTs
2021/10/28 11:03:03 This node's multiaddresses:
2021/10/28 11:03:03  - /ip4/172.16.3.205/tcp/53084
2021/10/28 11:03:03  - /ip4/127.0.0.1/tcp/53084
2021/10/28 11:03:03 
2021/10/28 11:03:03 Established connection to destination
> hello

作为被连接方B

先看代码:

……

阅读全文