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