1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * Live smooth streaming fragmenter 3cabdff1aSopenharmony_ci * Copyright (c) 2012 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#include "avc.h" 32cabdff1aSopenharmony_ci#include "url.h" 33cabdff1aSopenharmony_ci 34cabdff1aSopenharmony_ci#include "libavutil/opt.h" 35cabdff1aSopenharmony_ci#include "libavutil/avstring.h" 36cabdff1aSopenharmony_ci#include "libavutil/mathematics.h" 37cabdff1aSopenharmony_ci#include "libavutil/uuid.h" 38cabdff1aSopenharmony_ci 39cabdff1aSopenharmony_citypedef struct Fragment { 40cabdff1aSopenharmony_ci int64_t start_time, duration; 41cabdff1aSopenharmony_ci int n; 42cabdff1aSopenharmony_ci int64_t start_pos, size; 43cabdff1aSopenharmony_ci char file[1024]; 44cabdff1aSopenharmony_ci char infofile[1024]; 45cabdff1aSopenharmony_ci} Fragment; 46cabdff1aSopenharmony_ci 47cabdff1aSopenharmony_citypedef struct OutputStream { 48cabdff1aSopenharmony_ci AVFormatContext *ctx; 49cabdff1aSopenharmony_ci URLContext *out; // Current output stream where all output is written 50cabdff1aSopenharmony_ci URLContext *out2; // Auxiliary output stream where all output is also written 51cabdff1aSopenharmony_ci URLContext *tail_out; // The actual main output stream, if we're currently seeked back to write elsewhere 52cabdff1aSopenharmony_ci int64_t tail_pos, cur_pos, cur_start_pos; 53cabdff1aSopenharmony_ci int packets_written; 54cabdff1aSopenharmony_ci const char *stream_type_tag; 55cabdff1aSopenharmony_ci int nb_fragments, fragments_size, fragment_index; 56cabdff1aSopenharmony_ci Fragment **fragments; 57cabdff1aSopenharmony_ci 58cabdff1aSopenharmony_ci const char *fourcc; 59cabdff1aSopenharmony_ci char *private_str; 60cabdff1aSopenharmony_ci int packet_size; 61cabdff1aSopenharmony_ci int audio_tag; 62cabdff1aSopenharmony_ci char dirname[1024]; 63cabdff1aSopenharmony_ci uint8_t iobuf[32768]; 64cabdff1aSopenharmony_ci} OutputStream; 65cabdff1aSopenharmony_ci 66cabdff1aSopenharmony_citypedef struct SmoothStreamingContext { 67cabdff1aSopenharmony_ci const AVClass *class; /* Class for private options. */ 68cabdff1aSopenharmony_ci int window_size; 69cabdff1aSopenharmony_ci int extra_window_size; 70cabdff1aSopenharmony_ci int lookahead_count; 71cabdff1aSopenharmony_ci int min_frag_duration; 72cabdff1aSopenharmony_ci int remove_at_exit; 73cabdff1aSopenharmony_ci OutputStream *streams; 74cabdff1aSopenharmony_ci int has_video, has_audio; 75cabdff1aSopenharmony_ci int nb_fragments; 76cabdff1aSopenharmony_ci} SmoothStreamingContext; 77cabdff1aSopenharmony_ci 78cabdff1aSopenharmony_cistatic int ism_write(void *opaque, uint8_t *buf, int buf_size) 79cabdff1aSopenharmony_ci{ 80cabdff1aSopenharmony_ci OutputStream *os = opaque; 81cabdff1aSopenharmony_ci if (os->out) 82cabdff1aSopenharmony_ci ffurl_write(os->out, buf, buf_size); 83cabdff1aSopenharmony_ci if (os->out2) 84cabdff1aSopenharmony_ci ffurl_write(os->out2, buf, buf_size); 85cabdff1aSopenharmony_ci os->cur_pos += buf_size; 86cabdff1aSopenharmony_ci if (os->cur_pos >= os->tail_pos) 87cabdff1aSopenharmony_ci os->tail_pos = os->cur_pos; 88cabdff1aSopenharmony_ci return buf_size; 89cabdff1aSopenharmony_ci} 90cabdff1aSopenharmony_ci 91cabdff1aSopenharmony_cistatic int64_t ism_seek(void *opaque, int64_t offset, int whence) 92cabdff1aSopenharmony_ci{ 93cabdff1aSopenharmony_ci OutputStream *os = opaque; 94cabdff1aSopenharmony_ci int i; 95cabdff1aSopenharmony_ci if (whence != SEEK_SET) 96cabdff1aSopenharmony_ci return AVERROR(ENOSYS); 97cabdff1aSopenharmony_ci if (os->tail_out) { 98cabdff1aSopenharmony_ci ffurl_closep(&os->out); 99cabdff1aSopenharmony_ci ffurl_closep(&os->out2); 100cabdff1aSopenharmony_ci os->out = os->tail_out; 101cabdff1aSopenharmony_ci os->tail_out = NULL; 102cabdff1aSopenharmony_ci } 103cabdff1aSopenharmony_ci if (offset >= os->cur_start_pos) { 104cabdff1aSopenharmony_ci if (os->out) 105cabdff1aSopenharmony_ci ffurl_seek(os->out, offset - os->cur_start_pos, SEEK_SET); 106cabdff1aSopenharmony_ci os->cur_pos = offset; 107cabdff1aSopenharmony_ci return offset; 108cabdff1aSopenharmony_ci } 109cabdff1aSopenharmony_ci for (i = os->nb_fragments - 1; i >= 0; i--) { 110cabdff1aSopenharmony_ci Fragment *frag = os->fragments[i]; 111cabdff1aSopenharmony_ci if (offset >= frag->start_pos && offset < frag->start_pos + frag->size) { 112cabdff1aSopenharmony_ci int ret; 113cabdff1aSopenharmony_ci AVDictionary *opts = NULL; 114cabdff1aSopenharmony_ci os->tail_out = os->out; 115cabdff1aSopenharmony_ci av_dict_set(&opts, "truncate", "0", 0); 116cabdff1aSopenharmony_ci ret = ffurl_open_whitelist(&os->out, frag->file, AVIO_FLAG_WRITE, 117cabdff1aSopenharmony_ci &os->ctx->interrupt_callback, &opts, os->ctx->protocol_whitelist, os->ctx->protocol_blacklist, NULL); 118cabdff1aSopenharmony_ci av_dict_free(&opts); 119cabdff1aSopenharmony_ci if (ret < 0) { 120cabdff1aSopenharmony_ci os->out = os->tail_out; 121cabdff1aSopenharmony_ci os->tail_out = NULL; 122cabdff1aSopenharmony_ci return ret; 123cabdff1aSopenharmony_ci } 124cabdff1aSopenharmony_ci av_dict_set(&opts, "truncate", "0", 0); 125cabdff1aSopenharmony_ci ffurl_open_whitelist(&os->out2, frag->infofile, AVIO_FLAG_WRITE, 126cabdff1aSopenharmony_ci &os->ctx->interrupt_callback, &opts, os->ctx->protocol_whitelist, os->ctx->protocol_blacklist, NULL); 127cabdff1aSopenharmony_ci av_dict_free(&opts); 128cabdff1aSopenharmony_ci ffurl_seek(os->out, offset - frag->start_pos, SEEK_SET); 129cabdff1aSopenharmony_ci if (os->out2) 130cabdff1aSopenharmony_ci ffurl_seek(os->out2, offset - frag->start_pos, SEEK_SET); 131cabdff1aSopenharmony_ci os->cur_pos = offset; 132cabdff1aSopenharmony_ci return offset; 133cabdff1aSopenharmony_ci } 134cabdff1aSopenharmony_ci } 135cabdff1aSopenharmony_ci return AVERROR(EIO); 136cabdff1aSopenharmony_ci} 137cabdff1aSopenharmony_ci 138cabdff1aSopenharmony_cistatic void get_private_data(OutputStream *os) 139cabdff1aSopenharmony_ci{ 140cabdff1aSopenharmony_ci AVCodecParameters *par = os->ctx->streams[0]->codecpar; 141cabdff1aSopenharmony_ci uint8_t *ptr = par->extradata; 142cabdff1aSopenharmony_ci int size = par->extradata_size; 143cabdff1aSopenharmony_ci int i; 144cabdff1aSopenharmony_ci if (par->codec_id == AV_CODEC_ID_H264) { 145cabdff1aSopenharmony_ci ff_avc_write_annexb_extradata(ptr, &ptr, &size); 146cabdff1aSopenharmony_ci if (!ptr) 147cabdff1aSopenharmony_ci ptr = par->extradata; 148cabdff1aSopenharmony_ci } 149cabdff1aSopenharmony_ci if (!ptr) 150cabdff1aSopenharmony_ci return; 151cabdff1aSopenharmony_ci os->private_str = av_mallocz(2*size + 1); 152cabdff1aSopenharmony_ci if (!os->private_str) 153cabdff1aSopenharmony_ci goto fail; 154cabdff1aSopenharmony_ci for (i = 0; i < size; i++) 155cabdff1aSopenharmony_ci snprintf(&os->private_str[2*i], 3, "%02x", ptr[i]); 156cabdff1aSopenharmony_cifail: 157cabdff1aSopenharmony_ci if (ptr != par->extradata) 158cabdff1aSopenharmony_ci av_free(ptr); 159cabdff1aSopenharmony_ci} 160cabdff1aSopenharmony_ci 161cabdff1aSopenharmony_cistatic void ism_free(AVFormatContext *s) 162cabdff1aSopenharmony_ci{ 163cabdff1aSopenharmony_ci SmoothStreamingContext *c = s->priv_data; 164cabdff1aSopenharmony_ci int i, j; 165cabdff1aSopenharmony_ci if (!c->streams) 166cabdff1aSopenharmony_ci return; 167cabdff1aSopenharmony_ci for (i = 0; i < s->nb_streams; i++) { 168cabdff1aSopenharmony_ci OutputStream *os = &c->streams[i]; 169cabdff1aSopenharmony_ci ffurl_closep(&os->out); 170cabdff1aSopenharmony_ci ffurl_closep(&os->out2); 171cabdff1aSopenharmony_ci ffurl_closep(&os->tail_out); 172cabdff1aSopenharmony_ci if (os->ctx && os->ctx->pb) 173cabdff1aSopenharmony_ci avio_context_free(&os->ctx->pb); 174cabdff1aSopenharmony_ci avformat_free_context(os->ctx); 175cabdff1aSopenharmony_ci av_freep(&os->private_str); 176cabdff1aSopenharmony_ci for (j = 0; j < os->nb_fragments; j++) 177cabdff1aSopenharmony_ci av_freep(&os->fragments[j]); 178cabdff1aSopenharmony_ci av_freep(&os->fragments); 179cabdff1aSopenharmony_ci } 180cabdff1aSopenharmony_ci av_freep(&c->streams); 181cabdff1aSopenharmony_ci} 182cabdff1aSopenharmony_ci 183cabdff1aSopenharmony_cistatic void output_chunk_list(OutputStream *os, AVIOContext *out, int final, int skip, int window_size) 184cabdff1aSopenharmony_ci{ 185cabdff1aSopenharmony_ci int removed = 0, i, start = 0; 186cabdff1aSopenharmony_ci if (os->nb_fragments <= 0) 187cabdff1aSopenharmony_ci return; 188cabdff1aSopenharmony_ci if (os->fragments[0]->n > 0) 189cabdff1aSopenharmony_ci removed = 1; 190cabdff1aSopenharmony_ci if (final) 191cabdff1aSopenharmony_ci skip = 0; 192cabdff1aSopenharmony_ci if (window_size) 193cabdff1aSopenharmony_ci start = FFMAX(os->nb_fragments - skip - window_size, 0); 194cabdff1aSopenharmony_ci for (i = start; i < os->nb_fragments - skip; i++) { 195cabdff1aSopenharmony_ci Fragment *frag = os->fragments[i]; 196cabdff1aSopenharmony_ci if (!final || removed) 197cabdff1aSopenharmony_ci avio_printf(out, "<c t=\"%"PRIu64"\" d=\"%"PRIu64"\" />\n", frag->start_time, frag->duration); 198cabdff1aSopenharmony_ci else 199cabdff1aSopenharmony_ci avio_printf(out, "<c n=\"%d\" d=\"%"PRIu64"\" />\n", frag->n, frag->duration); 200cabdff1aSopenharmony_ci } 201cabdff1aSopenharmony_ci} 202cabdff1aSopenharmony_ci 203cabdff1aSopenharmony_cistatic int write_manifest(AVFormatContext *s, int final) 204cabdff1aSopenharmony_ci{ 205cabdff1aSopenharmony_ci SmoothStreamingContext *c = s->priv_data; 206cabdff1aSopenharmony_ci AVIOContext *out; 207cabdff1aSopenharmony_ci char filename[1024], temp_filename[1024]; 208cabdff1aSopenharmony_ci int ret, i, video_chunks = 0, audio_chunks = 0, video_streams = 0, audio_streams = 0; 209cabdff1aSopenharmony_ci int64_t duration = 0; 210cabdff1aSopenharmony_ci 211cabdff1aSopenharmony_ci snprintf(filename, sizeof(filename), "%s/Manifest", s->url); 212cabdff1aSopenharmony_ci snprintf(temp_filename, sizeof(temp_filename), "%s/Manifest.tmp", s->url); 213cabdff1aSopenharmony_ci ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, NULL); 214cabdff1aSopenharmony_ci if (ret < 0) { 215cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); 216cabdff1aSopenharmony_ci return ret; 217cabdff1aSopenharmony_ci } 218cabdff1aSopenharmony_ci avio_printf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); 219cabdff1aSopenharmony_ci for (i = 0; i < s->nb_streams; i++) { 220cabdff1aSopenharmony_ci OutputStream *os = &c->streams[i]; 221cabdff1aSopenharmony_ci if (os->nb_fragments > 0) { 222cabdff1aSopenharmony_ci Fragment *last = os->fragments[os->nb_fragments - 1]; 223cabdff1aSopenharmony_ci duration = last->start_time + last->duration; 224cabdff1aSopenharmony_ci } 225cabdff1aSopenharmony_ci if (s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { 226cabdff1aSopenharmony_ci video_chunks = os->nb_fragments; 227cabdff1aSopenharmony_ci video_streams++; 228cabdff1aSopenharmony_ci } else { 229cabdff1aSopenharmony_ci audio_chunks = os->nb_fragments; 230cabdff1aSopenharmony_ci audio_streams++; 231cabdff1aSopenharmony_ci } 232cabdff1aSopenharmony_ci } 233cabdff1aSopenharmony_ci if (!final) { 234cabdff1aSopenharmony_ci duration = 0; 235cabdff1aSopenharmony_ci video_chunks = audio_chunks = 0; 236cabdff1aSopenharmony_ci } 237cabdff1aSopenharmony_ci if (c->window_size) { 238cabdff1aSopenharmony_ci video_chunks = FFMIN(video_chunks, c->window_size); 239cabdff1aSopenharmony_ci audio_chunks = FFMIN(audio_chunks, c->window_size); 240cabdff1aSopenharmony_ci } 241cabdff1aSopenharmony_ci avio_printf(out, "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" Duration=\"%"PRIu64"\"", duration); 242cabdff1aSopenharmony_ci if (!final) 243cabdff1aSopenharmony_ci avio_printf(out, " IsLive=\"true\" LookAheadFragmentCount=\"%d\" DVRWindowLength=\"0\"", c->lookahead_count); 244cabdff1aSopenharmony_ci avio_printf(out, ">\n"); 245cabdff1aSopenharmony_ci if (c->has_video) { 246cabdff1aSopenharmony_ci int last = -1, index = 0; 247cabdff1aSopenharmony_ci avio_printf(out, "<StreamIndex Type=\"video\" QualityLevels=\"%d\" Chunks=\"%d\" Url=\"QualityLevels({bitrate})/Fragments(video={start time})\">\n", video_streams, video_chunks); 248cabdff1aSopenharmony_ci for (i = 0; i < s->nb_streams; i++) { 249cabdff1aSopenharmony_ci OutputStream *os = &c->streams[i]; 250cabdff1aSopenharmony_ci if (s->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) 251cabdff1aSopenharmony_ci continue; 252cabdff1aSopenharmony_ci last = i; 253cabdff1aSopenharmony_ci avio_printf(out, "<QualityLevel Index=\"%d\" Bitrate=\"%"PRId64"\" FourCC=\"%s\" MaxWidth=\"%d\" MaxHeight=\"%d\" CodecPrivateData=\"%s\" />\n", index, s->streams[i]->codecpar->bit_rate, os->fourcc, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height, os->private_str); 254cabdff1aSopenharmony_ci index++; 255cabdff1aSopenharmony_ci } 256cabdff1aSopenharmony_ci output_chunk_list(&c->streams[last], out, final, c->lookahead_count, c->window_size); 257cabdff1aSopenharmony_ci avio_printf(out, "</StreamIndex>\n"); 258cabdff1aSopenharmony_ci } 259cabdff1aSopenharmony_ci if (c->has_audio) { 260cabdff1aSopenharmony_ci int last = -1, index = 0; 261cabdff1aSopenharmony_ci avio_printf(out, "<StreamIndex Type=\"audio\" QualityLevels=\"%d\" Chunks=\"%d\" Url=\"QualityLevels({bitrate})/Fragments(audio={start time})\">\n", audio_streams, audio_chunks); 262cabdff1aSopenharmony_ci for (i = 0; i < s->nb_streams; i++) { 263cabdff1aSopenharmony_ci OutputStream *os = &c->streams[i]; 264cabdff1aSopenharmony_ci if (s->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) 265cabdff1aSopenharmony_ci continue; 266cabdff1aSopenharmony_ci last = i; 267cabdff1aSopenharmony_ci avio_printf(out, "<QualityLevel Index=\"%d\" Bitrate=\"%"PRId64"\" FourCC=\"%s\" SamplingRate=\"%d\" Channels=\"%d\" BitsPerSample=\"16\" PacketSize=\"%d\" AudioTag=\"%d\" CodecPrivateData=\"%s\" />\n", 268cabdff1aSopenharmony_ci index, s->streams[i]->codecpar->bit_rate, os->fourcc, s->streams[i]->codecpar->sample_rate, 269cabdff1aSopenharmony_ci s->streams[i]->codecpar->ch_layout.nb_channels, os->packet_size, os->audio_tag, os->private_str); 270cabdff1aSopenharmony_ci index++; 271cabdff1aSopenharmony_ci } 272cabdff1aSopenharmony_ci output_chunk_list(&c->streams[last], out, final, c->lookahead_count, c->window_size); 273cabdff1aSopenharmony_ci avio_printf(out, "</StreamIndex>\n"); 274cabdff1aSopenharmony_ci } 275cabdff1aSopenharmony_ci avio_printf(out, "</SmoothStreamingMedia>\n"); 276cabdff1aSopenharmony_ci avio_flush(out); 277cabdff1aSopenharmony_ci ff_format_io_close(s, &out); 278cabdff1aSopenharmony_ci return ff_rename(temp_filename, filename, s); 279cabdff1aSopenharmony_ci} 280cabdff1aSopenharmony_ci 281cabdff1aSopenharmony_cistatic int ism_write_header(AVFormatContext *s) 282cabdff1aSopenharmony_ci{ 283cabdff1aSopenharmony_ci SmoothStreamingContext *c = s->priv_data; 284cabdff1aSopenharmony_ci int ret = 0, i; 285cabdff1aSopenharmony_ci const AVOutputFormat *oformat; 286cabdff1aSopenharmony_ci 287cabdff1aSopenharmony_ci if (mkdir(s->url, 0777) == -1 && errno != EEXIST) { 288cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "mkdir failed\n"); 289cabdff1aSopenharmony_ci return AVERROR(errno); 290cabdff1aSopenharmony_ci } 291cabdff1aSopenharmony_ci 292cabdff1aSopenharmony_ci oformat = av_guess_format("ismv", NULL, NULL); 293cabdff1aSopenharmony_ci if (!oformat) { 294cabdff1aSopenharmony_ci return AVERROR_MUXER_NOT_FOUND; 295cabdff1aSopenharmony_ci } 296cabdff1aSopenharmony_ci 297cabdff1aSopenharmony_ci c->streams = av_calloc(s->nb_streams, sizeof(*c->streams)); 298cabdff1aSopenharmony_ci if (!c->streams) { 299cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 300cabdff1aSopenharmony_ci } 301cabdff1aSopenharmony_ci 302cabdff1aSopenharmony_ci for (i = 0; i < s->nb_streams; i++) { 303cabdff1aSopenharmony_ci OutputStream *os = &c->streams[i]; 304cabdff1aSopenharmony_ci AVFormatContext *ctx; 305cabdff1aSopenharmony_ci AVStream *st; 306cabdff1aSopenharmony_ci AVDictionary *opts = NULL; 307cabdff1aSopenharmony_ci 308cabdff1aSopenharmony_ci if (!s->streams[i]->codecpar->bit_rate) { 309cabdff1aSopenharmony_ci av_log(s, AV_LOG_WARNING, "No bit rate set for stream %d\n", i); 310cabdff1aSopenharmony_ci // create a tmp name for the directory of fragments 311cabdff1aSopenharmony_ci snprintf(os->dirname, sizeof(os->dirname), "%s/QualityLevels(Tmp_%d)", s->url, i); 312cabdff1aSopenharmony_ci } else { 313cabdff1aSopenharmony_ci snprintf(os->dirname, sizeof(os->dirname), "%s/QualityLevels(%"PRId64")", s->url, s->streams[i]->codecpar->bit_rate); 314cabdff1aSopenharmony_ci } 315cabdff1aSopenharmony_ci 316cabdff1aSopenharmony_ci if (mkdir(os->dirname, 0777) == -1 && errno != EEXIST) { 317cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "mkdir failed\n"); 318cabdff1aSopenharmony_ci return AVERROR(errno); 319cabdff1aSopenharmony_ci } 320cabdff1aSopenharmony_ci 321cabdff1aSopenharmony_ci os->ctx = ctx = avformat_alloc_context(); 322cabdff1aSopenharmony_ci if (!ctx) { 323cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 324cabdff1aSopenharmony_ci } 325cabdff1aSopenharmony_ci if ((ret = ff_copy_whiteblacklists(ctx, s)) < 0) 326cabdff1aSopenharmony_ci return ret; 327cabdff1aSopenharmony_ci ctx->oformat = oformat; 328cabdff1aSopenharmony_ci ctx->interrupt_callback = s->interrupt_callback; 329cabdff1aSopenharmony_ci 330cabdff1aSopenharmony_ci if (!(st = avformat_new_stream(ctx, NULL))) { 331cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 332cabdff1aSopenharmony_ci } 333cabdff1aSopenharmony_ci avcodec_parameters_copy(st->codecpar, s->streams[i]->codecpar); 334cabdff1aSopenharmony_ci st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio; 335cabdff1aSopenharmony_ci st->time_base = s->streams[i]->time_base; 336cabdff1aSopenharmony_ci 337cabdff1aSopenharmony_ci ctx->pb = avio_alloc_context(os->iobuf, sizeof(os->iobuf), 1, os, NULL, ism_write, ism_seek); 338cabdff1aSopenharmony_ci if (!ctx->pb) { 339cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 340cabdff1aSopenharmony_ci } 341cabdff1aSopenharmony_ci 342cabdff1aSopenharmony_ci av_dict_set_int(&opts, "ism_lookahead", c->lookahead_count, 0); 343cabdff1aSopenharmony_ci av_dict_set(&opts, "movflags", "frag_custom", 0); 344cabdff1aSopenharmony_ci ret = avformat_write_header(ctx, &opts); 345cabdff1aSopenharmony_ci av_dict_free(&opts); 346cabdff1aSopenharmony_ci if (ret < 0) { 347cabdff1aSopenharmony_ci return ret; 348cabdff1aSopenharmony_ci } 349cabdff1aSopenharmony_ci avio_flush(ctx->pb); 350cabdff1aSopenharmony_ci s->streams[i]->time_base = st->time_base; 351cabdff1aSopenharmony_ci if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { 352cabdff1aSopenharmony_ci c->has_video = 1; 353cabdff1aSopenharmony_ci os->stream_type_tag = "video"; 354cabdff1aSopenharmony_ci if (st->codecpar->codec_id == AV_CODEC_ID_H264) { 355cabdff1aSopenharmony_ci os->fourcc = "H264"; 356cabdff1aSopenharmony_ci } else if (st->codecpar->codec_id == AV_CODEC_ID_VC1) { 357cabdff1aSopenharmony_ci os->fourcc = "WVC1"; 358cabdff1aSopenharmony_ci } else { 359cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Unsupported video codec\n"); 360cabdff1aSopenharmony_ci return AVERROR(EINVAL); 361cabdff1aSopenharmony_ci } 362cabdff1aSopenharmony_ci } else { 363cabdff1aSopenharmony_ci c->has_audio = 1; 364cabdff1aSopenharmony_ci os->stream_type_tag = "audio"; 365cabdff1aSopenharmony_ci if (st->codecpar->codec_id == AV_CODEC_ID_AAC) { 366cabdff1aSopenharmony_ci os->fourcc = "AACL"; 367cabdff1aSopenharmony_ci os->audio_tag = 0xff; 368cabdff1aSopenharmony_ci } else if (st->codecpar->codec_id == AV_CODEC_ID_WMAPRO) { 369cabdff1aSopenharmony_ci os->fourcc = "WMAP"; 370cabdff1aSopenharmony_ci os->audio_tag = 0x0162; 371cabdff1aSopenharmony_ci } else { 372cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Unsupported audio codec\n"); 373cabdff1aSopenharmony_ci return AVERROR(EINVAL); 374cabdff1aSopenharmony_ci } 375cabdff1aSopenharmony_ci os->packet_size = st->codecpar->block_align ? st->codecpar->block_align : 4; 376cabdff1aSopenharmony_ci } 377cabdff1aSopenharmony_ci get_private_data(os); 378cabdff1aSopenharmony_ci } 379cabdff1aSopenharmony_ci 380cabdff1aSopenharmony_ci if (!c->has_video && c->min_frag_duration <= 0) { 381cabdff1aSopenharmony_ci av_log(s, AV_LOG_WARNING, "no video stream and no min frag duration set\n"); 382cabdff1aSopenharmony_ci return AVERROR(EINVAL); 383cabdff1aSopenharmony_ci } 384cabdff1aSopenharmony_ci ret = write_manifest(s, 0); 385cabdff1aSopenharmony_ci if (ret < 0) 386cabdff1aSopenharmony_ci return ret; 387cabdff1aSopenharmony_ci 388cabdff1aSopenharmony_ci return 0; 389cabdff1aSopenharmony_ci} 390cabdff1aSopenharmony_ci 391cabdff1aSopenharmony_cistatic int parse_fragment(AVFormatContext *s, const char *filename, int64_t *start_ts, int64_t *duration, int64_t *moof_size, int64_t size) 392cabdff1aSopenharmony_ci{ 393cabdff1aSopenharmony_ci AVIOContext *in; 394cabdff1aSopenharmony_ci int ret; 395cabdff1aSopenharmony_ci uint32_t len; 396cabdff1aSopenharmony_ci if ((ret = s->io_open(s, &in, filename, AVIO_FLAG_READ, NULL)) < 0) 397cabdff1aSopenharmony_ci return ret; 398cabdff1aSopenharmony_ci ret = AVERROR(EIO); 399cabdff1aSopenharmony_ci *moof_size = avio_rb32(in); 400cabdff1aSopenharmony_ci if (*moof_size < 8 || *moof_size > size) 401cabdff1aSopenharmony_ci goto fail; 402cabdff1aSopenharmony_ci if (avio_rl32(in) != MKTAG('m','o','o','f')) 403cabdff1aSopenharmony_ci goto fail; 404cabdff1aSopenharmony_ci len = avio_rb32(in); 405cabdff1aSopenharmony_ci if (len > *moof_size) 406cabdff1aSopenharmony_ci goto fail; 407cabdff1aSopenharmony_ci if (avio_rl32(in) != MKTAG('m','f','h','d')) 408cabdff1aSopenharmony_ci goto fail; 409cabdff1aSopenharmony_ci avio_seek(in, len - 8, SEEK_CUR); 410cabdff1aSopenharmony_ci avio_rb32(in); /* traf size */ 411cabdff1aSopenharmony_ci if (avio_rl32(in) != MKTAG('t','r','a','f')) 412cabdff1aSopenharmony_ci goto fail; 413cabdff1aSopenharmony_ci while (avio_tell(in) < *moof_size) { 414cabdff1aSopenharmony_ci uint32_t len = avio_rb32(in); 415cabdff1aSopenharmony_ci uint32_t tag = avio_rl32(in); 416cabdff1aSopenharmony_ci int64_t end = avio_tell(in) + len - 8; 417cabdff1aSopenharmony_ci if (len < 8 || len >= *moof_size) 418cabdff1aSopenharmony_ci goto fail; 419cabdff1aSopenharmony_ci if (tag == MKTAG('u','u','i','d')) { 420cabdff1aSopenharmony_ci static const AVUUID tfxd = { 421cabdff1aSopenharmony_ci 0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6, 422cabdff1aSopenharmony_ci 0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2 423cabdff1aSopenharmony_ci }; 424cabdff1aSopenharmony_ci AVUUID uuid; 425cabdff1aSopenharmony_ci avio_read(in, uuid, 16); 426cabdff1aSopenharmony_ci if (av_uuid_equal(uuid, tfxd) && len >= 8 + 16 + 4 + 16) { 427cabdff1aSopenharmony_ci avio_seek(in, 4, SEEK_CUR); 428cabdff1aSopenharmony_ci *start_ts = avio_rb64(in); 429cabdff1aSopenharmony_ci *duration = avio_rb64(in); 430cabdff1aSopenharmony_ci ret = 0; 431cabdff1aSopenharmony_ci break; 432cabdff1aSopenharmony_ci } 433cabdff1aSopenharmony_ci } 434cabdff1aSopenharmony_ci avio_seek(in, end, SEEK_SET); 435cabdff1aSopenharmony_ci } 436cabdff1aSopenharmony_cifail: 437cabdff1aSopenharmony_ci ff_format_io_close(s, &in); 438cabdff1aSopenharmony_ci return ret; 439cabdff1aSopenharmony_ci} 440cabdff1aSopenharmony_ci 441cabdff1aSopenharmony_cistatic int add_fragment(OutputStream *os, const char *file, const char *infofile, int64_t start_time, int64_t duration, int64_t start_pos, int64_t size) 442cabdff1aSopenharmony_ci{ 443cabdff1aSopenharmony_ci int err; 444cabdff1aSopenharmony_ci Fragment *frag; 445cabdff1aSopenharmony_ci if (os->nb_fragments >= os->fragments_size) { 446cabdff1aSopenharmony_ci os->fragments_size = (os->fragments_size + 1) * 2; 447cabdff1aSopenharmony_ci if ((err = av_reallocp_array(&os->fragments, sizeof(*os->fragments), 448cabdff1aSopenharmony_ci os->fragments_size)) < 0) { 449cabdff1aSopenharmony_ci os->fragments_size = 0; 450cabdff1aSopenharmony_ci os->nb_fragments = 0; 451cabdff1aSopenharmony_ci return err; 452cabdff1aSopenharmony_ci } 453cabdff1aSopenharmony_ci } 454cabdff1aSopenharmony_ci frag = av_mallocz(sizeof(*frag)); 455cabdff1aSopenharmony_ci if (!frag) 456cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 457cabdff1aSopenharmony_ci av_strlcpy(frag->file, file, sizeof(frag->file)); 458cabdff1aSopenharmony_ci av_strlcpy(frag->infofile, infofile, sizeof(frag->infofile)); 459cabdff1aSopenharmony_ci frag->start_time = start_time; 460cabdff1aSopenharmony_ci frag->duration = duration; 461cabdff1aSopenharmony_ci frag->start_pos = start_pos; 462cabdff1aSopenharmony_ci frag->size = size; 463cabdff1aSopenharmony_ci frag->n = os->fragment_index; 464cabdff1aSopenharmony_ci os->fragments[os->nb_fragments++] = frag; 465cabdff1aSopenharmony_ci os->fragment_index++; 466cabdff1aSopenharmony_ci return 0; 467cabdff1aSopenharmony_ci} 468cabdff1aSopenharmony_ci 469cabdff1aSopenharmony_cistatic int copy_moof(AVFormatContext *s, const char* infile, const char *outfile, int64_t size) 470cabdff1aSopenharmony_ci{ 471cabdff1aSopenharmony_ci AVIOContext *in, *out; 472cabdff1aSopenharmony_ci int ret = 0; 473cabdff1aSopenharmony_ci if ((ret = s->io_open(s, &in, infile, AVIO_FLAG_READ, NULL)) < 0) 474cabdff1aSopenharmony_ci return ret; 475cabdff1aSopenharmony_ci if ((ret = s->io_open(s, &out, outfile, AVIO_FLAG_WRITE, NULL)) < 0) { 476cabdff1aSopenharmony_ci ff_format_io_close(s, &in); 477cabdff1aSopenharmony_ci return ret; 478cabdff1aSopenharmony_ci } 479cabdff1aSopenharmony_ci while (size > 0) { 480cabdff1aSopenharmony_ci uint8_t buf[8192]; 481cabdff1aSopenharmony_ci int n = FFMIN(size, sizeof(buf)); 482cabdff1aSopenharmony_ci n = avio_read(in, buf, n); 483cabdff1aSopenharmony_ci if (n <= 0) { 484cabdff1aSopenharmony_ci ret = AVERROR(EIO); 485cabdff1aSopenharmony_ci break; 486cabdff1aSopenharmony_ci } 487cabdff1aSopenharmony_ci avio_write(out, buf, n); 488cabdff1aSopenharmony_ci size -= n; 489cabdff1aSopenharmony_ci } 490cabdff1aSopenharmony_ci avio_flush(out); 491cabdff1aSopenharmony_ci ff_format_io_close(s, &out); 492cabdff1aSopenharmony_ci ff_format_io_close(s, &in); 493cabdff1aSopenharmony_ci return ret; 494cabdff1aSopenharmony_ci} 495cabdff1aSopenharmony_ci 496cabdff1aSopenharmony_cistatic int ism_flush(AVFormatContext *s, int final) 497cabdff1aSopenharmony_ci{ 498cabdff1aSopenharmony_ci SmoothStreamingContext *c = s->priv_data; 499cabdff1aSopenharmony_ci int i, ret = 0; 500cabdff1aSopenharmony_ci 501cabdff1aSopenharmony_ci for (i = 0; i < s->nb_streams; i++) { 502cabdff1aSopenharmony_ci OutputStream *os = &c->streams[i]; 503cabdff1aSopenharmony_ci char filename[1024], target_filename[1024], header_filename[1024], curr_dirname[1024]; 504cabdff1aSopenharmony_ci int64_t size; 505cabdff1aSopenharmony_ci int64_t start_ts, duration, moof_size; 506cabdff1aSopenharmony_ci if (!os->packets_written) 507cabdff1aSopenharmony_ci continue; 508cabdff1aSopenharmony_ci 509cabdff1aSopenharmony_ci snprintf(filename, sizeof(filename), "%s/temp", os->dirname); 510cabdff1aSopenharmony_ci ret = ffurl_open_whitelist(&os->out, filename, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist, NULL); 511cabdff1aSopenharmony_ci if (ret < 0) 512cabdff1aSopenharmony_ci break; 513cabdff1aSopenharmony_ci os->cur_start_pos = os->tail_pos; 514cabdff1aSopenharmony_ci av_write_frame(os->ctx, NULL); 515cabdff1aSopenharmony_ci avio_flush(os->ctx->pb); 516cabdff1aSopenharmony_ci os->packets_written = 0; 517cabdff1aSopenharmony_ci if (!os->out || os->tail_out) 518cabdff1aSopenharmony_ci return AVERROR(EIO); 519cabdff1aSopenharmony_ci 520cabdff1aSopenharmony_ci ffurl_closep(&os->out); 521cabdff1aSopenharmony_ci size = os->tail_pos - os->cur_start_pos; 522cabdff1aSopenharmony_ci if ((ret = parse_fragment(s, filename, &start_ts, &duration, &moof_size, size)) < 0) 523cabdff1aSopenharmony_ci break; 524cabdff1aSopenharmony_ci 525cabdff1aSopenharmony_ci if (!s->streams[i]->codecpar->bit_rate) { 526cabdff1aSopenharmony_ci int64_t bitrate = (int64_t) size * 8 * AV_TIME_BASE / av_rescale_q(duration, s->streams[i]->time_base, AV_TIME_BASE_Q); 527cabdff1aSopenharmony_ci if (!bitrate) { 528cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "calculating bitrate got zero.\n"); 529cabdff1aSopenharmony_ci ret = AVERROR(EINVAL); 530cabdff1aSopenharmony_ci return ret; 531cabdff1aSopenharmony_ci } 532cabdff1aSopenharmony_ci 533cabdff1aSopenharmony_ci av_log(s, AV_LOG_DEBUG, "calculated bitrate: %"PRId64"\n", bitrate); 534cabdff1aSopenharmony_ci s->streams[i]->codecpar->bit_rate = bitrate; 535cabdff1aSopenharmony_ci memcpy(curr_dirname, os->dirname, sizeof(os->dirname)); 536cabdff1aSopenharmony_ci snprintf(os->dirname, sizeof(os->dirname), "%s/QualityLevels(%"PRId64")", s->url, s->streams[i]->codecpar->bit_rate); 537cabdff1aSopenharmony_ci snprintf(filename, sizeof(filename), "%s/temp", os->dirname); 538cabdff1aSopenharmony_ci 539cabdff1aSopenharmony_ci // rename the tmp folder back to the correct name since we now have the bitrate 540cabdff1aSopenharmony_ci if ((ret = ff_rename((const char*)curr_dirname, os->dirname, s)) < 0) 541cabdff1aSopenharmony_ci return ret; 542cabdff1aSopenharmony_ci } 543cabdff1aSopenharmony_ci 544cabdff1aSopenharmony_ci snprintf(header_filename, sizeof(header_filename), "%s/FragmentInfo(%s=%"PRIu64")", os->dirname, os->stream_type_tag, start_ts); 545cabdff1aSopenharmony_ci snprintf(target_filename, sizeof(target_filename), "%s/Fragments(%s=%"PRIu64")", os->dirname, os->stream_type_tag, start_ts); 546cabdff1aSopenharmony_ci copy_moof(s, filename, header_filename, moof_size); 547cabdff1aSopenharmony_ci ret = ff_rename(filename, target_filename, s); 548cabdff1aSopenharmony_ci if (ret < 0) 549cabdff1aSopenharmony_ci break; 550cabdff1aSopenharmony_ci add_fragment(os, target_filename, header_filename, start_ts, duration, 551cabdff1aSopenharmony_ci os->cur_start_pos, size); 552cabdff1aSopenharmony_ci } 553cabdff1aSopenharmony_ci 554cabdff1aSopenharmony_ci if (c->window_size || (final && c->remove_at_exit)) { 555cabdff1aSopenharmony_ci for (i = 0; i < s->nb_streams; i++) { 556cabdff1aSopenharmony_ci OutputStream *os = &c->streams[i]; 557cabdff1aSopenharmony_ci int j; 558cabdff1aSopenharmony_ci int remove = os->nb_fragments - c->window_size - c->extra_window_size - c->lookahead_count; 559cabdff1aSopenharmony_ci if (final && c->remove_at_exit) 560cabdff1aSopenharmony_ci remove = os->nb_fragments; 561cabdff1aSopenharmony_ci if (remove > 0) { 562cabdff1aSopenharmony_ci for (j = 0; j < remove; j++) { 563cabdff1aSopenharmony_ci unlink(os->fragments[j]->file); 564cabdff1aSopenharmony_ci unlink(os->fragments[j]->infofile); 565cabdff1aSopenharmony_ci av_freep(&os->fragments[j]); 566cabdff1aSopenharmony_ci } 567cabdff1aSopenharmony_ci os->nb_fragments -= remove; 568cabdff1aSopenharmony_ci memmove(os->fragments, os->fragments + remove, os->nb_fragments * sizeof(*os->fragments)); 569cabdff1aSopenharmony_ci } 570cabdff1aSopenharmony_ci if (final && c->remove_at_exit) 571cabdff1aSopenharmony_ci rmdir(os->dirname); 572cabdff1aSopenharmony_ci } 573cabdff1aSopenharmony_ci } 574cabdff1aSopenharmony_ci 575cabdff1aSopenharmony_ci if (ret >= 0) 576cabdff1aSopenharmony_ci ret = write_manifest(s, final); 577cabdff1aSopenharmony_ci return ret; 578cabdff1aSopenharmony_ci} 579cabdff1aSopenharmony_ci 580cabdff1aSopenharmony_cistatic int ism_write_packet(AVFormatContext *s, AVPacket *pkt) 581cabdff1aSopenharmony_ci{ 582cabdff1aSopenharmony_ci SmoothStreamingContext *c = s->priv_data; 583cabdff1aSopenharmony_ci AVStream *st = s->streams[pkt->stream_index]; 584cabdff1aSopenharmony_ci FFStream *const sti = ffstream(st); 585cabdff1aSopenharmony_ci OutputStream *os = &c->streams[pkt->stream_index]; 586cabdff1aSopenharmony_ci int64_t end_dts = (c->nb_fragments + 1) * (int64_t) c->min_frag_duration; 587cabdff1aSopenharmony_ci int ret; 588cabdff1aSopenharmony_ci 589cabdff1aSopenharmony_ci if (sti->first_dts == AV_NOPTS_VALUE) 590cabdff1aSopenharmony_ci sti->first_dts = pkt->dts; 591cabdff1aSopenharmony_ci 592cabdff1aSopenharmony_ci if ((!c->has_video || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && 593cabdff1aSopenharmony_ci av_compare_ts(pkt->dts - sti->first_dts, st->time_base, 594cabdff1aSopenharmony_ci end_dts, AV_TIME_BASE_Q) >= 0 && 595cabdff1aSopenharmony_ci pkt->flags & AV_PKT_FLAG_KEY && os->packets_written) { 596cabdff1aSopenharmony_ci 597cabdff1aSopenharmony_ci if ((ret = ism_flush(s, 0)) < 0) 598cabdff1aSopenharmony_ci return ret; 599cabdff1aSopenharmony_ci c->nb_fragments++; 600cabdff1aSopenharmony_ci } 601cabdff1aSopenharmony_ci 602cabdff1aSopenharmony_ci os->packets_written++; 603cabdff1aSopenharmony_ci return ff_write_chained(os->ctx, 0, pkt, s, 0); 604cabdff1aSopenharmony_ci} 605cabdff1aSopenharmony_ci 606cabdff1aSopenharmony_cistatic int ism_write_trailer(AVFormatContext *s) 607cabdff1aSopenharmony_ci{ 608cabdff1aSopenharmony_ci SmoothStreamingContext *c = s->priv_data; 609cabdff1aSopenharmony_ci ism_flush(s, 1); 610cabdff1aSopenharmony_ci 611cabdff1aSopenharmony_ci if (c->remove_at_exit) { 612cabdff1aSopenharmony_ci char filename[1024]; 613cabdff1aSopenharmony_ci snprintf(filename, sizeof(filename), "%s/Manifest", s->url); 614cabdff1aSopenharmony_ci unlink(filename); 615cabdff1aSopenharmony_ci rmdir(s->url); 616cabdff1aSopenharmony_ci } 617cabdff1aSopenharmony_ci 618cabdff1aSopenharmony_ci return 0; 619cabdff1aSopenharmony_ci} 620cabdff1aSopenharmony_ci 621cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(SmoothStreamingContext, x) 622cabdff1aSopenharmony_ci#define E AV_OPT_FLAG_ENCODING_PARAM 623cabdff1aSopenharmony_cistatic const AVOption options[] = { 624cabdff1aSopenharmony_ci { "window_size", "number of fragments kept in the manifest", OFFSET(window_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, E }, 625cabdff1aSopenharmony_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 }, 626cabdff1aSopenharmony_ci { "lookahead_count", "number of lookahead fragments", OFFSET(lookahead_count), AV_OPT_TYPE_INT, { .i64 = 2 }, 0, INT_MAX, E }, 627cabdff1aSopenharmony_ci { "min_frag_duration", "minimum fragment duration (in microseconds)", OFFSET(min_frag_duration), AV_OPT_TYPE_INT64, { .i64 = 5000000 }, 0, INT_MAX, E }, 628cabdff1aSopenharmony_ci { "remove_at_exit", "remove all fragments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, 629cabdff1aSopenharmony_ci { NULL }, 630cabdff1aSopenharmony_ci}; 631cabdff1aSopenharmony_ci 632cabdff1aSopenharmony_cistatic const AVClass ism_class = { 633cabdff1aSopenharmony_ci .class_name = "smooth streaming muxer", 634cabdff1aSopenharmony_ci .item_name = av_default_item_name, 635cabdff1aSopenharmony_ci .option = options, 636cabdff1aSopenharmony_ci .version = LIBAVUTIL_VERSION_INT, 637cabdff1aSopenharmony_ci}; 638cabdff1aSopenharmony_ci 639cabdff1aSopenharmony_ci 640cabdff1aSopenharmony_ciconst AVOutputFormat ff_smoothstreaming_muxer = { 641cabdff1aSopenharmony_ci .name = "smoothstreaming", 642cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("Smooth Streaming Muxer"), 643cabdff1aSopenharmony_ci .priv_data_size = sizeof(SmoothStreamingContext), 644cabdff1aSopenharmony_ci .audio_codec = AV_CODEC_ID_AAC, 645cabdff1aSopenharmony_ci .video_codec = AV_CODEC_ID_H264, 646cabdff1aSopenharmony_ci .flags = AVFMT_GLOBALHEADER | AVFMT_NOFILE, 647cabdff1aSopenharmony_ci .write_header = ism_write_header, 648cabdff1aSopenharmony_ci .write_packet = ism_write_packet, 649cabdff1aSopenharmony_ci .write_trailer = ism_write_trailer, 650cabdff1aSopenharmony_ci .deinit = ism_free, 651cabdff1aSopenharmony_ci .priv_class = &ism_class, 652cabdff1aSopenharmony_ci}; 653