X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fhlsenc.c;h=a9fa5d8aef164c5168aa4c28705f4070ac24b690;hb=6372c9dc9972318e75a5889ad6c52500b8294099;hp=7ab7cbb9ab1c6200ff761b32fe3d5de9baa253aa;hpb=cbe3f28d0ac7e102edf1dfd7733a303016a1b788;p=ffmpeg diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 7ab7cbb9ab1..a9fa5d8aef1 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -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 },