History

avio: Allow custom IO users to get labels for the output bytestream

This allows callers with avio write callbacks to get the bytestream positions that correspond to keyframes, suitable for live streaming. In the simplest form, a caller could expect that a header is written to the bytestream during the avformat_write_header, and the data output to the avio context during e.g. av_write_frame corresponds exactly to the current packet passed in. When combined with av_interleaved_write_frame, and with muxers that do buffering (most muxers that do some sort of fragmenting or clustering), the mapping from input data to bytestream positions is nontrivial. This allows callers to get directly information about what part of the bytestream is what, without having to resort to assumptions about the muxer behaviour. One keyframe/fragment/block can still be split into multiple (if they are larger than the aviocontext buffer), which would call the callback with e.g. AVIO_DATA_MARKER_SYNC_POINT, followed by AVIO_DATA_MARKER_UNKNOWN for the second time it is called with the following data.

Martin Storsjö committed 10 years ago

Commit db7968b1 parent 8e757715 files · +127 −3Browse files

Unified · Split

5 files
  1. doc/APIchanges +4 −0
  2. libavformat/avio.h +66 −0
  3. libavformat/aviobuf.c +50 −2
  4. libavformat/mux.c +6 −0
  5. libavformat/version.h +1 −1
doc/APIchanges+4−0
@@ -13,6 +13,10 @@ libavutil: 2015-08-28 · 12 unmodified lines
13 13
14API changes, most recent first:14API changes, most recent first:
15 15
162016-xx-xx - xxxxxxx - lavf 57.7.0 - avio.h
17 Add AVIODataMarkerType, write_data_type, ignore_boundary_point and
18 avio_write_marker.
19
162016-xx-xx - xxxxxxx - lavu 55.12.0 - opt.h202016-xx-xx - xxxxxxx - lavu 55.12.0 - opt.h
17 Add av_stereo3d_type_name() and av_stereo3d_from_name().21 Add av_stereo3d_type_name() and av_stereo3d_from_name().
18 22
libavformat/avio.h+66−0
@@ -53,6 +53,42 @@ typedef struct AVIOInterruptCB { · 52 unmodified lines
53 void *opaque;53 void *opaque;
54} AVIOInterruptCB;54} AVIOInterruptCB;
55 55
56/**
57 * Different data types that can be returned via the AVIO
58 * write_data_type callback.
59 */
60enum AVIODataMarkerType {
61 /**
62 * Header data; this needs to be present for the stream to be decodeable.
63 */
64 AVIO_DATA_MARKER_HEADER,
65 /**
66 * A point in the output bytestream where a decoder can start decoding
67 * (i.e. a keyframe). A demuxer/decoder given the data flagged with
68 * AVIO_DATA_MARKER_HEADER, followed by any AVIO_DATA_MARKER_SYNC_POINT,
69 * should give decodeable results.
70 */
71 AVIO_DATA_MARKER_SYNC_POINT,
72 /**
73 * A point in the output bytestream where a demuxer can start parsing
74 * (for non self synchronizing bytestream formats). That is, any
75 * non-keyframe packet start point.
76 */
77 AVIO_DATA_MARKER_BOUNDARY_POINT,
78 /**
79 * This is any, unlabelled data. It can either be a muxer not marking
80 * any positions at all, it can be an actual boundary/sync point
81 * that the muxer chooses not to mark, or a later part of a packet/fragment
82 * that is cut into multiple write callbacks due to limited IO buffer size.
83 */
84 AVIO_DATA_MARKER_UNKNOWN,
85 /**
86 * Trailer data, which doesn't contain actual content, but only for
87 * finalizing the output file.
88 */
89 AVIO_DATA_MARKER_TRAILER
90};
91
56/**92/**
57 * Bytestream IO Context.93 * Bytestream IO Context.
58 * New fields can be added to the end with minor version bumps.94 * New fields can be added to the end with minor version bumps.
@@ -115,6 +151,24 @@ typedef struct AVIOContext { · 56 unmodified lines
115 * A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable.151 * A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable.
116 */152 */
117 int seekable;153 int seekable;
154
155 /**
156 * A callback that is used instead of write_packet.
157 */
158 int (*write_data_type)(void *opaque, uint8_t *buf, int buf_size,
159 enum AVIODataMarkerType type, int64_t time);
160 /**
161 * If set, don't call write_data_type separately for AVIO_DATA_MARKER_BOUNDARY_POINT,
162 * but ignore them and treat them as AVIO_DATA_MARKER_UNKNOWN (to avoid needlessly
163 * small chunks of data returned from the callback).
164 */
165 int ignore_boundary_point;
166
167 /**
168 * Internal, not meant to be used from outside of AVIOContext.
169 */
170 enum AVIODataMarkerType current_type;
171 int64_t last_time;
118} AVIOContext;172} AVIOContext;
119 173
120/**174/**
@@ -192,6 +246,18 @@ int avio_put_str16le(AVIOContext *s, const char *str); · 71 unmodified lines
192 */246 */
193int avio_put_str16be(AVIOContext *s, const char *str);247int avio_put_str16be(AVIOContext *s, const char *str);
194 248
249/**
250 * Mark the written bytestream as a specific type.
251 *
252 * Zero-length ranges are omitted from the output.
253 *
254 * @param time the stream time the current bytestream pos corresponds to
255 * (in AV_TIME_BASE units), or AV_NOPTS_VALUE if unknown or not
256 * applicable
257 * @param type the kind of data written starting at the current pos
258 */
259void avio_write_marker(AVIOContext *s, int64_t time, enum AVIODataMarkerType type);
260
195/**261/**
196 * ORing this as the "whence" parameter to a seek function causes it to262 * ORing this as the "whence" parameter to a seek function causes it to
197 * return the filesize without seeking anywhere. Supporting this is optional.263 * return the filesize without seeking anywhere. Supporting this is optional.
libavformat/aviobuf.c+50−2
@@ -140,6 +140,11 @@ int ffio_init_context(AVIOContext *s, · 139 unmodified lines
140 s->read_pause = NULL;140 s->read_pause = NULL;
141 s->read_seek = NULL;141 s->read_seek = NULL;
142 142
143 s->write_data_type = NULL;
144 s->ignore_boundary_point = 0;
145 s->current_type = AVIO_DATA_MARKER_UNKNOWN;
146 s->last_time = AV_NOPTS_VALUE;
147
143 return 0;148 return 0;
144}149}
145 150
@@ -163,13 +168,25 @@ AVIOContext *avio_alloc_context( · 17 unmodified lines
163static void flush_buffer(AVIOContext *s)168static void flush_buffer(AVIOContext *s)
164{169{
165 if (s->buf_ptr > s->buffer) {170 if (s->buf_ptr > s->buffer) {
166 if (s->write_packet && !s->error) {171 if (!s->error) {
167 int ret = s->write_packet(s->opaque, s->buffer,172 int ret = 0;
173 if (s->write_data_type)
174 ret = s->write_data_type(s->opaque, s->buffer,
175 s->buf_ptr - s->buffer,
176 s->current_type,
177 s->last_time);
178 else if (s->write_packet)
179 ret = s->write_packet(s->opaque, s->buffer,
168 s->buf_ptr - s->buffer);180 s->buf_ptr - s->buffer);
169 if (ret < 0) {181 if (ret < 0) {
170 s->error = ret;182 s->error = ret;
171 }183 }
172 }184 }
185 if (s->current_type == AVIO_DATA_MARKER_SYNC_POINT ||
186 s->current_type == AVIO_DATA_MARKER_BOUNDARY_POINT) {
187 s->current_type = AVIO_DATA_MARKER_UNKNOWN;
188 }
189 s->last_time = AV_NOPTS_VALUE;
173 if (s->update_checksum) {190 if (s->update_checksum) {
174 s->checksum = s->update_checksum(s->checksum, s->checksum_ptr,191 s->checksum = s->update_checksum(s->checksum, s->checksum_ptr,
175 s->buf_ptr - s->checksum_ptr);192 s->buf_ptr - s->checksum_ptr);
@@ -402,6 +419,37 @@ void avio_wb24(AVIOContext *s, unsigned int val) · 226 unmodified lines
402 avio_w8(s, val);419 avio_w8(s, val);
403}420}
404 421
422void avio_write_marker(AVIOContext *s, int64_t time, enum AVIODataMarkerType type)
423{
424 if (!s->write_data_type)
425 return;
426 // If ignoring boundary points, just treat it as unknown
427 if (type == AVIO_DATA_MARKER_BOUNDARY_POINT && s->ignore_boundary_point)
428 type = AVIO_DATA_MARKER_UNKNOWN;
429 // Avoid unnecessary flushes if we are already in non-header/trailer
430 // data and setting the type to unknown
431 if (type == AVIO_DATA_MARKER_UNKNOWN &&
432 (s->current_type != AVIO_DATA_MARKER_HEADER &&
433 s->current_type != AVIO_DATA_MARKER_TRAILER))
434 return;
435
436 switch (type) {
437 case AVIO_DATA_MARKER_HEADER:
438 case AVIO_DATA_MARKER_TRAILER:
439 // For header/trailer, ignore a new marker of the same type;
440 // consecutive header/trailer markers can be merged.
441 if (type == s->current_type)
442 return;
443 break;
444 }
445
446 // If we've reached here, we have a new, noteworthy marker.
447 // Flush the previous data and mark the start of the new data.
448 avio_flush(s);
449 s->current_type = type;
450 s->last_time = time;
451}
452
405/* Input stream */453/* Input stream */
406 454
407static void fill_buffer(AVIOContext *s)455static void fill_buffer(AVIOContext *s)
libavformat/mux.c+6−0
@@ -256,11 +256,15 @@ int avformat_write_header(AVFormatContext *s, AVDictionary **options) · 255 unmodified lines
256 if (ret = init_muxer(s, options))256 if (ret = init_muxer(s, options))
257 return ret;257 return ret;
258 258
259 if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb)
260 avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_HEADER);
259 if (s->oformat->write_header) {261 if (s->oformat->write_header) {
260 ret = s->oformat->write_header(s);262 ret = s->oformat->write_header(s);
261 if (ret < 0)263 if (ret < 0)
262 return ret;264 return ret;
263 }265 }
266 if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb)
267 avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_UNKNOWN);
264 268
265 if (s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO) {269 if (s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO) {
266 if (s->oformat->flags & (AVFMT_TS_NEGATIVE | AVFMT_NOTIMESTAMPS)) {270 if (s->oformat->flags & (AVFMT_TS_NEGATIVE | AVFMT_NOTIMESTAMPS)) {
@@ -704,6 +708,8 @@ int av_write_trailer(AVFormatContext *s) · 437 unmodified lines
704 goto fail;708 goto fail;
705 }709 }
706 710
711 if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb)
712 avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_TRAILER);
707 if (s->oformat->write_trailer)713 if (s->oformat->write_trailer)
708 ret = s->oformat->write_trailer(s);714 ret = s->oformat->write_trailer(s);
709 715
libavformat/version.h+1−1
@@ -30,7 +30,7 @@ · 29 unmodified lines
30#include "libavutil/version.h"30#include "libavutil/version.h"
31 31
32#define LIBAVFORMAT_VERSION_MAJOR 5732#define LIBAVFORMAT_VERSION_MAJOR 57
33#define LIBAVFORMAT_VERSION_MINOR 633#define LIBAVFORMAT_VERSION_MINOR 7
34#define LIBAVFORMAT_VERSION_MICRO 034#define LIBAVFORMAT_VERSION_MICRO 0
35 35
36#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \36#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t