Sunday, October 6, 2024

Create CMAF/MPEG-DASH/HLS streams with directory of jpegs

I wanted to support CMAF to package and support HTTP streams.  It supports both MPEG-DASH and HLS protocols.  

Environment:

Ubuntu 20.04.6 LTS

Nginx version: nginx/1.27.1

ffmpeg version using apt-get: apt-get install -y libx264-dev=2:0.155.2917+git0a84d98-2 && apt-get install -y ffmpeg=7:4.2.7-0ubuntu0.1

bento4 version using wget: https://www.bok.net/Bento4/binaries/Bento4-SDK-1-6-0-634.x86_64-unknown-linux.zip.  I unzipped and moved the /bin and /utils to /usr/local/bin/bento4

Use ffmpeg to create a mp4 using a directory of jpegs

The jpeg files had seconds and nanoseconds in the filename (e.g. 1717013439117358336.jpeg - 1717013439 seconds 117358336 nanoseconds) and had 1440x1080 pixels resolution.

ffmpeg -y -framerate 30 -pattern_type glob -i "/home/cherryshoe/processed/feed/*.jpeg" -s:v 1440x1080 -c:v libx264 -crf 18 -pix_fmt yuv420p videos/feed.mp4

  • -y: overrides output files without asking
  • -framerate: is calculated by the number of files in the directory divided by the number of seconds covered by the available jpeg files.
    • Frame rate can be calculated with the available files in the time period span.  Or you can inject as many frames as you want to get the desired framerate (via code to duplicate jpeg files), which is what I did to get 30 frames per second.
  • -pattern_type glob: in conjunction with -i option will read a list of input files
  • -s:v specifies width x height of output
  • -c:v libx264 specifies libx264 video encoding
  • -crf 18 is the constant rate factor: For x264 the valid range is 0-51 (default is 23). Consider 18 to be visually lossless or nearly so
  • -pxt_fmt yuv420p is the input pixel format
Bento4

I chose bento4 since it's a mp4 toolkit to support MP4 and DASH/HLS/CMAF media format needs.

1. Created fragmented mp4s with mp4fragment utility:

mp4fragment feed1.mp4 feed1-frag.mp4

2.  Use mp4dash to creates a MPEG DASH and HLS output from one or more fragmented MP4 files

mp4dash --hls --use-segment-timeline --force --mpd-name=master.mpd --output-dir ~/TEMP/output feed1-frag.mp4 

  • --hls to also support HLS (creates m3u8 files)
  • --use-segment-timeline (necessary if segment durations vary)
  • --force allows output to an existing directory
  • --mpd-name=master.mpd (match playlist filename of HLS m3u8)
  • --output-dir is where you want the output files to be saved to

This creates an output format like:

- master.m3u8

- master.mpd

  - video

    - avc1

      - iframes.m3u8

        - init.mp4

        - media.m3u8

        - seg-*.m4s

3. Copy the output recursively to a folder where you want to store stream files, e.g. /mnt/streams/feed1

sudo cp -r ~/TEMP/output/* /mnt/streams/feed1

4.  Configure nginx.  The following can be added to the existing nginx.conf file after standard installation.  No extra modules need to be installed other than the standard installation.

Since we want /mnt/streams to be the root folder of any HTTP based streaming streams, the location is configured with "/streams" and the root is configuerd as "/mnt".

# CMAF streaming configuration
server {
    listen 8080;
    sendfile off;
    tcp_nopush on;
    aio on;
    directio 512;

    location /streams {
        # Disable cache
        add_header 'Cache-Control' 'no-cache';

        # CORS setup
        add_header 'Access-Control-Allow-Origin' '*' always;
        add_header 'Access-Control-Expose-Headers' 'Content-Length';

        # allow CORS preflight requests
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Max-Age' 1728000;
            add_header 'Content-Type' 'text/plain charset=UTF-8';
            add_header 'Content-Length' 0;
            return 204;
        }

        types {
            application/dash+xml mpd; # support DASH manifest
            application/vnd.apple.mpegurl m3u8; # support HLS manifest
            video/mp4 m4s; # support DASH video chunks
        }

        root /mnt/;
    }
}

5.  HTML sample code using video.js player - https://videojs.com/

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Live streaming</title>
    <link href="https://vjs.zencdn.net/8.12.0/video-js.css" rel="stylesheet" />
    <script defer src="https://vjs.zencdn.net/8.12.0/video.min.js"></script>
  </head>
  <body>
    <table>
      <tr>
        <td>
            HLS<br>
            <video
              id="video-1"
              class="video-js"
              preload="auto"
              width="400"
              controls
              data-setup="{}"
            >
              <source
                src="http://localhost/streams/feed1/master.m3u8"
                type="application/x-mpegURL"
              />
            </video>
        </td>
      </tr>
      <tr>
        <td>
            DASH<br>
            <video
              id="video-2"
              class="video-js"
              preload="auto"
              width="400"
              controls
              data-setup="{}"
            >
              <source
                src="http://localhost/streams/feed1/master.mpd"
                type="application/dash+xml"
              />
            </video>
        </td>
      </tr>
    </table>
  </body>
</html>

No comments:

Post a Comment

I appreciate your time in leaving a comment!