转载地址:https://blog.csdn.net/c_m_deng/article/details/8487456

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

首先,从前面的文章有分析过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. 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()函数中的一段代码

[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. 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(),下面截取该函数中的一段代码

[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. return p_demux->pf_demux( p_demux );
  6. }

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

[cpp] view plain copy

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

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

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

[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. 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中读取的数据,继续下面的代码:

[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. 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)函数

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

[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. 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)函数

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

[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. 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. 网络摄像机IPCamera RTSP直播播放网络/权限/音视频数据/花屏问题检测与分析助手EasyRTSPClient

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

  3. android音视频工程师,音视频学习 (十三) Android 中通过 FFmpeg 命令对音视频编辑处理(已开源)...

    ## 音视频学习 (十三) Android 中通过 FFmpeg 命令对音视频编辑处理(已开源) ## 视音频编辑器 ## 前言 有时候我们想对音视频进行加工处理,比如视频编辑.添加字幕.裁剪等功能处 ...

  4. 视频直播源码在Android端实现1对1音视频实时通话

    我们要使用 WebRTC 进行音视频互动时需要申请访问硬件的权限,至少要申请以下三种权限 Camera 权限 Record Audio 权限 Intenet 权限 在Android中,申请权限分为静态 ...

  5. Android如何回调编码后的音视频数据

    有开发者提到,在RTMP推送端的基础上,希望能回调编码后的音视频数据,便于开发者对接第三方系统,如GB28181. 为此,我们加了一下接口: 1. 设置音视频callback 对应接口: /*** S ...

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

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

  7. WebRTC系列-RTCDataChannel发送非音视频数据

    文章目录 1. RTCDataChannel基本介绍 2. RTCDataChannel实战 2.1 negotiated=false 创建及事件处理 2.2 negotiated= true 创建及 ...

  8. 音视频方案,音视频扩展内容- 音视频数据解析,码流分析及质量评价(笔记)3

    -- 音视频编解码流程如下图: -- 关于音频各种参数: freq:音频数据的采样率.常用的有48000,44100等. format:音频数据的格式.举例几种格式: AUDIO_U16SYS:Uns ...

  9. 基于iOS的网络音视频实时传输系统(二)- 捕获音视频数据

    下载 GitHub: client 端:https://github.com/AmoAmoAmo/Smart_Device_Client server端:https://github.com/AmoA ...

最新文章

  1. 使用read write 读写socket
  2. 为什么大厂们一边在疯狂裁员,一边又在大量招人?
  3. Mysql数据库(一)——mysql数据库初体验
  4. junit 5测试异常处理_在JUnit中处理异常的3种方式。 选择哪一个?
  5. matlab变量由非标量,matlab中的if语句
  6. Python数模笔记-Sklearn(2)聚类分析
  7. Android适配底部返回键等虚拟键盘的完美解决方案
  8. [翻译]用 Puppet 搭建易管理的服务器基础架构(3)
  9. 今天提前回去吧,整理一下,为下周做好准备。
  10. 双击jar包 运行SpringBoot项目
  11. idea 代码格式化快捷方式
  12. 机房收费系统——可行性研究报告
  13. love2d教程10--粒子效果
  14. Kafka3.x详细笔记
  15. 【源码】基于心电图的数据分析
  16. 前端后端路径斜杆问题
  17. GD32,在电源电路VDD VDDA VREF+/-参考设计
  18. gvim之Vimdiff 使用
  19. Oracle 11gR2 RAC 单网卡 转 双网卡绑定 配置步骤
  20. Python自动化办公:将文本文档内容批量分类导入Excel表格

热门文章

  1. 复选框、单行文本框、组合框
  2. SVN 锁定无法提交命令执行失败
  3. 仿华为手机管家“一键优化”Loading加载框
  4. ichunqiu云境 - Delegation Writeup
  5. 如何搭建一个FMS的视频流媒体服务器用于监控直播?
  6. “股神”巴菲特的六大投资良策
  7. MacBook 使用 Loopback 录屏和录音频(MacBook 录屏教程/录视频教程/Loopback 教程)
  8. 连续三年入选?Bespin Global又在Gartner魔力象限上浓墨重彩了一笔……
  9. springtools安装失败_安装Spring Tool Suite(STS)并打开后有非常多的错误
  10. NFS实践 | 一个NAS盘使用的问题分享