00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <strings.h>
00023 #include <float.h>
00024
00025 #include "avformat.h"
00026 #include "internal.h"
00027
00028 #include "libavutil/log.h"
00029 #include "libavutil/opt.h"
00030 #include "libavutil/avstring.h"
00031 #include "libavutil/parseutils.h"
00032 #include "libavutil/mathematics.h"
00033
00034 typedef struct {
00035 const AVClass *class;
00036 int number;
00037 AVFormatContext *avf;
00038 char *format;
00039 char *list;
00040 float time;
00041 int size;
00042 int wrap;
00043 int64_t offset_time;
00044 int64_t recording_time;
00045 int has_video;
00046 AVIOContext *pb;
00047 } SegmentContext;
00048
00049 static int segment_start(AVFormatContext *s)
00050 {
00051 SegmentContext *c = s->priv_data;
00052 AVFormatContext *oc = c->avf;
00053 int err = 0;
00054
00055 if (c->wrap)
00056 c->number %= c->wrap;
00057
00058 if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
00059 s->filename, c->number++) < 0)
00060 return AVERROR(EINVAL);
00061
00062 if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
00063 &s->interrupt_callback, NULL)) < 0)
00064 return err;
00065
00066 if (!oc->priv_data && oc->oformat->priv_data_size > 0) {
00067 oc->priv_data = av_mallocz(oc->oformat->priv_data_size);
00068 if (!oc->priv_data) {
00069 avio_close(oc->pb);
00070 return AVERROR(ENOMEM);
00071 }
00072 if (oc->oformat->priv_class) {
00073 *(const AVClass**)oc->priv_data = oc->oformat->priv_class;
00074 av_opt_set_defaults(oc->priv_data);
00075 }
00076 }
00077
00078 if ((err = oc->oformat->write_header(oc)) < 0) {
00079 goto fail;
00080 }
00081
00082 return 0;
00083
00084 fail:
00085 avio_close(oc->pb);
00086 av_freep(&oc->priv_data);
00087
00088 return err;
00089 }
00090
00091 static int segment_end(AVFormatContext *oc)
00092 {
00093 int ret = 0;
00094
00095 if (oc->oformat->write_trailer)
00096 ret = oc->oformat->write_trailer(oc);
00097
00098 avio_close(oc->pb);
00099 if (oc->oformat->priv_class)
00100 av_opt_free(oc->priv_data);
00101 av_freep(&oc->priv_data);
00102
00103 return ret;
00104 }
00105
00106 static int seg_write_header(AVFormatContext *s)
00107 {
00108 SegmentContext *seg = s->priv_data;
00109 AVFormatContext *oc;
00110 int ret, i;
00111
00112 seg->number = 0;
00113 seg->offset_time = 0;
00114 seg->recording_time = seg->time * 1000000;
00115
00116 oc = avformat_alloc_context();
00117
00118 if (!oc)
00119 return AVERROR(ENOMEM);
00120
00121 if (seg->list)
00122 if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
00123 &s->interrupt_callback, NULL)) < 0)
00124 goto fail;
00125
00126 for (i = 0; i< s->nb_streams; i++)
00127 seg->has_video +=
00128 (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO);
00129
00130 if (seg->has_video > 1)
00131 av_log(s, AV_LOG_WARNING,
00132 "More than a single video stream present, "
00133 "expect issues decoding it.\n");
00134
00135 oc->oformat = av_guess_format(seg->format, s->filename, NULL);
00136
00137 if (!oc->oformat) {
00138 ret = AVERROR_MUXER_NOT_FOUND;
00139 goto fail;
00140 }
00141 if (oc->oformat->flags & AVFMT_NOFILE) {
00142 av_log(s, AV_LOG_ERROR, "format %s not supported.\n",
00143 oc->oformat->name);
00144 ret = AVERROR(EINVAL);
00145 goto fail;
00146 }
00147
00148 seg->avf = oc;
00149
00150 oc->streams = s->streams;
00151 oc->nb_streams = s->nb_streams;
00152
00153 if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
00154 s->filename, seg->number++) < 0) {
00155 ret = AVERROR(EINVAL);
00156 goto fail;
00157 }
00158
00159 if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
00160 &s->interrupt_callback, NULL)) < 0)
00161 goto fail;
00162
00163 if ((ret = avformat_write_header(oc, NULL)) < 0) {
00164 avio_close(oc->pb);
00165 goto fail;
00166 }
00167
00168 if (seg->list) {
00169 avio_printf(seg->pb, "%s\n", oc->filename);
00170 avio_flush(seg->pb);
00171 }
00172
00173 fail:
00174 if (ret) {
00175 if (oc) {
00176 oc->streams = NULL;
00177 oc->nb_streams = 0;
00178 avformat_free_context(oc);
00179 }
00180 if (seg->list)
00181 avio_close(seg->pb);
00182 }
00183 return ret;
00184 }
00185
00186 static int seg_write_packet(AVFormatContext *s, AVPacket *pkt)
00187 {
00188 SegmentContext *seg = s->priv_data;
00189 AVFormatContext *oc = seg->avf;
00190 AVStream *st = oc->streams[pkt->stream_index];
00191 int64_t end_pts = seg->recording_time * seg->number;
00192 int ret;
00193
00194 if ((seg->has_video && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) &&
00195 av_compare_ts(pkt->pts, st->time_base,
00196 end_pts, AV_TIME_BASE_Q) >= 0 &&
00197 pkt->flags & AV_PKT_FLAG_KEY) {
00198
00199 av_log(s, AV_LOG_DEBUG, "Next segment starts at %d %"PRId64"\n",
00200 pkt->stream_index, pkt->pts);
00201
00202 ret = segment_end(oc);
00203
00204 if (!ret)
00205 ret = segment_start(s);
00206
00207 if (ret)
00208 goto fail;
00209
00210 if (seg->list) {
00211 avio_printf(seg->pb, "%s\n", oc->filename);
00212 avio_flush(seg->pb);
00213 if (seg->size && !(seg->number % seg->size)) {
00214 avio_close(seg->pb);
00215 if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
00216 &s->interrupt_callback, NULL)) < 0)
00217 goto fail;
00218 }
00219 }
00220 }
00221
00222 ret = oc->oformat->write_packet(oc, pkt);
00223
00224 fail:
00225 if (ret < 0) {
00226 oc->streams = NULL;
00227 oc->nb_streams = 0;
00228 if (seg->list)
00229 avio_close(seg->pb);
00230 avformat_free_context(oc);
00231 }
00232
00233 return ret;
00234 }
00235
00236 static int seg_write_trailer(struct AVFormatContext *s)
00237 {
00238 SegmentContext *seg = s->priv_data;
00239 AVFormatContext *oc = seg->avf;
00240 int ret = segment_end(oc);
00241 if (seg->list)
00242 avio_close(seg->pb);
00243 oc->streams = NULL;
00244 oc->nb_streams = 0;
00245 avformat_free_context(oc);
00246 return ret;
00247 }
00248
00249 #define OFFSET(x) offsetof(SegmentContext, x)
00250 #define E AV_OPT_FLAG_ENCODING_PARAM
00251 static const AVOption options[] = {
00252 { "segment_format", "container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
00253 { "segment_time", "segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E },
00254 { "segment_list", "output the segment list", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
00255 { "segment_list_size", "maximum number of playlist entries", OFFSET(size), AV_OPT_TYPE_INT, {.dbl = 5}, 0, INT_MAX, E },
00256 { "segment_wrap", "number after which the index wraps", OFFSET(wrap), AV_OPT_TYPE_INT, {.dbl = 0}, 0, INT_MAX, E },
00257 { NULL },
00258 };
00259
00260 static const AVClass seg_class = {
00261 .class_name = "segment muxer",
00262 .item_name = av_default_item_name,
00263 .option = options,
00264 .version = LIBAVUTIL_VERSION_INT,
00265 };
00266
00267
00268 AVOutputFormat ff_segment_muxer = {
00269 .name = "segment",
00270 .long_name = NULL_IF_CONFIG_SMALL("segment muxer"),
00271 .priv_data_size = sizeof(SegmentContext),
00272 .flags = AVFMT_GLOBALHEADER | AVFMT_NOFILE,
00273 .write_header = seg_write_header,
00274 .write_packet = seg_write_packet,
00275 .write_trailer = seg_write_trailer,
00276 .priv_class = &seg_class,
00277 };