|
|
@@ -41,6 +41,7 @@ typedef struct { |
|
|
|
int size; /**< Set by a private option. */ |
|
|
|
int wrap; /**< Set by a private option. */ |
|
|
|
int individual_header_trailer; /**< Set by a private option. */ |
|
|
|
int write_header_trailer; /**< Set by a private option. */ |
|
|
|
int64_t offset_time; |
|
|
|
int64_t recording_time; |
|
|
|
int has_video; |
|
|
@@ -119,6 +120,26 @@ static int segment_end(AVFormatContext *oc, int write_trailer) |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
static int open_null_ctx(AVIOContext **ctx) |
|
|
|
{ |
|
|
|
int buf_size = 32768; |
|
|
|
uint8_t *buf = av_malloc(buf_size); |
|
|
|
if (!buf) |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
*ctx = avio_alloc_context(buf, buf_size, AVIO_FLAG_WRITE, NULL, NULL, NULL, NULL); |
|
|
|
if (!*ctx) { |
|
|
|
av_free(buf); |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
} |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static void close_null_ctx(AVIOContext *pb) |
|
|
|
{ |
|
|
|
av_free(pb->buffer); |
|
|
|
av_free(pb); |
|
|
|
} |
|
|
|
|
|
|
|
static int seg_write_header(AVFormatContext *s) |
|
|
|
{ |
|
|
|
SegmentContext *seg = s->priv_data; |
|
|
@@ -128,6 +149,8 @@ static int seg_write_header(AVFormatContext *s) |
|
|
|
seg->number = 0; |
|
|
|
seg->offset_time = 0; |
|
|
|
seg->recording_time = seg->time * 1000000; |
|
|
|
if (!seg->write_header_trailer) |
|
|
|
seg->individual_header_trailer = 0; |
|
|
|
|
|
|
|
if (seg->list) |
|
|
|
if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE, |
|
|
@@ -166,15 +189,27 @@ static int seg_write_header(AVFormatContext *s) |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
|
|
|
|
if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, |
|
|
|
&s->interrupt_callback, NULL)) < 0) |
|
|
|
goto fail; |
|
|
|
if (seg->write_header_trailer) { |
|
|
|
if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, |
|
|
|
&s->interrupt_callback, NULL)) < 0) |
|
|
|
goto fail; |
|
|
|
} else { |
|
|
|
if ((ret = open_null_ctx(&oc->pb)) < 0) |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
|
|
|
|
if ((ret = avformat_write_header(oc, NULL)) < 0) { |
|
|
|
avio_close(oc->pb); |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
|
|
|
|
if (!seg->write_header_trailer) { |
|
|
|
close_null_ctx(oc->pb); |
|
|
|
if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, |
|
|
|
&s->interrupt_callback, NULL)) < 0) |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
|
|
|
|
if (seg->list) { |
|
|
|
avio_printf(seg->pb, "%s\n", oc->filename); |
|
|
|
avio_flush(seg->pb); |
|
|
@@ -244,7 +279,15 @@ static int seg_write_trailer(struct AVFormatContext *s) |
|
|
|
{ |
|
|
|
SegmentContext *seg = s->priv_data; |
|
|
|
AVFormatContext *oc = seg->avf; |
|
|
|
int ret = segment_end(oc); |
|
|
|
int ret; |
|
|
|
if (!seg->write_header_trailer) { |
|
|
|
ret = segment_end(oc, 0); |
|
|
|
open_null_ctx(&oc->pb); |
|
|
|
av_write_trailer(oc); |
|
|
|
close_null_ctx(oc->pb); |
|
|
|
} else { |
|
|
|
ret = segment_end(oc, 1); |
|
|
|
} |
|
|
|
if (seg->list) |
|
|
|
avio_close(seg->pb); |
|
|
|
avformat_free_context(oc); |
|
|
@@ -260,6 +303,7 @@ static const AVOption options[] = { |
|
|
|
{ "segment_list_size", "maximum number of playlist entries", OFFSET(size), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E }, |
|
|
|
{ "segment_wrap", "number after which the index wraps", OFFSET(wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, |
|
|
|
{ "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E }, |
|
|
|
{ "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 }, |
|
|
|
{ NULL }, |
|
|
|
}; |
|
|
|
|
|
|
|