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 <stdint.h> 25 26#include "libavutil/time_internal.h" 27 28#include "avformat.h" 29#include "hlsplaylist.h" 30 31void ff_hls_write_playlist_version(AVIOContext *out, int version) 32{ 33 if (!out) 34 return; 35 avio_printf(out, "#EXTM3U\n"); 36 avio_printf(out, "#EXT-X-VERSION:%d\n", version); 37} 38 39void ff_hls_write_audio_rendition(AVIOContext *out, const char *agroup, 40 const char *filename, const char *language, 41 int name_id, int is_default) 42{ 43 if (!out || !agroup || !filename) 44 return; 45 46 avio_printf(out, "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"group_%s\"", agroup); 47 avio_printf(out, ",NAME=\"audio_%d\",DEFAULT=%s,", name_id, is_default ? "YES" : "NO"); 48 if (language) { 49 avio_printf(out, "LANGUAGE=\"%s\",", language); 50 } 51 avio_printf(out, "URI=\"%s\"\n", filename); 52} 53 54void ff_hls_write_subtitle_rendition(AVIOContext *out, const char *sgroup, 55 const char *filename, const char *language, 56 int name_id, int is_default) 57{ 58 if (!out || !filename) 59 return; 60 61 avio_printf(out, "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"%s\"", sgroup); 62 avio_printf(out, ",NAME=\"subtitle_%d\",DEFAULT=%s,", name_id, is_default ? "YES" : "NO"); 63 if (language) { 64 avio_printf(out, "LANGUAGE=\"%s\",", language); 65 } 66 avio_printf(out, "URI=\"%s\"\n", filename); 67} 68 69void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, int bandwidth, 70 const char *filename, const char *agroup, 71 const char *codecs, const char *ccgroup, 72 const char *sgroup) 73{ 74 if (!out || !filename) 75 return; 76 77 if (!bandwidth) { 78 av_log(NULL, AV_LOG_WARNING, 79 "Bandwidth info not available, set audio and video bitrates\n"); 80 return; 81 } 82 83 avio_printf(out, "#EXT-X-STREAM-INF:BANDWIDTH=%d", bandwidth); 84 if (st && st->codecpar->width > 0 && st->codecpar->height > 0) 85 avio_printf(out, ",RESOLUTION=%dx%d", st->codecpar->width, 86 st->codecpar->height); 87 if (codecs && codecs[0]) 88 avio_printf(out, ",CODECS=\"%s\"", codecs); 89 if (agroup && agroup[0]) 90 avio_printf(out, ",AUDIO=\"group_%s\"", agroup); 91 if (ccgroup && ccgroup[0]) 92 avio_printf(out, ",CLOSED-CAPTIONS=\"%s\"", ccgroup); 93 if (sgroup && sgroup[0]) 94 avio_printf(out, ",SUBTITLES=\"%s\"", sgroup); 95 avio_printf(out, "\n%s\n\n", filename); 96} 97 98void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache, 99 int target_duration, int64_t sequence, 100 uint32_t playlist_type, int iframe_mode) 101{ 102 if (!out) 103 return; 104 ff_hls_write_playlist_version(out, version); 105 if (allowcache == 0 || allowcache == 1) { 106 avio_printf(out, "#EXT-X-ALLOW-CACHE:%s\n", allowcache == 0 ? "NO" : "YES"); 107 } 108 avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration); 109 avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); 110 av_log(NULL, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); 111 112 if (playlist_type == PLAYLIST_TYPE_EVENT) { 113 avio_printf(out, "#EXT-X-PLAYLIST-TYPE:EVENT\n"); 114 } else if (playlist_type == PLAYLIST_TYPE_VOD) { 115 avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n"); 116 } 117 if (iframe_mode) { 118 avio_printf(out, "#EXT-X-I-FRAMES-ONLY\n"); 119 } 120} 121 122void ff_hls_write_init_file(AVIOContext *out, const char *filename, 123 int byterange_mode, int64_t size, int64_t pos) 124{ 125 avio_printf(out, "#EXT-X-MAP:URI=\"%s\"", filename); 126 if (byterange_mode) { 127 avio_printf(out, ",BYTERANGE=\"%"PRId64"@%"PRId64"\"", size, pos); 128 } 129 avio_printf(out, "\n"); 130} 131 132int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, 133 int byterange_mode, double duration, 134 int round_duration, int64_t size, 135 int64_t pos /* Used only if HLS_SINGLE_FILE flag is set */, 136 const char *baseurl /* Ignored if NULL */, 137 const char *filename, double *prog_date_time, 138 int64_t video_keyframe_size, int64_t video_keyframe_pos, 139 int iframe_mode) 140{ 141 if (!out || !filename) 142 return AVERROR(EINVAL); 143 144 if (insert_discont) { 145 avio_printf(out, "#EXT-X-DISCONTINUITY\n"); 146 } 147 if (round_duration) 148 avio_printf(out, "#EXTINF:%ld,\n", lrint(duration)); 149 else 150 avio_printf(out, "#EXTINF:%f,\n", duration); 151 if (byterange_mode) 152 avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64"\n", iframe_mode ? video_keyframe_size : size, 153 iframe_mode ? video_keyframe_pos : pos); 154 155 if (prog_date_time) { 156 time_t tt, wrongsecs; 157 int milli; 158 struct tm *tm, tmpbuf; 159 char buf0[128], buf1[128]; 160 tt = (int64_t)*prog_date_time; 161 milli = av_clip(lrint(1000*(*prog_date_time - tt)), 0, 999); 162 tm = localtime_r(&tt, &tmpbuf); 163 if (!strftime(buf0, sizeof(buf0), "%Y-%m-%dT%H:%M:%S", tm)) { 164 av_log(NULL, AV_LOG_DEBUG, "strftime error in ff_hls_write_file_entry\n"); 165 return AVERROR_UNKNOWN; 166 } 167 if (!strftime(buf1, sizeof(buf1), "%z", tm) || buf1[1]<'0' ||buf1[1]>'2') { 168 int tz_min, dst = tm->tm_isdst; 169 tm = gmtime_r(&tt, &tmpbuf); 170 tm->tm_isdst = dst; 171 wrongsecs = mktime(tm); 172 tz_min = (FFABS(wrongsecs - tt) + 30) / 60; 173 snprintf(buf1, sizeof(buf1), 174 "%c%02d%02d", 175 wrongsecs <= tt ? '+' : '-', 176 tz_min / 60, 177 tz_min % 60); 178 } 179 avio_printf(out, "#EXT-X-PROGRAM-DATE-TIME:%s.%03d%s\n", buf0, milli, buf1); 180 *prog_date_time += duration; 181 } 182 if (baseurl) 183 avio_printf(out, "%s", baseurl); 184 avio_printf(out, "%s\n", filename); 185 186 return 0; 187} 188 189void ff_hls_write_end_list(AVIOContext *out) 190{ 191 if (!out) 192 return; 193 avio_printf(out, "#EXT-X-ENDLIST\n"); 194} 195 196