当前位置: 首页 > article >正文

VLC 播放的音视频数据处理流水线搭建

VLC 播放的音视频数据处理流水线搭建

    • 音视频流播放处理循环
    • 音频输出处理流水线

VLC 用 input_thread_t 对象直接或间接管理音视频播放有关的各种资源,包括 AccessDemuxDecodeOutputFilter 等,这个类型定义 (位于 vlc-3.0.16/include/vlc_input.h) 如下:

struct input_thread_t
{
    VLC_COMMON_MEMBERS
};

input_thread_t 是个抽象类型,VLC 中这个类型的具体实现为 input_thread_private_t,后者定义 (位于 vlc-3.0.16/src/input/input_internal.h) 如下:

#define INPUT_CONTROL_FIFO_SIZE    100

/* input_source_t: gathers all information per input source */
typedef struct
{
    VLC_COMMON_MEMBERS

    demux_t  *p_demux; /**< Demux object (most downstream) */

    /* Title infos for that input */
    bool         b_title_demux; /* Titles/Seekpoints provided by demux */
    int          i_title;
    input_title_t **title;

    int i_title_offset;
    int i_seekpoint_offset;

    int i_title_start;
    int i_title_end;
    int i_seekpoint_start;
    int i_seekpoint_end;

    /* Properties */
    bool b_can_pause;
    bool b_can_pace_control;
    bool b_can_rate_control;
    bool b_can_stream_record;
    bool b_rescale_ts;
    double f_fps;

    /* */
    int64_t i_pts_delay;

    bool       b_eof;   /* eof of demuxer */

} input_source_t;

typedef struct
{
    int         i_type;
    vlc_value_t val;
} input_control_t;

/** Private input fields */
typedef struct input_thread_private_t
{
    struct input_thread_t input;

    /* Global properties */
    bool        b_preparsing;
    bool        b_can_pause;
    bool        b_can_rate_control;
    bool        b_can_pace_control;

    /* Current state */
    int         i_state;
    bool        is_running;
    bool        is_stopped;
    bool        b_recording;
    int         i_rate;

    /* Playtime configuration and state */
    int64_t     i_start;    /* :start-time,0 by default */
    int64_t     i_stop;     /* :stop-time, 0 if none */
    int64_t     i_time;     /* Current time */
    bool        b_fast_seek;/* :input-fast-seek */

    /* Output */
    bool            b_out_pace_control; /* XXX Move it ot es_sout ? */
    sout_instance_t *p_sout;            /* Idem ? */
    es_out_t        *p_es_out;
    es_out_t        *p_es_out_display;
    vlc_viewpoint_t viewpoint;
    bool            viewpoint_changed;
    vlc_renderer_item_t *p_renderer;

    /* Title infos FIXME multi-input (not easy) ? */
    int          i_title;
    const input_title_t **title;

    int i_title_offset;
    int i_seekpoint_offset;

    /* User bookmarks FIXME won't be easy with multiples input */
    seekpoint_t bookmark;
    int         i_bookmark;
    seekpoint_t **pp_bookmark;

    /* Input attachment */
    int i_attachment;
    input_attachment_t **attachment;
    const demux_t **attachment_demux;

    /* Main input properties */

    /* Input item */
    input_item_t   *p_item;

    /* Main source */
    input_source_t *master;
    /* Slave sources (subs, and others) */
    int            i_slave;
    input_source_t **slave;

    /* Resources */
    input_resource_t *p_resource;
    input_resource_t *p_resource_private;

    /* Stats counters */
    struct {
        counter_t *p_read_packets;
        counter_t *p_read_bytes;
        counter_t *p_input_bitrate;
        counter_t *p_demux_read;
        counter_t *p_demux_bitrate;
        counter_t *p_demux_corrupted;
        counter_t *p_demux_discontinuity;
        counter_t *p_decoded_audio;
        counter_t *p_decoded_video;
        counter_t *p_decoded_sub;
        counter_t *p_sout_sent_packets;
        counter_t *p_sout_sent_bytes;
        counter_t *p_sout_send_bitrate;
        counter_t *p_played_abuffers;
        counter_t *p_lost_abuffers;
        counter_t *p_displayed_pictures;
        counter_t *p_lost_pictures;
        vlc_mutex_t counters_lock;
    } counters;

    /* Buffer of pending actions */
    vlc_mutex_t lock_control;
    vlc_cond_t  wait_control;
    int i_control;
    input_control_t control[INPUT_CONTROL_FIFO_SIZE];

    vlc_thread_t thread;
    vlc_interrupt_t interrupt;
} input_thread_private_t;

