diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index f98f04100c..5cf8c89ee7 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -531,6 +531,7 @@ static int hls_mux_init(AVFormatContext *s) HLSContext *hls = s->priv_data; AVFormatContext *oc; AVFormatContext *vtt_oc = NULL; + int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0); int i, ret; ret = avformat_alloc_output_context2(&hls->avf, hls->oformat, NULL, NULL); @@ -584,7 +585,11 @@ static int hls_mux_init(AVFormatContext *s) hls->fmp4_init_mode = 0; if (hls->segment_type == SEGMENT_TYPE_FMP4) { - hls->fmp4_init_mode = 1; + if (hls->max_seg_size > 0) { + av_log(s, AV_LOG_WARNING, "Multi-file byterange mode is currently unsupported in the HLS muxer.\n"); + return AVERROR_PATCHWELCOME; + } + hls->fmp4_init_mode = !byterange_mode; if ((ret = s->io_open(s, &oc->pb, hls->base_output_dirname, AVIO_FLAG_WRITE, NULL)) < 0) { av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", hls->fmp4_init_filename); return ret; @@ -980,9 +985,6 @@ static void write_m3u8_head_block(HLSContext *hls, AVIOContext *out, int version } avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration); avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); - if (hls->segment_type == SEGMENT_TYPE_FMP4) { - avio_printf(out, "#EXT-X-MAP:URI=\"%s\"\n", hls->fmp4_init_filename); - } av_log(hls, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); } @@ -1066,13 +1068,21 @@ static int hls_window(AVFormatContext *s, int last) avio_printf(out, "#EXT-X-DISCONTINUITY\n"); } - if (hls->flags & HLS_ROUND_DURATIONS) - avio_printf(out, "#EXTINF:%ld,\n", lrint(en->duration)); - else - avio_printf(out, "#EXTINF:%f,\n", en->duration); - if (byterange_mode) - avio_printf(out, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n", - en->size, en->pos); + if ((hls->segment_type == SEGMENT_TYPE_FMP4) && (en == hls->segments)) { + avio_printf(out, "#EXT-X-MAP:URI=\"%s\"", hls->fmp4_init_filename); + if (hls->flags & HLS_SINGLE_FILE) { + avio_printf(out, ",BYTERANGE=\"%"PRId64"@%"PRId64"\"", en->size, en->pos); + } + avio_printf(out, "\n"); + } else { + if (hls->flags & HLS_ROUND_DURATIONS) + avio_printf(out, "#EXTINF:%ld,\n", lrint(en->duration)); + else + avio_printf(out, "#EXTINF:%f,\n", en->duration); + if (byterange_mode) + avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64"\n", + en->size, en->pos); + } if (hls->flags & HLS_PROGRAM_DATE_TIME) { time_t tt, wrongsecs; int milli; @@ -1097,9 +1107,11 @@ static int hls_window(AVFormatContext *s, int last) avio_printf(out, "#EXT-X-PROGRAM-DATE-TIME:%s.%03d%s\n", buf0, milli, buf1); prog_date_time += en->duration; } - if (hls->baseurl) - avio_printf(out, "%s", hls->baseurl); - avio_printf(out, "%s\n", en->filename); + if (!((hls->segment_type == SEGMENT_TYPE_FMP4) && (en == hls->segments))) { + if (hls->baseurl) + avio_printf(out, "%s", hls->baseurl); + avio_printf(out, "%s\n", en->filename); + } } if (last && (hls->flags & HLS_OMIT_ENDLIST)==0) @@ -1262,7 +1274,7 @@ static int hls_start(AVFormatContext *s) } av_dict_free(&options); - if (c->segment_type == SEGMENT_TYPE_FMP4) { + if (c->segment_type == SEGMENT_TYPE_FMP4 && !(c->flags & HLS_SINGLE_FILE)) { write_styp(oc->pb); } else { /* We only require one PAT/PMT per segment. */ @@ -1315,15 +1327,10 @@ static int hls_write_header(AVFormatContext *s) const char *pattern_localtime_fmt = get_default_pattern_localtime_fmt(s); const char *vtt_pattern = "%d.vtt"; AVDictionary *options = NULL; - int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0); int basename_size; int vtt_basename_size; if (hls->segment_type == SEGMENT_TYPE_FMP4) { - if (byterange_mode) { - av_log(s, AV_LOG_WARNING, "Have not support fmp4 byterange mode yet now\n"); - return AVERROR_PATCHWELCOME; - } pattern = "%d.m4s"; } if ((hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) || @@ -1400,8 +1407,13 @@ static int hls_write_header(AVFormatContext *s) goto fail; } } else { - if (hls->flags & HLS_SINGLE_FILE) - pattern = ".ts"; + if (hls->flags & HLS_SINGLE_FILE) { + if (hls->segment_type == SEGMENT_TYPE_FMP4) { + pattern = ".m4s"; + } else { + pattern = ".ts"; + } + } if (hls->use_localtime) { basename_size = strlen(s->filename) + strlen(pattern_localtime_fmt) + 1; @@ -1490,6 +1502,14 @@ static int hls_write_header(AVFormatContext *s) av_strlcat(hls->vtt_basename, vtt_pattern, vtt_basename_size); } + if ((hls->flags & HLS_SINGLE_FILE) && (hls->segment_type == SEGMENT_TYPE_FMP4)) { + hls->fmp4_init_filename = av_strdup(hls->basename); + if (!hls->fmp4_init_filename) { + ret = AVERROR(ENOMEM); + goto fail; + } + } + if ((ret = hls_mux_init(s)) < 0) goto fail; @@ -1504,7 +1524,7 @@ static int hls_write_header(AVFormatContext *s) } } - if (hls->segment_type != SEGMENT_TYPE_FMP4) { + if (hls->segment_type != SEGMENT_TYPE_FMP4 || hls->flags & HLS_SINGLE_FILE) { if ((ret = hls_start(s)) < 0) goto fail; } @@ -1545,6 +1565,7 @@ fail: av_dict_free(&options); if (ret < 0) { + av_freep(&hls->fmp4_init_filename); av_freep(&hls->basename); av_freep(&hls->vtt_basename); av_freep(&hls->key_basename); @@ -1643,7 +1664,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) hls->number--; } - if (!hls->fmp4_init_mode) + if (!hls->fmp4_init_mode || byterange_mode) ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size); hls->start_pos = new_start_pos; @@ -1679,9 +1700,10 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) return ret; } - if ((ret = hls_window(s, 0)) < 0) { - return ret; - } + if (!hls->fmp4_init_mode || byterange_mode) + if ((ret = hls_window(s, 0)) < 0) { + return ret; + } } ret = ff_write_chained(oc, stream_index, pkt, s, 0); @@ -1722,6 +1744,7 @@ static int hls_write_trailer(struct AVFormatContext *s) hls->size = avio_tell(hls->vtt_avf->pb) - hls->start_pos; ff_format_io_close(s, &vtt_oc->pb); } + av_freep(&hls->fmp4_init_filename); av_freep(&hls->basename); av_freep(&hls->base_output_dirname); av_freep(&hls->key_basename);