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