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 
102 static int avio_closep(AVIOContext **s)
103 {
104  int ret = avio_close(*s);
105  *s = NULL;
106  return ret;
107 }
108 
110 {
111  avio_closep(&seg->pb);
113  seg->avf = NULL;
114 }
115 
117 {
118  SegmentContext *seg = s->priv_data;
119  AVFormatContext *oc;
120  int ret, i;
121 
122  seg->number = 0;
123  seg->offset_time = 0;
124  seg->recording_time = seg->time * 1000000;
125 
126  if (seg->list)
127  if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
128  &s->interrupt_callback, NULL)) < 0)
129  return ret;
130 
131  for (i = 0; i< s->nb_streams; i++)
132  seg->has_video +=
134 
135  if (seg->has_video > 1)
137  "More than a single video stream present, "
138  "expect issues decoding it.\n");
139 
140  oc = avformat_alloc_context();
141 
142  if (!oc) {
143  ret = AVERROR(ENOMEM);
144  goto fail;
145  }
146 
147  oc->oformat = av_guess_format(seg->format, s->filename, NULL);
148 
149  if (!oc->oformat) {
151  goto fail;
152  }
153  if (oc->oformat->flags & AVFMT_NOFILE) {
154  av_log(s, AV_LOG_ERROR, "format %s not supported.\n",
155  oc->oformat->name);
156  ret = AVERROR(EINVAL);
157  goto fail;
158  }
159 
160  seg->avf = oc;
161 
162  oc->streams = s->streams;
163  oc->nb_streams = s->nb_streams;
164 
165  if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
166  s->filename, seg->number++) < 0) {
167  ret = AVERROR(EINVAL);
168  goto fail;
169  }
170 
171  if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
172  &s->interrupt_callback, NULL)) < 0)
173  goto fail;
174 
175  if ((ret = avformat_write_header(oc, NULL)) < 0) {
176  avio_close(oc->pb);
177  goto fail;
178  }
179 
180  if (seg->list) {
181  avio_printf(seg->pb, "%s\n", oc->filename);
182  avio_flush(seg->pb);
183  }
184 
185 fail:
186  if (ret < 0)
187  seg_free_context(seg);
188 
189  return ret;
190 }
191 
193 {
194  SegmentContext *seg = s->priv_data;
195  AVFormatContext *oc = seg->avf;
196  AVStream *st = oc->streams[pkt->stream_index];
197  int64_t end_pts = seg->recording_time * seg->number;
198  int ret;
199 
200  if (!oc)
201  return AVERROR(EINVAL);
202 
203  if ((seg->has_video && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) &&
204  av_compare_ts(pkt->pts, st->time_base,
205  end_pts, AV_TIME_BASE_Q) >= 0 &&
206  pkt->flags & AV_PKT_FLAG_KEY) {
207 
208  av_log(s, AV_LOG_DEBUG, "Next segment starts at %d %"PRId64"\n",
209  pkt->stream_index, pkt->pts);
210 
211  ret = segment_end(oc);
212 
213  if (!ret)
214  ret = segment_start(s);
215 
216  if (ret)
217  goto fail;
218 
219  if (seg->list) {
220  avio_printf(seg->pb, "%s\n", oc->filename);
221  avio_flush(seg->pb);
222  if (!(seg->number % seg->size)) {
223  avio_close(seg->pb);
224  if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
225  &s->interrupt_callback, NULL)) < 0)
226  goto fail;
227 
228  }
229  }
230  }
231 
232  ret = oc->oformat->write_packet(oc, pkt);
233 
234 fail:
235  if (ret < 0)
236  seg_free_context(seg);
237 
238  return ret;
239 }
240 
241 static int seg_write_trailer(struct AVFormatContext *s)
242 {
243  SegmentContext *seg = s->priv_data;
244  AVFormatContext *oc = seg->avf;
245  int ret = 0;
246  if (!oc)
247  goto fail;
248  ret = segment_end(oc);
249  if (seg->list)
250  avio_close(seg->pb);
251  oc->streams = NULL;
252  oc->nb_streams = 0;
254 fail:
255  return ret;
256 }
257 
258 #define OFFSET(x) offsetof(SegmentContext, x)
259 #define E AV_OPT_FLAG_ENCODING_PARAM
260 static const AVOption options[] = {
261  { "segment_format", "container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
262  { "segment_time", "segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E },
263  { "segment_list", "output the segment list", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
264  { "segment_list_size", "maximum number of playlist entries", OFFSET(size), AV_OPT_TYPE_INT, {.dbl = 5}, 0, INT_MAX, E },
265  { NULL },
266 };
267 
268 static const AVClass seg_class = {
269  .class_name = "segment muxer",
270  .item_name = av_default_item_name,
271  .option = options,
272  .version = LIBAVUTIL_VERSION_INT,
273 };
274 
275 
277  .name = "segment",
278  .long_name = NULL_IF_CONFIG_SMALL("segment muxer"),
279  .priv_data_size = sizeof(SegmentContext),
284  .priv_class = &seg_class,
285 };