播放 VLC 播放列表中的一个音视频流的时候,音视频流的播放通过 PlayItem() 函数起动,这个函数被调用的调用栈如下:

Thread 3 "vlc" hit Breakpoint 14, PlayItem (p_item=<optimized out>, p_playlist=0xaaaaaab37980) at playlist/thread.c:227
227	        if( input_Start( p_input_thread ) )
(gdb) bt
#0  PlayItem (p_item=<optimized out>, p_playlist=0xaaaaaab37980) at playlist/thread.c:227
#1  Next (p_playlist=0xaaaaaab37980) at playlist/thread.c:474
#2  Thread (data=0xaaaaaab37980) at playlist/thread.c:499
#3  0x0000fffff7e4d5c8 in start_thread (arg=0x0) at ./nptl/pthread_create.c:442

PlayItem() 函数定义 (位于 vlc-3.0.16/src/playlist/thread.c) 如下:

static bool PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
{
    playlist_private_t *p_sys = pl_priv(p_playlist);
    input_item_t *p_input = p_item->p_input;
    vlc_renderer_item_t *p_renderer;

    PL_ASSERT_LOCKED;

    msg_Dbg( p_playlist, "creating new input thread" );

    p_item->i_nb_played++;
    set_current_status_item( p_playlist, p_item );
    p_renderer = p_sys->p_renderer;
    /* Retain the renderer now to avoid it to be released by
     * playlist_SetRenderer when we exit the locked scope. If the last reference
     * was to be released, we would use a dangling pointer */
    if( p_renderer )
        vlc_renderer_item_hold( p_renderer );
    assert( p_sys->p_input == NULL );
    PL_UNLOCK;

    libvlc_MetadataCancel( p_playlist->obj.libvlc, p_item );

    input_thread_t *p_input_thread = input_Create( p_playlist, p_input, NULL,
                                                   p_sys->p_input_resource,
                                                   p_renderer );
    if( p_renderer )
        vlc_renderer_item_release( p_renderer );
    if( likely(p_input_thread != NULL) )
    {
        var_AddCallback( p_input_thread, "intf-event",
                         InputEvent, p_playlist );

        if( input_Start( p_input_thread ) )
        {
            var_DelCallback( p_input_thread, "intf-event",
                             InputEvent, p_playlist );
            vlc_object_release( p_input_thread );
            p_input_thread = NULL;
        }
    }

    /* TODO store art policy in playlist private data */
    char *psz_arturl = input_item_GetArtURL( p_input );
    /* p_input->p_meta should not be null after a successful CreateThread */
    bool b_has_art = !EMPTY_STR( psz_arturl );

    if( !b_has_art || strncmp( psz_arturl, "attachment://", 13 ) )
    {
        PL_DEBUG( "requesting art for new input thread" );
        libvlc_ArtRequest( p_playlist->obj.libvlc, p_input, META_REQUEST_OPTION_NONE );
    }
    free( psz_arturl );

    PL_LOCK;
    p_sys->p_input = p_input_thread;
    PL_UNLOCK;

    var_SetAddress( p_playlist, "input-current", p_input_thread );

    PL_LOCK;
    return p_input_thread != NULL;
}

PlayItem() 函数接收 playlist_item_t 类型的 p_item 参数,这个类型定义 (位于 vlc-3.0.16/include/vlc_playlist.h) 如下:

/** playlist item / node */
struct playlist_item_t
{
    input_item_t           *p_input;    /**< Linked input item */

    playlist_item_t      **pp_children; /**< Children nodes/items */
    playlist_item_t       *p_parent;    /**< Item parent */
    int                    i_children;  /**< Number of children, -1 if not a node */
    unsigned               i_nb_played; /**< Times played */

    int                    i_id;        /**< Playlist item specific id */
    uint8_t                i_flags;     /**< Flags \see playlist_item_flags_e */
};

