[FFmpeg-devel] [PATCH] libavformat: Add FIFO pseudo-muxer

Jan Sebechlebsky sebechlebskyjan at gmail.com
Wed Jul 13 17:16:36 EEST 2016



On 07/12/2016 10:28 AM, Nicolas George wrote:
> Le quintidi 25 messidor, an CCXXIV, Marton Balint a écrit :
>> The fifo muxer never returns EAGAIN. It silently drops the packets in
>> non-blocking mode on a full queue. This behaviour is useful for the tee
>> muxer case, when you don't want one slow/unreliable (network) output to
>> block reading the input, therefore blocking fast outputs (disk) as well.
> Wait a minute. This is way to specific to be the default behaviour, let
> alone the only one.
>
>> As far as I know, in the current API, if the user gets a negative return
>> value from av_write_frame(), it should be handled as a fatal error. EAGAIN
>> is not handled/interpreted specially. The same is true for
>> av_write_trailer(), and calling av_write_trailer - regardless of the return
>> value - frees all private resources for the context as well, so you cannot
>> change the semantics of av_write_trailer to not free private data in case of
>> EAGAIN, because it would cause unfreed data for legacy users.
> You are wrong. Returning EAGAIN so that the caller try again later is the
> normal behaviour for muxers that support non-blocking operation. I grant you
> that there are only between one and three of them, but still, that is how
> they work.
>
> And that is also how they are supposed to work, because that is how
> non-blocking protocols work, and also how non-blocking works outside FFmpeg.
If you take a look at av_write_trailer it really frees all the resources 
in case
of any error. There is also no other way in API to free the resources 
associated
with AVFormatContext, than through
av_write_trailer.
     I was able to find two muxers which support AVFMT_FLAG_NONBLOCK - 
v4l2enc.c
and pulse_audio_enc.c. Neither of these two can return EAGAIN from 
write_trailer_call.

     Write trailer of v4l2enc.c contains simple close() call and always 
returns 0.

     The pulse audio muxer is however similar to FIFO muxer, the 
mainloop of the pulse audio is
being run in separate thread. If you take a look at the write_trailer of 
pulse audio muxer (also always returns 0):

static av_cold int pulse_write_trailer(AVFormatContext *h)
{
     PulseData *s = h->priv_data;

     if (s->mainloop) {
         ...
         pa_threaded_mainloop_unlock(s->mainloop);
         pa_threaded_mainloop_stop(s->mainloop);
         pa_threaded_mainloop_free(s->mainloop);
         s->mainloop = NULL;
     }

     return 0;
}

pa_threaded_mainloop_stop() contains:

void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) {
     ...

     pa_thread_join(m->thread);
}

So I guess the write_trailer call of pulse audio muxer should be
considered blocking too - even when AVFMT_FLAG_NONBLOCK flag is set.

I guess I could implement non-blocking calls in FIFO muxer:

- block_on_overflow should be renamed to something like 
drop_packets_on_overflow
   to prevent confusion...
- av_write_trailer would have to be modified not to free resources on 
AVERROR(EAGAIN)
- separate API function for freeing AVOuputContext resources would have 
to be created,
   let's say av_format_deinit(AVFormatContext *).
- FIFO muxer would behave the same way as currently in case 
AVFMT_FLAG_NONBLOCK
   would be unset.
- In case AVFMT_FLAG_NONBLOCK would be set:
     - write_packet would return AVERROR(EAGAIN) in case the queue is 
full if
       drop_packets_on_overflow was not set. If it was, it would drop 
packet, request queue flushing
       and return 0
     - write_trailer would check if the thread is still running. If yes 
write_trailer request would be send
       (if it was not yet send) and AVERROR(EAGAIN) returned.
     - original interrupt callback would be wrapped by custom one which 
would check terminating condition
       (flag) of FIFO first and if it would be not set, it would call 
the original callback.
     - deinit function would be modified so it would check if the thread 
is still running, if it was, terminating
       condition would be set, and pthread_join called, then resources 
would be freed. This can be also implemented
       in with use of condition variable which would be set at the 
moment when the thread is exiting and checked
       with pthread_cond_wait_timeout before pthread_join. If the time 
out would be reached, thread would be
       canceled - but canceling the thread by force can introduce many 
problems.

I can implement it, but I'm really in doubt if it is worth increased code
complexity and more risky situations which could happen, so I would 
prefer not to :)

Regards,
Jan


More information about the ffmpeg-devel mailing list