Weird Color & video when converting from NV12 to mp4 using x264enc

Hello, I’m trying to save the camera output I get from my device to a mp4 file. I’m using a Jetson Orin Nano device with a Rpi HQ camera. I cannot use nvidias hardware encoders since theyre not supported.

So far I’ve tried to do it like this:

gst_args = (
    "gst-launch-1.0",
    "nvarguscamerasrc",
    "!",
    "video/x-raw(memory:NVMM), width=1280, height=720, framerate=20/1, format=NV12",
    "!",
    "nvvidconv",
    "!",
    "video/x-raw, format=I420",
    "!",
    "x264enc",
    "!",
    "mp4mux",
    "!",
    "filesink",
    f"location={filename}",
)

even though this runs without any errors, I cannot playback the video I recorded, and it tells me that the video is corrupt. I believe I’m missing a small point and forgot to convert something to a different format but I cant seem to find it, appreciate any help, thanks!

Piping the output to ffmpeg and encoding from there kinda works, now the colors & the general video is broken though.

Here’s the command I used:

gst_args = (
    "gst-launch-1.0",
    "nvarguscamerasrc",
    "!",
    "video/x-raw(memory:NVMM), width=1280, height=720, framerate=20/1, format=NV12",
    "!",
    "nvvidconv",
    "!",
    "video/x-raw, format=I420",
    "!",
    "videoconvert",
    "!",
    "filesink",
    "location=/dev/stdout",
    "sync=false",
)


ffmpeg_args = (
    "ffmpeg",
    "-f",
    "rawvideo",
    "-pix_fmt",
    "yuv420p",
    "-s",
    "1280x720",
    "-r",
    "20",
    "-i",
    "-",
    "-c:v",
    "libx264",
    "-preset",
    "veryfast",
    "-crf",
    f"{quality}",
    filename,
)

If you are generating the mp4 file using gst-launch, don’t forget to add “-e”, then when you want to stop, send a SIGINT and wait for the process to exit. Otherwise, the file header will not get written properly.

If you are writing a program, I’d advise you to use the GStreamer API instead of calling gst-launch, this way you can get better error handling. You can still create the pipeline object with gst_parse_launch(), but then use the APIs to start it, and stop it.