Go开发人员开发WebRTC的福音
Pion 是 WebRTC API 的纯 Golang 实现,极大提升了 WebRTC 应用开发的效率。Pion 提供了简单易用的 API,你可以使用它开发一些非常有创意的项目,例如:
- 发送一个视频文件到多个浏览器,实现同步的电影观看体验。
- 将嵌入式设备的摄像头内容发送到浏览器,无需额外服务器。
- 在两台服务器之间安全传输数据,不需要复杂的发布订阅机制。
- 录制网络摄像头视频,并在服务器端进行特效处理。
- 实现远程控制机器人,并实时传输摄像头画面。
- 构建一个根据音频/视频流做出决策的会议应用。
Pion实现的WebRTC特性
- PeerConnection API: 完整实现了 webrtc-pc 和 webrtc-stats,支持发送/接收音视频,数据通道和重新谈判(Renegotiation)。
- 连接性: 包含完整的 ICE 代理,支持 ICE 重启、Trickle ICE、STUN、TURN(UDP、TCP、DTLS 和 TLS)、mDNS 候选等。
- 数据通道: 支持排序/未排序,有损/无损的通信。
- 媒体处理: 提供直接访问 RTP/RTCP 的 API,支持 Opus、PCM、H264、VP8、VP9 等媒体格式,允许自定义打包器,并集成 IVF、Ogg、H264 和 Matroska 格式,方便发送和保存。
- 安全性: 支持 DTLS v1.2 和 SRTP 的多种加密算法,提供 GCM 套件的硬件加速。
支持的平台
Pion 支持的操作系统和架构包括:Windows、macOS、Linux、FreeBSD、iOS、Android、WASM、386、amd64、arm、mips、ppc64。
示例:发送视频文件到浏览器
以下是一个使用 Pion 发送视频文件到浏览器的示例代码:
package main
import (
"bufio"
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strings"
"time"
"github.com/pion/webrtc/v4"
"github.com/pion/webrtc/v4/pkg/media"
"github.com/pion/webrtc/v4/pkg/media/ivfreader"
)
const (
videoFileName = "output.ivf"
)
func main() {
// 检查视频文件是否存在
_, err := os.Stat(videoFileName)
haveVideoFile := !os.IsNotExist(err)
if !haveVideoFile {
panic("找不到视频文件 `" + videoFileName + "`")
}
// 创建一个新的 RTCPeerConnection
peerConnection, err := webrtc.NewPeerConnection(webrtc.Configuration{
ICEServers: []webrtc.ICEServer{
{ URLs: []string{"stun:stun.l.google.com:19302"} },
},
})
if err != nil {
panic(err)
}
defer peerConnection.Close()
iceConnectedCtx, iceConnectedCtxCancel := context.WithCancel(context.Background())
// 打开视频文件
file, openErr := os.Open(videoFileName)
if openErr != nil {
panic(openErr)
}
ivf, header, ivfErr := ivfreader.NewWith(file)
if ivfErr != nil {
panic(ivfErr)
}
// 创建视频轨道
videoTrack, videoTrackErr := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion")
if videoTrackErr != nil {
panic(videoTrackErr)
}
rtpSender, videoTrackErr := peerConnection.AddTrack(videoTrack)
if videoTrackErr != nil {
panic(videoTrackErr)
}
go func() {
rtcpBuf := make([]byte, 1500)
for {
if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil {
return
}
}
}()
go func() {
<-iceConnectedCtx.Done()
ticker := time.NewTicker(time.Millisecond * time.Duration((float32(header.TimebaseNumerator)/float32(header.TimebaseDenominator))*1000))
defer ticker.Stop()
for ; true; <-ticker.C {
frame, _, ivfErr := ivf.ParseNextFrame()
if errors.Is(ivfErr, io.EOF) {
fmt.Println("所有视频帧已发送")
os.Exit(0)
}
if ivfErr != nil {
panic(ivfErr)
}
if ivfErr = videoTrack.WriteSample(media.Sample{Data: frame, Duration: time.Second}); ivfErr != nil {
panic(ivfErr)
}
}
}()
// 设置 ICE 连接状态处理程序
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
fmt.Printf("连接状态已更改:%s \n", connectionState.String())
if connectionState == webrtc.ICEConnectionStateConnected {
iceConnectedCtxCancel()
}
})
// 等待提供 SDP offer
offer := webrtc.SessionDescription{}
decode(readUntilNewline(), &offer)
// 设置远程描述
if err = peerConnection.SetRemoteDescription(offer); err != nil {
panic(err)
}
// 创建 SDP answer
answer, err := peerConnection.CreateAnswer(nil)
if err != nil {
panic(err)
}
// 完成 ICE 收集
gatherComplete := webrtc.GatheringCompletePromise(peerConnection)
// 设置本地描述
if err = peerConnection.SetLocalDescription(answer); err != nil {
panic(err)
}
<-gatherComplete
// 输出 SDP answer
fmt.Println(encode(peerConnection.LocalDescription()))
select {}
}
func readUntilNewline() (in string) {
r := bufio.NewReader(os.Stdin)
in, _ = r.ReadString('\n')
return strings.TrimSpace(in)
}
func encode(obj *webrtc.SessionDescription) string {
b, err := json.Marshal(obj)
if err != nil {
panic(err)
}
return base64.StdEncoding.EncodeToString(b)
}
func decode(in string, obj *webrtc.SessionDescription) {
b, err := base64.StdEncoding.DecodeString(in)
if err != nil {
panic(err)
}
if err = json.Unmarshal(b, obj); err != nil {
panic(err)
}
}
这个示例展示了如何使用 Pion 从磁盘发送视频到浏览器。更详细的示例和内容请访问 Pion 的 GitHub 项目:Pion GitHub。