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_MEMBERSdemux_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 bythe 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, itdepends 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 whenpreparsing.*/
};

这里的几个类型的结构关系大概如下图所示:
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_SOUTif( 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.rhkb.cn/news/482900.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浅谈edusrc挖掘技巧+信息收集新姿势

目录 1 前言 2 信息收集资产收集 2.1域名查询 2.2邮箱查询 2.3 ICP备案信息查询 3 综合资产查询姿势 3.1 FOFA鹰图 3.2企查查/小蓝本 3.3 黑客语法&#xff08;Google必应&#xff09; 4 统一身份认证登录绕过 4.1逻辑缺陷绕过 4.2爆破账户/前端绕过验证 5 纯手工信…

Ubuntu Linux操作系统

一、 安装和搭建 Thank you for downloading Ubuntu Desktop | Ubuntu &#xff08;这里我们只提供一个下载地址&#xff0c;详细的下载安装可以参考其他博客&#xff09; 二、ubuntu的用户使用 2.1 常规用户登陆方式 在系统root用户是无法直接登录的,因为root用户的权限过…

RDIFramework.NET CS敏捷开发框架 SOA服务三种访问(直连、WCF、WebAPI)方式

1、介绍 在软件开发领域&#xff0c;尤其是企业级应用开发中&#xff0c;灵活性、开放性、可扩展性往往是项目成功的关键因素。对于C/S项目&#xff0c;如何高效地与后端数据库进行交互&#xff0c;以及如何提供多样化的服务访问方式&#xff0c;是开发者需要深入考虑的问题。…

ProtoBuf快速上手(C++)

在快速上⼿中&#xff0c;会编写第⼀版本的通讯录 1.0。在通讯录 1.0 版本中&#xff0c;将实现&#xff1a; • 对⼀个联系⼈的信息使⽤ PB 进⾏序列化&#xff0c;并将结果打印出来。 • 对序列化后的内容使⽤ PB 进⾏反序列&#xff0c;解析出联系⼈信息并打印出来。 •…

PHP 方头像转为圆图

业务需要把创建海报上的用户头像由方形转为圆形&#xff0c;前端的样式设置不能用。 故采用GD的函数来对方图进行裁剪处理为圆图。 目录 裁剪函数 本地图片 远程图片 效果 参考文章 总结 裁剪函数 从网上找的一个裁剪图片的函数。 代码如下&#xff1a; /* * 将图片切…

代理IP地址的含义与设置指南‌

在数字化时代&#xff0c;互联网已经成为我们日常生活不可或缺的一部分。然而&#xff0c;在享受互联网带来的便利的同时&#xff0c;我们也面临着隐私泄露、访问限制等问题。代理IP地址作为一种有效的网络工具&#xff0c;能够帮助我们解决这些问题。本文将详细介绍代理IP地址…

基于Java Springboot个人财务APP且微信小程序

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 微信…

红队/白帽必经之路(16)——如何用Metasploit 在边路进行信息刺探及爆破登录[既然是红队,那就对自己狠一点!!!]

欢迎各位彦祖与热巴畅游本人专栏与博客 你的三连是我最大的动力 以下图片仅代表专栏特色 [点击箭头指向的专栏名即可闪现] 专栏跑道一 ➡️网络空间安全——全栈前沿技术持续深入学习 专栏跑道二 ➡️ 24 Network Security -LJS ​ ​ ​ 专栏跑道三 ➡️ MYSQL REDIS Advan…

vue实现echarts饼图自动轮播

echarts官网&#xff1a;Examples - Apache ECharts echartsFn.ts 把echarts函数封装成一个文件 import * as echarts from "echarts";const seriesData [{"value": 12,"name": "过流报警"},{"value": 102,"name&qu…

C++之异常智能指针其他

C之异常&智能指针&其他 异常关于函数异常声明异常的优劣 智能指针auto_ptrunique_ptrshared_ptrweak_ptr定制删除器 智能指针的历史与boost库 特殊类单例模式饿汉和懒汉的优缺点 C四种类型转换CIO流结语 异常 try括起来的的代码块中可能有throw一个异常&#xff08;可…

混沌工程/混沌测试/云原生测试/云平台测试

背景 私有云/公有云/混合云等具有复杂&#xff0c;分布式&#xff0c;环境多样性等特点&#xff0c;许多特殊场景引发的线上问题很难被有效发现。所以需要引入混沌工程&#xff0c;建立对系统抵御生产环境中失控条件的能力以及信心&#xff0c;提高系统面对未知风险得能力。 …

Hive学习基本概念

基本概念 hive是什么&#xff1f; Facebook 开源&#xff0c;用于解决海量结构化日志的数据统计。 基于Hadoop的一个数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张表&#xff0c;并提供类SQL查询功能 本质是将HQL转化为MapReduce程序。 Hive处理的数据存储在H…

数据分析流程中的Lambda架构,以及数据湖基于Hadoop、Spark的实现

文章目录 一、Lambda架构1、Lambda的三层架构2、简单解释&#xff1a;3、Lambda架构的优缺点 二、数据湖基于Hadoop、Spark的实现1、架构2、数据管理&#xff08;存储层的辅助功能&#xff09; 一、Lambda架构 1、Lambda的三层架构 Batch View&#xff08;批处理视图层&#…

算法笔记:力扣142.环形链表返回链表入口

该题目通俗来说就是需要返回节点的入口&#xff0c;这点与判断是否有环不同&#xff0c;有环是通过快慢指针的形式来判断&#xff0c;但当快慢指针相等的时候&#xff0c;此时的节点不一定是环的入口节点。所以这题需要注意。 关键API&#xff1a; map.putIfAbsent(key,value)…

医院管理系统

私信我获取源码和万字论文&#xff0c;制作不易&#xff0c;感谢点赞支持。 医院管理系统 摘要 随着信息互联网信息的飞速发展&#xff0c;医院也在创建着属于自己的管理系统。本文介绍了医院管理系统的开发全过程。通过分析企业对于医院管理系统的需求&#xff0c;创建了一个计…

说说Elasticsearch查询语句如何提升权重?

大家好&#xff0c;我是锋哥。今天分享关于【说说Elasticsearch查询语句如何提升权重&#xff1f;】面试题。希望对大家有帮助&#xff1b; 说说Elasticsearch查询语句如何提升权重&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 Elasticsearch 中&…

【Spring Security框架解析】

文章目录 Spring-security介绍Spring-security认证授权流程认证流程Security流程认证过滤器实现获取UserDetail信息 配置Security Spring-security介绍 Spring Security是一个功能强大且高度可定制的Java安全框架&#xff0c;用于保护基于Spring的应用程序。它提供了全面的安全…

[CISCN 2019华东南]Web11

[CISCN 2019华东南]Web11 给了两个链接但是都无法访问 这里我们直接抓包试一下 我们插入X-Forwarded-For:127.0.0.1 发现可以修改了右上角的IP地址&#xff0c;从而可以进行注入 {$smarty.version} 查看版本号 if标签执行PHP命令 {if phpinfo()}{/if} 查看协议 {if system(…

使用SpringBoot实现邮件发送(QQ邮箱为例)

使用SpringBoot实现邮件发送(QQ邮箱为例) 一、获取授权码 1.首先进入qq邮箱找到设置 2、账号栏目&#xff0c;找到POP3/SMTP服务 并开启服务 3、获取授权码 二、SpringBoot集成邮件发送 1.创建邮件发送服务类 package com.example.demo.service;import org.springframework…

hint: Updates were rejected because the tip of your current branch is behind!

问题 本地仓库往远段仓库推代码时候提示&#xff1a; error: failed to push some refs to 192.168.2.1:java-base/java-cloud.git hint: Updates were rejected because the tip of your current branch is behind! refs/heads/master:refs/heads/master [rejected] (…