WebRTC with Pion

For a project I’ve been using Pion WebRTC which is a Golang implementation of the WebRTC API.

It is easy to set up and provides great performance.

Some tips when using Pion WebRTC, or WebRTC in general:

  • Use multiple TURN/STUN servers. I’ve been using coturn and Twilio’s WebRTC servers.
  • Use Trickle ICE to speed up the initial connection
  • When using h264, use the h264reader to send NALs

Streaming with Pion WebRTC

Once you’ve done all the peer/ICE handling, you can send video/audio. Below is the piece of code I’ve been using to send a h264 stream through WebRTC:

		h264FrameDuration := time.Millisecond * 33 
		ticker := time.NewTicker(h264FrameDuration)
		for ; true; <-ticker.C {
			nal, h264Err := h264.NextNAL()
			if h264Err == io.EOF {
				fmt.Printf("All video frames parsed and sent")
			if h264Err != nil {

			if h264Err = videoTrack.WriteSample(media.Sample{Data: nal.Data, Duration: time.Second}); h264Err != nil {

Similar for audio, you can send incoming Opus packets like this:

audioTrack, audioTrackErr := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus}, "audio", "pion")

p := make([]byte, 960)

for {
	n, err := audioReader.Read(p)
	if err == io.EOF {
	if audioTrackErr = audioTrack.WriteSample(media.Sample{Data: p[:n], Duration: time.Millisecond * 20}); audioTrackErr != nil {

Signaling and WebRTC

A great way to do signaling is through Websockets. I can highly recommend using Gorilla WebSocket which provides a clean API to handle Websockets with Go.

Upon receiving an ICE candidate through a Websocket message, you can pass the candidate to Pion through a Go channel. The answer can be sent back, by sending a Websocket message back to the sender.


macOS Virtualization.Framework

The macOS Virtualization.Framework allows you to run up to 2 macOS VMs (Virtual Machines) on Apple hardware.

The limit of 2 VMs per machine is due to Apple’s EULA, explicitly setting a maximum of 2 copies of macOS per Apple machine.

The framework, which runs on Apple Silicon, comes with paravirtualized graphics which means using Metal on the VMs works pretty well.

To get started, you can either implement the framework yourself, or use one of the open-source projects: Tart or VirtualBuddy.

You’ll need an ipsw file of your macOS of choice (macOS Monterey or higher), after which you can create a brand new VM.

There are some limitations to using the framework:

  • No iCloud support (yet)
  • Bridge networks require a special entitlement ( from Apple
  • Random crashes, flaky performance with Xcode 14+ on macOS Ventura 13.4 and higher
  • No multiple resolution support

I’ve had some limited success with using Better Display’s old open-source project to add multiple resolution support to the virtualization.framework VM.

The advantages of using the framework is very fast boot times for macOS VMs, speedy graphics and ease of use.