[FFmpeg-devel] [PATCH] Add mkdir support for HLS localtime-generated segment files

Johan Ström johan at stromnet.se
Wed Feb 3 22:44:28 CET 2016


Hi,

this patch adds -use_localtime_mkdir option to the HLS encoder.
Use with -use_localtime, and set -hls_segment_filename to a path which 
contains a subdirectory i.e. /some/path/%Y%m%d/%Y%m%dT%H%M%S-%s.ts
This will mkdir the %Y%m%d-part of the path if it does not already exist.

In addition, each filename in the playlist output will be prefixed with 
this subdirectory (if playlist and segment shares the same base path).

Example:

$ ffmpeg .... -f hls -use_localtime 1 -use_localtime_mkdir 1 
-hls_segment_filename /dvr/test/%Y%m%d/%Y%m%dT%H%M%S-%s.ts 
/dvr/test/playlist.m3u8

This will create put segments created (today) in /dvr/test/20160203/, 
and the 20160203 dir will be automatically created if it does not 
already exist.
The m3u8 playlist will contain segment paths like this:

20160203/20160203T224121-1454535681.ts

The subdir support could possibly be enabled outside of the 
localtime/mkdir-scope.


Regards
Johan

-------------- next part --------------
From 40327f78d70b3f1b97b222de5adf70020c466db9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Johan=20Str=C3=B6m?= <johan at stromnet.se>
Date: Wed, 3 Feb 2016 22:20:07 +0100
Subject: [PATCH] hlsenc: add use_localtime_mkdir option to automatically
 create time-based directory

Use with -use_localtime, and set -hls_segment_filename to a path which
contains a subdirectory i.e. /some/path/%Y%m%d/%Y%m%dT%H%M%S-%s.ts
This will mkdir the %Y%m%d-part of the path if it does not already
exist.
In addition, each filename in the playlist output will be prefixed with
this subdirectory (if playlist and segment shares the same base path).
---
 libavformat/hlsenc.c | 46 ++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 40 insertions(+), 6 deletions(-)

diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
index bc28e3c..073618d 100644
--- a/libavformat/hlsenc.c
+++ b/libavformat/hlsenc.c
@@ -82,6 +82,7 @@ typedef struct HLSContext {
     char *segment_filename;
 
     int use_localtime;      ///< flag to expand filename with localtime
+    int use_localtime_mkdir;///< flag to mkdir dirname in timebased filename
     int allowcache;
     int64_t recording_time;
     int has_video;
@@ -303,16 +304,33 @@ static int hls_mux_init(AVFormatContext *s)
 }
 
 /* Create a new segment and append it to the segment list */
-static int hls_append_segment(HLSContext *hls, double duration, int64_t pos,
-                              int64_t size)
+static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, double duration,
+                              int64_t pos, int64_t size)
 {
     HLSSegment *en = av_malloc(sizeof(*en));
+    char *tmp, *p;
+    const char *pl_dir, *filename;
     int ret;
 
     if (!en)
         return AVERROR(ENOMEM);
 
-    av_strlcpy(en->filename, av_basename(hls->avf->filename), sizeof(en->filename));
+    filename = av_basename(hls->avf->filename);
+
+    if (hls->use_localtime_mkdir) {
+        /* Possibly prefix with mkdir'ed subdir, if playlist share same
+         * base path. */
+        tmp = av_strdup(s->filename);
+        if (!tmp)
+            return AVERROR(ENOMEM);
+
+        pl_dir = av_dirname(tmp);
+        p = hls->avf->filename;
+        if (strstr(p, pl_dir) == p)
+            filename = hls->avf->filename + strlen(pl_dir) + 1;
+        av_free(tmp);
+    }
+    av_strlcpy(en->filename, filename, sizeof(en->filename));
 
     if(hls->has_subtitle)
         av_strlcpy(en->sub_filename, av_basename(hls->vtt_avf->filename), sizeof(en->sub_filename));
@@ -509,6 +527,21 @@ static int hls_start(AVFormatContext *s)
                 av_log(oc, AV_LOG_ERROR, "Could not get segment filename with use_localtime\n");
                 return AVERROR(EINVAL);
             }
+
+            if (c->use_localtime_mkdir) {
+                const char *dir;
+                char *fn_copy = av_strdup(oc->filename);
+                if (!fn_copy) {
+                    return AVERROR(ENOMEM);
+                }
+                dir = av_dirname(fn_copy);
+                if (mkdir(dir, 0777) == -1 && errno != EEXIST) {
+                    av_log(oc, AV_LOG_ERROR, "Could not create directory %s with use_localtime_mkdir\n", dir);
+                    av_free(fn_copy);
+                    return AVERROR(errno);
+                }
+                av_free(fn_copy);
+            }
        } else if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
                                   c->basename, c->wrap ? c->sequence % c->wrap : c->sequence) < 0) {
             av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s' you can try use -use_localtime 1 with it\n", c->basename);
@@ -782,7 +815,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
 
         new_start_pos = avio_tell(hls->avf->pb);
         hls->size = new_start_pos - hls->start_pos;
-        ret = hls_append_segment(hls, hls->duration, hls->start_pos, hls->size);
+        ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size);
         hls->start_pos = new_start_pos;
         if (ret < 0)
             return ret;
@@ -829,7 +862,7 @@ static int hls_write_trailer(struct AVFormatContext *s)
     if (oc->pb) {
         hls->size = avio_tell(hls->avf->pb) - hls->start_pos;
         avio_closep(&oc->pb);
-        hls_append_segment(hls, hls->duration, hls->start_pos, hls->size);
+        hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size);
     }
 
     if (vtt_oc) {
@@ -875,7 +908,8 @@ static const AVOption options[] = {
     {"round_durations", "round durations in m3u8 to whole numbers", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_ROUND_DURATIONS }, 0, UINT_MAX,   E, "flags"},
     {"discont_start", "start the playlist with a discontinuity tag", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_DISCONT_START }, 0, UINT_MAX,   E, "flags"},
     {"omit_endlist", "Do not append an endlist when ending stream", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_OMIT_ENDLIST }, 0, UINT_MAX,   E, "flags"},
-    { "use_localtime", "set filename expansion with strftime at segment creation", OFFSET(use_localtime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
+    {"use_localtime", "set filename expansion with strftime at segment creation", OFFSET(use_localtime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
+    {"use_localtime_mkdir", "create last directory component in strftime-generated filename", OFFSET(use_localtime_mkdir), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
     {"method", "set the HTTP method", OFFSET(method), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,    E},
 
     { NULL },
-- 
2.7.0



More information about the ffmpeg-devel mailing list