基础概念:

  视频播放流程:读取原始数据->解复用->解码->显示

  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源码走读笔记相关推荐

  1. iOS WebviewJavascriptBridge 源码研读笔记

    这两天接近元旦,事情稍微少些,有些时间,索性写点什么,就从最擅长的iOS混合开发写起了,由于iOS开发经验不到四年吧,期间还搞了一年半的前端,有些知识可能还是积累的不足,能力不足,水平有限,可能有谬误 ...

  2. Transformers包tokenizer.encode()方法源码阅读笔记

    Transformers包tokenizer.encode()方法源码阅读笔记_天才小呵呵的博客-CSDN博客_tokenizer.encode

  3. 源码阅读笔记 BiLSTM+CRF做NER任务 流程图

    源码阅读笔记 BiLSTM+CRF做NER任务(二) 源码地址:https://github.com/ZhixiuYe/NER-pytorch 本篇正式进入源码的阅读,按照流程顺序,一一解剖. 一.流 ...

  4. Apache Spark源码走读之16 -- spark repl实现详解

    欢迎转载,转载请注明出处,徽沪一郎. 概要 之所以对spark shell的内部实现产生兴趣全部缘于好奇代码的编译加载过程,scala是需要编译才能执行的语言,但提供的scala repl可以实现代码 ...

  5. 代码分析:NASM源码阅读笔记

    NASM源码阅读笔记 NASM(Netwide Assembler)的使用文档和代码间的注释相当齐全,这给阅读源码 提供了很大的方便.按作者的说法,这是一个模块化的,可重用的x86汇编器, 而且能够被 ...

  6. SharpDevelop源码分析笔记(一)

    SharpDevelop自动命令启动UI部分(看SharpDevelop源码分析笔记随想) 参见:Fbt2008的大作  SharpDevelop源码分析笔记(一) 源文档 <http://ww ...

  7. batch spring 重复执行_Spring源码高级笔记之——Spring AOP应用

    Spring AOP应用 AOP本质:在不改变原有业务逻辑的情况下增强横切逻辑,横切逻辑代码往往是权限校验代码.日志代码.事务控制代码.性能监控代码. 第1节AOP相关术语 1.1业务主线 在讲解AO ...

  8. CI框架源码阅读笔记4 引导文件CodeIgniter.php

    到了这里,终于进入CI框架的核心了.既然是"引导"文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http:// ...

  9. Yii源码阅读笔记 - 日志组件

    2015-03-09 一 By youngsterxyf 使用 Yii框架为开发者提供两个静态方法进行日志记录: Yii::log($message, $level, $category); Yii: ...

最新文章

  1. 浏览器前进后退对下拉框数据的丢失(省市联动实现和例子)
  2. 该如何继续走下去。。
  3. javaScript不是java脚本
  4. dll文件的c++制作dll文件的c++制作
  5. [css] CSS的伪类和伪对象有什么不同?
  6. Word2Vec学习笔记(一)
  7. AMD宣布350亿美元收购赛灵思,CPU、GPU、FPGA全凑齐
  8. APP违法使用个人信息?不用怕,华为云VSS为你保驾护航
  9. 日本新研究:将光伏组件高温高湿试验速度提高70倍
  10. java邮箱正则表达式_Java正则表达式详解
  11. 特别实用的几种SQL语句送给大家,让你的SQL高大上!
  12. sklearn聚类模型评估代码_sklearn之聚类评估指标---轮廓系数
  13. fltk和glog在mac下的安装与编译
  14. 使用PEG估值法简单选股(1)
  15. 计算机文件丢失不能正常启动,电脑说文件丢失或损坏开不了机怎么办?
  16. 【软路由安装(PVE+ikuai)】
  17. LeetCode1153 字符串转化
  18. 树莓派耳机接口有电流声、杂音
  19. 透过协议看PCIe ASPM L1.2
  20. 28天打造专业红客(十一)

热门文章

  1. 拨号上网“769”错误的原因之一
  2. qt5 ffmpeg linux,C++实战ffmpeg和QT5跨平台视频播放器系列套餐
  3. 如何阻止华为网盘插件修改搜狗浏览器主页
  4. 死亡空间2显卡测试软件,恐怖窒息体验 中低端显卡闯荡《死亡空间2》(4)
  5. 碳化硅器件在新能源市场的应用
  6. WPS Office之PPT基础入门视频课程-陈慧-专题视频课程
  7. 初装缺陷跟踪工具螳螂Mantis1.1.1
  8. 基于JAVA物料追溯系统计算机毕业设计源码+数据库+lw文档+系统+部署
  9. 小学生打扫计算机教室的简报,小学生二年级看图写话-打扫教室
  10. 戴尔灵越7572重装Windows 提示:Windows无法安装到这个磁盘,选中的磁盘采用gpt分区形式