lavf/segment: add M3U8 list support
[ffmpeg.git] / libavformat / segment.c
1 /*
2  * Copyright (c) 2011, Luca Barbato
3  *
4  * This file is part of Libav.
5  *
6  * Libav is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * Libav is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with Libav; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20
21 /**
22  * @file generic segmenter
23  * M3U8 specification can be find here:
24  * @url{http://tools.ietf.org/id/draft-pantos-http-live-streaming-08.txt}
25  */
26
27 #include <float.h>
28
29 #include "avformat.h"
30 #include "internal.h"
31
32 #include "libavutil/avassert.h"
33 #include "libavutil/log.h"
34 #include "libavutil/opt.h"
35 #include "libavutil/avstring.h"
36 #include "libavutil/parseutils.h"
37 #include "libavutil/mathematics.h"
38
39 typedef enum {
40     LIST_TYPE_FLAT = 0,
41     LIST_TYPE_EXT,
42     LIST_TYPE_M3U8,
43     LIST_TYPE_NB,
44 } ListType;
45
46 typedef struct {
47     const AVClass *class;  /**< Class for private options. */
48     int segment_idx;       ///< index of the segment file to write, starting from 0
49     int segment_idx_wrap;  ///< number after which the index wraps
50     int segment_count;     ///< number of segment files already written
51     AVFormatContext *avf;
52     char *format;          ///< format to use for output segment files
53     char *list;            ///< filename for the segment list file
54     int   list_size;       ///< number of entries for the segment list file
55     double list_max_segment_time; ///< max segment time in the current list
56     ListType list_type;    ///< set the list type
57     AVIOContext *list_pb;  ///< list file put-byte context
58     char *time_str;        ///< segment duration specification string
59     int64_t time;          ///< segment duration
60     char *times_str;       ///< segment times specification string
61     int64_t *times;        ///< list of segment interval specification
62     int nb_times;          ///< number of elments in the times array
63     char *time_delta_str;  ///< approximation value duration used for the segment times
64     int64_t time_delta;
65     int has_video;
66     double start_time, end_time;
67 } SegmentContext;
68
69 static int segment_start(AVFormatContext *s)
70 {
71     SegmentContext *seg = s->priv_data;
72     AVFormatContext *oc = seg->avf;
73     int err = 0;
74
75     if (seg->segment_idx_wrap)
76         seg->segment_idx %= seg->segment_idx_wrap;
77
78     if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
79                               s->filename, seg->segment_idx++) < 0) {
80         av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", s->filename);
81         return AVERROR(EINVAL);
82     }
83     seg->segment_count++;
84
85     if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
86                           &s->interrupt_callback, NULL)) < 0)
87         return err;
88
89     if (!oc->priv_data && oc->oformat->priv_data_size > 0) {
90         oc->priv_data = av_mallocz(oc->oformat->priv_data_size);
91         if (!oc->priv_data) {
92             avio_close(oc->pb);
93             return AVERROR(ENOMEM);
94         }
95         if (oc->oformat->priv_class) {
96             *(const AVClass**)oc->priv_data = oc->oformat->priv_class;
97             av_opt_set_defaults(oc->priv_data);
98         }
99     }
100
101     if ((err = oc->oformat->write_header(oc)) < 0) {
102         goto fail;
103     }
104
105     return 0;
106
107 fail:
108     av_log(oc, AV_LOG_ERROR, "Failure occurred when starting segment '%s'\n",
109            oc->filename);
110     avio_close(oc->pb);
111     av_freep(&oc->priv_data);
112
113     return err;
114 }
115
116 static int segment_list_open(AVFormatContext *s)
117 {
118     SegmentContext *seg = s->priv_data;
119     int ret;
120
121     ret = avio_open2(&seg->list_pb, seg->list, AVIO_FLAG_WRITE,
122                      &s->interrupt_callback, NULL);
123     if (ret < 0)
124         return ret;
125     seg->list_max_segment_time = 0;
126
127     if (seg->list_type == LIST_TYPE_M3U8) {
128         avio_printf(seg->list_pb, "#EXTM3U\n");
129         avio_printf(seg->list_pb, "#EXT-X-VERSION:4\n");
130     }
131
132     return ret;
133 }
134
135 static void segment_list_close(AVFormatContext *s)
136 {
137     SegmentContext *seg = s->priv_data;
138
139     if (seg->list_type == LIST_TYPE_M3U8) {
140         avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%d\n",
141                     (int)(seg->list_max_segment_time + 0.5));
142         avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n");
143     }
144
145     avio_close(seg->list_pb);
146 }
147
148 static int segment_end(AVFormatContext *s)
149 {
150     SegmentContext *seg = s->priv_data;
151     AVFormatContext *oc = seg->avf;
152     int ret = 0;
153
154     if (oc->oformat->write_trailer)
155         ret = oc->oformat->write_trailer(oc);
156
157     if (ret < 0)
158         av_log(s, AV_LOG_ERROR, "Failure occurred when ending segment '%s'\n",
159                oc->filename);
160
161     if (seg->list) {
162         if (seg->list_size && !(seg->segment_count % seg->list_size)) {
163             segment_list_close(s);
164             if ((ret = segment_list_open(s)) < 0)
165                 goto end;
166         }
167
168         if (seg->list_type == LIST_TYPE_FLAT) {
169             avio_printf(seg->list_pb, "%s\n", oc->filename);
170         } else if (seg->list_type == LIST_TYPE_EXT) {
171             avio_printf(seg->list_pb, "%s,%f,%f\n", oc->filename, seg->start_time, seg->end_time);
172         } else if (seg->list_type == LIST_TYPE_M3U8) {
173             avio_printf(seg->list_pb, "#EXTINF:%f,\n%s\n",
174                         seg->end_time - seg->start_time, oc->filename);
175         }
176         seg->list_max_segment_time = FFMAX(seg->end_time - seg->start_time, seg->list_max_segment_time);
177         avio_flush(seg->list_pb);
178     }
179
180 end:
181     avio_close(oc->pb);
182     if (oc->oformat->priv_class)
183         av_opt_free(oc->priv_data);
184     av_freep(&oc->priv_data);
185
186     return ret;
187 }
188
189 static int parse_times(void *log_ctx, int64_t **times, int *nb_times,
190                        const char *times_str)
191 {
192     char *p;
193     int i, ret = 0;
194     char *times_str1 = av_strdup(times_str);
195     char *saveptr = NULL;
196
197     if (!times_str1)
198         return AVERROR(ENOMEM);
199
200 #define FAIL(err) ret = err; goto end
201
202     *nb_times = 1;
203     for (p = times_str1; *p; p++)
204         if (*p == ',')
205             (*nb_times)++;
206
207     *times = av_malloc(sizeof(**times) * *nb_times);
208     if (!*times) {
209         av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced times array\n");
210         FAIL(AVERROR(ENOMEM));
211     }
212
213     p = times_str1;
214     for (i = 0; i < *nb_times; i++) {
215         int64_t t;
216         char *tstr = av_strtok(p, ",", &saveptr);
217         av_assert0(tstr);
218         p = NULL;
219
220         ret = av_parse_time(&t, tstr, 1);
221         if (ret < 0) {
222             av_log(log_ctx, AV_LOG_ERROR,
223                    "Invalid time duration specification in %s\n", p);
224             FAIL(AVERROR(EINVAL));
225         }
226         (*times)[i] = t;
227
228         /* check on monotonicity */
229         if (i && (*times)[i-1] > (*times)[i]) {
230             av_log(log_ctx, AV_LOG_ERROR,
231                    "Specified time %f is greater than the following time %f\n",
232                    (float)((*times)[i])/1000000, (float)((*times)[i-1])/1000000);
233             FAIL(AVERROR(EINVAL));
234         }
235     }
236
237 end:
238     av_free(times_str1);
239     return ret;
240 }
241
242 static int seg_write_header(AVFormatContext *s)
243 {
244     SegmentContext *seg = s->priv_data;
245     AVFormatContext *oc;
246     int ret, i;
247
248     seg->segment_count = 0;
249
250     if (seg->time_str && seg->times_str) {
251         av_log(s, AV_LOG_ERROR,
252                "segment_time and segment_times options are mutually exclusive, select just one of them\n");
253         return AVERROR(EINVAL);
254     }
255
256     if (seg->times_str) {
257         if ((ret = parse_times(s, &seg->times, &seg->nb_times, seg->times_str)) < 0)
258             return ret;
259     } else {
260         /* set default value if not specified */
261         if (!seg->time_str)
262             seg->time_str = av_strdup("2");
263         if ((ret = av_parse_time(&seg->time, seg->time_str, 1)) < 0) {
264             av_log(s, AV_LOG_ERROR,
265                    "Invalid time duration specification '%s' for segment_time option\n",
266                    seg->time_str);
267             return ret;
268         }
269     }
270
271     if (seg->time_delta_str) {
272         if ((ret = av_parse_time(&seg->time_delta, seg->time_delta_str, 1)) < 0) {
273             av_log(s, AV_LOG_ERROR,
274                    "Invalid time duration specification '%s' for delta option\n",
275                    seg->time_delta_str);
276             return ret;
277         }
278     }
279
280     oc = avformat_alloc_context();
281
282     if (!oc)
283         return AVERROR(ENOMEM);
284
285     if (seg->list)
286         if ((ret = segment_list_open(s)) < 0)
287             goto fail;
288
289     for (i = 0; i< s->nb_streams; i++)
290         seg->has_video +=
291             (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO);
292
293     if (seg->has_video > 1)
294         av_log(s, AV_LOG_WARNING,
295                "More than a single video stream present, "
296                "expect issues decoding it.\n");
297
298     oc->oformat = av_guess_format(seg->format, s->filename, NULL);
299
300     if (!oc->oformat) {
301         ret = AVERROR_MUXER_NOT_FOUND;
302         goto fail;
303     }
304     if (oc->oformat->flags & AVFMT_NOFILE) {
305         av_log(s, AV_LOG_ERROR, "format %s not supported.\n",
306                oc->oformat->name);
307         ret = AVERROR(EINVAL);
308         goto fail;
309     }
310
311     seg->avf = oc;
312
313     oc->streams = s->streams;
314     oc->nb_streams = s->nb_streams;
315
316     if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
317                               s->filename, seg->segment_idx++) < 0) {
318         ret = AVERROR(EINVAL);
319         goto fail;
320     }
321     seg->segment_count++;
322
323     if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
324                           &s->interrupt_callback, NULL)) < 0)
325         goto fail;
326
327     if ((ret = avformat_write_header(oc, NULL)) < 0) {
328         avio_close(oc->pb);
329         goto fail;
330     }
331
332 fail:
333     if (ret) {
334         if (oc) {
335             oc->streams = NULL;
336             oc->nb_streams = 0;
337             avformat_free_context(oc);
338         }
339         if (seg->list)
340             segment_list_close(s);
341     }
342     return ret;
343 }
344
345 static int seg_write_packet(AVFormatContext *s, AVPacket *pkt)
346 {
347     SegmentContext *seg = s->priv_data;
348     AVFormatContext *oc = seg->avf;
349     AVStream *st = oc->streams[pkt->stream_index];
350     int64_t end_pts;
351     int ret;
352
353     if (seg->times) {
354         end_pts = seg->segment_count <= seg->nb_times ?
355             seg->times[seg->segment_count-1] : INT64_MAX;
356     } else {
357         end_pts = seg->time * seg->segment_count;
358     }
359
360     /* if the segment has video, start a new segment *only* with a key video frame */
361     if ((st->codec->codec_type == AVMEDIA_TYPE_VIDEO || !seg->has_video) &&
362         av_compare_ts(pkt->pts, st->time_base,
363                       end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0 &&
364         pkt->flags & AV_PKT_FLAG_KEY) {
365
366         av_log(s, AV_LOG_DEBUG, "Next segment starts with packet stream:%d pts:%"PRId64" pts_time:%f\n",
367                pkt->stream_index, pkt->pts, pkt->pts * av_q2d(st->time_base));
368
369         if ((ret = segment_end(s)) < 0 || (ret = segment_start(s)) < 0)
370             goto fail;
371         seg->start_time = (double)pkt->pts * av_q2d(st->time_base);
372     } else if (pkt->pts != AV_NOPTS_VALUE) {
373         seg->end_time = FFMAX(seg->end_time,
374                               (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base));
375     }
376
377     ret = oc->oformat->write_packet(oc, pkt);
378
379 fail:
380     if (ret < 0) {
381         oc->streams = NULL;
382         oc->nb_streams = 0;
383         if (seg->list)
384             avio_close(seg->list_pb);
385         avformat_free_context(oc);
386     }
387
388     return ret;
389 }
390
391 static int seg_write_trailer(struct AVFormatContext *s)
392 {
393     SegmentContext *seg = s->priv_data;
394     AVFormatContext *oc = seg->avf;
395     int ret = segment_end(s);
396     if (seg->list)
397         segment_list_close(s);
398
399     av_opt_free(seg);
400     av_freep(&seg->times);
401
402     oc->streams = NULL;
403     oc->nb_streams = 0;
404     avformat_free_context(oc);
405     return ret;
406 }
407
408 #define OFFSET(x) offsetof(SegmentContext, x)
409 #define E AV_OPT_FLAG_ENCODING_PARAM
410 static const AVOption options[] = {
411     { "segment_format",    "set container format used for the segments", OFFSET(format),  AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
412     { "segment_list",      "set the segment list filename",              OFFSET(list),    AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
413     { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size), AV_OPT_TYPE_INT,  {.dbl = 5},     0, INT_MAX, E },
414     { "segment_list_type", "set the segment list type",                  OFFSET(list_type), AV_OPT_TYPE_INT,  {.dbl = LIST_TYPE_FLAT}, 0, LIST_TYPE_NB-1, E, "list_type" },
415     { "flat", "flat format",     0, AV_OPT_TYPE_CONST, {.dbl=LIST_TYPE_FLAT }, INT_MIN, INT_MAX, 0, "list_type" },
416     { "ext",  "extended format", 0, AV_OPT_TYPE_CONST, {.dbl=LIST_TYPE_EXT  }, INT_MIN, INT_MAX, 0, "list_type" },
417     { "m3u8", "M3U8 format",     0, AV_OPT_TYPE_CONST, {.dbl=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, 0, "list_type" },
418     { "segment_time",      "set segment duration",                       OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
419     { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta_str), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, E },
420     { "segment_times",     "set segment split time points",              OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
421     { "segment_wrap",      "set number after which the index wraps",     OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.dbl = 0}, 0, INT_MAX, E },
422     { NULL },
423 };
424
425 static const AVClass seg_class = {
426     .class_name = "segment muxer",
427     .item_name  = av_default_item_name,
428     .option     = options,
429     .version    = LIBAVUTIL_VERSION_INT,
430 };
431
432 AVOutputFormat ff_segment_muxer = {
433     .name           = "segment",
434     .long_name      = NULL_IF_CONFIG_SMALL("segment"),
435     .priv_data_size = sizeof(SegmentContext),
436     .flags          = AVFMT_GLOBALHEADER | AVFMT_NOFILE,
437     .write_header   = seg_write_header,
438     .write_packet   = seg_write_packet,
439     .write_trailer  = seg_write_trailer,
440     .priv_class     = &seg_class,
441 };
442
443 static const AVClass sseg_class = {
444     .class_name = "stream_segment muxer",
445     .item_name  = av_default_item_name,
446     .option     = options,
447     .version    = LIBAVUTIL_VERSION_INT,
448 };
449
450 AVOutputFormat ff_stream_segment_muxer = {
451     .name           = "stream_segment,ssegment",
452     .long_name      = NULL_IF_CONFIG_SMALL("streaming segment muxer"),
453     .priv_data_size = sizeof(SegmentContext),
454     .flags          = AVFMT_NOFILE,
455     .write_header   = seg_write_header,
456     .write_packet   = seg_write_packet,
457     .write_trailer  = seg_write_trailer,
458     .priv_class     = &sseg_class,
459 };