2 * Live smooth streaming fragmenter
3 * Copyright (c) 2012 Martin Storsjo
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30 #include "os_support.h"
34 #include "libavutil/mem.h"
35 #include "libavutil/opt.h"
36 #include "libavutil/avstring.h"
37 #include "libavutil/mathematics.h"
38 #include "libavutil/uuid.h"
40 typedef struct Fragment
{
41 int64_t start_time
, duration
;
43 int64_t start_pos
, size
;
48 typedef struct OutputStream
{
50 URLContext
*out
; // Current output stream where all output is written
51 URLContext
*out2
; // Auxiliary output stream where all output is also written
52 URLContext
*tail_out
; // The actual main output stream, if we're currently seeked back to write elsewhere
53 int64_t tail_pos
, cur_pos
, cur_start_pos
;
55 const char *stream_type_tag
;
56 int nb_fragments
, fragments_size
, fragment_index
;
67 typedef struct SmoothStreamingContext
{
68 const AVClass
*class; /* Class for private options. */
70 int extra_window_size
;
72 int min_frag_duration
;
74 OutputStream
*streams
;
75 int has_video
, has_audio
;
77 } SmoothStreamingContext
;
79 static int ism_write(void *opaque
, const uint8_t *buf
, int buf_size
)
81 OutputStream
*os
= opaque
;
83 ffurl_write(os
->out
, buf
, buf_size
);
85 ffurl_write(os
->out2
, buf
, buf_size
);
86 os
->cur_pos
+= buf_size
;
87 if (os
->cur_pos
>= os
->tail_pos
)
88 os
->tail_pos
= os
->cur_pos
;
92 static int64_t ism_seek(void *opaque
, int64_t offset
, int whence
)
94 OutputStream
*os
= opaque
;
96 if (whence
!= SEEK_SET
)
97 return AVERROR(ENOSYS
);
99 ffurl_closep(&os
->out
);
100 ffurl_closep(&os
->out2
);
101 os
->out
= os
->tail_out
;
104 if (offset
>= os
->cur_start_pos
) {
106 ffurl_seek(os
->out
, offset
- os
->cur_start_pos
, SEEK_SET
);
107 os
->cur_pos
= offset
;
110 for (i
= os
->nb_fragments
- 1; i
>= 0; i
--) {
111 Fragment
*frag
= os
->fragments
[i
];
112 if (offset
>= frag
->start_pos
&& offset
< frag
->start_pos
+ frag
->size
) {
114 AVDictionary
*opts
= NULL
;
115 os
->tail_out
= os
->out
;
116 av_dict_set(&opts
, "truncate", "0", 0);
117 ret
= ffurl_open_whitelist(&os
->out
, frag
->file
, AVIO_FLAG_WRITE
,
118 &os
->ctx
->interrupt_callback
, &opts
, os
->ctx
->protocol_whitelist
, os
->ctx
->protocol_blacklist
, NULL
);
121 os
->out
= os
->tail_out
;
125 av_dict_set(&opts
, "truncate", "0", 0);
126 ffurl_open_whitelist(&os
->out2
, frag
->infofile
, AVIO_FLAG_WRITE
,
127 &os
->ctx
->interrupt_callback
, &opts
, os
->ctx
->protocol_whitelist
, os
->ctx
->protocol_blacklist
, NULL
);
129 ffurl_seek(os
->out
, offset
- frag
->start_pos
, SEEK_SET
);
131 ffurl_seek(os
->out2
, offset
- frag
->start_pos
, SEEK_SET
);
132 os
->cur_pos
= offset
;
139 static void get_private_data(OutputStream
*os
)
141 AVCodecParameters
*par
= os
->ctx
->streams
[0]->codecpar
;
142 uint8_t *ptr
= par
->extradata
;
143 int size
= par
->extradata_size
;
145 if (par
->codec_id
== AV_CODEC_ID_H264
) {
146 ff_avc_write_annexb_extradata(ptr
, &ptr
, &size
);
148 ptr
= par
->extradata
;
152 os
->private_str
= av_mallocz(2*size
+ 1);
153 if (!os
->private_str
)
155 for (i
= 0; i
< size
; i
++)
156 snprintf(&os
->private_str
[2*i
], 3, "%02x", ptr
[i
]);
158 if (ptr
!= par
->extradata
)
162 static void ism_free(AVFormatContext
*s
)
164 SmoothStreamingContext
*c
= s
->priv_data
;
168 for (i
= 0; i
< s
->nb_streams
; i
++) {
169 OutputStream
*os
= &c
->streams
[i
];
170 ffurl_closep(&os
->out
);
171 ffurl_closep(&os
->out2
);
172 ffurl_closep(&os
->tail_out
);
173 if (os
->ctx
&& os
->ctx
->pb
)
174 avio_context_free(&os
->ctx
->pb
);
175 avformat_free_context(os
->ctx
);
176 av_freep(&os
->private_str
);
177 for (j
= 0; j
< os
->nb_fragments
; j
++)
178 av_freep(&os
->fragments
[j
]);
179 av_freep(&os
->fragments
);
181 av_freep(&c
->streams
);
184 static void output_chunk_list(OutputStream
*os
, AVIOContext
*out
, int final
, int skip
, int window_size
)
186 int removed
= 0, i
, start
= 0;
187 if (os
->nb_fragments
<= 0)
189 if (os
->fragments
[0]->n
> 0)
194 start
= FFMAX(os
->nb_fragments
- skip
- window_size
, 0);
195 for (i
= start
; i
< os
->nb_fragments
- skip
; i
++) {
196 Fragment
*frag
= os
->fragments
[i
];
197 if (!final
|| removed
)
198 avio_printf(out
, "<c t=\"%"PRIu64
"\" d=\"%"PRIu64
"\" />\n", frag
->start_time
, frag
->duration
);
200 avio_printf(out
, "<c n=\"%d\" d=\"%"PRIu64
"\" />\n", frag
->n
, frag
->duration
);
204 static int write_manifest(AVFormatContext
*s
, int final
)
206 SmoothStreamingContext
*c
= s
->priv_data
;
208 char filename
[1024], temp_filename
[1024];
209 int ret
, i
, video_chunks
= 0, audio_chunks
= 0, video_streams
= 0, audio_streams
= 0;
210 int64_t duration
= 0;
212 snprintf(filename
, sizeof(filename
), "%s/Manifest", s
->url
);
213 snprintf(temp_filename
, sizeof(temp_filename
), "%s/Manifest.tmp", s
->url
);
214 ret
= s
->io_open(s
, &out
, temp_filename
, AVIO_FLAG_WRITE
, NULL
);
216 av_log(s
, AV_LOG_ERROR
, "Unable to open %s for writing\n", temp_filename
);
219 avio_printf(out
, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
220 for (i
= 0; i
< s
->nb_streams
; i
++) {
221 OutputStream
*os
= &c
->streams
[i
];
222 if (os
->nb_fragments
> 0) {
223 Fragment
*last
= os
->fragments
[os
->nb_fragments
- 1];
224 duration
= last
->start_time
+ last
->duration
;
226 if (s
->streams
[i
]->codecpar
->codec_type
== AVMEDIA_TYPE_VIDEO
) {
227 video_chunks
= os
->nb_fragments
;
230 audio_chunks
= os
->nb_fragments
;
236 video_chunks
= audio_chunks
= 0;
238 if (c
->window_size
) {
239 video_chunks
= FFMIN(video_chunks
, c
->window_size
);
240 audio_chunks
= FFMIN(audio_chunks
, c
->window_size
);
242 avio_printf(out
, "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" Duration=\"%"PRIu64
"\"", duration
);
244 avio_printf(out
, " IsLive=\"true\" LookAheadFragmentCount=\"%d\" DVRWindowLength=\"0\"", c
->lookahead_count
);
245 avio_printf(out
, ">\n");
247 int last
= -1, index
= 0;
248 avio_printf(out
, "<StreamIndex Type=\"video\" QualityLevels=\"%d\" Chunks=\"%d\" Url=\"QualityLevels({bitrate})/Fragments(video={start time})\">\n", video_streams
, video_chunks
);
249 for (i
= 0; i
< s
->nb_streams
; i
++) {
250 OutputStream
*os
= &c
->streams
[i
];
251 if (s
->streams
[i
]->codecpar
->codec_type
!= AVMEDIA_TYPE_VIDEO
)
254 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
);
257 output_chunk_list(&c
->streams
[last
], out
, final
, c
->lookahead_count
, c
->window_size
);
258 avio_printf(out
, "</StreamIndex>\n");
261 int last
= -1, index
= 0;
262 avio_printf(out
, "<StreamIndex Type=\"audio\" QualityLevels=\"%d\" Chunks=\"%d\" Url=\"QualityLevels({bitrate})/Fragments(audio={start time})\">\n", audio_streams
, audio_chunks
);
263 for (i
= 0; i
< s
->nb_streams
; i
++) {
264 OutputStream
*os
= &c
->streams
[i
];
265 if (s
->streams
[i
]->codecpar
->codec_type
!= AVMEDIA_TYPE_AUDIO
)
268 avio_printf(out
, "<QualityLevel Index=\"%d\" Bitrate=\"%"PRId64
"\" FourCC=\"%s\" SamplingRate=\"%d\" Channels=\"%d\" BitsPerSample=\"16\" PacketSize=\"%d\" AudioTag=\"%d\" CodecPrivateData=\"%s\" />\n",
269 index
, s
->streams
[i
]->codecpar
->bit_rate
, os
->fourcc
, s
->streams
[i
]->codecpar
->sample_rate
,
270 s
->streams
[i
]->codecpar
->ch_layout
.nb_channels
, os
->packet_size
, os
->audio_tag
, os
->private_str
);
273 output_chunk_list(&c
->streams
[last
], out
, final
, c
->lookahead_count
, c
->window_size
);
274 avio_printf(out
, "</StreamIndex>\n");
276 avio_printf(out
, "</SmoothStreamingMedia>\n");
278 ff_format_io_close(s
, &out
);
279 return ff_rename(temp_filename
, filename
, s
);
282 static int ism_write_header(AVFormatContext
*s
)
284 SmoothStreamingContext
*c
= s
->priv_data
;
286 const AVOutputFormat
*oformat
;
288 if (mkdir(s
->url
, 0777) == -1 && errno
!= EEXIST
) {
289 av_log(s
, AV_LOG_ERROR
, "mkdir failed\n");
290 return AVERROR(errno
);
293 oformat
= av_guess_format("ismv", NULL
, NULL
);
295 return AVERROR_MUXER_NOT_FOUND
;
298 c
->streams
= av_calloc(s
->nb_streams
, sizeof(*c
->streams
));
300 return AVERROR(ENOMEM
);
303 for (i
= 0; i
< s
->nb_streams
; i
++) {
304 OutputStream
*os
= &c
->streams
[i
];
305 AVFormatContext
*ctx
;
307 AVDictionary
*opts
= NULL
;
309 if (!s
->streams
[i
]->codecpar
->bit_rate
) {
310 av_log(s
, AV_LOG_WARNING
, "No bit rate set for stream %d\n", i
);
311 // create a tmp name for the directory of fragments
312 snprintf(os
->dirname
, sizeof(os
->dirname
), "%s/QualityLevels(Tmp_%d)", s
->url
, i
);
314 snprintf(os
->dirname
, sizeof(os
->dirname
), "%s/QualityLevels(%"PRId64
")", s
->url
, s
->streams
[i
]->codecpar
->bit_rate
);
317 if (mkdir(os
->dirname
, 0777) == -1 && errno
!= EEXIST
) {
318 av_log(s
, AV_LOG_ERROR
, "mkdir failed\n");
319 return AVERROR(errno
);
322 os
->ctx
= ctx
= avformat_alloc_context();
324 return AVERROR(ENOMEM
);
326 if ((ret
= ff_copy_whiteblacklists(ctx
, s
)) < 0)
328 ctx
->oformat
= oformat
;
329 ctx
->interrupt_callback
= s
->interrupt_callback
;
331 if (!(st
= avformat_new_stream(ctx
, NULL
))) {
332 return AVERROR(ENOMEM
);
334 if ((ret
= avcodec_parameters_copy(st
->codecpar
, s
->streams
[i
]->codecpar
)) < 0) {
337 st
->sample_aspect_ratio
= s
->streams
[i
]->sample_aspect_ratio
;
338 st
->time_base
= s
->streams
[i
]->time_base
;
340 ctx
->pb
= avio_alloc_context(os
->iobuf
, sizeof(os
->iobuf
), 1, os
, NULL
, ism_write
, ism_seek
);
342 return AVERROR(ENOMEM
);
345 av_dict_set_int(&opts
, "ism_lookahead", c
->lookahead_count
, 0);
346 av_dict_set(&opts
, "movflags", "+frag_custom", 0);
347 ret
= avformat_write_header(ctx
, &opts
);
353 s
->streams
[i
]->time_base
= st
->time_base
;
354 if (st
->codecpar
->codec_type
== AVMEDIA_TYPE_VIDEO
) {
356 os
->stream_type_tag
= "video";
357 if (st
->codecpar
->codec_id
== AV_CODEC_ID_H264
) {
359 } else if (st
->codecpar
->codec_id
== AV_CODEC_ID_VC1
) {
362 av_log(s
, AV_LOG_ERROR
, "Unsupported video codec\n");
363 return AVERROR(EINVAL
);
367 os
->stream_type_tag
= "audio";
368 if (st
->codecpar
->codec_id
== AV_CODEC_ID_AAC
) {
370 os
->audio_tag
= 0xff;
371 } else if (st
->codecpar
->codec_id
== AV_CODEC_ID_WMAPRO
) {
373 os
->audio_tag
= 0x0162;
375 av_log(s
, AV_LOG_ERROR
, "Unsupported audio codec\n");
376 return AVERROR(EINVAL
);
378 os
->packet_size
= st
->codecpar
->block_align
? st
->codecpar
->block_align
: 4;
380 get_private_data(os
);
383 if (!c
->has_video
&& c
->min_frag_duration
<= 0) {
384 av_log(s
, AV_LOG_WARNING
, "no video stream and no min frag duration set\n");
385 return AVERROR(EINVAL
);
387 ret
= write_manifest(s
, 0);
394 static int parse_fragment(AVFormatContext
*s
, const char *filename
, int64_t *start_ts
, int64_t *duration
, int64_t *moof_size
, int64_t size
)
399 if ((ret
= s
->io_open(s
, &in
, filename
, AVIO_FLAG_READ
, NULL
)) < 0)
402 *moof_size
= avio_rb32(in
);
403 if (*moof_size
< 8 || *moof_size
> size
)
405 if (avio_rl32(in
) != MKTAG('m','o','o','f'))
408 if (len
> *moof_size
)
410 if (avio_rl32(in
) != MKTAG('m','f','h','d'))
412 avio_seek(in
, len
- 8, SEEK_CUR
);
413 avio_rb32(in
); /* traf size */
414 if (avio_rl32(in
) != MKTAG('t','r','a','f'))
416 while (avio_tell(in
) < *moof_size
) {
417 uint32_t len
= avio_rb32(in
);
418 uint32_t tag
= avio_rl32(in
);
419 int64_t end
= avio_tell(in
) + len
- 8;
420 if (len
< 8 || len
>= *moof_size
)
422 if (tag
== MKTAG('u','u','i','d')) {
423 static const AVUUID tfxd
= {
424 0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6,
425 0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2
428 avio_read(in
, uuid
, 16);
429 if (av_uuid_equal(uuid
, tfxd
) && len
>= 8 + 16 + 4 + 16) {
430 avio_seek(in
, 4, SEEK_CUR
);
431 *start_ts
= avio_rb64(in
);
432 *duration
= avio_rb64(in
);
437 avio_seek(in
, end
, SEEK_SET
);
440 ff_format_io_close(s
, &in
);
444 static 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
)
448 if (os
->nb_fragments
>= os
->fragments_size
) {
449 os
->fragments_size
= (os
->fragments_size
+ 1) * 2;
450 if ((err
= av_reallocp_array(&os
->fragments
, sizeof(*os
->fragments
),
451 os
->fragments_size
)) < 0) {
452 os
->fragments_size
= 0;
453 os
->nb_fragments
= 0;
457 frag
= av_mallocz(sizeof(*frag
));
459 return AVERROR(ENOMEM
);
460 av_strlcpy(frag
->file
, file
, sizeof(frag
->file
));
461 av_strlcpy(frag
->infofile
, infofile
, sizeof(frag
->infofile
));
462 frag
->start_time
= start_time
;
463 frag
->duration
= duration
;
464 frag
->start_pos
= start_pos
;
466 frag
->n
= os
->fragment_index
;
467 os
->fragments
[os
->nb_fragments
++] = frag
;
468 os
->fragment_index
++;
472 static int copy_moof(AVFormatContext
*s
, const char* infile
, const char *outfile
, int64_t size
)
474 AVIOContext
*in
, *out
;
476 if ((ret
= s
->io_open(s
, &in
, infile
, AVIO_FLAG_READ
, NULL
)) < 0)
478 if ((ret
= s
->io_open(s
, &out
, outfile
, AVIO_FLAG_WRITE
, NULL
)) < 0) {
479 ff_format_io_close(s
, &in
);
484 int n
= FFMIN(size
, sizeof(buf
));
485 n
= avio_read(in
, buf
, n
);
490 avio_write(out
, buf
, n
);
494 ff_format_io_close(s
, &out
);
495 ff_format_io_close(s
, &in
);
499 static int ism_flush(AVFormatContext
*s
, int final
)
501 SmoothStreamingContext
*c
= s
->priv_data
;
504 for (i
= 0; i
< s
->nb_streams
; i
++) {
505 OutputStream
*os
= &c
->streams
[i
];
506 char filename
[1024], target_filename
[1024], header_filename
[1024], curr_dirname
[1024];
508 int64_t start_ts
, duration
, moof_size
;
509 if (!os
->packets_written
)
512 snprintf(filename
, sizeof(filename
), "%s/temp", os
->dirname
);
513 ret
= ffurl_open_whitelist(&os
->out
, filename
, AVIO_FLAG_WRITE
, &s
->interrupt_callback
, NULL
, s
->protocol_whitelist
, s
->protocol_blacklist
, NULL
);
516 os
->cur_start_pos
= os
->tail_pos
;
517 av_write_frame(os
->ctx
, NULL
);
518 avio_flush(os
->ctx
->pb
);
519 os
->packets_written
= 0;
520 if (!os
->out
|| os
->tail_out
)
523 ffurl_closep(&os
->out
);
524 size
= os
->tail_pos
- os
->cur_start_pos
;
525 if ((ret
= parse_fragment(s
, filename
, &start_ts
, &duration
, &moof_size
, size
)) < 0)
528 if (!s
->streams
[i
]->codecpar
->bit_rate
) {
529 int64_t bitrate
= (int64_t) size
* 8 * AV_TIME_BASE
/ av_rescale_q(duration
, s
->streams
[i
]->time_base
, AV_TIME_BASE_Q
);
531 av_log(s
, AV_LOG_ERROR
, "calculating bitrate got zero.\n");
532 ret
= AVERROR(EINVAL
);
536 av_log(s
, AV_LOG_DEBUG
, "calculated bitrate: %"PRId64
"\n", bitrate
);
537 s
->streams
[i
]->codecpar
->bit_rate
= bitrate
;
538 memcpy(curr_dirname
, os
->dirname
, sizeof(os
->dirname
));
539 snprintf(os
->dirname
, sizeof(os
->dirname
), "%s/QualityLevels(%"PRId64
")", s
->url
, s
->streams
[i
]->codecpar
->bit_rate
);
540 snprintf(filename
, sizeof(filename
), "%s/temp", os
->dirname
);
542 // rename the tmp folder back to the correct name since we now have the bitrate
543 if ((ret
= ff_rename((const char*)curr_dirname
, os
->dirname
, s
)) < 0)
547 snprintf(header_filename
, sizeof(header_filename
), "%s/FragmentInfo(%s=%"PRIu64
")", os
->dirname
, os
->stream_type_tag
, start_ts
);
548 snprintf(target_filename
, sizeof(target_filename
), "%s/Fragments(%s=%"PRIu64
")", os
->dirname
, os
->stream_type_tag
, start_ts
);
549 copy_moof(s
, filename
, header_filename
, moof_size
);
550 ret
= ff_rename(filename
, target_filename
, s
);
553 add_fragment(os
, target_filename
, header_filename
, start_ts
, duration
,
554 os
->cur_start_pos
, size
);
557 if (c
->window_size
|| (final
&& c
->remove_at_exit
)) {
558 for (i
= 0; i
< s
->nb_streams
; i
++) {
559 OutputStream
*os
= &c
->streams
[i
];
561 int remove
= os
->nb_fragments
- c
->window_size
- c
->extra_window_size
- c
->lookahead_count
;
562 if (final
&& c
->remove_at_exit
)
563 remove
= os
->nb_fragments
;
565 for (j
= 0; j
< remove
; j
++) {
566 unlink(os
->fragments
[j
]->file
);
567 unlink(os
->fragments
[j
]->infofile
);
568 av_freep(&os
->fragments
[j
]);
570 os
->nb_fragments
-= remove
;
571 memmove(os
->fragments
, os
->fragments
+ remove
, os
->nb_fragments
* sizeof(*os
->fragments
));
573 if (final
&& c
->remove_at_exit
)
579 ret
= write_manifest(s
, final
);
583 static int ism_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
585 SmoothStreamingContext
*c
= s
->priv_data
;
586 AVStream
*st
= s
->streams
[pkt
->stream_index
];
587 FFStream
*const sti
= ffstream(st
);
588 OutputStream
*os
= &c
->streams
[pkt
->stream_index
];
589 int64_t end_dts
= (c
->nb_fragments
+ 1) * (int64_t) c
->min_frag_duration
;
592 if (sti
->first_dts
== AV_NOPTS_VALUE
)
593 sti
->first_dts
= pkt
->dts
;
595 if ((!c
->has_video
|| st
->codecpar
->codec_type
== AVMEDIA_TYPE_VIDEO
) &&
596 av_compare_ts(pkt
->dts
- sti
->first_dts
, st
->time_base
,
597 end_dts
, AV_TIME_BASE_Q
) >= 0 &&
598 pkt
->flags
& AV_PKT_FLAG_KEY
&& os
->packets_written
) {
600 if ((ret
= ism_flush(s
, 0)) < 0)
605 os
->packets_written
++;
606 return ff_write_chained(os
->ctx
, 0, pkt
, s
, 0);
609 static int ism_write_trailer(AVFormatContext
*s
)
611 SmoothStreamingContext
*c
= s
->priv_data
;
614 if (c
->remove_at_exit
) {
616 snprintf(filename
, sizeof(filename
), "%s/Manifest", s
->url
);
624 #define OFFSET(x) offsetof(SmoothStreamingContext, x)
625 #define E AV_OPT_FLAG_ENCODING_PARAM
626 static const AVOption options
[] = {
627 { "window_size", "number of fragments kept in the manifest", OFFSET(window_size
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, INT_MAX
, E
},
628 { "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
},
629 { "lookahead_count", "number of lookahead fragments", OFFSET(lookahead_count
), AV_OPT_TYPE_INT
, { .i64
= 2 }, 0, INT_MAX
, E
},
630 { "min_frag_duration", "minimum fragment duration (in microseconds)", OFFSET(min_frag_duration
), AV_OPT_TYPE_INT64
, { .i64
= 5000000 }, 0, INT_MAX
, E
},
631 { "remove_at_exit", "remove all fragments when finished", OFFSET(remove_at_exit
), AV_OPT_TYPE_BOOL
, { .i64
= 0 }, 0, 1, E
},
635 static const AVClass ism_class
= {
636 .class_name
= "smooth streaming muxer",
637 .item_name
= av_default_item_name
,
639 .version
= LIBAVUTIL_VERSION_INT
,
643 const FFOutputFormat ff_smoothstreaming_muxer
= {
644 .p
.name
= "smoothstreaming",
645 .p
.long_name
= NULL_IF_CONFIG_SMALL("Smooth Streaming Muxer"),
646 .p
.audio_codec
= AV_CODEC_ID_AAC
,
647 .p
.video_codec
= AV_CODEC_ID_H264
,
648 .p
.flags
= AVFMT_GLOBALHEADER
| AVFMT_NOFILE
,
649 .p
.priv_class
= &ism_class
,
650 .priv_data_size
= sizeof(SmoothStreamingContext
),
651 .write_header
= ism_write_header
,
652 .write_packet
= ism_write_packet
,
653 .write_trailer
= ism_write_trailer
,