]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/hlsenc.c
Merge commit '5b9cdf8cba114c41239bf0f9f5e0ccb6977d1c8d'
[ffmpeg] / libavformat / hlsenc.c
index 7ab7cbb9ab1c6200ff761b32fe3d5de9baa253aa..a9fa5d8aef164c5168aa4c28705f4070ac24b690 100644 (file)
@@ -64,6 +64,13 @@ typedef enum HLSFlags {
     HLS_OMIT_ENDLIST = (1 << 4),
 } HLSFlags;
 
+typedef enum {
+    PLAYLIST_TYPE_NONE,
+    PLAYLIST_TYPE_EVENT,
+    PLAYLIST_TYPE_VOD,
+    PLAYLIST_TYPE_NB,
+} PlaylistType;
+
 typedef struct HLSContext {
     const AVClass *class;  // Class for private options.
     unsigned number;
@@ -79,9 +86,11 @@ typedef struct HLSContext {
     int max_nb_segments;   // Set by a private option.
     int  wrap;             // Set by a private option.
     uint32_t flags;        // enum HLSFlags
+    uint32_t pl_type;      // enum PlaylistType
     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;
@@ -287,14 +296,14 @@ static int hls_mux_init(AVFormatContext *s)
     for (i = 0; i < s->nb_streams; i++) {
         AVStream *st;
         AVFormatContext *loc;
-        if (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_SUBTITLE)
+        if (s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
             loc = vtt_oc;
         else
             loc = oc;
 
         if (!(st = avformat_new_stream(loc, NULL)))
             return AVERROR(ENOMEM);
-        avcodec_copy_context(st->codec, s->streams[i]->codec);
+        avcodec_parameters_copy(st->codecpar, s->streams[i]->codecpar);
         st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
         st->time_base = s->streams[i]->time_base;
     }
@@ -304,16 +313,35 @@ 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) {
+            av_free(en);
+            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));
@@ -337,6 +365,10 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos,
 
     hls->last_segment = en;
 
+    // EVENT or VOD playlists imply sliding window cannot be used
+    if (hls->pl_type != PLAYLIST_TYPE_NONE)
+        hls->max_nb_segments = 0;
+
     if (hls->max_nb_segments && hls->nb_entries >= hls->max_nb_segments) {
         en = hls->segments;
         hls->segments = en->next;
@@ -396,7 +428,7 @@ static int hls_window(AVFormatContext *s, int last)
 
     set_http_options(&options, hls);
     snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", s->filename);
-    if ((ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, NULL)) < 0)
+    if ((ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, &options)) < 0)
         goto fail;
 
     for (en = hls->segments; en; en = en->next) {
@@ -412,6 +444,11 @@ static int hls_window(AVFormatContext *s, int last)
     }
     avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration);
     avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
+    if (hls->pl_type == PLAYLIST_TYPE_EVENT) {
+        avio_printf(out, "#EXT-X-PLAYLIST-TYPE:EVENT\n");
+    } else if (hls->pl_type == PLAYLIST_TYPE_VOD) {
+        avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n");
+    }
 
     av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n",
            sequence);
@@ -508,7 +545,22 @@ static int hls_start(AVFormatContext *s)
                 av_log(oc, AV_LOG_ERROR, "Could not get segment filename with use_localtime\n");
                 return AVERROR(EINVAL);
             }
-       } else if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
+
+            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);
             return AVERROR(EINVAL);
@@ -607,9 +659,9 @@ static int hls_write_header(AVFormatContext *s)
 
     for (i = 0; i < s->nb_streams; i++) {
         hls->has_video +=
-            s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO;
+            s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
         hls->has_subtitle +=
-            s->streams[i]->codec->codec_type == AVMEDIA_TYPE_SUBTITLE;
+            s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE;
     }
 
     if (hls->has_video > 1)
@@ -711,7 +763,7 @@ static int hls_write_header(AVFormatContext *s)
     for (i = 0; i < s->nb_streams; i++) {
         AVStream *inner_st;
         AVStream *outer_st = s->streams[i];
-        if (outer_st->codec->codec_type != AVMEDIA_TYPE_SUBTITLE)
+        if (outer_st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
             inner_st = hls->avf->streams[i];
         else if (hls->vtt_avf)
             inner_st = hls->vtt_avf->streams[0];
@@ -747,7 +799,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
     int ret, can_split = 1;
     int stream_index = 0;
 
-    if( st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE ) {
+    if( st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE ) {
         oc = hls->vtt_avf;
         stream_index = 0;
     } else {
@@ -760,9 +812,9 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
     }
 
     if (hls->has_video) {
-        can_split = st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
+        can_split = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
                     pkt->flags & AV_PKT_FLAG_KEY;
-        is_ref_pkt = st->codec->codec_type == AVMEDIA_TYPE_VIDEO;
+        is_ref_pkt = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
     }
     if (pkt->pts == AV_NOPTS_VALUE)
         is_ref_pkt = can_split = 0;
@@ -778,7 +830,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;
@@ -801,7 +853,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
         if (ret < 0)
             return ret;
 
-        if( st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE )
+        if( st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE )
             oc = hls->vtt_avf;
         else
         oc = hls->avf;
@@ -825,7 +877,7 @@ static int hls_write_trailer(struct AVFormatContext *s)
     if (oc->pb) {
         hls->size = avio_tell(hls->avf->pb) - hls->start_pos;
         ff_format_io_close(s, &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) {
@@ -871,7 +923,11 @@ 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 },
+    {"hls_playlist_type", "set the HLS playlist type", OFFSET(pl_type), AV_OPT_TYPE_INT, {.i64 = PLAYLIST_TYPE_NONE }, 0, PLAYLIST_TYPE_NB-1, E, "pl_type" },
+    {"event", "EVENT playlist", 0, AV_OPT_TYPE_CONST, {.i64 = PLAYLIST_TYPE_EVENT }, INT_MIN, INT_MAX, E, "pl_type" },
+    {"vod", "VOD playlist", 0, AV_OPT_TYPE_CONST, {.i64 = PLAYLIST_TYPE_VOD }, INT_MIN, INT_MAX, E, "pl_type" },
     {"method", "set the HTTP method", OFFSET(method), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,    E},
 
     { NULL },