> On Mar 30, 2015, at 21:23, Rodger Combs <rodger.co...@gmail.com> wrote: > > This needs a fair bit of testing and review before merge. > --- > libavformat/segment.c | 259 ++++++++++++++++++++++++++++++++++++++------------ > 1 file changed, 198 insertions(+), 61 deletions(-) > > diff --git a/libavformat/segment.c b/libavformat/segment.c > index 69038ca..4d934a2 100644 > --- a/libavformat/segment.c > +++ b/libavformat/segment.c > @@ -48,8 +48,10 @@ typedef struct SegmentListEntry { > int64_t start_pts; > int64_t offset_pts; > char *filename; > + char *full_filename; > struct SegmentListEntry *next; > int64_t last_duration; > + size_t start_offset; > } SegmentListEntry; > > typedef enum { > @@ -114,7 +116,13 @@ typedef struct SegmentContext { > > SegmentListEntry cur_entry; > SegmentListEntry *segment_list_entries; > + SegmentListEntry *segment_list_entries_all; > SegmentListEntry *segment_list_entries_end; > + SegmentListEntry *segment_list_entry_writing; > + int seekback; ///< allow seeking back to previous segments > + AVIOContext *cur_pb; ///< current segment put-byte context > + size_t write_offset; > + size_t max_offset; > } SegmentContext; > > static void print_csv_escaped_str(AVIOContext *ctx, const char *str) > @@ -133,6 +141,122 @@ static void print_csv_escaped_str(AVIOContext *ctx, > const char *str) > avio_w8(ctx, '"'); > } > > +static int64_t virtual_seek(void *priv, int64_t target, int whence) > +{ > + AVFormatContext *s = priv; > + SegmentContext *seg = s->priv_data; > + SegmentListEntry *it, *current = NULL; > + int64_t offset = target; > + int64_t ret; > + > + if (whence != SEEK_SET) > + return AVERROR(EINVAL); > + if (offset < 0) > + return AVERROR(EINVAL); > + > + if (offset >= seg->max_offset) { > + avio_closep(&seg->cur_pb); > + seg->write_offset = offset; > + return offset; > + } > + > + if (seg->cur_entry.start_offset <= offset) { > + current = &seg->cur_entry; > + } else { > + for (it = seg->segment_list_entries_all; it; it = it->next) { > + if (it->start_offset <= offset) > + current = it; > + else if (it->start_offset > offset) > + break; > + } > + } > + > + offset -= current->start_offset; > + > + if (current != seg->segment_list_entry_writing) { > + int is_seekback = (current != &seg->cur_entry) && > seg->segment_list_entries; > + char *new_filename; > + AVIOContext *new_ctx = NULL; > + AVDictionary *options = NULL; > + > + if (!seg->seekback && is_seekback) > + return AVERROR(EINVAL); > + > + new_filename = current->full_filename; > + > + if (new_filename) { > + if (is_seekback) > + av_dict_set_int(&options, "truncate", 0, 0); > + if ((ret = avio_open2(&new_ctx, new_filename, AVIO_FLAG_WRITE, > + &s->interrupt_callback, &options)) < 0) { > + av_log(s, AV_LOG_ERROR, "Failed to seek into segment > '%s'\n", new_filename); > + return ret; > + } > + } > + > + avio_close(seg->cur_pb); > + seg->cur_pb = new_ctx; > + seg->segment_list_entry_writing = current; > + } > + > + if (seg->cur_pb) > + if ((ret = avio_seek(seg->cur_pb, offset, SEEK_SET)) < 0) > + return ret; > + > + seg->write_offset = offset; > + > + return target; > +} > + > +static int virtual_write(void *priv, uint8_t *buf, int buf_size) > +{ > + AVFormatContext *s = priv; > + SegmentContext *seg = s->priv_data; > + int ret = 0; > + int written = 0; > + > + while (written < buf_size) { > + SegmentListEntry *cur = seg->segment_list_entry_writing; > + size_t start = cur->start_offset + seg->write_offset; > + SegmentListEntry *next = cur->next ? cur->next : (cur == > &seg->cur_entry ? NULL : &seg->cur_entry); > + size_t end = next ? next->start_offset : SIZE_MAX; > + int to_write = FFMIN(end - start, buf_size - written); > + if (seg->cur_pb) > + avio_write(seg->cur_pb, buf, to_write); > + buf += to_write; > + written += to_write; > + seg->write_offset += to_write; > + if (written < buf_size) > + if ((ret = virtual_seek(s, end, SEEK_SET)) < 0) > + return ret; > + } > + > + return written; > +} > + > +static void virtual_close(SegmentContext *seg) > +{ > + avio_closep(&seg->cur_pb); > + av_freep(&seg->avf->pb); > +} > + > +static int open_virtual_ctx(AVFormatContext *s, AVIOContext **ctx) > +{ > + SegmentContext *seg = s->priv_data; > + 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, s, NULL, > + virtual_write, virtual_seek); > + if (!*ctx) { > + av_free(buf); > + return AVERROR(ENOMEM); > + } > + (*ctx)->seekable = seg->seekback; > + return virtual_seek(s, 0, SEEK_SET); > +} > + > static int segment_mux_init(AVFormatContext *s) > { > SegmentContext *seg = s->priv_data; > @@ -196,6 +320,10 @@ static int set_segment_filename(AVFormatContext *s) > return AVERROR(EINVAL); > } > > + seg->cur_entry.full_filename = av_strdup(oc->filename); > + if (!seg->cur_entry.full_filename) > + return AVERROR(ENOMEM); > + > /* copy modified name in list entry */ > size = strlen(av_basename(oc->filename)) + 1; > if (seg->entry_prefix) > @@ -232,13 +360,17 @@ static int segment_start(AVFormatContext *s, int > write_header) > if ((err = set_segment_filename(s)) < 0) > return err; > > - if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, > - &s->interrupt_callback, NULL)) < 0) { > - av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", > oc->filename); > - return err; > + if (seg->individual_header_trailer) { > + if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, > + &s->interrupt_callback, NULL)) < 0) { > + av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", > oc->filename); > + return err; > + } > + } else { > + seg->cur_entry.start_offset += seg->write_offset; > + if ((err = virtual_seek(s, seg->cur_entry.start_offset, SEEK_SET)) < > 0) > + return err; > } > - if (!seg->individual_header_trailer) > - oc->pb->seekable = 0; > > if (oc->oformat->priv_class && oc->priv_data) > av_opt_set(oc->priv_data, "mpegts_flags", "+resend_headers", 0); > @@ -326,6 +458,7 @@ static int segment_end(AVFormatContext *s, int > write_trailer, int is_last) > { > SegmentContext *seg = s->priv_data; > AVFormatContext *oc = seg->avf; > + SegmentListEntry *entry; > int ret = 0; > > av_write_frame(oc, NULL); /* Flush any buffered data (fragmented mp4) */ > @@ -336,28 +469,28 @@ static int segment_end(AVFormatContext *s, int > write_trailer, int is_last) > av_log(s, AV_LOG_ERROR, "Failure occurred when ending segment '%s'\n", > oc->filename); > > + entry = av_mallocz(sizeof(*entry)); > + if (!entry) { > + ret = AVERROR(ENOMEM); > + goto end; > + } > + > + /* append new element */ > + memcpy(entry, &seg->cur_entry, sizeof(*entry)); > + if (!seg->segment_list_entries) > + seg->segment_list_entries_all->next = seg->segment_list_entries = > entry; > + else > + seg->segment_list_entries_end->next = entry; > + seg->segment_list_entries_end = entry; > + > + seg->segment_list_entry_writing = NULL; > + > if (seg->list) { > if (seg->list_size || seg->list_type == LIST_TYPE_M3U8) { > - SegmentListEntry *entry = av_mallocz(sizeof(*entry)); > - if (!entry) { > - ret = AVERROR(ENOMEM); > - goto end; > - } > - > - /* append new element */ > - memcpy(entry, &seg->cur_entry, sizeof(*entry)); > - if (!seg->segment_list_entries) > - seg->segment_list_entries = seg->segment_list_entries_end = > entry; > - else > - seg->segment_list_entries_end->next = entry; > - seg->segment_list_entries_end = entry; > > /* drop first item */ > if (seg->list_size && seg->segment_count >= seg->list_size) { > - entry = seg->segment_list_entries; > seg->segment_list_entries = seg->segment_list_entries->next; > - av_freep(&entry->filename); > - av_freep(&entry); > } > > if ((ret = segment_list_open(s)) < 0) > @@ -378,7 +511,8 @@ static int segment_end(AVFormatContext *s, int > write_trailer, int is_last) > seg->segment_count++; > > end: > - avio_closep(&oc->pb); > + if (seg->individual_header_trailer) > + avio_closep(&oc->pb); > > return ret; > } > @@ -500,26 +634,6 @@ end: > 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_ctxp(AVIOContext **pb) > -{ > - av_freep(&(*pb)->buffer); > - av_freep(pb); > -} > - > static int select_reference_stream(AVFormatContext *s) > { > SegmentContext *seg = s->priv_data; > @@ -580,6 +694,8 @@ static int select_reference_stream(AVFormatContext *s) > static void seg_free_context(SegmentContext *seg) > { > avio_closep(&seg->list_pb); > + if (!seg->individual_header_trailer) > + virtual_close(seg); > avformat_free_context(seg->avf); > seg->avf = NULL; > } > @@ -592,6 +708,14 @@ static int seg_write_header(AVFormatContext *s) > int ret; > int i; > > + seg->max_offset = SIZE_MAX; > + > + if (seg->write_header_trailer && !seg->header_filename) { > + seg->cur_entry.start_offset = 0; > + } else { > + seg->cur_entry.start_offset = SIZE_MAX; > + } > + > seg->segment_count = 0; > if (!seg->write_header_trailer) > seg->individual_header_trailer = 0; > @@ -676,16 +800,28 @@ static int seg_write_header(AVFormatContext *s) > if ((ret = set_segment_filename(s)) < 0) > goto fail; > > - if (seg->write_header_trailer) { > - if ((ret = avio_open2(&oc->pb, seg->header_filename ? > seg->header_filename : oc->filename, AVIO_FLAG_WRITE, > + seg->segment_list_entries_all = > av_mallocz(sizeof(*seg->segment_list_entries_all)); > + if (!seg->segment_list_entries_all) { > + ret = ENOMEM; > + goto fail; > + } > + > + if (seg->header_filename) { > + seg->segment_list_entries_all->full_filename = > av_strdup(seg->header_filename); > + if (!seg->segment_list_entries_all->full_filename) { > + ret = ENOMEM; > + goto fail; > + } > + } > + > + if (seg->individual_header_trailer) { > + if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, > &s->interrupt_callback, NULL)) < 0) { > av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", > oc->filename); > goto fail; > } > - if (!seg->individual_header_trailer) > - oc->pb->seekable = 0; > } else { > - if ((ret = open_null_ctx(&oc->pb)) < 0) > + if ((ret = open_virtual_ctx(s, &oc->pb)) < 0) > goto fail; > } > > @@ -715,17 +851,11 @@ static int seg_write_header(AVFormatContext *s) > s->avoid_negative_ts = 1; > > if (!seg->write_header_trailer || seg->header_filename) { > - if (seg->header_filename) { > - av_write_frame(oc, NULL); > - avio_closep(&oc->pb); > - } else { > - close_null_ctxp(&oc->pb); > - } > - if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, > - &s->interrupt_callback, NULL)) < 0) > + av_write_frame(oc, NULL); > + seg->cur_entry.start_offset = seg->write_offset; > + if ((ret = virtual_seek(s, seg->write_offset, SEEK_SET)) < 0) > goto fail; > - if (!seg->individual_header_trailer) > - oc->pb->seekable = 0; > + ret = 0; > } > > fail: > @@ -859,12 +989,17 @@ static int seg_write_trailer(struct AVFormatContext *s) > if (!seg->write_header_trailer) { > if ((ret = segment_end(s, 0, 1)) < 0) > goto fail; > - open_null_ctx(&oc->pb); > + > + seg->max_offset = seg->cur_entry.start_offset + seg->write_offset; > + virtual_seek(oc->pb, seg->max_offset, SEEK_SET); > ret = av_write_trailer(oc); > - close_null_ctxp(&oc->pb); > } else { > ret = segment_end(s, 1, 1); > } > + > + if (!seg->individual_header_trailer) > + virtual_close(seg); > + > fail: > if (seg->list) > avio_closep(&seg->list_pb); > @@ -874,9 +1009,10 @@ fail: > av_freep(&seg->times); > av_freep(&seg->frames); > > - cur = seg->segment_list_entries; > + cur = seg->segment_list_entries_all; > while (cur) { > next = cur->next; > + av_freep(&cur->full_filename); > av_freep(&cur->filename); > av_free(cur); > cur = next; > @@ -925,6 +1061,7 @@ static const AVOption options[] = { > { "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 }, > { "reset_timestamps", "reset timestamps at the begin of each segment", > OFFSET(reset_timestamps), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E }, > { "initial_offset", "set initial timestamp offset", > OFFSET(initial_offset), AV_OPT_TYPE_DURATION, {.i64 = 0}, -INT64_MAX, > INT64_MAX, E }, > + { "segment_seekback", "allow seeking back to previous segments", > OFFSET(seekback), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E }, > { NULL }, > }; > > -- > 2.3.4 >
Bump. _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel