Generate single MPEG-Dash segment with ffmpeg

ffmpegmpeg-dashtranscodingvideo

I've been trying to implement a Plex-like video player that transcodes an arbitrary video file on-demand, and plays it with MPEG-Dash on a webpage. I was able to implement the client side player with the dash.js reference implementation, so it will dynamically request segments from the server (using SegmentTemplate in the mpd file).

But I'm having some problems generating these chunks in real-time. Ffmpeg lets me set -ss and -t to define the boundaries of the segment I need, but they don't play properly in the player because they're "full" video files rather than Dash segments.

So how do I adjust my ffmpeg command to transcode just the part I need as a Dash segment, without having to generate the segments for the entire video file in advance?

The input video file can be any format, so it cannot be assumed it's in an mp4/dash-compatible codec. So transcoding (with ffmpeg or similar tool) is required.

My current ffmpeg command looks like this (after lots of trying):

ffmpeg -ss 10 -t 5 -i video.mkv -f mp4 -c:a aac -c:v h264 -copyts -movflags empty_moov+frag_keyframe temp/segment.mp4

The client-side player should be able to buffer the next X segments, and the user should be able to view the current position on the duration bar and seek to a different position. So treating it as a live stream isn't an option.

Best Answer

It sounds like what you are describing is live streaming rather than VOD - live streams are continuous, usually real time video streams and VOD is typically a video file which is served when the user requests it.

The usual way VOD is done in larger solutions is to segment the video first and then to package it on demand into the required streaming protocol, usually HLS or DASH at this time. This allows an operator minimise the different formats they need to maintain.

The emerging CMAF standard helps support this by using the same format for the segments for both HLS and DASH. If you search for 'CMAF' you will see many explanations of the history and the official page is here also: https://www.iso.org/standard/71975.html

Open source tools exist to help you convert an MP4 file straight into DASH - MP4Box is one of the most common ones: https://gpac.wp.imt.fr/mp4box/dash/

ffmpeg also includes information in the documentation to support VOD: https://www.ffmpeg.org/ffmpeg-formats.html#dash-2 including an example:

ffmpeg -re -i <input> -map 0 -map 0 -c:a libfdk_aac -c:v libx264 \
-b:v:0 800k -b:v:1 300k -s:v:1 320x170 -profile:v:1 baseline \
-profile:v:0 main -bf 1 -keyint_min 120 -g 120 -sc_threshold 0 \
-b_strategy 0 -ar:a:1 22050 -use_timeline 1 -use_template 1 \
-window_size 5 -adaptation_sets "id=0,streams=v id=1,streams=a" \
-f dash /path/to/out.mpd

If it is actually a live stream you are looking at then the input is typically not an MP4 file but a stream in some format like HLS, RTMP, MPEG-TS etc.

Taking an input in this format and providing a live profile DASH output is more complicated. Generally a dedicated packager is used to do this. The open source Shaka Packager (https://github.com/google/shaka-player) would be a good place to look and it includes examples to produce DASH live output:

Assuming you want to allow the user watch while the video file is being generated then one way to do this is to make the stream look like a live stream, i.e. a 'VOD to Live' case.

You can use restreaming in Ffmpeg to transcode and stream to UDP and then feed that into a packager.

The ffmpeg documentation includes this note:

-re (input) Read input at native frame rate. Mainly used to simulate a grab device, or live input stream (e.g. when reading from a file). Should not be used with actual grab devices or live input streams (where it can cause packet loss). By default ffmpeg attempts to read the input(s) as fast as possible. This option will slow down the reading of the input(s) to the native frame rate of the input(s). It is useful for real-time output (e.g. live streaming).

This give you a flow that looks like:

mp4 file -> ffmpeg -> packager -> live DASH stream -> client

Using a packager to do this means you don't have to worry about updating the manifest when new segments are available or older ones not available.

There is an example here on the Wowza packager site (at the time of writing) which you could look at and experiment with, substituting you now files or using theirs - the output should work with any packager that can accept a UDP input stream: https://www.wowza.com/docs/how-to-restream-using-ffmpeg-with-wowza-streaming-engine

Related Topic