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  return ret;
189 }
190 
192 {
193  SegmentContext *seg = s->priv_data;
194  AVFormatContext *oc = seg->avf;
195  AVStream *st = oc->streams[pkt->stream_index];
196  int64_t end_pts = seg->recording_time * seg->number;
197  int ret;
198 
199  if (!oc)
200  return AVERROR(EINVAL);
201 
202  if ((seg->has_video && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) &&
203  av_compare_ts(pkt->pts, st->time_base,
204  end_pts, AV_TIME_BASE_Q) >= 0 &&
205  pkt->flags & AV_PKT_FLAG_KEY) {
206 
207  av_log(s, AV_LOG_DEBUG, "Next segment starts at %d %"PRId64"\n",
208  pkt->stream_index, pkt->pts);
209 
210  ret = segment_end(oc);
211 
212  if (!ret)
213  ret = segment_start(s);
214 
215  if (ret)
216  goto fail;
217 
218  if (seg->list) {
219  avio_printf(seg->pb, "%s\n", oc->filename);
220  avio_flush(seg->pb);
221  if (!(seg->number % seg->size)) {
222  avio_close(seg->pb);
223  if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
224  &s->interrupt_callback, NULL)) < 0)
225  goto fail;
226 
227  }
228  }
229  }
230 
231  ret = oc->oformat->write_packet(oc, pkt);
232 
233 fail:
234  if (ret < 0)
235  seg_free_context(seg);
236 
237  return ret;
238 }
239 
240 static int seg_write_trailer(struct AVFormatContext *s)
241 {
242  SegmentContext *seg = s->priv_data;
243  AVFormatContext *oc = seg->avf;
244  int ret = 0;
245  if (!oc)
246  goto fail;
247  ret = segment_end(oc);
248  if (seg->list)
249  avio_close(seg->pb);
250  oc->streams = NULL;
251  oc->nb_streams = 0;
253 fail:
254  return ret;
255 }
256 
257 #define OFFSET(x) offsetof(SegmentContext, x)
258 #define E AV_OPT_FLAG_ENCODING_PARAM
259 static const AVOption options[] = {
260  { "segment_format", "container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
261  { "segment_time", "segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E },
262  { "segment_list", "output the segment list", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
263  { "segment_list_size", "maximum number of playlist entries", OFFSET(size), AV_OPT_TYPE_INT, {.dbl = 5}, 0, INT_MAX, E },
264  { NULL },
265 };
266 
267 static const AVClass seg_class = {
268  .class_name = "segment muxer",
269  .item_name = av_default_item_name,
270  .option = options,
271  .version = LIBAVUTIL_VERSION_INT,
272 };
273 
274 
276  .name = "segment",
277  .long_name = NULL_IF_CONFIG_SMALL("segment muxer"),
278  .priv_data_size = sizeof(SegmentContext),
283  .priv_class = &seg_class,
284 };