@ -34,6 +34,9 @@
# include "libavutil/rational.h"
# include "libavutil/samplefmt.h"
# define FF_INTERNAL_FIELDS 1
# include "framequeue.h"
# include "audio.h"
# include "avfilter.h"
# include "formats.h"
@ -135,6 +138,10 @@ int avfilter_link(AVFilterContext *src, unsigned srcpad,
{
AVFilterLink * link ;
av_assert0 ( src - > graph ) ;
av_assert0 ( dst - > graph ) ;
av_assert0 ( src - > graph = = dst - > graph ) ;
if ( src - > nb_outputs < = srcpad | | dst - > nb_inputs < = dstpad | |
src - > outputs [ srcpad ] | | dst - > inputs [ dstpad ] )
return AVERROR ( EINVAL ) ;
@ -160,6 +167,7 @@ int avfilter_link(AVFilterContext *src, unsigned srcpad,
link - > type = src - > output_pads [ srcpad ] . type ;
av_assert0 ( AV_PIX_FMT_NONE = = - 1 & & AV_SAMPLE_FMT_NONE = = - 1 ) ;
link - > format = - 1 ;
ff_framequeue_init ( & link - > fifo , & src - > graph - > internal - > frame_queues ) ;
return 0 ;
}
@ -170,6 +178,7 @@ void avfilter_link_free(AVFilterLink **link)
return ;
av_frame_free ( & ( * link ) - > partial_buf ) ;
ff_framequeue_free ( & ( * link ) - > fifo ) ;
ff_video_frame_pool_uninit ( ( FFVideoFramePool * * ) & ( * link ) - > video_frame_pool ) ;
av_freep ( link ) ;
@ -180,16 +189,46 @@ int avfilter_link_get_channels(AVFilterLink *link)
return link - > channels ;
}
static void ff_filter_set_ready ( AVFilterContext * filter , unsigned priority )
{
filter - > ready = FFMAX ( filter - > ready , priority ) ;
}
/**
* Clear frame_blocked_in on all outputs .
* This is necessary whenever something changes on input .
*/
static void filter_unblock ( AVFilterContext * filter )
{
unsigned i ;
for ( i = 0 ; i < filter - > nb_outputs ; i + + )
filter - > outputs [ i ] - > frame_blocked_in = 0 ;
}
void ff_avfilter_link_set_in_status ( AVFilterLink * link , int status , int64_t pts )
{
ff_avfilter_link_set_out_status ( link , status , pts ) ;
if ( link - > status_in = = status )
return ;
av_assert0 ( ! link - > status_in ) ;
link - > status_in = status ;
link - > status_in_pts = pts ;
link - > frame_wanted_out = 0 ;
link - > frame_blocked_in = 0 ;
filter_unblock ( link - > dst ) ;
ff_filter_set_ready ( link - > dst , 200 ) ;
}
void ff_avfilter_link_set_out_status ( AVFilterLink * link , int status , int64_t pts )
{
link - > status = status ;
link - > frame_wanted_in = link - > frame_wanted_out = 0 ;
ff_update_link_current_pts ( link , pts ) ;
av_assert0 ( ! link - > frame_wanted_out ) ;
av_assert0 ( ! link - > status_out ) ;
link - > status_out = status ;
if ( pts ! = AV_NOPTS_VALUE )
ff_update_link_current_pts ( link , pts ) ;
filter_unblock ( link - > dst ) ;
ff_filter_set_ready ( link - > src , 200 ) ;
}
void avfilter_link_set_closed ( AVFilterLink * link , int closed )
@ -370,10 +409,23 @@ int ff_request_frame(AVFilterLink *link)
{
FF_TPRINTF_START ( NULL , request_frame ) ; ff_tlog_link ( NULL , link , 1 ) ;
if ( link - > status )
return link - > status ;
link - > frame_wanted_in = 1 ;
if ( link - > status_out )
return link - > status_out ;
if ( link - > status_in ) {
if ( ff_framequeue_queued_frames ( & link - > fifo ) ) {
av_assert1 ( ! link - > frame_wanted_out ) ;
av_assert1 ( link - > dst - > ready > = 300 ) ;
return 0 ;
} else {
/* Acknowledge status change. Filters using ff_request_frame() will
handle the change automatically . Filters can also check the
status directly but none do yet . */
ff_avfilter_link_set_out_status ( link , link - > status_in , link - > status_in_pts ) ;
return link - > status_out ;
}
}
link - > frame_wanted_out = 1 ;
ff_filter_set_ready ( link - > src , 100 ) ;
return 0 ;
}
@ -382,22 +434,17 @@ int ff_request_frame_to_filter(AVFilterLink *link)
int ret = - 1 ;
FF_TPRINTF_START ( NULL , request_frame_to_filter ) ; ff_tlog_link ( NULL , link , 1 ) ;
link - > frame_wanted_in = 0 ;
/* Assume the filter is blocked, let the method clear it if not */
link - > frame_blocked_in = 1 ;
if ( link - > srcpad - > request_frame )
ret = link - > srcpad - > request_frame ( link ) ;
else if ( link - > src - > inputs [ 0 ] )
ret = ff_request_frame ( link - > src - > inputs [ 0 ] ) ;
if ( ret = = AVERROR_EOF & & link - > partial_buf ) {
AVFrame * pbuf = link - > partial_buf ;
link - > partial_buf = NULL ;
ret = ff_filter_frame_framed ( link , pbuf ) ;
ff_avfilter_link_set_in_status ( link , AVERROR_EOF , AV_NOPTS_VALUE ) ;
link - > frame_wanted_out = 0 ;
return ret ;
}
if ( ret < 0 ) {
if ( ret ! = AVERROR ( EAGAIN ) & & ret ! = link - > status )
if ( ret ! = AVERROR ( EAGAIN ) & & ret ! = link - > status_in )
ff_avfilter_link_set_in_status ( link , ret , AV_NOPTS_VALUE ) ;
if ( ret = = AVERROR_EOF )
ret = 0 ;
}
return ret ;
}
@ -1056,11 +1103,6 @@ static int ff_filter_frame_framed(AVFilterLink *link, AVFrame *frame)
AVFilterCommand * cmd = link - > dst - > command_queue ;
int64_t pts ;
if ( link - > status ) {
av_frame_free ( & frame ) ;
return link - > status ;
}
if ( ! ( filter_frame = dst - > filter_frame ) )
filter_frame = default_filter_frame ;
@ -1142,52 +1184,9 @@ fail:
return ret ;
}
static int ff_filter_frame_needs_framing ( AVFilterLink * link , AVFrame * frame )
{
int insamples = frame - > nb_samples , inpos = 0 , nb_samples ;
AVFrame * pbuf = link - > partial_buf ;
int nb_channels = av_frame_get_channels ( frame ) ;
int ret = 0 ;
/* Handle framing (min_samples, max_samples) */
while ( insamples ) {
if ( ! pbuf ) {
AVRational samples_tb = { 1 , link - > sample_rate } ;
pbuf = ff_get_audio_buffer ( link , link - > partial_buf_size ) ;
if ( ! pbuf ) {
av_log ( link - > dst , AV_LOG_WARNING ,
" Samples dropped due to memory allocation failure. \n " ) ;
return 0 ;
}
av_frame_copy_props ( pbuf , frame ) ;
pbuf - > pts = frame - > pts ;
if ( pbuf - > pts ! = AV_NOPTS_VALUE )
pbuf - > pts + = av_rescale_q ( inpos , samples_tb , link - > time_base ) ;
pbuf - > nb_samples = 0 ;
}
nb_samples = FFMIN ( insamples ,
link - > partial_buf_size - pbuf - > nb_samples ) ;
av_samples_copy ( pbuf - > extended_data , frame - > extended_data ,
pbuf - > nb_samples , inpos ,
nb_samples , nb_channels , link - > format ) ;
inpos + = nb_samples ;
insamples - = nb_samples ;
pbuf - > nb_samples + = nb_samples ;
if ( pbuf - > nb_samples > = link - > min_samples ) {
ret = ff_filter_frame_framed ( link , pbuf ) ;
pbuf = NULL ;
} else {
if ( link - > frame_wanted_out )
link - > frame_wanted_in = 1 ;
}
}
av_frame_free ( & frame ) ;
link - > partial_buf = pbuf ;
return ret ;
}
int ff_filter_frame ( AVFilterLink * link , AVFrame * frame )
{
int ret ;
FF_TPRINTF_START ( NULL , filter_frame ) ; ff_tlog_link ( NULL , link , 1 ) ; ff_tlog ( NULL , " " ) ; ff_tlog_ref ( NULL , frame , 1 ) ;
/* Consistency checks */
@ -1220,23 +1219,329 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
}
}
link - > frame_wanted_out = 0 ;
link - > frame_blocked_in = link - > frame_ wanted_out = 0 ;
link - > frame_count_in + + ;
/* Go directly to actual filtering if possible */
if ( link - > type = = AVMEDIA_TYPE_AUDIO & &
link - > min_samples & &
( link - > partial_buf | |
frame - > nb_samples < link - > min_samples | |
frame - > nb_samples > link - > max_samples ) ) {
return ff_filter_frame_needs_framing ( link , frame ) ;
} else {
return ff_filter_frame_framed ( link , frame ) ;
filter_unblock ( link - > dst ) ;
ret = ff_framequeue_add ( & link - > fifo , frame ) ;
if ( ret < 0 ) {
av_frame_free ( & frame ) ;
return ret ;
}
ff_filter_set_ready ( link - > dst , 300 ) ;
return 0 ;
error :
av_frame_free ( & frame ) ;
return AVERROR_PATCHWELCOME ;
}
static int samples_ready ( AVFilterLink * link )
{
return ff_framequeue_queued_frames ( & link - > fifo ) & &
( ff_framequeue_queued_samples ( & link - > fifo ) > = link - > min_samples | |
link - > status_in ) ;
}
static int take_samples ( AVFilterLink * link , unsigned min , unsigned max ,
AVFrame * * rframe )
{
AVFrame * frame0 , * frame , * buf ;
unsigned nb_samples , nb_frames , i , p ;
int ret ;
/* Note: this function relies on no format changes and must only be
called with enough samples . */
av_assert1 ( samples_ready ( link ) ) ;
frame0 = frame = ff_framequeue_peek ( & link - > fifo , 0 ) ;
if ( frame - > nb_samples > = min & & frame - > nb_samples < max ) {
* rframe = ff_framequeue_take ( & link - > fifo ) ;
return 0 ;
}
nb_frames = 0 ;
nb_samples = 0 ;
while ( 1 ) {
if ( nb_samples + frame - > nb_samples > max ) {
if ( nb_samples < min )
nb_samples = max ;
break ;
}
nb_samples + = frame - > nb_samples ;
nb_frames + + ;
if ( nb_frames = = ff_framequeue_queued_frames ( & link - > fifo ) )
break ;
frame = ff_framequeue_peek ( & link - > fifo , nb_frames ) ;
}
buf = ff_get_audio_buffer ( link , nb_samples ) ;
if ( ! buf )
return AVERROR ( ENOMEM ) ;
ret = av_frame_copy_props ( buf , frame0 ) ;
if ( ret < 0 ) {
av_frame_free ( & buf ) ;
return ret ;
}
buf - > pts = frame0 - > pts ;
p = 0 ;
for ( i = 0 ; i < nb_frames ; i + + ) {
frame = ff_framequeue_take ( & link - > fifo ) ;
av_samples_copy ( buf - > extended_data , frame - > extended_data , p , 0 ,
frame - > nb_samples , link - > channels , link - > format ) ;
p + = frame - > nb_samples ;
}
if ( p < nb_samples ) {
unsigned n = nb_samples - p ;
frame = ff_framequeue_peek ( & link - > fifo , 0 ) ;
av_samples_copy ( buf - > extended_data , frame - > extended_data , p , 0 , n ,
link - > channels , link - > format ) ;
frame - > nb_samples - = n ;
av_samples_copy ( frame - > extended_data , frame - > extended_data , 0 , n ,
frame - > nb_samples , link - > channels , link - > format ) ;
if ( frame - > pts ! = AV_NOPTS_VALUE )
frame - > pts + = av_rescale_q ( n , av_make_q ( 1 , link - > sample_rate ) , link - > time_base ) ;
ff_framequeue_update_peeked ( & link - > fifo , 0 ) ;
ff_framequeue_skip_samples ( & link - > fifo , n ) ;
}
* rframe = buf ;
return 0 ;
}
int ff_filter_frame_to_filter ( AVFilterLink * link )
{
AVFrame * frame ;
AVFilterContext * dst = link - > dst ;
int ret ;
av_assert1 ( ff_framequeue_queued_frames ( & link - > fifo ) ) ;
if ( link - > min_samples ) {
int min = link - > min_samples ;
if ( link - > status_in )
min = FFMIN ( min , ff_framequeue_queued_samples ( & link - > fifo ) ) ;
ret = take_samples ( link , min , link - > max_samples , & frame ) ;
if ( ret < 0 )
return ret ;
} else {
frame = ff_framequeue_take ( & link - > fifo ) ;
}
/* The filter will soon have received a new frame, that may allow it to
produce one or more : unblock its outputs . */
filter_unblock ( dst ) ;
ret = ff_filter_frame_framed ( link , frame ) ;
if ( ret < 0 & & ret ! = link - > status_out ) {
ff_avfilter_link_set_out_status ( link , ret , AV_NOPTS_VALUE ) ;
} else {
/* Run once again, to see if several frames were available, or if
the input status has also changed , or any other reason . */
ff_filter_set_ready ( dst , 300 ) ;
}
return ret ;
}
static int forward_status_change ( AVFilterContext * filter , AVFilterLink * in )
{
unsigned out = 0 , progress = 0 ;
int ret ;
av_assert0 ( ! in - > status_out ) ;
if ( ! filter - > nb_outputs ) {
/* not necessary with the current API and sinks */
return 0 ;
}
while ( ! in - > status_out ) {
if ( ! filter - > outputs [ out ] - > status_in ) {
progress + + ;
ret = ff_request_frame_to_filter ( filter - > outputs [ out ] ) ;
if ( ret < 0 )
return ret ;
}
if ( + + out = = filter - > nb_outputs ) {
if ( ! progress ) {
/* Every output already closed: input no longer interesting
( example : overlay in shortest mode , other input closed ) . */
ff_avfilter_link_set_out_status ( in , in - > status_in , in - > status_in_pts ) ;
return 0 ;
}
progress = 0 ;
out = 0 ;
}
}
ff_filter_set_ready ( filter , 200 ) ;
return 0 ;
}
# define FFERROR_NOT_READY FFERRTAG('N','R','D','Y')
static int ff_filter_activate_default ( AVFilterContext * filter )
{
unsigned i ;
for ( i = 0 ; i < filter - > nb_inputs ; i + + ) {
if ( samples_ready ( filter - > inputs [ i ] ) ) {
return ff_filter_frame_to_filter ( filter - > inputs [ i ] ) ;
}
}
for ( i = 0 ; i < filter - > nb_inputs ; i + + ) {
if ( filter - > inputs [ i ] - > status_in & & ! filter - > inputs [ i ] - > status_out ) {
av_assert1 ( ! ff_framequeue_queued_frames ( & filter - > inputs [ i ] - > fifo ) ) ;
return forward_status_change ( filter , filter - > inputs [ i ] ) ;
}
}
for ( i = 0 ; i < filter - > nb_outputs ; i + + ) {
if ( filter - > outputs [ i ] - > frame_wanted_out & &
! filter - > outputs [ i ] - > frame_blocked_in ) {
return ff_request_frame_to_filter ( filter - > outputs [ i ] ) ;
}
}
return FFERROR_NOT_READY ;
}
/*
Filter scheduling and activation
When a filter is activated , it must :
- if possible , output a frame ;
- else , if relevant , forward the input status change ;
- else , check outputs for wanted frames and forward the requests .
The following AVFilterLink fields are used for activation :
- frame_wanted_out :
This field indicates if a frame is needed on this input of the
destination filter . A positive value indicates that a frame is needed
to process queued frames or internal data or to satisfy the
application ; a zero value indicates that a frame is not especially
needed but could be processed anyway ; a negative value indicates that a
frame would just be queued .
It is set by filters using ff_request_frame ( ) or ff_request_no_frame ( ) ,
when requested by the application through a specific API or when it is
set on one of the outputs .
It is cleared when a frame is sent from the source using
ff_filter_frame ( ) .
It is also cleared when a status change is sent from the source using
ff_avfilter_link_set_in_status ( ) .
- frame_blocked_in :
This field means that the source filter can not generate a frame as is .
Its goal is to avoid repeatedly calling the request_frame ( ) method on
the same link .
It is set by the framework on all outputs of a filter before activating it .
It is automatically cleared by ff_filter_frame ( ) .
It is also automatically cleared by ff_avfilter_link_set_in_status ( ) .
It is also cleared on all outputs ( using filter_unblock ( ) ) when
something happens on an input : processing a frame or changing the
status .
- fifo :
Contains the frames queued on a filter input . If it contains frames and
frame_wanted_out is not set , then the filter can be activated . If that
result in the filter not able to use these frames , the filter must set
frame_wanted_out to ask for more frames .
- status_in and status_in_pts :
Status ( EOF or error code ) of the link and timestamp of the status
change ( in link time base , same as frames ) as seen from the input of
the link . The status change is considered happening after the frames
queued in fifo .
It is set by the source filter using ff_avfilter_link_set_in_status ( ) .
- status_out :
Status of the link as seen from the output of the link . The status
change is considered having already happened .
It is set by the destination filter using
ff_avfilter_link_set_out_status ( ) .
Filters are activated according to the ready field , set using the
ff_filter_set_ready ( ) . Eventually , a priority queue will be used .
ff_filter_set_ready ( ) is called whenever anything could cause progress to
be possible . Marking a filter ready when it is not is not a problem ,
except for the small overhead it causes .
Conditions that cause a filter to be marked ready are :
- frames added on an input link ;
- changes in the input or output status of an input link ;
- requests for a frame on an output link ;
- after any actual processing using the legacy methods ( filter_frame ( ) ,
and request_frame ( ) to acknowledge status changes ) , to run once more
and check if enough input was present for several frames .
Exemples of scenarios to consider :
- buffersrc : activate if frame_wanted_out to notify the application ;
activate when the application adds a frame to push it immediately .
- testsrc : activate only if frame_wanted_out to produce and push a frame .
- concat ( not at stitch points ) : can process a frame on any output .
Activate if frame_wanted_out on output to forward on the corresponding
input . Activate when a frame is present on input to process it
immediately .
- framesync : needs at least one frame on each input ; extra frames on the
wrong input will accumulate . When a frame is first added on one input ,
set frame_wanted_out < 0 on it to avoid getting more ( would trigger
testsrc ) and frame_wanted_out > 0 on the other to allow processing it .
Activation of old filters :
In order to activate a filter implementing the legacy filter_frame ( ) and
request_frame ( ) methods , perform the first possible of the following
actions :
- If an input has frames in fifo and frame_wanted_out = = 0 , dequeue a
frame and call filter_frame ( ) .
Ratinale : filter frames as soon as possible instead of leaving them
queued ; frame_wanted_out < 0 is not possible since the old API does not
set it nor provides any similar feedback ; frame_wanted_out > 0 happens
when min_samples > 0 and there are not enough samples queued .
- If an input has status_in set but not status_out , try to call
request_frame ( ) on one of the outputs in the hope that it will trigger
request_frame ( ) on the input with status_in and acknowledge it . This is
awkward and fragile , filters with several inputs or outputs should be
updated to direct activation as soon as possible .
- If an output has frame_wanted_out > 0 and not frame_blocked_in , call
request_frame ( ) .
Rationale : checking frame_blocked_in is necessary to avoid requesting
repeatedly on a blocked input if another is not blocked ( example :
[ buffersrc1 ] [ testsrc1 ] [ buffersrc2 ] [ testsrc2 ] concat = v = 2 ) .
TODO : respect needs_fifo and remove auto - inserted fifos .
*/
int ff_filter_activate ( AVFilterContext * filter )
{
int ret ;
filter - > ready = 0 ;
ret = ff_filter_activate_default ( filter ) ;
if ( ret = = FFERROR_NOT_READY )
ret = 0 ;
return ret ;
}
const AVClass * avfilter_get_class ( void )
{
return & avfilter_class ;