1、VLC中有很多demux/mux/encoder/decoder模块,因此需要先了解这些模块的加载原理,模块的加载原理基本一致,因此举例分析MP4解复用模块如何加载完成的,主要流程如下:

// vlc中MP4解复用模块的实现代码位于【vlc/modules/demux/mp4/mp4.c】中
// 模块声明加载代码片段:
vlc_module_begin ()set_category( CAT_INPUT )set_subcategory( SUBCAT_INPUT_DEMUX )set_description( N_("MP4 stream demuxer") )set_shortname( N_("MP4") )set_capability( "demux", 240 )set_callbacks( Open, Close )add_category_hint("Hacks", NULL, true)add_bool( CFG_PREFIX"m4a-audioonly", false, MP4_M4A_TEXT, MP4_M4A_LONGTEXT, true )
vlc_module_end ()// 开始和结束两个方法是vlc的两个宏定义,且所有模块的声明加载信息均在这两个宏定义之间
// 其中【set_callbacks】参数为所有模块加载时必须声明的方法回调即打开和关闭该模块的功能
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );// 注:可以添加当前模块的子模块,如h26x的实现:h264+h265
vlc_module_begin ()set_shortname( "H264")set_category( CAT_INPUT )set_subcategory( SUBCAT_INPUT_DEMUX )set_description( N_("H264 video demuxer" ) )set_capability( "demux", 6 )set_section( N_("H264 video demuxer" ), NULL )add_float( "h264-fps", 0.0, FPS_TEXT, FPS_LONGTEXT, true )set_callbacks( OpenH264, Close )add_shortcut( "h264" )add_submodule()set_shortname( "HEVC")set_category( CAT_INPUT )set_subcategory( SUBCAT_INPUT_DEMUX )set_description( N_("HEVC/H.265 video demuxer" ) )set_capability( "demux", 6 )set_section( N_("HEVC/H.265 video demuxer" ), NULL )add_float( "hevc-fps", 0.0, FPS_TEXT, FPS_LONGTEXT, true )set_callbacks( OpenHEVC, Close )add_shortcut( "hevc", "h265" )vlc_module_end ()// set_callback宏定义如下:
#define set_callbacks( activate, deactivate ) \if (vlc_module_set(VLC_MODULE_CB_OPEN, #activate, (void *)(activate)) \|| vlc_module_set(VLC_MODULE_CB_CLOSE, #deactivate, \(void *)(deactivate))) \goto error;// 因此通过分析可知初始化模块信息后对应的两个字段如下结构体中两方法指针:void *pf_activatevoid *pf_deactivate
/*** Internal module descriptor*/
struct module_t
{vlc_plugin_t *plugin; /**< Plug-in/library containing the module */module_t   *next;/** Shortcuts to the module */unsigned    i_shortcuts;const char **pp_shortcuts;/** Variables set by the module to identify itself*/const char *psz_shortname;                              /**< Module name */const char *psz_longname;                   /**< Module descriptive name */const char *psz_help;        /**< Long help string for "special" modules */const char *psz_capability;                              /**< Capability */int      i_score;                          /**< Score for the capability *//* Callbacks */const char *activate_name;const char *deactivate_name;void *pf_activate;void *pf_deactivate;
};// 如pf_activate方法主要由【vlc/src/modules/modules.c】这里的两个方法中调用:
static int module_load (vlc_object_t *obj, module_t *m,vlc_activate_t init, va_list args)
// 或下面该方法中的调用
#undef module_start
int module_start (vlc_object_t *obj, const module_t *m)
{int (*activate) (vlc_object_t *) = m->pf_activate;return (activate != NULL) ? activate (obj) : VLC_SUCCESS;
}
// 或是该方法进行初始化
static int generic_start(void *func, va_list ap)
{vlc_object_t *obj = va_arg(ap, vlc_object_t *);int (*activate)(vlc_object_t *) = func;return activate(obj);
}// 通过代码调用链可知,这两方法会在此前讲过的vlc初始化过程等均有调用加载,后续会具体分析一下

2、接下来,交互途径主要是如下功能方法:

// 位于【vlc/include/vlc_demux.h】
static inline int demux_Control( demux_t *p_demux, int i_query, ... )
{va_list args;int     i_result;va_start( args, i_query );i_result = demux_vaControl( p_demux, i_query, args );va_end( args );return i_result;
}// 位于【vlc/src/input/demux.c】
int demux_vaControl( demux_t *demux, int query, va_list args )
{// 通过query查询动作枚举来控制解复用输出功能及与解复用端沟通获取数据等if( demux->s != NULL )switch( query ){/* Legacy fallback for missing getters in synchronous demuxers */case DEMUX_CAN_PAUSE:case DEMUX_CAN_CONTROL_PACE:case DEMUX_GET_PTS_DELAY:{int ret;va_list ap;va_copy( ap, args );ret = demux->pf_control( demux, query, args );if( ret != VLC_SUCCESS )ret = vlc_stream_vaControl( demux->s, query, ap );va_end( ap );return ret;}/* Some demuxers need to control pause directly (e.g. adaptive),* but many legacy demuxers do not understand pause at all.* If DEMUX_CAN_PAUSE is not implemented, bypass the demuxer and* byte stream. If DEMUX_CAN_PAUSE is implemented and pause is* supported, pause the demuxer normally. Else, something went very* wrong.** Note that this requires asynchronous/threaded demuxers to* always return VLC_SUCCESS for DEMUX_CAN_PAUSE, so that they are* never bypassed. Otherwise, we would reenter demux->s callbacks* and break thread safety. At the time of writing, asynchronous or* threaded *non-access* demuxers do not exist and are not fully* supported by the input thread, so this is theoretical. */case DEMUX_SET_PAUSE_STATE:{bool can_pause;if( demux_ControlInternal( demux, DEMUX_CAN_PAUSE,&can_pause ) )return vlc_stream_vaControl( demux->s, query, args );/* The caller shall not pause if pause is unsupported. */assert( can_pause );break;}}return demux->pf_control( demux, query, args );
}