playlist_item_t 类型包含一个类型为 input_item_t 的字段 p_input,其中包含要播放的音视频流的各种信息,如 URI,名称,基础流的个数及格式等,input_item_t 类型的详细定义 (位于 vlc-3.0.16/include/vlc_input_item.h) 如下:

struct info_t
{
    char *psz_name;            /**< Name of this info */
    char *psz_value;           /**< Value of the info */
};

struct info_category_t
{
    char   *psz_name;      /**< Name of this category */
    int    i_infos;        /**< Number of infos in the category */
    struct info_t **pp_infos;     /**< Pointer to an array of infos */
};

/**
 * Describes an input and is used to spawn input_thread_t objects.
 */
struct input_item_t
{
    char       *psz_name;            /**< text describing this item */
    char       *psz_uri;             /**< mrl of this item */

    int        i_options;            /**< Number of input options */
    char       **ppsz_options;       /**< Array of input options */
    uint8_t    *optflagv;            /**< Some flags of input options */
    unsigned   optflagc;
    input_item_opaque_t *opaques;    /**< List of opaque pointer values */

    mtime_t    i_duration;           /**< Duration in microseconds */


    int        i_categories;         /**< Number of info categories */
    info_category_t **pp_categories; /**< Pointer to the first info category */

    int         i_es;                /**< Number of es format descriptions */
    es_format_t **es;                /**< Es formats */

    input_stats_t *p_stats;          /**< Statistics */

    vlc_meta_t *p_meta;

    int         i_epg;               /**< Number of EPG entries */
    vlc_epg_t   **pp_epg;            /**< EPG entries */
    int64_t     i_epg_time;          /** EPG timedate as epoch time */
    const vlc_epg_t *p_epg_table;    /** running/selected program cur/next EPG table */

    int         i_slaves;            /**< Number of slaves */
    input_item_slave_t **pp_slaves;  /**< Slave entries that will be loaded by
                                          the input_thread */

    vlc_event_manager_t event_manager;

    vlc_mutex_t lock;                 /**< Lock for the item */

    uint8_t     i_type;              /**< Type (file, disc, ... see input_item_type_e) */
    bool        b_net;               /**< Net: always true for TYPE_STREAM, it
                                          depends for others types */
    bool        b_error_when_reading;/**< Error When Reading */

    int         i_preparse_depth;    /**< How many level of sub items can be preparsed:
                                          -1: recursive, 0: none, >0: n levels */

    bool        b_preparse_interact; /**< Force interaction with the user when
                                          preparsing.*/
};

这里的几个类型的结构关系大概如下图所示:
VLC Objects
PlayItem() 函数主要是调用 input_Create() 函数创建 input_thread_t/input_thread_private_t 对象,并调用 input_Start() 函数启动对音视频流的处理。

input_Create()input_Start() 函数函数定义 (位于 vlc-3.0.16/src/input/input.c) 如下:

input_thread_t *input_Create( vlc_object_t *p_parent,
                              input_item_t *p_item,
                              const char *psz_log, input_resource_t *p_resource,
                              vlc_renderer_item_t *p_renderer )
{
    return Create( p_parent, p_item, psz_log, false, p_resource, p_renderer );
}
 . . . . . .
/**
 * Start a input_thread_t created by input_Create.
 *
 * You must not start an already running input_thread_t.
 *
 * \param the input thread to start
 */
int input_Start( input_thread_t *p_input )
{
    input_thread_private_t *priv = input_priv(p_input);
    void *(*func)(void *) = Run;

    if( priv->b_preparsing )
        func = Preparse;

    assert( !priv->is_running );
    /* Create thread and wait for its readiness. */
    priv->is_running = !vlc_clone( &priv->thread, func, priv,
                                   VLC_THREAD_PRIORITY_INPUT );
    if( !priv->is_running )
    {
        input_ChangeState( p_input, ERROR_S );
        msg_Err( p_input, "cannot create input thread" );
        return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}
 . . . . . .
 static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
                               const char *psz_header, bool b_preparsing,
                               input_resource_t *p_resource,
                               vlc_renderer_item_t *p_renderer )
{
    /* Allocate descriptor */
    input_thread_private_t *priv;

    priv = vlc_custom_create( p_parent, sizeof( *priv ), "input" );
    if( unlikely(priv == NULL) )
        return NULL;

    input_thread_t *p_input = &priv->input;
 . . . . . .
 }
