11

Recently bought an ip-cam which outputs a RTSP stream. I'm using the segment option of FFmpeg to create 60 minute long recordings.

I want FFmpeg to write the files to a directory based on Year/Month/Date, and write to a file Hour-Minute.mp4 For example: /raid1/homes/share/public/recordings/queue/bedroom/2017/04/23/13-05.mp4 for a recording started on 23 april 2017, 13:05.

Unfortunately FFmpeg seems to not create the directory hierarchy. FFmpeg quits since the directory can not be found.

Input #0, rtsp, from 'rtsp://192.168.1.240/unicast':
  Metadata:
    title           : LIVE555 Streaming Media v2014.07.04
    comment         : LIVE555 Streaming Media v2014.07.04
  Duration: N/A, start: 0.000750, bitrate: N/A
    Stream #0:0: Video: h264 (High), yuv420p, 1920x1080, 90k tbr, 90k tbn, 180k tbc
    Stream #0:1: Audio: pcm_alaw, 8000 Hz, 1 channels, s16, 64 kb/s
[segment @ 0x2557300] Using AVStream.codec to pass codec parameters to muxers is deprecated, use AVStream.codecpar instead.
[segment @ 0x2557300] Failed to open segment '/raid1/homes/share/public/recordings/queue/bedroom/2017/04/23/14-19.mp4'
Output #0, segment, to '/raid1/homes/share/public/recordings/queue/bedroom/%Y/%m/%d/%H-%M.mp4':
  Metadata:
    title           : LIVE555 Streaming Media v2014.07.04
    comment         : LIVE555 Streaming Media v2014.07.04
    encoder         : Lavf57.41.100
    Stream #0:0: Video: h264, yuv420p, 1920x1080, q=2-31, 90k tbr, 90k tbn, 90k tbc
Stream mapping:
  Stream #0:0 -> #0:0 (copy)
Could not write header for output file #0 (incorrect codec parameters ?): No such file or directory

record.sh is as follows:

#!/bin/sh
ffmpeg -stimeout 600\
 -rtsp_transport udp \
 -i rtsp://192.168.1.240/unicast \
 -c copy \
 -map 0:0 \
 -f segment \
 -segment_time 3600 \
 -segment_wrap 100 \
 -segment_format mov \
 -strftime 1 \
 -reset_timestamps 1 \
 "/raid1/homes/share/public/recordings/queue/bedroom/%Y/%m/%d/%H-%M.mp4"

I've tried not using a directory hierachy: "/raid1/homes/share/public/recordings/queue/bedroom/%Y-%m-%d_%H-%M.mp4". This works fine.

$ ffmpeg -version
ffmpeg version N-80901-gfebc862 Copyright (c) 2000-2016 the FFmpeg developers
built with gcc 4.8 (Ubuntu 4.8.4-2ubuntu1~14.04.3)
configuration: --extra-libs=-ldl --prefix=/opt/ffmpeg --mandir=/usr/share/man --enable-avresample --disable-debug --enable-nonfree --enable-gpl --enable-version3 --enable-libopencore-amrnb --enable-libopencore-amrwb --disable-decoder=amrnb --disable-decoder=amrwb --enable-libpulse --enable-libfreetype --enable-gnutls --enable-libx264 --enable-libx265 --enable-libfdk-aac --enable-libvorbis --enable-libmp3lame --enable-libopus --enable-libvpx --enable-libspeex --enable-libass --enable-avisynth --enable-libsoxr --enable-libxvid --enable-libvidstab
libavutil      55. 28.100 / 55. 28.100
libavcodec     57. 48.101 / 57. 48.101
libavformat    57. 41.100 / 57. 41.100
libavdevice    57.  0.102 / 57.  0.102
libavfilter     6. 47.100 /  6. 47.100
libavresample   3.  0.  0 /  3.  0.  0
libswscale      4.  1.100 /  4.  1.100
libswresample   2.  1.100 /  2.  1.100
libpostproc    54.  0.100 / 54.  0.100

Can FFmpeg create output directories on the go?

David
  • 131
  • 1
  • 1
  • 8

1 Answers1

7

Nope, FFmpeg can't do that

We can add a few lines to the top of your record.sh script to create the directory for you, though.

#!/bin/sh

BASEDIRECTORY = /raid1/homes/share/public/recordings/queue/bedroom

YEAR=$(date +"%Y");
MONTH=$(date +"%m");
DAY=$(date +"%d");

# Create a directory tree for the day 
# using the current year, month, and day
mkdir -p $BASEDIRECTORY/$YEAR/$MONTH/$DAY

# Start FFmpeg hourly recordings
ffmpeg -stimeout 600\
 -rtsp_transport udp \
 -i rtsp://192.168.1.240/unicast \
 -c copy \
 -map 0:0 \
 -f segment \
 -segment_time 3600 \
 -segment_wrap 100 \
 -segment_format mov \
 -strftime 1 \
 -reset_timestamps 1 \
 "$BASEDIRECTORY/$YEAR/$MONTH/$DAY/%H-%M.mp4"

Now we just need record.sh to restart overnight to create a new folder for the $DAY. That sounds like a perfect job for cron: How to write a cron that will run a script every day at midnight?

Note: this script now requires root privilege so if you're getting the error below and cron isn't working, make sure you're typing sudo ./record.sh while testing and install your crontab as the root user like this: sudo crotab -u root -e, as @dashesy points out in his comment.

This is because mkdir with the -p or --parent option requires root privilege and if the directory isn't there FFmpeg will throw this error:

Failed to open segment '/raid1/homes/share/public/recordings/queue/bedroom/2019/01/31/10-37.mp4'
Could not write header for output file #0 (incorrect codec parameters ?): No such file or directory
Stephen
  • 79
  • 1
  • 4
  • 3
    Update: for HLS use `-strftime 1` with `-strftime_mkdir 1` and `-hls_segment_filename '%Y/%m/%d/%H-%M.ts'`. FFmpeg will create the directory structure. See the FFmpeg documentation here: https://ffmpeg.org/ffmpeg-formats.html#Options-5 – Stephen Sep 03 '19 at 22:15
  • HLS? Whats that? Can it do it or not? – john k Nov 03 '19 at 18:28
  • 1
    None of this actually requires root, these errors are just because the script was run as root (either in the root user's crontab or run with sudo), so it created directories while running under the root user (therefore owned by root). As long as the script is added to a normal user's crontab and the directories are owned by that user, you can run this script as that user (without root/sudo). – Travis Britz Nov 06 '19 at 16:46
  • I've proposed a patch for ffmpeg image2 muxer to allow it to work in a similar way to how hls `-strftime_mkdir 1` work. The thread (and proposed patch) is here: http://ffmpeg.org/pipermail/ffmpeg-devel/2023-August/312866.html After this patch is approved, one can think of proposing the same parameter to be implemented for `segment`. – Alexandre Schmidt Aug 01 '23 at 20:33