3 * Copyright (c) 2013 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"
32 #include "libavutil/avstring.h"
33 #include "libavutil/base64.h"
34 #include "libavutil/intreadwrite.h"
35 #include "libavutil/mathematics.h"
36 #include "libavutil/mem.h"
37 #include "libavutil/opt.h"
39 typedef struct Fragment
{
41 int64_t start_time
, duration
;
45 typedef struct OutputStream
{
51 char temp_filename
[1024];
52 int64_t frag_start_ts
, last_ts
;
55 int nb_fragments
, fragments_size
, fragment_index
;
58 int has_audio
, has_video
;
63 uint8_t *extra_packets
[2];
64 int extra_packet_sizes
[2];
68 typedef struct HDSContext
{
69 const AVClass
*class; /* Class for private options. */
71 int extra_window_size
;
72 int min_frag_duration
;
75 OutputStream
*streams
;
79 static int parse_header(OutputStream
*os
, const uint8_t *buf
, int buf_size
)
82 return AVERROR_INVALIDDATA
;
83 if (memcmp(buf
, "FLV", 3))
84 return AVERROR_INVALIDDATA
;
87 while (buf_size
>= 11 + 4) {
89 int size
= AV_RB24(&buf
[1]) + 11 + 4;
91 return AVERROR_INVALIDDATA
;
92 if (type
== 8 || type
== 9) {
93 if (os
->nb_extra_packets
>= FF_ARRAY_ELEMS(os
->extra_packets
))
94 return AVERROR_INVALIDDATA
;
95 os
->extra_packet_sizes
[os
->nb_extra_packets
] = size
;
96 os
->extra_packets
[os
->nb_extra_packets
] = av_memdup(buf
, size
);
97 if (!os
->extra_packets
[os
->nb_extra_packets
])
98 return AVERROR(ENOMEM
);
99 os
->nb_extra_packets
++;
100 } else if (type
== 0x12) {
102 return AVERROR_INVALIDDATA
;
103 os
->metadata_size
= size
- 11 - 4;
104 os
->metadata
= av_memdup(buf
+ 11, os
->metadata_size
);
106 return AVERROR(ENOMEM
);
112 return AVERROR_INVALIDDATA
;
116 static int hds_write(void *opaque
, const uint8_t *buf
, int buf_size
)
118 OutputStream
*os
= opaque
;
120 avio_write(os
->out
, buf
, buf_size
);
122 if (!os
->metadata_size
) {
124 // Assuming the IO buffer is large enough to fit the
125 // FLV header and all metadata and extradata packets
126 if ((ret
= parse_header(os
, buf
, buf_size
)) < 0)
133 static void hds_free(AVFormatContext
*s
)
135 HDSContext
*c
= s
->priv_data
;
139 for (i
= 0; i
< s
->nb_streams
; i
++) {
140 OutputStream
*os
= &c
->streams
[i
];
142 ff_format_io_close(s
, &os
->out
);
143 if (os
->ctx
&& os
->ctx_inited
)
144 av_write_trailer(os
->ctx
);
146 avio_context_free(&os
->ctx
->pb
);
147 avformat_free_context(os
->ctx
);
148 av_freep(&os
->metadata
);
149 for (j
= 0; j
< os
->nb_extra_packets
; j
++)
150 av_freep(&os
->extra_packets
[j
]);
151 for (j
= 0; j
< os
->nb_fragments
; j
++)
152 av_freep(&os
->fragments
[j
]);
153 av_freep(&os
->fragments
);
155 av_freep(&c
->streams
);
158 static int write_manifest(AVFormatContext
*s
, int final
)
160 HDSContext
*c
= s
->priv_data
;
162 char filename
[1024], temp_filename
[1024];
166 if (c
->nb_streams
> 0)
167 duration
= c
->streams
[0].last_ts
* av_q2d(s
->streams
[0]->time_base
);
169 snprintf(filename
, sizeof(filename
), "%s/index.f4m", s
->url
);
170 snprintf(temp_filename
, sizeof(temp_filename
), "%s/index.f4m.tmp", s
->url
);
171 ret
= s
->io_open(s
, &out
, temp_filename
, AVIO_FLAG_WRITE
, NULL
);
173 av_log(s
, AV_LOG_ERROR
, "Unable to open %s for writing\n", temp_filename
);
176 avio_printf(out
, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
177 avio_printf(out
, "<manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">\n");
178 avio_printf(out
, "\t<id>%s</id>\n", av_basename(s
->url
));
179 avio_printf(out
, "\t<streamType>%s</streamType>\n",
180 final
? "recorded" : "live");
181 avio_printf(out
, "\t<deliveryType>streaming</deliveryType>\n");
183 avio_printf(out
, "\t<duration>%f</duration>\n", duration
);
184 for (i
= 0; i
< c
->nb_streams
; i
++) {
185 OutputStream
*os
= &c
->streams
[i
];
186 int b64_size
= AV_BASE64_SIZE(os
->metadata_size
);
187 char *base64
= av_malloc(b64_size
);
189 ff_format_io_close(s
, &out
);
190 return AVERROR(ENOMEM
);
192 av_base64_encode(base64
, b64_size
, os
->metadata
, os
->metadata_size
);
194 avio_printf(out
, "\t<bootstrapInfo profile=\"named\" url=\"stream%d.abst\" id=\"bootstrap%d\" />\n", i
, i
);
195 avio_printf(out
, "\t<media bitrate=\"%d\" url=\"stream%d\" bootstrapInfoId=\"bootstrap%d\">\n", os
->bitrate
/1000, i
, i
);
196 avio_printf(out
, "\t\t<metadata>%s</metadata>\n", base64
);
197 avio_printf(out
, "\t</media>\n");
200 avio_printf(out
, "</manifest>\n");
202 ff_format_io_close(s
, &out
);
203 return ff_rename(temp_filename
, filename
, s
);
206 static void update_size(AVIOContext
*out
, int64_t pos
)
208 int64_t end
= avio_tell(out
);
209 avio_seek(out
, pos
, SEEK_SET
);
210 avio_wb32(out
, end
- pos
);
211 avio_seek(out
, end
, SEEK_SET
);
214 /* Note, the .abst files need to be served with the "binary/octet"
215 * mime type, otherwise at least the OSMF player can easily fail
216 * with "stream not found" when polling for the next fragment. */
217 static int write_abst(AVFormatContext
*s
, OutputStream
*os
, int final
)
219 HDSContext
*c
= s
->priv_data
;
221 char filename
[1024], temp_filename
[1024];
223 int64_t asrt_pos
, afrt_pos
;
224 int start
= 0, fragments
;
225 int index
= s
->streams
[os
->first_stream
]->id
;
226 int64_t cur_media_time
= 0;
228 start
= FFMAX(os
->nb_fragments
- c
->window_size
, 0);
229 fragments
= os
->nb_fragments
- start
;
231 cur_media_time
= os
->last_ts
;
232 else if (os
->nb_fragments
)
233 cur_media_time
= os
->fragments
[os
->nb_fragments
- 1]->start_time
;
235 snprintf(filename
, sizeof(filename
),
236 "%s/stream%d.abst", s
->url
, index
);
237 snprintf(temp_filename
, sizeof(temp_filename
),
238 "%s/stream%d.abst.tmp", s
->url
, index
);
239 ret
= s
->io_open(s
, &out
, temp_filename
, AVIO_FLAG_WRITE
, NULL
);
241 av_log(s
, AV_LOG_ERROR
, "Unable to open %s for writing\n", temp_filename
);
244 avio_wb32(out
, 0); // abst size
245 avio_wl32(out
, MKTAG('a','b','s','t'));
246 avio_wb32(out
, 0); // version + flags
247 avio_wb32(out
, os
->fragment_index
- 1); // BootstrapinfoVersion
248 avio_w8(out
, final
? 0 : 0x20); // profile, live, update
249 avio_wb32(out
, 1000); // timescale
250 avio_wb64(out
, cur_media_time
);
251 avio_wb64(out
, 0); // SmpteTimeCodeOffset
252 avio_w8(out
, 0); // MovieIdentifer (null string)
253 avio_w8(out
, 0); // ServerEntryCount
254 avio_w8(out
, 0); // QualityEntryCount
255 avio_w8(out
, 0); // DrmData (null string)
256 avio_w8(out
, 0); // MetaData (null string)
257 avio_w8(out
, 1); // SegmentRunTableCount
258 asrt_pos
= avio_tell(out
);
259 avio_wb32(out
, 0); // asrt size
260 avio_wl32(out
, MKTAG('a','s','r','t'));
261 avio_wb32(out
, 0); // version + flags
262 avio_w8(out
, 0); // QualityEntryCount
263 avio_wb32(out
, 1); // SegmentRunEntryCount
264 avio_wb32(out
, 1); // FirstSegment
265 avio_wb32(out
, final
? (os
->fragment_index
- 1) : 0xffffffff); // FragmentsPerSegment
266 update_size(out
, asrt_pos
);
267 avio_w8(out
, 1); // FragmentRunTableCount
268 afrt_pos
= avio_tell(out
);
269 avio_wb32(out
, 0); // afrt size
270 avio_wl32(out
, MKTAG('a','f','r','t'));
271 avio_wb32(out
, 0); // version + flags
272 avio_wb32(out
, 1000); // timescale
273 avio_w8(out
, 0); // QualityEntryCount
274 avio_wb32(out
, fragments
); // FragmentRunEntryCount
275 for (i
= start
; i
< os
->nb_fragments
; i
++) {
276 avio_wb32(out
, os
->fragments
[i
]->n
);
277 avio_wb64(out
, os
->fragments
[i
]->start_time
);
278 avio_wb32(out
, os
->fragments
[i
]->duration
);
280 update_size(out
, afrt_pos
);
282 ff_format_io_close(s
, &out
);
283 return ff_rename(temp_filename
, filename
, s
);
286 static int init_file(AVFormatContext
*s
, OutputStream
*os
, int64_t start_ts
)
289 ret
= s
->io_open(s
, &os
->out
, os
->temp_filename
, AVIO_FLAG_WRITE
, NULL
);
292 avio_wb32(os
->out
, 0);
293 avio_wl32(os
->out
, MKTAG('m','d','a','t'));
294 for (i
= 0; i
< os
->nb_extra_packets
; i
++) {
295 AV_WB24(os
->extra_packets
[i
] + 4, start_ts
);
296 os
->extra_packets
[i
][7] = (start_ts
>> 24) & 0x7f;
297 avio_write(os
->out
, os
->extra_packets
[i
], os
->extra_packet_sizes
[i
]);
302 static void close_file(AVFormatContext
*s
, OutputStream
*os
)
304 int64_t pos
= avio_tell(os
->out
);
305 avio_seek(os
->out
, 0, SEEK_SET
);
306 avio_wb32(os
->out
, pos
);
308 ff_format_io_close(s
, &os
->out
);
311 static int hds_write_header(AVFormatContext
*s
)
313 HDSContext
*c
= s
->priv_data
;
314 const AVOutputFormat
*oformat
;
317 if (mkdir(s
->url
, 0777) == -1 && errno
!= EEXIST
) {
318 av_log(s
, AV_LOG_ERROR
, "Failed to create directory %s\n", s
->url
);
319 return AVERROR(errno
);
322 oformat
= av_guess_format("flv", NULL
, NULL
);
324 return AVERROR_MUXER_NOT_FOUND
;
327 c
->streams
= av_calloc(s
->nb_streams
, sizeof(*c
->streams
));
329 return AVERROR(ENOMEM
);
332 for (i
= 0; i
< s
->nb_streams
; i
++) {
333 OutputStream
*os
= &c
->streams
[c
->nb_streams
];
334 AVFormatContext
*ctx
;
335 AVStream
*st
= s
->streams
[i
];
337 if (!st
->codecpar
->bit_rate
) {
338 av_log(s
, AV_LOG_ERROR
, "No bit rate set for stream %d\n", i
);
339 return AVERROR(EINVAL
);
341 if (st
->codecpar
->codec_type
== AVMEDIA_TYPE_VIDEO
) {
347 } else if (st
->codecpar
->codec_type
== AVMEDIA_TYPE_AUDIO
) {
354 av_log(s
, AV_LOG_ERROR
, "Unsupported stream type in stream %d\n", i
);
355 return AVERROR(EINVAL
);
357 os
->bitrate
+= s
->streams
[i
]->codecpar
->bit_rate
;
360 os
->first_stream
= i
;
361 ctx
= avformat_alloc_context();
363 return AVERROR(ENOMEM
);
366 ctx
->oformat
= oformat
;
367 ctx
->interrupt_callback
= s
->interrupt_callback
;
368 ctx
->flags
= s
->flags
;
370 ctx
->pb
= avio_alloc_context(os
->iobuf
, sizeof(os
->iobuf
),
372 NULL
, hds_write
, NULL
);
374 return AVERROR(ENOMEM
);
379 s
->streams
[i
]->id
= c
->nb_streams
;
381 if (!(st
= avformat_new_stream(ctx
, NULL
))) {
382 return AVERROR(ENOMEM
);
384 avcodec_parameters_copy(st
->codecpar
, s
->streams
[i
]->codecpar
);
385 st
->codecpar
->codec_tag
= 0;
386 st
->sample_aspect_ratio
= s
->streams
[i
]->sample_aspect_ratio
;
387 st
->time_base
= s
->streams
[i
]->time_base
;
389 if (c
->streams
[c
->nb_streams
].ctx
)
392 for (i
= 0; i
< c
->nb_streams
; i
++) {
393 OutputStream
*os
= &c
->streams
[i
];
395 if ((ret
= avformat_write_header(os
->ctx
, NULL
)) < 0) {
399 avio_flush(os
->ctx
->pb
);
400 for (j
= 0; j
< os
->ctx
->nb_streams
; j
++)
401 s
->streams
[os
->first_stream
+ j
]->time_base
= os
->ctx
->streams
[j
]->time_base
;
403 snprintf(os
->temp_filename
, sizeof(os
->temp_filename
),
404 "%s/stream%d_temp", s
->url
, i
);
405 ret
= init_file(s
, os
, 0);
409 if (!os
->has_video
&& c
->min_frag_duration
<= 0) {
410 av_log(s
, AV_LOG_WARNING
,
411 "No video stream in output stream %d and no min frag duration set\n", i
);
413 os
->fragment_index
= 1;
414 write_abst(s
, os
, 0);
416 ret
= write_manifest(s
, 0);
421 static int add_fragment(OutputStream
*os
, const char *file
,
422 int64_t start_time
, int64_t duration
)
427 if (os
->nb_fragments
>= os
->fragments_size
) {
429 os
->fragments_size
= (os
->fragments_size
+ 1) * 2;
430 if ((ret
= av_reallocp_array(&os
->fragments
, os
->fragments_size
,
431 sizeof(*os
->fragments
))) < 0) {
432 os
->fragments_size
= 0;
433 os
->nb_fragments
= 0;
437 frag
= av_mallocz(sizeof(*frag
));
439 return AVERROR(ENOMEM
);
440 av_strlcpy(frag
->file
, file
, sizeof(frag
->file
));
441 frag
->start_time
= start_time
;
442 frag
->duration
= duration
;
443 frag
->n
= os
->fragment_index
;
444 os
->fragments
[os
->nb_fragments
++] = frag
;
445 os
->fragment_index
++;
449 static int hds_flush(AVFormatContext
*s
, OutputStream
*os
, int final
,
452 HDSContext
*c
= s
->priv_data
;
454 char target_filename
[1024];
455 int index
= s
->streams
[os
->first_stream
]->id
;
457 if (!os
->packets_written
)
460 avio_flush(os
->ctx
->pb
);
461 os
->packets_written
= 0;
464 snprintf(target_filename
, sizeof(target_filename
),
465 "%s/stream%dSeg1-Frag%d", s
->url
, index
, os
->fragment_index
);
466 ret
= ff_rename(os
->temp_filename
, target_filename
, s
);
469 add_fragment(os
, target_filename
, os
->frag_start_ts
, end_ts
- os
->frag_start_ts
);
472 ret
= init_file(s
, os
, end_ts
);
477 if (c
->window_size
|| (final
&& c
->remove_at_exit
)) {
478 int remove
= os
->nb_fragments
- c
->window_size
- c
->extra_window_size
;
479 if (final
&& c
->remove_at_exit
)
480 remove
= os
->nb_fragments
;
482 for (i
= 0; i
< remove
; i
++) {
483 unlink(os
->fragments
[i
]->file
);
484 av_freep(&os
->fragments
[i
]);
486 os
->nb_fragments
-= remove
;
487 memmove(os
->fragments
, os
->fragments
+ remove
,
488 os
->nb_fragments
* sizeof(*os
->fragments
));
493 ret
= write_abst(s
, os
, final
);
497 static int hds_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
499 HDSContext
*c
= s
->priv_data
;
500 AVStream
*st
= s
->streams
[pkt
->stream_index
];
501 FFStream
*const sti
= ffstream(st
);
502 OutputStream
*os
= &c
->streams
[s
->streams
[pkt
->stream_index
]->id
];
503 int64_t end_dts
= os
->fragment_index
* (int64_t)c
->min_frag_duration
;
506 if (sti
->first_dts
== AV_NOPTS_VALUE
)
507 sti
->first_dts
= pkt
->dts
;
509 if ((!os
->has_video
|| st
->codecpar
->codec_type
== AVMEDIA_TYPE_VIDEO
) &&
510 av_compare_ts(pkt
->dts
- sti
->first_dts
, st
->time_base
,
511 end_dts
, AV_TIME_BASE_Q
) >= 0 &&
512 pkt
->flags
& AV_PKT_FLAG_KEY
&& os
->packets_written
) {
514 if ((ret
= hds_flush(s
, os
, 0, pkt
->dts
)) < 0)
518 // Note, these fragment start timestamps, that represent a whole
519 // OutputStream, assume all streams in it have the same time base.
520 if (!os
->packets_written
)
521 os
->frag_start_ts
= pkt
->dts
;
522 os
->last_ts
= pkt
->dts
;
524 os
->packets_written
++;
525 return ff_write_chained(os
->ctx
, pkt
->stream_index
- os
->first_stream
, pkt
, s
, 0);
528 static int hds_write_trailer(AVFormatContext
*s
)
530 HDSContext
*c
= s
->priv_data
;
533 for (i
= 0; i
< c
->nb_streams
; i
++)
534 hds_flush(s
, &c
->streams
[i
], 1, c
->streams
[i
].last_ts
);
535 write_manifest(s
, 1);
537 if (c
->remove_at_exit
) {
539 snprintf(filename
, sizeof(filename
), "%s/index.f4m", s
->url
);
541 for (i
= 0; i
< c
->nb_streams
; i
++) {
542 snprintf(filename
, sizeof(filename
), "%s/stream%d.abst", s
->url
, i
);
551 #define OFFSET(x) offsetof(HDSContext, x)
552 #define E AV_OPT_FLAG_ENCODING_PARAM
553 static const AVOption options
[] = {
554 { "window_size", "number of fragments kept in the manifest", OFFSET(window_size
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, INT_MAX
, E
},
555 { "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
},
556 { "min_frag_duration", "minimum fragment duration (in microseconds)", OFFSET(min_frag_duration
), AV_OPT_TYPE_INT64
, { .i64
= 10000000 }, 0, INT_MAX
, E
},
557 { "remove_at_exit", "remove all fragments when finished", OFFSET(remove_at_exit
), AV_OPT_TYPE_BOOL
, { .i64
= 0 }, 0, 1, E
},
561 static const AVClass hds_class
= {
562 .class_name
= "HDS muxer",
563 .item_name
= av_default_item_name
,
565 .version
= LIBAVUTIL_VERSION_INT
,
568 const FFOutputFormat ff_hds_muxer
= {
570 .p
.long_name
= NULL_IF_CONFIG_SMALL("HDS Muxer"),
571 .p
.audio_codec
= AV_CODEC_ID_AAC
,
572 .p
.video_codec
= AV_CODEC_ID_H264
,
573 .p
.flags
= AVFMT_GLOBALHEADER
| AVFMT_NOFILE
,
574 .p
.priv_class
= &hds_class
,
575 .priv_data_size
= sizeof(HDSContext
),
576 .write_header
= hds_write_header
,
577 .write_packet
= hds_write_packet
,
578 .write_trailer
= hds_write_trailer
,