VLCKit源码走读笔记

news/2024/7/10 22:23:02 标签: ffmpeg

基础概念:

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

  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


http://www.niftyadmin.cn/n/1439228.html

相关文章

R语言中对文本数据进行主题模型topic modeling分析

主题建模 在文本挖掘中&#xff0c;我们经常收集一些文档集合&#xff0c;例如博客文章或新闻文章&#xff0c;我们希望将其分成自然组&#xff0c;以便我们可以分别理解它们。主题建模是对这些文档进行无监督分类的一种方法&#xff0c;类似于对数字数据进行聚类&#xff0c;即…

使用JBuilder和WTK2.2搭建MIDP1.0和MIDP2.0开发环境(转)

我们知道&#xff0c;WTK最新的是2.2版本。我很喜欢使用这个版本&#xff0c;为什么呢&#xff1f; 1、包含了JSR-184 Mobile 3D API for J2ME?&#xff0c;并有一个DEMO&#xff0c;让我们领略了手机3D游戏的风采。 2、默认的模拟器够大&#xff0c;用起来非常的舒服 3、同时…

谈谈百度搜索解封经验与技巧(转)

admin5站长网被百度解封&#xff0c;迎来了广大站长的热烈关注。qq上和各大站长社区都在讨论admin5为什么能被解封。各种猜测&#xff0c;各种询问都有。也有不少人直接了当的问我究竟花了多少钱。究竟找了谁。给我1万要我帮他的也去去解封一下&#xff0c;无语&#xff0c;在很…

3GPP R4在3G试验网中的应用和相关分析(转)

下一代网络的演进主要按照两条路线来发展&#xff1a;第一条路线主要体现为传统电信业务从专用TDM承载向统一共享式IP/ATM多业务网络承载的转移&#xff0c;在维持用户接入方式不变的前提下借助接入网关完成网络层业务传输的分组化。原电路交换网络设备被划分为物理上独立的控制…

esp32(M5STACK)在线体验(Ubuntu)

我们往m5stack烧录的固件是可以在线编程的 具体使用方法可以参考 https://github.com/m5stack/M5Cloud/blob/master/README_CN.md 下面来实际体验一下我们先用手机连接热点接下来按照屏幕上的提示&#xff0c;发送SSID与密码在PC上访问网站http://io.m5stack.com 登录账号与密码…

集群通信走向误区(转)

移动通信从应用方面划分为公众移动通信网和专用移动通信网。公用移动通信网俗称移动电话&#xff0c;专用移动通信网俗称集群电话。在专用移动通信网中又分为共同调度集群移动通信网和专用集群通信网&#xff0c;由于应用范围的不同&#xff0c;要求的网络结构&#xff0c;技术…

Apache beam中的便携式有状态大数据处理

Apache beam中的便携式有状态大数据处理 目标&#xff1a; 什么是 apache beam&#xff1f;状态计时器例子&小demo一、什么是 apache beam&#xff1f; 上面两个图片一个是正面切图&#xff0c;一个是横向切图&#xff1b; 这里只是大数据对于批量处理和流处理的一些生态圈…

casbin的分析

casbin的分析 问题 一般的项目中&#xff0c;都会有权限认证模块&#xff0c;用来控制不同的角色&#xff0c;可以访问的功能。比较出名的权限控制模型有ACL和RABC。如果每个项目中&#xff0c;都重新实现权限控制模块&#xff0c;这样操作会比较繁琐&#xff0c;希望有一个统一…