通过ndk-gdb跟踪调试vlc-android来分析从连接到RTSP服务器并接收到音视频数据包后的处理过程。

首先,从前面的文章有分析过vlc-android的处理过程通过线程函数Run()(Src/input/input.c)来处理的,代码如下:

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

而Init()函数中关于live555连接服务器的处理在前面的一篇文章中已经初略分析,这里我们主要从MainLoop()函数入手,由于代码量过大(后续分篇补上),暂时仅分析关于通过live555接收数据包到发出信号通知解码器可以进行解码的过程。

下面为MainLoop()函数中的一段代码

  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. i_wakeup = es_out_GetWakeup( p_input->p->p_es_out );
  7. }
  8. else if( !es_out_GetEmpty( p_input->p->p_es_out ) )
  9. {
  10. msg_Dbg( p_input, "waiting decoder fifos to empty" );
  11. i_wakeup = mdate() + INPUT_IDLE_SLEEP;
  12. }
  13. ...
  14. ...
  15. ...

从上面代码可知将会调用MainLoopDemux(),下面截取该函数中的一段代码

  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)

  1. static inline int demux_Demux( demux_t *p_demux )
  2. {
  3. if( !p_demux->pf_demux )
  4. return 1;
  5. return p_demux->pf_demux( p_demux );
  6. }

这里的p_demux->pf_demux()所指向的函数是通过live555.cpp中的open()函数来指定,下面为一段open()函数的截取代码:

  1. ...
  2. ...
  3. p_demux->pf_demux  = Demux;
  4. p_demux->pf_control= Control;
  5. ...
  6. ...

即指向live555.cpp文件中的Demux()函数,从上面的分析可知最终调用了这里的Demux()函数,这里截取该函数的一段代码如下:

  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. if( tk->waiting == 0 )
  9. {
  10. tk->waiting = 1;
  11. tk->sub->readSource()->getNextFrame( tk->p_buffer, tk->i_buffer,
  12. StreamRead, tk, StreamClose, tk );
  13. }
  14. }
  15. ...
  16. ...

通过前文的分析,我们知道这里的tk->p_buffer即为我们需要的待解码数据,而对于该数据的处理我们继续跟踪,由前文分析,接下来将会执行StreamRead()函数,如下:

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

从上面的部分代码可知,这里根据不同的格式进行不同的处理,但都会调用memcpy()函数将上面获取的tk->p_buffer数据复制到p_block->p_buffer中去,这样我们就保存下来了通过live555从socket中读取的数据,继续下面的代码:

  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. if( tk->b_muxed )
  11. stream_DemuxSend( tk->p_out_muxed, p_block );
  12. else if( tk->b_asf )
  13. stream_DemuxSend( p_sys->p_out_asf, p_block );
  14. else
  15. es_out_Send( p_demux->out, tk->p_es, p_block );
  16. }

接下来会调用es_out_Send()(vlc/include/Vlc_es_out.h)函数

  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()函数

  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. vlc_mutex_lock( &p_sys->lock );
  7. TsAutoStop( p_out );
  8. CmdInitSend( &cmd, p_es, p_block );
  9. if( p_sys->b_delayed )
  10. TsPushCmd( p_sys->p_ts, &cmd );
  11. else
  12. i_ret = CmdExecuteSend( p_sys->p_out, &cmd) ;
  13. vlc_mutex_unlock( &p_sys->lock );
  14. return i_ret;
  15. }

继续跟踪,先执行CmdInitSend()将p_block数据保存下来到cmd中,接下来将会执行CmdExcuteSend()函数

  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. p_cmd->u.send.p_block = NULL;
  5. if( p_block )
  6. {
  7. if( p_cmd->u.send.p_es->p_es )
  8. return es_out_Send( p_out, p_cmd->u.send.p_es->p_es, p_block );
  9. block_Release( p_block );
  10. }
  11. return VLC_EGENERIC;
  12. }

继续执行es_out_Send()(vlc/include/Vlc_es_out.h)函数

  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()函数,截取该函数的一段代码:

  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)函数,如下:

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

最后,调用src/misc/block.c文件中的block_FifoPut()函数,如下:

  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. if (p_block == NULL)
  6. return 0;
  7. for (p_last = p_block; ; p_last = p_last->p_next)
  8. {
  9. i_size += p_last->i_buffer;
  10. i_depth++;
  11. if (!p_last->p_next)
  12. break;
  13. }
  14. vlc_mutex_lock (&p_fifo->lock);
  15. *p_fifo->pp_last = p_block;
  16. p_fifo->pp_last = &p_last->p_next;
  17. p_fifo->i_depth += i_depth;
  18. p_fifo->i_size += i_size;
  19. /* We queued at least one block: wake up one read-waiting thread */
  20. vlc_cond_signal( &p_fifo->wait );
  21. vlc_mutex_unlock( &p_fifo->lock );
  22. return i_size;
  23. }

这里,即通过调用vlc_cond_signal( &p_fifo->wait )(此为VLC所封装的一个系统的线程条件信号函数),通知正在等待p_fifo->wait条件的函数,而正在等待该条件的函数在得到这个信号后即调用解码函数开始解码。