static void *Run( void *data )
{
    input_thread_private_t *priv = data;
    input_thread_t *p_input = &priv->input;

    vlc_interrupt_set(&priv->interrupt);

    if( !Init( p_input ) )
    {
        if( priv->b_can_pace_control && priv->b_out_pace_control )
        {
            /* We don't want a high input priority here or we'll
             * end-up sucking up all the CPU time */
            vlc_set_priority( priv->thread, VLC_THREAD_PRIORITY_LOW );
        }

        MainLoop( p_input, true ); /* FIXME it can be wrong (like with VLM) */

        /* Clean up */
        End( p_input );
    }

    input_SendEventDead( p_input );
    return NULL;
}

除了 b_preparsing 参数传 false 外,input_Create() 函数将接收的各个参数传给 Create(),通过后者创建 input_thread_private_t 对象。input_Start() 函数启动一个新的线程处理音视频流,默认情况下,新起的线程跑 Run() 函数,但如果设置了 b_preparsing,新起的线程跑 Preparse() 函数。

播放列表中各个音频流的信息,也通过 input_thread_t 获取。当我们往 VLC 的播放列表拖入一个文件时,UI 事件向播放列表添加一个输入项,输入项最终被放入播放列表预解析器的待解析队列中,这个调用过程如下面的调用栈所示:

#0  background_worker_Push (worker=0xaaaaaaae2e40, entity=entity@entry=0xffffc885fec0, id=id@entry=0xffffc87f5ac0, timeout=timeout@entry=-1) at misc/background_worker.c:211
#1  0x0000fffff7ced04c in playlist_preparser_Push (preparser=0xaaaaaaadbe00, item=item@entry=0xffffc885fec0, i_options=META_REQUEST_OPTION_NONE, timeout=-1, id=0xffffc87f5ac0)
    at playlist/preparser.c:178
#2  0x0000fffff7cca748 in vlc_MetadataRequest
    (libvlc=0xaaaaaaab58e0, item=item@entry=0xffffc885fec0, i_options=i_options@entry=META_REQUEST_OPTION_NONE, timeout=timeout@entry=-1, id=id@entry=0xffffc87f5ac0) at libvlc.c:504
#3  0x0000fffff7ceee5c in playlist_Preparse (p_item=0xffffc87f5ac0, p_playlist=0xaaaaaab37980) at playlist/item.c:750
#4  playlist_NodeAddInput (p_playlist=p_playlist@entry=0xaaaaaab37980, p_input=p_input@entry=0xffffc885fec0, p_parent=<optimized out>, i_pos=i_pos@entry=-1) at playlist/item.c:541
#5  0x0000fffff7ceef20 in playlist_AddInput (p_playlist=p_playlist@entry=0xaaaaaab37980, p_input=p_input@entry=0xffffc885fec0, play_now=play_now@entry=false, b_playlist=b_playlist@entry=true)
    at playlist/item.c:504
#6  0x0000fffff7ceefe4 in playlist_AddExt
    (p_playlist=0xaaaaaab37980, psz_uri=<optimized out>, psz_name=<optimized out>, play_now=false, i_options=0, ppsz_options=0x0, i_option_flags=2, b_playlist=true) at playlist/item.c:483
#7  0x0000ffffe51d4440 in Open::openMRLwithOptions(intf_thread_t*, QString const&, QStringList*, bool, bool, char const*)
    (p_intf=0xaaaaaad8e8d0, mrl=..., options=<optimized out>, b_start=false, b_playlist=true, title=0x0) at /usr/include/aarch64-linux-gnu/qt5/QtCore/qarraydata.h:61
#8  0x0000ffffe51ba768 in MainInterface::dropEventPlay(QDropEvent*, bool, bool) (this=0xffffc81475f0, event=0xffffe319d020, b_play=<optimized out>, b_playlist=true)
    at gui/qt/main_interface.cpp:1580
