Michael Niedermayer: > On Thu, Mar 11, 2021 at 02:18:36PM -0300, James Almer wrote: >> On 3/11/2021 1:35 PM, Michael Niedermayer wrote: >>> On Wed, Mar 10, 2021 at 05:59:11PM -0300, James Almer wrote: >>>> On 3/10/2021 5:18 PM, Michael Niedermayer wrote: >>>>> On Mon, Feb 22, 2021 at 07:27:34PM -0300, James Almer wrote: >>>>>> On 2/21/2021 6:04 PM, James Almer wrote: >>>>>>> On 2/21/2021 5:29 PM, Mark Thompson wrote: >>>>>>>> On 21/02/2021 20:00, James Almer wrote: >>>>>>>>> On 2/21/2021 4:13 PM, Mark Thompson wrote: >>>>>>>>>> On 21/02/2021 17:35, James Almer wrote: >>>>>>>>>>> This callback is functionally the same as get_buffer2() >>>>>>>>>>> is for decoders, and >>>>>>>>>>> implements for the new encode API the functionality of >>>>>>>>>>> the old encode API had >>>>>>>>>>> where the user could provide their own buffers. >>>>>>>>>>> >>>>>>>>>>> Signed-off-by: James Almer <jamr...@gmail.com> >>>>>>>>>>> --- >>>>>>>>>>> Used the names Lynne suggested this time, plus a line >>>>>>>>>>> about how the callback >>>>>>>>>>> must be thread safe. >>>>>>>>>>> >>>>>>>>>>> libavcodec/avcodec.h | 45 ++++++++++++++++++++++++++++++++++++ >>>>>>>>>>> libavcodec/codec.h | 8 ++++--- >>>>>>>>>>> libavcodec/encode.c | 54 >>>>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++- >>>>>>>>>>> libavcodec/encode.h | 8 +++++++ >>>>>>>>>>> libavcodec/options.c | 1 + >>>>>>>>>>> 5 files changed, 112 insertions(+), 4 deletions(-) >>>>>>>>>>> >>>>>>>>>>> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h >>>>>>>>>>> index 7dbf083a24..e60eb16ce1 100644 >>>>>>>>>>> --- a/libavcodec/avcodec.h >>>>>>>>>>> +++ b/libavcodec/avcodec.h >>>>>>>>>>> @@ -513,6 +513,11 @@ typedef struct AVProducerReferenceTime { >>>>>>>>>>> */ >>>>>>>>>>> #define AV_GET_BUFFER_FLAG_REF (1 << 0) >>>>>>>>>>> +/** >>>>>>>>>>> + * The encoder will keep a reference to the packet and >>>>>>>>>>> may reuse it later. >>>>>>>>>>> + */ >>>>>>>>>>> +#define AV_GET_ENCODER_BUFFER_FLAG_REF (1 << 0) >>>>>>>>>>> + >>>>>>>>>>> struct AVCodecInternal; >>>>>>>>>>> /** >>>>>>>>>>> @@ -2346,6 +2351,39 @@ typedef struct AVCodecContext { >>>>>>>>>>> * - encoding: set by user >>>>>>>>>>> */ >>>>>>>>>>> int export_side_data; >>>>>>>>>>> + >>>>>>>>>>> + /** >>>>>>>>>>> + * This callback is called at the beginning of each >>>>>>>>>>> packet to get a data >>>>>>>>>>> + * buffer for it. >>>>>>>>>>> + * >>>>>>>>>>> + * The following field will be set in the packet >>>>>>>>>>> before this callback is >>>>>>>>>>> + * called: >>>>>>>>>>> + * - size >>>>>>>>>>> + * This callback must use the above value to >>>>>>>>>>> calculate the required buffer size, >>>>>>>>>>> + * which must padded by at least >>>>>>>>>>> AV_INPUT_BUFFER_PADDING_SIZE bytes. >>>>>>>>>>> + * >>>>>>>>>>> + * This callback must fill the following fields in the packet: >>>>>>>>>>> + * - data >>>>>>>>>> >>>>>>>>>> Is the data pointer allowed to be in write-only memory? >>>>>>>>> >>>>>>>>> I'm not sure what the use case for this would be, so probably no? >>>>>>>> >>>>>>>> The two use-cases I see for this API are: >>>>>>>> >>>>>>>> * You want to avoid a copy when combining the output with something >>>>>>>> else. E.g. you pass a pointer to the block of memory following >>>>>>>> where you are going to put your header data (for something you are >>>>>>>> going to send over the network, say). >>>>>>>> >>>>>>>> * You want to avoid a copy when passing the output directly to >>>>>>>> something external. E.g. you pass a pointer to a memory-mapped >>>>>>>> device buffer (such as a V4L2 buffer, say). >>>>>>>> >>>>>>>> In the second case, write-only memory on an external device seems >>>>>>>> possible, as does memory which is, say, readable but uncached, so >>>>>>>> reading it is a really bad idea. >>>>>>> >>>>>>> Allowing the second case would depend on how encoders behave. Some may >>>>>>> attempt to read data already written to the output packet. It's not like >>>>>>> all of them allocate the packet, do a memcpy from an internal buffer, >>>>>>> then return. >>>>>>> There is also the flag meant to signal that the encoder will keep a >>>>>>> reference to the packet around, which more or less implies it will be >>>>>>> read later in the encoding process. >>>>>>> >>>>>>> The doxy for avcodec_encode_video2(), which allowed the user to provide >>>>>>> their own buffers in the output packet, does not mention any kind of >>>>>>> requirement for the data pointer, so I don't think we can say it's an >>>>>>> allowed scenario here either. >>>>>>> >>>>>>>> >>>>>>>>>> Does it have any alignment requirements? >>>>>>>>> >>>>>>>>> No, just padding. AVPacket doesn't require alignment for the payload. >>>>>>>> >>>>>>>> I think say that explicitly. avcodec_default_get_encoder_buffer() >>>>>>>> does give you aligned memory, even though it isn't needed. >>>>>>> >>>>>>> Would saying "There's no alignment requirement for the data pointer" add >>>>>>> anything of value to the doxy? If i don't mention any kind of alignment >>>>>>> requirement, it's because there isn't any, and it's implicit. >>>>>>> I listed the requirements the user needs to keep in mind, like the >>>>>>> padding and the need for an AVBufferRef. But if you think it's worth >>>>>>> adding, then sure. >>>>>>> >>>>>>>> >>>>>>>>>>> + * - buf must contain a pointer to an AVBufferRef >>>>>>>>>>> structure. The packet's >>>>>>>>>>> + * data pointer must be contained in it. >>>>>>>>>>> + * See: av_buffer_create(), av_buffer_alloc(), >>>>>>>>>>> and av_buffer_ref(). >>>>>>>>>>> + * >>>>>>>>>>> + * If AV_CODEC_CAP_DR1 is not set then >>>>>>>>>>> get_encoder_buffer() must call >>>>>>>>>>> + * avcodec_default_get_encoder_buffer() instead of >>>>>>>>>>> providing a buffer allocated by >>>>>>>>>>> + * some other means. >>>>>>>>>>> + * >>>>>>>>>>> + * If AV_GET_ENCODER_BUFFER_FLAG_REF is set in >>>>>>>>>>> flags then the packet may be reused >>>>>>>>>>> + * (read and/or written to if it is writable) later >>>>>>>>>>> by libavcodec. >>>>>>>>>>> + * >>>>>>>>>>> + * This callback must be thread-safe, as when frame >>>>>>>>>>> multithreading is used, it may >>>>>>>>>>> + * be called from multiple threads simultaneously. >>>>>>>>>> >>>>>>>>>> Allowing simulatenous calls feels unexpectedly tricky. Is >>>>>>>>>> it really necessary? >>>>>>>>> >>>>>>>>> This was a suggestion by Lynne, i personally don't know. We >>>>>>>>> support frame threading encoding (For intra-only codecs), but >>>>>>>>> currently ff_alloc_packet2() does not seem to be thread safe, >>>>>>>>> seeing it calls av_fast_padded_malloc(), yet it's called by >>>>>>>>> frame threaded encoders. >>>>>>>>> Should i remove this? >>>>>>>> >>>>>>>> I don't know, I was asking only because it sounds tricky. For cases >>>>>>>> with a limited number of buffers available (like memory-mapped >>>>>>>> devices) you are going to need locking anyway, so maybe rentrancy >>>>>>>> adds no additional inconvenience. >>>>>>>> >>>>>>>>>>> + * >>>>>>>>>>> + * @see avcodec_default_get_encoder_buffer() >>>>>>>>>>> + * >>>>>>>>>>> + * - encoding: Set by libavcodec, user can override. >>>>>>>>>>> + * - decoding: unused >>>>>>>>>>> + */ >>>>>>>>>>> + int (*get_encoder_buffer)(struct AVCodecContext *s, >>>>>>>>>>> AVPacket *pkt, int flags); >>>>>>>>>> >>>>>>>>>> Can the encoder ask for arbitrarily many packets? >>>>>>>>>> >>>>>>>>>> Can the user return "not yet" somehow to this if they have a >>>>>>>>>> fixed output buffer pool but no buffer is currently >>>>>>>>>> available? >>>>>>>>> >>>>>>>>> No, as is it can't. Return values < 0 are considered errors. >>>>>>>>> >>>>>>>>>> >>>>>>>>>> I don't much like the idea of the user suspending the thread >>>>>>>>>> in the callback until they have some available, which might >>>>>>>>>> work in some cases but might also deadlock if an >>>>>>>>>> avcodec_receive_packet() call is blocked by it. >>>>>>>>> >>>>>>>>> Can we make what's in essence a malloc() call return something >>>>>>>>> like EAGAIN, and this in turn be propagated back to >>>>>>>>> encode_receive_packet_internal()? >>>>>>>> >>>>>>>> Maybe, or if it has many threads maybe it could wait for something >>>>>>>> else to finish first. >>>>>>>> >>>>>>>>> Couldn't this potentially end up in the forbidden scenario of >>>>>>>>> avcodec_send_frame() and avcodec_receive_packet() both returning >>>>>>>>> EAGAIN? >>>>>>>> >>>>>>>> Yes. If the forbidden case happens then the encoder is stuck anyway >>>>>>>> and can't make any forward progress so we need to error out >>>>>>>> properly, but the EAGAIN return isn't needed if there is something >>>>>>>> else to do on another thread. >>>>>>> >>>>>>> Ok, but I'm not familiar or knowledgeable enough with the frame thread >>>>>>> encoder code to implement this. >>>>>> >>>>>> Looked at bit into this. AVCodec->encode2() based encoders don't support >>>>>> returning EAGAIN at all, as it completely breaks the frame threading >>>>>> logic. >>>>>> It would require a considerable rewrite in order to re-add a task that >>>>>> didn't fail but also didn't succeed. >>>>>> >>>>>> Non frame threading encoders could probably support it with some minimal >>>>>> changes, but i don't think suddenly letting an scenario that was until >>>>>> now >>>>>> guaranteed to never happen start happening (avcodec_send_frame() and >>>>>> avcodec_receive_packet() both returning EAGAIN) is a good idea. It's an >>>>>> API >>>>>> break. >>>>>> Letting the user's custom get_encode_buffer() callback suspend the >>>>>> thread is >>>>>> IMO acceptable. In frame threading scenarios, the other threads are still >>>>>> working on their own packets (afaics none depends on the others, since >>>>>> it's >>>>>> intra only encoders only). >>>>> >>>>> I think it was not suggested in the thread so: >>>>> if the users allocation fails the code can fallback to the default >>>>> allocator >>>>> That would lead to the relation: >>>>> If a users allocator can fail (out of buffers) it must be able to handle >>>>> that only some of the returned packets are from its own allocator >>>> >>>> In general, custom allocators are used when the caller doesn't want to use >>>> the default one. But yes, they could use >>>> avcodec_default_get_encoder_buffer() as fallback, which is why it was added >>>> to begin with. Same applies to get_buffer2() custom implementations, and so >>>> far i don't think anybody had issues identifying what allocated a packet >>>> buffer. >>>> >>>> One of the additions to AVPacket people were talking about was a user >>>> opaque >>>> field that libav* would never touch or look at beyond propagating them >>>> around all the way to the output AVFrame, if any. This opaque field could >>>> perhaps store such allocator specific information the caller could use to >>>> identify packets allocated by their own allocator, or those by >>>> avcodec_default_get_encoder_buffer(). >>>> >>>>> >>>>> About alignment, we should at least recommand that allocated packets are >>>>> aligned not less than what out av_malloc() would align to. >>>>> Is there a reason to align less ? >>>> >>>> There's no alignment requirement for AVPacket->data, and av_new_packet() >>>> uses av_buffer_realloc(), which does not guarantee any alignment whatsoever >>>> on platforms other than Windows. So basically, packet payload buffers >>>> allocated by our own helpers never had any alignment. >>> >>> for the purpose of exporting raw images, alignment would be "nice to have" >>> because later filters may need it or need to memcpy >> >> Filters don't use AVPackets, they use AVFrames. > > demuxers return AVPackets, so do encoders. > These can contain raw frames. > > also i see for example in rawdec: > frame->buf[0] = av_buffer_ref(avpkt->buf); >
That seems to be completely broken: I see no check for whether the packet is writable at all. Will investigate. - Andreas _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".