segment.c
Go to the documentation of this file.
1 /*
2  * Generic segmenter
3  * Copyright (c) 2011, Luca Barbato
4  *
5  * This file is part of Libav.
6  *
7  * Libav 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.
11  *
12  * Libav 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.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with Libav; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 #include <strings.h>
23 #include <float.h>
24 
25 #include "avformat.h"
26 #include "internal.h"
27 
28 #include "libavutil/log.h"
29 #include "libavutil/opt.h"
30 #include "libavutil/avstring.h"
31 #include "libavutil/parseutils.h"
32 #include "libavutil/mathematics.h"
33 
34 typedef struct {
35  const AVClass *class;
36  int number;
38  char *format;
39  char *list;
40  float time;
41  int size;
42  int64_t offset_time;
43  int64_t recording_time;
44  int has_video;
47 
49 {
50  SegmentContext *c = s->priv_data;
51  AVFormatContext *oc = c->avf;
52  int err = 0;
53 
54  if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
55  s->filename, c->number++) < 0)
56  return AVERROR(EINVAL);
57 
58  if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
59  &s->interrupt_callback, NULL)) < 0)
60  return err;
61 
62  if (!oc->priv_data && oc->oformat->priv_data_size > 0) {
64  if (!oc->priv_data) {
65  avio_close(oc->pb);
66  return AVERROR(ENOMEM);
67  }
68  if (oc->oformat->priv_class) {
69  *(const AVClass**)oc->priv_data = oc->oformat->priv_class;
71  }
72  }
73 
74  if ((err = oc->oformat->write_header(oc)) < 0) {
75  goto fail;
76  }
77 
78  return 0;
79 
80 fail:
81  avio_close(oc->pb);
82  av_freep(&oc->priv_data);
83 
84  return err;
85 }
86 
88 {
89  int ret = 0;
90 
91  if (oc->oformat->write_trailer)
92  ret = oc->oformat->write_trailer(oc);
93 
94  avio_close(oc->pb);
95  if (oc->oformat->priv_class)
97  av_freep(&oc->priv_data);
98 
99  return ret;
100 }
101 
103 {
104  SegmentContext *seg = s->priv_data;
105  AVFormatContext *oc;
106  int ret, i;
107 
108  seg->number = 0;
109  seg->offset_time = 0;
110  seg->recording_time = seg->time * 1000000;
111 
112  if (seg->list)
113  if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
114  &s->interrupt_callback, NULL)) < 0)
115  return ret;
116 
117  for (i = 0; i< s->nb_streams; i++)
118  seg->has_video +=
120 
121  if (seg->has_video > 1)
123  "More than a single video stream present, "
124  "expect issues decoding it.\n");
125 
126  oc = avformat_alloc_context();
127 
128  if (!oc) {
129  ret = AVERROR(ENOMEM);
130  goto fail;
131  }
132 
133  oc->oformat = av_guess_format(seg->format, s->filename, NULL);
134 
135  if (!oc->oformat) {
137  goto fail;
138  }
139  if (oc->oformat->flags & AVFMT_NOFILE) {
140  av_log(s, AV_LOG_ERROR, "format %s not supported.\n",
141  oc->oformat->name);
142  ret = AVERROR(EINVAL);
143  goto fail;
144  }
145 
146  seg->avf = oc;
147 
148  oc->streams = s->streams;
149  oc->nb_streams = s->nb_streams;
150 
151  if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
152  s->filename, seg->number++) < 0) {
153  ret = AVERROR(EINVAL);
154  goto fail;
155  }
156 
157  if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
158  &s->interrupt_callback, NULL)) < 0)
159  goto fail;
160 
161  if ((ret = avformat_write_header(oc, NULL)) < 0) {
162  avio_close(oc->pb);
163  goto fail;
164  }
165 
166  if (seg->list) {
167  avio_printf(seg->pb, "%s\n", oc->filename);
168  avio_flush(seg->pb);
169  }
170 
171 fail:
172  if (ret) {
173  oc->streams = NULL;
174  oc->nb_streams = 0;
175  if (seg->list)
176  avio_close(seg->pb);
178  }
179  return ret;
180 }
181 
183 {
184  SegmentContext *seg = s->priv_data;
185  AVFormatContext *oc = seg->avf;
186  AVStream *st = oc->streams[pkt->stream_index];
187  int64_t end_pts = seg->recording_time * seg->number;
188  int ret;
189 
190  if ((seg->has_video && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) &&
191  av_compare_ts(pkt->pts, st->time_base,
192  end_pts, AV_TIME_BASE_Q) >= 0 &&
193  pkt->flags & AV_PKT_FLAG_KEY) {
194 
195  av_log(s, AV_LOG_DEBUG, "Next segment starts at %d %"PRId64"\n",
196  pkt->stream_index, pkt->pts);
197 
198  ret = segment_end(oc);
199 
200  if (!ret)
201  ret = segment_start(s);
202 
203  if (ret)
204  goto fail;
205 
206  if (seg->list) {
207  avio_printf(seg->pb, "%s\n", oc->filename);
208  avio_flush(seg->pb);
209  if (!(seg->number % seg->size)) {
210  avio_close(seg->pb);
211  if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
212  &s->interrupt_callback, NULL)) < 0)
213  goto fail;
214 
215  }
216  }
217  }
218 
219  ret = oc->oformat->write_packet(oc, pkt);
220 
221 fail:
222  if (ret < 0) {
223  oc->streams = NULL;
224  oc->nb_streams = 0;
225  if (seg->list)
226  avio_close(seg->pb);
228  }
229 
230  return ret;
231 }
232 
233 static int seg_write_trailer(struct AVFormatContext *s)
234 {
235  SegmentContext *seg = s->priv_data;
236  AVFormatContext *oc = seg->avf;
237  int ret = segment_end(oc);
238  if (seg->list)
239  avio_close(seg->pb);
240  oc->streams = NULL;
241  oc->nb_streams = 0;
243  return ret;
244 }
245 
246 #define OFFSET(x) offsetof(SegmentContext, x)
247 #define E AV_OPT_FLAG_ENCODING_PARAM
248 static const AVOption options[] = {
249  { "segment_format", "container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
250  { "segment_time", "segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E },
251  { "segment_list", "output the segment list", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
252  { "segment_list_size", "maximum number of playlist entries", OFFSET(size), AV_OPT_TYPE_INT, {.dbl = 5}, 0, INT_MAX, E },
253  { NULL },
254 };
255 
256 static const AVClass seg_class = {
257  .class_name = "segment muxer",
258  .item_name = av_default_item_name,
259  .option = options,
260  .version = LIBAVUTIL_VERSION_INT,
261 };
262 
263 
265  .name = "segment",
266  .long_name = NULL_IF_CONFIG_SMALL("segment muxer"),
267  .priv_data_size = sizeof(SegmentContext),
272  .priv_class = &seg_class,
273 };