[FFmpeg-devel] [PATCH] lavf/movenc: Use a dynamic buffer when writing the mfra box
Andreas Rheinhardt
andreas.rheinhardt at gmail.com
Tue Jun 23 18:59:25 EEST 2020
Derek Buitenhuis:
> When doing streamed output, with e.g. +dash, if the mfra box ended
> up being larger than the AVIOContext write buffer, the (unchecked)
> seeking back to update the box size would silently fail and produce
> an invalid mfra box.
>
> This is similar to how other boxes are written in fragmented mode.
>
> Signed-off-by: Derek Buitenhuis <derek.buitenhuis at gmail.com>
> ---
> libavformat/movenc.c | 40 ++++++++++++++++++++++++++++------------
> 1 file changed, 28 insertions(+), 12 deletions(-)
>
> diff --git a/libavformat/movenc.c b/libavformat/movenc.c
> index b8e45760ee..2927865cb4 100644
> --- a/libavformat/movenc.c
> +++ b/libavformat/movenc.c
> @@ -4798,28 +4798,42 @@ static int mov_write_tfra_tag(AVIOContext *pb, MOVTrack *track)
>
> static int mov_write_mfra_tag(AVIOContext *pb, MOVMuxContext *mov)
> {
> - int64_t pos = avio_tell(pb);
> - int i;
> + AVIOContext *mfra_pb;
> + int i, ret, sz;
> + uint8_t *buf;
>
> - avio_wb32(pb, 0); /* size placeholder */
> - ffio_wfourcc(pb, "mfra");
> + ret = avio_open_dyn_buf(&mfra_pb);
> + if (ret < 0)
> + return ret;
> +
> + avio_wb32(mfra_pb, 0); /* size placeholder */
> + ffio_wfourcc(mfra_pb, "mfra");
> /* An empty mfra atom is enough to indicate to the publishing point that
> * the stream has ended. */
> if (mov->flags & FF_MOV_FLAG_ISML)
> - return update_size(pb, pos);
> + goto done_mfra;
>
> for (i = 0; i < mov->nb_streams; i++) {
> MOVTrack *track = &mov->tracks[i];
> if (track->nb_frag_info)
> - mov_write_tfra_tag(pb, track);
> + mov_write_tfra_tag(mfra_pb, track);
> }
>
> - avio_wb32(pb, 16);
> - ffio_wfourcc(pb, "mfro");
> - avio_wb32(pb, 0); /* version + flags */
> - avio_wb32(pb, avio_tell(pb) + 4 - pos);
> + avio_wb32(mfra_pb, 16);
> + ffio_wfourcc(mfra_pb, "mfro");
> + avio_wb32(mfra_pb, 0); /* version + flags */
> + avio_wb32(mfra_pb, avio_tell(mfra_pb) + 4);
>
> - return update_size(pb, pos);
> +done_mfra:
> +
> + sz = update_size(mfra_pb, 0);
> + ret = avio_close_dyn_buf(mfra_pb, &buf);
Use avio_get_dyn_buf in combination with ffio_free_dyn_buf. That way you
save an allocation+copy in case the data fits into the dynamic buffer's
write buffer (it has a size of 1024 bytes; I don't know how common it is
for this box to be smaller).
> + if (ret < 0)
> + return ret;
Furthermore, avio_close_dyn_buf doesn't even provide a way to check for
errors; e.g. it does not allow you to distinguish between truncation
errors and non-truncation errors (due to returning an int, the internal
buffer is capped to INT_MAX). avio_close_dyn_buf actually may not return
anything negative (it is documented to just return the length of the
byte buffer), yet due to a bug it returns -AV_INPUT_BUFFER_PADDING_SIZE
on allocation error.
In contrast there is a way to check for errors with avio_get_dyn_buf:
Check the AVIOContext's error flag after avio_get_dyn_buf.
> + avio_write(pb, buf, ret);
> + av_free(buf);
> +
> + return sz;
> }
>
> static int mov_write_mdat_tag(AVIOContext *pb, MOVMuxContext *mov)
> @@ -6987,7 +7001,9 @@ static int mov_write_trailer(AVFormatContext *s)
> }
> if (!(mov->flags & FF_MOV_FLAG_SKIP_TRAILER)) {
> avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_TRAILER);
> - mov_write_mfra_tag(pb, mov);
> + res = mov_write_mfra_tag(pb, mov);
> + if (res < 0)
> + return res;
> }
> }
>
>
More information about the ffmpeg-devel
mailing list