#include "libavutil/parseutils.h"
#include "libavutil/mathematics.h"
+typedef enum {
+ LIST_TYPE_FLAT = 0,
+ LIST_TYPE_EXT,
+ LIST_TYPE_NB,
+} ListType;
+
typedef struct {
const AVClass *class; /**< Class for private options. */
int number;
char *format; ///< format to use for output segment files
char *list; ///< filename for the segment list file
int list_size; ///< number of entries for the segment list file
+ ListType list_type; ///< set the list type
+ AVIOContext *list_pb; ///< list file put-byte context
float time; ///< segment duration
int wrap; ///< number after which the index wraps
int64_t recording_time;
int has_video;
- AVIOContext *pb;
+ double start_time, end_time;
} SegmentContext;
static int segment_start(AVFormatContext *s)
seg->number %= seg->wrap;
if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
- s->filename, seg->number++) < 0)
+ s->filename, seg->number++) < 0) {
+ av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", s->filename);
return AVERROR(EINVAL);
+ }
if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
&s->interrupt_callback, NULL)) < 0)
if (seg->list) {
if (seg->list_size && !(seg->number % seg->list_size)) {
- avio_close(seg->pb);
- if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
+ avio_close(seg->list_pb);
+ if ((ret = avio_open2(&seg->list_pb, seg->list, AVIO_FLAG_WRITE,
&s->interrupt_callback, NULL)) < 0)
goto end;
}
- avio_printf(seg->pb, "%s\n", oc->filename);
- avio_flush(seg->pb);
+
+ if (seg->list_type == LIST_TYPE_FLAT) {
+ avio_printf(seg->list_pb, "%s\n", oc->filename);
+ } else if (seg->list_type == LIST_TYPE_EXT) {
+ avio_printf(seg->list_pb, "%s,%f,%f\n", oc->filename, seg->start_time, seg->end_time);
+ }
+ avio_flush(seg->list_pb);
}
end:
return AVERROR(ENOMEM);
if (seg->list)
- if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
+ if ((ret = avio_open2(&seg->list_pb, seg->list, AVIO_FLAG_WRITE,
&s->interrupt_callback, NULL)) < 0)
goto fail;
avformat_free_context(oc);
}
if (seg->list)
- avio_close(seg->pb);
+ avio_close(seg->list_pb);
}
return ret;
}
int64_t end_pts = seg->recording_time * seg->number;
int ret;
- if ((seg->has_video && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) &&
+ /* if the segment has video, start a new segment *only* with a key video frame */
+ if ((st->codec->codec_type == AVMEDIA_TYPE_VIDEO || !seg->has_video) &&
av_compare_ts(pkt->pts, st->time_base,
end_pts, AV_TIME_BASE_Q) >= 0 &&
pkt->flags & AV_PKT_FLAG_KEY) {
if ((ret = segment_end(s)) < 0 || (ret = segment_start(s)) < 0)
goto fail;
+ seg->start_time = (double)pkt->pts * av_q2d(st->time_base);
+ } else if (pkt->pts != AV_NOPTS_VALUE) {
+ seg->end_time = FFMAX(seg->end_time,
+ (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base));
}
ret = oc->oformat->write_packet(oc, pkt);
oc->streams = NULL;
oc->nb_streams = 0;
if (seg->list)
- avio_close(seg->pb);
+ avio_close(seg->list_pb);
avformat_free_context(oc);
}
AVFormatContext *oc = seg->avf;
int ret = segment_end(s);
if (seg->list)
- avio_close(seg->pb);
+ avio_close(seg->list_pb);
oc->streams = NULL;
oc->nb_streams = 0;
avformat_free_context(oc);
{ "segment_time", "set segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E },
{ "segment_list", "set the segment list filename", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
{ "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size), AV_OPT_TYPE_INT, {.dbl = 5}, 0, INT_MAX, E },
+ { "segment_list_type", "set the segment list type", OFFSET(list_type), AV_OPT_TYPE_INT, {.dbl = LIST_TYPE_FLAT}, 0, LIST_TYPE_NB-1, E, "list_type" },
+ { "flat", "flat format", 0, AV_OPT_TYPE_CONST, {.dbl=LIST_TYPE_FLAT }, INT_MIN, INT_MAX, 0, "list_type" },
+ { "ext", "extended format", 0, AV_OPT_TYPE_CONST, {.dbl=LIST_TYPE_EXT }, INT_MIN, INT_MAX, 0, "list_type" },
{ "segment_wrap", "set number after which the index wraps", OFFSET(wrap), AV_OPT_TYPE_INT, {.dbl = 0}, 0, INT_MAX, E },
{ NULL },
};