1、VLC中有很多demux/mux/encoder/decoder模块,因此需要先了解这些模块的加载原理,模块的加载原理基本一致,因此举例分析MP4解复用模块如何加载完成的,主要流程如下:
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 ()
static int Open ( vlc_object_t * );
static void Close( vlc_object_t * );
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 ()
#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_activate
void *pf_deactivate
struct module_t
{
vlc_plugin_t *plugin;
module_t *next;
unsigned i_shortcuts;
const char **pp_shortcuts;
const char *psz_shortname;
const char *psz_longname;
const char *psz_help;
const char *psz_capability;
int i_score;
const char *activate_name;
const char *deactivate_name;
void *pf_activate;
void *pf_deactivate;
};
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);
}
2、接下来,交互途径主要是如下功能方法:
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;
}
int demux_vaControl( demux_t *demux, int query, va_list args )
{
if( demux->s != NULL )
switch( query )
{
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;
}
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 );
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模块中:
static int Open( vlc_object_t * p_this ){
p_demux->pf_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 )
{
*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;
int64_t q = i_value / i_timescale;
int64_t r = i_value % i_timescale;
return q * i_newscale + r * i_newscale / i_timescale;
}
由此其他操作命令同理也通过类似的这种方式进行的,参数传递的是可变参数。
3、媒体数据流模块加载媒体数据流结构体信息和解复用模块创建数据交互通道流程分析:
struct stream_t
{
VLC_COMMON_MEMBERS
module_t *p_module;
char *psz_name;
char *psz_url;
const char *psz_location;
char *psz_filepath;
bool b_preparsing;
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);
void *p_sys;
};
stream_t *stream_AccessNew(vlc_object_t *parent, input_thread_t *input,
bool preparsing, const char *url)
{
stream_t *s = vlc_stream_CommonNew(parent, AStreamDestroy);
if (unlikely(s == NULL))
return NULL;
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)
{
s->pf_block = AStreamReadBlock;
cachename = "prefetch,cache_block";
}
else
if (access->pf_read != NULL)
{
s->pf_read = AStreamReadStream;
cachename = "prefetch,cache_read";
}
else
{
cachename = NULL;
}
if (access->pf_readdir != NULL)
s->pf_readdir = AStreamReadDir;
else
s->pf_readdir = AStreamNoReadDir;
s->pf_seek = AStreamSeek;
s->pf_control = AStreamControl;
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 (redirc < MAX_REDIR)
{
char *url = access->psz_url;
msg_Dbg(access, "creating access: %s", url);
const 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;
access->psz_filepath = get_path(access->psz_location);
if (access->psz_filepath != NULL)
msg_Dbg(access, " (path: %s)", access->psz_filepath);
access->p_module = module_need(access, "access", access->psz_name,
true);
if (access->p_module != NULL)
{
while (redirc > 0)
free(redirv[--redirc]);
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);
}
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, ...)
{
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);
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; 的代理功能实现:
static ssize_t AStreamReadStream(stream_t *s, void *buf, size_t len)
{
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;
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】
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_t *p_ftyp;
const MP4_Box_t *p_mvhd = NULL;
const MP4_Box_t *p_mvex = NULL;
bool b_enabled_es;
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] ) )
{
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;
case ATOM_ftyp:
if( p_peek[8] == 'f' && p_peek[9] == '4' && p_peek[10] == 'v' )
return VLC_EGENERIC;
break;
default:
return VLC_EGENERIC;
}
p_sys = calloc( 1, sizeof( demux_sys_t ) );
if ( !p_sys )
return VLC_EGENERIC;
vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
if( p_sys->b_seekable )
vlc_stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &p_sys->b_fastseekable );
p_demux->pf_demux = Demux;
p_demux->pf_control = Control;
p_sys->context.i_lastseqnumber = UINT32_MAX;
p_demux->p_sys = p_sys;
if( LoadInitFrag( p_demux ) != VLC_SUCCESS )
goto error;
MP4_BoxDumpStructure( p_demux->s, p_sys->p_root );
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;
}
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)" );
}
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;
}
p_sys->p_moov->i_type = ATOM_moov;
}
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 );
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 );
}
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" ) ) )
{
if( !p_rmra )
{
msg_Err( p_demux, "cannot find /moov/mvhd" );
goto error;
}
else
{
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;
}
}
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;
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;
}
if( (p_sys->p_meta = vlc_meta_New()) )
MP4_LoadMeta( p_sys, p_sys->p_meta );
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 );
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 )
{
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
{
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 );
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;
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;
MP4_Box_t *p_vroot = MP4_BoxNew( ATOM_root );
if( p_vroot == NULL )
return NULL;
p_vroot->i_shortsize = 1;
uint64_t i_size;
if( vlc_stream_GetSize( p_stream, &i_size ) == 0 )
p_vroot->i_size = i_size;
{
const uint32_t stoplist[] = { ATOM_moov, ATOM_mdat, 0 };
i_result = MP4_ReadBoxContainerChildren( p_stream, p_vroot, stoplist );
}
if( i_result && !MP4_BoxGet( p_vroot, "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;
}
const uint32_t stoplist[] = { ATOM_moov, 0 };
i_result = MP4_ReadBoxContainerChildren( p_stream, p_vroot, stoplist );
}
if( !i_result )
goto error;
if( MP4_BoxCount( p_vroot, "moov/mvex" ) > 0 )
{
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;
}
if( vlc_stream_Tell( p_stream ) + 8 < (uint64_t) stream_Size( p_stream ) )
{
i_result = MP4_ReadBoxContainerChildren( p_stream, p_vroot, NULL );
if( !i_result )
goto error;
}
MP4_Box_t *p_moov;
MP4_Box_t *p_cmov;
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" ) ) ) )
{
p_moov->i_type = ATOM_skip;
p_moov = p_cmov->data.p_cmov->p_moov;
p_cmov->data.p_cmov->p_moov = NULL;
p_moov->p_father = p_vroot;
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;
}
后续流程分析请见后续章节分析
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)