]> git.sesse.net Git - ffmpeg/blob - libavformat/segment.c
Merge commit 'f61272f0efd80da437570aad2c40e00f9d3f4fe6'
[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 /* #define DEBUG */
28
29 #include <float.h>
30
31 #include "avformat.h"
32 #include "internal.h"
33
34 #include "libavutil/log.h"
35 #include "libavutil/opt.h"
36 #include "libavutil/avstring.h"
37 #include "libavutil/parseutils.h"
38 #include "libavutil/mathematics.h"
39 #include "libavutil/timestamp.h"
40
41 typedef enum {
42     LIST_TYPE_UNDEFINED = -1,
43     LIST_TYPE_FLAT = 0,
44     LIST_TYPE_CSV,
45     LIST_TYPE_M3U8,
46     LIST_TYPE_EXT, ///< deprecated
47     LIST_TYPE_NB,
48 } ListType;
49
50 #define SEGMENT_LIST_FLAG_CACHE 1
51 #define SEGMENT_LIST_FLAG_LIVE  2
52
53 typedef struct {
54     const AVClass *class;  /**< Class for private options. */
55     int segment_idx;       ///< index of the segment file to write, starting from 0
56     int segment_idx_wrap;  ///< number after which the index wraps
57     int segment_count;     ///< number of segment files already written
58     AVOutputFormat *oformat;
59     AVFormatContext *avf;
60     char *format;          ///< format to use for output segment files
61     char *list;            ///< filename for the segment list file
62     int   list_flags;      ///< flags affecting list generation
63     int   list_size;       ///< number of entries for the segment list file
64     double list_max_segment_time; ///< max segment time in the current list
65     ListType list_type;    ///< set the list type
66     AVIOContext *list_pb;  ///< list file put-byte context
67     char *time_str;        ///< segment duration specification string
68     int64_t time;          ///< segment duration
69
70     char *times_str;       ///< segment times specification string
71     int64_t *times;        ///< list of segment interval specification
72     int nb_times;          ///< number of elments in the times array
73
74     char *frames_str;      ///< segment frame numbers specification string
75     int *frames;           ///< list of frame number specification
76     int nb_frames;         ///< number of elments in the frames array
77     int frame_count;
78
79     char *time_delta_str;  ///< approximation value duration used for the segment times
80     int64_t time_delta;
81     int  individual_header_trailer; /**< Set by a private option. */
82     int  write_header_trailer; /**< Set by a private option. */
83
84     int reset_timestamps;  ///< reset timestamps at the begin of each segment
85     char *reference_stream_specifier; ///< reference stream specifier
86     int   reference_stream_index;
87
88     double start_time, end_time;
89     int64_t start_pts, start_dts;
90     int is_first_pkt;      ///< tells if it is the first packet in the segment
91 } SegmentContext;
92
93 static void print_csv_escaped_str(AVIOContext *ctx, const char *str)
94 {
95     int needs_quoting = !!str[strcspn(str, "\",\n\r")];
96
97     if (needs_quoting)
98         avio_w8(ctx, '"');
99
100     for (; *str; str++) {
101         if (*str == '"')
102             avio_w8(ctx, '"');
103         avio_w8(ctx, *str);
104     }
105     if (needs_quoting)
106         avio_w8(ctx, '"');
107 }
108
109 static int segment_mux_init(AVFormatContext *s)
110 {
111     SegmentContext *seg = s->priv_data;
112     AVFormatContext *oc;
113     int i;
114
115     seg->avf = oc = avformat_alloc_context();
116     if (!oc)
117         return AVERROR(ENOMEM);
118
119     oc->oformat            = seg->oformat;
120     oc->interrupt_callback = s->interrupt_callback;
121
122     for (i = 0; i < s->nb_streams; i++) {
123         AVStream *st;
124         AVCodecContext *icodec, *ocodec;
125
126         if (!(st = avformat_new_stream(oc, NULL)))
127             return AVERROR(ENOMEM);
128         icodec = s->streams[i]->codec;
129         ocodec = st->codec;
130         avcodec_copy_context(ocodec, icodec);
131         if (!oc->oformat->codec_tag ||
132             av_codec_get_id (oc->oformat->codec_tag, icodec->codec_tag) == ocodec->codec_id ||
133             av_codec_get_tag(oc->oformat->codec_tag, icodec->codec_id) <= 0) {
134             ocodec->codec_tag = icodec->codec_tag;
135         } else {
136             ocodec->codec_tag = 0;
137         }
138         st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
139     }
140
141     return 0;
142 }
143
144 static int set_segment_filename(AVFormatContext *s)
145 {
146     SegmentContext *seg = s->priv_data;
147     AVFormatContext *oc = seg->avf;
148
149     if (seg->segment_idx_wrap)
150         seg->segment_idx %= seg->segment_idx_wrap;
151     if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
152                               s->filename, seg->segment_idx) < 0) {
153         av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", s->filename);
154         return AVERROR(EINVAL);
155     }
156     return 0;
157 }
158
159 static int segment_start(AVFormatContext *s, int write_header)
160 {
161     SegmentContext *seg = s->priv_data;
162     AVFormatContext *oc = seg->avf;
163     int err = 0;
164
165     if (write_header) {
166         avformat_free_context(oc);
167         seg->avf = NULL;
168         if ((err = segment_mux_init(s)) < 0)
169             return err;
170         oc = seg->avf;
171     }
172
173     seg->segment_idx++;
174     if ((err = set_segment_filename(s)) < 0)
175         return err;
176     seg->segment_count++;
177
178     if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
179                           &s->interrupt_callback, NULL)) < 0)
180         return err;
181
182     if (oc->oformat->priv_class && oc->priv_data)
183         av_opt_set(oc->priv_data, "resend_headers", "1", 0); /* mpegts specific */
184
185     if (write_header) {
186         if ((err = avformat_write_header(oc, NULL)) < 0)
187             return err;
188     }
189
190     seg->is_first_pkt = 1;
191     return 0;
192 }
193
194 static int segment_list_open(AVFormatContext *s)
195 {
196     SegmentContext *seg = s->priv_data;
197     int ret;
198
199     ret = avio_open2(&seg->list_pb, seg->list, AVIO_FLAG_WRITE,
200                      &s->interrupt_callback, NULL);
201     if (ret < 0)
202         return ret;
203     seg->list_max_segment_time = 0;
204
205     if (seg->list_type == LIST_TYPE_M3U8) {
206         avio_printf(seg->list_pb, "#EXTM3U\n");
207         avio_printf(seg->list_pb, "#EXT-X-VERSION:3\n");
208         avio_printf(seg->list_pb, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg->segment_idx);
209         avio_printf(seg->list_pb, "#EXT-X-ALLOWCACHE:%d\n",
210                     !!(seg->list_flags & SEGMENT_LIST_FLAG_CACHE));
211         if (seg->list_flags & SEGMENT_LIST_FLAG_LIVE)
212             avio_printf(seg->list_pb,
213                         "#EXT-X-TARGETDURATION:%"PRId64"\n", seg->time / 1000000);
214     }
215
216     return ret;
217 }
218
219 static void segment_list_close(AVFormatContext *s)
220 {
221     SegmentContext *seg = s->priv_data;
222
223     if (seg->list_type == LIST_TYPE_M3U8) {
224         if (!(seg->list_flags & SEGMENT_LIST_FLAG_LIVE))
225             avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%d\n",
226                         (int)ceil(seg->list_max_segment_time));
227         avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n");
228     }
229
230     avio_close(seg->list_pb);
231 }
232
233 static int segment_end(AVFormatContext *s, int write_trailer)
234 {
235     SegmentContext *seg = s->priv_data;
236     AVFormatContext *oc = seg->avf;
237     int ret = 0;
238
239     av_write_frame(oc, NULL); /* Flush any buffered data (fragmented mp4) */
240     if (write_trailer)
241         ret = av_write_trailer(oc);
242
243     if (ret < 0)
244         av_log(s, AV_LOG_ERROR, "Failure occurred when ending segment '%s'\n",
245                oc->filename);
246
247     if (seg->list) {
248         if (seg->list_size && !(seg->segment_count % seg->list_size)) {
249             segment_list_close(s);
250             if ((ret = segment_list_open(s)) < 0)
251                 goto end;
252         }
253
254         if (seg->list_type == LIST_TYPE_FLAT) {
255             avio_printf(seg->list_pb, "%s\n", oc->filename);
256         } else if (seg->list_type == LIST_TYPE_CSV || seg->list_type == LIST_TYPE_EXT) {
257             print_csv_escaped_str(seg->list_pb, oc->filename);
258             avio_printf(seg->list_pb, ",%f,%f\n", seg->start_time, seg->end_time);
259         } else if (seg->list_type == LIST_TYPE_M3U8) {
260             avio_printf(seg->list_pb, "#EXTINF:%f,\n%s\n",
261                         seg->end_time - seg->start_time, oc->filename);
262         }
263         seg->list_max_segment_time = FFMAX(seg->end_time - seg->start_time, seg->list_max_segment_time);
264         avio_flush(seg->list_pb);
265     }
266
267 end:
268     avio_close(oc->pb);
269
270     return ret;
271 }
272
273 static int parse_times(void *log_ctx, int64_t **times, int *nb_times,
274                        const char *times_str)
275 {
276     char *p;
277     int i, ret = 0;
278     char *times_str1 = av_strdup(times_str);
279     char *saveptr = NULL;
280
281     if (!times_str1)
282         return AVERROR(ENOMEM);
283
284 #define FAIL(err) ret = err; goto end
285
286     *nb_times = 1;
287     for (p = times_str1; *p; p++)
288         if (*p == ',')
289             (*nb_times)++;
290
291     *times = av_malloc(sizeof(**times) * *nb_times);
292     if (!*times) {
293         av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced times array\n");
294         FAIL(AVERROR(ENOMEM));
295     }
296
297     p = times_str1;
298     for (i = 0; i < *nb_times; i++) {
299         int64_t t;
300         char *tstr = av_strtok(p, ",", &saveptr);
301         p = NULL;
302
303         if (!tstr || !tstr[0]) {
304             av_log(log_ctx, AV_LOG_ERROR, "Empty time specification in times list %s\n",
305                    times_str);
306             FAIL(AVERROR(EINVAL));
307         }
308
309         ret = av_parse_time(&t, tstr, 1);
310         if (ret < 0) {
311             av_log(log_ctx, AV_LOG_ERROR,
312                    "Invalid time duration specification '%s' in times list %s\n", tstr, times_str);
313             FAIL(AVERROR(EINVAL));
314         }
315         (*times)[i] = t;
316
317         /* check on monotonicity */
318         if (i && (*times)[i-1] > (*times)[i]) {
319             av_log(log_ctx, AV_LOG_ERROR,
320                    "Specified time %f is greater than the following time %f\n",
321                    (float)((*times)[i])/1000000, (float)((*times)[i-1])/1000000);
322             FAIL(AVERROR(EINVAL));
323         }
324     }
325
326 end:
327     av_free(times_str1);
328     return ret;
329 }
330
331 static int parse_frames(void *log_ctx, int **frames, int *nb_frames,
332                         const char *frames_str)
333 {
334     char *p;
335     int i, ret = 0;
336     char *frames_str1 = av_strdup(frames_str);
337     char *saveptr = NULL;
338
339     if (!frames_str1)
340         return AVERROR(ENOMEM);
341
342 #define FAIL(err) ret = err; goto end
343
344     *nb_frames = 1;
345     for (p = frames_str1; *p; p++)
346         if (*p == ',')
347             (*nb_frames)++;
348
349     *frames = av_malloc(sizeof(**frames) * *nb_frames);
350     if (!*frames) {
351         av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced frames array\n");
352         FAIL(AVERROR(ENOMEM));
353     }
354
355     p = frames_str1;
356     for (i = 0; i < *nb_frames; i++) {
357         long int f;
358         char *tailptr;
359         char *fstr = av_strtok(p, ",", &saveptr);
360
361         p = NULL;
362         if (!fstr) {
363             av_log(log_ctx, AV_LOG_ERROR, "Empty frame specification in frame list %s\n",
364                    frames_str);
365             FAIL(AVERROR(EINVAL));
366         }
367         f = strtol(fstr, &tailptr, 10);
368         if (*tailptr || f <= 0 || f >= INT_MAX) {
369             av_log(log_ctx, AV_LOG_ERROR,
370                    "Invalid argument '%s', must be a positive integer <= INT64_MAX\n",
371                    fstr);
372             FAIL(AVERROR(EINVAL));
373         }
374         (*frames)[i] = f;
375
376         /* check on monotonicity */
377         if (i && (*frames)[i-1] > (*frames)[i]) {
378             av_log(log_ctx, AV_LOG_ERROR,
379                    "Specified frame %d is greater than the following frame %d\n",
380                    (*frames)[i], (*frames)[i-1]);
381             FAIL(AVERROR(EINVAL));
382         }
383     }
384
385 end:
386     av_free(frames_str1);
387     return ret;
388 }
389
390 static int open_null_ctx(AVIOContext **ctx)
391 {
392     int buf_size = 32768;
393     uint8_t *buf = av_malloc(buf_size);
394     if (!buf)
395         return AVERROR(ENOMEM);
396     *ctx = avio_alloc_context(buf, buf_size, AVIO_FLAG_WRITE, NULL, NULL, NULL, NULL);
397     if (!*ctx) {
398         av_free(buf);
399         return AVERROR(ENOMEM);
400     }
401     return 0;
402 }
403
404 static void close_null_ctx(AVIOContext *pb)
405 {
406     av_free(pb->buffer);
407     av_free(pb);
408 }
409
410 static int seg_write_header(AVFormatContext *s)
411 {
412     SegmentContext *seg = s->priv_data;
413     AVFormatContext *oc = NULL;
414     int ret, i;
415
416     seg->segment_count = 0;
417     if (!seg->write_header_trailer)
418         seg->individual_header_trailer = 0;
419
420     if (!!seg->time_str + !!seg->times_str + !!seg->frames_str > 1) {
421         av_log(s, AV_LOG_ERROR,
422                "segment_time, segment_times, and segment_frames options "
423                "are mutually exclusive, select just one of them\n");
424         return AVERROR(EINVAL);
425     }
426
427     if ((seg->list_flags & SEGMENT_LIST_FLAG_LIVE) && (seg->times_str || seg->frames_str)) {
428         av_log(s, AV_LOG_ERROR,
429                "segment_flags +live and segment_times or segment_frames options are mutually exclusive: "
430                "specify segment_time option if you want a live-friendly list\n");
431         return AVERROR(EINVAL);
432     }
433
434     if (seg->times_str) {
435         if ((ret = parse_times(s, &seg->times, &seg->nb_times, seg->times_str)) < 0)
436             return ret;
437     } else if (seg->frames_str) {
438         if ((ret = parse_frames(s, &seg->frames, &seg->nb_frames, seg->frames_str)) < 0)
439             return ret;
440     } else {
441         /* set default value if not specified */
442         if (!seg->time_str)
443             seg->time_str = av_strdup("2");
444         if ((ret = av_parse_time(&seg->time, seg->time_str, 1)) < 0) {
445             av_log(s, AV_LOG_ERROR,
446                    "Invalid time duration specification '%s' for segment_time option\n",
447                    seg->time_str);
448             return ret;
449         }
450     }
451
452     if (seg->time_delta_str) {
453         if ((ret = av_parse_time(&seg->time_delta, seg->time_delta_str, 1)) < 0) {
454             av_log(s, AV_LOG_ERROR,
455                    "Invalid time duration specification '%s' for delta option\n",
456                    seg->time_delta_str);
457             return ret;
458         }
459     }
460
461     if (seg->list) {
462         if (seg->list_type == LIST_TYPE_UNDEFINED) {
463             if      (av_match_ext(seg->list, "csv" )) seg->list_type = LIST_TYPE_CSV;
464             else if (av_match_ext(seg->list, "ext" )) seg->list_type = LIST_TYPE_EXT;
465             else if (av_match_ext(seg->list, "m3u8")) seg->list_type = LIST_TYPE_M3U8;
466             else                                      seg->list_type = LIST_TYPE_FLAT;
467         }
468         if ((ret = segment_list_open(s)) < 0)
469             goto fail;
470     }
471     if (seg->list_type == LIST_TYPE_EXT)
472         av_log(s, AV_LOG_WARNING, "'ext' list type option is deprecated in favor of 'csv'\n");
473
474     seg->reference_stream_index = -1;
475     if (!strcmp(seg->reference_stream_specifier, "auto")) {
476         /* select first index of type with highest priority */
477         int type_index_map[AVMEDIA_TYPE_NB];
478         static const enum AVMediaType type_priority_list[] = {
479             AVMEDIA_TYPE_VIDEO,
480             AVMEDIA_TYPE_AUDIO,
481             AVMEDIA_TYPE_SUBTITLE,
482             AVMEDIA_TYPE_DATA,
483             AVMEDIA_TYPE_ATTACHMENT
484         };
485         enum AVMediaType type;
486
487         for (i = 0; i < AVMEDIA_TYPE_NB; i++)
488             type_index_map[i] = -1;
489
490         /* select first index for each type */
491         for (i = 0; i < s->nb_streams; i++) {
492             type = s->streams[i]->codec->codec_type;
493             if ((unsigned)type < AVMEDIA_TYPE_NB && type_index_map[type] == -1)
494                 type_index_map[type] = i;
495         }
496
497         for (i = 0; i < FF_ARRAY_ELEMS(type_priority_list); i++) {
498             type = type_priority_list[i];
499             if ((seg->reference_stream_index = type_index_map[type]) >= 0)
500                 break;
501         }
502     } else {
503         for (i = 0; i < s->nb_streams; i++) {
504             ret = avformat_match_stream_specifier(s, s->streams[i],
505                                                   seg->reference_stream_specifier);
506             if (ret < 0)
507                 goto fail;
508             if (ret > 0) {
509                 seg->reference_stream_index = i;
510                 break;
511             }
512         }
513     }
514
515     if (seg->reference_stream_index < 0) {
516         av_log(s, AV_LOG_ERROR, "Could not select stream matching identifier '%s'\n",
517                seg->reference_stream_specifier);
518         ret = AVERROR(EINVAL);
519         goto fail;
520     }
521
522     av_log(s, AV_LOG_VERBOSE, "Selected stream id:%d type:%s\n",
523            seg->reference_stream_index,
524            av_get_media_type_string(s->streams[seg->reference_stream_index]->codec->codec_type));
525
526     seg->oformat = av_guess_format(seg->format, s->filename, NULL);
527
528     if (!seg->oformat) {
529         ret = AVERROR_MUXER_NOT_FOUND;
530         goto fail;
531     }
532     if (seg->oformat->flags & AVFMT_NOFILE) {
533         av_log(s, AV_LOG_ERROR, "format %s not supported.\n",
534                seg->oformat->name);
535         ret = AVERROR(EINVAL);
536         goto fail;
537     }
538
539     if ((ret = segment_mux_init(s)) < 0)
540         goto fail;
541     oc = seg->avf;
542
543     if ((ret = set_segment_filename(s)) < 0)
544         goto fail;
545     seg->segment_count++;
546
547     if (seg->write_header_trailer) {
548         if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
549                               &s->interrupt_callback, NULL)) < 0)
550             goto fail;
551     } else {
552         if ((ret = open_null_ctx(&oc->pb)) < 0)
553             goto fail;
554     }
555
556     if ((ret = avformat_write_header(oc, NULL)) < 0) {
557         avio_close(oc->pb);
558         goto fail;
559     }
560     seg->is_first_pkt = 1;
561
562     if (oc->avoid_negative_ts > 0 && s->avoid_negative_ts < 0)
563         s->avoid_negative_ts = 1;
564
565     if (!seg->write_header_trailer) {
566         close_null_ctx(oc->pb);
567         if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
568                               &s->interrupt_callback, NULL)) < 0)
569             goto fail;
570     }
571
572 fail:
573     if (ret) {
574         if (seg->list)
575             segment_list_close(s);
576         if (seg->avf)
577             avformat_free_context(seg->avf);
578     }
579     return ret;
580 }
581
582 static int seg_write_packet(AVFormatContext *s, AVPacket *pkt)
583 {
584     SegmentContext *seg = s->priv_data;
585     AVFormatContext *oc = seg->avf;
586     AVStream *st = s->streams[pkt->stream_index];
587     int64_t end_pts = INT64_MAX;
588     int start_frame = INT_MAX;
589     int ret;
590
591     if (seg->times) {
592         end_pts = seg->segment_count <= seg->nb_times ?
593             seg->times[seg->segment_count-1] : INT64_MAX;
594     } else if (seg->frames) {
595         start_frame = seg->segment_count <= seg->nb_frames ?
596             seg->frames[seg->segment_count-1] : INT_MAX;
597     } else {
598         end_pts = seg->time * seg->segment_count;
599     }
600
601     av_dlog(s, "packet stream:%d pts:%s pts_time:%s is_key:%d frame:%d\n",
602            pkt->stream_index, av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
603            pkt->flags & AV_PKT_FLAG_KEY,
604            pkt->stream_index == seg->reference_stream_index ? seg->frame_count : -1);
605
606     if (pkt->stream_index == seg->reference_stream_index &&
607         pkt->flags & AV_PKT_FLAG_KEY &&
608         (seg->frame_count >= start_frame ||
609          (pkt->pts != AV_NOPTS_VALUE &&
610           av_compare_ts(pkt->pts, st->time_base,
611                         end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0))) {
612         ret = segment_end(s, seg->individual_header_trailer);
613
614         if (!ret)
615             ret = segment_start(s, seg->individual_header_trailer);
616
617         if (ret)
618             goto fail;
619
620         oc = seg->avf;
621
622         seg->start_time = (double)pkt->pts * av_q2d(st->time_base);
623         seg->start_pts = av_rescale_q(pkt->pts, st->time_base, AV_TIME_BASE_Q);
624         seg->start_dts = pkt->dts != AV_NOPTS_VALUE ?
625             av_rescale_q(pkt->dts, st->time_base, AV_TIME_BASE_Q) : seg->start_pts;
626     } else if (pkt->pts != AV_NOPTS_VALUE) {
627         seg->end_time = FFMAX(seg->end_time,
628                               (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base));
629     }
630
631     if (seg->is_first_pkt) {
632         av_log(s, AV_LOG_DEBUG, "segment:'%s' starts with packet stream:%d pts:%s pts_time:%s frame:%d\n",
633                seg->avf->filename, pkt->stream_index,
634                av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base), seg->frame_count);
635         seg->is_first_pkt = 0;
636     }
637
638     if (seg->reset_timestamps) {
639         av_log(s, AV_LOG_DEBUG, "start_pts:%s pts:%s start_dts:%s dts:%s",
640                av_ts2timestr(seg->start_pts, &AV_TIME_BASE_Q), av_ts2timestr(pkt->pts, &st->time_base),
641                av_ts2timestr(seg->start_dts, &AV_TIME_BASE_Q), av_ts2timestr(pkt->dts, &st->time_base));
642
643         /* compute new timestamps */
644         if (pkt->pts != AV_NOPTS_VALUE)
645             pkt->pts -= av_rescale_q(seg->start_pts, AV_TIME_BASE_Q, st->time_base);
646         if (pkt->dts != AV_NOPTS_VALUE)
647             pkt->dts -= av_rescale_q(seg->start_dts, AV_TIME_BASE_Q, st->time_base);
648
649         av_log(s, AV_LOG_DEBUG, " -> pts:%s dts:%s\n",
650                av_ts2timestr(pkt->pts, &st->time_base), av_ts2timestr(pkt->dts, &st->time_base));
651     }
652
653     ret = ff_write_chained(oc, pkt->stream_index, pkt, s);
654
655 fail:
656     if (pkt->stream_index == seg->reference_stream_index)
657         seg->frame_count++;
658
659     if (ret < 0) {
660         if (seg->list)
661             avio_close(seg->list_pb);
662         avformat_free_context(oc);
663     }
664
665     return ret;
666 }
667
668 static int seg_write_trailer(struct AVFormatContext *s)
669 {
670     SegmentContext *seg = s->priv_data;
671     AVFormatContext *oc = seg->avf;
672     int ret;
673     if (!seg->write_header_trailer) {
674         if ((ret = segment_end(s, 0)) < 0)
675             goto fail;
676         open_null_ctx(&oc->pb);
677         ret = av_write_trailer(oc);
678         close_null_ctx(oc->pb);
679     } else {
680         ret = segment_end(s, 1);
681     }
682 fail:
683     if (seg->list)
684         segment_list_close(s);
685
686     av_opt_free(seg);
687     av_freep(&seg->times);
688     av_freep(&seg->frames);
689
690     avformat_free_context(oc);
691     return ret;
692 }
693
694 #define OFFSET(x) offsetof(SegmentContext, x)
695 #define E AV_OPT_FLAG_ENCODING_PARAM
696 static const AVOption options[] = {
697     { "reference_stream",  "set reference stream", OFFSET(reference_stream_specifier), AV_OPT_TYPE_STRING, {.str = "auto"}, CHAR_MIN, CHAR_MAX, E },
698     { "segment_format",    "set container format used for the segments", OFFSET(format),  AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
699     { "segment_list",      "set the segment list filename",              OFFSET(list),    AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
700
701     { "segment_list_flags","set flags affecting segment list generation", OFFSET(list_flags), AV_OPT_TYPE_FLAGS, {.i64 = SEGMENT_LIST_FLAG_CACHE }, 0, UINT_MAX, E, "list_flags"},
702     { "cache",             "allow list caching",                                    0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_CACHE }, INT_MIN, INT_MAX,   E, "list_flags"},
703     { "live",              "enable live-friendly list generation (useful for HLS)", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_LIVE }, INT_MIN, INT_MAX,    E, "list_flags"},
704
705     { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size), AV_OPT_TYPE_INT,  {.i64 = 0},     0, INT_MAX, E },
706
707     { "segment_list_type", "set the segment list type",                  OFFSET(list_type), AV_OPT_TYPE_INT,  {.i64 = LIST_TYPE_UNDEFINED}, -1, LIST_TYPE_NB-1, E, "list_type" },
708     { "flat", "flat format",     0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FLAT }, INT_MIN, INT_MAX, E, "list_type" },
709     { "csv",  "csv format",      0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_CSV  }, INT_MIN, INT_MAX, E, "list_type" },
710     { "ext",  "extended format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_EXT  }, INT_MIN, INT_MAX, E, "list_type" },
711     { "m3u8", "M3U8 format",     0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" },
712     { "hls", "Apple HTTP Live Streaming compatible", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" },
713
714     { "segment_time",      "set segment duration",                       OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
715     { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta_str), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, E },
716     { "segment_times",     "set segment split time points",              OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
717     { "segment_frames",    "set segment split frame numbers",            OFFSET(frames_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
718     { "segment_wrap",      "set number after which the index wraps",     OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
719     { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
720
721     { "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
722     { "write_header_trailer", "write a header to the first segment and a trailer to the last one", OFFSET(write_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
723     { "reset_timestamps", "reset timestamps at the begin of each segment", OFFSET(reset_timestamps), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
724     { NULL },
725 };
726
727 static const AVClass seg_class = {
728     .class_name = "segment muxer",
729     .item_name  = av_default_item_name,
730     .option     = options,
731     .version    = LIBAVUTIL_VERSION_INT,
732 };
733
734 AVOutputFormat ff_segment_muxer = {
735     .name           = "segment",
736     .long_name      = NULL_IF_CONFIG_SMALL("segment"),
737     .priv_data_size = sizeof(SegmentContext),
738     .flags          = AVFMT_NOFILE|AVFMT_GLOBALHEADER,
739     .write_header   = seg_write_header,
740     .write_packet   = seg_write_packet,
741     .write_trailer  = seg_write_trailer,
742     .priv_class     = &seg_class,
743 };
744
745 static const AVClass sseg_class = {
746     .class_name = "stream_segment muxer",
747     .item_name  = av_default_item_name,
748     .option     = options,
749     .version    = LIBAVUTIL_VERSION_INT,
750 };
751
752 AVOutputFormat ff_stream_segment_muxer = {
753     .name           = "stream_segment,ssegment",
754     .long_name      = NULL_IF_CONFIG_SMALL("streaming segment muxer"),
755     .priv_data_size = sizeof(SegmentContext),
756     .flags          = AVFMT_NOFILE,
757     .write_header   = seg_write_header,
758     .write_packet   = seg_write_packet,
759     .write_trailer  = seg_write_trailer,
760     .priv_class     = &sseg_class,
761 };