2.1 举例说明:如java层若想获取媒体播放总时长,则该时长信息初始化在此前分析的Init方法中:

demux_Control( master->p_demux, DEMUX_GET_LENGTH, &i_length )

然后会执行demux模块的该方法去获取:

demux->pf_control( demux, query, args )

有上面第1部分分析可知:该方法调用到对应模块的该方法实现,如mp4模块中:

// 位于【vlc/modules/demux/mp4/mp4.c】的如下方法中赋值初始化的:
static int Open( vlc_object_t * p_this ){// ...省略其他代码p_demux->pf_control = Control;
}
// Control方法的实现:
static int Control( demux_t *p_demux, int i_query, va_list args )
{// ...省略其他代码demux_sys_t *p_sys = p_demux->p_sys;int64_t i64, *pi64;const uint64_t i_duration = __MAX(p_sys->i_duration, p_sys->i_cumulated_duration);switch( i_query ){case DEMUX_GET_LENGTH:pi64 = va_arg( args, int64_t * );if( p_sys->i_timescale > 0 ){// MP4中的视频时间缩放单位大于0,则计算其真正的视频时长*pi64 = MP4_rescale( i_duration,p_sys->i_timescale, CLOCK_FREQ );}else *pi64 = 0;return VLC_SUCCESS;         }
}static int64_t MP4_rescale( int64_t i_value, uint32_t i_timescale, uint32_t i_newscale )
{if( i_timescale == i_newscale )return i_value;if( i_value <= INT64_MAX / i_newscale )return i_value * i_newscale / i_timescale;/* overflow */int64_t q = i_value / i_timescale;int64_t r = i_value % i_timescale;return q * i_newscale + r * i_newscale / i_timescale;
}

由此其他操作命令同理也通过类似的这种方式进行的,参数传递的是可变参数。

3、媒体数据流模块加载媒体数据流结构体信息和解复用模块创建数据交互通道流程分析:

// 首先确定媒体数据流结构体定义如下:位于【vlc/include/vlc_stream.h】
/*** \defgroup stream Stream* \ingroup input* Buffered input byte streams* @{* \file* Byte streams and byte stream filter modules interface*//*** stream_t definition*/struct stream_t
{VLC_COMMON_MEMBERS/* Module properties for stream filter */module_t    *p_module;char        *psz_name;char        *psz_url; /**< Full URL or MRL (can be NULL) */const char  *psz_location; /**< Location (URL with the scheme stripped) */char        *psz_filepath; /**< Local file path (if applicable) */bool         b_preparsing; /**< True if this access is used to preparse *//* Stream source for stream filter */stream_t *p_source;ssize_t     (*pf_read)(stream_t *, void *buf, size_t len);block_t    *(*pf_block)(stream_t *, bool *eof);int         (*pf_control)(stream_t *, int i_query, va_list);/*** Private data pointer*/void *p_sys;// ...省略部分代码
};// 媒体流结构信息创建方法为:位于【vlc/src/input/access.c】
// 【注:该方法是在播放初始化流程Init方法调用链中调用的,
// 并将该返回值赋值给了demux_t结构中的s字段(stream_t *s)】
stream_t *stream_AccessNew(vlc_object_t *parent, input_thread_t *input,bool preparsing, const char *url)
{// 该方法实现为根据URL创建一个媒体原始流结构信息// 注意:该方法中有两个stream流结构体,// 但是第一个s结构代表播放器通用的流结构信息。// 第二个access结构才是代表我们真正数据流访问模块的功能提供者,// 如rtsp/http/file/tcp等方式进行的数据传递读取数据流,// 而如下实现中第二个access流结构被赋值到第一个结构体的【p_sys】指针中,// 从整个调用链实现分析可知:这个做法其实类似面向对象中的装饰者或代理类的实现方案,// 可以实现模块化方案的加载。// 具体如何代理功能的,下面功能中只举例分析该功能【s->pf_read = AStreamReadStream;】// 代理实现者即初始化一个默认结构信息,用来代理下面的accessstream_t *s = vlc_stream_CommonNew(parent, AStreamDestroy);if (unlikely(s == NULL))return NULL;// 真正模块功能实现提供者access,见后面3.1部分分析stream_t *access = access_New(VLC_OBJECT(s), input, preparsing, url);if (access == NULL){stream_CommonDelete(s);return NULL;}s->p_input = input;s->psz_url = strdup(access->psz_url);const char *cachename;if (access->pf_block != NULL){// 代理调用真正对应模块实现者access的对应方法功能s->pf_block = AStreamReadBlock;cachename = "prefetch,cache_block";}elseif (access->pf_read != NULL){// 代理调用真正对应模块实现者access的对应方法功能// 具体如何代理功能实现,见3.2小节分析s->pf_read = AStreamReadStream;cachename = "prefetch,cache_read";}else{cachename = NULL;}if (access->pf_readdir != NULL)s->pf_readdir = AStreamReadDir;elses->pf_readdir = AStreamNoReadDir;// 这两方法的实现其实是代理调用真正对应模块实现者access的对应方法功能s->pf_seek    = AStreamSeek;s->pf_control = AStreamControl;// 第二个access流结构被赋值到第一个结构体的【p_sys】指针中s->p_sys      = access;if (cachename != NULL)s = stream_FilterChainNew(s, cachename);return stream_FilterAutoNew(s);
}