#9  0x0000ffffe4b96378 in QWidget::event(QEvent*) () at /usr/lib/aarch64-linux-gnu/libQt5Widgets.so.5
#10 0x0000ffffe4b52ac0 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () at /usr/lib/aarch64-linux-gnu/libQt5Widgets.so.5
#11 0x0000ffffe4b5a2e0 in QApplication::notify(QObject*, QEvent*) () at /usr/lib/aarch64-linux-gnu/libQt5Widgets.so.5
#12 0x0000ffffe40acb90 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () at /usr/lib/aarch64-linux-gnu/libQt5Core.so.5

其中 input_item_t 对象在 playlist_AddExt() 函数中创建,它所属的 playlist_item_t 对象则在 playlist_AddInput() 函数中通过 playlist_NodeAddInput() 创建。VLC 起一个后台线程来预解析音视频流,VLC 调用 PreparserOpenInput() 函数预解析音视频流,这个函数被调用的调用栈如下:

Thread 32 "vlc" hit Breakpoint 7, PreparserOpenInput (preparser_=0xaaaaaaadbe00, item_=0xffffc8730830, out=0xffffb021e790) at playlist/preparser.c:55
55	    playlist_preparser_t* preparser = preparser_;
(gdb) bt
#0  PreparserOpenInput (preparser_=0xaaaaaaadbe00, item_=0xffffc8730830, out=0xffffb021e790) at playlist/preparser.c:55
#1  0x0000fffff7d43c1c in Thread (data=0xaaaaaaae2e40) at misc/background_worker.c:115
#2  0x0000fffff7e4d5c8 in start_thread (arg=0x0) at ./nptl/pthread_create.c:442

PreparserOpenInput() 函数定义 (位于 vlc-3.0.16/src/playlist/preparser.c) 如下:

static int PreparserOpenInput( void* preparser_, void* item_, void** out )
{
    playlist_preparser_t* preparser = preparser_;

    input_thread_t* input = input_CreatePreparser( preparser->owner, item_ );
    if( !input )
    {
        input_item_SignalPreparseEnded( item_, ITEM_PREPARSE_FAILED );
        return VLC_EGENERIC;
    }

    var_AddCallback( input, "intf-event", InputEvent, preparser->worker );
    if( input_Start( input ) )
    {
        var_DelCallback( input, "intf-event", InputEvent, preparser->worker );
        input_Close( input );
        input_item_SignalPreparseEnded( item_, ITEM_PREPARSE_FAILED );
        return VLC_EGENERIC;
    }

    *out = input;
    return VLC_SUCCESS;
}

与前面看到的 PlayItem() 函数有些类似,但它调用 input_CreatePreparser() 函数创建 input_thread_private_t 对象,同时由于配置了 b_preparsinginput_Start() 函数起的线程将运行 Preparse() 函数,且创建的 input_thread_private_t 对象被配置了 OBJECT_FLAGS_QUIET | OBJECT_FLAGS_NOINTERACT,这意味着无法通过 VLC 的日志机制为这个对象输出日志。input_CreatePreparser()Preparse() 函数定义 (位于 vlc-3.0.16/src/input/input.c) 如下:

static void *Preparse( void *data )
{
    input_thread_private_t *priv = data;
    input_thread_t *p_input = &priv->input;

    vlc_interrupt_set(&priv->interrupt);

    if( !Init( p_input ) )
    {   /* if the demux is a playlist, call Mainloop that will call
         * demux_Demux in order to fetch sub items */
        bool b_is_playlist = false;

        if ( input_item_ShouldPreparseSubItems( priv->p_item )
          && demux_Control( priv->master->p_demux, DEMUX_IS_PLAYLIST,
                            &b_is_playlist ) )
            b_is_playlist = false;
        if( b_is_playlist )
            MainLoop( p_input, false );
        End( p_input );
    }

    input_SendEventDead( p_input );
    return NULL;
}
 . . . . . .
input_thread_t *input_CreatePreparser( vlc_object_t *parent,
                                       input_item_t *item )
{
    return Create( parent, item, NULL, true, NULL, NULL );
}

input_CreatePreparser() 函数也是 Create() 函数的包装,只是它的 b_preparsing 参数传的是 truePreparse() 函数与 Run() 函数类似,只是它不运行音视频流的 Demux 循环。预解析处理有超时限制,VLC 并不总是会等预解析完成。预解析成功完成时,可以在 VLC 的播放列表中看到通过 demuxer 获得的音视频流的时长等信息,没能成功完成时,则只能看到文件名作为标题。

