[FFmpeg-devel] segment: Create missing directories

Submitted by Julien Gaulmin on Sept. 6, 2018, 3:02 p.m.

Details

Message ID d94bb4cd-5be8-e848-5cf2-47891adf5c6a@4g-technology.eu
State New
Headers show

Commit Message

Julien Gaulmin Sept. 6, 2018, 3:02 p.m.
I find this patch very useful. Here is a version against master as
recommended by Ricardo.

Works well for me. Thanks Georgi.

Le 20/02/2018 à 12:17, Georgi Chorbadzhiyski a écrit :
> The attached patch allows segment muxer to be used for file archiving by
> allowing it to automatically create the output directories. For example the
> following should work as expected:
> 
> ffmpeg
>   ...input_params...
>   -f segment \
>      -segment_atclocktime 1 \
>      -segment_time 5 \
>      -write_empty_segments 1 \
>      -segment_format_options movflags=+faststart \
>      -strftime 1 output_directory/mychannel/%Y/%m/%d/%H/%M/mychannel-%s-%Y%m%d-%H%M%S.mp4
> 
> The patch is against ffmpeg-3.3.6

Comments

Michael Niedermayer Sept. 7, 2018, 9:46 p.m.
On Thu, Sep 06, 2018 at 05:02:28PM +0200, Julien Gaulmin wrote:
> I find this patch very useful. Here is a version against master as
> recommended by Ricardo.
> 
> Works well for me. Thanks Georgi.
> 
> Le 20/02/2018 à 12:17, Georgi Chorbadzhiyski a écrit :
> > The attached patch allows segment muxer to be used for file archiving by
> > allowing it to automatically create the output directories. For example the
> > following should work as expected:
> > 
> > ffmpeg
> >   ...input_params...
> >   -f segment \
> >      -segment_atclocktime 1 \
> >      -segment_time 5 \
> >      -write_empty_segments 1 \
> >      -segment_format_options movflags=+faststart \
> >      -strftime 1 output_directory/mychannel/%Y/%m/%d/%H/%M/mychannel-%s-%Y%m%d-%H%M%S.mp4
> > 
> > The patch is against ffmpeg-3.3.6
> 
> -- 
> Julien GAULMIN <julien.gaulmin@4g-technology.eu>

>  segment.c |   54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 54 insertions(+)
> 1e5970ff8eafbb989ad29b77cd88fd0e4a259df1  segment_create_dir.master.patch
> diff --git a/libavformat/segment.c b/libavformat/segment.c
> index 7fb4dc7d21..f6b9ea8b89 100644
> --- a/libavformat/segment.c
> +++ b/libavformat/segment.c

fails to build on mingw32
CC	libavformat/segment.o
src/libavformat/segment.c: In function ‘create_dir’:
src/libavformat/segment.c:250:18: error: expected identifier or ‘*’ before ‘;’ token
             goto OUT;
                  ^
src/libavformat/segment.c:253:1: error: expected expression before ‘:’ token
 OUT:
 ^
src/libavformat/segment.c:255:1: error: control reaches end of non-void function [-Werror=return-type]
 }
 ^