3.1、access_New(VLC_OBJECT(s), input, preparsing, url); 实现分析:
位于【vlc/src/input/access.c】

static stream_t *access_New(vlc_object_t *parent, input_thread_t *input,bool preparsing, const char *mrl)
{char *redirv[MAX_REDIR];unsigned redirc = 0;// 创建一个默认结构stream_t *access = vlc_stream_CommonNew(parent, vlc_access_Destroy);if (unlikely(access == NULL))return NULL;// 进行初始化access->p_input = input;access->psz_name = NULL;access->psz_url = strdup(mrl);access->psz_filepath = NULL;access->b_preparsing = preparsing;if (unlikely(access->psz_url == NULL))goto error;// while循环体主要实现为根据媒体输入源URL【URI】,// 选择加载对应能够加载该媒体数据源的获取数据模块【file/http/rtsp/tcp等】while (redirc < MAX_REDIR){char *url = access->psz_url;msg_Dbg(access, "creating access: %s", url);// 获取【"://"】之后的字符串即可能真正的URLconst char *p = strstr(url, "://");if (p == NULL)goto error;access->psz_name = strndup(url, p - url);if (unlikely(access->psz_name == NULL))goto error;access->psz_location = p + 3;// 根据URL尝试解析其文件路径access->psz_filepath = get_path(access->psz_location);if (access->psz_filepath != NULL)msg_Dbg(access, " (path: %s)", access->psz_filepath);// 加载能够处理该类型输入流的获取数据流模块,见3.1.1部分分析access->p_module = module_need(access, "access", access->psz_name,true);if (access->p_module != NULL) /* success */{while (redirc > 0)free(redirv[--redirc]);// 注意:此处判断非常重要,而该值的赋值是在【module_need】功能执行时赋值的// 【即对应模块在模块初始化入口方法中赋值的】assert(access->pf_control != NULL);return access;}// ... 省略其他代码}msg_Err(access, "too many redirections");
error:// ... 省略其他代码return NULL;
}

3.1.1、module_need实现:位于【vlc/src/modules/modules.c】

