1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * Live HDS fragmenter 3cabdff1aSopenharmony_ci * Copyright (c) 2013 Martin Storsjo 4cabdff1aSopenharmony_ci * 5cabdff1aSopenharmony_ci * This file is part of FFmpeg. 6cabdff1aSopenharmony_ci * 7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 11cabdff1aSopenharmony_ci * 12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15cabdff1aSopenharmony_ci * Lesser General Public License for more details. 16cabdff1aSopenharmony_ci * 17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20cabdff1aSopenharmony_ci */ 21cabdff1aSopenharmony_ci 22cabdff1aSopenharmony_ci#include "config.h" 23cabdff1aSopenharmony_ci#if HAVE_UNISTD_H 24cabdff1aSopenharmony_ci#include <unistd.h> 25cabdff1aSopenharmony_ci#endif 26cabdff1aSopenharmony_ci 27cabdff1aSopenharmony_ci#include "avformat.h" 28cabdff1aSopenharmony_ci#include "internal.h" 29cabdff1aSopenharmony_ci#include "mux.h" 30cabdff1aSopenharmony_ci#include "os_support.h" 31cabdff1aSopenharmony_ci 32cabdff1aSopenharmony_ci#include "libavutil/avstring.h" 33cabdff1aSopenharmony_ci#include "libavutil/base64.h" 34cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h" 35cabdff1aSopenharmony_ci#include "libavutil/mathematics.h" 36cabdff1aSopenharmony_ci#include "libavutil/opt.h" 37cabdff1aSopenharmony_ci 38cabdff1aSopenharmony_citypedef struct Fragment { 39cabdff1aSopenharmony_ci char file[1024]; 40cabdff1aSopenharmony_ci int64_t start_time, duration; 41cabdff1aSopenharmony_ci int n; 42cabdff1aSopenharmony_ci} Fragment; 43cabdff1aSopenharmony_ci 44cabdff1aSopenharmony_citypedef struct OutputStream { 45cabdff1aSopenharmony_ci int bitrate; 46cabdff1aSopenharmony_ci int first_stream; 47cabdff1aSopenharmony_ci AVFormatContext *ctx; 48cabdff1aSopenharmony_ci int ctx_inited; 49cabdff1aSopenharmony_ci uint8_t iobuf[32768]; 50cabdff1aSopenharmony_ci char temp_filename[1024]; 51cabdff1aSopenharmony_ci int64_t frag_start_ts, last_ts; 52cabdff1aSopenharmony_ci AVIOContext *out; 53cabdff1aSopenharmony_ci int packets_written; 54cabdff1aSopenharmony_ci int nb_fragments, fragments_size, fragment_index; 55cabdff1aSopenharmony_ci Fragment **fragments; 56cabdff1aSopenharmony_ci 57cabdff1aSopenharmony_ci int has_audio, has_video; 58cabdff1aSopenharmony_ci 59cabdff1aSopenharmony_ci uint8_t *metadata; 60cabdff1aSopenharmony_ci int metadata_size; 61cabdff1aSopenharmony_ci 62cabdff1aSopenharmony_ci uint8_t *extra_packets[2]; 63cabdff1aSopenharmony_ci int extra_packet_sizes[2]; 64cabdff1aSopenharmony_ci int nb_extra_packets; 65cabdff1aSopenharmony_ci} OutputStream; 66cabdff1aSopenharmony_ci 67cabdff1aSopenharmony_citypedef struct HDSContext { 68cabdff1aSopenharmony_ci const AVClass *class; /* Class for private options. */ 69cabdff1aSopenharmony_ci int window_size; 70cabdff1aSopenharmony_ci int extra_window_size; 71cabdff1aSopenharmony_ci int min_frag_duration; 72cabdff1aSopenharmony_ci int remove_at_exit; 73cabdff1aSopenharmony_ci 74cabdff1aSopenharmony_ci OutputStream *streams; 75cabdff1aSopenharmony_ci int nb_streams; 76cabdff1aSopenharmony_ci} HDSContext; 77cabdff1aSopenharmony_ci 78cabdff1aSopenharmony_cistatic int parse_header(OutputStream *os, const uint8_t *buf, int buf_size) 79cabdff1aSopenharmony_ci{ 80cabdff1aSopenharmony_ci if (buf_size < 13) 81cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 82cabdff1aSopenharmony_ci if (memcmp(buf, "FLV", 3)) 83cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 84cabdff1aSopenharmony_ci buf += 13; 85cabdff1aSopenharmony_ci buf_size -= 13; 86cabdff1aSopenharmony_ci while (buf_size >= 11 + 4) { 87cabdff1aSopenharmony_ci int type = buf[0]; 88cabdff1aSopenharmony_ci int size = AV_RB24(&buf[1]) + 11 + 4; 89cabdff1aSopenharmony_ci if (size > buf_size) 90cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 91cabdff1aSopenharmony_ci if (type == 8 || type == 9) { 92cabdff1aSopenharmony_ci if (os->nb_extra_packets >= FF_ARRAY_ELEMS(os->extra_packets)) 93cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 94cabdff1aSopenharmony_ci os->extra_packet_sizes[os->nb_extra_packets] = size; 95cabdff1aSopenharmony_ci os->extra_packets[os->nb_extra_packets] = av_memdup(buf, size); 96cabdff1aSopenharmony_ci if (!os->extra_packets[os->nb_extra_packets]) 97cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 98cabdff1aSopenharmony_ci os->nb_extra_packets++; 99cabdff1aSopenharmony_ci } else if (type == 0x12) { 100cabdff1aSopenharmony_ci if (os->metadata) 101cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 102cabdff1aSopenharmony_ci os->metadata_size = size - 11 - 4; 103cabdff1aSopenharmony_ci os->metadata = av_memdup(buf + 11, os->metadata_size); 104cabdff1aSopenharmony_ci if (!os->metadata) 105cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 106cabdff1aSopenharmony_ci } 107cabdff1aSopenharmony_ci buf += size; 108cabdff1aSopenharmony_ci buf_size -= size; 109cabdff1aSopenharmony_ci } 110cabdff1aSopenharmony_ci if (!os->metadata) 111cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 112cabdff1aSopenharmony_ci return 0; 113cabdff1aSopenharmony_ci} 114cabdff1aSopenharmony_ci 115cabdff1aSopenharmony_cistatic int hds_write(void *opaque, uint8_t *buf, int buf_size) 116cabdff1aSopenharmony_ci{ 117cabdff1aSopenharmony_ci OutputStream *os = opaque; 118cabdff1aSopenharmony_ci if (os->out) { 119cabdff1aSopenharmony_ci avio_write(os->out, buf, buf_size); 120cabdff1aSopenharmony_ci } else { 121cabdff1aSopenharmony_ci if (!os->metadata_size) { 122cabdff1aSopenharmony_ci int ret; 123cabdff1aSopenharmony_ci // Assuming the IO buffer is large enough to fit the 124cabdff1aSopenharmony_ci // FLV header and all metadata and extradata packets 125cabdff1aSopenharmony_ci if ((ret = parse_header(os, buf, buf_size)) < 0) 126cabdff1aSopenharmony_ci return ret; 127cabdff1aSopenharmony_ci } 128cabdff1aSopenharmony_ci } 129cabdff1aSopenharmony_ci return buf_size; 130cabdff1aSopenharmony_ci} 131cabdff1aSopenharmony_ci 132cabdff1aSopenharmony_cistatic void hds_free(AVFormatContext *s) 133cabdff1aSopenharmony_ci{ 134cabdff1aSopenharmony_ci HDSContext *c = s->priv_data; 135cabdff1aSopenharmony_ci int i, j; 136cabdff1aSopenharmony_ci if (!c->streams) 137cabdff1aSopenharmony_ci return; 138cabdff1aSopenharmony_ci for (i = 0; i < s->nb_streams; i++) { 139cabdff1aSopenharmony_ci OutputStream *os = &c->streams[i]; 140cabdff1aSopenharmony_ci if (os->out) 141cabdff1aSopenharmony_ci ff_format_io_close(s, &os->out); 142cabdff1aSopenharmony_ci if (os->ctx && os->ctx_inited) 143cabdff1aSopenharmony_ci av_write_trailer(os->ctx); 144cabdff1aSopenharmony_ci if (os->ctx) 145cabdff1aSopenharmony_ci avio_context_free(&os->ctx->pb); 146cabdff1aSopenharmony_ci avformat_free_context(os->ctx); 147cabdff1aSopenharmony_ci av_freep(&os->metadata); 148cabdff1aSopenharmony_ci for (j = 0; j < os->nb_extra_packets; j++) 149cabdff1aSopenharmony_ci av_freep(&os->extra_packets[j]); 150cabdff1aSopenharmony_ci for (j = 0; j < os->nb_fragments; j++) 151cabdff1aSopenharmony_ci av_freep(&os->fragments[j]); 152cabdff1aSopenharmony_ci av_freep(&os->fragments); 153cabdff1aSopenharmony_ci } 154cabdff1aSopenharmony_ci av_freep(&c->streams); 155cabdff1aSopenharmony_ci} 156cabdff1aSopenharmony_ci 157cabdff1aSopenharmony_cistatic int write_manifest(AVFormatContext *s, int final) 158cabdff1aSopenharmony_ci{ 159cabdff1aSopenharmony_ci HDSContext *c = s->priv_data; 160cabdff1aSopenharmony_ci AVIOContext *out; 161cabdff1aSopenharmony_ci char filename[1024], temp_filename[1024]; 162cabdff1aSopenharmony_ci int ret, i; 163cabdff1aSopenharmony_ci double duration = 0; 164cabdff1aSopenharmony_ci 165cabdff1aSopenharmony_ci if (c->nb_streams > 0) 166cabdff1aSopenharmony_ci duration = c->streams[0].last_ts * av_q2d(s->streams[0]->time_base); 167cabdff1aSopenharmony_ci 168cabdff1aSopenharmony_ci snprintf(filename, sizeof(filename), "%s/index.f4m", s->url); 169cabdff1aSopenharmony_ci snprintf(temp_filename, sizeof(temp_filename), "%s/index.f4m.tmp", s->url); 170cabdff1aSopenharmony_ci ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, NULL); 171cabdff1aSopenharmony_ci if (ret < 0) { 172cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); 173cabdff1aSopenharmony_ci return ret; 174cabdff1aSopenharmony_ci } 175cabdff1aSopenharmony_ci avio_printf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); 176cabdff1aSopenharmony_ci avio_printf(out, "<manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">\n"); 177cabdff1aSopenharmony_ci avio_printf(out, "\t<id>%s</id>\n", av_basename(s->url)); 178cabdff1aSopenharmony_ci avio_printf(out, "\t<streamType>%s</streamType>\n", 179cabdff1aSopenharmony_ci final ? "recorded" : "live"); 180cabdff1aSopenharmony_ci avio_printf(out, "\t<deliveryType>streaming</deliveryType>\n"); 181cabdff1aSopenharmony_ci if (final) 182cabdff1aSopenharmony_ci avio_printf(out, "\t<duration>%f</duration>\n", duration); 183cabdff1aSopenharmony_ci for (i = 0; i < c->nb_streams; i++) { 184cabdff1aSopenharmony_ci OutputStream *os = &c->streams[i]; 185cabdff1aSopenharmony_ci int b64_size = AV_BASE64_SIZE(os->metadata_size); 186cabdff1aSopenharmony_ci char *base64 = av_malloc(b64_size); 187cabdff1aSopenharmony_ci if (!base64) { 188cabdff1aSopenharmony_ci ff_format_io_close(s, &out); 189cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 190cabdff1aSopenharmony_ci } 191cabdff1aSopenharmony_ci av_base64_encode(base64, b64_size, os->metadata, os->metadata_size); 192cabdff1aSopenharmony_ci 193cabdff1aSopenharmony_ci avio_printf(out, "\t<bootstrapInfo profile=\"named\" url=\"stream%d.abst\" id=\"bootstrap%d\" />\n", i, i); 194cabdff1aSopenharmony_ci avio_printf(out, "\t<media bitrate=\"%d\" url=\"stream%d\" bootstrapInfoId=\"bootstrap%d\">\n", os->bitrate/1000, i, i); 195cabdff1aSopenharmony_ci avio_printf(out, "\t\t<metadata>%s</metadata>\n", base64); 196cabdff1aSopenharmony_ci avio_printf(out, "\t</media>\n"); 197cabdff1aSopenharmony_ci av_free(base64); 198cabdff1aSopenharmony_ci } 199cabdff1aSopenharmony_ci avio_printf(out, "</manifest>\n"); 200cabdff1aSopenharmony_ci avio_flush(out); 201cabdff1aSopenharmony_ci ff_format_io_close(s, &out); 202cabdff1aSopenharmony_ci return ff_rename(temp_filename, filename, s); 203cabdff1aSopenharmony_ci} 204cabdff1aSopenharmony_ci 205cabdff1aSopenharmony_cistatic void update_size(AVIOContext *out, int64_t pos) 206cabdff1aSopenharmony_ci{ 207cabdff1aSopenharmony_ci int64_t end = avio_tell(out); 208cabdff1aSopenharmony_ci avio_seek(out, pos, SEEK_SET); 209cabdff1aSopenharmony_ci avio_wb32(out, end - pos); 210cabdff1aSopenharmony_ci avio_seek(out, end, SEEK_SET); 211cabdff1aSopenharmony_ci} 212cabdff1aSopenharmony_ci 213cabdff1aSopenharmony_ci/* Note, the .abst files need to be served with the "binary/octet" 214cabdff1aSopenharmony_ci * mime type, otherwise at least the OSMF player can easily fail 215cabdff1aSopenharmony_ci * with "stream not found" when polling for the next fragment. */ 216cabdff1aSopenharmony_cistatic int write_abst(AVFormatContext *s, OutputStream *os, int final) 217cabdff1aSopenharmony_ci{ 218cabdff1aSopenharmony_ci HDSContext *c = s->priv_data; 219cabdff1aSopenharmony_ci AVIOContext *out; 220cabdff1aSopenharmony_ci char filename[1024], temp_filename[1024]; 221cabdff1aSopenharmony_ci int i, ret; 222cabdff1aSopenharmony_ci int64_t asrt_pos, afrt_pos; 223cabdff1aSopenharmony_ci int start = 0, fragments; 224cabdff1aSopenharmony_ci int index = s->streams[os->first_stream]->id; 225cabdff1aSopenharmony_ci int64_t cur_media_time = 0; 226cabdff1aSopenharmony_ci if (c->window_size) 227cabdff1aSopenharmony_ci start = FFMAX(os->nb_fragments - c->window_size, 0); 228cabdff1aSopenharmony_ci fragments = os->nb_fragments - start; 229cabdff1aSopenharmony_ci if (final) 230cabdff1aSopenharmony_ci cur_media_time = os->last_ts; 231cabdff1aSopenharmony_ci else if (os->nb_fragments) 232cabdff1aSopenharmony_ci cur_media_time = os->fragments[os->nb_fragments - 1]->start_time; 233cabdff1aSopenharmony_ci 234cabdff1aSopenharmony_ci snprintf(filename, sizeof(filename), 235cabdff1aSopenharmony_ci "%s/stream%d.abst", s->url, index); 236cabdff1aSopenharmony_ci snprintf(temp_filename, sizeof(temp_filename), 237cabdff1aSopenharmony_ci "%s/stream%d.abst.tmp", s->url, index); 238cabdff1aSopenharmony_ci ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, NULL); 239cabdff1aSopenharmony_ci if (ret < 0) { 240cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); 241cabdff1aSopenharmony_ci return ret; 242cabdff1aSopenharmony_ci } 243cabdff1aSopenharmony_ci avio_wb32(out, 0); // abst size 244cabdff1aSopenharmony_ci avio_wl32(out, MKTAG('a','b','s','t')); 245cabdff1aSopenharmony_ci avio_wb32(out, 0); // version + flags 246cabdff1aSopenharmony_ci avio_wb32(out, os->fragment_index - 1); // BootstrapinfoVersion 247cabdff1aSopenharmony_ci avio_w8(out, final ? 0 : 0x20); // profile, live, update 248cabdff1aSopenharmony_ci avio_wb32(out, 1000); // timescale 249cabdff1aSopenharmony_ci avio_wb64(out, cur_media_time); 250cabdff1aSopenharmony_ci avio_wb64(out, 0); // SmpteTimeCodeOffset 251cabdff1aSopenharmony_ci avio_w8(out, 0); // MovieIdentifer (null string) 252cabdff1aSopenharmony_ci avio_w8(out, 0); // ServerEntryCount 253cabdff1aSopenharmony_ci avio_w8(out, 0); // QualityEntryCount 254cabdff1aSopenharmony_ci avio_w8(out, 0); // DrmData (null string) 255cabdff1aSopenharmony_ci avio_w8(out, 0); // MetaData (null string) 256cabdff1aSopenharmony_ci avio_w8(out, 1); // SegmentRunTableCount 257cabdff1aSopenharmony_ci asrt_pos = avio_tell(out); 258cabdff1aSopenharmony_ci avio_wb32(out, 0); // asrt size 259cabdff1aSopenharmony_ci avio_wl32(out, MKTAG('a','s','r','t')); 260cabdff1aSopenharmony_ci avio_wb32(out, 0); // version + flags 261cabdff1aSopenharmony_ci avio_w8(out, 0); // QualityEntryCount 262cabdff1aSopenharmony_ci avio_wb32(out, 1); // SegmentRunEntryCount 263cabdff1aSopenharmony_ci avio_wb32(out, 1); // FirstSegment 264cabdff1aSopenharmony_ci avio_wb32(out, final ? (os->fragment_index - 1) : 0xffffffff); // FragmentsPerSegment 265cabdff1aSopenharmony_ci update_size(out, asrt_pos); 266cabdff1aSopenharmony_ci avio_w8(out, 1); // FragmentRunTableCount 267cabdff1aSopenharmony_ci afrt_pos = avio_tell(out); 268cabdff1aSopenharmony_ci avio_wb32(out, 0); // afrt size 269cabdff1aSopenharmony_ci avio_wl32(out, MKTAG('a','f','r','t')); 270cabdff1aSopenharmony_ci avio_wb32(out, 0); // version + flags 271cabdff1aSopenharmony_ci avio_wb32(out, 1000); // timescale 272cabdff1aSopenharmony_ci avio_w8(out, 0); // QualityEntryCount 273cabdff1aSopenharmony_ci avio_wb32(out, fragments); // FragmentRunEntryCount 274cabdff1aSopenharmony_ci for (i = start; i < os->nb_fragments; i++) { 275cabdff1aSopenharmony_ci avio_wb32(out, os->fragments[i]->n); 276cabdff1aSopenharmony_ci avio_wb64(out, os->fragments[i]->start_time); 277cabdff1aSopenharmony_ci avio_wb32(out, os->fragments[i]->duration); 278cabdff1aSopenharmony_ci } 279cabdff1aSopenharmony_ci update_size(out, afrt_pos); 280cabdff1aSopenharmony_ci update_size(out, 0); 281cabdff1aSopenharmony_ci ff_format_io_close(s, &out); 282cabdff1aSopenharmony_ci return ff_rename(temp_filename, filename, s); 283cabdff1aSopenharmony_ci} 284cabdff1aSopenharmony_ci 285cabdff1aSopenharmony_cistatic int init_file(AVFormatContext *s, OutputStream *os, int64_t start_ts) 286cabdff1aSopenharmony_ci{ 287cabdff1aSopenharmony_ci int ret, i; 288cabdff1aSopenharmony_ci ret = s->io_open(s, &os->out, os->temp_filename, AVIO_FLAG_WRITE, NULL); 289cabdff1aSopenharmony_ci if (ret < 0) 290cabdff1aSopenharmony_ci return ret; 291cabdff1aSopenharmony_ci avio_wb32(os->out, 0); 292cabdff1aSopenharmony_ci avio_wl32(os->out, MKTAG('m','d','a','t')); 293cabdff1aSopenharmony_ci for (i = 0; i < os->nb_extra_packets; i++) { 294cabdff1aSopenharmony_ci AV_WB24(os->extra_packets[i] + 4, start_ts); 295cabdff1aSopenharmony_ci os->extra_packets[i][7] = (start_ts >> 24) & 0x7f; 296cabdff1aSopenharmony_ci avio_write(os->out, os->extra_packets[i], os->extra_packet_sizes[i]); 297cabdff1aSopenharmony_ci } 298cabdff1aSopenharmony_ci return 0; 299cabdff1aSopenharmony_ci} 300cabdff1aSopenharmony_ci 301cabdff1aSopenharmony_cistatic void close_file(AVFormatContext *s, OutputStream *os) 302cabdff1aSopenharmony_ci{ 303cabdff1aSopenharmony_ci int64_t pos = avio_tell(os->out); 304cabdff1aSopenharmony_ci avio_seek(os->out, 0, SEEK_SET); 305cabdff1aSopenharmony_ci avio_wb32(os->out, pos); 306cabdff1aSopenharmony_ci avio_flush(os->out); 307cabdff1aSopenharmony_ci ff_format_io_close(s, &os->out); 308cabdff1aSopenharmony_ci} 309cabdff1aSopenharmony_ci 310cabdff1aSopenharmony_cistatic int hds_write_header(AVFormatContext *s) 311cabdff1aSopenharmony_ci{ 312cabdff1aSopenharmony_ci HDSContext *c = s->priv_data; 313cabdff1aSopenharmony_ci const AVOutputFormat *oformat; 314cabdff1aSopenharmony_ci int ret = 0, i; 315cabdff1aSopenharmony_ci 316cabdff1aSopenharmony_ci if (mkdir(s->url, 0777) == -1 && errno != EEXIST) { 317cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR , "Failed to create directory %s\n", s->url); 318cabdff1aSopenharmony_ci return AVERROR(errno); 319cabdff1aSopenharmony_ci } 320cabdff1aSopenharmony_ci 321cabdff1aSopenharmony_ci oformat = av_guess_format("flv", NULL, NULL); 322cabdff1aSopenharmony_ci if (!oformat) { 323cabdff1aSopenharmony_ci return AVERROR_MUXER_NOT_FOUND; 324cabdff1aSopenharmony_ci } 325cabdff1aSopenharmony_ci 326cabdff1aSopenharmony_ci c->streams = av_calloc(s->nb_streams, sizeof(*c->streams)); 327cabdff1aSopenharmony_ci if (!c->streams) { 328cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 329cabdff1aSopenharmony_ci } 330cabdff1aSopenharmony_ci 331cabdff1aSopenharmony_ci for (i = 0; i < s->nb_streams; i++) { 332cabdff1aSopenharmony_ci OutputStream *os = &c->streams[c->nb_streams]; 333cabdff1aSopenharmony_ci AVFormatContext *ctx; 334cabdff1aSopenharmony_ci AVStream *st = s->streams[i]; 335cabdff1aSopenharmony_ci 336cabdff1aSopenharmony_ci if (!st->codecpar->bit_rate) { 337cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "No bit rate set for stream %d\n", i); 338cabdff1aSopenharmony_ci return AVERROR(EINVAL); 339cabdff1aSopenharmony_ci } 340cabdff1aSopenharmony_ci if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { 341cabdff1aSopenharmony_ci if (os->has_video) { 342cabdff1aSopenharmony_ci c->nb_streams++; 343cabdff1aSopenharmony_ci os++; 344cabdff1aSopenharmony_ci } 345cabdff1aSopenharmony_ci os->has_video = 1; 346cabdff1aSopenharmony_ci } else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { 347cabdff1aSopenharmony_ci if (os->has_audio) { 348cabdff1aSopenharmony_ci c->nb_streams++; 349cabdff1aSopenharmony_ci os++; 350cabdff1aSopenharmony_ci } 351cabdff1aSopenharmony_ci os->has_audio = 1; 352cabdff1aSopenharmony_ci } else { 353cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Unsupported stream type in stream %d\n", i); 354cabdff1aSopenharmony_ci return AVERROR(EINVAL); 355cabdff1aSopenharmony_ci } 356cabdff1aSopenharmony_ci os->bitrate += s->streams[i]->codecpar->bit_rate; 357cabdff1aSopenharmony_ci 358cabdff1aSopenharmony_ci if (!os->ctx) { 359cabdff1aSopenharmony_ci os->first_stream = i; 360cabdff1aSopenharmony_ci ctx = avformat_alloc_context(); 361cabdff1aSopenharmony_ci if (!ctx) { 362cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 363cabdff1aSopenharmony_ci } 364cabdff1aSopenharmony_ci os->ctx = ctx; 365cabdff1aSopenharmony_ci ctx->oformat = oformat; 366cabdff1aSopenharmony_ci ctx->interrupt_callback = s->interrupt_callback; 367cabdff1aSopenharmony_ci ctx->flags = s->flags; 368cabdff1aSopenharmony_ci 369cabdff1aSopenharmony_ci ctx->pb = avio_alloc_context(os->iobuf, sizeof(os->iobuf), 370cabdff1aSopenharmony_ci 1, os, 371cabdff1aSopenharmony_ci NULL, hds_write, NULL); 372cabdff1aSopenharmony_ci if (!ctx->pb) { 373cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 374cabdff1aSopenharmony_ci } 375cabdff1aSopenharmony_ci } else { 376cabdff1aSopenharmony_ci ctx = os->ctx; 377cabdff1aSopenharmony_ci } 378cabdff1aSopenharmony_ci s->streams[i]->id = c->nb_streams; 379cabdff1aSopenharmony_ci 380cabdff1aSopenharmony_ci if (!(st = avformat_new_stream(ctx, NULL))) { 381cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 382cabdff1aSopenharmony_ci } 383cabdff1aSopenharmony_ci avcodec_parameters_copy(st->codecpar, s->streams[i]->codecpar); 384cabdff1aSopenharmony_ci st->codecpar->codec_tag = 0; 385cabdff1aSopenharmony_ci st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio; 386cabdff1aSopenharmony_ci st->time_base = s->streams[i]->time_base; 387cabdff1aSopenharmony_ci } 388cabdff1aSopenharmony_ci if (c->streams[c->nb_streams].ctx) 389cabdff1aSopenharmony_ci c->nb_streams++; 390cabdff1aSopenharmony_ci 391cabdff1aSopenharmony_ci for (i = 0; i < c->nb_streams; i++) { 392cabdff1aSopenharmony_ci OutputStream *os = &c->streams[i]; 393cabdff1aSopenharmony_ci int j; 394cabdff1aSopenharmony_ci if ((ret = avformat_write_header(os->ctx, NULL)) < 0) { 395cabdff1aSopenharmony_ci return ret; 396cabdff1aSopenharmony_ci } 397cabdff1aSopenharmony_ci os->ctx_inited = 1; 398cabdff1aSopenharmony_ci avio_flush(os->ctx->pb); 399cabdff1aSopenharmony_ci for (j = 0; j < os->ctx->nb_streams; j++) 400cabdff1aSopenharmony_ci s->streams[os->first_stream + j]->time_base = os->ctx->streams[j]->time_base; 401cabdff1aSopenharmony_ci 402cabdff1aSopenharmony_ci snprintf(os->temp_filename, sizeof(os->temp_filename), 403cabdff1aSopenharmony_ci "%s/stream%d_temp", s->url, i); 404cabdff1aSopenharmony_ci ret = init_file(s, os, 0); 405cabdff1aSopenharmony_ci if (ret < 0) 406cabdff1aSopenharmony_ci return ret; 407cabdff1aSopenharmony_ci 408cabdff1aSopenharmony_ci if (!os->has_video && c->min_frag_duration <= 0) { 409cabdff1aSopenharmony_ci av_log(s, AV_LOG_WARNING, 410cabdff1aSopenharmony_ci "No video stream in output stream %d and no min frag duration set\n", i); 411cabdff1aSopenharmony_ci } 412cabdff1aSopenharmony_ci os->fragment_index = 1; 413cabdff1aSopenharmony_ci write_abst(s, os, 0); 414cabdff1aSopenharmony_ci } 415cabdff1aSopenharmony_ci ret = write_manifest(s, 0); 416cabdff1aSopenharmony_ci 417cabdff1aSopenharmony_ci return ret; 418cabdff1aSopenharmony_ci} 419cabdff1aSopenharmony_ci 420cabdff1aSopenharmony_cistatic int add_fragment(OutputStream *os, const char *file, 421cabdff1aSopenharmony_ci int64_t start_time, int64_t duration) 422cabdff1aSopenharmony_ci{ 423cabdff1aSopenharmony_ci Fragment *frag; 424cabdff1aSopenharmony_ci if (duration == 0) 425cabdff1aSopenharmony_ci duration = 1; 426cabdff1aSopenharmony_ci if (os->nb_fragments >= os->fragments_size) { 427cabdff1aSopenharmony_ci int ret; 428cabdff1aSopenharmony_ci os->fragments_size = (os->fragments_size + 1) * 2; 429cabdff1aSopenharmony_ci if ((ret = av_reallocp_array(&os->fragments, os->fragments_size, 430cabdff1aSopenharmony_ci sizeof(*os->fragments))) < 0) { 431cabdff1aSopenharmony_ci os->fragments_size = 0; 432cabdff1aSopenharmony_ci os->nb_fragments = 0; 433cabdff1aSopenharmony_ci return ret; 434cabdff1aSopenharmony_ci } 435cabdff1aSopenharmony_ci } 436cabdff1aSopenharmony_ci frag = av_mallocz(sizeof(*frag)); 437cabdff1aSopenharmony_ci if (!frag) 438cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 439cabdff1aSopenharmony_ci av_strlcpy(frag->file, file, sizeof(frag->file)); 440cabdff1aSopenharmony_ci frag->start_time = start_time; 441cabdff1aSopenharmony_ci frag->duration = duration; 442cabdff1aSopenharmony_ci frag->n = os->fragment_index; 443cabdff1aSopenharmony_ci os->fragments[os->nb_fragments++] = frag; 444cabdff1aSopenharmony_ci os->fragment_index++; 445cabdff1aSopenharmony_ci return 0; 446cabdff1aSopenharmony_ci} 447cabdff1aSopenharmony_ci 448cabdff1aSopenharmony_cistatic int hds_flush(AVFormatContext *s, OutputStream *os, int final, 449cabdff1aSopenharmony_ci int64_t end_ts) 450cabdff1aSopenharmony_ci{ 451cabdff1aSopenharmony_ci HDSContext *c = s->priv_data; 452cabdff1aSopenharmony_ci int i, ret = 0; 453cabdff1aSopenharmony_ci char target_filename[1024]; 454cabdff1aSopenharmony_ci int index = s->streams[os->first_stream]->id; 455cabdff1aSopenharmony_ci 456cabdff1aSopenharmony_ci if (!os->packets_written) 457cabdff1aSopenharmony_ci return 0; 458cabdff1aSopenharmony_ci 459cabdff1aSopenharmony_ci avio_flush(os->ctx->pb); 460cabdff1aSopenharmony_ci os->packets_written = 0; 461cabdff1aSopenharmony_ci close_file(s, os); 462cabdff1aSopenharmony_ci 463cabdff1aSopenharmony_ci snprintf(target_filename, sizeof(target_filename), 464cabdff1aSopenharmony_ci "%s/stream%dSeg1-Frag%d", s->url, index, os->fragment_index); 465cabdff1aSopenharmony_ci ret = ff_rename(os->temp_filename, target_filename, s); 466cabdff1aSopenharmony_ci if (ret < 0) 467cabdff1aSopenharmony_ci return ret; 468cabdff1aSopenharmony_ci add_fragment(os, target_filename, os->frag_start_ts, end_ts - os->frag_start_ts); 469cabdff1aSopenharmony_ci 470cabdff1aSopenharmony_ci if (!final) { 471cabdff1aSopenharmony_ci ret = init_file(s, os, end_ts); 472cabdff1aSopenharmony_ci if (ret < 0) 473cabdff1aSopenharmony_ci return ret; 474cabdff1aSopenharmony_ci } 475cabdff1aSopenharmony_ci 476cabdff1aSopenharmony_ci if (c->window_size || (final && c->remove_at_exit)) { 477cabdff1aSopenharmony_ci int remove = os->nb_fragments - c->window_size - c->extra_window_size; 478cabdff1aSopenharmony_ci if (final && c->remove_at_exit) 479cabdff1aSopenharmony_ci remove = os->nb_fragments; 480cabdff1aSopenharmony_ci if (remove > 0) { 481cabdff1aSopenharmony_ci for (i = 0; i < remove; i++) { 482cabdff1aSopenharmony_ci unlink(os->fragments[i]->file); 483cabdff1aSopenharmony_ci av_freep(&os->fragments[i]); 484cabdff1aSopenharmony_ci } 485cabdff1aSopenharmony_ci os->nb_fragments -= remove; 486cabdff1aSopenharmony_ci memmove(os->fragments, os->fragments + remove, 487cabdff1aSopenharmony_ci os->nb_fragments * sizeof(*os->fragments)); 488cabdff1aSopenharmony_ci } 489cabdff1aSopenharmony_ci } 490cabdff1aSopenharmony_ci 491cabdff1aSopenharmony_ci if (ret >= 0) 492cabdff1aSopenharmony_ci ret = write_abst(s, os, final); 493cabdff1aSopenharmony_ci return ret; 494cabdff1aSopenharmony_ci} 495cabdff1aSopenharmony_ci 496cabdff1aSopenharmony_cistatic int hds_write_packet(AVFormatContext *s, AVPacket *pkt) 497cabdff1aSopenharmony_ci{ 498cabdff1aSopenharmony_ci HDSContext *c = s->priv_data; 499cabdff1aSopenharmony_ci AVStream *st = s->streams[pkt->stream_index]; 500cabdff1aSopenharmony_ci FFStream *const sti = ffstream(st); 501cabdff1aSopenharmony_ci OutputStream *os = &c->streams[s->streams[pkt->stream_index]->id]; 502cabdff1aSopenharmony_ci int64_t end_dts = os->fragment_index * (int64_t)c->min_frag_duration; 503cabdff1aSopenharmony_ci int ret; 504cabdff1aSopenharmony_ci 505cabdff1aSopenharmony_ci if (sti->first_dts == AV_NOPTS_VALUE) 506cabdff1aSopenharmony_ci sti->first_dts = pkt->dts; 507cabdff1aSopenharmony_ci 508cabdff1aSopenharmony_ci if ((!os->has_video || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && 509cabdff1aSopenharmony_ci av_compare_ts(pkt->dts - sti->first_dts, st->time_base, 510cabdff1aSopenharmony_ci end_dts, AV_TIME_BASE_Q) >= 0 && 511cabdff1aSopenharmony_ci pkt->flags & AV_PKT_FLAG_KEY && os->packets_written) { 512cabdff1aSopenharmony_ci 513cabdff1aSopenharmony_ci if ((ret = hds_flush(s, os, 0, pkt->dts)) < 0) 514cabdff1aSopenharmony_ci return ret; 515cabdff1aSopenharmony_ci } 516cabdff1aSopenharmony_ci 517cabdff1aSopenharmony_ci // Note, these fragment start timestamps, that represent a whole 518cabdff1aSopenharmony_ci // OutputStream, assume all streams in it have the same time base. 519cabdff1aSopenharmony_ci if (!os->packets_written) 520cabdff1aSopenharmony_ci os->frag_start_ts = pkt->dts; 521cabdff1aSopenharmony_ci os->last_ts = pkt->dts; 522cabdff1aSopenharmony_ci 523cabdff1aSopenharmony_ci os->packets_written++; 524cabdff1aSopenharmony_ci return ff_write_chained(os->ctx, pkt->stream_index - os->first_stream, pkt, s, 0); 525cabdff1aSopenharmony_ci} 526cabdff1aSopenharmony_ci 527cabdff1aSopenharmony_cistatic int hds_write_trailer(AVFormatContext *s) 528cabdff1aSopenharmony_ci{ 529cabdff1aSopenharmony_ci HDSContext *c = s->priv_data; 530cabdff1aSopenharmony_ci int i; 531cabdff1aSopenharmony_ci 532cabdff1aSopenharmony_ci for (i = 0; i < c->nb_streams; i++) 533cabdff1aSopenharmony_ci hds_flush(s, &c->streams[i], 1, c->streams[i].last_ts); 534cabdff1aSopenharmony_ci write_manifest(s, 1); 535cabdff1aSopenharmony_ci 536cabdff1aSopenharmony_ci if (c->remove_at_exit) { 537cabdff1aSopenharmony_ci char filename[1024]; 538cabdff1aSopenharmony_ci snprintf(filename, sizeof(filename), "%s/index.f4m", s->url); 539cabdff1aSopenharmony_ci unlink(filename); 540cabdff1aSopenharmony_ci for (i = 0; i < c->nb_streams; i++) { 541cabdff1aSopenharmony_ci snprintf(filename, sizeof(filename), "%s/stream%d.abst", s->url, i); 542cabdff1aSopenharmony_ci unlink(filename); 543cabdff1aSopenharmony_ci } 544cabdff1aSopenharmony_ci rmdir(s->url); 545cabdff1aSopenharmony_ci } 546cabdff1aSopenharmony_ci 547cabdff1aSopenharmony_ci return 0; 548cabdff1aSopenharmony_ci} 549cabdff1aSopenharmony_ci 550cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(HDSContext, x) 551cabdff1aSopenharmony_ci#define E AV_OPT_FLAG_ENCODING_PARAM 552cabdff1aSopenharmony_cistatic const AVOption options[] = { 553cabdff1aSopenharmony_ci { "window_size", "number of fragments kept in the manifest", OFFSET(window_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, E }, 554cabdff1aSopenharmony_ci { "extra_window_size", "number of fragments kept outside of the manifest before removing from disk", OFFSET(extra_window_size), AV_OPT_TYPE_INT, { .i64 = 5 }, 0, INT_MAX, E }, 555cabdff1aSopenharmony_ci { "min_frag_duration", "minimum fragment duration (in microseconds)", OFFSET(min_frag_duration), AV_OPT_TYPE_INT64, { .i64 = 10000000 }, 0, INT_MAX, E }, 556cabdff1aSopenharmony_ci { "remove_at_exit", "remove all fragments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, 557cabdff1aSopenharmony_ci { NULL }, 558cabdff1aSopenharmony_ci}; 559cabdff1aSopenharmony_ci 560cabdff1aSopenharmony_cistatic const AVClass hds_class = { 561cabdff1aSopenharmony_ci .class_name = "HDS muxer", 562cabdff1aSopenharmony_ci .item_name = av_default_item_name, 563cabdff1aSopenharmony_ci .option = options, 564cabdff1aSopenharmony_ci .version = LIBAVUTIL_VERSION_INT, 565cabdff1aSopenharmony_ci}; 566cabdff1aSopenharmony_ci 567cabdff1aSopenharmony_ciconst AVOutputFormat ff_hds_muxer = { 568cabdff1aSopenharmony_ci .name = "hds", 569cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("HDS Muxer"), 570cabdff1aSopenharmony_ci .priv_data_size = sizeof(HDSContext), 571cabdff1aSopenharmony_ci .audio_codec = AV_CODEC_ID_AAC, 572cabdff1aSopenharmony_ci .video_codec = AV_CODEC_ID_H264, 573cabdff1aSopenharmony_ci .flags = AVFMT_GLOBALHEADER | AVFMT_NOFILE, 574cabdff1aSopenharmony_ci .write_header = hds_write_header, 575cabdff1aSopenharmony_ci .write_packet = hds_write_packet, 576cabdff1aSopenharmony_ci .write_trailer = hds_write_trailer, 577cabdff1aSopenharmony_ci .deinit = hds_free, 578cabdff1aSopenharmony_ci .priv_class = &hds_class, 579cabdff1aSopenharmony_ci}; 580