vlc-android對於經過Live555接收到音視頻數據包後的處理分析

經過ndk-gdb跟蹤調試vlc-android來分析從鏈接到RTSP服務器並接收到音視頻數據包後的處理過程。android

首先,從前面的文章有分析過vlc-android的處理過程經過線程函數Run()(Src/input/input.c)來處理的,代碼以下:服務器

[cpp]  view plain copy
 
  1. static void *Run( void *obj )  
  2. {  
  3.     input_thread_t *p_input = (input_thread_t *)obj;  
  4.     const int canc = vlc_savecancel();  
  5.   
  6.     if( Init( p_input ) )  
  7.         goto exit;  
  8.   
  9.     MainLoop( p_input, true ); /* FIXME it can be wrong (like with VLM) */  
  10.   
  11.     /* Clean up */  
  12.     End( p_input );  
  13.   
  14. exit:  
  15.     /* Tell we're dead */  
  16.     vlc_mutex_lock( &p_input->p->lock_control );  
  17.     const bool b_abort = p_input->p->b_abort;  
  18.     vlc_mutex_unlock( &p_input->p->lock_control );  
  19.   
  20.     if( b_abort )  
  21.         input_SendEventAbort( p_input );  
  22.     input_SendEventDead( p_input );  
  23.   
  24.     vlc_restorecancel( canc );  
  25.     return NULL;  
  26. }  


 

而Init()函數中關於live555鏈接服務器的處理在前面的一篇文章中已經初略分析,這裏咱們主要從MainLoop()函數入手,因爲代碼量過大(後續分篇補上),暫時僅分析關於經過live555接收數據包到發出信號通知解碼器能夠進行解碼的過程。多線程

下面爲MainLoop()函數中的一段代碼socket

 