音视频流播放处理循环

VLC 中,input_Start() 函数启动一个新的线程跑 Run() 函数来播放音视频流,这个函数调用 Init() 函数初始化音视频流播放处理过程,初始化前面通过 input_Create() 函数创建的 input_thread_private_t 对象,随后调用 MainLoop() 函数跑一个处理循环,最后调用 End() 函数结束处理。

音视频流播放处理过程初始化,需要完成许多事情,如为要播放的音视频流创建 access 以便于获取数据,创建 demuxer,创建 decoder 等。具体来看 Init() 函数的定义 (位于 vlc-3.0.16/src/input/input.c) 如下:

static int Init( input_thread_t * p_input )
{
    input_thread_private_t *priv = input_priv(p_input);
    input_source_t *master;

    if( var_Type( p_input->obj.parent, "meta-file" ) )
    {
        msg_Dbg( p_input, "Input is a meta file: disabling unneeded options" );
        var_SetString( p_input, "sout", "" );
        var_SetBool( p_input, "sout-all", false );
        var_SetString( p_input, "input-slave", "" );
        var_SetInteger( p_input, "input-repeat", 0 );
        var_SetString( p_input, "sub-file", "" );
        var_SetBool( p_input, "sub-autodetect-file", false );
    }

    InitStatistics( p_input );
#ifdef ENABLE_SOUT
    if( InitSout( p_input ) )
        goto error;
#endif

    /* Create es out */
    priv->p_es_out = input_EsOutTimeshiftNew( p_input, priv->p_es_out_display,
                                              priv->i_rate );
    if( priv->p_es_out == NULL )
        goto error;

    /* */
    input_ChangeState( p_input, OPENING_S );
    input_SendEventCache( p_input, 0.0 );

    /* */
    master = InputSourceNew( p_input, priv->p_item->psz_uri, NULL, false );
    if( master == NULL )
        goto error;
    priv->master = master;

    InitTitle( p_input );

    /* Load master infos */
    /* Init length */
    mtime_t i_length;
    if( demux_Control( master->p_demux, DEMUX_GET_LENGTH, &i_length ) )
        i_length = 0;
    if( i_length <= 0 )
        i_length = input_item_GetDuration( priv->p_item );
    input_SendEventLength( p_input, i_length );

    input_SendEventPosition( p_input, 0.0, 0 );

    if( !priv->b_preparsing )
    {
        StartTitle( p_input );
        SetSubtitlesOptions( p_input );
        LoadSlaves( p_input );
        InitPrograms( p_input );

        double f_rate = var_InheritFloat( p_input, "rate" );
        if( f_rate != 0.0 && f_rate != 1.0 )
        {
            vlc_value_t val = { .i_int = INPUT_RATE_DEFAULT / f_rate };
            input_ControlPush( p_input, INPUT_CONTROL_SET_RATE, &val );
        }
    }

    if( !priv->b_preparsing && priv->p_sout )
    {
        priv->b_out_pace_control = priv->p_sout->i_out_pace_nocontrol > 0;

        msg_Dbg( p_input, "starting in %ssync mode",
                 priv->b_out_pace_control ? "a" : "" );
    }

    vlc_meta_t *p_meta = vlc_meta_New();
    if( p_meta != NULL )
    {
        /* Get meta data from users */
        InputMetaUser( p_input, p_meta );

        /* Get meta data from master input */
        InputSourceMeta( p_input, master, p_meta );

        /* And from slave */
        for( int i = 0; i < priv->i_slave; i++ )
            InputSourceMeta( p_input, priv->slave[i], p_meta );

        es_out_ControlSetMeta( priv->p_es_out, p_meta );
        vlc_meta_Delete( p_meta );
    }

    msg_Dbg( p_input, "`%s' successfully opened",
             input_priv(p_input)->p_item->psz_uri );

    /* initialization is complete */
    input_ChangeState( p_input, PLAYING_S );

    return VLC_SUCCESS;

error:
    input_ChangeState( p_input, ERROR_S );

    if( input_priv(p_input)->p_es_out )
        es_out_Delete( input_priv(p_input)->p_es_out );
    es_out_SetMode( input_priv(p_input)->p_es_out_display, ES_OUT_MODE_END );
    if( input_priv(p_input)->p_resource )
    {
        if( input_priv(p_input)->p_sout )
            input_resource_RequestSout( input_priv(p_input)->p_resource,
                                         input_priv(p_input)->p_sout, NULL );
        input_resource_SetInput( input_priv(p_input)->p_resource, NULL );
        if( input_priv(p_input)->p_resource_private )
            input_resource_Terminate( input_priv(p_input)->p_resource_private );
    }

    if( !priv->b_preparsing && libvlc_stats( p_input ) )
    {
#define EXIT_COUNTER( c ) do { if( input_priv(p_input)->counters.p_##c ) \
                                   stats_CounterClean( input_priv(p_input)->counters.p_##c );\
                               input_priv(p_input)->counters.p_##c = NULL; } while(0)
        EXIT_COUNTER( read_bytes );
        EXIT_COUNTER( read_packets );
        EXIT_COUNTER( demux_read );
        EXIT_COUNTER( input_bitrate );
        EXIT_COUNTER( demux_bitrate );
        EXIT_COUNTER( demux_corrupted );
        EXIT_COUNTER( demux_discontinuity );
        EXIT_COUNTER( played_abuffers );
        EXIT_COUNTER( lost_abuffers );
        EXIT_COUNTER( displayed_pictures );
        EXIT_COUNTER( lost_pictures );
        EXIT_COUNTER( decoded_audio );
        EXIT_COUNTER( decoded_video );
        EXIT_COUNTER( decoded_sub );

        if( input_priv(p_input)->p_sout )
        {
            EXIT_COUNTER( sout_sent_packets );
            EXIT_COUNTER( sout_sent_bytes );
            EXIT_COUNTER( sout_send_bitrate );
        }
#undef EXIT_COUNTER
    }

    /* Mark them deleted */
    input_priv(p_input)->p_es_out = NULL;
    input_priv(p_input)->p_sout = NULL;

    return VLC_EGENERIC;
}

Init() 函数做的主要的事情如下:

  1. 初始化统计计数器。
  2. 创建 es outesElementary streams,即基本流。后面更详细地对 es 作解释。
  3. 调用 InputSourceNew() 函数创建 input source。VLC 的 input source 指解码之前,对音视频流数据做处理的组件,这包括 access 组件,access 组件后面执行缓存策略的 stream_filter 组件,再后面自动的 stream_filter 组件,再后面更上层指定的其它 stream_filter 组件,再后面的 demux 组件。或者对于类型的流,可以将 demux 和数据获取合并的情况,为 access_demux 组件。demux 组件或 access_demux 组件后面的是若干个更上层指定的 demux_filter 组件。所有这些由 input_source_t 对象描述,它的 demux_t 类型的字段

http://www.kler.cn/a/419918.html

相关文章:

  • MySql:理解数据库
  • 使用ESP32通过Arduino IDE点亮1.8寸TFT显示屏
  • PHP RabbitMQ连接超时问题
  • 计算机科学中的主要协议
  • 文件比较和文件流
  • Ubuntu操作
  • vim插件管理器vim-plug替代vim-bundle
  • 腾讯rapidJson使用例子
  • 我与Linux的爱恋:共享内存
  • 【新人系列】Python 入门(十五):异常类型
  • Java 8 Stream API 入门教程:轻松使用 map、filter 和 collect 进行数据处理
  • PyCharm中Python项目打包并运行到服务器的简明指南
  • SpringBoot3 + Vue3 由浅入深的交互 基础交互教学2
  • 数据库管理-第267期 23ai:Oracle Data Redaction演示(20241128)
  • CSS学习记录03
  • NLP 的发展历程
  • 洛谷 P1308 [NOIP2011 普及组] 统计单词数 C语言
  • 对于大规模的淘宝API接口数据,有什么高效的处理方法?
  • 2. langgraph中的Tool Calling (How to handle tool calling errors)
  • AI在SEO中的应用与关键词优化探讨
  • 011变长子网掩码
  • React 前端框架1
  • <<WTF-Solidity>>学习笔记(part 25-28)
  • 物联网——WatchDog(监听器)
  • 关于正点原子STM32F404探索者V3ZGT6 RS232串口的使用
  • navicat连接mysql 8.0以上版本2059错误