FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/hlsenc.c
Date: 2026-07-19 01:11:25
Exec Total Coverage
Lines: 940 1830 51.4%
Functions: 31 47 66.0%
Branches: 615 1344 45.8%

Line Branch Exec Source
1 /*
2 * Apple HTTP Live Streaming segmenter
3 * Copyright (c) 2012, Luca Barbato
4 * Copyright (c) 2017 Akamai Technologies, Inc.
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include "config.h"
24 #include "config_components.h"
25 #include <stdint.h>
26 #include <time.h>
27 #if HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30
31 #include "libavutil/attributes_internal.h"
32 #include "libavutil/avassert.h"
33 #include "libavutil/mathematics.h"
34 #include "libavutil/avstring.h"
35 #include "libavutil/bprint.h"
36 #include "libavutil/intreadwrite.h"
37 #include "libavutil/mem.h"
38 #include "libavutil/opt.h"
39 #include "libavutil/parseutils.h"
40 #include "libavutil/log.h"
41 #include "libavutil/random_seed.h"
42 #include "libavutil/time.h"
43 #include "libavutil/time_internal.h"
44
45 #include "libavcodec/defs.h"
46
47 #include "avformat.h"
48 #include "avio_internal.h"
49 #if CONFIG_HTTP_PROTOCOL
50 #include "http.h"
51 #endif
52 #include "hlsplaylist.h"
53 #include "internal.h"
54 #include "mux.h"
55 #include "os_support.h"
56 #include "url.h"
57
58 typedef enum {
59 HLS_START_SEQUENCE_AS_START_NUMBER = 0,
60 HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH = 1,
61 HLS_START_SEQUENCE_AS_FORMATTED_DATETIME = 2, // YYYYMMDDhhmmss
62 HLS_START_SEQUENCE_AS_MICROSECONDS_SINCE_EPOCH = 3,
63 HLS_START_SEQUENCE_LAST, // unused
64 } StartSequenceSourceType;
65
66 typedef enum {
67 CODEC_ATTRIBUTE_WRITTEN = 0,
68 CODEC_ATTRIBUTE_WILL_NOT_BE_WRITTEN,
69 } CodecAttributeStatus;
70
71 #define KEYSIZE 16
72 #define LINE_BUFFER_SIZE MAX_URL_SIZE
73 #define HLS_MICROSECOND_UNIT 1000000
74 #define BUFSIZE (16 * 1024)
75 #define POSTFIX_PATTERN "_%d"
76
77 typedef struct HLSSegment {
78 const char *filename;
79 const char *sub_filename;
80 double duration; /* in seconds */
81 int discont;
82 int64_t pos;
83 int64_t size;
84 unsigned var_stream_idx;
85
86 const char *key_uri;
87 char iv_string[KEYSIZE*2 + 1];
88
89 struct HLSSegment *next;
90 double discont_program_date_time;
91
92 char buf[]; /* for filename, sub_filename and key_uri */
93 } HLSSegment;
94
95 typedef enum HLSFlags {
96 // Generate a single media file and use byte ranges in the playlist.
97 HLS_SINGLE_FILE = (1 << 0),
98 HLS_DELETE_SEGMENTS = (1 << 1),
99 HLS_ROUND_DURATIONS = (1 << 2),
100 HLS_DISCONT_START = (1 << 3),
101 HLS_OMIT_ENDLIST = (1 << 4),
102 HLS_SPLIT_BY_TIME = (1 << 5),
103 HLS_APPEND_LIST = (1 << 6),
104 HLS_PROGRAM_DATE_TIME = (1 << 7),
105 HLS_SECOND_LEVEL_SEGMENT_INDEX = (1 << 8), // include segment index in segment filenames when use_localtime e.g.: %%03d
106 HLS_SECOND_LEVEL_SEGMENT_DURATION = (1 << 9), // include segment duration (microsec) in segment filenames when use_localtime e.g.: %%09t
107 HLS_SECOND_LEVEL_SEGMENT_SIZE = (1 << 10), // include segment size (bytes) in segment filenames when use_localtime e.g.: %%014s
108 HLS_TEMP_FILE = (1 << 11),
109 HLS_PERIODIC_REKEY = (1 << 12),
110 HLS_INDEPENDENT_SEGMENTS = (1 << 13),
111 HLS_I_FRAMES_ONLY = (1 << 14),
112 } HLSFlags;
113
114 typedef enum {
115 SEGMENT_TYPE_MPEGTS,
116 SEGMENT_TYPE_FMP4,
117 } SegmentType;
118
119 typedef struct VariantStream {
120 unsigned var_stream_idx;
121 unsigned number;
122 int64_t sequence;
123 const AVOutputFormat *oformat;
124 const AVOutputFormat *vtt_oformat;
125 AVIOContext *out;
126 AVIOContext *out_single_file;
127 int packets_written;
128 int init_range_length;
129 uint8_t *temp_buffer;
130 uint8_t *init_buffer;
131
132 AVFormatContext *avf;
133 AVFormatContext *vtt_avf;
134
135 int has_video;
136 int has_subtitle;
137 int new_start;
138 int start_pts_from_audio;
139 double dpp; // duration per packet
140 int64_t start_pts;
141 int64_t end_pts;
142 int64_t video_lastpos;
143 double duration; // last segment duration computed so far, in seconds
144 int64_t start_pos; // last segment starting position
145 int64_t size; // last segment size
146 int nb_entries;
147 int discontinuity_set;
148 int discontinuity;
149 int reference_stream_index;
150
151 int64_t total_size;
152 double total_duration;
153 int64_t avg_bitrate;
154 int64_t max_bitrate;
155
156 HLSSegment *segments;
157 HLSSegment *last_segment;
158 HLSSegment *old_segments;
159
160 char *basename_tmp;
161 char *basename;
162 char *vtt_basename;
163 char *vtt_m3u8_name;
164 char *m3u8_name;
165
166 double initial_prog_date_time;
167 char current_segment_final_filename_fmt[MAX_URL_SIZE]; // when renaming segments
168
169 char *fmp4_init_filename;
170 char *base_output_dirname;
171
172 int encrypt_started;
173
174 char key_file[LINE_BUFFER_SIZE + 1];
175 char key_uri[LINE_BUFFER_SIZE + 1];
176 char key_string[KEYSIZE*2 + 1];
177 char iv_string[KEYSIZE*2 + 1];
178
179 AVStream **streams;
180 char codec_attr[128];
181 CodecAttributeStatus attr_status;
182 unsigned int nb_streams;
183 int m3u8_created; /* status of media play-list creation */
184 int is_default; /* default status of audio group */
185 const char *language; /* audio language name */
186 const char *agroup; /* audio group name */
187 const char *sgroup; /* subtitle group name */
188 const char *ccgroup; /* closed caption group name */
189 const char *varname; /* variant name */
190 const char *subtitle_varname; /* subtitle variant name */
191 } VariantStream;
192
193 typedef struct ClosedCaptionsStream {
194 const char *ccgroup; /* closed caption group name */
195 const char *instreamid; /* closed captions INSTREAM-ID */
196 const char *language; /* closed captions language */
197 } ClosedCaptionsStream;
198
199 typedef struct HLSContext {
200 const AVClass *class; // Class for private options.
201 int64_t start_sequence;
202 uint32_t start_sequence_source_type; // enum StartSequenceSourceType
203
204 int64_t time; // Set by a private option.
205 int64_t init_time; // Set by a private option.
206 int max_nb_segments; // Set by a private option.
207 int hls_delete_threshold; // Set by a private option.
208 uint32_t flags; // enum HLSFlags
209 uint32_t pl_type; // enum PlaylistType
210 char *segment_filename;
211 char *fmp4_init_filename;
212 int segment_type;
213 int resend_init_file; ///< resend init file into disk after refresh m3u8
214
215 int use_localtime; ///< flag to expand filename with localtime
216 int use_localtime_mkdir;///< flag to mkdir dirname in timebased filename
217 int allowcache;
218 int64_t recording_time;
219 int64_t max_seg_size; // every segment file max size
220
221 char *baseurl;
222 char *vtt_format_options_str;
223 char *subtitle_filename;
224 AVDictionary *format_options;
225
226 int encrypt;
227 char *key;
228 char *key_url;
229 char *iv;
230 char *key_basename;
231 int encrypt_started;
232
233 char *key_info_file;
234 char key_file[LINE_BUFFER_SIZE + 1];
235 char key_uri[LINE_BUFFER_SIZE + 1];
236 char key_string[KEYSIZE*2 + 1];
237 char iv_string[KEYSIZE*2 + 1];
238 AVDictionary *vtt_format_options;
239
240 char *method;
241 char *user_agent;
242
243 VariantStream *var_streams;
244 unsigned int nb_varstreams;
245 ClosedCaptionsStream *cc_streams;
246 unsigned int nb_ccstreams;
247
248 int master_m3u8_created; /* status of master play-list creation */
249 char *master_m3u8_url; /* URL of the master m3u8 file */
250 int version; /* HLS version */
251 char *var_stream_map; /* user specified variant stream map string */
252 char *cc_stream_map; /* user specified closed caption streams map string */
253 char *master_pl_name;
254 unsigned int master_publish_rate;
255 int http_persistent;
256 AVIOContext *m3u8_out;
257 AVIOContext *sub_m3u8_out;
258 AVIOContext *http_delete;
259 int64_t timeout;
260 int ignore_io_errors;
261 char *headers;
262 int has_default_key; /* has DEFAULT field of var_stream_map */
263 int has_video_m3u8; /* has video stream m3u8 list */
264 } HLSContext;
265
266 static int strftime_expand(const char *fmt, char **dest)
267 {
268 int r = 1;
269 time_t now0;
270 struct tm *tm, tmpbuf;
271 char *buf;
272
273 buf = av_mallocz(MAX_URL_SIZE);
274 if (!buf)
275 return AVERROR(ENOMEM);
276
277 time(&now0);
278 tm = localtime_r(&now0, &tmpbuf);
279 r = strftime(buf, MAX_URL_SIZE, fmt, tm);
280 if (!r) {
281 av_free(buf);
282 return AVERROR(EINVAL);
283 }
284 *dest = buf;
285
286 return r;
287 }
288
289 194 static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, const char *filename,
290 AVDictionary **options)
291 {
292 194 HLSContext *hls = s->priv_data;
293
1/2
✓ Branch 0 taken 194 times.
✗ Branch 1 not taken.
194 int http_base_proto = filename ? ff_is_http_proto(filename) : 0;
294 194 int err = AVERROR_MUXER_NOT_FOUND;
295
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 194 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
194 if (!*pb || !http_base_proto || !hls->http_persistent) {
296 194 err = s->io_open(s, pb, filename, AVIO_FLAG_WRITE, options);
297 #if CONFIG_HTTP_PROTOCOL
298 } else {
299 URLContext *http_url_context = ffio_geturlcontext(*pb);
300 av_assert0(http_url_context);
301 err = ff_http_do_new_request(http_url_context, filename);
302 if (err < 0)
303 ff_format_io_close(s, pb);
304
305 #endif
306 }
307 194 return err;
308 }
309
310 297 static int hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename)
311 {
312 297 HLSContext *hls = s->priv_data;
313
2/2
✓ Branch 0 taken 195 times.
✓ Branch 1 taken 102 times.
297 int http_base_proto = filename ? ff_is_http_proto(filename) : 0;
314 297 int ret = 0;
315
2/2
✓ Branch 0 taken 104 times.
✓ Branch 1 taken 193 times.
297 if (!*pb)
316 104 return ret;
317
1/8
✗ Branch 0 not taken.
✓ Branch 1 taken 193 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
193 if (!http_base_proto || !hls->http_persistent || hls->key_info_file || hls->encrypt) {
318 193 ff_format_io_close(s, pb);
319 #if CONFIG_HTTP_PROTOCOL
320 } else {
321 URLContext *http_url_context = ffio_geturlcontext(*pb);
322 av_assert0(http_url_context);
323 avio_flush(*pb);
324 ret = ffurl_shutdown(http_url_context, AVIO_FLAG_WRITE);
325 #endif
326 }
327 193 return ret;
328 }
329
330 192 static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSContext *c)
331 {
332 192 int http_base_proto = ff_is_http_proto(s->url);
333
334
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 192 times.
192 if (c->method) {
335 av_dict_set(options, "method", c->method, 0);
336
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 192 times.
192 } else if (http_base_proto) {
337 av_dict_set(options, "method", "PUT", 0);
338 }
339
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 192 times.
192 if (c->user_agent)
340 av_dict_set(options, "user_agent", c->user_agent, 0);
341
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 192 times.
192 if (c->http_persistent)
342 av_dict_set_int(options, "multiple_requests", 1, 0);
343
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 192 times.
192 if (c->timeout >= 0)
344 av_dict_set_int(options, "timeout", c->timeout, 0);
345
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 192 times.
192 if (c->headers)
346 av_dict_set(options, "headers", c->headers, 0);
347 192 }
348
349 23 static void write_codec_attr(AVStream *st, VariantStream *vs)
350 {
351 23 int codec_strlen = strlen(vs->codec_attr);
352 char attr[32];
353 AVBPrint buffer;
354
355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
356 return;
357
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 22 times.
23 if (vs->attr_status == CODEC_ATTRIBUTE_WILL_NOT_BE_WRITTEN)
358 1 return;
359
360 22 av_bprint_init_for_buffer(&buffer, attr, sizeof(attr));
361
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 13 times.
22 if (ff_make_codec_str(vs->avf, st->codecpar, &st->avg_frame_rate,
362 &buffer) < 0)
363 9 goto fail;
364
365 // Don't write the same attribute multiple times
366
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 if (!av_stristr(vs->codec_attr, attr)) {
367
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 snprintf(vs->codec_attr + codec_strlen,
368 sizeof(vs->codec_attr) - codec_strlen,
369 "%s%s", codec_strlen ? "," : "", attr);
370 }
371 13 return;
372
373 9 fail:
374 9 vs->codec_attr[0] = '\0';
375 9 vs->attr_status = CODEC_ATTRIBUTE_WILL_NOT_BE_WRITTEN;
376 9 return;
377 }
378
379 static int replace_str_data_in_filename(char **s, const char *filename, char placeholder, const char *datastring)
380 {
381 const char *p;
382 char c;
383 int addchar_count;
384 int found_count = 0;
385 AVBPrint buf;
386 int ret;
387
388 av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
389
390 p = filename;
391 for (;;) {
392 c = *p;
393 if (c == '\0')
394 break;
395 if (c == '%' && *(p+1) == '%') // %%
396 addchar_count = 2;
397 else if (c == '%' && *(p+1) == placeholder) {
398 av_bprintf(&buf, "%s", datastring);
399 p += 2;
400 addchar_count = 0;
401 found_count ++;
402 } else
403 addchar_count = 1;
404
405 if (addchar_count > 0) {
406 av_bprint_append_data(&buf, p, addchar_count);
407 p += addchar_count;
408 }
409 }
410 if (!av_bprint_is_complete(&buf)) {
411 av_bprint_finalize(&buf, NULL);
412 return AVERROR(ENOMEM);
413 }
414 if ((ret = av_bprint_finalize(&buf, s)) < 0)
415 return ret;
416 return found_count;
417 }
418
419 86 static int replace_int_data_in_filename(char **s, const char *filename, char placeholder, int64_t number)
420 {
421 const char *p;
422 char c;
423 int nd, addchar_count;
424 86 int found_count = 0;
425 AVBPrint buf;
426 int ret;
427
428 86 av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
429
430 86 p = filename;
431 for (;;) {
432 7551 c = *p;
433
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 7465 times.
7551 if (c == '\0')
434 86 break;
435
3/4
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 7377 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 88 times.
7465 if (c == '%' && *(p+1) == '%') // %%
436 addchar_count = 2;
437
6/6
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 7377 times.
✓ Branch 2 taken 65 times.
✓ Branch 3 taken 23 times.
✓ Branch 4 taken 63 times.
✓ Branch 5 taken 2 times.
7465 else if (c == '%' && (av_isdigit(*(p+1)) || *(p+1) == placeholder)) {
438 86 nd = 0;
439 86 addchar_count = 1;
440
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 86 times.
132 while (av_isdigit(*(p + addchar_count))) {
441 46 nd = nd * 10 + *(p + addchar_count) - '0';
442 46 addchar_count++;
443 }
444
445
1/2
✓ Branch 0 taken 86 times.
✗ Branch 1 not taken.
86 if (*(p + addchar_count) == placeholder) {
446
1/2
✓ Branch 0 taken 86 times.
✗ Branch 1 not taken.
86 av_bprintf(&buf, "%0*"PRId64, (number < 0) ? nd : nd++, number);
447 86 p += (addchar_count + 1);
448 86 addchar_count = 0;
449 86 found_count++;
450 }
451
452 } else
453 7379 addchar_count = 1;
454
455 7465 av_bprint_append_data(&buf, p, addchar_count);
456 7465 p += addchar_count;
457 }
458
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 86 times.
86 if (!av_bprint_is_complete(&buf)) {
459 av_bprint_finalize(&buf, NULL);
460 return AVERROR(ENOMEM);
461 }
462
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 86 times.
86 if ((ret = av_bprint_finalize(&buf, s)) < 0)
463 return ret;
464 86 return found_count;
465 }
466
467 6 static void write_styp(AVIOContext *pb)
468 {
469 6 avio_wb32(pb, 24);
470 6 ffio_wfourcc(pb, "styp");
471 6 ffio_wfourcc(pb, "msdh");
472 6 avio_wb32(pb, 0); /* minor */
473 6 ffio_wfourcc(pb, "msdh");
474 6 ffio_wfourcc(pb, "msix");
475 6 }
476
477 98 static int flush_dynbuf(VariantStream *vs, int *range_length)
478 {
479 98 AVFormatContext *ctx = vs->avf;
480
481
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 98 times.
98 if (!ctx->pb) {
482 return AVERROR(EINVAL);
483 }
484
485 // flush
486 98 av_write_frame(ctx, NULL);
487
488 // write out to file
489 98 *range_length = avio_close_dyn_buf(ctx->pb, &vs->temp_buffer);
490 98 ctx->pb = NULL;
491 98 avio_write(vs->out, vs->temp_buffer, *range_length);
492 98 avio_flush(vs->out);
493
494 // re-open buffer
495 98 return avio_open_dyn_buf(&ctx->pb);
496 }
497
498 static void reflush_dynbuf(VariantStream *vs, int *range_length)
499 {
500 // re-open buffer
501 avio_write(vs->out, vs->temp_buffer, *range_length);
502 }
503
504 static int hls_delete_file(HLSContext *hls, AVFormatContext *avf,
505 char *path, const char *proto)
506 {
507 if (hls->method || (proto && !av_strcasecmp(proto, "http"))) {
508 AVDictionary *opt = NULL;
509 int ret;
510
511 set_http_options(avf, &opt, hls);
512 av_dict_set(&opt, "method", "DELETE", 0);
513
514 ret = hlsenc_io_open(avf, &hls->http_delete, path, &opt);
515 av_dict_free(&opt);
516 if (ret < 0)
517 return hls->ignore_io_errors ? 1 : ret;
518
519 //Nothing to write
520 hlsenc_io_close(avf, &hls->http_delete, path);
521 } else if (unlink(path) < 0) {
522 av_log(hls, AV_LOG_ERROR, "failed to delete old segment %s: %s\n",
523 path, strerror(errno));
524 }
525 return 0;
526 }
527
528 static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls,
529 VariantStream *vs)
530 {
531
532 HLSSegment *segment, *previous_segment = NULL;
533 float playlist_duration = 0.0f;
534 int ret = 0;
535 int segment_cnt = 0;
536 AVBPrint path;
537 const char *dirname = NULL;
538 char *dirname_r = NULL;
539 char *dirname_repl = NULL;
540 const char *vtt_dirname = NULL;
541 char *vtt_dirname_r = NULL;
542 const char *proto = NULL;
543
544 av_bprint_init(&path, 0, AV_BPRINT_SIZE_UNLIMITED);
545
546 segment = vs->segments;
547 while (segment) {
548 playlist_duration += segment->duration;
549 segment = segment->next;
550 }
551
552 segment = vs->old_segments;
553 segment_cnt = 0;
554 while (segment) {
555 playlist_duration -= segment->duration;
556 previous_segment = segment;
557 segment = previous_segment->next;
558 segment_cnt++;
559 if (playlist_duration <= -previous_segment->duration) {
560 previous_segment->next = NULL;
561 break;
562 }
563 if (segment_cnt >= hls->hls_delete_threshold) {
564 previous_segment->next = NULL;
565 break;
566 }
567 }
568
569 if (segment && !hls->use_localtime_mkdir) {
570 dirname_r = hls->segment_filename ? av_strdup(hls->segment_filename): av_strdup(vs->avf->url);
571 dirname = av_dirname(dirname_r);
572 }
573
574 /* if %v is present in the file's directory
575 * all segment belongs to the same variant, so do it only once before the loop*/
576 if (dirname && av_stristr(dirname, "%v")) {
577 if (!vs->varname) {
578 if (replace_int_data_in_filename(&dirname_repl, dirname, 'v', segment->var_stream_idx) < 1) {
579 ret = AVERROR(EINVAL);
580 goto fail;
581 }
582 } else {
583 if (replace_str_data_in_filename(&dirname_repl, dirname, 'v', vs->varname) < 1) {
584 ret = AVERROR(EINVAL);
585 goto fail;
586 }
587 }
588
589 dirname = dirname_repl;
590 }
591
592 while (segment) {
593 av_log(hls, AV_LOG_DEBUG, "deleting old segment %s\n",
594 segment->filename);
595 if (!hls->use_localtime_mkdir) // segment->filename contains basename only
596 av_bprintf(&path, "%s/", dirname);
597 av_bprintf(&path, "%s", segment->filename);
598
599 if (!av_bprint_is_complete(&path)) {
600 ret = AVERROR(ENOMEM);
601 goto fail;
602 }
603
604 proto = avio_find_protocol_name(s->url);
605 if (ret = hls_delete_file(hls, s, path.str, proto))
606 goto fail;
607
608 if ((segment->sub_filename[0] != '\0')) {
609 vtt_dirname_r = av_strdup(vs->vtt_avf->url);
610 vtt_dirname = av_dirname(vtt_dirname_r);
611
612 av_bprint_clear(&path);
613 av_bprintf(&path, "%s/%s", vtt_dirname, segment->sub_filename);
614 av_freep(&vtt_dirname_r);
615
616 if (!av_bprint_is_complete(&path)) {
617 ret = AVERROR(ENOMEM);
618 goto fail;
619 }
620
621 if (ret = hls_delete_file(hls, s, path.str, proto))
622 goto fail;
623 }
624 av_bprint_clear(&path);
625 previous_segment = segment;
626 segment = previous_segment->next;
627 av_freep(&previous_segment);
628 }
629
630 fail:
631 av_bprint_finalize(&path, NULL);
632 av_freep(&dirname_r);
633 av_freep(&dirname_repl);
634
635 return ret;
636 }
637
638 static int do_encrypt(AVFormatContext *s, VariantStream *vs)
639 {
640 HLSContext *hls = s->priv_data;
641 int ret;
642 int len;
643 AVIOContext *pb;
644 uint8_t key[KEYSIZE];
645 char * key_basename_source = (hls->master_m3u8_url) ? hls->master_m3u8_url : s->url;
646
647 len = strlen(key_basename_source) + 4 + 1;
648 hls->key_basename = av_mallocz(len);
649 if (!hls->key_basename)
650 return AVERROR(ENOMEM);
651
652 av_strlcpy(hls->key_basename, key_basename_source, len);
653 av_strlcat(hls->key_basename, ".key", len);
654
655 if (hls->key_url) {
656 av_strlcpy(hls->key_file, hls->key_url, sizeof(hls->key_file));
657 av_strlcpy(hls->key_uri, hls->key_url, sizeof(hls->key_uri));
658 } else {
659 av_strlcpy(hls->key_file, hls->key_basename, sizeof(hls->key_file));
660 av_strlcpy(hls->key_uri, hls->key_basename, sizeof(hls->key_uri));
661 }
662
663 if (!*hls->iv_string) {
664 uint8_t iv[16] = { 0 };
665 char buf[33];
666
667 if (!hls->iv) {
668 AV_WB64(iv + 8, vs->sequence);
669 } else {
670 memcpy(iv, hls->iv, sizeof(iv));
671 }
672 ff_data_to_hex(buf, iv, sizeof(iv), 0);
673 memcpy(hls->iv_string, buf, sizeof(hls->iv_string));
674 }
675
676 if (!*hls->key_uri) {
677 av_log(hls, AV_LOG_ERROR, "no key URI specified in key info file\n");
678 return AVERROR(EINVAL);
679 }
680
681 if (!*hls->key_file) {
682 av_log(hls, AV_LOG_ERROR, "no key file specified in key info file\n");
683 return AVERROR(EINVAL);
684 }
685
686 if (!*hls->key_string) {
687 AVDictionary *options = NULL;
688 if (!hls->key) {
689 if ((ret = av_random_bytes(key, sizeof(key))) < 0) {
690 av_log(s, AV_LOG_ERROR, "Cannot generate a strong random key\n");
691 return ret;
692 }
693 } else {
694 memcpy(key, hls->key, sizeof(key));
695 }
696
697 ff_data_to_hex(hls->key_string, key, sizeof(key), 0);
698 set_http_options(s, &options, hls);
699 ret = s->io_open(s, &pb, hls->key_file, AVIO_FLAG_WRITE, &options);
700 av_dict_free(&options);
701 if (ret < 0)
702 return ret;
703 avio_seek(pb, 0, SEEK_CUR);
704 avio_write(pb, key, KEYSIZE);
705 ff_format_io_close(s, &pb);
706 }
707 return 0;
708 }
709
710
711 static int hls_encryption_start(AVFormatContext *s, VariantStream *vs)
712 {
713 HLSContext *hls = s->priv_data;
714 int ret;
715 AVIOContext *pb;
716 uint8_t key[KEYSIZE];
717 AVDictionary *options = NULL;
718
719 set_http_options(s, &options, hls);
720 ret = s->io_open(s, &pb, hls->key_info_file, AVIO_FLAG_READ, &options);
721 av_dict_free(&options);
722 if (ret < 0) {
723 av_log(hls, AV_LOG_ERROR,
724 "error opening key info file %s\n", hls->key_info_file);
725 return ret;
726 }
727
728 ff_get_line(pb, vs->key_uri, sizeof(vs->key_uri));
729 vs->key_uri[strcspn(vs->key_uri, "\r\n")] = '\0';
730
731 ff_get_line(pb, vs->key_file, sizeof(vs->key_file));
732 vs->key_file[strcspn(vs->key_file, "\r\n")] = '\0';
733
734 ff_get_line(pb, vs->iv_string, sizeof(vs->iv_string));
735 vs->iv_string[strcspn(vs->iv_string, "\r\n")] = '\0';
736
737 ff_format_io_close(s, &pb);
738
739 if (!*vs->key_uri) {
740 av_log(hls, AV_LOG_ERROR, "no key URI specified in key info file\n");
741 return AVERROR(EINVAL);
742 }
743
744 if (!*vs->key_file) {
745 av_log(hls, AV_LOG_ERROR, "no key file specified in key info file\n");
746 return AVERROR(EINVAL);
747 }
748
749 set_http_options(s, &options, hls);
750 ret = s->io_open(s, &pb, vs->key_file, AVIO_FLAG_READ, &options);
751 av_dict_free(&options);
752 if (ret < 0) {
753 av_log(hls, AV_LOG_ERROR, "error opening key file %s\n", vs->key_file);
754 return ret;
755 }
756
757 ret = avio_read(pb, key, sizeof(key));
758 ff_format_io_close(s, &pb);
759 if (ret != sizeof(key)) {
760 av_log(hls, AV_LOG_ERROR, "error reading key file %s\n", vs->key_file);
761 if (ret >= 0 || ret == AVERROR_EOF)
762 ret = AVERROR(EINVAL);
763 return ret;
764 }
765 ff_data_to_hex(vs->key_string, key, sizeof(key), 0);
766
767 return 0;
768 }
769
770 22 static int hls_mux_init(AVFormatContext *s, VariantStream *vs)
771 {
772 22 AVDictionary *options = NULL;
773 22 HLSContext *hls = s->priv_data;
774 AVFormatContext *oc;
775 22 AVFormatContext *vtt_oc = NULL;
776
4/4
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 18 times.
22 int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0);
777 int remaining_options;
778 int i, ret;
779
780 22 ret = avformat_alloc_output_context2(&vs->avf, vs->oformat, NULL, NULL);
781
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (ret < 0)
782 return ret;
783 22 oc = vs->avf;
784
785 22 oc->url = av_strdup("");
786
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (!oc->url)
787 return AVERROR(ENOMEM);
788
789 22 oc->interrupt_callback = s->interrupt_callback;
790 22 oc->max_delay = s->max_delay;
791 22 oc->opaque = s->opaque;
792 22 oc->io_open = s->io_open;
793 22 oc->io_close2 = s->io_close2;
794 22 oc->strict_std_compliance = s->strict_std_compliance;
795 22 oc->flags = s->flags;
796 22 av_dict_copy(&oc->metadata, s->metadata, 0);
797
798
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (vs->vtt_oformat) {
799 ret = avformat_alloc_output_context2(&vs->vtt_avf, vs->vtt_oformat, NULL, NULL);
800 if (ret < 0)
801 return ret;
802 vtt_oc = vs->vtt_avf;
803 vtt_oc->flags = s->flags;
804 av_dict_copy(&vtt_oc->metadata, s->metadata, 0);
805 }
806
807
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 22 times.
44 for (i = 0; i < vs->nb_streams; i++) {
808 AVStream *st;
809 AVFormatContext *loc;
810
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (vs->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
811 loc = vtt_oc;
812 else
813 22 loc = oc;
814
815
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
22 if (!(st = avformat_new_stream(loc, NULL)))
816 return AVERROR(ENOMEM);
817 22 ret = avcodec_parameters_copy(st->codecpar, vs->streams[i]->codecpar);
818
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (ret < 0)
819 return ret;
820
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 19 times.
22 if (!oc->oformat->codec_tag ||
821
3/4
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
5 av_codec_get_id (oc->oformat->codec_tag, vs->streams[i]->codecpar->codec_tag) == st->codecpar->codec_id ||
822 2 av_codec_get_tag(oc->oformat->codec_tag, vs->streams[i]->codecpar->codec_id) <= 0) {
823 20 st->codecpar->codec_tag = vs->streams[i]->codecpar->codec_tag;
824 } else {
825 2 st->codecpar->codec_tag = 0;
826 }
827
828 22 st->sample_aspect_ratio = vs->streams[i]->sample_aspect_ratio;
829 22 st->time_base = vs->streams[i]->time_base;
830 22 av_dict_copy(&st->metadata, vs->streams[i]->metadata, 0);
831 22 st->id = vs->streams[i]->id;
832 }
833
834
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 for (i = 0; i < s->nb_programs; i++) {
835 ret = av_program_copy(oc, (const AVFormatContext *)s, s->programs[i]->id, 0);
836 if (ret < 0) {
837 av_log(s, AV_LOG_ERROR, "unable to transfer program %d to child muxer\n", s->programs[i]->id);
838 return ret;
839 }
840 }
841
842 22 vs->start_pos = 0;
843 22 vs->new_start = 1;
844
845
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
22 if (hls->segment_type == SEGMENT_TYPE_FMP4 && hls->max_seg_size > 0) {
846 if (hls->http_persistent > 0) {
847 //TODO: Support fragment fmp4 for http persistent in HLS muxer.
848 av_log(s, AV_LOG_WARNING, "http persistent mode is currently unsupported for fragment mp4 in the HLS muxer.\n");
849 }
850 if (hls->max_seg_size > 0) {
851 av_log(s, AV_LOG_WARNING, "Multi-file byterange mode is currently unsupported in the HLS muxer.\n");
852 return AVERROR_PATCHWELCOME;
853 }
854 }
855
856
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
22 if ((ret = avio_open_dyn_buf(&oc->pb)) < 0)
857 return ret;
858
859
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 19 times.
22 if (hls->segment_type == SEGMENT_TYPE_FMP4) {
860 3 set_http_options(s, &options, hls);
861
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (byterange_mode) {
862 1 ret = hlsenc_io_open(s, &vs->out, vs->basename, &options);
863 } else {
864 2 ret = hlsenc_io_open(s, &vs->out, vs->base_output_dirname, &options);
865 }
866 3 av_dict_free(&options);
867 }
868
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (ret < 0) {
869 av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", vs->fmp4_init_filename);
870 return ret;
871 }
872
873 22 av_dict_copy(&options, hls->format_options, 0);
874
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 19 times.
22 if (hls->segment_type == SEGMENT_TYPE_FMP4) {
875 3 av_dict_set(&options, "fflags", "-autobsf", 0);
876 3 av_dict_set(&options, "movflags", "+frag_custom+dash+delay_moov", AV_DICT_APPEND);
877 } else {
878 /* We only require one PAT/PMT per segment. */
879 char period[21];
880 19 snprintf(period, sizeof(period), "%d", (INT_MAX / 2) - 1);
881 19 av_dict_set(&options, "sdt_period", period, AV_DICT_DONT_OVERWRITE);
882 19 av_dict_set(&options, "pat_period", period, AV_DICT_DONT_OVERWRITE);
883 }
884 22 ret = avformat_init_output(oc, &options);
885 22 remaining_options = av_dict_count(options);
886 22 av_dict_free(&options);
887
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (ret < 0)
888 return ret;
889
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (remaining_options) {
890 av_log(s, AV_LOG_ERROR, "Some of the provided format options are not recognized\n");
891 return AVERROR(EINVAL);
892 }
893 22 avio_flush(oc->pb);
894 22 return 0;
895 }
896
897 193 static HLSSegment *find_segment_by_filename(HLSSegment *segment, const char *filename)
898 {
899
2/2
✓ Branch 0 taken 222 times.
✓ Branch 1 taken 174 times.
396 while (segment) {
900
2/2
✓ Branch 1 taken 19 times.
✓ Branch 2 taken 203 times.
222 if (!av_strcasecmp(segment->filename,filename))
901 19 return segment;
902 203 segment = segment->next;
903 }
904 174 return (HLSSegment *) NULL;
905 }
906
907 106 static int sls_flags_filename_process(struct AVFormatContext *s, HLSContext *hls,
908 VariantStream *vs,
909 double duration, int64_t pos, int64_t size)
910 {
911
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
106 if ((hls->flags & (HLS_SECOND_LEVEL_SEGMENT_SIZE | HLS_SECOND_LEVEL_SEGMENT_DURATION)) &&
912 strlen(vs->current_segment_final_filename_fmt)) {
913 char * new_url = av_strdup(vs->current_segment_final_filename_fmt);
914 if (!new_url) {
915 return AVERROR(ENOMEM);
916 }
917 ff_format_set_url(vs->avf, new_url);
918 if (hls->flags & HLS_SECOND_LEVEL_SEGMENT_SIZE) {
919 char *filename = NULL;
920 if (replace_int_data_in_filename(&filename, vs->avf->url, 's', pos + size) < 1) {
921 av_log(hls, AV_LOG_ERROR,
922 "Invalid second level segment filename template '%s', "
923 "you can try to remove second_level_segment_size flag\n",
924 vs->avf->url);
925 av_freep(&filename);
926 return AVERROR(EINVAL);
927 }
928 ff_format_set_url(vs->avf, filename);
929 }
930 if (hls->flags & HLS_SECOND_LEVEL_SEGMENT_DURATION) {
931 char *filename = NULL;
932 if (replace_int_data_in_filename(&filename, vs->avf->url,
933 't', (int64_t)round(duration * HLS_MICROSECOND_UNIT)) < 1) {
934 av_log(hls, AV_LOG_ERROR,
935 "Invalid second level segment filename template '%s', "
936 "you can try to remove second_level_segment_duration flag\n",
937 vs->avf->url);
938 av_freep(&filename);
939 return AVERROR(EINVAL);
940 }
941 ff_format_set_url(vs->avf, filename);
942 }
943 }
944 106 return 0;
945 }
946
947 22 static int sls_flag_check_duration_size_index(HLSContext *hls)
948 {
949 22 int ret = 0;
950
951
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (hls->flags & HLS_SECOND_LEVEL_SEGMENT_DURATION) {
952 av_log(hls, AV_LOG_ERROR,
953 "second_level_segment_duration hls_flag requires strftime to be true\n");
954 ret = AVERROR(EINVAL);
955 }
956
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (hls->flags & HLS_SECOND_LEVEL_SEGMENT_SIZE) {
957 av_log(hls, AV_LOG_ERROR,
958 "second_level_segment_size hls_flag requires strfime to be true\n");
959 ret = AVERROR(EINVAL);
960 }
961
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (hls->flags & HLS_SECOND_LEVEL_SEGMENT_INDEX) {
962 av_log(hls, AV_LOG_ERROR,
963 "second_level_segment_index hls_flag requires strftime to be true\n");
964 ret = AVERROR(EINVAL);
965 }
966
967 22 return ret;
968 }
969
970 static int sls_flag_check_duration_size(HLSContext *hls, VariantStream *vs)
971 {
972 const char *proto = avio_find_protocol_name(vs->basename);
973 int segment_renaming_ok = proto && !strcmp(proto, "file");
974 int ret = 0;
975
976 if ((hls->flags & HLS_SECOND_LEVEL_SEGMENT_DURATION) && !segment_renaming_ok) {
977 av_log(hls, AV_LOG_ERROR,
978 "second_level_segment_duration hls_flag works only with file protocol segment names\n");
979 ret = AVERROR(EINVAL);
980 }
981 if ((hls->flags & HLS_SECOND_LEVEL_SEGMENT_SIZE) && !segment_renaming_ok) {
982 av_log(hls, AV_LOG_ERROR,
983 "second_level_segment_size hls_flag works only with file protocol segment names\n");
984 ret = AVERROR(EINVAL);
985 }
986
987 return ret;
988 }
989
990 85 static void sls_flag_file_rename(HLSContext *hls, VariantStream *vs, char *old_filename) {
991
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 85 times.
85 if ((hls->flags & (HLS_SECOND_LEVEL_SEGMENT_SIZE | HLS_SECOND_LEVEL_SEGMENT_DURATION)) &&
992 strlen(vs->current_segment_final_filename_fmt)) {
993 ff_rename(old_filename, vs->avf->url, hls);
994 }
995 85 }
996
997 static int sls_flag_use_localtime_filename(AVFormatContext *oc, HLSContext *c, VariantStream *vs)
998 {
999 if (c->flags & HLS_SECOND_LEVEL_SEGMENT_INDEX) {
1000 char *filename = NULL;
1001 if (replace_int_data_in_filename(&filename,
1002 oc->url, 'd', vs->sequence) < 1) {
1003 av_log(c, AV_LOG_ERROR, "Invalid second level segment filename template '%s', "
1004 "you can try to remove second_level_segment_index flag\n",
1005 oc->url);
1006 av_freep(&filename);
1007 return AVERROR(EINVAL);
1008 }
1009 ff_format_set_url(oc, filename);
1010 }
1011 if (c->flags & (HLS_SECOND_LEVEL_SEGMENT_SIZE | HLS_SECOND_LEVEL_SEGMENT_DURATION)) {
1012 av_strlcpy(vs->current_segment_final_filename_fmt, oc->url,
1013 sizeof(vs->current_segment_final_filename_fmt));
1014 if (c->flags & HLS_SECOND_LEVEL_SEGMENT_SIZE) {
1015 char *filename = NULL;
1016 if (replace_int_data_in_filename(&filename, oc->url, 's', 0) < 1) {
1017 av_log(c, AV_LOG_ERROR, "Invalid second level segment filename template '%s', "
1018 "you can try to remove second_level_segment_size flag\n",
1019 oc->url);
1020 av_freep(&filename);
1021 return AVERROR(EINVAL);
1022 }
1023 ff_format_set_url(oc, filename);
1024 }
1025 if (c->flags & HLS_SECOND_LEVEL_SEGMENT_DURATION) {
1026 char *filename = NULL;
1027 if (replace_int_data_in_filename(&filename, oc->url, 't', 0) < 1) {
1028 av_log(c, AV_LOG_ERROR, "Invalid second level segment filename template '%s', "
1029 "you can try to remove second_level_segment_duration flag\n",
1030 oc->url);
1031 av_freep(&filename);
1032 return AVERROR(EINVAL);
1033 }
1034 ff_format_set_url(oc, filename);
1035 }
1036 }
1037 return 0;
1038 }
1039
1040 /* Create a new segment and append it to the segment list */
1041 106 static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls,
1042 VariantStream *vs, double duration, int64_t pos,
1043 int64_t size)
1044 {
1045 106 HLSSegment en0 = {0};
1046 106 HLSSegment *en = &en0;
1047 const char *filename;
1048
4/4
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 80 times.
106 int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0);
1049 int ret;
1050 AVBPrint bp;
1051
1052 106 vs->total_size += size;
1053 106 vs->total_duration += duration;
1054
1/2
✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
106 if (duration > 0.5) {
1055 // Don't include the final, possibly very short segment in the
1056 // calculation of the max bitrate.
1057 106 int cur_bitrate = (int)(8 * size / duration);
1058
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 48 times.
106 if (cur_bitrate > vs->max_bitrate)
1059 58 vs->max_bitrate = cur_bitrate;
1060 }
1061
1/2
✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
106 if (vs->total_duration > 0)
1062 106 vs->avg_bitrate = (int)(8 * vs->total_size / vs->total_duration);
1063
1064 106 en->var_stream_idx = vs->var_stream_idx;
1065 106 ret = sls_flags_filename_process(s, hls, vs, duration, pos, size);
1066
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
106 if (ret < 0)
1067 return ret;
1068
1069 106 filename = av_basename(vs->avf->url);
1070
1071
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
106 if (hls->use_localtime_mkdir) {
1072 filename = vs->avf->url;
1073 }
1074
4/6
✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 87 times.
✓ Branch 4 taken 19 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 87 times.
106 if (vs->nb_entries <= 5000 && (find_segment_by_filename(vs->segments, filename) || find_segment_by_filename(vs->old_segments, filename))
1075
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 && !byterange_mode) {
1076 av_log(hls, AV_LOG_WARNING, "Duplicated segment filename detected: %s\n", filename);
1077 }
1078
1079 106 av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
1080 106 av_bprintf(&bp, "%s%c", filename, 0);
1081
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
106 av_bprintf(&bp, "%s%c", vs->has_subtitle ? av_basename(vs->vtt_avf->url) : "", 0);
1082
1083 106 en->duration = duration;
1084 106 en->pos = pos;
1085 106 en->size = size;
1086
1087
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 105 times.
106 if (vs->discontinuity) {
1088 1 en->discont = 1;
1089 1 vs->discontinuity = 0;
1090 }
1091
1092
2/4
✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 106 times.
106 if (hls->key_info_file || hls->encrypt) {
1093 av_bprintf(&bp, "%s%c", vs->key_uri, 0);
1094 av_strlcpy(en->iv_string, vs->iv_string, sizeof(en->iv_string));
1095 } else {
1096 106 av_bprint_chars(&bp, 0, 1);
1097 }
1098
1099 106 en = ff_bprint_finalize_as_fam(&bp, &en0, offsetof(HLSSegment, buf));
1100
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
106 if (!en)
1101 return AVERROR(ENOMEM);
1102 #define NEXT(s) ((s) + strlen(s) + 1)
1103 106 en->filename = en->buf;
1104 106 en->sub_filename = NEXT(en->filename);
1105 106 en->key_uri = NEXT(en->sub_filename);
1106 #undef NEXT
1107
1108
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 84 times.
106 if (!vs->segments)
1109 22 vs->segments = en;
1110 else
1111 84 vs->last_segment->next = en;
1112
1113 106 vs->last_segment = en;
1114
1115 // EVENT or VOD playlists imply sliding window cannot be used
1116
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 93 times.
106 if (hls->pl_type != PLAYLIST_TYPE_NONE)
1117 13 hls->max_nb_segments = 0;
1118
1119
4/4
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 75 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 23 times.
106 if (hls->max_nb_segments && vs->nb_entries >= hls->max_nb_segments) {
1120 8 en = vs->segments;
1121
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 if (!en->next->discont_program_date_time && !en->discont_program_date_time)
1122 8 vs->initial_prog_date_time += en->duration;
1123 8 vs->segments = en->next;
1124
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if (en && hls->flags & HLS_DELETE_SEGMENTS &&
1125 !(hls->flags & HLS_SINGLE_FILE)) {
1126 en->next = vs->old_segments;
1127 vs->old_segments = en;
1128 if ((ret = hls_delete_old_segments(s, hls, vs)) < 0)
1129 return ret;
1130 } else
1131 8 av_freep(&en);
1132 } else
1133 98 vs->nb_entries++;
1134
1135
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 96 times.
106 if (hls->max_seg_size > 0) {
1136 10 return 0;
1137 }
1138 96 vs->sequence++;
1139
1140 96 return 0;
1141 }
1142
1143 static int extract_segment_number(const char *filename) {
1144 const char *dot = strrchr(filename, '.');
1145 const char *num_start = dot - 1;
1146
1147 while (num_start > filename && *num_start >= '0' && *num_start <= '9') {
1148 num_start--;
1149 }
1150
1151 num_start++;
1152
1153 if (num_start == dot)
1154 return -1;
1155
1156 return atoi(num_start);
1157 }
1158
1159 1 static int parse_playlist(AVFormatContext *s, const char *url, VariantStream *vs)
1160 {
1161 1 HLSContext *hls = s->priv_data;
1162 AVIOContext *in;
1163 1 int ret = 0, is_segment = 0;
1164 int64_t new_start_pos;
1165 char line[MAX_URL_SIZE];
1166 const char *ptr;
1167 const char *end;
1168 1 double discont_program_date_time = 0;
1169
1170
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if ((ret = s->io_open(s, &in, url, AVIO_FLAG_READ, NULL)) < 0)
1171 return ret;
1172
1173 1 ff_get_chomp_line(in, line, sizeof(line));
1174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (strcmp(line, "#EXTM3U")) {
1175 ret = AVERROR_INVALIDDATA;
1176 goto fail;
1177 }
1178
1179 1 vs->discontinuity = 0;
1180
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 1 times.
11 while (!avio_feof(in)) {
1181 10 ff_get_chomp_line(in, line, sizeof(line));
1182
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 9 times.
10 if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
1183 1 int64_t tmp_sequence = strtoll(ptr, NULL, 10);
1184
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (tmp_sequence < vs->sequence)
1185 av_log(hls, AV_LOG_VERBOSE,
1186 "Found playlist sequence number was smaller """
1187 "than specified start sequence number: %"PRId64" < %"PRId64", "
1188 "omitting\n", tmp_sequence, hls->start_sequence);
1189 else {
1190 1 av_log(hls, AV_LOG_DEBUG, "Found playlist sequence number: %"PRId64"\n", tmp_sequence);
1191 1 vs->sequence = tmp_sequence;
1192 }
1193
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 } else if (av_strstart(line, "#EXT-X-DISCONTINUITY", &ptr)) {
1194 is_segment = 1;
1195 vs->discontinuity = 1;
1196
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 7 times.
9 } else if (av_strstart(line, "#EXTINF:", &ptr)) {
1197 2 is_segment = 1;
1198 2 vs->duration = atof(ptr);
1199
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 } else if (av_stristart(line, "#EXT-X-KEY:", &ptr)) {
1200 ptr = av_stristr(line, "URI=\"");
1201 if (ptr) {
1202 ptr += strlen("URI=\"");
1203 end = av_stristr(ptr, ",");
1204 if (end) {
1205 av_strlcpy(vs->key_uri, ptr, end - ptr);
1206 } else {
1207 av_strlcpy(vs->key_uri, ptr, sizeof(vs->key_uri));
1208 }
1209 }
1210
1211 ptr = av_stristr(line, "IV=0x");
1212 if (ptr) {
1213 ptr += strlen("IV=0x");
1214 end = av_stristr(ptr, ",");
1215 if (end) {
1216 av_strlcpy(vs->iv_string, ptr, end - ptr);
1217 } else {
1218 av_strlcpy(vs->iv_string, ptr, sizeof(vs->iv_string));
1219 }
1220 }
1221
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 } else if (av_strstart(line, "#EXT-X-PROGRAM-DATE-TIME:", &ptr)) {
1222 struct tm program_date_time = { 0 };
1223 double ms = 0;
1224 char *q = av_small_strptime(ptr, "%Y-%m-%dT%H:%M:%S", &program_date_time);
1225
1226 if (!q) {
1227 ret = AVERROR_INVALIDDATA;
1228 goto fail;
1229 }
1230 if (*q == '.')
1231 ms = atof(q + 1);
1232 program_date_time.tm_isdst = -1;
1233
1234 errno = 0;
1235 time_t t = mktime(&program_date_time);
1236 if (t == (time_t)-1 && errno == EOVERFLOW) {
1237 ret = AVERROR_INVALIDDATA;
1238 goto fail;
1239 }
1240 discont_program_date_time = t;
1241
1242 discont_program_date_time += (double)(ms / 1000);
1243
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 3 times.
7 } else if (av_strstart(line, "#", NULL)) {
1244 4 continue;
1245
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 } else if (line[0]) {
1246
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (is_segment) {
1247 2 char *new_file = av_strdup(line);
1248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!new_file) {
1249 ret = AVERROR(ENOMEM);
1250 goto fail;
1251 }
1252 2 ff_format_set_url(vs->avf, new_file);
1253
1254
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (vs->has_subtitle) {
1255 int vtt_index = extract_segment_number(line);
1256 const char *vtt_basename = av_basename(vs->vtt_basename);
1257 char *vtt_file = NULL;
1258 ret = replace_int_data_in_filename(&vtt_file, vtt_basename, 'd', vtt_index);
1259 if (ret < 0 || !vtt_file) {
1260 ret = AVERROR(ENOMEM);
1261 goto fail;
1262 }
1263
1264 ff_format_set_url(vs->vtt_avf, vtt_file);
1265 }
1266
1267 2 is_segment = 0;
1268 2 new_start_pos = avio_tell(vs->avf->pb);
1269 2 vs->size = new_start_pos - vs->start_pos;
1270 2 ret = hls_append_segment(s, hls, vs, vs->duration, vs->start_pos, vs->size);
1271
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (discont_program_date_time) {
1272 vs->last_segment->discont_program_date_time = discont_program_date_time;
1273 discont_program_date_time += vs->duration;
1274 }
1275
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ret < 0)
1276 goto fail;
1277 2 vs->start_pos = new_start_pos;
1278 }
1279 }
1280 }
1281
1282 1 fail:
1283 1 ff_format_io_close(s, &in);
1284 1 return ret;
1285 }
1286
1287 44 static void hls_free_segments(HLSSegment *p)
1288 {
1289 HLSSegment *en;
1290
1291
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 44 times.
142 while (p) {
1292 98 en = p;
1293 98 p = p->next;
1294 98 av_freep(&en);
1295 }
1296 44 }
1297
1298 static int hls_rename_temp_file(AVFormatContext *s, AVFormatContext *oc)
1299 {
1300 size_t len = strlen(oc->url);
1301 char *final_filename = av_strdup(oc->url);
1302 int ret;
1303
1304 if (!final_filename)
1305 return AVERROR(ENOMEM);
1306 final_filename[len-4] = '\0';
1307 ret = ff_rename(oc->url, final_filename, s);
1308 oc->url[len-4] = '\0';
1309 av_freep(&final_filename);
1310 return ret;
1311 }
1312
1313 9 static const char* get_relative_url(const char *master_url, const char *media_url)
1314 {
1315 9 const char *p = strrchr(master_url, '/');
1316 9 size_t base_len = 0;
1317
1318
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (!p) p = strrchr(master_url, '\\');
1319
1320
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 if (p) {
1321 9 base_len = p - master_url;
1322
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 if (av_strncasecmp(master_url, media_url, base_len)) {
1323 av_log(NULL, AV_LOG_WARNING, "Unable to find relative url\n");
1324 return NULL;
1325 }
1326 } else {
1327 return media_url;
1328 }
1329
1330 9 return media_url + base_len + 1;
1331 }
1332
1333 3 static int64_t get_stream_bit_rate(AVStream *stream)
1334 {
1335 3 const AVPacketSideData *sd = av_packet_side_data_get(
1336 3 stream->codecpar->coded_side_data, stream->codecpar->nb_coded_side_data,
1337 AV_PKT_DATA_CPB_PROPERTIES
1338 );
1339
1340
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (stream->codecpar->bit_rate)
1341 3 return stream->codecpar->bit_rate;
1342 else if (sd) {
1343 AVCPBProperties *props = (AVCPBProperties*)sd->data;
1344 return props->max_bitrate;
1345 }
1346
1347 return 0;
1348 }
1349
1350 6 static int create_master_playlist(AVFormatContext *s,
1351 VariantStream * const input_vs,
1352 int final)
1353 {
1354 6 HLSContext *hls = s->priv_data;
1355 VariantStream *vs, *temp_vs;
1356 AVStream *vid_st, *aud_st;
1357 6 AVDictionary *options = NULL;
1358 unsigned int i, j;
1359 int ret, bandwidth, avg_bandwidth;
1360 6 const char *m3u8_rel_name = NULL;
1361 6 const char *vtt_m3u8_rel_name = NULL;
1362 const char *ccgroup;
1363 6 const char *sgroup = NULL;
1364 ClosedCaptionsStream *ccs;
1365 6 const char *proto = avio_find_protocol_name(hls->master_m3u8_url);
1366
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 int is_file_proto = proto && !strcmp(proto, "file");
1367
3/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
6 int use_temp_file = is_file_proto && ((hls->flags & HLS_TEMP_FILE) || hls->master_publish_rate);
1368 char temp_filename[MAX_URL_SIZE];
1369 int nb_channels;
1370
1371 6 input_vs->m3u8_created = 1;
1372
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 if (!hls->master_m3u8_created) {
1373 /* For the first time, wait until all the media playlists are created */
1374
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 for (i = 0; i < hls->nb_varstreams; i++)
1375
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (!hls->var_streams[i].m3u8_created)
1376 1 return 0;
1377 } else {
1378 /* Keep publishing the master playlist at the configured rate */
1379
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
4 if ((&hls->var_streams[0] != input_vs || !hls->master_publish_rate ||
1380
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
4 input_vs->number % hls->master_publish_rate) && !final)
1381 2 return 0;
1382 }
1383
1384 3 set_http_options(s, &options, hls);
1385
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 snprintf(temp_filename, sizeof(temp_filename), use_temp_file ? "%s.tmp" : "%s", hls->master_m3u8_url);
1386 3 ret = hlsenc_io_open(s, &hls->m3u8_out, temp_filename, &options);
1387 3 av_dict_free(&options);
1388
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (ret < 0) {
1389 av_log(s, AV_LOG_ERROR, "Failed to open master play list file '%s'\n",
1390 temp_filename);
1391 goto fail;
1392 }
1393
1394 3 ff_hls_write_playlist_version(hls->m3u8_out, hls->version);
1395
1396
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 for (i = 0; i < hls->nb_ccstreams; i++) {
1397 ccs = &(hls->cc_streams[i]);
1398 avio_printf(hls->m3u8_out, "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS");
1399 avio_printf(hls->m3u8_out, ",GROUP-ID=\"%s\"", ccs->ccgroup);
1400 avio_printf(hls->m3u8_out, ",NAME=\"%s\"", ccs->instreamid);
1401 if (ccs->language)
1402 avio_printf(hls->m3u8_out, ",LANGUAGE=\"%s\"", ccs->language);
1403 avio_printf(hls->m3u8_out, ",INSTREAM-ID=\"%s\"\n", ccs->instreamid);
1404 }
1405
1406 /* For audio only variant streams add #EXT-X-MEDIA tag with attributes*/
1407
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 for (i = 0; i < hls->nb_varstreams; i++) {
1408 6 vs = &(hls->var_streams[i]);
1409
1410
4/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
6 if (vs->has_video || vs->has_subtitle || !vs->agroup)
1411 3 continue;
1412
1413 3 m3u8_rel_name = get_relative_url(hls->master_m3u8_url, vs->m3u8_name);
1414
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!m3u8_rel_name) {
1415 av_log(s, AV_LOG_ERROR, "Unable to find relative URL\n");
1416 goto fail;
1417 }
1418 3 nb_channels = 0;
1419
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 for (j = 0; j < vs->nb_streams; j++)
1420
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
1421
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (vs->streams[j]->codecpar->ch_layout.nb_channels > nb_channels)
1422 3 nb_channels = vs->streams[j]->codecpar->ch_layout.nb_channels;
1423
1424
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 ff_hls_write_audio_rendition(hls->m3u8_out, vs->agroup, m3u8_rel_name, vs->language, i, hls->has_default_key ? vs->is_default : 1, nb_channels);
1425 }
1426
1427 /* For variant streams with video add #EXT-X-STREAM-INF tag with attributes*/
1428
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 for (i = 0; i < hls->nb_varstreams; i++) {
1429 6 vs = &(hls->var_streams[i]);
1430
1431 6 m3u8_rel_name = get_relative_url(hls->master_m3u8_url, vs->m3u8_name);
1432
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!m3u8_rel_name) {
1433 av_log(s, AV_LOG_ERROR, "Unable to find relative URL\n");
1434 goto fail;
1435 }
1436
1437 6 vid_st = NULL;
1438 6 aud_st = NULL;
1439
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 for (j = 0; j < vs->nb_streams; j++) {
1440
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
1441 3 vid_st = vs->streams[j];
1442
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 else if (vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
1443 3 aud_st = vs->streams[j];
1444 }
1445
1446
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
6 if (!vid_st && !aud_st) {
1447 av_log(s, AV_LOG_WARNING, "Media stream not found\n");
1448 continue;
1449 }
1450
1451 /**
1452 * Traverse through the list of audio only rendition streams and find
1453 * the rendition which has highest bitrate in the same audio group
1454 */
1455
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (vs->agroup) {
1456
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
18 for (j = 0; j < hls->nb_varstreams; j++) {
1457 12 temp_vs = &(hls->var_streams[j]);
1458
3/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
12 if (!temp_vs->has_video && !temp_vs->has_subtitle &&
1459
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
12 temp_vs->agroup &&
1460 6 !av_strcasecmp(temp_vs->agroup, vs->agroup)) {
1461
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (!aud_st)
1462 3 aud_st = temp_vs->streams[0];
1463 6 if (temp_vs->streams[0]->codecpar->bit_rate >
1464
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 aud_st->codecpar->bit_rate)
1465 aud_st = temp_vs->streams[0];
1466 }
1467 }
1468 }
1469
1470
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 if (final) {
1471 4 bandwidth = vs->max_bitrate;
1472 4 avg_bandwidth = vs->avg_bitrate;
1473 } else {
1474 2 bandwidth = 0;
1475 2 avg_bandwidth = 0;
1476
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (vid_st)
1477 1 bandwidth += get_stream_bit_rate(vid_st);
1478
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (aud_st)
1479 2 bandwidth += get_stream_bit_rate(aud_st);
1480 2 bandwidth += bandwidth / 10;
1481 }
1482
1483 6 ccgroup = NULL;
1484
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
6 if (vid_st && vs->ccgroup) {
1485 /* check if this group name is available in the cc map string */
1486 for (j = 0; j < hls->nb_ccstreams; j++) {
1487 ccs = &(hls->cc_streams[j]);
1488 if (!av_strcasecmp(ccs->ccgroup, vs->ccgroup)) {
1489 ccgroup = vs->ccgroup;
1490 break;
1491 }
1492 }
1493 if (j == hls->nb_ccstreams)
1494 av_log(s, AV_LOG_WARNING, "mapping ccgroup %s not found\n",
1495 vs->ccgroup);
1496 }
1497
1498
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
6 if (vid_st && vs->sgroup) {
1499 sgroup = vs->sgroup;
1500 vtt_m3u8_rel_name = get_relative_url(hls->master_m3u8_url, vs->vtt_m3u8_name);
1501 if (!vtt_m3u8_rel_name) {
1502 av_log(s, AV_LOG_WARNING, "Unable to find relative subtitle URL\n");
1503 break;
1504 }
1505
1506 ff_hls_write_subtitle_rendition(hls->m3u8_out, sgroup, vtt_m3u8_rel_name, vs->language,
1507 vs->subtitle_varname, i, hls->has_default_key ? vs->is_default : 1);
1508 }
1509
1510
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if (!hls->has_default_key || !hls->has_video_m3u8) {
1511 ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, avg_bandwidth, m3u8_rel_name,
1512 aud_st ? vs->agroup : NULL, vs->codec_attr, ccgroup, sgroup);
1513 } else {
1514
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (vid_st) {
1515 3 ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, avg_bandwidth, m3u8_rel_name,
1516
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 aud_st ? vs->agroup : NULL, vs->codec_attr, ccgroup, sgroup);
1517 }
1518 }
1519 }
1520 3 fail:
1521
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (ret >=0)
1522 3 hls->master_m3u8_created = 1;
1523 3 hlsenc_io_close(s, &hls->m3u8_out, temp_filename);
1524
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (use_temp_file)
1525 ff_rename(temp_filename, hls->master_m3u8_url, s);
1526
1527 3 return ret;
1528 }
1529
1530 102 static int hls_window(AVFormatContext *s, int last, VariantStream *vs)
1531 {
1532 102 HLSContext *hls = s->priv_data;
1533 HLSSegment *en;
1534 102 int target_duration = 0;
1535 102 int ret = 0;
1536 char temp_filename[MAX_URL_SIZE];
1537 char temp_vtt_filename[MAX_URL_SIZE];
1538 102 int64_t sequence = FFMAX(hls->start_sequence, vs->sequence - vs->nb_entries);
1539 102 const char *proto = avio_find_protocol_name(vs->m3u8_name);
1540
2/4
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 102 times.
✗ Branch 3 not taken.
102 int is_file_proto = proto && !strcmp(proto, "file");
1541
4/6
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 102 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 101 times.
✓ Branch 5 taken 1 times.
102 int use_temp_file = is_file_proto && ((hls->flags & HLS_TEMP_FILE) || !(hls->pl_type == PLAYLIST_TYPE_VOD));
1542 static unsigned warned_non_file;
1543 102 const char *key_uri = NULL;
1544 102 char *iv_string = NULL;
1545 102 AVDictionary *options = NULL;
1546 102 double prog_date_time = vs->initial_prog_date_time;
1547
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 double *prog_date_time_p = (hls->flags & HLS_PROGRAM_DATE_TIME) ? &prog_date_time : NULL;
1548
4/4
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 76 times.
102 int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0);
1549
1550 102 hls->version = 2;
1551
2/2
✓ Branch 0 taken 99 times.
✓ Branch 1 taken 3 times.
102 if (!(hls->flags & HLS_ROUND_DURATIONS)) {
1552 99 hls->version = 3;
1553 }
1554
1555
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 76 times.
102 if (byterange_mode) {
1556 26 hls->version = 4;
1557 26 sequence = 0;
1558 }
1559
1560
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 96 times.
102 if (hls->flags & HLS_I_FRAMES_ONLY) {
1561 6 hls->version = 4;
1562 }
1563
1564
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 99 times.
102 if (hls->flags & HLS_INDEPENDENT_SEGMENTS) {
1565 3 hls->version = 6;
1566 }
1567
1568
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 93 times.
102 if (hls->segment_type == SEGMENT_TYPE_FMP4) {
1569 9 hls->version = 7;
1570 }
1571
1572
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
102 if (!is_file_proto && (hls->flags & HLS_TEMP_FILE) && !warned_non_file++)
1573 av_log(s, AV_LOG_ERROR, "Cannot use rename on non file protocol, this may lead to races and temporary partial files\n");
1574
1575 102 set_http_options(s, &options, hls);
1576
2/2
✓ Branch 0 taken 101 times.
✓ Branch 1 taken 1 times.
102 snprintf(temp_filename, sizeof(temp_filename), use_temp_file ? "%s.tmp" : "%s", vs->m3u8_name);
1577
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 76 times.
102 ret = hlsenc_io_open(s, byterange_mode ? &hls->m3u8_out : &vs->out, temp_filename, &options);
1578 102 av_dict_free(&options);
1579
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 if (ret < 0) {
1580 goto fail;
1581 }
1582
1583
2/2
✓ Branch 0 taken 355 times.
✓ Branch 1 taken 102 times.
457 for (en = vs->segments; en; en = en->next) {
1584
2/2
✓ Branch 0 taken 270 times.
✓ Branch 1 taken 85 times.
355 if (target_duration <= en->duration)
1585 270 target_duration = lrint(en->duration);
1586 }
1587
1588 102 vs->discontinuity_set = 0;
1589 102 ff_hls_write_playlist_header(byterange_mode ? hls->m3u8_out : vs->out, hls->version, hls->allowcache,
1590
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 76 times.
102 target_duration, sequence, hls->pl_type, hls->flags & HLS_I_FRAMES_ONLY);
1591
1592
4/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 99 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
102 if ((hls->flags & HLS_DISCONT_START) && sequence==hls->start_sequence && vs->discontinuity_set==0) {
1593
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 avio_printf(byterange_mode ? hls->m3u8_out : vs->out, "#EXT-X-DISCONTINUITY\n");
1594 3 vs->discontinuity_set = 1;
1595 }
1596
4/4
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 77 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 22 times.
102 if (vs->has_video && (hls->flags & HLS_INDEPENDENT_SEGMENTS)) {
1597
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 avio_printf(byterange_mode ? hls->m3u8_out : vs->out, "#EXT-X-INDEPENDENT-SEGMENTS\n");
1598 }
1599
2/2
✓ Branch 0 taken 355 times.
✓ Branch 1 taken 102 times.
457 for (en = vs->segments; en; en = en->next) {
1600
2/10
✓ Branch 0 taken 355 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 355 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
355 if ((hls->encrypt || hls->key_info_file) && (!key_uri || strcmp(en->key_uri, key_uri) ||
1601 av_strcasecmp(en->iv_string, iv_string))) {
1602 avio_printf(byterange_mode ? hls->m3u8_out : vs->out, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"", en->key_uri);
1603 if (*en->iv_string)
1604 avio_printf(byterange_mode ? hls->m3u8_out : vs->out, ",IV=0x%s", en->iv_string);
1605 avio_printf(byterange_mode ? hls->m3u8_out : vs->out, "\n");
1606 key_uri = en->key_uri;
1607 iv_string = en->iv_string;
1608 }
1609
1610
4/4
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 333 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 13 times.
355 if ((hls->segment_type == SEGMENT_TYPE_FMP4) && (en == vs->segments)) {
1611
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 6 times.
9 ff_hls_write_init_file(byterange_mode ? hls->m3u8_out : vs->out, (hls->flags & HLS_SINGLE_FILE) ? en->filename : vs->fmp4_init_filename,
1612
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 6 times.
9 hls->flags & HLS_SINGLE_FILE, vs->init_range_length, 0);
1613 }
1614
1615 710 ret = ff_hls_write_file_entry(byterange_mode ? hls->m3u8_out : vs->out, en->discont, byterange_mode,
1616 355 en->duration, hls->flags & HLS_ROUND_DURATIONS,
1617
2/2
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 233 times.
355 en->size, en->pos, hls->baseurl,
1618 en->filename,
1619 355 en->discont_program_date_time ? &en->discont_program_date_time : prog_date_time_p,
1620
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 355 times.
355 hls->flags & HLS_I_FRAMES_ONLY);
1621
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 355 times.
355 if (en->discont_program_date_time)
1622 en->discont_program_date_time -= en->duration;
1623
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 355 times.
355 if (ret < 0) {
1624 av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n");
1625 }
1626 }
1627
1628
4/4
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 80 times.
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 2 times.
102 if (last && (hls->flags & HLS_OMIT_ENDLIST)==0)
1629
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 16 times.
20 ff_hls_write_end_list(byterange_mode ? hls->m3u8_out : vs->out);
1630
1631
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 if (vs->vtt_m3u8_name) {
1632 set_http_options(vs->vtt_avf, &options, hls);
1633 snprintf(temp_vtt_filename, sizeof(temp_vtt_filename), use_temp_file ? "%s.tmp" : "%s", vs->vtt_m3u8_name);
1634 ret = hlsenc_io_open(s, &hls->sub_m3u8_out, temp_vtt_filename, &options);
1635 av_dict_free(&options);
1636 if (ret < 0) {
1637 goto fail;
1638 }
1639 ff_hls_write_playlist_header(hls->sub_m3u8_out, hls->version, hls->allowcache,
1640 target_duration, sequence, PLAYLIST_TYPE_NONE, 0);
1641 for (en = vs->segments; en; en = en->next) {
1642 ret = ff_hls_write_file_entry(hls->sub_m3u8_out, en->discont, byterange_mode,
1643 en->duration, 0, en->size, en->pos,
1644 hls->baseurl, en->sub_filename, NULL, 0);
1645 if (ret < 0) {
1646 av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n");
1647 }
1648 }
1649
1650 if (last && !(hls->flags & HLS_OMIT_ENDLIST))
1651 ff_hls_write_end_list(hls->sub_m3u8_out);
1652
1653 }
1654
1655 102 fail:
1656 102 av_dict_free(&options);
1657
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 76 times.
102 ret = hlsenc_io_close(s, byterange_mode ? &hls->m3u8_out : &vs->out, temp_filename);
1658
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 if (ret < 0) {
1659 return ret;
1660 }
1661 102 hlsenc_io_close(s, &hls->sub_m3u8_out, vs->vtt_m3u8_name);
1662
2/2
✓ Branch 0 taken 101 times.
✓ Branch 1 taken 1 times.
102 if (use_temp_file) {
1663 101 ff_rename(temp_filename, vs->m3u8_name, s);
1664
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 101 times.
101 if (vs->vtt_m3u8_name)
1665 ff_rename(temp_vtt_filename, vs->vtt_m3u8_name, s);
1666 }
1667
3/4
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 96 times.
102 if (ret >= 0 && hls->master_pl_name)
1668
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (create_master_playlist(s, vs, last) < 0)
1669 av_log(s, AV_LOG_WARNING, "Master playlist creation failed\n");
1670
1671 102 return ret;
1672 }
1673
1674 85 static int hls_start(AVFormatContext *s, VariantStream *vs)
1675 {
1676 85 HLSContext *c = s->priv_data;
1677 85 AVFormatContext *oc = vs->avf;
1678 85 AVFormatContext *vtt_oc = vs->vtt_avf;
1679 85 AVDictionary *options = NULL;
1680 85 const char *proto = NULL;
1681 85 int use_temp_file = 0;
1682 char iv_string[KEYSIZE*2 + 1];
1683 85 int err = 0;
1684
1685
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 82 times.
85 if (c->flags & HLS_SINGLE_FILE) {
1686 3 char *new_name = av_strdup(vs->basename);
1687
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!new_name)
1688 return AVERROR(ENOMEM);
1689 3 ff_format_set_url(oc, new_name);
1690
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (vs->vtt_basename) {
1691 new_name = av_strdup(vs->vtt_basename);
1692 if (!new_name)
1693 return AVERROR(ENOMEM);
1694 ff_format_set_url(vtt_oc, new_name);
1695 }
1696
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 78 times.
82 } else if (c->max_seg_size > 0) {
1697 4 char *filename = NULL;
1698
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (replace_int_data_in_filename(&filename,
1699 4 vs->basename, 'd', vs->sequence) < 1) {
1700 av_freep(&filename);
1701 av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s', you can try to use -strftime 1 with it\n", vs->basename);
1702 return AVERROR(EINVAL);
1703 }
1704 4 ff_format_set_url(oc, filename);
1705 } else {
1706
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if (c->use_localtime) {
1707 int r;
1708 char *expanded = NULL;
1709
1710 r = strftime_expand(vs->basename, &expanded);
1711 if (r < 0) {
1712 av_log(oc, AV_LOG_ERROR, "Could not get segment filename with strftime\n");
1713 return r;
1714 }
1715 ff_format_set_url(oc, expanded);
1716
1717 err = sls_flag_use_localtime_filename(oc, c, vs);
1718 if (err < 0) {
1719 return AVERROR(ENOMEM);
1720 }
1721
1722 if (c->use_localtime_mkdir) {
1723 const char *dir;
1724 char *fn_copy = av_strdup(oc->url);
1725 if (!fn_copy)
1726 return AVERROR(ENOMEM);
1727 dir = av_dirname(fn_copy);
1728 if (ff_mkdir_p(dir) == -1 && errno != EEXIST) {
1729 av_log(oc, AV_LOG_ERROR, "Could not create directory %s with use_localtime_mkdir\n", dir);
1730 av_freep(&fn_copy);
1731 return AVERROR(errno);
1732 }
1733 av_freep(&fn_copy);
1734 }
1735 } else {
1736 78 char *filename = NULL;
1737
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if (replace_int_data_in_filename(&filename,
1738 78 vs->basename, 'd', vs->sequence) < 1) {
1739 av_freep(&filename);
1740 av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s' you can try to use -strftime 1 with it\n", vs->basename);
1741 return AVERROR(EINVAL);
1742 }
1743 78 ff_format_set_url(oc, filename);
1744 }
1745
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if (vs->vtt_basename) {
1746 char *filename = NULL;
1747 if (replace_int_data_in_filename(&filename,
1748 vs->vtt_basename, 'd', vs->sequence) < 1) {
1749 av_freep(&filename);
1750 av_log(vtt_oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", vs->vtt_basename);
1751 return AVERROR(EINVAL);
1752 }
1753 ff_format_set_url(vtt_oc, filename);
1754 }
1755 }
1756
1757 85 proto = avio_find_protocol_name(oc->url);
1758
3/6
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 85 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 85 times.
85 use_temp_file = proto && !strcmp(proto, "file") && (c->flags & HLS_TEMP_FILE);
1759
1760
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 85 times.
85 if (use_temp_file) {
1761 char *new_name = av_asprintf("%s.tmp", oc->url);
1762 if (!new_name)
1763 return AVERROR(ENOMEM);
1764 ff_format_set_url(oc, new_name);
1765 }
1766
1767
2/4
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 85 times.
85 if (c->key_info_file || c->encrypt) {
1768 if (c->segment_type == SEGMENT_TYPE_FMP4) {
1769 av_log(s, AV_LOG_ERROR, "Encrypted fmp4 not yet supported\n");
1770 return AVERROR_PATCHWELCOME;
1771 }
1772
1773 if (c->key_info_file && c->encrypt) {
1774 av_log(s, AV_LOG_WARNING, "Cannot use both -hls_key_info_file and -hls_enc,"
1775 " ignoring -hls_enc\n");
1776 }
1777
1778 if (!vs->encrypt_started || (c->flags & HLS_PERIODIC_REKEY)) {
1779 if (c->key_info_file) {
1780 if ((err = hls_encryption_start(s, vs)) < 0)
1781 goto fail;
1782 } else {
1783 if (!c->encrypt_started) {
1784 if ((err = do_encrypt(s, vs)) < 0)
1785 goto fail;
1786 c->encrypt_started = 1;
1787 }
1788 av_strlcpy(vs->key_uri, c->key_uri, sizeof(vs->key_uri));
1789 av_strlcpy(vs->key_string, c->key_string, sizeof(vs->key_string));
1790 av_strlcpy(vs->iv_string, c->iv_string, sizeof(vs->iv_string));
1791 }
1792 vs->encrypt_started = 1;
1793 }
1794 err = av_strlcpy(iv_string, vs->iv_string, sizeof(iv_string));
1795 if (!err) {
1796 snprintf(iv_string, sizeof(iv_string), "%032"PRIx64, vs->sequence);
1797 memset(vs->iv_string, 0, sizeof(vs->iv_string));
1798 memcpy(vs->iv_string, iv_string, sizeof(iv_string));
1799 }
1800 }
1801
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 7 times.
85 if (c->segment_type != SEGMENT_TYPE_FMP4) {
1802
2/4
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
78 if (oc->oformat->priv_class && oc->priv_data) {
1803 78 av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0);
1804 }
1805
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 76 times.
78 if (c->flags & HLS_SINGLE_FILE) {
1806
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (c->key_info_file || c->encrypt) {
1807 av_dict_set(&options, "encryption_key", vs->key_string, 0);
1808 av_dict_set(&options, "encryption_iv", vs->iv_string, 0);
1809
1810 /* Write temp file with cryption content */
1811 av_freep(&vs->basename_tmp);
1812 vs->basename_tmp = av_asprintf("crypto:%s.tmp", oc->url);
1813
1814 /* append temp file content into single file */
1815 av_freep(&vs->basename);
1816 vs->basename = av_asprintf("%s", oc->url);
1817 } else {
1818 2 vs->basename_tmp = vs->basename;
1819 }
1820 2 set_http_options(s, &options, c);
1821
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!vs->out_single_file)
1822
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if ((err = hlsenc_io_open(s, &vs->out_single_file, vs->basename, &options)) < 0) {
1823 if (c->ignore_io_errors)
1824 err = 0;
1825 goto fail;
1826 }
1827
1828
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if ((err = hlsenc_io_open(s, &vs->out, vs->basename_tmp, &options)) < 0) {
1829 if (c->ignore_io_errors)
1830 err = 0;
1831 goto fail;
1832 }
1833
1834 }
1835 }
1836
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 85 times.
85 if (vs->vtt_basename) {
1837 set_http_options(s, &options, c);
1838 if ((err = hlsenc_io_open(s, &vtt_oc->pb, vtt_oc->url, &options)) < 0) {
1839 if (c->ignore_io_errors)
1840 err = 0;
1841 goto fail;
1842 }
1843 }
1844 85 av_dict_free(&options);
1845
1846
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 85 times.
85 if (vs->vtt_basename) {
1847 err = avformat_write_header(vtt_oc,NULL);
1848 if (err < 0)
1849 return err;
1850 }
1851
1852 85 return 0;
1853 fail:
1854 av_dict_free(&options);
1855
1856 return err;
1857 }
1858
1859 static const char * get_default_pattern_localtime_fmt(AVFormatContext *s)
1860 {
1861 HLSContext *hls = s->priv_data;
1862 #if HAVE_LIBC_MSVCRT
1863 // no %s support on MSVC, which invokes the invalid parameter handler
1864 // on unsupported format strings, instead of returning an error
1865 int strftime_s_supported = 0;
1866 #else
1867 char b[21];
1868 time_t t = time(NULL);
1869 struct tm tmbuf, *p = localtime_r(&t, &tmbuf);
1870 // no %s support when strftime returned error or left format string unchanged
1871 int strftime_s_supported = strftime(b, sizeof(b), "%s", p) && strcmp(b, "%s");
1872 #endif
1873
1874 if (hls->segment_type == SEGMENT_TYPE_FMP4) {
1875 return strftime_s_supported ? "-%s.m4s" : "-%Y%m%d%H%M%S.m4s";
1876 }
1877 return strftime_s_supported ? "-%s.ts" : "-%Y%m%d%H%M%S.ts";
1878 }
1879
1880 static int append_postfix(char *name, int name_buf_len, int i)
1881 {
1882 char *p;
1883 char extension[10] = {'\0'};
1884
1885 p = strrchr(name, '.');
1886 if (p) {
1887 av_strlcpy(extension, p, sizeof(extension));
1888 *p = '\0';
1889 }
1890
1891 snprintf(name + strlen(name), name_buf_len - strlen(name), POSTFIX_PATTERN, i);
1892
1893 if (strlen(extension))
1894 av_strlcat(name, extension, name_buf_len);
1895
1896 return 0;
1897 }
1898
1899 45 static int validate_name(int nb_vs, const char *fn)
1900 {
1901 const char *filename, *subdir_name;
1902 45 char *fn_dup = NULL;
1903 45 int ret = 0;
1904
1905
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45 times.
45 if (!fn)
1906 return AVERROR(EINVAL);
1907
1908 45 fn_dup = av_strdup(fn);
1909
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45 times.
45 if (!fn_dup)
1910 return AVERROR(ENOMEM);
1911 45 filename = av_basename(fn);
1912 45 subdir_name = av_dirname(fn_dup);
1913
1914
3/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 43 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
45 if (nb_vs > 1 && !av_stristr(filename, "%v") && !av_stristr(subdir_name, "%v")) {
1915 av_log(NULL, AV_LOG_ERROR, "More than 1 variant streams are present, %%v is expected "
1916 "either in the filename or in the sub-directory name of file %s\n", fn);
1917 ret = AVERROR(EINVAL);
1918 goto fail;
1919 }
1920
1921
3/4
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 2 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
45 if (av_stristr(filename, "%v") && av_stristr(subdir_name, "%v")) {
1922 av_log(NULL, AV_LOG_ERROR, "%%v is expected either in the filename or "
1923 "in the sub-directory name of file %s, but only in one of them\n", fn);
1924 ret = AVERROR(EINVAL);
1925 goto fail;
1926 }
1927
1928 45 fail:
1929 45 av_freep(&fn_dup);
1930 45 return ret;
1931 }
1932
1933 44 static int format_name(const char *buf, char **s, int index, const char *varname)
1934 {
1935 const char *proto, *dir;
1936 44 char *orig_buf_dup = NULL, *mod_buf_dup = NULL;
1937 44 int ret = 0;
1938
1939 44 orig_buf_dup = av_strdup(buf);
1940
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 if (!orig_buf_dup)
1941 return AVERROR(ENOMEM);
1942
1943
2/2
✓ Branch 1 taken 40 times.
✓ Branch 2 taken 4 times.
44 if (!av_stristr(buf, "%v")) {
1944 40 *s = orig_buf_dup;
1945 40 return 0;
1946 }
1947
1948
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (!varname) {
1949
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (replace_int_data_in_filename(s, orig_buf_dup, 'v', index) < 1) {
1950 ret = AVERROR(EINVAL);
1951 goto fail;
1952 }
1953 } else {
1954 if (replace_str_data_in_filename(s, orig_buf_dup, 'v', varname) < 1) {
1955 ret = AVERROR(EINVAL);
1956 goto fail;
1957 }
1958 }
1959
1960 4 proto = avio_find_protocol_name(orig_buf_dup);
1961 4 dir = av_dirname(orig_buf_dup);
1962
1963 /* if %v is present in the file's directory, create sub-directory */
1964
1/6
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
4 if (av_stristr(dir, "%v") && proto && !strcmp(proto, "file")) {
1965 mod_buf_dup = av_strdup(*s);
1966 dir = av_dirname(mod_buf_dup);
1967 if (ff_mkdir_p(dir) == -1 && errno != EEXIST) {
1968 ret = AVERROR(errno);
1969 goto fail;
1970 }
1971 }
1972
1973 4 fail:
1974 4 av_freep(&orig_buf_dup);
1975 4 av_freep(&mod_buf_dup);
1976 4 return ret;
1977 }
1978
1979 2 static int get_nth_codec_stream_index(AVFormatContext *s,
1980 enum AVMediaType codec_type,
1981 int64_t stream_id)
1982 {
1983 unsigned int stream_index, cnt;
1984
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (stream_id < 0 || stream_id > s->nb_streams - 1)
1985 return -1;
1986 2 cnt = 0;
1987
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 for (stream_index = 0; stream_index < s->nb_streams; stream_index++) {
1988
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (s->streams[stream_index]->codecpar->codec_type != codec_type)
1989 1 continue;
1990
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (cnt == stream_id)
1991 2 return stream_index;
1992 cnt++;
1993 }
1994 return -1;
1995 }
1996
1997 1 static int parse_variant_stream_mapstring(AVFormatContext *s)
1998 {
1999 1 HLSContext *hls = s->priv_data;
2000 VariantStream *vs;
2001 int stream_index, i, j;
2002 enum AVMediaType codec_type;
2003 1 int nb_varstreams = 0, nb_streams;
2004 char *p, *q, *saveptr1, *saveptr2, *varstr, *keyval;
2005 const char *val;
2006
2007 /**
2008 * Expected format for var_stream_map string is as below:
2009 * "a:0,v:0 a:1,v:1"
2010 * "a:0,agroup:a0,default:1,language:ENG a:1,agroup:a1,default:0 v:0,agroup:a0 v:1,agroup:a1"
2011 * This string specifies how to group the audio, video and subtitle streams
2012 * into different variant streams. The variant stream groups are separated
2013 * by space.
2014 *
2015 * a:, v:, s: are keys to specify audio, video and subtitle streams
2016 * respectively. Allowed values are 0 to 9 digits (limited just based on
2017 * practical usage)
2018 *
2019 * agroup: is key to specify audio group. A string can be given as value.
2020 * sgroup: is key to specify subtitle group. A string can be given as value.
2021 */
2022 1 p = av_strdup(hls->var_stream_map);
2023
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!p)
2024 return AVERROR(ENOMEM);
2025
2026 1 q = p;
2027
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
3 while (av_strtok(q, " \t", &saveptr1)) {
2028 2 q = NULL;
2029 2 nb_varstreams++;
2030 }
2031 1 av_freep(&p);
2032
2033 1 hls->var_streams = av_mallocz(sizeof(*hls->var_streams) * nb_varstreams);
2034
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!hls->var_streams)
2035 return AVERROR(ENOMEM);
2036 1 hls->nb_varstreams = nb_varstreams;
2037
2038 1 p = hls->var_stream_map;
2039 1 nb_varstreams = 0;
2040
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
3 while (varstr = av_strtok(p, " \t", &saveptr1)) {
2041 2 p = NULL;
2042
2043
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (nb_varstreams < hls->nb_varstreams) {
2044 2 vs = &(hls->var_streams[nb_varstreams]);
2045 2 vs->var_stream_idx = nb_varstreams;
2046 2 vs->is_default = 0;
2047 2 nb_varstreams++;
2048 } else
2049 return AVERROR(EINVAL);
2050
2051 2 q = varstr;
2052 while (1) {
2053
5/6
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
8 if (!av_strncasecmp(q, "a:", 2) || !av_strncasecmp(q, "v:", 2) ||
2054 3 !av_strncasecmp(q, "s:", 2))
2055 2 vs->nb_streams++;
2056 5 q = strchr(q, ',');
2057
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if (!q)
2058 2 break;
2059 3 q++;
2060 }
2061 2 vs->streams = av_mallocz(sizeof(AVStream *) * vs->nb_streams);
2062
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!vs->streams)
2063 return AVERROR(ENOMEM);
2064
2065 2 nb_streams = 0;
2066
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 2 times.
7 while (keyval = av_strtok(varstr, ",", &saveptr2)) {
2067 int64_t num;
2068 char *end;
2069 5 varstr = NULL;
2070
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if (av_strstart(keyval, "language:", &val)) {
2071 vs->language = val;
2072 3 continue;
2073
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
5 } else if (av_strstart(keyval, "default:", &val)) {
2074
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1 vs->is_default = (!av_strncasecmp(val, "YES", strlen("YES")) ||
2075 (!av_strncasecmp(val, "1", strlen("1"))));
2076 1 hls->has_default_key = 1;
2077 1 continue;
2078
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 } else if (av_strstart(keyval, "name:", &val)) {
2079 vs->varname = val;
2080 continue;
2081
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 } else if (av_strstart(keyval, "sname:", &val)) {
2082 vs->subtitle_varname = val;
2083 continue;
2084
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 } else if (av_strstart(keyval, "agroup:", &val)) {
2085 2 vs->agroup = val;
2086 2 continue;
2087
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 } else if (av_strstart(keyval, "sgroup:", &val)) {
2088 vs->sgroup = val;
2089 continue;
2090
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 } else if (av_strstart(keyval, "ccgroup:", &val)) {
2091 vs->ccgroup = val;
2092 continue;
2093
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 } else if (av_strstart(keyval, "v:", &val)) {
2094 1 codec_type = AVMEDIA_TYPE_VIDEO;
2095 1 hls->has_video_m3u8 = 1;
2096
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 } else if (av_strstart(keyval, "a:", &val)) {
2097 1 codec_type = AVMEDIA_TYPE_AUDIO;
2098 } else if (av_strstart(keyval, "s:", &val)) {
2099 codec_type = AVMEDIA_TYPE_SUBTITLE;
2100 } else {
2101 av_log(s, AV_LOG_ERROR, "Invalid keyval %s\n", keyval);
2102 return AVERROR(EINVAL);
2103 }
2104
2105 2 num = strtoll(val, &end, 10);
2106
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!av_isdigit(*val) || *end != '\0') {
2107 av_log(s, AV_LOG_ERROR, "Invalid stream number: '%s'\n", val);
2108 return AVERROR(EINVAL);
2109 }
2110 2 stream_index = get_nth_codec_stream_index(s, codec_type, num);
2111
2112
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (stream_index >= 0 && nb_streams < vs->nb_streams) {
2113
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 for (i = 0; nb_streams > 0 && i < nb_streams; i++) {
2114 if (vs->streams[i] == s->streams[stream_index]) {
2115 av_log(s, AV_LOG_ERROR, "Same elementary stream found more than once inside "
2116 "variant definition #%d\n", nb_varstreams - 1);
2117 return AVERROR(EINVAL);
2118 }
2119 }
2120
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
3 for (j = 0; nb_varstreams > 1 && j < nb_varstreams - 1; j++) {
2121
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (i = 0; i < hls->var_streams[j].nb_streams; i++) {
2122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (hls->var_streams[j].streams[i] == s->streams[stream_index]) {
2123 av_log(s, AV_LOG_ERROR, "Same elementary stream found more than once "
2124 "in two different variant definitions #%d and #%d\n",
2125 j, nb_varstreams - 1);
2126 return AVERROR(EINVAL);
2127 }
2128 }
2129 }
2130 2 vs->streams[nb_streams++] = s->streams[stream_index];
2131 } else {
2132 av_log(s, AV_LOG_ERROR, "Unable to map stream at %s\n", keyval);
2133 return AVERROR(EINVAL);
2134 }
2135 }
2136 }
2137 1 av_log(s, AV_LOG_DEBUG, "Number of variant streams %d\n",
2138 hls->nb_varstreams);
2139
2140 1 return 0;
2141 }
2142
2143 static int parse_cc_stream_mapstring(AVFormatContext *s)
2144 {
2145 HLSContext *hls = s->priv_data;
2146 int nb_ccstreams = 0;
2147 char *p, *q, *ccstr, *keyval;
2148 char *saveptr1 = NULL, *saveptr2 = NULL;
2149 const char *val;
2150 ClosedCaptionsStream *ccs;
2151
2152 p = av_strdup(hls->cc_stream_map);
2153 if(!p)
2154 return AVERROR(ENOMEM);
2155
2156 q = p;
2157 while (av_strtok(q, " \t", &saveptr1)) {
2158 q = NULL;
2159 nb_ccstreams++;
2160 }
2161 av_freep(&p);
2162
2163 hls->cc_streams = av_mallocz(sizeof(*hls->cc_streams) * nb_ccstreams);
2164 if (!hls->cc_streams)
2165 return AVERROR(ENOMEM);
2166 hls->nb_ccstreams = nb_ccstreams;
2167
2168 p = hls->cc_stream_map;
2169 nb_ccstreams = 0;
2170 while (ccstr = av_strtok(p, " \t", &saveptr1)) {
2171 p = NULL;
2172
2173 if (nb_ccstreams < hls->nb_ccstreams)
2174 ccs = &(hls->cc_streams[nb_ccstreams++]);
2175 else
2176 return AVERROR(EINVAL);
2177
2178 while (keyval = av_strtok(ccstr, ",", &saveptr2)) {
2179 ccstr = NULL;
2180
2181 if (av_strstart(keyval, "ccgroup:", &val)) {
2182 ccs->ccgroup = val;
2183 } else if (av_strstart(keyval, "instreamid:", &val)) {
2184 ccs->instreamid = val;
2185 } else if (av_strstart(keyval, "language:", &val)) {
2186 ccs->language = val;
2187 } else {
2188 av_log(s, AV_LOG_ERROR, "Invalid keyval %s\n", keyval);
2189 return AVERROR(EINVAL);
2190 }
2191 }
2192
2193 if (!ccs->ccgroup || !ccs->instreamid) {
2194 av_log(s, AV_LOG_ERROR, "Insufficient parameters in cc stream map string\n");
2195 return AVERROR(EINVAL);
2196 }
2197
2198 if (av_strstart(ccs->instreamid, "CC", &val)) {
2199 if (atoi(val) < 1 || atoi(val) > 4) {
2200 av_log(s, AV_LOG_ERROR, "Invalid instream ID CC index %d in %s, range 1-4\n",
2201 atoi(val), ccs->instreamid);
2202 return AVERROR(EINVAL);
2203 }
2204 } else if (av_strstart(ccs->instreamid, "SERVICE", &val)) {
2205 if (atoi(val) < 1 || atoi(val) > 63) {
2206 av_log(s, AV_LOG_ERROR, "Invalid instream ID SERVICE index %d in %s, range 1-63 \n",
2207 atoi(val), ccs->instreamid);
2208 return AVERROR(EINVAL);
2209 }
2210 } else {
2211 av_log(s, AV_LOG_ERROR, "Invalid instream ID %s, supported are CCn or SERVICEn\n",
2212 ccs->instreamid);
2213 return AVERROR(EINVAL);
2214 }
2215 }
2216
2217 return 0;
2218 }
2219
2220 21 static int update_variant_stream_info(AVFormatContext *s)
2221 {
2222 21 HLSContext *hls = s->priv_data;
2223 unsigned int i;
2224 21 int ret = 0;
2225
2226
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if (hls->cc_stream_map) {
2227 ret = parse_cc_stream_mapstring(s);
2228 if (ret < 0)
2229 return ret;
2230 }
2231
2232
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 20 times.
21 if (hls->var_stream_map) {
2233 1 return parse_variant_stream_mapstring(s);
2234 } else {
2235 //By default, a single variant stream with all the codec streams is created
2236 20 hls->var_streams = av_mallocz(sizeof(*hls->var_streams));
2237
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (!hls->var_streams)
2238 return AVERROR(ENOMEM);
2239 20 hls->nb_varstreams = 1;
2240
2241 20 hls->var_streams[0].var_stream_idx = 0;
2242 20 hls->var_streams[0].nb_streams = s->nb_streams;
2243 40 hls->var_streams[0].streams = av_mallocz(sizeof(AVStream *) *
2244 20 hls->var_streams[0].nb_streams);
2245
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (!hls->var_streams[0].streams)
2246 return AVERROR(ENOMEM);
2247
2248 //by default, the first available ccgroup is mapped to the variant stream
2249
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (hls->nb_ccstreams)
2250 hls->var_streams[0].ccgroup = hls->cc_streams[0].ccgroup;
2251
2252
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
40 for (i = 0; i < s->nb_streams; i++)
2253 20 hls->var_streams[0].streams[i] = s->streams[i];
2254 }
2255 20 return 0;
2256 }
2257
2258 1 static int update_master_pl_info(AVFormatContext *s)
2259 {
2260 1 HLSContext *hls = s->priv_data;
2261 const char *dir;
2262 1 char *fn1= NULL, *fn2 = NULL;
2263 1 int ret = 0;
2264
2265 1 fn1 = av_strdup(s->url);
2266
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!fn1)
2267 return AVERROR(ENOMEM);
2268 1 dir = av_dirname(fn1);
2269
2270 /**
2271 * if output file's directory has %v, variants are created in sub-directories
2272 * then master is created at the sub-directories level
2273 */
2274
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 if (dir && av_stristr(av_basename(dir), "%v")) {
2275 fn2 = av_strdup(dir);
2276 if (!fn2) {
2277 ret = AVERROR(ENOMEM);
2278 goto fail;
2279 }
2280 dir = av_dirname(fn2);
2281 }
2282
2283
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 if (dir && strcmp(dir, "."))
2284 1 hls->master_m3u8_url = av_append_path_component(dir, hls->master_pl_name);
2285 else
2286 hls->master_m3u8_url = av_strdup(hls->master_pl_name);
2287
2288
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!hls->master_m3u8_url) {
2289 ret = AVERROR(ENOMEM);
2290 goto fail;
2291 }
2292
2293 1 fail:
2294 1 av_freep(&fn1);
2295 1 av_freep(&fn2);
2296
2297 1 return ret;
2298 }
2299
2300 21 static int hls_write_header(AVFormatContext *s)
2301 {
2302 21 HLSContext *hls = s->priv_data;
2303 int ret, i, j;
2304 21 VariantStream *vs = NULL;
2305
2306
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 21 times.
43 for (i = 0; i < hls->nb_varstreams; i++) {
2307 22 int subtitle_streams = 0;
2308 22 vs = &hls->var_streams[i];
2309
2310 22 ret = avformat_write_header(vs->avf, NULL);
2311
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (ret < 0)
2312 return ret;
2313 //av_assert0(s->nb_streams == hls->avf->nb_streams);
2314
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 22 times.
44 for (j = 0; j < vs->nb_streams; j++) {
2315 AVStream *inner_st;
2316 22 AVStream *outer_st = vs->streams[j];
2317
2318
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 21 times.
22 if (hls->max_seg_size > 0) {
2319
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if ((outer_st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) &&
2320 (outer_st->codecpar->bit_rate > hls->max_seg_size)) {
2321 av_log(s, AV_LOG_WARNING, "Your video bitrate is bigger than hls_segment_size, "
2322 "(%"PRId64 " > %"PRId64 "), the result maybe not be what you want.",
2323 outer_st->codecpar->bit_rate, hls->max_seg_size);
2324 }
2325 }
2326
2327
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 if (outer_st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
2328 22 inner_st = vs->avf->streams[j - subtitle_streams];
2329 else if (vs->vtt_avf) {
2330 inner_st = vs->vtt_avf->streams[0];
2331 subtitle_streams++;
2332 } else {
2333 /* We have a subtitle stream, when the user does not want one */
2334 inner_st = NULL;
2335 continue;
2336 }
2337 22 avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den);
2338
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (outer_st->codecpar->codec_id == AV_CODEC_ID_HEVC &&
2339 outer_st->codecpar->codec_tag != MKTAG('h','v','c','1')) {
2340 av_log(s, AV_LOG_WARNING, "Stream HEVC is not hvc1, you should use tag:v hvc1 to set it.\n");
2341 }
2342 22 write_codec_attr(outer_st, vs);
2343
2344 }
2345 /* Update the Codec Attr string for the mapped audio groups */
2346
4/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 8 times.
22 if (vs->has_video && vs->agroup) {
2347
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (j = 0; j < hls->nb_varstreams; j++) {
2348 2 VariantStream *vs_agroup = &(hls->var_streams[j]);
2349
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 if (!vs_agroup->has_video && !vs_agroup->has_subtitle &&
2350
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 vs_agroup->agroup &&
2351 1 !av_strcasecmp(vs_agroup->agroup, vs->agroup)) {
2352 1 write_codec_attr(vs_agroup->streams[0], vs);
2353 }
2354 }
2355 }
2356 }
2357
2358 21 return 0;
2359 }
2360
2361 static int hls_init_file_resend(AVFormatContext *s, VariantStream *vs)
2362 {
2363 HLSContext *hls = s->priv_data;
2364 AVDictionary *options = NULL;
2365 int ret = 0;
2366
2367 set_http_options(s, &options, hls);
2368 ret = hlsenc_io_open(s, &vs->out, vs->base_output_dirname, &options);
2369 av_dict_free(&options);
2370 if (ret < 0)
2371 return ret;
2372 avio_write(vs->out, vs->init_buffer, vs->init_range_length);
2373 hlsenc_io_close(s, &vs->out, hls->fmp4_init_filename);
2374
2375 return ret;
2376 }
2377
2378 static int64_t append_single_file(AVFormatContext *s, VariantStream *vs)
2379 {
2380 int64_t ret = 0;
2381 int64_t read_byte = 0;
2382 int64_t total_size = 0;
2383 char *filename = NULL;
2384 char buf[BUFSIZE];
2385 AVFormatContext *oc = vs->avf;
2386
2387 hlsenc_io_close(s, &vs->out, vs->basename_tmp);
2388 filename = av_asprintf("%s.tmp", oc->url);
2389 ret = s->io_open(s, &vs->out, filename, AVIO_FLAG_READ, NULL);
2390 if (ret < 0) {
2391 av_free(filename);
2392 return ret;
2393 }
2394
2395 do {
2396 read_byte = avio_read(vs->out, buf, BUFSIZE);
2397 if (read_byte > 0) {
2398 avio_write(vs->out_single_file, buf, read_byte);
2399 total_size += read_byte;
2400 ret = total_size;
2401 }
2402 } while (read_byte > 0);
2403
2404 hlsenc_io_close(s, &vs->out, filename);
2405 av_free(filename);
2406
2407 return ret;
2408 }
2409 7380 static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
2410 {
2411 7380 HLSContext *hls = s->priv_data;
2412 7380 AVFormatContext *oc = NULL;
2413 7380 AVStream *st = s->streams[pkt->stream_index];
2414 7380 int64_t end_pts = 0;
2415 7380 int is_ref_pkt = 1;
2416 7380 int ret = 0, can_split = 1, i, j;
2417 7380 int stream_index = 0;
2418 7380 int range_length = 0;
2419 7380 const char *proto = NULL;
2420 7380 int use_temp_file = 0;
2421 7380 VariantStream *vs = NULL;
2422 7380 char *old_filename = NULL;
2423
2424
1/2
✓ Branch 0 taken 7610 times.
✗ Branch 1 not taken.
7610 for (i = 0; i < hls->nb_varstreams; i++) {
2425 7610 int subtitle_streams = 0;
2426 7610 vs = &hls->var_streams[i];
2427
2/2
✓ Branch 0 taken 7610 times.
✓ Branch 1 taken 230 times.
7840 for (j = 0; j < vs->nb_streams; j++) {
2428
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7610 times.
7610 if (vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
2429 subtitle_streams++;
2430 }
2431
2/2
✓ Branch 0 taken 7380 times.
✓ Branch 1 taken 230 times.
7610 if (vs->streams[j] == st) {
2432
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7380 times.
7380 if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
2433 oc = vs->vtt_avf;
2434 stream_index = 0;
2435 } else {
2436 7380 oc = vs->avf;
2437 7380 stream_index = j - subtitle_streams;
2438 }
2439 7380 break;
2440 }
2441 }
2442
2443
2/2
✓ Branch 0 taken 7380 times.
✓ Branch 1 taken 230 times.
7610 if (oc)
2444 7380 break;
2445 }
2446
2447
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7380 times.
7380 if (!oc) {
2448 av_log(s, AV_LOG_ERROR, "Unable to find mapping variant stream\n");
2449 return AVERROR(ENOMEM);
2450 }
2451
2452 7380 end_pts = hls->recording_time * vs->number;
2453
2454
4/4
✓ Branch 0 taken 341 times.
✓ Branch 1 taken 7039 times.
✓ Branch 2 taken 191 times.
✓ Branch 3 taken 150 times.
7380 if (vs->sequence - vs->nb_entries > hls->start_sequence && hls->init_time > 0) {
2455 /* reset end_pts, hls->recording_time at end of the init hls list */
2456 191 int64_t init_list_dur = hls->init_time * vs->nb_entries;
2457 191 int64_t after_init_list_dur = (vs->sequence - hls->start_sequence - vs->nb_entries) * hls->time;
2458 191 hls->recording_time = hls->time;
2459 191 end_pts = init_list_dur + after_init_list_dur ;
2460 }
2461
2462
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 7358 times.
7380 if (vs->start_pts == AV_NOPTS_VALUE) {
2463 22 vs->start_pts = av_rescale_q(pkt->pts, st->time_base, AV_TIME_BASE_Q);
2464
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 9 times.
22 if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
2465 13 vs->start_pts_from_audio = 1;
2466 }
2467
3/4
✓ Branch 0 taken 7296 times.
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7296 times.
7380 if (vs->start_pts_from_audio && st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
2468 int64_t video_start = av_rescale_q(pkt->pts, st->time_base, AV_TIME_BASE_Q);
2469 if (vs->start_pts > video_start) {
2470 vs->start_pts = video_start;
2471 vs->start_pts_from_audio = 0;
2472 }
2473 }
2474
2475
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 7296 times.
7380 if (vs->has_video) {
2476
1/2
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
168 can_split = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
2477
3/4
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 55 times.
84 ((pkt->flags & AV_PKT_FLAG_KEY) || (hls->flags & HLS_SPLIT_BY_TIME));
2478
2/4
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
84 is_ref_pkt = (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && (pkt->stream_index == vs->reference_stream_index);
2479 }
2480
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7380 times.
7380 if (pkt->pts == AV_NOPTS_VALUE)
2481 is_ref_pkt = can_split = 0;
2482
2483
1/2
✓ Branch 0 taken 7380 times.
✗ Branch 1 not taken.
7380 if (is_ref_pkt) {
2484
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 7358 times.
7380 if (vs->end_pts == AV_NOPTS_VALUE)
2485 22 vs->end_pts = pkt->pts;
2486
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 7358 times.
7380 if (vs->new_start) {
2487 22 vs->new_start = 0;
2488 22 vs->duration = (double)(pkt->pts - vs->end_pts)
2489 22 * st->time_base.num / st->time_base.den;
2490 22 vs->dpp = (double)(pkt->duration) * st->time_base.num / st->time_base.den;
2491 } else {
2492
1/2
✓ Branch 0 taken 7358 times.
✗ Branch 1 not taken.
7358 if (pkt->duration) {
2493 7358 vs->duration += (double)(pkt->duration) * st->time_base.num / st->time_base.den;
2494 } else {
2495 av_log(s, AV_LOG_WARNING, "Stream %d packet with pts %" PRId64 " has duration 0. The segment duration may not be precise.\n",
2496 pkt->stream_index, pkt->pts);
2497 vs->duration = (double)(pkt->pts - vs->end_pts) * st->time_base.num / st->time_base.den;
2498 }
2499 }
2500 }
2501
2502
4/4
✓ Branch 0 taken 7325 times.
✓ Branch 1 taken 55 times.
✓ Branch 2 taken 7303 times.
✓ Branch 3 taken 22 times.
7380 can_split = can_split && (pkt->pts - vs->end_pts > 0);
2503
6/6
✓ Branch 0 taken 7358 times.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 7303 times.
✓ Branch 3 taken 55 times.
✓ Branch 4 taken 82 times.
✓ Branch 5 taken 7221 times.
7380 if (vs->packets_written && can_split && (av_rescale_q(pkt->pts, st->time_base, AV_TIME_BASE_Q) - vs->start_pts
2504 >= end_pts)) {
2505 int64_t new_start_pos;
2506
4/4
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 60 times.
82 int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0);
2507 double cur_duration;
2508
2509 82 av_write_frame(oc, NULL); /* Flush any buffered data */
2510 82 new_start_pos = avio_tell(oc->pb);
2511 82 vs->size = new_start_pos - vs->start_pos;
2512 82 avio_flush(oc->pb);
2513
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 76 times.
82 if (hls->segment_type == SEGMENT_TYPE_FMP4) {
2514
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 if (!vs->init_range_length) {
2515 2 range_length = avio_close_dyn_buf(oc->pb, &vs->init_buffer);
2516
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (range_length <= 0)
2517 return AVERROR(EINVAL);
2518 2 avio_write(vs->out, vs->init_buffer, range_length);
2519
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!hls->resend_init_file)
2520 2 av_freep(&vs->init_buffer);
2521 2 vs->init_range_length = range_length;
2522 2 avio_open_dyn_buf(&oc->pb);
2523 2 vs->packets_written = 0;
2524 2 vs->start_pos = range_length;
2525
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!byterange_mode) {
2526 1 hlsenc_io_close(s, &vs->out, vs->base_output_dirname);
2527 }
2528 }
2529 }
2530
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 22 times.
82 if (!byterange_mode) {
2531
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (vs->vtt_avf) {
2532 hlsenc_io_close(s, &vs->vtt_avf->pb, vs->vtt_avf->url);
2533 }
2534 }
2535
2536
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 69 times.
82 if (hls->flags & HLS_SINGLE_FILE) {
2537 13 ret = flush_dynbuf(vs, &range_length);
2538 13 av_freep(&vs->temp_buffer);
2539
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (ret < 0) {
2540 return ret;
2541 }
2542 13 vs->size = range_length;
2543
2/4
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
13 if (hls->key_info_file || hls->encrypt)
2544 vs->size = append_single_file(s, vs);
2545 } else {
2546
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
69 if (oc->url[0]) {
2547 69 proto = avio_find_protocol_name(oc->url);
2548
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
138 use_temp_file = proto && !strcmp(proto, "file")
2549
2/4
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 69 times.
138 && (hls->flags & HLS_TEMP_FILE);
2550 }
2551
2552
6/6
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 60 times.
✓ Branch 5 taken 6 times.
69 if ((hls->max_seg_size > 0 && (vs->size + vs->start_pos >= hls->max_seg_size)) || !byterange_mode) {
2553 63 AVDictionary *options = NULL;
2554 63 char *filename = NULL;
2555
2/4
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 63 times.
63 if (hls->key_info_file || hls->encrypt) {
2556 av_dict_set(&options, "encryption_key", vs->key_string, 0);
2557 av_dict_set(&options, "encryption_iv", vs->iv_string, 0);
2558 filename = av_asprintf("crypto:%s", oc->url);
2559 } else {
2560 63 filename = av_asprintf("%s", oc->url);
2561 }
2562
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
63 if (!filename) {
2563 av_dict_free(&options);
2564 return AVERROR(ENOMEM);
2565 }
2566
2567 // look to rename the asset name
2568
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
63 if (use_temp_file)
2569 av_dict_set(&options, "mpegts_flags", "resend_headers", 0);
2570
2571 63 set_http_options(s, &options, hls);
2572
2573 63 ret = hlsenc_io_open(s, &vs->out, filename, &options);
2574
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
63 if (ret < 0) {
2575 av_log(s, hls->ignore_io_errors ? AV_LOG_WARNING : AV_LOG_ERROR,
2576 "Failed to open file '%s'\n", filename);
2577 av_freep(&filename);
2578 av_dict_free(&options);
2579 return hls->ignore_io_errors ? 0 : ret;
2580 }
2581
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 59 times.
63 if (hls->segment_type == SEGMENT_TYPE_FMP4) {
2582 4 write_styp(vs->out);
2583 }
2584 63 ret = flush_dynbuf(vs, &range_length);
2585
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
63 if (ret < 0) {
2586 av_freep(&filename);
2587 av_dict_free(&options);
2588 return ret;
2589 }
2590 63 vs->size = range_length;
2591 63 ret = hlsenc_io_close(s, &vs->out, filename);
2592
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
63 if (ret < 0) {
2593 av_log(s, AV_LOG_WARNING, "upload segment failed,"
2594 " will retry with a new http session.\n");
2595 ff_format_io_close(s, &vs->out);
2596 ret = hlsenc_io_open(s, &vs->out, filename, &options);
2597 if (ret >= 0) {
2598 reflush_dynbuf(vs, &range_length);
2599 ret = hlsenc_io_close(s, &vs->out, filename);
2600 }
2601 }
2602 63 av_dict_free(&options);
2603 63 av_freep(&vs->temp_buffer);
2604 63 av_freep(&filename);
2605 }
2606
2607
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 69 times.
69 if (use_temp_file)
2608 hls_rename_temp_file(s, oc);
2609 }
2610
2611
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 if (ret < 0)
2612 return ret;
2613
2614 82 old_filename = av_strdup(oc->url);
2615
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 if (!old_filename) {
2616 return AVERROR(ENOMEM);
2617 }
2618
2619 82 cur_duration = (double)(pkt->pts - vs->end_pts) * st->time_base.num / st->time_base.den;
2620 82 ret = hls_append_segment(s, hls, vs, cur_duration, vs->start_pos, vs->size);
2621 82 vs->end_pts = pkt->pts;
2622 82 vs->duration = 0;
2623
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 if (ret < 0) {
2624 av_freep(&old_filename);
2625 return ret;
2626 }
2627
2628 // if we're building a VOD playlist, skip writing the manifest multiple times, and just wait until the end
2629
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 2 times.
82 if (hls->pl_type != PLAYLIST_TYPE_VOD) {
2630
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 80 times.
80 if ((ret = hls_window(s, 0, vs)) < 0) {
2631 av_log(s, AV_LOG_WARNING, "upload playlist failed, will retry with a new http session.\n");
2632 ff_format_io_close(s, &vs->out);
2633 if ((ret = hls_window(s, 0, vs)) < 0) {
2634 av_freep(&old_filename);
2635 return ret;
2636 }
2637 }
2638 }
2639
2640
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
82 if (hls->resend_init_file && hls->segment_type == SEGMENT_TYPE_FMP4) {
2641 ret = hls_init_file_resend(s, vs);
2642 if (ret < 0) {
2643 av_freep(&old_filename);
2644 return ret;
2645 }
2646 }
2647
2648
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 69 times.
82 if (hls->flags & HLS_SINGLE_FILE) {
2649 13 vs->start_pos += vs->size;
2650
2/4
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
13 if (hls->key_info_file || hls->encrypt)
2651 ret = hls_start(s, vs);
2652
4/6
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
13 if (hls->segment_type == SEGMENT_TYPE_MPEGTS && oc->oformat->priv_class && oc->priv_data) {
2653 11 av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0);
2654 }
2655
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 60 times.
69 } else if (hls->max_seg_size > 0) {
2656
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 6 times.
9 if (vs->size + vs->start_pos >= hls->max_seg_size) {
2657 3 vs->sequence++;
2658 3 sls_flag_file_rename(hls, vs, old_filename);
2659 3 ret = hls_start(s, vs);
2660 3 vs->start_pos = 0;
2661 /* When split segment by byte, the duration is short than hls_time,
2662 * so it is not enough one segment duration as hls_time, */
2663 } else {
2664 6 vs->start_pos = new_start_pos;
2665 }
2666 } else {
2667 60 vs->start_pos = 0;
2668 60 sls_flag_file_rename(hls, vs, old_filename);
2669 60 ret = hls_start(s, vs);
2670 }
2671 82 vs->number++;
2672 82 av_freep(&old_filename);
2673
2674
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 if (ret < 0) {
2675 return ret;
2676 }
2677 }
2678
2679 7380 vs->packets_written++;
2680
1/2
✓ Branch 0 taken 7380 times.
✗ Branch 1 not taken.
7380 if (oc->pb) {
2681 7380 ret = ff_write_chained(oc, stream_index, pkt, s, 0);
2682
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7380 times.
7380 if (hls->ignore_io_errors)
2683 ret = 0;
2684 }
2685
2686 7380 return ret;
2687 }
2688
2689 21 static void hls_deinit(AVFormatContext *s)
2690 {
2691 21 HLSContext *hls = s->priv_data;
2692 21 int i = 0;
2693 21 VariantStream *vs = NULL;
2694
2695
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 21 times.
43 for (i = 0; i < hls->nb_varstreams; i++) {
2696 22 vs = &hls->var_streams[i];
2697
2698 22 av_freep(&vs->basename);
2699 22 av_freep(&vs->base_output_dirname);
2700 22 av_freep(&vs->fmp4_init_filename);
2701 22 av_freep(&vs->vtt_basename);
2702 22 av_freep(&vs->vtt_m3u8_name);
2703
2704 22 avformat_free_context(vs->vtt_avf);
2705 22 avformat_free_context(vs->avf);
2706
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (hls->resend_init_file)
2707 av_freep(&vs->init_buffer);
2708 22 hls_free_segments(vs->segments);
2709 22 hls_free_segments(vs->old_segments);
2710 22 av_freep(&vs->m3u8_name);
2711 22 av_freep(&vs->streams);
2712 }
2713
2714 21 ff_format_io_close(s, &hls->m3u8_out);
2715 21 ff_format_io_close(s, &hls->sub_m3u8_out);
2716 21 ff_format_io_close(s, &hls->http_delete);
2717 21 av_freep(&hls->key_basename);
2718 21 av_freep(&hls->var_streams);
2719 21 av_freep(&hls->cc_streams);
2720 21 av_freep(&hls->master_m3u8_url);
2721 21 }
2722
2723 21 static int hls_write_trailer(struct AVFormatContext *s)
2724 {
2725 21 HLSContext *hls = s->priv_data;
2726 21 AVFormatContext *oc = NULL;
2727 21 AVFormatContext *vtt_oc = NULL;
2728 21 char *old_filename = NULL;
2729 21 const char *proto = NULL;
2730 21 int use_temp_file = 0;
2731 int i;
2732 21 int ret = 0;
2733 21 VariantStream *vs = NULL;
2734 21 AVDictionary *options = NULL;
2735 int range_length, byterange_mode;
2736
2737
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 21 times.
43 for (i = 0; i < hls->nb_varstreams; i++) {
2738 22 char *filename = NULL;
2739 22 vs = &hls->var_streams[i];
2740 22 oc = vs->avf;
2741 22 vtt_oc = vs->vtt_avf;
2742 22 old_filename = av_strdup(oc->url);
2743 22 use_temp_file = 0;
2744
2745
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (!old_filename) {
2746 return AVERROR(ENOMEM);
2747 }
2748
2/4
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 22 times.
22 if (hls->key_info_file || hls->encrypt) {
2749 av_dict_set(&options, "encryption_key", vs->key_string, 0);
2750 av_dict_set(&options, "encryption_iv", vs->iv_string, 0);
2751 filename = av_asprintf("crypto:%s", oc->url);
2752 } else {
2753 22 filename = av_asprintf("%s", oc->url);
2754 }
2755
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (!filename) {
2756 av_dict_free(&options);
2757 av_freep(&old_filename);
2758 return AVERROR(ENOMEM);
2759 }
2760
2761
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 19 times.
22 if (hls->segment_type == SEGMENT_TYPE_FMP4) {
2762
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (!vs->init_range_length) {
2763 1 uint8_t *buffer = NULL;
2764 1 av_write_frame(oc, NULL); /* Flush any buffered data */
2765
2766 1 int init_range_length = avio_close_dyn_buf(oc->pb, &buffer);
2767 1 avio_write(vs->out, buffer, init_range_length);
2768 1 av_freep(&buffer);
2769 1 vs->init_range_length = init_range_length;
2770 1 avio_open_dyn_buf(&oc->pb);
2771 1 vs->packets_written = 0;
2772 1 vs->start_pos = init_range_length;
2773
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0);
2774
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!byterange_mode) {
2775 1 ff_format_io_close(s, &vs->out);
2776 1 hlsenc_io_close(s, &vs->out, vs->base_output_dirname);
2777 }
2778 }
2779 }
2780
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 3 times.
22 if (!(hls->flags & HLS_SINGLE_FILE)) {
2781 19 set_http_options(s, &options, hls);
2782 19 ret = hlsenc_io_open(s, &vs->out, filename, &options);
2783
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if (ret < 0) {
2784 av_log(s, AV_LOG_ERROR, "Failed to open file '%s'\n", oc->url);
2785 goto failed;
2786 }
2787
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 17 times.
19 if (hls->segment_type == SEGMENT_TYPE_FMP4)
2788 2 write_styp(vs->out);
2789 }
2790 22 ret = flush_dynbuf(vs, &range_length);
2791
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (ret < 0)
2792 goto failed;
2793
2794 22 vs->size = range_length;
2795 22 ret = hlsenc_io_close(s, &vs->out, filename);
2796
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (ret < 0) {
2797 av_log(s, AV_LOG_WARNING, "upload segment failed, will retry with a new http session.\n");
2798 ff_format_io_close(s, &vs->out);
2799 ret = hlsenc_io_open(s, &vs->out, filename, &options);
2800 if (ret < 0) {
2801 av_log(s, AV_LOG_ERROR, "Failed to open file '%s'\n", oc->url);
2802 goto failed;
2803 }
2804 reflush_dynbuf(vs, &range_length);
2805 ret = hlsenc_io_close(s, &vs->out, filename);
2806 if (ret < 0)
2807 av_log(s, AV_LOG_WARNING, "Failed to upload file '%s' at the end.\n", oc->url);
2808 }
2809
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 3 times.
22 if (hls->flags & HLS_SINGLE_FILE) {
2810
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (hls->key_info_file || hls->encrypt) {
2811 vs->size = append_single_file(s, vs);
2812 }
2813 3 hlsenc_io_close(s, &vs->out_single_file, vs->basename);
2814 }
2815 19 failed:
2816 22 av_freep(&vs->temp_buffer);
2817 22 av_dict_free(&options);
2818 22 av_freep(&filename);
2819 22 av_write_trailer(oc);
2820
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 if (oc->url[0]) {
2821 22 proto = avio_find_protocol_name(oc->url);
2822
3/6
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 22 times.
22 use_temp_file = proto && !strcmp(proto, "file") && (hls->flags & HLS_TEMP_FILE);
2823 }
2824
2825 // rename that segment from .tmp to the real one
2826
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
22 if (use_temp_file && !(hls->flags & HLS_SINGLE_FILE)) {
2827 hls_rename_temp_file(s, oc);
2828 av_freep(&old_filename);
2829 old_filename = av_strdup(oc->url);
2830
2831 if (!old_filename) {
2832 return AVERROR(ENOMEM);
2833 }
2834 }
2835
2836 /* after av_write_trailer, then duration + 1 duration per packet */
2837 22 hls_append_segment(s, hls, vs, vs->duration + vs->dpp, vs->start_pos, vs->size);
2838
2839 22 sls_flag_file_rename(hls, vs, old_filename);
2840
2841
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (vtt_oc) {
2842 if (vtt_oc->pb)
2843 av_write_trailer(vtt_oc);
2844 vs->size = avio_tell(vs->vtt_avf->pb) - vs->start_pos;
2845 ff_format_io_close(s, &vtt_oc->pb);
2846 }
2847 22 ret = hls_window(s, 1, vs);
2848
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (ret < 0) {
2849 av_log(s, AV_LOG_WARNING, "upload playlist failed, will retry with a new http session.\n");
2850 ff_format_io_close(s, &vs->out);
2851 hls_window(s, 1, vs);
2852 }
2853 22 ffio_free_dyn_buf(&oc->pb);
2854
2855 22 av_free(old_filename);
2856 }
2857
2858 21 return 0;
2859 }
2860
2861
2862 21 static int hls_init(AVFormatContext *s)
2863 {
2864 21 int ret = 0;
2865 21 int i = 0;
2866 21 int j = 0;
2867 21 HLSContext *hls = s->priv_data;
2868 const char *pattern;
2869 21 VariantStream *vs = NULL;
2870
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 18 times.
21 const char *vtt_pattern = hls->flags & HLS_SINGLE_FILE ? ".vtt" : "%d.vtt";
2871 21 int http_base_proto = ff_is_http_proto(s->url);
2872 21 int fmp4_init_filename_len = strlen(hls->fmp4_init_filename) + 1;
2873 21 double initial_program_date_time = av_gettime() / 1000000.0;
2874
2875
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if (hls->use_localtime) {
2876 pattern = get_default_pattern_localtime_fmt(s);
2877 } else {
2878
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 18 times.
21 pattern = hls->segment_type == SEGMENT_TYPE_FMP4 ? "%d.m4s" : "%d.ts";
2879
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 18 times.
21 if (hls->flags & HLS_SINGLE_FILE)
2880 3 pattern += 2;
2881 }
2882
2883 21 hls->has_default_key = 0;
2884 21 hls->has_video_m3u8 = 0;
2885 21 ret = update_variant_stream_info(s);
2886
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if (ret < 0) {
2887 av_log(s, AV_LOG_ERROR, "Variant stream info update failed with status %x\n",
2888 ret);
2889 return ret;
2890 }
2891
2892
2/4
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
21 if (!hls->method && http_base_proto) {
2893 av_log(hls, AV_LOG_WARNING, "No HTTP method set, hls muxer defaulting to method PUT.\n");
2894 }
2895
2896 21 ret = validate_name(hls->nb_varstreams, s->url);
2897
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if (ret < 0)
2898 return ret;
2899
2900
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 if (hls->segment_filename) {
2901 21 ret = validate_name(hls->nb_varstreams, hls->segment_filename);
2902
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if (ret < 0)
2903 return ret;
2904 }
2905
2906
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 18 times.
21 if (av_strcasecmp(hls->fmp4_init_filename, "init.mp4")) {
2907 3 ret = validate_name(hls->nb_varstreams, hls->fmp4_init_filename);
2908
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (ret < 0)
2909 return ret;
2910 }
2911
2912
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if (hls->subtitle_filename) {
2913 ret = validate_name(hls->nb_varstreams, hls->subtitle_filename);
2914 if (ret < 0)
2915 return ret;
2916 }
2917
2918
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 20 times.
21 if (hls->master_pl_name) {
2919 1 ret = update_master_pl_info(s);
2920
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0) {
2921 av_log(s, AV_LOG_ERROR, "Master stream info update failed with status %x\n",
2922 ret);
2923 return ret;
2924 }
2925 }
2926
2927
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 if ((hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) ||
2928
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_MICROSECONDS_SINCE_EPOCH) ||
2929
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_FORMATTED_DATETIME)) {
2930 time_t t = time(NULL);
2931 if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_MICROSECONDS_SINCE_EPOCH) {
2932 hls->start_sequence = av_gettime();
2933 } else if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) {
2934 hls->start_sequence = (int64_t)t;
2935 } else if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_FORMATTED_DATETIME) {
2936 char b[15];
2937 struct tm *p, tmbuf;
2938 if (!(p = localtime_r(&t, &tmbuf)))
2939 return AVERROR(errno);
2940 if (!strftime(b, sizeof(b), "%Y%m%d%H%M%S", p))
2941 return AVERROR(ENOMEM);
2942 hls->start_sequence = strtoll(b, NULL, 10);
2943 }
2944 av_log(hls, AV_LOG_DEBUG, "start_number evaluated to %"PRId64"\n", hls->start_sequence);
2945 }
2946
2947
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
21 hls->recording_time = hls->init_time && hls->max_nb_segments > 0 ? hls->init_time : hls->time;
2948
2949
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
21 if (hls->flags & HLS_SPLIT_BY_TIME && hls->flags & HLS_INDEPENDENT_SEGMENTS) {
2950 // Independent segments cannot be guaranteed when splitting by time
2951 hls->flags &= ~HLS_INDEPENDENT_SEGMENTS;
2952 av_log(s, AV_LOG_WARNING,
2953 "'split_by_time' and 'independent_segments' cannot be "
2954 "enabled together. Disabling 'independent_segments' flag\n");
2955 }
2956
2957
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 21 times.
43 for (i = 0; i < hls->nb_varstreams; i++) {
2958 22 vs = &hls->var_streams[i];
2959
2960 22 ret = format_name(s->url, &vs->m3u8_name, i, vs->varname);
2961
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (ret < 0)
2962 return ret;
2963
2964 22 vs->sequence = hls->start_sequence;
2965 22 vs->start_pts = AV_NOPTS_VALUE;
2966 22 vs->end_pts = AV_NOPTS_VALUE;
2967 22 vs->current_segment_final_filename_fmt[0] = '\0';
2968 22 vs->initial_prog_date_time = initial_program_date_time;
2969
2970
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 22 times.
44 for (j = 0; j < vs->nb_streams; j++) {
2971 22 vs->has_video += vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
2972 /* Get one video stream to reference for split segments
2973 * so use the first video stream index. */
2974
3/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
22 if ((vs->has_video == 1) && (vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)) {
2975 9 vs->reference_stream_index = vs->streams[j]->index;
2976 }
2977 22 vs->has_subtitle += vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE;
2978 }
2979
2980
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (vs->has_video > 1)
2981 av_log(s, AV_LOG_WARNING, "More than a single video stream present, expect issues decoding it.\n");
2982
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 19 times.
22 if (hls->segment_type == SEGMENT_TYPE_FMP4) {
2983 #if CONFIG_MP4_MUXER
2984 EXTERN const FFOutputFormat ff_mp4_muxer;
2985 3 vs->oformat = &ff_mp4_muxer.p;
2986 #else
2987 return AVERROR_MUXER_NOT_FOUND;
2988 #endif
2989 } else {
2990 EXTERN const FFOutputFormat ff_mpegts_muxer;
2991 19 vs->oformat = &ff_mpegts_muxer.p;
2992 }
2993
2994
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 if (hls->segment_filename) {
2995 22 ret = format_name(hls->segment_filename, &vs->basename, i, vs->varname);
2996
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (ret < 0)
2997 return ret;
2998 } else {
2999 char *p = strrchr(vs->m3u8_name, '.');
3000 if (p)
3001 *p = '\0';
3002
3003 vs->basename = av_asprintf("%s%s", vs->m3u8_name, pattern);
3004 if (!vs->basename)
3005 return AVERROR(ENOMEM);
3006
3007 if (p)
3008 *p = '.';
3009 }
3010
3011
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 19 times.
22 if (hls->segment_type == SEGMENT_TYPE_FMP4) {
3012
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (hls->nb_varstreams > 1)
3013 fmp4_init_filename_len += strlen(POSTFIX_PATTERN);
3014
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (hls->flags & HLS_SINGLE_FILE) {
3015 1 vs->fmp4_init_filename = av_strdup(vs->basename);
3016
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!vs->fmp4_init_filename)
3017 return AVERROR(ENOMEM);
3018 } else {
3019 2 vs->fmp4_init_filename = av_malloc(fmp4_init_filename_len);
3020
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!vs->fmp4_init_filename)
3021 return AVERROR(ENOMEM);
3022 2 av_strlcpy(vs->fmp4_init_filename, hls->fmp4_init_filename,
3023 fmp4_init_filename_len);
3024
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (hls->nb_varstreams > 1) {
3025 if (av_stristr(vs->fmp4_init_filename, "%v")) {
3026 av_freep(&vs->fmp4_init_filename);
3027 ret = format_name(hls->fmp4_init_filename,
3028 &vs->fmp4_init_filename, i, vs->varname);
3029 } else {
3030 ret = append_postfix(vs->fmp4_init_filename, fmp4_init_filename_len, i);
3031 }
3032 if (ret < 0)
3033 return ret;
3034 }
3035
3036
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (hls->use_localtime) {
3037 int r;
3038 char *expanded = NULL;
3039
3040 r = strftime_expand(vs->fmp4_init_filename, &expanded);
3041 if (r < 0) {
3042 av_log(s, AV_LOG_ERROR, "Could not get segment filename with strftime\n");
3043 return r;
3044 }
3045 av_free(vs->fmp4_init_filename);
3046 vs->fmp4_init_filename = expanded;
3047 }
3048
3049 2 char *p = strrchr(vs->m3u8_name, '/');
3050
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (p) {
3051 2 char tmp = *(++p);
3052 2 *p = '\0';
3053 2 vs->base_output_dirname = av_asprintf("%s%s", vs->m3u8_name,
3054 vs->fmp4_init_filename);
3055 2 *p = tmp;
3056 } else {
3057 vs->base_output_dirname = av_strdup(vs->fmp4_init_filename);
3058 }
3059
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!vs->base_output_dirname)
3060 return AVERROR(ENOMEM);
3061 }
3062 }
3063
3064
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 ret = hls->use_localtime ? sls_flag_check_duration_size(hls, vs) : sls_flag_check_duration_size_index(hls);
3065
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (ret < 0)
3066 return ret;
3067
3068
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (vs->has_subtitle) {
3069 EXTERN const FFOutputFormat ff_webvtt_muxer;
3070 vs->vtt_oformat = &ff_webvtt_muxer.p;
3071
3072 char *p = strrchr(vs->m3u8_name, '.');
3073 if (p)
3074 *p = '\0';
3075
3076 vs->vtt_basename = av_asprintf("%s%s", vs->m3u8_name, vtt_pattern);
3077 if (!vs->vtt_basename)
3078 return AVERROR(ENOMEM);
3079
3080 if (hls->subtitle_filename) {
3081 ret = format_name(hls->subtitle_filename, &vs->vtt_m3u8_name, i, vs->varname);
3082 if (ret < 0)
3083 return ret;
3084 } else {
3085 vs->vtt_m3u8_name = av_asprintf("%s_vtt.m3u8", vs->m3u8_name);
3086 if (!vs->vtt_m3u8_name)
3087 return AVERROR(ENOMEM);
3088 }
3089 if (p)
3090 *p = '.';
3091 }
3092
3093
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
22 if ((ret = hls_mux_init(s, vs)) < 0)
3094 return ret;
3095
3096
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 21 times.
22 if (hls->flags & HLS_APPEND_LIST) {
3097 1 parse_playlist(s, vs->m3u8_name, vs);
3098 1 vs->discontinuity = 1;
3099
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (hls->init_time > 0) {
3100 av_log(s, AV_LOG_WARNING, "append_list mode does not support hls_init_time,"
3101 " hls_init_time value will have no effect\n");
3102 hls->init_time = 0;
3103 hls->recording_time = hls->time;
3104 }
3105 }
3106
3107
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
22 if ((ret = hls_start(s, vs)) < 0)
3108 return ret;
3109 22 vs->number++;
3110 }
3111
3112 21 return ret;
3113 }
3114
3115 #define OFFSET(x) offsetof(HLSContext, x)
3116 #define E AV_OPT_FLAG_ENCODING_PARAM
3117 static const AVOption options[] = {
3118 {"start_number", "set first number in the sequence", OFFSET(start_sequence),AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, E},
3119 {"hls_time", "set segment length", OFFSET(time), AV_OPT_TYPE_DURATION, {.i64 = 2000000}, 0, INT64_MAX, E},
3120 {"hls_init_time", "set segment length at init list", OFFSET(init_time), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, INT64_MAX, E},
3121 {"hls_list_size", "set maximum number of playlist entries", OFFSET(max_nb_segments), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E},
3122 {"hls_delete_threshold", "set number of unreferenced segments to keep before deleting", OFFSET(hls_delete_threshold), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, E},
3123 {"hls_vtt_options","set hls vtt list of options for the container format used for hls", OFFSET(vtt_format_options_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
3124 {"hls_allow_cache", "explicitly set whether the client MAY (1) or MUST NOT (0) cache media segments", OFFSET(allowcache), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, E},
3125 {"hls_base_url", "url to prepend to each playlist entry", OFFSET(baseurl), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
3126 {"hls_segment_filename", "filename template for segment files", OFFSET(segment_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
3127 {"hls_segment_options","set segments files format options of hls", OFFSET(format_options), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, E},
3128 {"hls_segment_size", "maximum size per segment file, (in bytes)", OFFSET(max_seg_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E},
3129 {"hls_key_info_file", "file with key URI and key file path", OFFSET(key_info_file), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
3130 {"hls_enc", "enable AES128 encryption support", OFFSET(encrypt), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E},
3131 {"hls_enc_key", "hex-coded 16 byte key to encrypt the segments", OFFSET(key), AV_OPT_TYPE_STRING, .flags = E},
3132 {"hls_enc_key_url", "url to access the key to decrypt the segments", OFFSET(key_url), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
3133 {"hls_enc_iv", "hex-coded 16 byte initialization vector", OFFSET(iv), AV_OPT_TYPE_STRING, .flags = E},
3134 {"hls_subtitle_path", "set path of hls subtitles", OFFSET(subtitle_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
3135 {"hls_segment_type", "set hls segment files type", OFFSET(segment_type), AV_OPT_TYPE_INT, {.i64 = SEGMENT_TYPE_MPEGTS }, 0, SEGMENT_TYPE_FMP4, E, .unit = "segment_type"},
3136 {"mpegts", "make segment file to mpegts files in m3u8", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_MPEGTS }, 0, UINT_MAX, E, .unit = "segment_type"},
3137 {"fmp4", "make segment file to fragment mp4 files in m3u8", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_FMP4 }, 0, UINT_MAX, E, .unit = "segment_type"},
3138 {"hls_fmp4_init_filename", "set fragment mp4 file init filename", OFFSET(fmp4_init_filename), AV_OPT_TYPE_STRING, {.str = "init.mp4"}, 0, 0, E},
3139 {"hls_fmp4_init_resend", "resend fragment mp4 init file after refresh m3u8 every time", OFFSET(resend_init_file), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
3140 {"hls_flags", "set flags affecting HLS playlist and media file generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, .unit = "flags"},
3141 {"single_file", "generate a single media file indexed with byte ranges", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SINGLE_FILE }, 0, UINT_MAX, E, .unit = "flags"},
3142 {"temp_file", "write segment and playlist to temporary file and rename when complete", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_TEMP_FILE }, 0, UINT_MAX, E, .unit = "flags"},
3143 {"delete_segments", "delete segment files that are no longer part of the playlist", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_DELETE_SEGMENTS }, 0, UINT_MAX, E, .unit = "flags"},
3144 {"round_durations", "round durations in m3u8 to whole numbers", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_ROUND_DURATIONS }, 0, UINT_MAX, E, .unit = "flags"},
3145 {"discont_start", "start the playlist with a discontinuity tag", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_DISCONT_START }, 0, UINT_MAX, E, .unit = "flags"},
3146 {"omit_endlist", "Do not append an endlist when ending stream", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_OMIT_ENDLIST }, 0, UINT_MAX, E, .unit = "flags"},
3147 {"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, .unit = "flags"},
3148 {"append_list", "append the new segments into old hls segment list", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_APPEND_LIST }, 0, UINT_MAX, E, .unit = "flags"},
3149 {"program_date_time", "add EXT-X-PROGRAM-DATE-TIME", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_PROGRAM_DATE_TIME }, 0, UINT_MAX, E, .unit = "flags"},
3150 {"second_level_segment_index", "include segment index in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_INDEX }, 0, UINT_MAX, E, .unit = "flags"},
3151 {"second_level_segment_duration", "include segment duration in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_DURATION }, 0, UINT_MAX, E, .unit = "flags"},
3152 {"second_level_segment_size", "include segment size in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_SIZE }, 0, UINT_MAX, E, .unit = "flags"},
3153 {"periodic_rekey", "reload keyinfo file periodically for re-keying", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_PERIODIC_REKEY }, 0, UINT_MAX, E, .unit = "flags"},
3154 {"independent_segments", "add EXT-X-INDEPENDENT-SEGMENTS, whenever applicable", 0, AV_OPT_TYPE_CONST, { .i64 = HLS_INDEPENDENT_SEGMENTS }, 0, UINT_MAX, E, .unit = "flags"},
3155 {"iframes_only", "add EXT-X-I-FRAMES-ONLY, whenever applicable", 0, AV_OPT_TYPE_CONST, { .i64 = HLS_I_FRAMES_ONLY }, 0, UINT_MAX, E, .unit = "flags"},
3156 {"strftime", "set filename expansion with strftime at segment creation", OFFSET(use_localtime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
3157 {"strftime_mkdir", "create last directory component in strftime-generated filename", OFFSET(use_localtime_mkdir), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
3158 {"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, .unit = "pl_type" },
3159 {"event", "EVENT playlist", 0, AV_OPT_TYPE_CONST, {.i64 = PLAYLIST_TYPE_EVENT }, INT_MIN, INT_MAX, E, .unit = "pl_type" },
3160 {"vod", "VOD playlist", 0, AV_OPT_TYPE_CONST, {.i64 = PLAYLIST_TYPE_VOD }, INT_MIN, INT_MAX, E, .unit = "pl_type" },
3161 {"method", "set the HTTP method(default: PUT)", OFFSET(method), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
3162 {"hls_start_number_source", "set source of first number in sequence", OFFSET(start_sequence_source_type), AV_OPT_TYPE_INT, {.i64 = HLS_START_SEQUENCE_AS_START_NUMBER }, 0, HLS_START_SEQUENCE_LAST-1, E, .unit = "start_sequence_source_type" },
3163 {"generic", "start_number value (default)", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_START_NUMBER }, INT_MIN, INT_MAX, E, .unit = "start_sequence_source_type" },
3164 {"epoch", "seconds since epoch", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH }, INT_MIN, INT_MAX, E, .unit = "start_sequence_source_type" },
3165 {"epoch_us", "microseconds since epoch", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_MICROSECONDS_SINCE_EPOCH }, INT_MIN, INT_MAX, E, .unit = "start_sequence_source_type" },
3166 {"datetime", "current datetime as YYYYMMDDhhmmss", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_FORMATTED_DATETIME }, INT_MIN, INT_MAX, E, .unit = "start_sequence_source_type" },
3167 {"http_user_agent", "override User-Agent field in HTTP header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
3168 {"var_stream_map", "Variant stream map string", OFFSET(var_stream_map), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
3169 {"cc_stream_map", "Closed captions stream map string", OFFSET(cc_stream_map), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
3170 {"master_pl_name", "Create HLS master playlist with this name", OFFSET(master_pl_name), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
3171 {"master_pl_publish_rate", "Publish master play list every after this many segment intervals", OFFSET(master_publish_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, UINT_MAX, E},
3172 {"http_persistent", "Use persistent HTTP connections", OFFSET(http_persistent), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
3173 {"timeout", "set timeout for socket I/O operations", OFFSET(timeout), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX, .flags = E },
3174 {"ignore_io_errors", "Ignore IO errors for stable long-duration runs with network output", OFFSET(ignore_io_errors), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
3175 {"headers", "set custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
3176 { NULL },
3177 };
3178
3179 static const AVClass hls_class = {
3180 .class_name = "hls muxer",
3181 .item_name = av_default_item_name,
3182 .option = options,
3183 .version = LIBAVUTIL_VERSION_INT,
3184 };
3185
3186
3187 const FFOutputFormat ff_hls_muxer = {
3188 .p.name = "hls",
3189 .p.long_name = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"),
3190 .p.extensions = "m3u8",
3191 .p.audio_codec = AV_CODEC_ID_AAC,
3192 .p.video_codec = AV_CODEC_ID_H264,
3193 .p.subtitle_codec = AV_CODEC_ID_WEBVTT,
3194 .p.flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER | AVFMT_NODIMENSIONS,
3195 .p.priv_class = &hls_class,
3196 .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH,
3197 .priv_data_size = sizeof(HLSContext),
3198 .init = hls_init,
3199 .write_header = hls_write_header,
3200 .write_packet = hls_write_packet,
3201 .write_trailer = hls_write_trailer,
3202 .deinit = hls_deinit,
3203 };
3204