Last updated: June 13, 2025

<span aria-hidden="true" id="adaptive-video-streaming-in-go-with-ffmpeg"></span>

# Adaptive video streaming in Go with FFmpeg

![Kevin van Zonneveld](/assets/images/teammates/avatar-kvz-4.jpg?dpl=dpl_5eQDcFwLGbzg3PfkSEW2a2j2x5pY)

**Kevin van Zonneveld**

Co-founder · Amsterdam, The Netherlands · Show bio

[](https://x.com/kvz)[](https://github.com/kvz)

Adaptive streaming is essential for delivering high-quality video content efficiently across various network conditions. In this DevTip, we'll explore how to implement adaptive video streaming in Go using FFmpeg, specifically focusing on HTTP Live Streaming (HLS) and MPEG-DASH formats. Before we dive in, ensure you have a Go development environment set up and FFmpeg installed on your system.

<span aria-hidden="true" id="introduction-to-adaptive-streaming-hls-vs-mpeg-dash"></span>

## Introduction to adaptive streaming: HLS vs MPEG-DASH

Adaptive bitrate streaming dynamically adjusts video quality based on the viewer's network conditions. Two popular standards are HLS (developed by Apple) and MPEG-DASH (an open standard). HLS uses `.m3u8` manifest files, while MPEG-DASH utilizes `.mpd` files. Both formats segment videos into smaller chunks, allowing seamless quality adjustments.

<span aria-hidden="true" id="setting-up-ffmpeg-with-go-choosing-the-right-wrapper"></span>

## Setting up FFmpeg with Go: choosing the right wrapper

Several Go wrappers for FFmpeg exist, but `goffmpeg` (specifically `v2`) stands out due to its simplicity and active maintenance. First, initialize a Go module in your project directory if you haven't already:

```bash
go mod init your_module_name

```

Then, install `goffmpeg/v2` using:

```bash
go get github.com/xfrr/goffmpeg/v2

```

After updating your code and `go.mod` file, it's good practice to run `go mod tidy` to clean up dependencies.

<span aria-hidden="true" id="converting-videos-to-hls-format-with-goffmpeg"></span>

## Converting videos to HLS format with goffmpeg

Here's how you can convert a video to HLS format using `goffmpeg/v2`:

```go
package main

import (
	"log"
	"time"
	"github.com/xfrr/goffmpeg/v2/transcoder"
)

func main() {
	trans := transcoder.New() // Use the New() constructor

	inputPath := "input.mp4"
	outputPath := "output.m3u8"

	// Initialize the transcoder with input, output, and HLS options using the builder pattern
	err := trans.
		Input(inputPath).
		Output(outputPath).
		OutputOptions(
			transcoder.WithOutputFormat("hls"),              // Specify HLS format
			transcoder.HLSSegmentDuration(10*time.Second), // Set HLS segment duration
			transcoder.HLSPlaylistType("vod"),             // Set HLS playlist type
		).
		Initialize()

	if err != nil {
		log.Fatalf("Failed to initialize transcoder: %v", err)
	}

	// Run the transcoding process
	done := trans.Run(true) // Pass true to show progress, false to hide

	// Handle progress output (Progress is a struct in v2)
	progress := trans.Output()
	for p := range progress {
		log.Printf("Progress: %+v\n", p) // Example: log the Progress struct
	}

	// Wait for the transcoding to complete
	err = <-done
	if err != nil {
		log.Fatalf("HLS conversion failed: %v", err)
	} else {
		log.Println("HLS conversion completed successfully.")
	}
}

```

<span aria-hidden="true" id="implementing-mpeg-dash-conversion-in-go"></span>

## Implementing MPEG-DASH conversion in Go

To convert videos to MPEG-DASH, you can use FFmpeg directly via Go's `os/exec` package. This approach gives you direct access to FFmpeg's command-line interface.

```go
package main

import (
	"log"
	"os/exec"
)

func main() {
	cmd := exec.Command("ffmpeg", "-i", "input.mp4", "-f", "dash", "output.mpd")
	err := cmd.Run()
	if err != nil {
		log.Fatal(err)
	}
}

```

<span aria-hidden="true" id="creating-adaptive-bitrate-variants"></span>

## Creating adaptive bitrate variants

Adaptive streaming requires multiple bitrate variants to cater to different network speeds. Here's how to generate them for MPEG-DASH using FFmpeg. For production scenarios, you might also consider separate bitrate variants for audio streams.

```go
package main

import (
	"log"
	"os/exec"
)

func main() {
	cmd := exec.Command("ffmpeg", "-i", "input.mp4",
		"-map", "0:v", "-map", "0:a", // Map video and audio streams
		"-b:v:0", "800k", "-b:v:1", "1200k", "-b:v:2", "2000k", // Define video bitrates for different variants
		"-f", "dash", "output.mpd")

	err := cmd.Run()
	if err != nil {
		log.Fatal(err)
	}
}

```

<span aria-hidden="true" id="building-a-streaming-server-with-segment-handling"></span>

## Building a streaming server with segment handling

Use Go's built-in HTTP server to serve video segments. Ensure the directory specified (e.g.,`./videos`) exists and contains your manifest files (`.mpd` or `.m3u8`) and the corresponding video segment files.

```go
package main

import (
	"log"
	"net/http"
)

func main() {
	// Serve files from the "./videos" directory under the "/videos/" path
	http.Handle("/videos/", http.StripPrefix("/videos/", http.FileServer(http.Dir("./videos"))))
	log.Println("Starting server on :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

```

<span aria-hidden="true" id="optimizing-performance-with-concurrent-processing"></span>

## Optimizing performance with concurrent processing

Leverage Go's concurrency features (goroutines and wait groups) to process multiple videos simultaneously, significantly speeding up batch operations.

```go
package main

import (
	"log"
	"sync"
	"os/exec"
)

func processVideo(input, output string, wg *sync.WaitGroup) {
	defer wg.Done() // Decrement the counter when the goroutine completes
	log.Printf("Processing %s to %s\n", input, output)
	cmd := exec.Command("ffmpeg", "-i", input, "-f", "dash", output)
	if err := cmd.Run(); err != nil {
		log.Printf("Error processing %s: %v\n", input, err)
	} else {
		log.Printf("Finished processing %s\n", input)
	}
}

func main() {
	var wg sync.WaitGroup
	videos := []struct{ input, output string }{
		{"input1.mp4", "output1.mpd"},
		{"input2.mp4", "output2.mpd"},
		// Add more videos here
	}

	for _, v := range videos {
		wg.Add(1) // Increment the counter for each goroutine
		go processVideo(v.input, v.output, &wg)
	}

	wg.Wait() // Wait for all goroutines to complete
	log.Println("All videos processed.")
}

```

<span aria-hidden="true" id="testing-and-debugging-your-streaming-implementation"></span>

## Testing and debugging your streaming implementation

Use tools like VLC media player or browser-based HLS/DASH players (e.g., Shaka Player, Video.js) to test your streams. Verify that your manifest files (`.m3u8` or `.mpd`) correctly reference the video segments and that playback adapts to simulated network condition changes if your player supports it. Check FFmpeg logs for any errors during transcoding.

<span aria-hidden="true" id="conclusion"></span>

## Conclusion

Implementing adaptive streaming in Go with FFmpeg is straightforward and powerful. By following these steps, you can efficiently deliver high-quality video content tailored to your users' network conditions.

If you're looking for a managed solution, Transloadit's[🤖 /video/adaptive](/docs/robots/video-adaptive.md) Robot simplifies adaptive streaming by handling HLS and MPEG-DASH conversions seamlessly. Check out our [go-sdk](/docs/sdks/go-sdk.md) for easy integration.

\#golang#ffmpeg#video-streaming#adaptive-bitrate-streaming#goffmpeg#video-encoding-service

### 👩‍💻 Join 20k+ developers

Sign up for our [monthly newsletter](/newsletters.md) to receive direct links to 3 exclusive tech — and 2 product updates. No less, no more.

Your email:

Get access

## File uploading and encoding. Made simple.

Transloadit streamlines file handling for developers, trusted by brands like Coursera and The New York Times. We’re known for a reliable API, top-notch support, and a strong commitment to open source, with projects like [Uppy⁠](https://uppy.io) and [Tus⁠](https://tus.io) setting standards in file processing.

[Sign up](/c/)[Book a Demo](https://survey.typeform.com/to/kRg47Xi5)

No credit card needed

Cancel anytime
