Files
webrtc/main.go
Keyvan Fatehi ffc1470a7c transcoding
2023-02-22 23:39:02 -08:00

139 lines
3.8 KiB
Go

package main
import (
"errors"
"fmt"
"io"
"log"
"secureput"
"strings"
"github.com/asticode/go-astiav"
"github.com/pion/webrtc/v3"
)
var visionTrack *VisionIpcTrack
func ReplaceTrack(prefix string, peerConnection *webrtc.PeerConnection) {
var err error
if visionTrack != nil {
visionTrack.Stop()
}
visionTrack, err = NewVisionIpcTrack(prefix + "EncodeData")
if err != nil {
log.Fatal(fmt.Errorf("main: creating track failed: %w", err))
}
// Create a video track
videoTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeH264}, "video", "pion")
if err != nil {
panic(err)
}
rtpSender, err := peerConnection.AddTrack(videoTrack)
if err != nil {
panic(err)
}
// Later on, we will use rtpSender.ReplaceTrack() for graceful track replacement
// Read incoming RTCP packets
// Before these packets are returned they are processed by interceptors. For things
// like NACK this needs to be called.
go func() {
rtcpBuf := make([]byte, 1500)
for {
if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil {
return
}
}
}()
// Set the handler for ICE connection state
// This will notify you when the peer has connected/disconnected
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
fmt.Printf("Connection State has changed %s \n", connectionState.String())
if connectionState.String() == "disconnected" {
visionTrack.Stop()
} else if connectionState == webrtc.ICEConnectionStateFailed {
visionTrack.Stop()
if closeErr := peerConnection.Close(); closeErr != nil {
log.Println(fmt.Errorf("main: peer connection closed due to error: %w", err))
}
} else if connectionState.String() == "connected" {
astiav.SetLogLevel(astiav.LogLevelError)
astiav.SetLogCallback(func(l astiav.LogLevel, fmt, msg, parent string) {
log.Printf("ffmpeg log: %s (level: %d)\n", strings.TrimSpace(msg), l)
})
go func() {
encoderParams := EncoderParams{
Width: visionTrack.Width(),
Height: visionTrack.Height(),
TimeBase: visionTrack.TimeBase(),
AspectRatio: visionTrack.AspectRatio(),
PixelFormat: visionTrack.PixelFormat(),
}
encoder, err := NewEncoder(encoderParams)
if err != nil {
log.Fatal(fmt.Errorf("main: creating track failed: %w", err))
return
}
defer encoder.Close()
go visionTrack.Start()
defer visionTrack.Stop()
for visionTrack != nil {
var rtpPacket []byte
for frame := range visionTrack.Frame {
// Do something with decoded frame
fmt.Println(frame.Roll)
// so right now frame is a raw decoded AVFrame
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/frame.h#L317
avframe := frame.Frame
// we need to:
// 1. transcode to h264 with adaptive bitrate using astiav
outFrame, err := encoder.Encode(avframe)
if err != nil {
log.Println(fmt.Errorf("encode error: %w", err))
continue
}
// 2. create an RTP packet with h264 data inside using pion's rtp packetizer
fmt.Println(len(outFrame.Data()))
// 3. write the RTP packet to the videoTrack
if _, err = videoTrack.Write(rtpPacket); err != nil {
if errors.Is(err, io.ErrClosedPipe) {
// The peerConnection has been closed.
return
}
log.Println(fmt.Errorf("rtp write error: %w", err))
}
}
}
}()
}
})
}
func main() {
signal := secureput.Create("go-webrtc-body")
signal.DeviceMetadata = map[string]interface{}{"isRobot": true}
signal.Gui = &Face{app: &signal}
go signal.RunDaemonMode()
if !signal.Paired() {
go signal.Gui.Show()
log.Println("Waiting to pair.")
<-signal.PairWaitChannel
}
signal.OnPeerConnectionCreated = func(pc *webrtc.PeerConnection) {
ReplaceTrack("road", pc)
}
for {
select {}
}
}