VLCKit源码走读笔记
基础概念:
视频播放流程:读取原始数据->解复用->解码->显示
playlist: playlist表示播放列表,VLC在启动后,即创建一个playlist thread,用户输入后,动态创建input。
input: input表示输入,当用户通过界面输入一个文件或者流地址时,input thread 被动态创建,该线程的生命周期直到本次播放结束。
access: access表示访问,是VLC抽象的一个层,该层向下直接使用文件或网络IO接口,向上为stream层服务,提供IO接口。
stream: stream表示流,是VLC抽象的一个层,该层向下直接使用access层提供的IO接口,向上为demux层服务,提供IO接口。
demux: demux表示解复用,是视频技术中的概念,该层向下直接使用stream层提供的IO接口,数据出来后送es_out。
es_out: es_out表示输出,是VLC抽象的一个层,该层获取demux后的数据,送decode解码。
decode: decode表示解码,是视频技术中的概念,获取es_out出来的数据(通过一个fifo交互),解码后送output。
output: output表示输出,获取从decode出来的数据,送readerer。
readerer: readerer表示显示,获取从output出来的数据(通过一个fifo交互),然后显示。
流程分析: libvlc_new=>libvlc_media_player_new=>libvlc_media_new_location=>libvlc_media_player_set_media=>libvlc_media_release=>libvlc_media_player_play=>libvlc_media_player_stop=>libvlc_media_player_release=>libvlc_media_release=>libvlc_release
1)main函数(\bin\vlc.c)
1、调用libvlc_new()初始化一个libvlc_instance_t实例,该实例在程序运行过程中唯一。
1.1、调用libvlc_InternalCreate创建一个libvlc_int_t。
1.2、调用libvlc_InternalInit初始化libvlc_int_t实例。
1.3、初始化libvlc_instance_t其他成员。
2、调用libvlc_set_exit_handler设置VLC退出时的回调函数。
3、调用(\lib\playlist.c)libvlc_add_intf-->(\src\interface\interface.c)libvlc_InternalAddIntf 添加模块。
3.1、获取playlist,如果为空,则调用(\src\playlist\engine.c)playlist_Create创建一个playlist结构,并调用(src/playlist/thread.c)playlist_Activate创建新的playlist线程(src/playlist/thread.c)Thread。
3.2、调用intf_Create创建一个默认的interface。
3.2.1、调用vlc_custom_create创建一个vlc object(intf_thread_t)。
3.2.2、注册一个添加interface的回调方法。
3.2.3、调用module_need加载一个interface模块。
4、调用(\lib\playlist.c)libvlc_playlist_play-->(\src\interface\interface.c)libvlc_InternalPlay,如果播放列表不为空,并且被设置为自动播放,则播放播放列表内容。
2)创建一个输入
1、初始化成功后,程序运行在playlist的线程Thread(src/playlist/thread.c)中,循环接收界面输入的请求。
2、当输入一个新的文件或者流地址,在PlaylistVAControl获得信号,并发送该信号。
3、Thread接收到播放请求后,在Next()中调用PlayItem方法。
3.1、调用input_Create创建一个input结构,并初始化各种成员,其中包括调用(\src\input\es_out.c)input_EsOutNew创建p_es_out_display。
3.2、调用input_Start创建一个input线程Run(src/input/input.c)。
3)初始化输入
调用Run(src/input/input.c)中的Init方法,开始初始化。
1、调用(src/input/es_out_timeshift.c)input_EsOutTimeshiftNew新建一个50M的Timeshift(暂停缓存),包括创建并初始化p_es_out(es_out),与后续步骤9相关。
2、调用input_ChangeState设置input的状态为OPENING_S。
3、调用(src/input/input.c)InputSourceNew。
3.1、调用input_SplitMRL分解输入uri。
3.2、调用InputDemuxNew创建demux_t
3.2.1、以stream形参为NULL调用demux_NewAdvanced加载"access_demux"模块。
3.2.2、如果没有合适的"access_demux"模块,则调用(\src\input\access.c)stream_AccessNew创建一个实际的p_stream。
3.2.2.1、调用access_New创建stream_t结构体access。
3.2.2.1.1、调用(\src\input\stream.c)vlc_stream_CommonNew创建stream_t结构体access。
3.2.2.1.2、调用module_need加载合适的access模块。
3.2.2.1.3、调用access模块的Open*方法,以(\modules\access\avio.c)avio模块为例。
3.2.2.1.3.1、调用(\modules\codec\avcodec\avcommon.h)vlc_init_avformat初始化VLC即avformat环境。
3.2.2.1.3.2、调用avio_open2打开该uri。
3.2.2.1.3.3、设置access的IO方法指针。
3.2.2.2、根据模式(stream/block)设置steam层的IO方法指针。stream层的IO方法实际指向access层对应的IO方法指针。
3.2.2.3、为stream层的缓冲申请并初始化内存。
3.2.2.4、调用AStreamPrebufferStream执行一次读操作。
3.2.3、调用(\src\input\stream_filter.c)stream_FilterChainNew---Add specified stream filter(s)。
3.2.4、调用(\src\input\demux.c)demux_NewAdvanced创建一个demux。
3.2.4.1 调用vlc_custom_create创建demux_t结构体。
3.2.4.2 调用module_need加载合适的demux模块。
3.2.4.3 调用demux模块的Open*方法,以(\modules\demux\avformat\demux.c)demux模块为例。
1 int avformat_OpenDemux( vlc_object_t *p_this ) 2 { 3 demux_t *p_demux = (demux_t*)p_this; 4 demux_sys_t *p_sys; 5 AVProbeData pd = { }; 6 AVInputFormat *fmt = NULL; 7 int64_t i_start_time = -1; 8 bool b_can_seek; 9 char *psz_url; 10 const uint8_t *peek; 11 int error; 12 13 /* Init Probe data */ 14 pd.buf_size = vlc_stream_Peek( p_demux->s, &peek, 2048 + 213 ); 15 if( pd.buf_size <= 0 ) 16 { 17 msg_Warn( p_demux, "cannot peek" ); 18 return VLC_EGENERIC; 19 } 20 21 pd.buf = malloc( pd.buf_size + AVPROBE_PADDING_SIZE ); 22 if( unlikely(pd.buf == NULL) ) 23 return VLC_ENOMEM; 24 25 memcpy( pd.buf, peek, pd.buf_size ); 26 memset( pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE ); 27 28 if( p_demux->psz_file ) 29 psz_url = strdup( p_demux->psz_file ); 30 else 31 { 32 if( asprintf( &psz_url, "%s://%s", p_demux->psz_access, 33 p_demux->psz_location ) == -1) 34 psz_url = NULL; 35 } 36 37 if( psz_url != NULL ) 38 msg_Dbg( p_demux, "trying url: %s", psz_url ); 39 40 pd.filename = psz_url; 41 42 vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_can_seek ); 43 44 vlc_init_avformat(p_this); 45 46 /* Guess format */ 47 char *psz_format = var_InheritString( p_this, "avformat-format" ); 48 if( psz_format ) 49 { 50 if( (fmt = av_find_input_format(psz_format)) ) 51 msg_Dbg( p_demux, "forcing format: %s", fmt->name ); 52 free( psz_format ); 53 } 54 55 if( fmt == NULL ) 56 fmt = av_probe_input_format( &pd, 1 ); 57 58 free( pd.buf ); 59 60 if( fmt == NULL ) 61 { 62 msg_Dbg( p_demux, "couldn't guess format" ); 63 free( psz_url ); 64 return VLC_EGENERIC; 65 } 66 67 if( !p_demux->obj.force ) 68 { 69 static const char ppsz_blacklist[][16] = { 70 /* Don't handle MPEG unless forced */ 71 "mpeg", "vcd", "vob", "mpegts", 72 /* libavformat's redirector won't work */ 73 "redir", "sdp", 74 /* Don't handle subtitles format */ 75 "ass", "srt", "microdvd", 76 /* No timestamps at all */ 77 "hevc", "h264", 78 "" 79 }; 80 81 for( int i = 0; *ppsz_blacklist[i]; i++ ) 82 { 83 if( !strcmp( fmt->name, ppsz_blacklist[i] ) ) 84 { 85 free( psz_url ); 86 return VLC_EGENERIC; 87 } 88 } 89 } 90 91 /* Don't trigger false alarms on bin files */ 92 if( !p_demux->obj.force && !strcmp( fmt->name, "psxstr" ) ) 93 { 94 int i_len; 95 96 if( !p_demux->psz_file ) 97 { 98 free( psz_url ); 99 return VLC_EGENERIC; 100 } 101 102 i_len = strlen( p_demux->psz_file ); 103 if( i_len < 4 ) 104 { 105 free( psz_url ); 106 return VLC_EGENERIC; 107 } 108 109 if( strcasecmp( &p_demux->psz_file[i_len - 4], ".str" ) && 110 strcasecmp( &p_demux->psz_file[i_len - 4], ".xai" ) && 111 strcasecmp( &p_demux->psz_file[i_len - 3], ".xa" ) ) 112 { 113 free( psz_url ); 114 return VLC_EGENERIC; 115 } 116 } 117 118 msg_Dbg( p_demux, "detected format: %s", fmt->name ); 119 120 /* Fill p_demux fields */ 121 p_demux->pf_demux = Demux; 122 p_demux->pf_control = Control; 123 p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) ); 124 if( !p_sys ) 125 { 126 free( psz_url ); 127 return VLC_ENOMEM; 128 } 129 p_sys->ic = 0; 130 p_sys->fmt = fmt; 131 p_sys->tracks = NULL; 132 p_sys->i_ssa_order = 0; 133 TAB_INIT( p_sys->i_attachments, p_sys->attachments); 134 p_sys->p_title = NULL; 135 136 /* Create I/O wrapper */ 137 unsigned char * p_io_buffer = av_malloc( AVFORMAT_IOBUFFER_SIZE ); 138 if( !p_io_buffer ) 139 { 140 free( psz_url ); 141 avformat_CloseDemux( p_this ); 142 return VLC_ENOMEM; 143 } 144 145 p_sys->ic = avformat_alloc_context(); 146 if( !p_sys->ic ) 147 { 148 av_free( p_io_buffer ); 149 free( psz_url ); 150 avformat_CloseDemux( p_this ); 151 return VLC_ENOMEM; 152 } 153 154 AVIOContext *pb = p_sys->ic->pb = avio_alloc_context( p_io_buffer, 155 AVFORMAT_IOBUFFER_SIZE, 0, p_demux, IORead, NULL, IOSeek ); 156 if( !pb ) 157 { 158 av_free( p_io_buffer ); 159 free( psz_url ); 160 avformat_CloseDemux( p_this ); 161 return VLC_ENOMEM; 162 } 163 164 p_sys->ic->pb->seekable = b_can_seek ? AVIO_SEEKABLE_NORMAL : 0; 165 error = avformat_open_input(&p_sys->ic, psz_url, p_sys->fmt, NULL); 166 167 if( error < 0 ) 168 { 169 msg_Err( p_demux, "Could not open %s: %s", psz_url, 170 vlc_strerror_c(AVUNERROR(error)) ); 171 av_free( pb->buffer ); 172 av_free( pb ); 173 p_sys->ic = NULL; 174 free( psz_url ); 175 avformat_CloseDemux( p_this ); 176 return VLC_EGENERIC; 177 } 178 free( psz_url ); 179 180 char *psz_opts = var_InheritString( p_demux, "avformat-options" ); 181 unsigned nb_streams = p_sys->ic->nb_streams; 182 183 AVDictionary *options[nb_streams ? nb_streams : 1]; 184 options[0] = NULL; 185 for (unsigned i = 1; i < nb_streams; i++) 186 options[i] = NULL; 187 if (psz_opts) { 188 vlc_av_get_options(psz_opts, &options[0]); 189 for (unsigned i = 1; i < nb_streams; i++) { 190 av_dict_copy(&options[i], options[0], 0); 191 } 192 free(psz_opts); 193 } 194 vlc_avcodec_lock(); /* avformat calls avcodec behind our back!!! */ 195 error = avformat_find_stream_info( p_sys->ic, options ); 196 vlc_avcodec_unlock(); 197 AVDictionaryEntry *t = NULL; 198 while ((t = av_dict_get(options[0], "", t, AV_DICT_IGNORE_SUFFIX))) { 199 msg_Err( p_demux, "Unknown option \"%s\"", t->key ); 200 } 201 av_dict_free(&options[0]); 202 for (unsigned i = 1; i < nb_streams; i++) { 203 av_dict_free(&options[i]); 204 } 205 206 nb_streams = p_sys->ic->nb_streams; /* it may have changed */ 207 if( !nb_streams ) 208 { 209 msg_Err( p_demux, "No streams found"); 210 avformat_CloseDemux( p_this ); 211 return VLC_EGENERIC; 212 } 213 p_sys->tracks = calloc( nb_streams, sizeof(*p_sys->tracks) ); 214 if( !p_sys->tracks ) 215 { 216 avformat_CloseDemux( p_this ); 217 return VLC_ENOMEM; 218 } 219 p_sys->i_tracks = nb_streams; 220 221 if( error < 0 ) 222 { 223 msg_Warn( p_demux, "Could not find stream info: %s", 224 vlc_strerror_c(AVUNERROR(error)) ); 225 } 226 227 for( unsigned i = 0; i < nb_streams; i++ ) 228 { 229 struct avformat_track_s *p_track = &p_sys->tracks[i]; 230 AVStream *s = p_sys->ic->streams[i]; 231 const AVCodecParameters *cp = s->codecpar; 232 es_format_t es_fmt; 233 const char *psz_type = "unknown"; 234 235 /* Do not use the cover art as a stream */ 236 if( s->disposition == AV_DISPOSITION_ATTACHED_PIC ) 237 continue; 238 239 vlc_fourcc_t fcc = GetVlcFourcc( cp->codec_id ); 240 switch( cp->codec_type ) 241 { 242 case AVMEDIA_TYPE_AUDIO: 243 es_format_Init( &es_fmt, AUDIO_ES, fcc ); 244 es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); 245 es_fmt.i_bitrate = cp->bit_rate; 246 es_fmt.audio.i_channels = cp->channels; 247 es_fmt.audio.i_rate = cp->sample_rate; 248 es_fmt.audio.i_bitspersample = cp->bits_per_coded_sample; 249 es_fmt.audio.i_blockalign = cp->block_align; 250 psz_type = "audio"; 251 252 if(cp->codec_id == AV_CODEC_ID_AAC_LATM) 253 { 254 es_fmt.i_original_fourcc = VLC_FOURCC('L','A','T','M'); 255 es_fmt.b_packetized = false; 256 } 257 else if(cp->codec_id == AV_CODEC_ID_AAC && p_sys->fmt->long_name && 258 strstr(p_sys->fmt->long_name, "raw ADTS AAC")) 259 { 260 es_fmt.i_original_fourcc = VLC_FOURCC('A','D','T','S'); 261 es_fmt.b_packetized = false; 262 } 263 break; 264 265 case AVMEDIA_TYPE_VIDEO: 266 es_format_Init( &es_fmt, VIDEO_ES, fcc ); 267 es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); 268 269 es_fmt.video.i_bits_per_pixel = cp->bits_per_coded_sample; 270 /* Special case for raw video data */ 271 if( cp->codec_id == AV_CODEC_ID_RAWVIDEO ) 272 { 273 msg_Dbg( p_demux, "raw video, pixel format: %i", cp->format ); 274 if( GetVlcChroma( &es_fmt.video, cp->format ) != VLC_SUCCESS) 275 { 276 msg_Err( p_demux, "was unable to find a FourCC match for raw video" ); 277 } 278 else 279 es_fmt.i_codec = es_fmt.video.i_chroma; 280 } 281 /* We need this for the h264 packetizer */ 282 else if( cp->codec_id == AV_CODEC_ID_H264 && ( p_sys->fmt == av_find_input_format("flv") || 283 p_sys->fmt == av_find_input_format("matroska") || p_sys->fmt == av_find_input_format("mp4") ) ) 284 es_fmt.i_original_fourcc = VLC_FOURCC( 'a', 'v', 'c', '1' ); 285 286 es_fmt.video.i_width = cp->width; 287 es_fmt.video.i_height = cp->height; 288 es_fmt.video.i_visible_width = es_fmt.video.i_width; 289 es_fmt.video.i_visible_height = es_fmt.video.i_height; 290 291 get_rotation(&es_fmt, s); 292 293 # warning FIXME: implement palette transmission 294 psz_type = "video"; 295 296 AVRational rate; 297 #if (LIBAVUTIL_VERSION_MICRO < 100) /* libav */ 298 # if (LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(55, 20, 0)) 299 rate.num = s->time_base.num; 300 rate.den = s->time_base.den; 301 # else 302 rate.num = s->codec->time_base.num; 303 rate.den = s->codec->time_base.den; 304 # endif 305 rate.den *= __MAX( s->codec->ticks_per_frame, 1 ); 306 #else /* ffmpeg */ 307 rate = av_guess_frame_rate( p_sys->ic, s, NULL ); 308 #endif 309 if( rate.den && rate.num ) 310 { 311 es_fmt.video.i_frame_rate = rate.num; 312 es_fmt.video.i_frame_rate_base = rate.den; 313 } 314 315 es_fmt.video.i_sar_num = s->sample_aspect_ratio.num; 316 if (s->sample_aspect_ratio.num > 0) 317 es_fmt.video.i_sar_den = s->sample_aspect_ratio.den; 318 else 319 es_fmt.video.i_sar_den = 0; 320 break; 321 322 case AVMEDIA_TYPE_SUBTITLE: 323 es_format_Init( &es_fmt, SPU_ES, fcc ); 324 es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); 325 if( strncmp( p_sys->ic->iformat->name, "matroska", 8 ) == 0 && 326 cp->codec_id == AV_CODEC_ID_DVD_SUBTITLE && 327 cp->extradata != NULL && 328 cp->extradata_size > 0 ) 329 { 330 char *psz_start; 331 char *psz_buf = malloc( cp->extradata_size + 1); 332 if( psz_buf != NULL ) 333 { 334 memcpy( psz_buf, cp->extradata , cp->extradata_size ); 335 psz_buf[cp->extradata_size] = '\0'; 336 337 psz_start = strstr( psz_buf, "size:" ); 338 if( psz_start && 339 vobsub_size_parse( psz_start, 340 &es_fmt.subs.spu.i_original_frame_width, 341 &es_fmt.subs.spu.i_original_frame_height ) == VLC_SUCCESS ) 342 { 343 msg_Dbg( p_demux, "original frame size: %dx%d", 344 es_fmt.subs.spu.i_original_frame_width, 345 es_fmt.subs.spu.i_original_frame_height ); 346 } 347 else 348 { 349 msg_Warn( p_demux, "reading original frame size failed" ); 350 } 351 352 psz_start = strstr( psz_buf, "palette:" ); 353 if( psz_start && 354 vobsub_palette_parse( psz_start, &es_fmt.subs.spu.palette[1] ) == VLC_SUCCESS ) 355 { 356 es_fmt.subs.spu.palette[0] = SPU_PALETTE_DEFINED; 357 msg_Dbg( p_demux, "vobsub palette read" ); 358 } 359 else 360 { 361 msg_Warn( p_demux, "reading original palette failed" ); 362 } 363 free( psz_buf ); 364 } 365 } 366 else if( cp->codec_id == AV_CODEC_ID_DVB_SUBTITLE && 367 cp->extradata_size > 3 ) 368 { 369 es_fmt.subs.dvb.i_id = GetWBE( cp->extradata ) | 370 (GetWBE( cp->extradata + 2 ) << 16); 371 } 372 373 psz_type = "subtitle"; 374 break; 375 376 default: 377 es_format_Init( &es_fmt, UNKNOWN_ES, 0 ); 378 es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); 379 #ifdef HAVE_AVUTIL_CODEC_ATTACHMENT 380 if( cp->codec_type == AVMEDIA_TYPE_ATTACHMENT ) 381 { 382 input_attachment_t *p_attachment; 383 384 psz_type = "attachment"; 385 if( cp->codec_id == AV_CODEC_ID_TTF ) 386 { 387 AVDictionaryEntry *filename = av_dict_get( s->metadata, "filename", NULL, 0 ); 388 if( filename && filename->value ) 389 { 390 p_attachment = vlc_input_attachment_New( 391 filename->value, "application/x-truetype-font", 392 NULL, cp->extradata, (int)cp->extradata_size ); 393 if( p_attachment ) 394 TAB_APPEND( p_sys->i_attachments, p_sys->attachments, 395 p_attachment ); 396 } 397 } 398 else msg_Warn( p_demux, "unsupported attachment type (%u) in avformat demux", cp->codec_id ); 399 } 400 else 401 #endif 402 { 403 if( cp->codec_type == AVMEDIA_TYPE_DATA ) 404 psz_type = "data"; 405 406 msg_Warn( p_demux, "unsupported track type (%u:%u) in avformat demux", cp->codec_type, cp->codec_id ); 407 } 408 break; 409 } 410 411 AVDictionaryEntry *language = av_dict_get( s->metadata, "language", NULL, 0 ); 412 if ( language && language->value ) 413 es_fmt.psz_language = strdup( language->value ); 414 415 if( s->disposition & AV_DISPOSITION_DEFAULT ) 416 es_fmt.i_priority = ES_PRIORITY_SELECTABLE_MIN + 1000; 417 418 #ifdef HAVE_AVUTIL_CODEC_ATTACHMENT 419 if( cp->codec_type != AVMEDIA_TYPE_ATTACHMENT ) 420 #endif 421 if( cp->codec_type != AVMEDIA_TYPE_DATA ) 422 { 423 const bool b_ogg = !strcmp( p_sys->fmt->name, "ogg" ); 424 const uint8_t *p_extra = cp->extradata; 425 unsigned i_extra = cp->extradata_size; 426 427 if( cp->codec_id == AV_CODEC_ID_THEORA && b_ogg ) 428 { 429 unsigned pi_size[3]; 430 const void *pp_data[3]; 431 unsigned i_count; 432 for( i_count = 0; i_count < 3; i_count++ ) 433 { 434 if( i_extra < 2 ) 435 break; 436 pi_size[i_count] = GetWBE( p_extra ); 437 pp_data[i_count] = &p_extra[2]; 438 if( i_extra < pi_size[i_count] + 2 ) 439 break; 440 441 p_extra += 2 + pi_size[i_count]; 442 i_extra -= 2 + pi_size[i_count]; 443 } 444 if( i_count > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra, 445 pi_size, pp_data, i_count ) ) 446 { 447 es_fmt.i_extra = 0; 448 es_fmt.p_extra = NULL; 449 } 450 } 451 else if( cp->codec_id == AV_CODEC_ID_SPEEX && b_ogg ) 452 { 453 const uint8_t p_dummy_comment[] = { 454 0, 0, 0, 0, 455 0, 0, 0, 0, 456 }; 457 unsigned pi_size[2]; 458 const void *pp_data[2]; 459 460 pi_size[0] = i_extra; 461 pp_data[0] = p_extra; 462 463 pi_size[1] = sizeof(p_dummy_comment); 464 pp_data[1] = p_dummy_comment; 465 466 if( pi_size[0] > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra, 467 pi_size, pp_data, 2 ) ) 468 { 469 es_fmt.i_extra = 0; 470 es_fmt.p_extra = NULL; 471 } 472 } 473 else if( cp->codec_id == AV_CODEC_ID_OPUS ) 474 { 475 const uint8_t p_dummy_comment[] = { 476 'O', 'p', 'u', 's', 477 'T', 'a', 'g', 's', 478 0, 0, 0, 0, /* Vendor String length */ 479 /* Vendor String */ 480 0, 0, 0, 0, /* User Comment List Length */ 481 482 }; 483 unsigned pi_size[2]; 484 const void *pp_data[2]; 485 486 pi_size[0] = i_extra; 487 pp_data[0] = p_extra; 488 489 pi_size[1] = sizeof(p_dummy_comment); 490 pp_data[1] = p_dummy_comment; 491 492 if( pi_size[0] > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra, 493 pi_size, pp_data, 2 ) ) 494 { 495 es_fmt.i_extra = 0; 496 es_fmt.p_extra = NULL; 497 } 498 } 499 else if( cp->extradata_size > 0 ) 500 { 501 es_fmt.p_extra = malloc( i_extra ); 502 if( es_fmt.p_extra ) 503 { 504 es_fmt.i_extra = i_extra; 505 memcpy( es_fmt.p_extra, p_extra, i_extra ); 506 } 507 } 508 509 p_track->p_es = es_out_Add( p_demux->out, &es_fmt ); 510 if( p_track->p_es && (s->disposition & AV_DISPOSITION_DEFAULT) ) 511 es_out_Control( p_demux->out, ES_OUT_SET_ES_DEFAULT, p_track->p_es ); 512 513 msg_Dbg( p_demux, "adding es: %s codec = %4.4s (%d)", 514 psz_type, (char*)&fcc, cp->codec_id ); 515 } 516 es_format_Clean( &es_fmt ); 517 } 518 519 if( p_sys->ic->start_time != (int64_t)AV_NOPTS_VALUE ) 520 i_start_time = p_sys->ic->start_time * 1000000 / AV_TIME_BASE; 521 522 msg_Dbg( p_demux, "AVFormat(%s %s) supported stream", AVPROVIDER(LIBAVFORMAT), LIBAVFORMAT_IDENT ); 523 msg_Dbg( p_demux, " - format = %s (%s)", 524 p_sys->fmt->name, p_sys->fmt->long_name ); 525 msg_Dbg( p_demux, " - start time = %"PRId64, i_start_time ); 526 msg_Dbg( p_demux, " - duration = %"PRId64, 527 ( p_sys->ic->duration != (int64_t)AV_NOPTS_VALUE ) ? 528 p_sys->ic->duration * 1000000 / AV_TIME_BASE : -1 ); 529 530 if( p_sys->ic->nb_chapters > 0 ) 531 { 532 p_sys->p_title = vlc_input_title_New(); 533 p_sys->p_title->i_length = p_sys->ic->duration * 1000000 / AV_TIME_BASE; 534 } 535 536 for( unsigned i = 0; i < p_sys->ic->nb_chapters; i++ ) 537 { 538 seekpoint_t *s = vlc_seekpoint_New(); 539 540 AVDictionaryEntry *title = av_dict_get( p_sys->ic->metadata, "title", NULL, 0); 541 if( title && title->value ) 542 { 543 s->psz_name = strdup( title->value ); 544 EnsureUTF8( s->psz_name ); 545 msg_Dbg( p_demux, " - chapter %d: %s", i, s->psz_name ); 546 } 547 s->i_time_offset = p_sys->ic->chapters[i]->start * 1000000 * 548 p_sys->ic->chapters[i]->time_base.num / 549 p_sys->ic->chapters[i]->time_base.den - 550 (i_start_time != -1 ? i_start_time : 0 ); 551 TAB_APPEND( p_sys->p_title->i_seekpoint, p_sys->p_title->seekpoint, s ); 552 } 553 554 ResetTime( p_demux, 0 ); 555 return VLC_SUCCESS; 556 }
查看代码
3.2.4.3.1 调用(\src\input\stream.c)stream_Peek从stream层获取数据,用于分析输入的文件格式。
3.2.4.3.2 调用av_probe_input_format分析输入的文件格式。
3.2.4.3.3 设置demux_sys_t结构体部分变量的值。
3.2.4.3.4 调用avformat_alloc_context分配AVFormatContext结构体。
3.2.4.3.5 调用avio_alloc_context设置AVFormatContext结构体的AVIOContext类型成员pb,并设置read和seek方法指针。
3.2.4.3.6 调用avformat_open_input打开一个输入。
3.2.4.3.7 调用avformat_find_stream_info分析流信息,该方法通过读取数据初始化流以及流解码信息。
3.2.4.3.8 根据分析的流信息,设置(es_format)es_fmt变量,并调用(\include\vlc_es_out.h)es_out_Add。
3.2.4.3.9 实际调用EsOutAdd(\src\input\es_out.c),添加一个es_out,有几个流就做几次es_out_Add操作,比如该输入中有一个视频流和一个音频流,则作两次es_out_Add操作。
3.2.5、设置record相关。
3.2.6、调用(\include\vlc_demux.h)demux_Control(DEMUX_GET_PTS_DELAY)-->(\src\input\demux.c)demux_vaControl设置demux pts delay。
3.2.7、调用demux_Control(DEMUX_GET_FPS)设置fps。
3.3、调用demux_Control(DEMUX_GET_LENGTH)获取输入的长度。
3.4、调用StartTitle显示标题。
3.5、调用LoadSubtitles加载字幕。
3.6、调用LoadSlaves。
3.7、调用(\src\input\input.c)InitPrograms,设置es_out和decoder相关。
3.7.1、调用UpdatePtsDelay计算正确的pts_delay值。
3.7.2、调用es_out_SetMode,设置es_out的mode为ES_OUT_MODE_AUTO。
3.7.3、调用demux_Control(DEMUX_SET_GROUP)。
3.8、在3.7.2中实际调用EsOutControlLocked进入case ES_OUT_SET_MODE分支。
\src\input\es_out.h
1 static inline int es_out_vaControl( es_out_t *out, int i_query, va_list args ) 2 { 3 return out->pf_control( out, i_query, args ); 4 } 5 6 static inline int es_out_Control( es_out_t *out, int i_query, ... ) 7 { 8 va_list args; 9 int i_result; 10 11 va_start( args, i_query ); 12 i_result = es_out_vaControl( out, i_query, args ); 13 va_end( args ); 14 return i_result; 15 }
View Code
\src\input\es_out.c
1 static int EsOutControlLocked( es_out_t *out, int i_query, va_list args ) 2 { 3 es_out_sys_t *p_sys = out->p_sys; 4 5 switch( i_query ) 6 { 7 8 ...... 9 10 case ES_OUT_SET_MODE: 11 { 12 const int i_mode = va_arg( args, int ); 13 assert( i_mode == ES_OUT_MODE_NONE || i_mode == ES_OUT_MODE_ALL || 14 i_mode == ES_OUT_MODE_AUTO || i_mode == ES_OUT_MODE_PARTIAL || 15 i_mode == ES_OUT_MODE_END ); 16 17 if( i_mode != ES_OUT_MODE_NONE && !p_sys->b_active && p_sys->i_es > 0 ) 18 { 19 /* XXX Terminate vout if there are tracks but no video one. 20 * This one is not mandatory but is he earliest place where it 21 * can be done */ 22 int i; 23 for( i = 0; i < p_sys->i_es; i++ ) 24 { 25 es_out_id_t *p_es = p_sys->es[i]; 26 if( p_es->fmt.i_cat == VIDEO_ES ) 27 break; 28 } 29 if( i >= p_sys->i_es ) 30 input_resource_TerminateVout( input_priv(p_sys->p_input)->p_resource ); 31 } 32 p_sys->b_active = i_mode != ES_OUT_MODE_NONE; 33 p_sys->i_mode = i_mode; 34 35 /* Reapply policy mode */ 36 for( int i = 0; i < p_sys->i_es; i++ ) 37 { 38 if( EsIsSelected( p_sys->es[i] ) ) 39 EsUnselect( out, p_sys->es[i], 40 p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); 41 } 42 for( int i = 0; i < p_sys->i_es; i++ ) 43 EsOutSelect( out, p_sys->es[i], false ); 44 if( i_mode == ES_OUT_MODE_END ) 45 EsOutTerminate( out ); 46 return VLC_SUCCESS; 47 } 48 49 ...... 50 51 default: 52 msg_Err( p_sys->p_input, "unknown query 0x%x in %s", i_query, 53 __func__ ); 54 return VLC_EGENERIC; 55 } 56 } 57 58 59 static int EsOutControl( es_out_t *out, int i_query, va_list args ) 60 { 61 es_out_sys_t *p_sys = out->p_sys; 62 int i_ret; 63 64 vlc_mutex_lock( &p_sys->lock ); 65 i_ret = EsOutControlLocked( out, i_query, args ); 66 vlc_mutex_unlock( &p_sys->lock ); 67 68 return i_ret; 69 }
View Code
3.8.1、设置es_out_sys_t 的b_active和i_mode。
3.8.2、调用EsOutSelect方法,根据指定模块选择一个es_out。
3.8.3、在EsOutSelect方法中进入ES_OUT_MODE_AUTO分支,进一步调用EsSelect方法,再进一步调用EsCreateDecoder方法创建decoder。
1 static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force ) 2 { 3 es_out_sys_t *p_sys = out->p_sys; 4 es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, es->fmt.i_cat ); 5 6 if( !p_sys->b_active || 7 ( !b_force && es->fmt.i_priority < ES_PRIORITY_SELECTABLE_MIN ) ) 8 { 9 return; 10 } 11 12 bool b_auto_unselect = p_esprops && p_sys->i_mode == ES_OUT_MODE_AUTO && 13 p_esprops->e_policy == ES_OUT_ES_POLICY_EXCLUSIVE && 14 p_esprops->p_main_es && p_esprops->p_main_es != es; 15 16 ...... 17 18 else if( p_sys->i_mode == ES_OUT_MODE_AUTO ) 19 { 20 const es_out_id_t *wanted_es = NULL; 21 22 if( es->p_pgrm != p_sys->p_pgrm || !p_esprops ) 23 return; 24 25 /* user designated by ID ES have higher prio than everything */ 26 if ( p_esprops->i_id >= 0 ) 27 { 28 if( es->i_id == p_esprops->i_id ) 29 wanted_es = es; 30 } 31 /* then per pos */ 32 else if( p_esprops->i_channel >= 0 ) 33 { 34 if( p_esprops->i_channel == es->i_channel ) 35 wanted_es = es; 36 } 37 else if( p_esprops->ppsz_language ) 38 { 39 /* If not deactivated */ 40 const int i_stop_idx = LanguageArrayIndex( p_esprops->ppsz_language, "none" ); 41 { 42 int current_es_idx = ( p_esprops->p_main_es == NULL ) ? -1 : 43 LanguageArrayIndex( p_esprops->ppsz_language, 44 p_esprops->p_main_es->psz_language_code ); 45 int es_idx = LanguageArrayIndex( p_esprops->ppsz_language, 46 es->psz_language_code ); 47 if( es_idx >= 0 && (i_stop_idx < 0 || i_stop_idx > es_idx) ) 48 { 49 /* Only select the language if it's in the list */ 50 if( p_esprops->p_main_es == NULL || 51 current_es_idx < 0 || /* current es was not selected by lang prefs */ 52 es_idx < current_es_idx || /* current es has lower lang prio */ 53 ( es_idx == current_es_idx && /* lang is same, but es has higher prio */ 54 p_esprops->p_main_es->fmt.i_priority < es->fmt.i_priority ) ) 55 { 56 wanted_es = es; 57 } 58 } 59 /* We did not find a language matching our prefs */ 60 else if( i_stop_idx < 0 ) /* If not fallback disabled by 'none' */ 61 { 62 /* Select if asked by demuxer */ 63 if( current_es_idx < 0 ) /* No es is currently selected by lang pref */ 64 { 65 /* If demux has specified a track */ 66 if( p_esprops->i_demux_id >= 0 && es->i_id == p_esprops->i_demux_id ) 67 { 68 wanted_es = es; 69 } 70 /* Otherwise, fallback by priority */ 71 else if( p_esprops->p_main_es == NULL || 72 es->fmt.i_priority > p_esprops->p_main_es->fmt.i_priority ) 73 { 74 if( p_esprops->b_autoselect ) 75 wanted_es = es; 76 } 77 } 78 } 79 } 80 81 } 82 /* If there is no user preference, select the default subtitle 83 * or adapt by ES priority */ 84 else if( p_esprops->i_demux_id >= 0 && es->i_id == p_esprops->i_demux_id ) 85 { 86 wanted_es = es; 87 } 88 else if( p_esprops->p_main_es == NULL || 89 es->fmt.i_priority > p_esprops->p_main_es->fmt.i_priority ) 90 { 91 if( p_esprops->b_autoselect ) 92 wanted_es = es; 93 } 94 95 if( wanted_es == es && !EsIsSelected( es ) ) 96 { 97 if( b_auto_unselect ) 98 EsUnselect( out, p_esprops->p_main_es, false ); 99 100 EsSelect( out, es ); 101 } 102 } 103 104 ...... 105 } 106 107 /* FIXME TODO handle priority here */ 108 if( p_esprops && p_sys->i_mode == ES_OUT_MODE_AUTO && EsIsSelected( es ) ) 109 p_esprops->p_main_es = es; 110 }
View Code
1 static void EsSelect( es_out_t *out, es_out_id_t *es ) 2 { 3 es_out_sys_t *p_sys = out->p_sys; 4 input_thread_t *p_input = p_sys->p_input; 5 6 if( EsIsSelected( es ) ) 7 { 8 msg_Warn( p_input, "ES 0x%x is already selected", es->i_id ); 9 return; 10 } 11 12 if( es->p_master ) 13 { 14 int i_channel; 15 if( !es->p_master->p_dec ) 16 return; 17 18 i_channel = EsOutGetClosedCaptionsChannel( &es->fmt ); 19 20 if( i_channel == -1 || 21 input_DecoderSetCcState( es->p_master->p_dec, es->fmt.i_codec, 22 i_channel, true ) ) 23 return; 24 } 25 else 26 { 27 const bool b_sout = input_priv(p_input)->p_sout != NULL; 28 if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES ) 29 { 30 if( !var_GetBool( p_input, b_sout ? "sout-video" : "video" ) ) 31 { 32 msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x", 33 es->i_id ); 34 return; 35 } 36 } 37 else if( es->fmt.i_cat == AUDIO_ES ) 38 { 39 if( !var_GetBool( p_input, b_sout ? "sout-audio" : "audio" ) ) 40 { 41 msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x", 42 es->i_id ); 43 return; 44 } 45 } 46 if( es->fmt.i_cat == SPU_ES ) 47 { 48 if( !var_GetBool( p_input, b_sout ? "sout-spu" : "spu" ) ) 49 { 50 msg_Dbg( p_input, "spu is disabled, not selecting ES 0x%x", 51 es->i_id ); 52 return; 53 } 54 } 55 56 EsCreateDecoder( out, es ); 57 58 if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm ) 59 return; 60 } 61 62 /* Mark it as selected */ 63 input_SendEventEsSelect( p_input, es->fmt.i_cat, es->i_id ); 64 input_SendEventTeletextSelect( p_input, EsFmtIsTeletext( &es->fmt ) ? es->i_id : -1 ); 65 }
View Code
1 static void EsCreateDecoder( es_out_t *out, es_out_id_t *p_es ) 2 { 3 es_out_sys_t *p_sys = out->p_sys; 4 input_thread_t *p_input = p_sys->p_input; 5 6 p_es->p_dec = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, input_priv(p_input)->p_sout ); 7 if( p_es->p_dec ) 8 { 9 if( p_sys->b_buffering ) 10 input_DecoderStartWait( p_es->p_dec ); 11 12 if( !p_es->p_master && p_sys->p_sout_record ) 13 { 14 p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record ); 15 if( p_es->p_dec_record && p_sys->b_buffering ) 16 input_DecoderStartWait( p_es->p_dec_record ); 17 } 18 } 19 20 EsOutDecoderChangeDelay( out, p_es ); 21 }
View Code
3.8.3.1、调用input_DecoderNew创建一个新的decoder。
\src\input\decoder.c
1 decoder_t *input_DecoderNew( input_thread_t *p_input, 2 es_format_t *fmt, input_clock_t *p_clock, 3 sout_instance_t *p_sout ) 4 { 5 return decoder_New( VLC_OBJECT(p_input), p_input, fmt, p_clock, 6 input_priv(p_input)->p_resource, p_sout ); 7 }
View Code
3.8.3.2、如果需要缓存,调用input_DecoderStartWait发送信号,开始线程等待。
1 void input_DecoderStartWait( decoder_t *p_dec ) 2 { 3 decoder_owner_sys_t *p_owner = p_dec->p_owner; 4 5 assert( !p_owner->b_waiting ); 6 7 vlc_mutex_lock( &p_owner->lock ); 8 p_owner->b_first = true; 9 p_owner->b_has_data = false; 10 p_owner->b_waiting = true; 11 vlc_cond_signal( &p_owner->wait_request ); 12 vlc_mutex_unlock( &p_owner->lock ); 13 }
View Code
3.8.3.3、调用EsOutDecoderChangeDelay设置decode delay。
1 static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es ) 2 { 3 es_out_sys_t *p_sys = out->p_sys; 4 5 mtime_t i_delay = 0; 6 if( p_es->fmt.i_cat == AUDIO_ES ) 7 i_delay = p_sys->i_audio_delay; 8 else if( p_es->fmt.i_cat == SPU_ES ) 9 i_delay = p_sys->i_spu_delay; 10 else 11 return; 12 13 if( p_es->p_dec ) 14 input_DecoderChangeDelay( p_es->p_dec, i_delay ); 15 if( p_es->p_dec_record ) 16 input_DecoderChangeDelay( p_es->p_dec_record, i_delay ); 17 }
View Code
3.9、在3.8.3.1中(\src\input\decoder.c)input_DecoderNew方法进入decoder_New方法。
3.9.1、调用CreateDecoder创建decoder配置结构体。
3.9.1.1、调用vlc_custom_create创建一个vlc object(decoder_t)。
3.9.1.2、新建decode fifo。
3.9.1.3、调用module_need加载适配的解码模块。
3.9.4.1、调用decode模块的OpenDecoder方法,以\modules\codec\avcodec\avcodec.c模块为例。
3.9.4.2、调用GetFfmpegCodec方法。
3.9.4.3、调用vlc_init_avcodec方法初始化解码环境。
3.9.4.4、调用avcodec_find_decoder设置AVCodec。
3.9.4.5、调用avcodec_alloc_context3分配一个AVCodecContext。
3.9.4.6、调用Init*Dec系列初始化解码环境。
3.9.1.4、初始化decoder_t结构体其他成员。
3.9.2、调用vlc_clone创建解码线程DecoderThread。
3.10、在3.9.1.5中,以InitVideoDec为例。
3.10.1、为decoder_sys_t结构分配内存。
3.10.2、设置相关回调方法
3.10.3、设置解码线程类型。
3.10.4、调用ffmpeg_InitCodec初始化extra data相关数据。
3.10.5、调用OpenVideoCodec方法,设置解码的长宽及采用率,进一步调用avcodec_open2打开codec。
3.11、根据需要,设置线程优先级。
3.12、设置meta相关。
3.13、初始化完成,设置该input的状态为PLAYING_S。
以上都是视频播放前的一些处理
4)播放视频
1 int libvlc_media_player_play( libvlc_media_player_t *p_mi ) 2 { 3 lock_input( p_mi ); 4 5 input_thread_t *p_input_thread = p_mi->input.p_thread; 6 if( p_input_thread ) 7 { 8 /* A thread already exists, send it a play message */ 9 input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S ); 10 unlock_input( p_mi ); 11 return 0; 12 } 13 14 /* Ignore previous exception */ 15 lock(p_mi); 16 17 if( !p_mi->p_md ) 18 { 19 unlock(p_mi); 20 unlock_input( p_mi ); 21 libvlc_printerr( "No associated media descriptor" ); 22 return -1; 23 } 24 25 for( size_t i = 0; i < ARRAY_SIZE( p_mi->selected_es ); ++i ) 26 p_mi->selected_es[i] = ES_INIT; 27 28 if( p_mi->p_md->p_cookie_jar ) 29 { 30 vlc_value_t cookies = { .p_address = p_mi->p_md->p_cookie_jar }; 31 var_SetChecked( p_mi, "http-cookies", VLC_VAR_ADDRESS, cookies ); 32 } 33 34 media_attach_preparsed_event( p_mi->p_md ); 35 36 p_input_thread = input_Create( p_mi, p_mi->p_md->p_input_item, NULL, 37 p_mi->input.p_resource, 38 p_mi->input.p_renderer ); 39 unlock(p_mi); 40 if( !p_input_thread ) 41 { 42 unlock_input(p_mi); 43 media_detach_preparsed_event( p_mi->p_md ); 44 libvlc_printerr( "Not enough memory" ); 45 return -1; 46 } 47 48 var_AddCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi ); 49 var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi ); 50 var_AddCallback( p_input_thread, "program-scrambled", input_scrambled_changed, p_mi ); 51 var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi ); 52 add_es_callbacks( p_input_thread, p_mi ); 53 54 if( input_Start( p_input_thread ) ) 55 { 56 unlock_input(p_mi); 57 del_es_callbacks( p_input_thread, p_mi ); 58 var_DelCallback( p_input_thread, "intf-event", input_event_changed, p_mi ); 59 var_DelCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi ); 60 var_DelCallback( p_input_thread, "program-scrambled", input_scrambled_changed, p_mi ); 61 var_DelCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi ); 62 input_Close( p_input_thread ); 63 media_detach_preparsed_event( p_mi->p_md ); 64 libvlc_printerr( "Input initialization failure" ); 65 return -1; 66 } 67 p_mi->input.p_thread = p_input_thread; 68 unlock_input(p_mi); 69 return 0; 70 }
View Code
4.1 、播放会去判断是否第一次进来播放:判断input.p_thread是否为空,如果不为空,直接调用input_Control()播放视频。如果为空,则是第一次进来播放,那么将调用input_Create()函数,它实际是调用(input.c)input_thread_t *Create()函数,来创建一个input_thread_t的对象,并初始化其中的对象,值得注意的是priv->p_es_out_display= input_EsOutNew( p_input, p_input->p->i_rate );该p_es_out_display将在后面作为真正传入数据到decoder的容器。
4.2、调用var_AddCallback注册播放过程中一些必要的回调函数。然后调用(input.c)input_Start()开始播放。
4.3 input_Start()会新起一个线程,来处理轮循播放,原线程返回。子线程的入口在Run()。Run函数首先调用Init()函数初始化线程对象input_thread_t *p_input。在Init()函数中,会初始化跟视频相关的信息如视频meta, InitStatistics()会初始化统计数据等等,值得注意的是,会调用InputSourceNew()函数,在前面模块的加载中讲到,在该函数中会分析视频url,并加载http模块,开始下载数据。然后进入了无线循环模式,MainLoop函数。
4.4、在MainLoop函数中,每次循环都会首先会判断是否暂停了,如果没暂停且没有到视频末尾,就调用MainLoopDemux()函数进入解封装。
4.5、在MainLoopDemux()函数中调用demux_Demux(p_demux );开始解封装,其中p_demux的创建是在前面InputSourceNew中创建的, 相关初始化已在前头分析。
4.6、实际调用Demux方法(\module\demux\avformat\demux.c)。
4.6.1、调用av_read_frame读取一帧数据。
4.6.2、判断读取无误时,则为block_t结构分配内存,并将这一帧从AVPacket中拷贝至block_t结构中。
4.6.3、时间戳相关处理。
4.6.4、根据需要调用es_out_SetPCR设置PCR。
4.6.5、调用es_out_Send将这一帧数据发送给es_out。
4.6.6、调用av_packet_unref释放这一帧数据。
4.7、调用es_out_Send后,实际调用EsOutSend(\src\input\es_out.c)方法。
4.7.1、调用stats_Update更新相关状态。
4.7.2、设置预读相关,如果需要预读,并且到的数据的pts小于预读需要的时间,则设置BLOCK_FLAG_PREROLL标志位。
4.7.3、check for sout mode,具体有sync 和async mode。
4.7.4、如果设置record,将数据dup后送decoder。
4.7.5、调用input_DecoderDecode(\src\input\decoder.c)将block_t的数据送至decode fifo中。
4.7.5.1、判断控制速度线程等待相关信息。
4.7.5.2、如果decode fifo超过最大长度,则清空重置decode fifo。
4.7.5.3、调用vlc_fifo_QueueUnlocked将该block_t的数据压入decode fifo,并通知读取线程。
4.7.6、格式变化判断处理相关
4.7.7、字幕处理相关
4.8、续4.7.5进入decode read thread,即DecoderThread(src/input/decoder.c)。
4.8.1、调用vlc_fifo_QueueUnlocked方法,从decode fifo中获取数据。
4.8.2、基于某些条件,发送停止等待消息给其他线程。
4.8.3、调用DecoderProcess方法开始decode a block。
4.8.4、进一步调用DecoderDecode方法。
4.9、续4.8.4调用pf_decode,这里以avcodec模块的decoder为例,即DecodeVideo(\modules\codec\avcodec\video.c)方法,在该方法中,开始真正的解码。
4.9.1、DecodeVideo->DecodeBlock,如果在Demux中获取的流信息中包含新的extradata,并且原来的extradata数据为空,则调用ffmpeg_InitCodec初始化codec,如果avcodec_is_open(p_context)为true,则调用OpenVideoCodec重新打开codec。
4.9.2、调用av_init_packet初始化解码数据包。
4.9.3、调用avcodec_decode_video2解码数据。
4.9.4、调用av_free_packet释放内存。
4.9.5、计算pts值,返回解码后的数据。
4.9.6、如果opaque为空,则调用ffmpeg_NewPictBuf方法创建一个新的picture buffer。具体调用回调指针pf_vout_buffer_new指向的vout_new_buffer,进一步调用input_resource_RequestVout最终调用VoutCreate。
4.9.6.1、调用vlc_custom_create创建一个vlc object(vout_thread_t)。
4.9.6.2、调用spu_Create初始化sub picture unit。
4.9.6.3、调用vlc_clone创建一个output线程Thread(src/video_output/video_output.c)。
4.9.6.4、output线程循环调用vout_control_Pop,首次进入ThreadControl方法中,执行ThreadStart方向,创建picture fifo(p->decoder_fifo)。
4.10、 pf_decode_video返回后,解码后的数据保存在p_pic中,进一步调用DecoderPlayVideo方法,在该方法中调用vout_PutPicture将解码后的数据压入picture fifo中。
4.11、当picture fifo中有数据后,vout线程调用ThreadDisplayPicture中的ThreadDisplayPreparePicture方法。
4.11.1、调用picture_fifo_Pop从picture fifo中获取解码后的数据。
4.11.2、如果延迟太大,并且设置延迟丢帧,则丢掉该帧数据。
4.12、调用ThreadDisplayRenderPicture显示图像。
VLC源码地址:https://github.com/videolan/vlckit
编译VLC库:https://wiki.videolan.org/VLCKit/
下载VLC库:http://nightlies.videolan.org/build/iOS/
VLC帮助文档:https://www.videolan.org/developers/vlc/doc/doxygen/html/index.html
对VLC的流程分析,最后显示部分的分析还不足,另外很多细节尚未深入。
参考:
https://blog.csdn.net/hpb21/article/details/43271095
https://blog.csdn.net/baohonglai/article/details/46475141
转载于:https://www.cnblogs.com/goodjobxjp/p/10334864.html
VLCKit源码走读笔记相关推荐
- iOS WebviewJavascriptBridge 源码研读笔记
这两天接近元旦,事情稍微少些,有些时间,索性写点什么,就从最擅长的iOS混合开发写起了,由于iOS开发经验不到四年吧,期间还搞了一年半的前端,有些知识可能还是积累的不足,能力不足,水平有限,可能有谬误 ...
- Transformers包tokenizer.encode()方法源码阅读笔记
Transformers包tokenizer.encode()方法源码阅读笔记_天才小呵呵的博客-CSDN博客_tokenizer.encode
- 源码阅读笔记 BiLSTM+CRF做NER任务 流程图
源码阅读笔记 BiLSTM+CRF做NER任务(二) 源码地址:https://github.com/ZhixiuYe/NER-pytorch 本篇正式进入源码的阅读,按照流程顺序,一一解剖. 一.流 ...
- Apache Spark源码走读之16 -- spark repl实现详解
欢迎转载,转载请注明出处,徽沪一郎. 概要 之所以对spark shell的内部实现产生兴趣全部缘于好奇代码的编译加载过程,scala是需要编译才能执行的语言,但提供的scala repl可以实现代码 ...
- 代码分析:NASM源码阅读笔记
NASM源码阅读笔记 NASM(Netwide Assembler)的使用文档和代码间的注释相当齐全,这给阅读源码 提供了很大的方便.按作者的说法,这是一个模块化的,可重用的x86汇编器, 而且能够被 ...
- SharpDevelop源码分析笔记(一)
SharpDevelop自动命令启动UI部分(看SharpDevelop源码分析笔记随想) 参见:Fbt2008的大作 SharpDevelop源码分析笔记(一) 源文档 <http://ww ...
- batch spring 重复执行_Spring源码高级笔记之——Spring AOP应用
Spring AOP应用 AOP本质:在不改变原有业务逻辑的情况下增强横切逻辑,横切逻辑代码往往是权限校验代码.日志代码.事务控制代码.性能监控代码. 第1节AOP相关术语 1.1业务主线 在讲解AO ...
- CI框架源码阅读笔记4 引导文件CodeIgniter.php
到了这里,终于进入CI框架的核心了.既然是"引导"文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http:// ...
- Yii源码阅读笔记 - 日志组件
2015-03-09 一 By youngsterxyf 使用 Yii框架为开发者提供两个静态方法进行日志记录: Yii::log($message, $level, $category); Yii: ...
最新文章
- 浏览器前进后退对下拉框数据的丢失(省市联动实现和例子)
- 该如何继续走下去。。
- javaScript不是java脚本
- dll文件的c++制作dll文件的c++制作
- [css] CSS的伪类和伪对象有什么不同?
- Word2Vec学习笔记(一)
- AMD宣布350亿美元收购赛灵思,CPU、GPU、FPGA全凑齐
- APP违法使用个人信息?不用怕,华为云VSS为你保驾护航
- 日本新研究:将光伏组件高温高湿试验速度提高70倍
- java邮箱正则表达式_Java正则表达式详解
- 特别实用的几种SQL语句送给大家,让你的SQL高大上!
- sklearn聚类模型评估代码_sklearn之聚类评估指标---轮廓系数
- fltk和glog在mac下的安装与编译
- 使用PEG估值法简单选股(1)
- 计算机文件丢失不能正常启动,电脑说文件丢失或损坏开不了机怎么办?
- 【软路由安装(PVE+ikuai)】
- LeetCode1153 字符串转化
- 树莓派耳机接口有电流声、杂音
- 透过协议看PCIe ASPM L1.2
- 28天打造专业红客(十一)
热门文章
- 拨号上网“769”错误的原因之一
- qt5 ffmpeg linux,C++实战ffmpeg和QT5跨平台视频播放器系列套餐
- 如何阻止华为网盘插件修改搜狗浏览器主页
- 死亡空间2显卡测试软件,恐怖窒息体验 中低端显卡闯荡《死亡空间2》(4)
- 碳化硅器件在新能源市场的应用
- WPS Office之PPT基础入门视频课程-陈慧-专题视频课程
- 初装缺陷跟踪工具螳螂Mantis1.1.1
- 基于JAVA物料追溯系统计算机毕业设计源码+数据库+lw文档+系统+部署
- 小学生打扫计算机教室的简报,小学生二年级看图写话-打扫教室
- 戴尔灵越7572重装Windows 提示:Windows无法安装到这个磁盘,选中的磁盘采用gpt分区形式