[cpp]  view plain copy
 
  1. if( !b_paused )  
  2.        {  
  3.            if( !p_input->p->input.b_eof )  
  4.            {  
  5.                MainLoopDemux( p_input, &b_force_update, &b_demux_polled, i_start_mdate );  
  6.   
  7.                i_wakeup = es_out_GetWakeup( p_input->p->p_es_out );  
  8.            }  
  9.            else if( !es_out_GetEmpty( p_input->p->p_es_out ) )  
  10.            {  
  11.                msg_Dbg( p_input, "waiting decoder fifos to empty" );  
  12.                i_wakeup = mdate() + INPUT_IDLE_SLEEP;  
  13.            }  
  14.     ...  
  15.     ...  
  16.     ...  


 

從上面代碼可知將會調用MainLoopDemux(),下面截取該函數中的一段代碼ide

 

[cpp]  view plain copy
 
  1. if( ( p_input->p->i_stop > 0 && p_input->p->i_time >= p_input->p->i_stop ) ||  
  2.        ( p_input->p->i_run > 0 && i_start_mdate+p_input->p->i_run < mdate() ) )  
  3.        i_ret = 0; /* EOF */  
  4.    else  
  5.        i_ret = demux_Demux( p_input->p->input.p_demux );  
  6.        ...  
  7.        ...  
  8.        ...  


 

繼續調用demux_Demux()(src/input/Demux.h)函數

 

[cpp]  view plain copy
 
  1. static inline int demux_Demux( demux_t *p_demux )  
  2. {  
  3.     if( !p_demux->pf_demux )  
  4.         return 1;  
  5.   
  6.     return p_demux->pf_demux( p_demux );  
  7. }  


 

 

這裏的p_demux->pf_demux()所指向的函數是經過live555.cpp中的open()函數來指定,下面爲一段open()函數的截取代碼:oop

 

[cpp]  view plain copy
 
  1. ...  
  2. ...    
  3. p_demux->pf_demux  = Demux;  
  4. p_demux->pf_control= Control;  
  5. ...  
  6. ...  


 

即指向live555.cpp文件中的Demux()函數,從上面的分析可知最終調用了這裏的Demux()函數,這裏截取該函數的一段代碼以下:ui

 

[cpp]  view plain copy
 
  1.      ...  
  2.      ...     
  3. /* First warn we want to read data */  
  4.     p_sys->event_data = 0;  
  5.     for( i = 0; i < p_sys->i_track; i++ )  
  6.     {  
  7.         live_track_t *tk = p_sys->track[i];  
  8.   
  9.         if( tk->waiting == 0 )  
  10.         {  
  11.             tk->waiting = 1;  
  12.             tk->sub->readSource()->getNextFrame( tk->p_buffer, tk->i_buffer,  
  13.                                           StreamRead, tk, StreamClose, tk );  
  14.         }  
  15.     }  
  16.     ...  
  17.     ...  


 

經過前文的分析,咱們知道這裏的tk->p_buffer即爲咱們須要的待解碼數據,而對於該數據的處理咱們繼續跟蹤,由前文分析,接下來將會執行StreamRead()函數,以下:idea

 

[cpp]  view plain copy
 
  1. ...  
  2. ...  
  3.    if( tk->fmt.i_codec == VLC_CODEC_AMR_NB ||  
  4.       tk->fmt.i_codec == VLC_CODEC_AMR_WB )  
  5.   {  
  6.       AMRAudioSource *amrSource = (AMRAudioSource*)tk->sub->readSource();  
  7.   
  8.       p_block = block_New( p_demux, i_size + 1 );  
  9.       p_block->p_buffer[0] = amrSource->lastFrameHeader();  
  10.       memcpy( p_block->p_buffer + 1, tk->p_buffer, i_size );  
  11.   }  
  12.   else if( tk->fmt.i_codec == VLC_CODEC_H261 )  
  13.   {  
  14.       H261VideoRTPSource *h261Source = (H261VideoRTPSource*)tk->sub->rtpSource();  
  15.       uint32_t header = h261Source->lastSpecialHeader();  
  16.       p_block = block_New( p_demux, i_size + 4 );  
  17.       memcpy( p_block->p_buffer, &header, 4 );  
  18.       memcpy( p_block->p_buffer + 4, tk->p_buffer, i_size );  
  19.   
  20.       if( tk->sub->rtpSource()->curPacketMarkerBit() )  
  21.           p_block->i_flags |= BLOCK_FLAG_END_OF_FRAME;  
  22.   }  
  23.   else if( tk->fmt.i_codec == VLC_CODEC_H264 )  
  24.   {  
  25.       if( (tk->p_buffer[0] & 0x1f) >= 24 )  
  26.           msg_Warn( p_demux, "unsupported NAL type for H264" );  
  27.   
  28.       /* Normal NAL type */  
  29.       p_block = block_New( p_demux, i_size + 4 );  
  30.       p_block->p_buffer[0] = 0x00;  
  31.       p_block->p_buffer[1] = 0x00;  
  32.       p_block->p_buffer[2] = 0x00;  
  33.       p_block->p_buffer[3] = 0x01;  
  34.       memcpy( &p_block->p_buffer[4], tk->p_buffer, i_size );  
  35.   }  
  36.   else if( tk->b_asf )  
  37.   {  
  38.       p_block = StreamParseAsf( p_demux, tk,  
  39.                                 tk->sub->rtpSource()->curPacketMarkerBit(),  
  40.                                 tk->p_buffer, i_size );  
  41.   }  
  42.   else  
  43.   {  
  44.       p_block = block_New( p_demux, i_size );  
  45.       memcpy( p_block->p_buffer, tk->p_buffer, i_size );  
  46.   }  
  47.   ...  
  48.   ...  


 

從上面的部分代碼可知,這裏根據不一樣的格式進行不一樣的處理,但都會調用memcpy()函數將上面獲取的tk->p_buffer數據複製到p_block->p_buffer中去,這樣咱們就保存下來了經過live555從socket中讀取的數據,繼續下面的代碼:spa

 

[cpp]  view plain copy
 
  1. if( p_block )  
  2.     {  
  3.         if( !tk->b_muxed && !tk->b_asf )  
  4.         {  
  5.             if( i_pts != tk->i_pts )  
  6.                 p_block->i_pts = VLC_TS_0 + i_pts;  
  7.             /*FIXME: for h264 you should check that packetization-mode=1 in sdp-file */  
  8.             p_block->i_dts = ( tk->fmt.i_codec == VLC_CODEC_MPGV ) ? VLC_TS_INVALID : (VLC_TS_0 + i_pts);  
  9.         }  
  10.   
  11.         if( tk->b_muxed )  
  12.             stream_DemuxSend( tk->p_out_muxed, p_block );  
  13.         else if( tk->b_asf )  
  14.             stream_DemuxSend( p_sys->p_out_asf, p_block );  
  15.         else  
  16.             es_out_Send( p_demux->out, tk->p_es, p_block );  
  17.     }  


 

 

接下來會調用es_out_Send()(vlc/include/Vlc_es_out.h)函數

 

[cpp]  view plain copy
 
  1. static inline int es_out_Send( es_out_t *out, es_out_id_t *id,  
  2.                                block_t *p_block )  
  3. {  
  4.     return out->pf_send( out, id, p_block );  
  5. }  


 

 

經過單步跟蹤,這裏out->pf_send()所指向的正是src/input/es_out_timeshift.c文件中的Send()函數

 

[cpp]  view plain copy
 
  1. static int Send( es_out_t *p_out, es_out_id_t *p_es, block_t *p_block )  
  2. {  
  3.     es_out_sys_t *p_sys = p_out->p_sys;  
  4.     ts_cmd_t cmd;  
  5.     int i_ret = VLC_SUCCESS;  
  6.   
  7.     vlc_mutex_lock( &p_sys->lock );  
  8.   
  9.     TsAutoStop( p_out );  
  10.   
  11.     CmdInitSend( &cmd, p_es, p_block );  
  12.     if( p_sys->b_delayed )  
  13.         TsPushCmd( p_sys->p_ts, &cmd );  
  14.     else  
  15.         i_ret = CmdExecuteSend( p_sys->p_out, &cmd) ;  
  16.   
  17.     vlc_mutex_unlock( &p_sys->lock );  
  18.   
  19.     return i_ret;  
  20. }  


 

 


繼續跟蹤,先執行CmdInitSend()將p_block數據保存下來到cmd中,接下來將會執行CmdExcuteSend()函數

 

[cpp]  view plain copy
 
  1. static int CmdExecuteSend( es_out_t *p_out, ts_cmd_t *p_cmd )  
  2. {  
  3.     block_t *p_block = p_cmd->u.send.p_block;  
  4.   
  5.     p_cmd->u.send.p_block = NULL;  
  6.   
  7.     if( p_block )  
  8.     {  
  9.         if( p_cmd->u.send.p_es->p_es )  
  10.             return es_out_Send( p_out, p_cmd->u.send.p_es->p_es, p_block );  
  11.         block_Release( p_block );  
  12.     }  
  13.     return VLC_EGENERIC;  
  14. }  


 

 


繼續執行es_out_Send()(vlc/include/Vlc_es_out.h)函數

 

[cpp]  view plain copy
 
  1. static inline int es_out_Send( es_out_t *out, es_out_id_t *id,  
  2.                                block_t *p_block )  
  3. {  
  4.     return out->pf_send( out, id, p_block );  
  5. }  


 

繼續跟蹤,這裏的out->pf_send()函數指向src/input/es_out.c文件中的EsOutSend()函數,截取該函數的一段代碼:

 

[cpp]  view plain copy
 
  1. ...  
  2. ...  
  3.   /* Decode */  
  4.   if( es->p_dec_record )  
  5.   {  
  6.       block_t *p_dup = block_Duplicate( p_block );  
  7.       if( p_dup )  
  8.           input_DecoderDecode( es->p_dec_record, p_dup,  
  9.                                p_input->p->b_out_pace_control );  
  10.   }  
  11.   input_DecoderDecode( es->p_dec, p_block,  
  12.                        p_input->p->b_out_pace_control );  
  13.   ...  
  14.   ...  


 


這裏將會調用第二個input_DecoderDecode()(src/input/Decoder.c)函數,以下:

 

[cpp]  view plain copy
 
  1. void input_DecoderDecode( decoder_t *p_dec, block_t *p_block, bool b_do_pace )  
  2. {  
  3.     decoder_owner_sys_t *p_owner = p_dec->p_owner;  
  4.   
  5.     if( b_do_pace )  
  6.     {  
  7.         /* The fifo is not consummed when buffering and so will 
  8.          * deadlock vlc. 
  9.          * There is no need to lock as b_buffering is never modify 
  10.          * inside decoder thread. */  
  11.         if( !p_owner->b_buffering )  
  12.             block_FifoPace( p_owner->p_fifo, 10, SIZE_MAX );  
  13.     }  
  14. #ifdef __arm__  
  15.     else if( block_FifoSize( p_owner->p_fifo ) > 50*1024*1024 /* 50 MiB */ )  
  16. #else  
  17.     else if( block_FifoSize( p_owner->p_fifo ) > 400*1024*1024 /* 400 MiB, ie ~ 50mb/s for 60s */ )  
  18. #endif  
  19.     {  
  20.         /* FIXME: ideally we would check the time amount of data 
  21.          * in the FIFO instead of its size. */  
  22.         msg_Warn( p_dec, "decoder/packetizer fifo full (data not "  
  23.                   "consumed quickly enough), resetting fifo!" );  
  24.         block_FifoEmpty( p_owner->p_fifo );  
  25.     }  
  26.   
  27.     block_FifoPut( p_owner->p_fifo, p_block );  
  28. }  


 

 

最後,調用src/misc/block.c文件中的block_FifoPut()函數,以下:

 

[cpp]  view plain copy
 
  1. size_t block_FifoPut( block_fifo_t *p_fifo, block_t *p_block )  
  2. {  
  3.     size_t i_size = 0, i_depth = 0;  
  4.     block_t *p_last;  
  5.   
  6.     if (p_block == NULL)  
  7.         return 0;  
  8.     for (p_last = p_block; ; p_last = p_last->p_next)  
  9.     {  
  10.         i_size += p_last->i_buffer;  
  11.         i_depth++;  
  12.         if (!p_last->p_next)  
  13.             break;  
  14.     }  
  15.   
  16.     vlc_mutex_lock (&p_fifo->lock);  
  17.     *p_fifo->pp_last = p_block;  
  18.     p_fifo->pp_last = &p_last->p_next;  
  19.     p_fifo->i_depth += i_depth;  
  20.     p_fifo->i_size += i_size;  
  21.     /* We queued at least one block: wake up one read-waiting thread */  
  22.     vlc_cond_signal( &p_fifo->wait );  
  23.     vlc_mutex_unlock( &p_fifo->lock );  
  24.   
  25.     return i_size;  
  26. }  


 

這裏,即經過調用vlc_cond_signal( &p_fifo->wait )(此爲VLC所封裝的一個系統的線程條件信號函數),通知正在等待p_fifo->wait條件的函數,而正在等待該條件的函數在獲得這個信號後即調用解碼函數開始解碼。

總結:VLC其實是一個多線程密集的工程,其中封裝了系統的線程函數來實現各個數據包的同步問題。

相關文章
相關標籤/搜索