On 05/10/17 23:59, Mark Thompson wrote:
> On 05/10/17 23:01, Michael Niedermayer wrote:
>> On Thu, Oct 05, 2017 at 09:03:40PM +0100, Mark Thompson wrote:
>>> On 05/10/17 17:47, Michael Niedermayer wrote:
>>>> On Wed, Oct 04, 2017 at 02:04:54PM +0200, wm4 wrote:
>>>>> On Wed, 4 Oct 2017 13:37:31 +0200
>>>>> Tobias Rapp <t.r...@noa-archive.com> wrote:
>>>>>
>>>>>> On 04.10.2017 11:34, wm4 wrote:
>>>>>>> On Wed, 4 Oct 2017 11:22:37 +0200
>>>>>>> Michael Niedermayer <mich...@niedermayer.cc> wrote:
>>>>>>>   
>>>>>>>> On Wed, Oct 04, 2017 at 09:12:29AM +0200, wm4 wrote:  
>>>>>>>>> On Tue, 3 Oct 2017 21:40:58 +0200
>>>>>>>>> Michael Niedermayer <mich...@niedermayer.cc> wrote:
>>>>>>>>>      
>>>>>>>>>> On Tue, Oct 03, 2017 at 03:15:13PM +0200, wm4 wrote:  
>>>>>>>>>>> From: Anton Khirnov <an...@khirnov.net>
>>>>>>>>>>>        
>>>>>>>>>>      
>>>>>>>>>>> Use the AVFrame.opaque_ref field. The original user's opaque_ref is
>>>>>>>>>>> wrapped in the lavc struct and then unwrapped before the frame is
>>>>>>>>>>> returned to the caller.  
>>>>>>>>>>
>>>>>>>>>> this is a ugly hack
>>>>>>>>>>
>>>>>>>>>> one and the same field should not be used to hold both the
>>>>>>>>>> users opaque_ref as well as a structure which is itself not a user
>>>>>>>>>> opaque_ref  
>>>>>>>>>
>>>>>>>>> While the AVFrame is within libavcodec, it's libavcodec's frame, not
>>>>>>>>> the user's. Thus your claim doesn't make too much sense. libavcodec
>>>>>>>>> fully controls the meaning for its own AVFrames' opaque_ref, but
>>>>>>>>> reconstruct the user's value when returning it.  
>>>>>>>>
>>>>>>>> i disagree  
>>>>>>>
>>>>>>> Well, you're wrong anyway.
>>>>>>>   
>>>>>>>> such hacks should not be added, we do have enough hacks already  
>>>>>>>
>>>>>>> It's not a hack.  
>>>>>>
>>>>>> Changing the semantics of a field during its lifetime, even when only 
>>>>>> done within private code, is at least unexpected behavior.
>>>>>
>>>>> That's not done.
>>>>
>>>> The semantics are defined by the docs, which state:
>>>> "AVBufferRef for free use by the API user."
>>>>
>>>> And before the patch this is true, all instances of this field are
>>>> controled by the user application and are consistent.
>>>>
>>>> after the patch the AVFrames used by a codec have their opaque_ref
>>>> replaced by a wraped structure relative to what the outside of this
>>>> codec has.
>>>>
>>>>
>>>>> Conceptually the AVFrame with the changed behavior is
>>>>> a new reference. Internally, AVFrame.opaque_ref will always have the
>>>>> same semantics, pointing to the FrameDecodeData struct. Only at points
>>>>> where the AVFrame ref is converted to/from the user struct this is
>>>>> changed.
>>>>>
>>>>>>> This is done strictly when returning a valid AVFrame, so there is no
>>>>>>> room for mistakes.  
>>>>>>
>>>>>> The room for mistake might not increase for external developers but it 
>>>>>> increases for internal developers (maintenance cost).
>>>>>
>>>>> Like where? There are only 2 places where the code needs to deal with
>>>>> it, and these are in shared code, not individual decoders.
>>>>
>>>> just greping for AVFrame in the headers shows callbacks, a direct
>>>> pointer to a AVFrame and the API functions that interface the codec
>>>>
>>>> Just thinking of a codec that instanciates another codec and how
>>>> exactly the callback which may originate from the user or the outer
>>>> codec would unwrap the potential nested wraping.
>>>> I really dont think we want this in FFmpeg
>>>> And this is just one example ...
>>>
>>
>>> I don't understand this discussion.
>>
>> yes, i realize this, but iam not sure why
>>
>> its pretty simple and clear but you seem to skip over parts of what
>> i said.
>>
>>
>>>
>>> As far as I can tell, the sequence is this:
>>>
>>> * libavcodec allocates an AVFrame structure.
>>> * libavcodec calls get_buffer2 with that AVFrame structure; the user fills 
>>> its fields.
>>> * libavcodec extracts the buffer references and metadata from the AVFrame, 
>>> and maybe frees it (some codecs reuse a single AVFrame for the lifetime of 
>>> the codec, others allocate them each time).
>>> ~ decoding happens, the buffers are written to ~
>>> * The user allocates a new AVFrame structure.
>>> * The user calls receive_frame with that new AVFrame structure; libavcodec 
>>> its fields with references to the decoded data and associated metadata.
>>> * The user can then read the buffers of the frame, along with its metadata.
>>>
>>> Why would it matter what happens in the middle?  The AVFrame structure at 
>>> the end is not the AVFrame structure at the start, and the user can't 
>>> assume anything about it at all - if they try to dereference a pointer to 
>>> the AVFrame supplied by libavcodec for get_buffer2 after get_buffer2 has 
>>> returned then they are definitely invoking undefined behaviour.
>>
>> First this does not consider nested codecs.
>> A decoder can instanciate a internal decoder, if that now uses the
>> user callback it would have to unwrap twice for it if it used a
>> callback from the outer decoder it has to unwrap once.
>> This of course also depends on how it instanciates the decoder, that
>> is at which API level.
> 
> All decoders pass back to their callers the opaque reference they received, 
> so I don't think nesting is relevant.
> 
>> Next, if you look at the API and search for AVFrame you will find
>> that there are more callbacks than get_buffer2() that use AVFrames.
>> there is for example also
>>  void (*draw_horiz_band)(struct AVCodecContext *s,
>>                             const AVFrame *src, int 
>> offset[AV_NUM_DATA_POINTERS],
>>                             int y, int type, int height);
>>
>> Which too has a AVFrame, which too must be unwraped and in a way specific
>> on the nesting depth of the decoder.
> 
> Hmm, ok, I didn't know about this one, and it was never mentioned before.
> 
>> and then there is
>> AVFrame *coded_frame;
>> in AVCodecContext
>>
>> Which can be accessed by the user as well as codec.
> 
> And immediately above it:
> 
>      * - decoding: unused
> 
>> Iam pretty sure if i look more ill find more examples.> 
>> And future API changes would all have to consider this wraping and
>> unwraping.
>>
>> And then all above is just about decoders, there are encoders too
>> some share code with decoders and at the same time encoders take
>> AVFrames from the user. Does the shared code expect wraped opaque_ref
>> or not?
>> All this adds alot of complexity to maintaining the code and it would
>> add many bugs
>>
>> The opaque_ref wraping is a really bad design. Iam not sure why
>> people defend it.
> 
> I disagree with your implication here.  I was under the impression that 
> decoders have exactly one entry and one exit point for frames (get_buffer2 
> and receive_frame): since the opaque reference is only considered at those 
> two points, there is no additional complexity anywhere and no other code need 
> care about it.  However, apparently the API abstraction has additional holes 
> in it which I was not aware of (the draw_horiz_band callback you have noted 
> above), which seems to invalidate that argument.
> 
> Are there any other holes in the decoding or encoding abstractions through 
> which frames are able to leak?  It would be helpful to have a list of these 
> somewhere so that people considering how AVFrames travel through libavcodec 
> don't get caught out by this sort of thing in future.

Having thought about this a bit more, the wrapped_avframe decoder rather messes 
with it too.  If the input frame (packet) has opaque_ref set then the unwrap 
process would try to use it and fail.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel

Reply via email to