經過ndk-gdb跟蹤調試vlc-android來分析從鏈接到RTSP服務器並接收到音視頻數據包後的處理過程。android
首先,從前面的文章有分析過vlc-android的處理過程經過線程函數Run()(Src/input/input.c)來處理的,代碼以下:服務器
- static void *Run( void *obj )
- {
- input_thread_t *p_input = (input_thread_t *)obj;
- const int canc = vlc_savecancel();
-
- if( Init( p_input ) )
- goto exit;
-
- MainLoop( p_input, true );
-
-
- End( p_input );
-
- exit:
-
- vlc_mutex_lock( &p_input->p->lock_control );
- const bool b_abort = p_input->p->b_abort;
- vlc_mutex_unlock( &p_input->p->lock_control );
-
- if( b_abort )
- input_SendEventAbort( p_input );
- input_SendEventDead( p_input );
-
- vlc_restorecancel( canc );
- return NULL;
- }
而Init()函數中關於live555鏈接服務器的處理在前面的一篇文章中已經初略分析,這裏咱們主要從MainLoop()函數入手,因爲代碼量過大(後續分篇補上),暫時僅分析關於經過live555接收數據包到發出信號通知解碼器能夠進行解碼的過程。多線程
下面爲MainLoop()函數中的一段代碼socket
- if( !b_paused )
- {
- if( !p_input->p->input.b_eof )
- {
- MainLoopDemux( p_input, &b_force_update, &b_demux_polled, i_start_mdate );
-
- i_wakeup = es_out_GetWakeup( p_input->p->p_es_out );
- }
- else if( !es_out_GetEmpty( p_input->p->p_es_out ) )
- {
- msg_Dbg( p_input, "waiting decoder fifos to empty" );
- i_wakeup = mdate() + INPUT_IDLE_SLEEP;
- }
- ...
- ...
- ...
從上面代碼可知將會調用MainLoopDemux(),下面截取該函數中的一段代碼ide
- if( ( p_input->p->i_stop > 0 && p_input->p->i_time >= p_input->p->i_stop ) ||
- ( p_input->p->i_run > 0 && i_start_mdate+p_input->p->i_run < mdate() ) )
- i_ret = 0;
- else
- i_ret = demux_Demux( p_input->p->input.p_demux );
- ...
- ...
- ...
繼續調用demux_Demux()(src/input/Demux.h)函數
- static inline int demux_Demux( demux_t *p_demux )
- {
- if( !p_demux->pf_demux )
- return 1;
-
- return p_demux->pf_demux( p_demux );
- }
這裏的p_demux->pf_demux()所指向的函數是經過live555.cpp中的open()函數來指定,下面爲一段open()函數的截取代碼:oop
- ...
- ...
- p_demux->pf_demux = Demux;
- p_demux->pf_control= Control;
- ...
- ...
即指向live555.cpp文件中的Demux()函數,從上面的分析可知最終調用了這裏的Demux()函數,這裏截取該函數的一段代碼以下:ui
- ...
- ...
- p_sys->event_data = 0;
- for( i = 0; i < p_sys->i_track; i++ )
- {
- live_track_t *tk = p_sys->track[i];
-
- if( tk->waiting == 0 )
- {
- tk->waiting = 1;
- tk->sub->readSource()->getNextFrame( tk->p_buffer, tk->i_buffer,
- StreamRead, tk, StreamClose, tk );
- }
- }
- ...
- ...
經過前文的分析,咱們知道這裏的tk->p_buffer即爲咱們須要的待解碼數據,而對於該數據的處理咱們繼續跟蹤,由前文分析,接下來將會執行StreamRead()函數,以下:idea
- ...
- ...
- if( tk->fmt.i_codec == VLC_CODEC_AMR_NB ||
- tk->fmt.i_codec == VLC_CODEC_AMR_WB )
- {
- AMRAudioSource *amrSource = (AMRAudioSource*)tk->sub->readSource();
-
- p_block = block_New( p_demux, i_size + 1 );
- p_block->p_buffer[0] = amrSource->lastFrameHeader();
- memcpy( p_block->p_buffer + 1, tk->p_buffer, i_size );
- }
- else if( tk->fmt.i_codec == VLC_CODEC_H261 )
- {
- H261VideoRTPSource *h261Source = (H261VideoRTPSource*)tk->sub->rtpSource();
- uint32_t header = h261Source->lastSpecialHeader();
- p_block = block_New( p_demux, i_size + 4 );
- memcpy( p_block->p_buffer, &header, 4 );
- memcpy( p_block->p_buffer + 4, tk->p_buffer, i_size );
-
- if( tk->sub->rtpSource()->curPacketMarkerBit() )
- p_block->i_flags |= BLOCK_FLAG_END_OF_FRAME;
- }
- else if( tk->fmt.i_codec == VLC_CODEC_H264 )
- {
- if( (tk->p_buffer[0] & 0x1f) >= 24 )
- msg_Warn( p_demux, "unsupported NAL type for H264" );
-
-
- p_block = block_New( p_demux, i_size + 4 );
- p_block->p_buffer[0] = 0x00;
- p_block->p_buffer[1] = 0x00;
- p_block->p_buffer[2] = 0x00;
- p_block->p_buffer[3] = 0x01;
- memcpy( &p_block->p_buffer[4], tk->p_buffer, i_size );
- }
- else if( tk->b_asf )
- {
- p_block = StreamParseAsf( p_demux, tk,
- tk->sub->rtpSource()->curPacketMarkerBit(),
- tk->p_buffer, i_size );
- }
- else
- {
- p_block = block_New( p_demux, i_size );
- memcpy( p_block->p_buffer, tk->p_buffer, i_size );
- }
- ...
- ...
從上面的部分代碼可知,這裏根據不一樣的格式進行不一樣的處理,但都會調用memcpy()函數將上面獲取的tk->p_buffer數據複製到p_block->p_buffer中去,這樣咱們就保存下來了經過live555從socket中讀取的數據,繼續下面的代碼:spa
- if( p_block )
- {
- if( !tk->b_muxed && !tk->b_asf )
- {
- if( i_pts != tk->i_pts )
- p_block->i_pts = VLC_TS_0 + i_pts;
-
- p_block->i_dts = ( tk->fmt.i_codec == VLC_CODEC_MPGV ) ? VLC_TS_INVALID : (VLC_TS_0 + i_pts);
- }
-
- if( tk->b_muxed )
- stream_DemuxSend( tk->p_out_muxed, p_block );
- else if( tk->b_asf )
- stream_DemuxSend( p_sys->p_out_asf, p_block );
- else
- es_out_Send( p_demux->out, tk->p_es, p_block );
- }
接下來會調用es_out_Send()(vlc/include/Vlc_es_out.h)函數
- static inline int es_out_Send( es_out_t *out, es_out_id_t *id,
- block_t *p_block )
- {
- return out->pf_send( out, id, p_block );
- }
經過單步跟蹤,這裏out->pf_send()所指向的正是src/input/es_out_timeshift.c文件中的Send()函數
- static int Send( es_out_t *p_out, es_out_id_t *p_es, block_t *p_block )
- {
- es_out_sys_t *p_sys = p_out->p_sys;
- ts_cmd_t cmd;
- int i_ret = VLC_SUCCESS;
-
- vlc_mutex_lock( &p_sys->lock );
-
- TsAutoStop( p_out );
-
- CmdInitSend( &cmd, p_es, p_block );
- if( p_sys->b_delayed )
- TsPushCmd( p_sys->p_ts, &cmd );
- else
- i_ret = CmdExecuteSend( p_sys->p_out, &cmd) ;
-
- vlc_mutex_unlock( &p_sys->lock );
-
- return i_ret;
- }
繼續跟蹤,先執行CmdInitSend()將p_block數據保存下來到cmd中,接下來將會執行CmdExcuteSend()函數
- static int CmdExecuteSend( es_out_t *p_out, ts_cmd_t *p_cmd )
- {
- block_t *p_block = p_cmd->u.send.p_block;
-
- p_cmd->u.send.p_block = NULL;
-
- if( p_block )
- {
- if( p_cmd->u.send.p_es->p_es )
- return es_out_Send( p_out, p_cmd->u.send.p_es->p_es, p_block );
- block_Release( p_block );
- }
- return VLC_EGENERIC;
- }
繼續執行es_out_Send()(vlc/include/Vlc_es_out.h)函數
- static inline int es_out_Send( es_out_t *out, es_out_id_t *id,
- block_t *p_block )
- {
- return out->pf_send( out, id, p_block );
- }
繼續跟蹤,這裏的out->pf_send()函數指向src/input/es_out.c文件中的EsOutSend()函數,截取該函數的一段代碼:
- ...
- ...
-
- if( es->p_dec_record )
- {
- block_t *p_dup = block_Duplicate( p_block );
- if( p_dup )
- input_DecoderDecode( es->p_dec_record, p_dup,
- p_input->p->b_out_pace_control );
- }
- input_DecoderDecode( es->p_dec, p_block,
- p_input->p->b_out_pace_control );
- ...
- ...
這裏將會調用第二個input_DecoderDecode()(src/input/Decoder.c)函數,以下:
- void input_DecoderDecode( decoder_t *p_dec, block_t *p_block, bool b_do_pace )
- {
- decoder_owner_sys_t *p_owner = p_dec->p_owner;
-
- if( b_do_pace )
- {
-
- if( !p_owner->b_buffering )
- block_FifoPace( p_owner->p_fifo, 10, SIZE_MAX );
- }
- #ifdef __arm__
- else if( block_FifoSize( p_owner->p_fifo ) > 50*1024*1024
- #else
- else if( block_FifoSize( p_owner->p_fifo ) > 400*1024*1024
- #endif
- {
-
- msg_Warn( p_dec, "decoder/packetizer fifo full (data not "
- "consumed quickly enough), resetting fifo!" );
- block_FifoEmpty( p_owner->p_fifo );
- }
-
- block_FifoPut( p_owner->p_fifo, p_block );
- }
最後,調用src/misc/block.c文件中的block_FifoPut()函數,以下:
- size_t block_FifoPut( block_fifo_t *p_fifo, block_t *p_block )
- {
- size_t i_size = 0, i_depth = 0;
- block_t *p_last;
-
- if (p_block == NULL)
- return 0;
- for (p_last = p_block; ; p_last = p_last->p_next)
- {
- i_size += p_last->i_buffer;
- i_depth++;
- if (!p_last->p_next)
- break;
- }
-
- vlc_mutex_lock (&p_fifo->lock);
- *p_fifo->pp_last = p_block;
- p_fifo->pp_last = &p_last->p_next;
- p_fifo->i_depth += i_depth;
- p_fifo->i_size += i_size;
-
- vlc_cond_signal( &p_fifo->wait );
- vlc_mutex_unlock( &p_fifo->lock );
-
- return i_size;
- }
這裏,即經過調用vlc_cond_signal( &p_fifo->wait )(此爲VLC所封裝的一個系統的線程條件信號函數),通知正在等待p_fifo->wait條件的函數,而正在等待該條件的函數在獲得這個信號後即調用解碼函數開始解碼。
總結:VLC其實是一個多線程密集的工程,其中封裝了系統的線程函數來實現各個數據包的同步問題。