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"
35 #include "libavutil/opt.h"
36 #include "libavutil/avstring.h"
37 #include "libavutil/mathematics.h"
38 #include "libavutil/intreadwrite.h"
43 int64_t start_time
, duration
;
45 int64_t start_pos
, size
;
53 URLContext
*out
; // Current output stream where all output is written
54 URLContext
*out2
; // Auxiliary output stream where all output is also written
55 URLContext
*tail_out
; // The actual main output stream, if we're currently seeked back to write elsewhere
56 int64_t tail_pos
, cur_pos
, cur_start_pos
;
58 const char *stream_type_tag
;
59 int nb_fragments
, fragments_size
, fragment_index
;
69 const AVClass
*class; /* Class for private options. */
71 int extra_window_size
;
73 int min_frag_duration
;
75 OutputStream
*streams
;
76 int has_video
, has_audio
;
78 } SmoothStreamingContext
;
80 static int ism_write(void *opaque
, uint8_t *buf
, int buf_size
)
82 OutputStream
*os
= opaque
;
84 ffurl_write(os
->out
, buf
, buf_size
);
86 ffurl_write(os
->out2
, buf
, buf_size
);
87 os
->cur_pos
+= buf_size
;
88 if (os
->cur_pos
>= os
->tail_pos
)
89 os
->tail_pos
= os
->cur_pos
;
93 static int64_t ism_seek(void *opaque
, int64_t offset
, int whence
)
95 OutputStream
*os
= opaque
;
97 if (whence
!= SEEK_SET
)
98 return AVERROR(ENOSYS
);
101 ffurl_close(os
->out
);
104 ffurl_close(os
->out2
);
106 os
->out
= os
->tail_out
;
110 if (offset
>= os
->cur_start_pos
) {
112 ffurl_seek(os
->out
, offset
- os
->cur_start_pos
, SEEK_SET
);
113 os
->cur_pos
= offset
;
116 for (i
= os
->nb_fragments
- 1; i
>= 0; i
--) {
117 Fragment
*frag
= os
->fragments
[i
];
118 if (offset
>= frag
->start_pos
&& offset
< frag
->start_pos
+ frag
->size
) {
120 AVDictionary
*opts
= NULL
;
121 os
->tail_out
= os
->out
;
122 av_dict_set(&opts
, "truncate", "0", 0);
123 ret
= ffurl_open(&os
->out
, frag
->file
, AVIO_FLAG_READ_WRITE
, &os
->ctx
->interrupt_callback
, &opts
);
126 os
->out
= os
->tail_out
;
130 av_dict_set(&opts
, "truncate", "0", 0);
131 ffurl_open(&os
->out2
, frag
->infofile
, AVIO_FLAG_READ_WRITE
, &os
->ctx
->interrupt_callback
, &opts
);
133 ffurl_seek(os
->out
, offset
- frag
->start_pos
, SEEK_SET
);
135 ffurl_seek(os
->out2
, offset
- frag
->start_pos
, SEEK_SET
);
136 os
->cur_pos
= offset
;
143 static void get_private_data(OutputStream
*os
)
145 AVCodecContext
*codec
= os
->ctx
->streams
[0]->codec
;
146 uint8_t *ptr
= codec
->extradata
;
147 int size
= codec
->extradata_size
;
149 if (codec
->codec_id
== AV_CODEC_ID_H264
) {
150 ff_avc_write_annexb_extradata(ptr
, &ptr
, &size
);
152 ptr
= codec
->extradata
;
156 os
->private_str
= av_mallocz(2*size
+ 1);
157 if (!os
->private_str
)
159 for (i
= 0; i
< size
; i
++)
160 snprintf(&os
->private_str
[2*i
], 3, "%02x", ptr
[i
]);
162 if (ptr
!= codec
->extradata
)
166 static void ism_free(AVFormatContext
*s
)
168 SmoothStreamingContext
*c
= s
->priv_data
;
172 for (i
= 0; i
< s
->nb_streams
; i
++) {
173 OutputStream
*os
= &c
->streams
[i
];
174 ffurl_close(os
->out
);
175 ffurl_close(os
->out2
);
176 ffurl_close(os
->tail_out
);
177 os
->out
= os
->out2
= os
->tail_out
= NULL
;
178 if (os
->ctx
&& os
->ctx_inited
)
179 av_write_trailer(os
->ctx
);
180 if (os
->ctx
&& os
->ctx
->pb
)
181 av_free(os
->ctx
->pb
);
183 avformat_free_context(os
->ctx
);
184 av_free(os
->private_str
);
185 for (j
= 0; j
< os
->nb_fragments
; j
++)
186 av_free(os
->fragments
[j
]);
187 av_free(os
->fragments
);
189 av_freep(&c
->streams
);
192 static void output_chunk_list(OutputStream
*os
, AVIOContext
*out
, int final
, int skip
, int window_size
)
194 int removed
= 0, i
, start
= 0;
195 if (os
->nb_fragments
<= 0)
197 if (os
->fragments
[0]->n
> 0)
202 start
= FFMAX(os
->nb_fragments
- skip
- window_size
, 0);
203 for (i
= start
; i
< os
->nb_fragments
- skip
; i
++) {
204 Fragment
*frag
= os
->fragments
[i
];
205 if (!final
|| removed
)
206 avio_printf(out
, "<c t=\"%"PRIu64
"\" d=\"%"PRIu64
"\" />\n", frag
->start_time
, frag
->duration
);
208 avio_printf(out
, "<c n=\"%d\" d=\"%"PRIu64
"\" />\n", frag
->n
, frag
->duration
);
212 static int write_manifest(AVFormatContext
*s
, int final
)
214 SmoothStreamingContext
*c
= s
->priv_data
;
216 char filename
[1024], temp_filename
[1024];
217 int ret
, i
, video_chunks
= 0, audio_chunks
= 0, video_streams
= 0, audio_streams
= 0;
218 int64_t duration
= 0;
220 snprintf(filename
, sizeof(filename
), "%s/Manifest", s
->filename
);
221 snprintf(temp_filename
, sizeof(temp_filename
), "%s/Manifest.tmp", s
->filename
);
222 ret
= avio_open2(&out
, temp_filename
, AVIO_FLAG_WRITE
, &s
->interrupt_callback
, NULL
);
224 av_log(s
, AV_LOG_ERROR
, "Unable to open %s for writing\n", temp_filename
);
227 avio_printf(out
, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
228 for (i
= 0; i
< s
->nb_streams
; i
++) {
229 OutputStream
*os
= &c
->streams
[i
];
230 if (os
->nb_fragments
> 0) {
231 Fragment
*last
= os
->fragments
[os
->nb_fragments
- 1];
232 duration
= last
->start_time
+ last
->duration
;
234 if (s
->streams
[i
]->codec
->codec_type
== AVMEDIA_TYPE_VIDEO
) {
235 video_chunks
= os
->nb_fragments
;
238 audio_chunks
= os
->nb_fragments
;
244 video_chunks
= audio_chunks
= 0;
246 if (c
->window_size
) {
247 video_chunks
= FFMIN(video_chunks
, c
->window_size
);
248 audio_chunks
= FFMIN(audio_chunks
, c
->window_size
);
250 avio_printf(out
, "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" Duration=\"%"PRIu64
"\"", duration
);
252 avio_printf(out
, " IsLive=\"true\" LookAheadFragmentCount=\"%d\" DVRWindowLength=\"0\"", c
->lookahead_count
);
253 avio_printf(out
, ">\n");
255 int last
= -1, index
= 0;
256 avio_printf(out
, "<StreamIndex Type=\"video\" QualityLevels=\"%d\" Chunks=\"%d\" Url=\"QualityLevels({bitrate})/Fragments(video={start time})\">\n", video_streams
, video_chunks
);
257 for (i
= 0; i
< s
->nb_streams
; i
++) {
258 OutputStream
*os
= &c
->streams
[i
];
259 if (s
->streams
[i
]->codec
->codec_type
!= AVMEDIA_TYPE_VIDEO
)
262 avio_printf(out
, "<QualityLevel Index=\"%d\" Bitrate=\"%d\" FourCC=\"%s\" MaxWidth=\"%d\" MaxHeight=\"%d\" CodecPrivateData=\"%s\" />\n", index
, s
->streams
[i
]->codec
->bit_rate
, os
->fourcc
, s
->streams
[i
]->codec
->width
, s
->streams
[i
]->codec
->height
, os
->private_str
);
265 output_chunk_list(&c
->streams
[last
], out
, final
, c
->lookahead_count
, c
->window_size
);
266 avio_printf(out
, "</StreamIndex>\n");
269 int last
= -1, index
= 0;
270 avio_printf(out
, "<StreamIndex Type=\"audio\" QualityLevels=\"%d\" Chunks=\"%d\" Url=\"QualityLevels({bitrate})/Fragments(audio={start time})\">\n", audio_streams
, audio_chunks
);
271 for (i
= 0; i
< s
->nb_streams
; i
++) {
272 OutputStream
*os
= &c
->streams
[i
];
273 if (s
->streams
[i
]->codec
->codec_type
!= AVMEDIA_TYPE_AUDIO
)
276 avio_printf(out
, "<QualityLevel Index=\"%d\" Bitrate=\"%d\" FourCC=\"%s\" SamplingRate=\"%d\" Channels=\"%d\" BitsPerSample=\"16\" PacketSize=\"%d\" AudioTag=\"%d\" CodecPrivateData=\"%s\" />\n", index
, s
->streams
[i
]->codec
->bit_rate
, os
->fourcc
, s
->streams
[i
]->codec
->sample_rate
, s
->streams
[i
]->codec
->channels
, os
->packet_size
, os
->audio_tag
, os
->private_str
);
279 output_chunk_list(&c
->streams
[last
], out
, final
, c
->lookahead_count
, c
->window_size
);
280 avio_printf(out
, "</StreamIndex>\n");
282 avio_printf(out
, "</SmoothStreamingMedia>\n");
285 rename(temp_filename
, filename
);
289 static int ism_write_header(AVFormatContext
*s
)
291 SmoothStreamingContext
*c
= s
->priv_data
;
293 AVOutputFormat
*oformat
;
295 if (mkdir(s
->filename
, 0777) == -1 && errno
!= EEXIST
) {
296 av_log(s
, AV_LOG_ERROR
, "mkdir failed\n");
297 ret
= AVERROR(errno
);
301 oformat
= av_guess_format("ismv", NULL
, NULL
);
303 ret
= AVERROR_MUXER_NOT_FOUND
;
307 c
->streams
= av_mallocz_array(s
->nb_streams
, sizeof(*c
->streams
));
309 ret
= AVERROR(ENOMEM
);
313 for (i
= 0; i
< s
->nb_streams
; i
++) {
314 OutputStream
*os
= &c
->streams
[i
];
315 AVFormatContext
*ctx
;
317 AVDictionary
*opts
= NULL
;
319 if (!s
->streams
[i
]->codec
->bit_rate
) {
320 av_log(s
, AV_LOG_ERROR
, "No bit rate set for stream %d\n", i
);
321 ret
= AVERROR(EINVAL
);
324 snprintf(os
->dirname
, sizeof(os
->dirname
), "%s/QualityLevels(%d)", s
->filename
, s
->streams
[i
]->codec
->bit_rate
);
325 if (mkdir(os
->dirname
, 0777) == -1 && errno
!= EEXIST
) {
326 ret
= AVERROR(errno
);
327 av_log(s
, AV_LOG_ERROR
, "mkdir failed\n");
331 ctx
= avformat_alloc_context();
333 ret
= AVERROR(ENOMEM
);
337 ctx
->oformat
= oformat
;
338 ctx
->interrupt_callback
= s
->interrupt_callback
;
340 if (!(st
= avformat_new_stream(ctx
, NULL
))) {
341 ret
= AVERROR(ENOMEM
);
344 avcodec_copy_context(st
->codec
, s
->streams
[i
]->codec
);
345 st
->sample_aspect_ratio
= s
->streams
[i
]->sample_aspect_ratio
;
347 ctx
->pb
= avio_alloc_context(os
->iobuf
, sizeof(os
->iobuf
), AVIO_FLAG_WRITE
, os
, NULL
, ism_write
, ism_seek
);
349 ret
= AVERROR(ENOMEM
);
353 av_dict_set_int(&opts
, "ism_lookahead", c
->lookahead_count
, 0);
354 av_dict_set(&opts
, "movflags", "frag_custom", 0);
355 if ((ret
= avformat_write_header(ctx
, &opts
)) < 0) {
361 s
->streams
[i
]->time_base
= st
->time_base
;
362 if (st
->codec
->codec_type
== AVMEDIA_TYPE_VIDEO
) {
364 os
->stream_type_tag
= "video";
365 if (st
->codec
->codec_id
== AV_CODEC_ID_H264
) {
367 } else if (st
->codec
->codec_id
== AV_CODEC_ID_VC1
) {
370 av_log(s
, AV_LOG_ERROR
, "Unsupported video codec\n");
371 ret
= AVERROR(EINVAL
);
376 os
->stream_type_tag
= "audio";
377 if (st
->codec
->codec_id
== AV_CODEC_ID_AAC
) {
379 os
->audio_tag
= 0xff;
380 } else if (st
->codec
->codec_id
== AV_CODEC_ID_WMAPRO
) {
382 os
->audio_tag
= 0x0162;
384 av_log(s
, AV_LOG_ERROR
, "Unsupported audio codec\n");
385 ret
= AVERROR(EINVAL
);
388 os
->packet_size
= st
->codec
->block_align
? st
->codec
->block_align
: 4;
390 get_private_data(os
);
393 if (!c
->has_video
&& c
->min_frag_duration
<= 0) {
394 av_log(s
, AV_LOG_WARNING
, "no video stream and no min frag duration set\n");
395 ret
= AVERROR(EINVAL
);
397 ret
= write_manifest(s
, 0);
405 static int parse_fragment(AVFormatContext
*s
, const char *filename
, int64_t *start_ts
, int64_t *duration
, int64_t *moof_size
, int64_t size
)
410 if ((ret
= avio_open2(&in
, filename
, AVIO_FLAG_READ
, &s
->interrupt_callback
, NULL
)) < 0)
413 *moof_size
= avio_rb32(in
);
414 if (*moof_size
< 8 || *moof_size
> size
)
416 if (avio_rl32(in
) != MKTAG('m','o','o','f'))
419 if (len
> *moof_size
)
421 if (avio_rl32(in
) != MKTAG('m','f','h','d'))
423 avio_seek(in
, len
- 8, SEEK_CUR
);
424 avio_rb32(in
); /* traf size */
425 if (avio_rl32(in
) != MKTAG('t','r','a','f'))
427 while (avio_tell(in
) < *moof_size
) {
428 uint32_t len
= avio_rb32(in
);
429 uint32_t tag
= avio_rl32(in
);
430 int64_t end
= avio_tell(in
) + len
- 8;
431 if (len
< 8 || len
>= *moof_size
)
433 if (tag
== MKTAG('u','u','i','d')) {
434 static const uint8_t tfxd
[] = {
435 0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6,
436 0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2
439 avio_read(in
, uuid
, 16);
440 if (!memcmp(uuid
, tfxd
, 16) && len
>= 8 + 16 + 4 + 16) {
441 avio_seek(in
, 4, SEEK_CUR
);
442 *start_ts
= avio_rb64(in
);
443 *duration
= avio_rb64(in
);
448 avio_seek(in
, end
, SEEK_SET
);
455 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
)
459 if (os
->nb_fragments
>= os
->fragments_size
) {
460 os
->fragments_size
= (os
->fragments_size
+ 1) * 2;
461 if ((err
= av_reallocp(&os
->fragments
, sizeof(*os
->fragments
) *
462 os
->fragments_size
)) < 0) {
463 os
->fragments_size
= 0;
464 os
->nb_fragments
= 0;
468 frag
= av_mallocz(sizeof(*frag
));
470 return AVERROR(ENOMEM
);
471 av_strlcpy(frag
->file
, file
, sizeof(frag
->file
));
472 av_strlcpy(frag
->infofile
, infofile
, sizeof(frag
->infofile
));
473 frag
->start_time
= start_time
;
474 frag
->duration
= duration
;
475 frag
->start_pos
= start_pos
;
477 frag
->n
= os
->fragment_index
;
478 os
->fragments
[os
->nb_fragments
++] = frag
;
479 os
->fragment_index
++;
483 static int copy_moof(AVFormatContext
*s
, const char* infile
, const char *outfile
, int64_t size
)
485 AVIOContext
*in
, *out
;
487 if ((ret
= avio_open2(&in
, infile
, AVIO_FLAG_READ
, &s
->interrupt_callback
, NULL
)) < 0)
489 if ((ret
= avio_open2(&out
, outfile
, AVIO_FLAG_WRITE
, &s
->interrupt_callback
, NULL
)) < 0) {
495 int n
= FFMIN(size
, sizeof(buf
));
496 n
= avio_read(in
, buf
, n
);
501 avio_write(out
, buf
, n
);
510 static int ism_flush(AVFormatContext
*s
, int final
)
512 SmoothStreamingContext
*c
= s
->priv_data
;
515 for (i
= 0; i
< s
->nb_streams
; i
++) {
516 OutputStream
*os
= &c
->streams
[i
];
517 char filename
[1024], target_filename
[1024], header_filename
[1024];
518 int64_t start_pos
= os
->tail_pos
, size
;
519 int64_t start_ts
, duration
, moof_size
;
520 if (!os
->packets_written
)
523 snprintf(filename
, sizeof(filename
), "%s/temp", os
->dirname
);
524 ret
= ffurl_open(&os
->out
, filename
, AVIO_FLAG_WRITE
, &s
->interrupt_callback
, NULL
);
527 os
->cur_start_pos
= os
->tail_pos
;
528 av_write_frame(os
->ctx
, NULL
);
529 avio_flush(os
->ctx
->pb
);
530 os
->packets_written
= 0;
531 if (!os
->out
|| os
->tail_out
)
534 ffurl_close(os
->out
);
536 size
= os
->tail_pos
- start_pos
;
537 if ((ret
= parse_fragment(s
, filename
, &start_ts
, &duration
, &moof_size
, size
)) < 0)
539 snprintf(header_filename
, sizeof(header_filename
), "%s/FragmentInfo(%s=%"PRIu64
")", os
->dirname
, os
->stream_type_tag
, start_ts
);
540 snprintf(target_filename
, sizeof(target_filename
), "%s/Fragments(%s=%"PRIu64
")", os
->dirname
, os
->stream_type_tag
, start_ts
);
541 copy_moof(s
, filename
, header_filename
, moof_size
);
542 rename(filename
, target_filename
);
543 add_fragment(os
, target_filename
, header_filename
, start_ts
, duration
, start_pos
, size
);
546 if (c
->window_size
|| (final
&& c
->remove_at_exit
)) {
547 for (i
= 0; i
< s
->nb_streams
; i
++) {
548 OutputStream
*os
= &c
->streams
[i
];
550 int remove
= os
->nb_fragments
- c
->window_size
- c
->extra_window_size
- c
->lookahead_count
;
551 if (final
&& c
->remove_at_exit
)
552 remove
= os
->nb_fragments
;
554 for (j
= 0; j
< remove
; j
++) {
555 unlink(os
->fragments
[j
]->file
);
556 unlink(os
->fragments
[j
]->infofile
);
557 av_free(os
->fragments
[j
]);
559 os
->nb_fragments
-= remove
;
560 memmove(os
->fragments
, os
->fragments
+ remove
, os
->nb_fragments
* sizeof(*os
->fragments
));
562 if (final
&& c
->remove_at_exit
)
568 ret
= write_manifest(s
, final
);
572 static int ism_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
574 SmoothStreamingContext
*c
= s
->priv_data
;
575 AVStream
*st
= s
->streams
[pkt
->stream_index
];
576 OutputStream
*os
= &c
->streams
[pkt
->stream_index
];
577 int64_t end_dts
= (c
->nb_fragments
+ 1LL) * c
->min_frag_duration
;
580 if (st
->first_dts
== AV_NOPTS_VALUE
)
581 st
->first_dts
= pkt
->dts
;
583 if ((!c
->has_video
|| st
->codec
->codec_type
== AVMEDIA_TYPE_VIDEO
) &&
584 av_compare_ts(pkt
->dts
- st
->first_dts
, st
->time_base
,
585 end_dts
, AV_TIME_BASE_Q
) >= 0 &&
586 pkt
->flags
& AV_PKT_FLAG_KEY
&& os
->packets_written
) {
588 if ((ret
= ism_flush(s
, 0)) < 0)
593 os
->packets_written
++;
594 return ff_write_chained(os
->ctx
, 0, pkt
, s
, 0);
597 static int ism_write_trailer(AVFormatContext
*s
)
599 SmoothStreamingContext
*c
= s
->priv_data
;
602 if (c
->remove_at_exit
) {
604 snprintf(filename
, sizeof(filename
), "%s/Manifest", s
->filename
);
613 #define OFFSET(x) offsetof(SmoothStreamingContext, x)
614 #define E AV_OPT_FLAG_ENCODING_PARAM
615 static const AVOption options
[] = {
616 { "window_size", "number of fragments kept in the manifest", OFFSET(window_size
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, INT_MAX
, E
},
617 { "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
},
618 { "lookahead_count", "number of lookahead fragments", OFFSET(lookahead_count
), AV_OPT_TYPE_INT
, { .i64
= 2 }, 0, INT_MAX
, E
},
619 { "min_frag_duration", "minimum fragment duration (in microseconds)", OFFSET(min_frag_duration
), AV_OPT_TYPE_INT64
, { .i64
= 5000000 }, 0, INT_MAX
, E
},
620 { "remove_at_exit", "remove all fragments when finished", OFFSET(remove_at_exit
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, 1, E
},
624 static const AVClass ism_class
= {
625 .class_name
= "smooth streaming muxer",
626 .item_name
= av_default_item_name
,
628 .version
= LIBAVUTIL_VERSION_INT
,
632 AVOutputFormat ff_smoothstreaming_muxer
= {
633 .name
= "smoothstreaming",
634 .long_name
= NULL_IF_CONFIG_SMALL("Smooth Streaming Muxer"),
635 .priv_data_size
= sizeof(SmoothStreamingContext
),
636 .audio_codec
= AV_CODEC_ID_AAC
,
637 .video_codec
= AV_CODEC_ID_H264
,
638 .flags
= AVFMT_GLOBALHEADER
| AVFMT_NOFILE
,
639 .write_header
= ism_write_header
,
640 .write_packet
= ism_write_packet
,
641 .write_trailer
= ism_write_trailer
,
642 .codec_tag
= (const AVCodecTag
* const []){ ff_mp4_obj_type
, 0 },
643 .priv_class
= &ism_class
,