00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00027 #include <float.h>
00028
00029 #include "avformat.h"
00030 #include "internal.h"
00031
00032 #include "libavutil/log.h"
00033 #include "libavutil/opt.h"
00034 #include "libavutil/avstring.h"
00035 #include "libavutil/parseutils.h"
00036 #include "libavutil/mathematics.h"
00037 #include "libavutil/timestamp.h"
00038
00039 typedef enum {
00040 LIST_TYPE_UNDEFINED = -1,
00041 LIST_TYPE_FLAT = 0,
00042 LIST_TYPE_CSV,
00043 LIST_TYPE_M3U8,
00044 LIST_TYPE_EXT,
00045 LIST_TYPE_NB,
00046 } ListType;
00047
00048
00049 #define SEGMENT_LIST_FLAG_CACHE 1
00050 #define SEGMENT_LIST_FLAG_LIVE 2
00051
00052 typedef struct {
00053 const AVClass *class;
00054 int segment_idx;
00055 int segment_idx_wrap;
00056 int segment_count;
00057 AVOutputFormat *oformat;
00058 AVFormatContext *avf;
00059 char *format;
00060 char *list;
00061 int list_flags;
00062 int list_size;
00063 double list_max_segment_time;
00064 ListType list_type;
00065 AVIOContext *list_pb;
00066 char *time_str;
00067 int64_t time;
00068 char *times_str;
00069 int64_t *times;
00070 int nb_times;
00071 char *time_delta_str;
00072 int64_t time_delta;
00073 int individual_header_trailer;
00074 int write_header_trailer;
00076 int reset_timestamps;
00077 int has_video;
00078 double start_time, end_time;
00079 int64_t start_pts, start_dts;
00080 int is_first_pkt;
00081 } SegmentContext;
00082
00083 static void print_csv_escaped_str(AVIOContext *ctx, const char *str)
00084 {
00085 int needs_quoting = !!str[strcspn(str, "\",\n\r")];
00086
00087 if (needs_quoting)
00088 avio_w8(ctx, '"');
00089
00090 for (; *str; str++) {
00091 if (*str == '"')
00092 avio_w8(ctx, '"');
00093 avio_w8(ctx, *str);
00094 }
00095 if (needs_quoting)
00096 avio_w8(ctx, '"');
00097 }
00098
00099 static int segment_mux_init(AVFormatContext *s)
00100 {
00101 SegmentContext *seg = s->priv_data;
00102 AVFormatContext *oc;
00103 int i;
00104
00105 seg->avf = oc = avformat_alloc_context();
00106 if (!oc)
00107 return AVERROR(ENOMEM);
00108
00109 oc->oformat = seg->oformat;
00110 oc->interrupt_callback = s->interrupt_callback;
00111
00112 for (i = 0; i < s->nb_streams; i++) {
00113 AVStream *st;
00114 AVCodecContext *icodec, *ocodec;
00115
00116 if (!(st = avformat_new_stream(oc, NULL)))
00117 return AVERROR(ENOMEM);
00118 icodec = s->streams[i]->codec;
00119 ocodec = st->codec;
00120 avcodec_copy_context(ocodec, icodec);
00121 if (!oc->oformat->codec_tag ||
00122 av_codec_get_id (oc->oformat->codec_tag, icodec->codec_tag) == ocodec->codec_id ||
00123 av_codec_get_tag(oc->oformat->codec_tag, icodec->codec_id) <= 0) {
00124 ocodec->codec_tag = icodec->codec_tag;
00125 } else {
00126 ocodec->codec_tag = 0;
00127 }
00128 st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
00129 }
00130
00131 return 0;
00132 }
00133
00134 static int set_segment_filename(AVFormatContext *s)
00135 {
00136 SegmentContext *seg = s->priv_data;
00137 AVFormatContext *oc = seg->avf;
00138
00139 if (seg->segment_idx_wrap)
00140 seg->segment_idx %= seg->segment_idx_wrap;
00141 if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
00142 s->filename, seg->segment_idx) < 0) {
00143 av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", s->filename);
00144 return AVERROR(EINVAL);
00145 }
00146 return 0;
00147 }
00148
00149 static int segment_start(AVFormatContext *s, int write_header)
00150 {
00151 SegmentContext *seg = s->priv_data;
00152 AVFormatContext *oc = seg->avf;
00153 int err = 0;
00154
00155 if (write_header) {
00156 avformat_free_context(oc);
00157 seg->avf = NULL;
00158 if ((err = segment_mux_init(s)) < 0)
00159 return err;
00160 oc = seg->avf;
00161 }
00162
00163 seg->segment_idx++;
00164 if ((err = set_segment_filename(s)) < 0)
00165 return err;
00166 seg->segment_count++;
00167
00168 if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
00169 &s->interrupt_callback, NULL)) < 0)
00170 return err;
00171
00172 if (oc->oformat->priv_class && oc->priv_data)
00173 av_opt_set(oc->priv_data, "resend_headers", "1", 0);
00174
00175 if (write_header) {
00176 if ((err = avformat_write_header(oc, NULL)) < 0)
00177 return err;
00178 }
00179
00180 seg->is_first_pkt = 1;
00181 return 0;
00182 }
00183
00184 static int segment_list_open(AVFormatContext *s)
00185 {
00186 SegmentContext *seg = s->priv_data;
00187 int ret;
00188
00189 ret = avio_open2(&seg->list_pb, seg->list, AVIO_FLAG_WRITE,
00190 &s->interrupt_callback, NULL);
00191 if (ret < 0)
00192 return ret;
00193 seg->list_max_segment_time = 0;
00194
00195 if (seg->list_type == LIST_TYPE_M3U8) {
00196 avio_printf(seg->list_pb, "#EXTM3U\n");
00197 avio_printf(seg->list_pb, "#EXT-X-VERSION:3\n");
00198 avio_printf(seg->list_pb, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg->segment_idx);
00199 avio_printf(seg->list_pb, "#EXT-X-ALLOWCACHE:%d\n",
00200 !!(seg->list_flags & SEGMENT_LIST_FLAG_CACHE));
00201 if (seg->list_flags & SEGMENT_LIST_FLAG_LIVE)
00202 avio_printf(seg->list_pb,
00203 "#EXT-X-TARGETDURATION:%"PRId64"\n", seg->time / 1000000);
00204 }
00205
00206 return ret;
00207 }
00208
00209 static void segment_list_close(AVFormatContext *s)
00210 {
00211 SegmentContext *seg = s->priv_data;
00212
00213 if (seg->list_type == LIST_TYPE_M3U8) {
00214 if (!(seg->list_flags & SEGMENT_LIST_FLAG_LIVE))
00215 avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%d\n",
00216 (int)ceil(seg->list_max_segment_time));
00217 avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n");
00218 }
00219
00220 avio_close(seg->list_pb);
00221 }
00222
00223 static int segment_end(AVFormatContext *s, int write_trailer)
00224 {
00225 SegmentContext *seg = s->priv_data;
00226 AVFormatContext *oc = seg->avf;
00227 int ret = 0;
00228
00229 av_write_frame(oc, NULL);
00230 if (write_trailer)
00231 ret = av_write_trailer(oc);
00232
00233 if (ret < 0)
00234 av_log(s, AV_LOG_ERROR, "Failure occurred when ending segment '%s'\n",
00235 oc->filename);
00236
00237 if (seg->list) {
00238 if (seg->list_size && !(seg->segment_count % seg->list_size)) {
00239 segment_list_close(s);
00240 if ((ret = segment_list_open(s)) < 0)
00241 goto end;
00242 }
00243
00244 if (seg->list_type == LIST_TYPE_FLAT) {
00245 avio_printf(seg->list_pb, "%s\n", oc->filename);
00246 } else if (seg->list_type == LIST_TYPE_CSV || seg->list_type == LIST_TYPE_EXT) {
00247 print_csv_escaped_str(seg->list_pb, oc->filename);
00248 avio_printf(seg->list_pb, ",%f,%f\n", seg->start_time, seg->end_time);
00249 } else if (seg->list_type == LIST_TYPE_M3U8) {
00250 avio_printf(seg->list_pb, "#EXTINF:%f,\n%s\n",
00251 seg->end_time - seg->start_time, oc->filename);
00252 }
00253 seg->list_max_segment_time = FFMAX(seg->end_time - seg->start_time, seg->list_max_segment_time);
00254 avio_flush(seg->list_pb);
00255 }
00256
00257 end:
00258 avio_close(oc->pb);
00259
00260 return ret;
00261 }
00262
00263 static int parse_times(void *log_ctx, int64_t **times, int *nb_times,
00264 const char *times_str)
00265 {
00266 char *p;
00267 int i, ret = 0;
00268 char *times_str1 = av_strdup(times_str);
00269 char *saveptr = NULL;
00270
00271 if (!times_str1)
00272 return AVERROR(ENOMEM);
00273
00274 #define FAIL(err) ret = err; goto end
00275
00276 *nb_times = 1;
00277 for (p = times_str1; *p; p++)
00278 if (*p == ',')
00279 (*nb_times)++;
00280
00281 *times = av_malloc(sizeof(**times) * *nb_times);
00282 if (!*times) {
00283 av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced times array\n");
00284 FAIL(AVERROR(ENOMEM));
00285 }
00286
00287 p = times_str1;
00288 for (i = 0; i < *nb_times; i++) {
00289 int64_t t;
00290 char *tstr = av_strtok(p, ",", &saveptr);
00291 p = NULL;
00292
00293 if (!tstr || !tstr[0]) {
00294 av_log(log_ctx, AV_LOG_ERROR, "Empty time specification in times list %s\n",
00295 times_str);
00296 FAIL(AVERROR(EINVAL));
00297 }
00298
00299 ret = av_parse_time(&t, tstr, 1);
00300 if (ret < 0) {
00301 av_log(log_ctx, AV_LOG_ERROR,
00302 "Invalid time duration specification '%s' in times list %s\n", tstr, times_str);
00303 FAIL(AVERROR(EINVAL));
00304 }
00305 (*times)[i] = t;
00306
00307
00308 if (i && (*times)[i-1] > (*times)[i]) {
00309 av_log(log_ctx, AV_LOG_ERROR,
00310 "Specified time %f is greater than the following time %f\n",
00311 (float)((*times)[i])/1000000, (float)((*times)[i-1])/1000000);
00312 FAIL(AVERROR(EINVAL));
00313 }
00314 }
00315
00316 end:
00317 av_free(times_str1);
00318 return ret;
00319 }
00320
00321 static int open_null_ctx(AVIOContext **ctx)
00322 {
00323 int buf_size = 32768;
00324 uint8_t *buf = av_malloc(buf_size);
00325 if (!buf)
00326 return AVERROR(ENOMEM);
00327 *ctx = avio_alloc_context(buf, buf_size, AVIO_FLAG_WRITE, NULL, NULL, NULL, NULL);
00328 if (!*ctx) {
00329 av_free(buf);
00330 return AVERROR(ENOMEM);
00331 }
00332 return 0;
00333 }
00334
00335 static void close_null_ctx(AVIOContext *pb)
00336 {
00337 av_free(pb->buffer);
00338 av_free(pb);
00339 }
00340
00341 static int seg_write_header(AVFormatContext *s)
00342 {
00343 SegmentContext *seg = s->priv_data;
00344 AVFormatContext *oc = NULL;
00345 int ret, i;
00346
00347 seg->segment_count = 0;
00348 if (!seg->write_header_trailer)
00349 seg->individual_header_trailer = 0;
00350
00351 if (seg->time_str && seg->times_str) {
00352 av_log(s, AV_LOG_ERROR,
00353 "segment_time and segment_times options are mutually exclusive, select just one of them\n");
00354 return AVERROR(EINVAL);
00355 }
00356
00357 if ((seg->list_flags & SEGMENT_LIST_FLAG_LIVE) && seg->times_str) {
00358 av_log(s, AV_LOG_ERROR,
00359 "segment_flags +live and segment_times options are mutually exclusive:"
00360 "specify -segment_time if you want a live-friendly list\n");
00361 return AVERROR(EINVAL);
00362 }
00363
00364 if (seg->times_str) {
00365 if ((ret = parse_times(s, &seg->times, &seg->nb_times, seg->times_str)) < 0)
00366 return ret;
00367 } else {
00368
00369 if (!seg->time_str)
00370 seg->time_str = av_strdup("2");
00371 if ((ret = av_parse_time(&seg->time, seg->time_str, 1)) < 0) {
00372 av_log(s, AV_LOG_ERROR,
00373 "Invalid time duration specification '%s' for segment_time option\n",
00374 seg->time_str);
00375 return ret;
00376 }
00377 }
00378
00379 if (seg->time_delta_str) {
00380 if ((ret = av_parse_time(&seg->time_delta, seg->time_delta_str, 1)) < 0) {
00381 av_log(s, AV_LOG_ERROR,
00382 "Invalid time duration specification '%s' for delta option\n",
00383 seg->time_delta_str);
00384 return ret;
00385 }
00386 }
00387
00388 if (seg->list) {
00389 if (seg->list_type == LIST_TYPE_UNDEFINED) {
00390 if (av_match_ext(seg->list, "csv" )) seg->list_type = LIST_TYPE_CSV;
00391 else if (av_match_ext(seg->list, "ext" )) seg->list_type = LIST_TYPE_EXT;
00392 else if (av_match_ext(seg->list, "m3u8")) seg->list_type = LIST_TYPE_M3U8;
00393 else seg->list_type = LIST_TYPE_FLAT;
00394 }
00395 if ((ret = segment_list_open(s)) < 0)
00396 goto fail;
00397 }
00398 if (seg->list_type == LIST_TYPE_EXT)
00399 av_log(s, AV_LOG_WARNING, "'ext' list type option is deprecated in favor of 'csv'\n");
00400
00401 for (i = 0; i < s->nb_streams; i++)
00402 seg->has_video +=
00403 (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO);
00404
00405 if (seg->has_video > 1)
00406 av_log(s, AV_LOG_WARNING,
00407 "More than a single video stream present, "
00408 "expect issues decoding it.\n");
00409
00410 seg->oformat = av_guess_format(seg->format, s->filename, NULL);
00411
00412 if (!seg->oformat) {
00413 ret = AVERROR_MUXER_NOT_FOUND;
00414 goto fail;
00415 }
00416 if (seg->oformat->flags & AVFMT_NOFILE) {
00417 av_log(s, AV_LOG_ERROR, "format %s not supported.\n",
00418 seg->oformat->name);
00419 ret = AVERROR(EINVAL);
00420 goto fail;
00421 }
00422
00423 if ((ret = segment_mux_init(s)) < 0)
00424 goto fail;
00425 oc = seg->avf;
00426
00427 if ((ret = set_segment_filename(s)) < 0)
00428 goto fail;
00429 seg->segment_count++;
00430
00431 if (seg->write_header_trailer) {
00432 if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
00433 &s->interrupt_callback, NULL)) < 0)
00434 goto fail;
00435 } else {
00436 if ((ret = open_null_ctx(&oc->pb)) < 0)
00437 goto fail;
00438 }
00439
00440 if ((ret = avformat_write_header(oc, NULL)) < 0) {
00441 avio_close(oc->pb);
00442 goto fail;
00443 }
00444 seg->is_first_pkt = 1;
00445
00446 if (oc->avoid_negative_ts > 0 && s->avoid_negative_ts < 0)
00447 s->avoid_negative_ts = 1;
00448
00449 if (!seg->write_header_trailer) {
00450 close_null_ctx(oc->pb);
00451 if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
00452 &s->interrupt_callback, NULL)) < 0)
00453 goto fail;
00454 }
00455
00456 fail:
00457 if (ret) {
00458 if (seg->list)
00459 segment_list_close(s);
00460 if (seg->avf)
00461 avformat_free_context(seg->avf);
00462 }
00463 return ret;
00464 }
00465
00466 static int seg_write_packet(AVFormatContext *s, AVPacket *pkt)
00467 {
00468 SegmentContext *seg = s->priv_data;
00469 AVFormatContext *oc = seg->avf;
00470 AVStream *st = s->streams[pkt->stream_index];
00471 int64_t end_pts;
00472 int ret;
00473
00474 if (seg->times) {
00475 end_pts = seg->segment_count <= seg->nb_times ?
00476 seg->times[seg->segment_count-1] : INT64_MAX;
00477 } else {
00478 end_pts = seg->time * seg->segment_count;
00479 }
00480
00481
00482 if ((st->codec->codec_type == AVMEDIA_TYPE_VIDEO || !seg->has_video) &&
00483 pkt->pts != AV_NOPTS_VALUE &&
00484 av_compare_ts(pkt->pts, st->time_base,
00485 end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0 &&
00486 pkt->flags & AV_PKT_FLAG_KEY) {
00487 ret = segment_end(s, seg->individual_header_trailer);
00488
00489 if (!ret)
00490 ret = segment_start(s, seg->individual_header_trailer);
00491
00492 if (ret)
00493 goto fail;
00494
00495 oc = seg->avf;
00496
00497 seg->start_time = (double)pkt->pts * av_q2d(st->time_base);
00498 seg->start_pts = av_rescale_q(pkt->pts, st->time_base, AV_TIME_BASE_Q);
00499 seg->start_dts = pkt->dts != AV_NOPTS_VALUE ?
00500 av_rescale_q(pkt->dts, st->time_base, AV_TIME_BASE_Q) : seg->start_pts;
00501 } else if (pkt->pts != AV_NOPTS_VALUE) {
00502 seg->end_time = FFMAX(seg->end_time,
00503 (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base));
00504 }
00505
00506 if (seg->is_first_pkt) {
00507 av_log(s, AV_LOG_DEBUG, "segment:'%s' starts with packet stream:%d pts:%s pts_time:%s\n",
00508 seg->avf->filename, pkt->stream_index,
00509 av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base));
00510 seg->is_first_pkt = 0;
00511 }
00512
00513 if (seg->reset_timestamps) {
00514 av_log(s, AV_LOG_DEBUG, "start_pts:%s pts:%s start_dts:%s dts:%s",
00515 av_ts2timestr(seg->start_pts, &AV_TIME_BASE_Q), av_ts2timestr(pkt->pts, &st->time_base),
00516 av_ts2timestr(seg->start_dts, &AV_TIME_BASE_Q), av_ts2timestr(pkt->dts, &st->time_base));
00517
00518
00519 if (pkt->pts != AV_NOPTS_VALUE)
00520 pkt->pts -= av_rescale_q(seg->start_pts, AV_TIME_BASE_Q, st->time_base);
00521 if (pkt->dts != AV_NOPTS_VALUE)
00522 pkt->dts -= av_rescale_q(seg->start_dts, AV_TIME_BASE_Q, st->time_base);
00523
00524 av_log(s, AV_LOG_DEBUG, " -> pts:%s dts:%s\n",
00525 av_ts2timestr(pkt->pts, &st->time_base), av_ts2timestr(pkt->dts, &st->time_base));
00526 }
00527
00528 ret = ff_write_chained(oc, pkt->stream_index, pkt, s);
00529
00530 fail:
00531 if (ret < 0) {
00532 if (seg->list)
00533 avio_close(seg->list_pb);
00534 avformat_free_context(oc);
00535 }
00536
00537 return ret;
00538 }
00539
00540 static int seg_write_trailer(struct AVFormatContext *s)
00541 {
00542 SegmentContext *seg = s->priv_data;
00543 AVFormatContext *oc = seg->avf;
00544 int ret;
00545 if (!seg->write_header_trailer) {
00546 if ((ret = segment_end(s, 0)) < 0)
00547 goto fail;
00548 open_null_ctx(&oc->pb);
00549 ret = av_write_trailer(oc);
00550 close_null_ctx(oc->pb);
00551 } else {
00552 ret = segment_end(s, 1);
00553 }
00554 fail:
00555 if (seg->list)
00556 segment_list_close(s);
00557
00558 av_opt_free(seg);
00559 av_freep(&seg->times);
00560
00561 avformat_free_context(oc);
00562 return ret;
00563 }
00564
00565 #define OFFSET(x) offsetof(SegmentContext, x)
00566 #define E AV_OPT_FLAG_ENCODING_PARAM
00567 static const AVOption options[] = {
00568 { "segment_format", "set container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
00569 { "segment_list", "set the segment list filename", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
00570
00571 { "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"},
00572 { "cache", "allow list caching", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_CACHE }, INT_MIN, INT_MAX, E, "list_flags"},
00573 { "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"},
00574
00575 { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
00576
00577 { "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" },
00578 { "flat", "flat format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FLAT }, INT_MIN, INT_MAX, E, "list_type" },
00579 { "csv", "csv format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_CSV }, INT_MIN, INT_MAX, E, "list_type" },
00580 { "ext", "extended format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_EXT }, INT_MIN, INT_MAX, E, "list_type" },
00581 { "m3u8", "M3U8 format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" },
00582 { "hls", "Apple HTTP Live Streaming compatible", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" },
00583
00584 { "segment_time", "set segment duration", OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
00585 { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta_str), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, E },
00586 { "segment_times", "set segment split time points", OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL}, 0, 0, E },
00587 { "segment_wrap", "set number after which the index wraps", OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
00588 { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
00589
00590 { "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
00591 { "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 },
00592 { "reset_timestamps", "reset timestamps at the begin of each segment", OFFSET(reset_timestamps), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
00593 { NULL },
00594 };
00595
00596 static const AVClass seg_class = {
00597 .class_name = "segment muxer",
00598 .item_name = av_default_item_name,
00599 .option = options,
00600 .version = LIBAVUTIL_VERSION_INT,
00601 };
00602
00603 AVOutputFormat ff_segment_muxer = {
00604 .name = "segment",
00605 .long_name = NULL_IF_CONFIG_SMALL("segment"),
00606 .priv_data_size = sizeof(SegmentContext),
00607 .flags = AVFMT_NOFILE|AVFMT_GLOBALHEADER,
00608 .write_header = seg_write_header,
00609 .write_packet = seg_write_packet,
00610 .write_trailer = seg_write_trailer,
00611 .priv_class = &seg_class,
00612 };
00613
00614 static const AVClass sseg_class = {
00615 .class_name = "stream_segment muxer",
00616 .item_name = av_default_item_name,
00617 .option = options,
00618 .version = LIBAVUTIL_VERSION_INT,
00619 };
00620
00621 AVOutputFormat ff_stream_segment_muxer = {
00622 .name = "stream_segment,ssegment",
00623 .long_name = NULL_IF_CONFIG_SMALL("streaming segment muxer"),
00624 .priv_data_size = sizeof(SegmentContext),
00625 .flags = AVFMT_NOFILE,
00626 .write_header = seg_write_header,
00627 .write_packet = seg_write_packet,
00628 .write_trailer = seg_write_trailer,
00629 .priv_class = &sseg_class,
00630 };