Message ID | d94bb4cd-5be8-e848-5cf2-47891adf5c6a@4g-technology.eu |
---|---|
State | New |
Headers | show |
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. [...]
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
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) {