Search code examples
videoffmpeggstreamer

Writing multi-stream MP4 file with Gstreamer


My Gstreamer 1.20 application is writing two H.264 video streams and one Opus audio stream to a MP4 file. The relevant parts of the pipeline looks as follows:

Video encoder (this part is present twice):

queue max-size-time=100000000 leaky=downstream ! x264enc bitrate=1500 speed-preset=ultrafast tune=zerolatency key-int-max=15 ! video/x-h264,profile=constrained-baseline,stream-format=avc ! queue ! mux.

Audio encoder:

queue max-size-time=100000000 leaky=downstream ! opusenc bitrate-type=vbr inband-fec=true ! queue ! opusparse ! mux.

Muxer and filesink:

mp4mux name=mux fragment-duration=1000 latency=100000000 ! filesink sync=true location=out.mp4

The result is an MP4 file which I can play just fine in Totem on Ubuntu, and even switch between video streams using "Switch angles".

However, none of the following work:

  • play it in VLC (plays a few seconds, then freezes)
  • do anything with FFMPEG, e.g. split the streams (results are garbage)
  • seek in the video (even in Totem)

In all cases, I get some messages about "Invalid NAL unit size" and/or "non-monotonous DTS". So what would I need to do to my output pipeline to get a "compliant" MP4 file?


Note: I can "fix" the resulting file by using GStreamer with qtdemux to split it into two raw H.264 files and a WAV file, and then use ffmpeg to re-encode it into MP4 with an MP3 audio track. But I'd rather have a workable MP4 file right away.


Solution

  • Your pipeline may lack video information from caps. You may check with:

    gst-launch-1.0 \
     videotestsrc ! video/x-raw,format=NV12,width=640,height=480,framerate=30/1 ! queue ! x264enc bitrate=1500 speed-preset=ultrafast tune=zerolatency key-int-max=15 insert-vui=1 ! video/x-h264,profile=constrained-baseline,stream-format=avc ! queue ! mux. \
     videotestsrc pattern=ball ! video/x-raw,format=NV12,width=640,height=480,framerate=30/1 ! queue ! x264enc bitrate=1500 speed-preset=ultrafast tune=zerolatency key-int-max=15 insert-vui=1 ! video/x-h264,profile=constrained-baseline,stream-format=avc ! queue ! mux. \
     audiotestsrc ! audioresample ! voaacenc ! queue ! mux. \
     qtmux name=mux fragment-duration=1000 ! queue ! filesink sync=true location=out.mp4
    
    # Or try mkv for opus:
    gst-launch-1.0 \
     videotestsrc ! queue ! x264enc bitrate=1500 speed-preset=ultrafast tune=zerolatency key-int-max=15 ! video/x-h264,profile=constrained-baseline,stream-format=avc ! queue ! mux. \
     videotestsrc pattern=ball ! queue ! x264enc bitrate=1500 speed-preset=ultrafast tune=zerolatency key-int-max=15 ! video/x-h264,profile=constrained-baseline,stream-format=avc ! queue ! mux. \
     audiotestsrc ! audioconvert ! opusenc ! queue ! mux.  \
     matroskamux name=mux ! queue ! filesink sync=true location=out.mkv
    

    Once recorded, you should be able to see the streams:

    # stream 0
    ffplay -autoexit out.mp4
    
    # stream 1
    ffplay -autoexit out.mp4 -vst v:1