Build Local Video Streaming with Spring Boot, FFmpeg, and ZLMediaKit

This tutorial demonstrates building a local video streaming pipeline using Spring Boot to orchestrate FFmpeg for transcoding and ZLMediaKit as the media server, covering Docker deployment, RTMP/HLS configuration, and a web-based FLV player using flv.js.

Top Architect
Top Architect
Top Architect
Build Local Video Streaming with Spring Boot, FFmpeg, and ZLMediaKit

Environment Preparation

ZLMediaKit Installation and Configuration

Deploy ZLMediaKit via Docker with the following command, exposing ports for RTMP (1935), HTTP API (8099), RTSP (8554), and WebRTC (10000/udp, 8000/udp). Mount a local config.ini for HLS settings.

# Pull image
docker pull zlmediakit/zlmediakit:master

# Start container
docker run -d \
  --name zlm-server \
  -p 1935:1935 \
  -p 8099:80 \
  -p 8554:554 \
  -p 10000:10000 \
  -p 10000:10000/udp \
  -p 8000:8000/udp \
  -v /docker-volumes/zlmediakit/conf/config.ini:/opt/media/conf/config.ini \
  zlmediakit/zlmediakit:master

Key HLS configuration in config.ini:

[hls]
broadcastRecordTs=0
deleteDelaySec=300  # Keep stream 5 minutes
fileBufSize=65536
filePath=./www      # Save path
segDur=2          # Segment duration (seconds)
segNum=1000       # Max segments in m3u8
segRetain=9999    # Retain history segments on disk

Verify startup with docker logs -f zlm-server.

FFmpeg Installation

Download FFmpeg (e.g., version 7.0.2) from the official site, extract, and add the bin directory to the system PATH environment variable.

Spring Boot Backend Implementation

Dependencies

Add Apache Commons Exec for process management:

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-exec</artifactId>
  <version>1.3</version>
</dependency>

Stream Configuration Class

Map properties under prefix stream:

@Data
@Component
@ConfigurationProperties(prefix = "stream")
public class StreamConfig {
    private String zlmHost;
    private Integer rtmpPort;
    private Integer httpPort;
    private String ffmpegPath;
    private String videoPath;
}

Stream Service Class

Core service managing FFmpeg processes via DefaultExecutor and ExecuteWatchdog. Uses ConcurrentHashMap to track active streams and manual stop flags.

Start Streaming ( startStream )

Validate video file exists.

Build RTMP URL: rtmp://{zlmHost}:{rtmpPort}/live/{streamKey}.

Construct FFmpeg command line: -re — read at native frame rate -i {videoPath} — input file -c:v libx264 — video codec -c:a aac — audio codec -f flv — output format -flvflags no_duration_filesize — FLV flags for streaming

Output to RTMP URL

Configure executor with infinite watchdog, output stream handler, and async ExecuteResultHandler to log completion/failure and clean up maps.

Store executor reference keyed by streamKey.

Stop Streaming ( stopStream )

Retrieve executor by streamKey.

Set manual stop flag.

Destroy process via watchdog.

Remove from maps and log result.

Utility Methods getPlayUrl(streamKey, protocol) — returns HTTP-FLV ( http://{host}:{httpPort}/live/{key}.live.flv) or HLS ( http://{host}:{httpPort}/live/{key}/hls.m3u8) playback URLs. isStreaming(streamKey) — checks if streamKey exists in active map.

Application Configuration (YAML)

stream:
  zlm-host: 192.168.159.129
  rtmp-port: 1935
  http-port: 8099
  ffmpeg-path: ffmpeg
  video-path: \videos\

spring:
  servlet:
    multipart:
      max-file-size: 1GB
      max-request-size: 1GB

Usage Instructions

Streaming Workflow

Start ZLMediaKit service.

Upload video file to server.

Call streaming endpoint with video path and stream key.

Spring Boot launches FFmpeg to push RTMP stream to ZLMediaKit.

Playback Workflow

Obtain playback URL (HTTP-FLV or HLS) via getPlayUrl.

Supports live playback and recorded playback (HLS).

Manual FFmpeg Test Command

ffmpeg -re -i "C:\Users\lyk19\Videos\8月9日.mp4" \
  -c:v libx264 -preset ultrafast -tune zerolatency \
  -c:a aac -ar 44100 -b:a 128k \
  -f flv rtmp://192.168.159.129:1935/live/stream

Frontend Player (flv.js)

HTML page loads flv.js from CDN, creates a <video> element, and attaches an FLV player instance with configuration:

flvPlayer = flvjs.createPlayer({
  type: 'flv',
  url: 'http://192.168.159.129:8099/live/stream.live.flv',
  isLive: true
}, {
  enableWorker: false,
  lazyLoad: true,
  lazyLoadMaxDuration: 3 * 60,
  deferLoadAfterSourceOpen: false,
  autoCleanupSourceBuffer: true,
  enableStashBuffer: false
});
flvPlayer.attachMediaElement(videoElement);
flvPlayer.load();

Event listeners handle errors, loading completion, recovery, and video element events (loadstart, canplay, playing, pause, error). UI controls for play, pause, stop, mute with status updates.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DockerSpring BootFFmpegVideo StreamingHLSRTMPflv.jsZLMediaKit
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.