cc1: some warnings being treated as errors
make: *** [libavformat/segment.o] Error 1
make: Target `all' not remade because of errors.

[...]
Marton Balint Sept. 7, 2018, 10:13 p.m.
On Thu, 6 Sep 2018, Julien Gaulmin wrote:

> I find this patch very useful. Here is a version against master as
> recommended by Ricardo.
>
> Works well for me. Thanks Georgi.
>
> Le 20/02/2018 à 12:17, Georgi Chorbadzhiyski a écrit :
>> The attached patch allows segment muxer to be used for file archiving by
>> allowing it to automatically create the output directories. For example the
>> following should work as expected:
>>
>> ffmpeg
>>   ...input_params...
>>   -f segment \
>>      -segment_atclocktime 1 \
>>      -segment_time 5 \
>>      -write_empty_segments 1 \
>>      -segment_format_options movflags=+faststart \
>>      -strftime 1 output_directory/mychannel/%Y/%m/%d/%H/%M/mychannel-%s-%Y%m%d-%H%M%S.mp4
>>
>> The patch is against ffmpeg-3.3.6

hlsenc.c has similar functionality, reuse and factorize code from there, 
and use a same option name to enable the functionality in the segment muxer.

Regards,
Marton

Patch hide | download patch | download mbox

diff --git a/libavformat/segment.c b/libavformat/segment.c
index 7fb4dc7d21..f6b9ea8b89 100644
--- a/libavformat/segment.c
+++ b/libavformat/segment.c
@@ -26,6 +26,7 @@ 
 
 #include <float.h>
 #include <time.h>
+#include <unistd.h>
 
 #include "avformat.h"
 #include "avio_internal.h"
@@ -230,6 +231,49 @@  static int set_segment_filename(AVFormatContext *s)
     return 0;
 }
 
+/* Note: This modifies *dir */
+static int create_dir(char *dir, mode_t mode) {
+    int ret = 0;
+    unsigned int i, dlen;
+    /* Shortcut, there are no deep directories */
+    if (strchr(dir, '/') == NULL)
+        return mkdir(dir, mode);
+    dlen = strlen(dir);
+    /* Skip first char (it can be /) */
+    for (i = 1; i < dlen; i++) {
+        if (dir[i] != '/')
+            continue;
+        dir[i] = '\0';
+        ret = mkdir(dir, mode);
+        dir[i] = '/';
+        if (ret < 0 && errno != EEXIST)
+            goto OUT;
+    }
+    ret = mkdir(dir, mode);
+OUT:
+    return ret;
+}
+
+static int segment_create_directory(AVFormatContext *oc, char *filename) {
+    char *dir, *fname;
+    /* Do nothing when the filename is URL */
+    if (strstr(filename, "://") != NULL)
+        return 0;
+    fname = av_strdup(filename);
+    if (!fname)
+        return AVERROR(ENOMEM);
+    dir = (char *)av_dirname(fname);
+    if (access(dir, W_OK) != F_OK)
+        av_log(oc, AV_LOG_INFO, "Create directory %s\n", dir);
+    if (create_dir(dir, 0777) == -1 && errno != EEXIST) {
+        av_log(oc, AV_LOG_ERROR, "Could not create directory %s\n", dir);
+        av_free(fname);
+        return AVERROR(errno);
+    }
+    av_free(fname);
+    return 0;
+}
+
 static int segment_start(AVFormatContext *s, int write_header)
 {
     SegmentContext *seg = s->priv_data;
@@ -251,6 +295,9 @@  static int segment_start(AVFormatContext *s, int write_header)
     if ((err = set_segment_filename(s)) < 0)
         return err;
 
+    if ((err = segment_create_directory(s, oc->url)) < 0)
+        return err;
+
     if ((err = s->io_open(s, &oc->pb, oc->url, AVIO_FLAG_WRITE, NULL)) < 0) {
         av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", oc->url);
         return err;
@@ -281,6 +328,10 @@  static int segment_list_open(AVFormatContext *s)
     int ret;
 
     snprintf(seg->temp_list_filename, sizeof(seg->temp_list_filename), seg->use_rename ? "%s.tmp" : "%s", seg->list);
+
+    if ((ret = segment_create_directory(s, seg->temp_list_filename)) < 0)
+        return ret;
+
     ret = s->io_open(s, &seg->list_pb, seg->temp_list_filename, AVIO_FLAG_WRITE, NULL);
     if (ret < 0) {
         av_log(s, AV_LOG_ERROR, "Failed to open segment list '%s'\n", seg->list);
@@ -750,6 +801,9 @@  static int seg_init(AVFormatContext *s)
     oc = seg->avf;
 
     if (seg->write_header_trailer) {
+        if ((ret = segment_create_directory(s, seg->header_filename ? seg->header_filename : oc->url)) < 0)
+            return ret;
+
         if ((ret = s->io_open(s, &oc->pb,
                               seg->header_filename ? seg->header_filename : oc->url,
                               AVIO_FLAG_WRITE, NULL)) < 0) {