#undef module_need
module_t *module_need(vlc_object_t *obj, const char *cap, const char *name,bool strict)
{return vlc_module_load(obj, cap, name, strict, generic_start, obj);
}// 注意此处的该方法非常重要,它就是前面第1小节分析过的加载模块流程中的初始化该模块的入口方法
static int generic_start(void *func, va_list ap)
{vlc_object_t *obj = va_arg(ap, vlc_object_t *);int (*activate)(vlc_object_t *) = func;return activate(obj);
}module_t *vlc_module_load(vlc_object_t *obj, const char *capability,const char *name, bool strict,vlc_activate_t probe, ...)
{// ... 省略其他代码// 注意此处变量【probe】就是指的上面的该方法【generic_start】// 实现原理:根据不同模块初始化模块加载时的功能类型声明,// 查找到所有符合功能要求的模块来匹配当前需要的功能【capability】int ret = module_load (obj, cand, probe, args);
}static int module_load (vlc_object_t *obj, module_t *m,vlc_activate_t init, va_list args)
{int ret = VLC_SUCCESS;if (module_Map(obj, m->plugin))return VLC_EGENERIC;if (m->pf_activate != NULL){// 模块的初始化功能方法指针存在时进入va_list ap;va_copy (ap, args);// 注意此处:init为前面的该方法【generic_start】指针,// 因此此处也就调用了该模块的模块初始化入口方法,// 如果初始化成功即可代表该模块可以处理当前媒体流的数据ret = init (m->pf_activate, ap);va_end (ap);}if (ret != VLC_SUCCESS)vlc_objres_clear(obj);return ret;
}

3.2、s->pf_read = AStreamReadStream; 的代理功能实现:

/* Read access */
static ssize_t AStreamReadStream(stream_t *s, void *buf, size_t len)
{// 根据上述的功能分析,可知此处的流结构[s->p_sys]即为真正的流处理模块功能实现者stream_t *access = s->p_sys;input_thread_t *input = s->p_input;if (vlc_stream_Eof(access))return 0;if (vlc_killed())return -1;// 代码追踪下去可知,该方法最终调用了://  [s->pf_read(s, buf, len);],该方法即access的实现方法,由此实现了代理功能ssize_t val = vlc_stream_ReadPartial(access, buf, len);if (val > 0 && input != NULL){uint64_t total;vlc_mutex_lock(&input_priv(input)->counters.counters_lock);stats_Update(input_priv(input)->counters.p_read_bytes, val, &total);stats_Update(input_priv(input)->counters.p_input_bitrate, total, NULL);stats_Update(input_priv(input)->counters.p_read_packets, 1, NULL);vlc_mutex_unlock(&input_priv(input)->counters.counters_lock);}return val;
}

4、基于上面的分析已大致知晓VLC中各个demux/mux/encoder/decoder功能模块的加载原理。如下分析一个模块的功能处理流程:【以mp4文件格式和H264编码的本地文件为例展开分析】。
首先分析模块初始化入口方法功能:位于【vlc/modules/demux/mp4/mp4.c】

// 检查文件并初始化MP4格式结构信息
static int Open( vlc_object_t * p_this )
{demux_t  *p_demux = (demux_t *)p_this;demux_sys_t     *p_sys;const uint8_t   *p_peek;// MP4的Box结构体MP4_Box_t       *p_ftyp;const MP4_Box_t *p_mvhd = NULL;const MP4_Box_t *p_mvex = NULL;bool      b_enabled_es;// 实现功能:简单检查输入源是否为MP4封装格式的文件,读取11个字节数据/* A little test to see if it could be a mp4 */if( vlc_stream_Peek( p_demux->s, &p_peek, 11 ) < 11 ) return VLC_EGENERIC;switch( VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] ) ){ // 此处判断第5、6、7、8位置的字节是否为以下对应有效的字节case ATOM_moov:case ATOM_foov:case ATOM_moof:case ATOM_mdat:case ATOM_udta:case ATOM_free:case ATOM_skip:case ATOM_wide:case ATOM_uuid:case VLC_FOURCC( 'p', 'n', 'o', 't' ):break;// 一般mp4文件这四个字节对应该值【ftyp】,并且要求f4v不支持case ATOM_ftyp:/* We don't yet support f4v, but avformat does. */if( p_peek[8] == 'f' && p_peek[9] == '4' && p_peek[10] == 'v' )return VLC_EGENERIC;break;default:return VLC_EGENERIC;}/* create our structure that will contains all data */p_sys = calloc( 1, sizeof( demux_sys_t ) );if ( !p_sys )return VLC_EGENERIC;// 检查输入源媒体流拉取端是否支持进行seek解析操作// 该方法位于【vlc_stream.h】中,并且最终调用了stream_t的方法即【s->pf_control(s, cmd, args);】// 而该方法此前已分析过,通过代理调用真正媒体流拉取实现者,// 分析本地mp4文件则此处为[file.c]文件拉取数据模块的该方法实现// 即FileControl( stream_t *p_access, int i_query, va_list args ),// 最终可知是否可以seek是根据文件的读取状态决定的/* I need to seek */vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );// 如果本地文件允许seek操作,则再次检查是否可以快速seek操作,同理可看到file模块的处理实现if( p_sys->b_seekable )vlc_stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &p_sys->b_fastseekable );// 设置支持的功能:解复用和交互控制,功能以方法指针链接/*Set exported functions */p_demux->pf_demux = Demux;p_demux->pf_control = Control;p_sys->context.i_lastseqnumber = UINT32_MAX;// 注意此处赋值关注点:前后两个结构体其实不是同一个结构体,// 但是此处巧妙的使用了结构体指针来进行赋值转换操作,因此是可行的。// 当然需注意再次使用时需要知道此前赋值的是啥才能有效转换回去p_demux->p_sys = p_sys;// 实现:解析文件加载MP4格式所有的Box结构体信息【链表结构实现】//【除了媒体流原始信息外-->即不包括编码的媒体流信息】// 分析见4.1小节if( LoadInitFrag( p_demux ) != VLC_SUCCESS )goto error;// Dump打印输出初始化的所有Box结构体信息MP4_BoxDumpStructure( p_demux->s, p_sys->p_root );// 获取到媒体文件类型【ftyp】结构体信息if( ( p_ftyp = MP4_BoxGet( p_sys->p_root, "/ftyp" ) ) ){switch( BOXDATA(p_ftyp)->i_major_brand ){case MAJOR_isom:msg_Dbg( p_demux,"ISO Media (isom) version %d.",BOXDATA(p_ftyp)->i_minor_version );break;case MAJOR_3gp4:case MAJOR_3gp5:case MAJOR_3gp6:case MAJOR_3gp7:msg_Dbg( p_demux, "3GPP Media Release: %4.4s",(char *)&BOXDATA(p_ftyp)->i_major_brand );break;case MAJOR_qt__:msg_Dbg( p_demux, "Apple QuickTime media" );break;case MAJOR_isml:msg_Dbg( p_demux, "PIFF (= isml = fMP4) media" );break;case MAJOR_dash:msg_Dbg( p_demux, "DASH Stream" );break;case MAJOR_M4A:msg_Dbg( p_demux, "iTunes audio" );if( var_InheritBool( p_demux, CFG_PREFIX"m4a-audioonly" ) )p_sys->hacks.es_cat_filters = AUDIO_ES;break;default:msg_Dbg( p_demux,"unrecognized major media specification (%4.4s).",(char*)&BOXDATA(p_ftyp)->i_major_brand );break;}/* also lookup in compatibility list */for(uint32_t i=0; i<BOXDATA(p_ftyp)->i_compatible_brands_count; i++){if (BOXDATA(p_ftyp)->i_compatible_brands[i] == MAJOR_dash){msg_Dbg( p_demux, "DASH Stream" );}else if (BOXDATA(p_ftyp)->i_compatible_brands[i] == VLC_FOURCC('s', 'm', 'o', 'o') ){msg_Dbg( p_demux, "Handling VLC Smooth Stream" );}}}else{msg_Dbg( p_demux, "file type box missing (assuming ISO Media)" );}// 检查【moov】结构体是否存在/* the file need to have one moov box */p_sys->p_moov = MP4_BoxGet( p_sys->p_root, "/moov" );if( unlikely(!p_sys->p_moov) ){p_sys->p_moov = MP4_BoxGet( p_sys->p_root, "/foov" );if( !p_sys->p_moov ){msg_Err( p_demux, "MP4 plugin discarded (no moov,foov,moof box)" );goto error;}/* we have a free box as a moov, rename it */p_sys->p_moov->i_type = ATOM_moov;}// 获取并初始化【mvhd】结构体信息p_mvhd = MP4_BoxGet( p_sys->p_moov, "mvhd" );if( p_mvhd && BOXDATA(p_mvhd) && BOXDATA(p_mvhd)->i_timescale ){p_sys->i_timescale = BOXDATA(p_mvhd)->i_timescale;p_sys->i_moov_duration = p_sys->i_duration = BOXDATA(p_mvhd)->i_duration;p_sys->i_cumulated_duration = BOXDATA(p_mvhd)->i_duration;}else{msg_Warn( p_demux, "No valid mvhd found" );goto error;}MP4_Box_t *p_rmra = MP4_BoxGet( p_sys->p_root, "/moov/rmra" );if( p_rmra != NULL && p_demux->p_input != NULL ){int        i_count = MP4_BoxCount( p_rmra, "rmda" );int        i;msg_Dbg( p_demux, "detected playlist mov file (%d ref)", i_count );input_thread_t *p_input = p_demux->p_input;input_item_t *p_current = input_GetItem( p_input );input_item_node_t *p_subitems = input_item_node_Create( p_current );for( i = 0; i < i_count; i++ ){MP4_Box_t *p_rdrf = MP4_BoxGet( p_rmra, "rmda[%d]/rdrf", i );char      *psz_ref;uint32_t  i_ref_type;if( !p_rdrf || !BOXDATA(p_rdrf) || !( psz_ref = strdup( BOXDATA(p_rdrf)->psz_ref ) ) ){continue;}i_ref_type = BOXDATA(p_rdrf)->i_ref_type;msg_Dbg( p_demux, "new ref=`%s' type=%4.4s",psz_ref, (char*)&i_ref_type );if( i_ref_type == VLC_FOURCC( 'u', 'r', 'l', ' ' ) ){if( strstr( psz_ref, "qt5gateQT" ) ){msg_Dbg( p_demux, "ignoring pseudo ref =`%s'", psz_ref );free( psz_ref );continue;}if( !strncmp( psz_ref, "http://", 7 ) ||!strncmp( psz_ref, "rtsp://", 7 ) ){;}else{char *psz_absolute;char *psz_path = strdup( p_demux->psz_location );char *end = strrchr( psz_path, '/' );if( end ) end[1] = '\0';else *psz_path = '\0';if( asprintf( &psz_absolute, "%s://%s%s",p_demux->psz_access, psz_path, psz_ref ) < 0 ){free( psz_ref );free( psz_path );input_item_node_Delete( p_subitems );return VLC_ENOMEM;}free( psz_ref );psz_ref = psz_absolute;free( psz_path );}msg_Dbg( p_demux, "adding ref = `%s'", psz_ref );input_item_t *p_item = input_item_New( psz_ref, NULL );input_item_CopyOptions( p_item, p_current );// 每个item数据可以有多个子item数据如媒体流渲染过滤器创建新的item等input_item_node_AppendItem( p_subitems, p_item );input_item_Release( p_item );}else{msg_Err( p_demux, "unknown ref type=%4.4s FIXME (send a bug report)",(char*)&BOXDATA(p_rdrf)->i_ref_type );}free( psz_ref );}// 最终调用了【out->pf_control( out, i_query, args )】,out为es_out_t结构体,// 而该结构体out创建是在【vlc/src/input/es_out_timeshift.c】的方法//【input_EsOutTimeshiftNew】中,而该方法在Init方法中调用。// 因此通过分析可知继续调用了【es_out_timeshift.c】的【Control】方法,// 又调用了【es_out_vaControl( p_sys->p_out, i_query, args )】,// 其中p_sys为es_out_sys_t结构体,其创建在【vlc/src/input/input.c】的【Create】方法中:// 即【priv->p_es_out_display = input_EsOutNew( p_input, priv->i_rate );】,// 而input_EsOutNew方法创建了该结构体,通过分析可知调用了【vlc/src/input/es_out.c】的【EsOutControl】该方法,// 然后在【EsOutControlLocked】方法中得到了处理,通过发送事件event回调得到的处理。/* FIXME: create a stream_filter sub-module for this */if (es_out_Control(p_demux->out, ES_OUT_POST_SUBNODE, p_subitems))input_item_node_Delete(p_subitems);}if( !(p_mvhd = MP4_BoxGet( p_sys->p_root, "/moov/mvhd" ) ) ){ // 没有找到【mvhd】结构体信息则进入if( !p_rmra ){msg_Err( p_demux, "cannot find /moov/mvhd" );goto error;}else{// 注:【DemuxRef】该方法为空实现msg_Warn( p_demux, "cannot find /moov/mvhd (pure ref file)" );p_demux->pf_demux = DemuxRef;return VLC_SUCCESS;}}else{p_sys->i_timescale = BOXDATA(p_mvhd)->i_timescale;if( p_sys->i_timescale == 0 ){msg_Err( p_this, "bad timescale" );goto error;}}// 获取音视频等【track】Box结构体信息个数const unsigned i_tracks = MP4_BoxCount( p_sys->p_root, "/moov/trak" );if( i_tracks < 1 ){msg_Err( p_demux, "cannot find any /moov/trak" );goto error;}msg_Dbg( p_demux, "found %u track%c", i_tracks, i_tracks ? 's':' ' );if( CreateTracks( p_demux, i_tracks ) != VLC_SUCCESS )goto error;/* Search the first chap reference (like quicktime) and* check that at least 1 stream is enabled */p_sys->p_tref_chap = NULL;b_enabled_es = false;for( unsigned i = 0; i < p_sys->i_tracks; i++ ){MP4_Box_t *p_trak = MP4_BoxGet( p_sys->p_root, "/moov/trak[%d]", i );MP4_Box_t *p_tkhd = MP4_BoxGet( p_trak, "tkhd" );if( p_tkhd && BOXDATA(p_tkhd) && (BOXDATA(p_tkhd)->i_flags&MP4_TRACK_ENABLED) )b_enabled_es = true;MP4_Box_t *p_chap = MP4_BoxGet( p_trak, "tref/chap", i );if( p_chap && p_chap->data.p_tref_generic &&p_chap->data.p_tref_generic->i_entry_count > 0 && !p_sys->p_tref_chap )p_sys->p_tref_chap = p_chap;}// 初始化并保存一些媒体元数据/* Set and store metadata */if( (p_sys->p_meta = vlc_meta_New()) )MP4_LoadMeta( p_sys, p_sys->p_meta );// 执行每个track并获取有用信息/* now process each track and extract all useful information */for( unsigned i = 0; i < p_sys->i_tracks; i++ ){MP4_Box_t *p_trak = MP4_BoxGet( p_sys->p_root, "/moov/trak[%u]", i );// 解析track信息并创建运行时所需数据如展示的宽高、语言类型、音视频track或字幕track信息解析、编解码类型、// 创建并初始化数据块chunk索引列表和样本simple索引列表、为每一个track创建一个ES结构体信息、fps信息、MP4_TrackSetup( p_demux, &p_sys->track[i], p_trak, true, !b_enabled_es );if( p_sys->track[i].b_ok && !p_sys->track[i].b_chapters_source ){const char *psz_cat;switch( p_sys->track[i].fmt.i_cat ){case( VIDEO_ES ):psz_cat = "video";break;case( AUDIO_ES ):psz_cat = "audio";break;case( SPU_ES ):psz_cat = "subtitle";break;default:psz_cat = "unknown";break;}msg_Dbg( p_demux, "adding track[Id 0x%x] %s (%s) language %s",p_sys->track[i].i_track_ID, psz_cat,p_sys->track[i].b_enable ? "enable":"disable",p_sys->track[i].fmt.psz_language ?p_sys->track[i].fmt.psz_language : "undef" );}else if( p_sys->track[i].b_ok && p_sys->track[i].b_chapters_source ){msg_Dbg( p_demux, "using track[Id 0x%x] for chapter language %s",p_sys->track[i].i_track_ID,p_sys->track[i].fmt.psz_language ?p_sys->track[i].fmt.psz_language : "undef" );}else{msg_Dbg( p_demux, "ignoring track[Id 0x%x]",p_sys->track[i].i_track_ID );}}p_mvex = MP4_BoxGet( p_sys->p_moov, "mvex" );if( p_mvex != NULL ){const MP4_Box_t *p_mehd = MP4_BoxGet( p_mvex, "mehd");if ( p_mehd && BOXDATA(p_mehd) ){if( BOXDATA(p_mehd)->i_fragment_duration > p_sys->i_duration ){p_sys->b_fragmented = true;p_sys->i_duration = BOXDATA(p_mehd)->i_fragment_duration;}}const MP4_Box_t *p_sidx = MP4_BoxGet( p_sys->p_root, "sidx");if( p_sidx )p_sys->b_fragmented = true;if ( p_sys->b_seekable ){if( !p_sys->b_fragmented /* as unknown */ ){/* Probe remaining to check if there's really fragmentsor if that file is just ready to append fragments */ProbeFragments( p_demux, (p_sys->i_duration == 0), &p_sys->b_fragmented );}if( vlc_stream_Seek( p_demux->s, p_sys->p_moov->i_pos ) != VLC_SUCCESS )goto error;}else /* Handle as fragmented by default as we can't see moof */{p_sys->context.p_fragment_atom = p_sys->p_moov;p_sys->context.i_current_box_type = ATOM_moov;p_sys->b_fragmented = true;}}if( p_sys->b_fragmented ){// 此处改变了该值p_demux->pf_demux = DemuxFrag;msg_Dbg( p_demux, "Set Fragmented demux mode" );}if( !p_sys->b_seekable && p_demux->pf_demux == Demux ){msg_Warn( p_demux, "MP4 plugin discarded (not seekable)" );goto error;}if( p_sys->i_tracks > 1 && !p_sys->b_fastseekable ){uint64_t i_max_continuity;bool b_flat;MP4_GetInterleaving( p_demux, &i_max_continuity, &b_flat );if( b_flat )msg_Warn( p_demux, "that media doesn't look interleaved, will need to seek");else if( i_max_continuity > DEMUX_TRACK_MAX_PRELOAD )msg_Warn( p_demux, "that media doesn't look properly interleaved, will need to seek");}// 加载字幕【章节】数据/* */LoadChapter( p_demux );// ASF文件格式功能【ASF是windows开发使用的媒体格式】p_sys->asfpacketsys.p_demux = p_demux;p_sys->asfpacketsys.pi_preroll = &p_sys->i_preroll;p_sys->asfpacketsys.pi_preroll_start = &p_sys->i_preroll_start;p_sys->asfpacketsys.pf_doskip = NULL;p_sys->asfpacketsys.pf_send = MP4ASF_Send;p_sys->asfpacketsys.pf_gettrackinfo = MP4ASF_GetTrackInfo;p_sys->asfpacketsys.pf_updatetime = NULL;p_sys->asfpacketsys.pf_setaspectratio = NULL;return VLC_SUCCESS;error:if( vlc_stream_Tell( p_demux->s ) > 0 ){if( vlc_stream_Seek( p_demux->s, 0 ) != VLC_SUCCESS )msg_Warn( p_demux, "Can't reset stream position from probing" );}Close( p_this );return VLC_EGENERIC;
}

4.1、LoadInitFrag分析:位于【vlc/modules/demux/mp4/mp4.c】

static int LoadInitFrag( demux_t *p_demux )
{demux_sys_t *p_sys = p_demux->p_sys;/* Load all boxes ( except raw data ) */if( ( p_sys->p_root = MP4_BoxGetRoot( p_demux->s ) ) == NULL ){goto LoadInitFragError;}return VLC_SUCCESS;LoadInitFragError:msg_Warn( p_demux, "MP4 plugin discarded (not a valid initialization chunk)" );return VLC_EGENERIC;
}MP4_Box_t *MP4_BoxGetRoot( stream_t *p_stream )
{int i_result;// 初始化了一个“虚假”root节点的Box数据结构体,作为开始节点MP4_Box_t *p_vroot = MP4_BoxNew( ATOM_root );if( p_vroot == NULL )return NULL;p_vroot->i_shortsize = 1;uint64_t i_size;// 调用了【vlc_stream_Control( s, STREAM_GET_SIZE, size )】,// 则如上面类似分析可知最终调用了【file.c】的FileControl方法,// 通过获取当前文件得到文件总大小if( vlc_stream_GetSize( p_stream, &i_size ) == 0 )p_vroot->i_size = i_size;// 获取第一个有效结构体信息【moov】,并且如果读取成功则移动当前已读BOX容器位置position,// 然后对文件读取端进行seek操作/* First get the moov */{const uint32_t stoplist[] = { ATOM_moov, ATOM_mdat, 0 };i_result = MP4_ReadBoxContainerChildren( p_stream, p_vroot, stoplist );}/* mdat appeared first */if( i_result && !MP4_BoxGet( p_vroot, "moov" ) ){ // 没有找到moov结构体信息bool b_seekable;if( vlc_stream_Control( p_stream, STREAM_CAN_SEEK, &b_seekable ) != VLC_SUCCESS || !b_seekable ){msg_Err( p_stream, "no moov before mdat and the stream is not seekable" );goto error;}// 重新加载moov结构体信息/* continue loading up to moov */const uint32_t stoplist[] = { ATOM_moov, 0 };i_result = MP4_ReadBoxContainerChildren( p_stream, p_vroot, stoplist );}if( !i_result )goto error;/* If there is a mvex box, it means fragmented MP4, and we're done */if( MP4_BoxCount( p_vroot, "moov/mvex" ) > 0 ){ // 当前已读取完毕则进入/* Read a bit more atoms as we might have an index between moov and moof */const uint32_t stoplist[] = { ATOM_sidx, 0 };const uint32_t excludelist[] = { ATOM_moof, ATOM_mdat, 0 };MP4_ReadBoxContainerChildrenIndexed( p_stream, p_vroot, stoplist, excludelist, false );return p_vroot;}// 当前读取偏移位置小于文件大小,则继续读取剩余Box结构数据信息【循环读取】if( vlc_stream_Tell( p_stream ) + 8 < (uint64_t) stream_Size( p_stream ) ){/* Get the rest of the file */i_result = MP4_ReadBoxContainerChildren( p_stream, p_vroot, NULL );if( !i_result )goto error;}MP4_Box_t *p_moov;MP4_Box_t *p_cmov;// 检查是否有一个cmov,如果是则用未压缩的替换压缩的moov/* check if there is a cmov, if so replacecompressed moov by  uncompressed one */if( ( ( p_moov = MP4_BoxGet( p_vroot, "moov" ) ) &&( p_cmov = MP4_BoxGet( p_vroot, "moov/cmov" ) ) ) ||( ( p_moov = MP4_BoxGet( p_vroot, "foov" ) ) &&( p_cmov = MP4_BoxGet( p_vroot, "foov/cmov" ) ) ) ){/* rename the compressed moov as a box to skip */p_moov->i_type = ATOM_skip;/* get uncompressed p_moov */p_moov = p_cmov->data.p_cmov->p_moov;p_cmov->data.p_cmov->p_moov = NULL;/* make p_root father of this new moov */p_moov->p_father = p_vroot;/* insert this new moov box as first child of p_root */p_moov->p_next = p_vroot->p_first;p_vroot->p_first = p_moov;}return p_vroot;error:MP4_BoxFree( p_vroot );MP4_Seek( p_stream, 0 );return NULL;
}

后续流程分析请见后续章节分析

【四】【vlc-android】播放控制交互与demux解复用层、媒体数据流拉取层的具体数据传递和控制流程源码分析相关推荐

  1. Android 数据Parcel序列化过程源码分析

    在Android系统中,所有的服务都必须注册到ServiceManger中,当客户进程需要请求某一服务时,首先从服务管家ServiceManger中查找出该服务,然后通过RPC远程调用的方式使用该服务 ...

  2. Android服务注册完整过程源码分析

    前面从不同片段分析了Android的Binder通信机制,本文结合前面介绍的内容,对整个Android的Binder通信过程进行一次完整的分析.分析以AudioService服务的注册过程为例. 由于 ...

  3. Android服务查询完整过程源码分析

    Android服务注册完整过程源码分析中从上到下详细分析了Android系统的服务注册过程,本文同样针对AudioService服务来介绍Android服务的查询过程. 客户端进程数据发送过程 pri ...

  4. Activity启动流程源码分析(基于Android N)

    Activity启动流程源码分析 一个Activity启动分为两种启动方式,一种是从Launcher界面上的图标点击启动,另一种是从一个Activity中设置按钮点击启动另外一个Activity.这里 ...

  5. Android系统默认Home应用程序(Launcher)的启动过程源码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还须要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...

  6. Android应用程序启动Binder线程源码分析

    Android的应用程序包括Java应用及本地应用,Java应用运行在davik虚拟机中,由zygote进程来创建启动,而本地服务应用在Android系统启动时,通过配置init.rc文件来由Init ...

  7. Android uevent进程源码分析

    在Android Init进程源码分析中讲到init进程会依次执行被加入到待执行队列action_queue中的Action,在init.rc中我们有这么一段配置: 11 on early-init1 ...

  8. VLC Android播放器介绍

    VLC目录结构 在ubuntu下编译vlc完成后,就可以将vlc-android部分导入android studio或者eclipse当中,完成java端的代码定制修改和编译,也可以自己将需要的部分添 ...

  9. 【七】【vlc-android】vlc的decoder控制层传输数据与ffmpeg音频解码模块decoder层进行解码的数据交互流程源码分析

    [以mp4文件格式和AAC.H264编码的本地文件为例展开分析] 由第六章节可知音频解码器的加载方式与视频解码器也是类似的. 通过此前分析过的模块加载方式,可找到ffmpeg解码和编码模块的初始化加载 ...

最新文章

  1. Ubuntu16.04 配置pytorch
  2. Alpha(5/10)
  3. [题解]BZOJ1004 序列函数
  4. reactjs中的事件处理
  5. k8s调度之node的亲和性以及pod的亲和性/反亲和性
  6. linux飞信机器人的安装fetion
  7. custompage.width 不能小数吗_基金净值暴涨暴跌,背后的原因你清楚吗?
  8. ASP.NET MVC 3发布报错(ASP.NET MVC 3在没有安装环境的服务器上运行)的解决方案
  9. 编辑距离(信息学奥赛一本通-T1276)
  10. 触发器的创建和使用(sql2005)
  11. php内置web server
  12. 同济大学高等数学第7版视频
  13. zenmap扫描ip段_扫描工具——Nmap用法详解
  14. html刷浏览量,批量刷网页点击量工具
  15. html字体外颜色轮廓,文字轮廓效果
  16. 服务器上qq邮件不能打开方式,QQ邮箱打不开的处理方法
  17. wxid转微信号软件执行代码。
  18. linux装回win10系统无法开机,Win10/Linux双系统删除之后出现grub无法开机修复方法...
  19. ping 快ping
  20. Ubuntu14.04网易云音乐的下载及安装 ssh安装 卸载 安装输入法

热门文章

  1. 线性调频雷达信号的脉冲压缩-匹配滤波
  2. [EULAR文摘] 利用蛋白组学技术开发一项蛋白评分用于预测TNFi疗效
  3. 【最全干货】SQL注入大合集
  4. c语言已知年第几天求哪月哪日,已知今天的日期(年月日),求明天的日期(年月日)...
  5. 直播代码,Android开机速度优化
  6. 2018年1月学习心得报告
  7. Jenkins+Gitlab+Generic Webhook Trigger插件
  8. 读书笔记_《当下的力量》_精华书摘
  9. 操作系统:虚拟地址翻译为物理地址的过程
  10. 5G XR及多媒体增强技术分析