When ffmpeg exit by exception, start a new ffmpeg will
cover the old segment list, add this flag can continue
append the new segments into old hls segment list
Signed-off-by: LiuQi <liuqi@gosun.com>
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
... | ... |
@@ -495,6 +495,10 @@ Will produce the playlist, @file{out.m3u8}, and a single segment file, |
495 | 495 |
Segment files removed from the playlist are deleted after a period of time |
496 | 496 |
equal to the duration of the segment plus the duration of the playlist. |
497 | 497 |
|
498 |
+@item hls_flags append_list |
|
499 |
+Append new segments into the end of old segment list, |
|
500 |
+and remove the @code{#EXT-X-ENDLIST} from the old segment list. |
|
501 |
+ |
|
498 | 502 |
@item hls_flags round_durations |
499 | 503 |
Round the duration info in the playlist file segment info to integer |
500 | 504 |
values, instead of using floating point. |
... | ... |
@@ -63,6 +63,7 @@ typedef enum HLSFlags { |
63 | 63 |
HLS_DISCONT_START = (1 << 3), |
64 | 64 |
HLS_OMIT_ENDLIST = (1 << 4), |
65 | 65 |
HLS_SPLIT_BY_TIME = (1 << 5), |
66 |
+ HLS_APPEND_LIST = (1 << 6), |
|
66 | 67 |
} HLSFlags; |
67 | 68 |
|
68 | 69 |
typedef enum { |
... | ... |
@@ -265,6 +266,14 @@ static int hls_encryption_start(AVFormatContext *s) |
265 | 265 |
return 0; |
266 | 266 |
} |
267 | 267 |
|
268 |
+static int read_chomp_line(AVIOContext *s, char *buf, int maxlen) |
|
269 |
+{ |
|
270 |
+ int len = ff_get_line(s, buf, maxlen); |
|
271 |
+ while (len > 0 && av_isspace(buf[len - 1])) |
|
272 |
+ buf[--len] = '\0'; |
|
273 |
+ return len; |
|
274 |
+} |
|
275 |
+ |
|
268 | 276 |
static int hls_mux_init(AVFormatContext *s) |
269 | 277 |
{ |
270 | 278 |
HLSContext *hls = s->priv_data; |
... | ... |
@@ -389,6 +398,54 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, double |
389 | 389 |
return 0; |
390 | 390 |
} |
391 | 391 |
|
392 |
+static int parse_playlist(AVFormatContext *s, const char *url) |
|
393 |
+{ |
|
394 |
+ HLSContext *hls = s->priv_data; |
|
395 |
+ AVIOContext *in; |
|
396 |
+ int ret = 0, is_segment = 0; |
|
397 |
+ int64_t new_start_pos; |
|
398 |
+ char line[1024]; |
|
399 |
+ const char *ptr; |
|
400 |
+ |
|
401 |
+ if ((ret = ffio_open_whitelist(&in, url, AVIO_FLAG_READ, |
|
402 |
+ &s->interrupt_callback, NULL, |
|
403 |
+ s->protocol_whitelist, s->protocol_blacklist)) < 0) |
|
404 |
+ return ret; |
|
405 |
+ |
|
406 |
+ read_chomp_line(in, line, sizeof(line)); |
|
407 |
+ if (strcmp(line, "#EXTM3U")) { |
|
408 |
+ ret = AVERROR_INVALIDDATA; |
|
409 |
+ goto fail; |
|
410 |
+ } |
|
411 |
+ |
|
412 |
+ while (!avio_feof(in)) { |
|
413 |
+ read_chomp_line(in, line, sizeof(line)); |
|
414 |
+ if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) { |
|
415 |
+ hls->sequence = atoi(ptr); |
|
416 |
+ } else if (av_strstart(line, "#EXTINF:", &ptr)) { |
|
417 |
+ is_segment = 1; |
|
418 |
+ hls->duration = atof(ptr); |
|
419 |
+ } else if (av_strstart(line, "#", NULL)) { |
|
420 |
+ continue; |
|
421 |
+ } else if (line[0]) { |
|
422 |
+ if (is_segment) { |
|
423 |
+ is_segment = 0; |
|
424 |
+ new_start_pos = avio_tell(hls->avf->pb); |
|
425 |
+ hls->size = new_start_pos - hls->start_pos; |
|
426 |
+ av_strlcpy(hls->avf->filename, line, sizeof(line)); |
|
427 |
+ ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size); |
|
428 |
+ if (ret < 0) |
|
429 |
+ goto fail; |
|
430 |
+ hls->start_pos = new_start_pos; |
|
431 |
+ } |
|
432 |
+ } |
|
433 |
+ } |
|
434 |
+ |
|
435 |
+fail: |
|
436 |
+ avio_close(in); |
|
437 |
+ return ret; |
|
438 |
+} |
|
439 |
+ |
|
392 | 440 |
static void hls_free_segments(HLSSegment *p) |
393 | 441 |
{ |
394 | 442 |
HLSSegment *en; |
... | ... |
@@ -752,6 +809,10 @@ static int hls_write_header(AVFormatContext *s) |
752 | 752 |
if ((ret = hls_mux_init(s)) < 0) |
753 | 753 |
goto fail; |
754 | 754 |
|
755 |
+ if (hls->flags & HLS_APPEND_LIST) { |
|
756 |
+ parse_playlist(s, s->filename); |
|
757 |
+ } |
|
758 |
+ |
|
755 | 759 |
if ((ret = hls_start(s)) < 0) |
756 | 760 |
goto fail; |
757 | 761 |
|
... | ... |
@@ -927,6 +988,7 @@ static const AVOption options[] = { |
927 | 927 |
{"discont_start", "start the playlist with a discontinuity tag", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_DISCONT_START }, 0, UINT_MAX, E, "flags"}, |
928 | 928 |
{"omit_endlist", "Do not append an endlist when ending stream", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_OMIT_ENDLIST }, 0, UINT_MAX, E, "flags"}, |
929 | 929 |
{"split_by_time", "split the hls segment by time which user set by hls_time", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SPLIT_BY_TIME }, 0, UINT_MAX, E, "flags"}, |
930 |
+ {"append_list", "append the new segments into old hls segment list", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_APPEND_LIST }, 0, UINT_MAX, E, "flags"}, |
|
930 | 931 |
{"use_localtime", "set filename expansion with strftime at segment creation", OFFSET(use_localtime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, |
931 | 932 |
{"use_localtime_mkdir", "create last directory component in strftime-generated filename", OFFSET(use_localtime_mkdir), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, |
932 | 933 |
{"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" }, |