FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/hlsenc.c
Date: 2026-09-01 17:16:25
Exec Total Coverage
Lines: 943 1843 51.2%
Functions: 31 47 66.0%
Branches: 619 1360 45.5%

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