[FFmpeg-devel] [PATCH 5/6] avformat/mux: Don't modify packets we don't own
Marton Balint
cus at passwd.hu
Fri Apr 17 00:27:18 EEST 2020
On Thu, 16 Apr 2020, Andreas Rheinhardt wrote:
> 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.
Wow, that really confused me :)
Then write something like: uncoded frames are *also* freed here.
Thanks,
Marton
More information about the ffmpeg-devel
mailing list