总结:VLC实际上是一个多线程密集的工程,其中封装了系统的线程函数来实现各个数据包的同步问题。

vlc-android对于通过Live555接收到音视频数据包后的处理分析的更多相关文章

  1. 网络摄像机IPCamera RTSP直播播放网络/权限/音视频数据/花屏问题检测与分析助手EasyRTSPClient

    前言 最近在项目中遇到一个奇怪的问题,同样的SDK调用,访问海康摄像机的RTSP流,发保活OPTIONS命令保活,一个正常,而另一个一发就会被IPC断开,先看现场截图: 图1:发OPTIONS,摄像机 ...

  2. Android多媒体框架总结(1) - 利用MediaMuxer合成音视频数据流程分析

    场景介绍: 设备端通过服务器传向客户端(Android手机)实时发送视频数据(H.264)和音频数据(g711a或g711u), 需要在客户端将音视频数据保存为MP4文件存放在本地,用户可以通过APP ...

  3. [工具]利用EasyRTSPClient工具检查摄像机RTSP流不能播放原因以及排查音视频数据无法播放问题

    出现问题 我们在做流媒体开发的过程中,进程会出现摄像机RTSP流莫名其妙无法播放的问题,而我们常用的vlc经常是直接弹出一个无法播放的提示框就完事了,没有说明出错的原因,或者在vlc的消息里面能看到日 ...

  4. Android利用tcpdump和wireshark抓取网络数据包

    Android利用tcpdump和wireshark抓取网络数据包 主要介绍如何利用tcpdump抓取andorid手机上网络数据请求,利用Wireshark可以清晰的查看到网络请求的各个过程包括三次 ...

  5. FFmpeg采集音视频数据命令

    文章转自:https://www.jianshu.com/p/4709ccbda3f9 1.ffmpeg 把文件当做直播推送至服务器 (RTMP + FLV) ffmpeg - re -i demo. ...

  6. EasyCamera海康摄像机向EasyDarwin云平台推送音视频数据的缓存设计

    本文转自EasyDarwin团队成员Alex的博客:http://blog.csdn.net/cai6811376 EasyCamera在向EasyDarwin云平台推送音视频数据时,有时一个I帧会很 ...

  7. 史上最全的音视频SDK包分享给大家

    史上最全的音视频SDK包分享给大家 概述一下SDK功能: 项目 详情视频通信  支持多种分辨率的视频通信语音通信  提供语音通信,可支持高清宽带语音动态创建房间  可以根据需要,随时创建房间H5 支持 ...

  8. Android Webview中解决H5的音视频不能自动播放的问题

    在开发webview的时候,当加载有声音的网页的时候,声音不会自动播放, 解决方法:在webview中调用js方法.这个方法需要在webview的setWebViewClient方法之后在onPage ...

  9. WebRTC音视频引擎研究(1)--整体架构分析

    WebRTC技术交流群:234795279 原文地址:http://blog.csdn.net/temotemo/article/details/7530504     1.WebRTC目的     ...

随机推荐

  1. 复数类(C++练习一)

    写一个复数类,实现基本的运算,目的熟悉封装与数据抽象. 类的定义 #include <iostream> #include <vector> using namespace s ...

  2. git的使用与积累

    之前对git可以说是一无所知,不过现在做工程要用到,于是就花点时间找了一些资料,本文也只是各种git学习资料的集合,权当是学习笔记吧 一:git的安装与配置 首先,git其实一般在linux环境下都是 ...

  3. Linux中的ln

    在安装了wdcp或在正常使用wdcp后,如有意无意用使用了yum更新系统或安装软件,有时会直接更新安装了yum源里的apache,这时问题就来了打开所有的网站或页面,都是提示Apache欢迎页面 这个 ...

  4. php的一些小笔记--数学函数

    通常我们使用的数学函数不多,经常出现的有 floor 地板->舍去 ceil   天花板->进一 round 四舍五入 rand 随机数 mt_rand 产生更好的随机数 pow 指数表达 ...

  5. X-factor Chains(POJ3421 素数)

    X-factor Chains Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6212   Accepted: 1928 D ...

  6. PASCAL的优越性:官方的说法(不需要Makefile,节约大量的时间)

    也许你认为为什么我选择pascal代替其他的语言,像C.或者您会拿FreePascal和其他的pascal编译器作比较,那么好,这里您看看FreePascal为什么好: 1.pascal是一个非常简洁 ...

  7. SQL Server 2008空间数据应用系列四:基础空间对象与函数应用

    原文:SQL Server 2008空间数据应用系列四:基础空间对象与函数应用 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft SQL Server 2008 R2调测. ...

  8. ListView.setOnItemClickListener无效

    如果ListView中的单个Item的view中存在checkbox,button等view,会导致ListView.setOnItemClickListener无效, 事件会被子View捕获到,Li ...

  9. C# ADO基础 SqlHelper

    class SqlHelper { //这个是将连接数据库的字符串写到配置文件中的 private static string connStr = ConfigurationManager.Conne ...

  10. qsort排序算法

      七种qsort排序方法 <本文中排序都是采用的从小到大排序> 一.对int类型数组排序 int num[100]; Sample: int cmp ( const void *a ,  ...