[FFmpeg-devel] [PATCH 5/6] avformat/mux: Don't modify packets we don't own
Andreas Rheinhardt
andreas.rheinhardt at gmail.com
Fri Apr 17 00:19:29 EEST 2020
Marton Balint:
>
>
> On Sat, 11 Apr 2020, Andreas Rheinhardt wrote:
>
>> The documentation of av_write_frame() explicitly states that the function
>> doesn't take ownership of the packets sent to it; while av_write_frame()
>> does not directly unreference the packets after having written them, it
>> nevertheless modifies the packet in various ways:
>> 1. The timestamps might be modified either by prepare_input_packet() or
>> compute_muxer_pkt_fields().
>> 2. If a bitstream filter gets applied, it takes ownership of the
>> reference and the side-data in the packet sent to it.
>> In case of do_packet_auto_bsf(), the end result is that the returned
>> packet
>> contains the output of the last bsf in the chain. If an error happens,
>> a blank packet will be returned; a packet may also simply not lead to
>> any output (vp9_superframe).
>> This also implies that side data needs to be really copied and can't be
>> shared with the input packet.
>> The method choosen here minimizes copying of data: When the input isn't
>> refcounted and no bitstream filter is applied, the packet's data will
>> not be copied.
>>
>> Notice that packets that contain uncoded frames are exempt from this
>> because these packets are not owned by and returned to the user. This
>> also moves unreferencing the packets containing uncoded frames to
>> av_write_frame() in the noninterleaved codepath; in the interleaved
>> codepath, these packets are already freed in
>> av_interleaved_write_frame(),
>> so that unreferencing the packets in write_uncoded_frame_internal() is
>> no longer needed. It has been removed.
>>
>> Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt at gmail.com>
>> ---
>> I was actually surprised when I realized how freeing uncoded frames in
>> the noninterleaved codepath could be left to av_write_frame().
>>
>> libavformat/mux.c | 48 +++++++++++++++++++++++++++++++++++------------
>> 1 file changed, 36 insertions(+), 12 deletions(-)
>>
>> diff --git a/libavformat/mux.c b/libavformat/mux.c
>> index cae9f42d11..48c0d4cd5f 100644
>> --- a/libavformat/mux.c
>> +++ b/libavformat/mux.c
>> @@ -874,11 +874,12 @@ static int do_packet_auto_bsf(AVFormatContext
>> *s, AVPacket *pkt) {
>> return 1;
>> }
>>
>> -int av_write_frame(AVFormatContext *s, AVPacket *pkt)
>> +int av_write_frame(AVFormatContext *s, AVPacket *in)
>> {
>> + AVPacket local_pkt, *pkt = &local_pkt;
>> int ret;
>>
>> - if (!pkt) {
>> + if (!in) {
>> if (s->oformat->flags & AVFMT_ALLOW_FLUSH) {
>> ret = s->oformat->write_packet(s, NULL);
>> flush_if_needed(s);
>> @@ -889,22 +890,49 @@ int av_write_frame(AVFormatContext *s, AVPacket
>> *pkt)
>> return 1;
>> }
>>
>> + if (in->flags & AV_PKT_FLAG_UNCODED_FRAME) {
>> + pkt = in;
>> + } else {
>> + /* We don't own in, so we have to make sure not to modify it.
>> + * The following avoids copying in's data unnecessarily.
>> + * Copying side data is unavoidable as a bitstream filter
>> + * may change it, e.g. free it on errors. */
>> + pkt->buf = NULL;
>> + pkt->data = in->data;
>> + pkt->size = in->size;
>> + ret = av_packet_copy_props(pkt, in);
>> + if (ret < 0)
>> + return ret;
>> + if (in->buf) {
>> + pkt->buf = av_buffer_ref(in->buf);
>> + if (!pkt->buf) {
>> + ret = AVERROR(ENOMEM);
>> + goto fail;
>> + }
>> + }
>> + }
>> +
>> ret = prepare_input_packet(s, pkt);
>> if (ret < 0)
>> - return ret;
>> + goto fail;
>>
>> ret = do_packet_auto_bsf(s, pkt);
>> if (ret <= 0)
>> - return ret;
>> + goto fail;
>>
>> #if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTX
>> ret = compute_muxer_pkt_fields(s, s->streams[pkt->stream_index],
>> pkt);
>>
>> if (ret < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))
>> - return ret;
>> + goto fail;
>> #endif
>>
>> - return write_packet(s, pkt);
>> + ret = write_packet(s, pkt);
>> +
>> +fail:
>> + // Uncoded frames using the noninterleaved codepath are freed here
>
> This comment does not seem accurate. Pkt must always be unrefed here,
> not only for the uncoded_frames (where it happens to be == in), or I
> miss something? If not, then I'd just simply remove this comment.
Of course ordinary packets need to be unreferenced here, too; but I
thought that someone reading and potentially changing av_write_frame()
is already aware of that. But they might not be aware of the fact that
(contrary to the usual behaviour of av_write_frame()) some packets not
created in av_write_frame() are unreferenced there, hence this comment.
- Andreas
More information about the ffmpeg-devel
mailing list