http://www.cnblogs.com/jiayayao/p/6858253.html

  代码分析前,先要了解TS流基本概念:TS流之基本概念。

  VLC解析TS流是通过libts库来分离的,libts库使用libdvbpsi库来解TS表。VLC使用模块加载机制来加载libts库,具体调用的文件是ts.c.

1. libts库在加载的时候,会将以下如下两个函数注册下去,当接收到PAT或者PMT的时候,会进行调用。PAT和PMT每隔一段时间就会发送一次,以更新节目信息。

static void PATCallBack( void*, dvbpsi_pat_t * );
static void PMTCallBack( void *data, dvbpsi_pmt_t *p_pmt );

2. PATCallBack回调函数,获取并保存所有节目列表及其PID。

static void PATCallBack( void *data, dvbpsi_pat_t *p_pat )
{demux_t              *p_demux = (demux_t *)data;demux_sys_t          *p_sys = p_demux->p_sys;dvbpsi_pat_program_t *p_program;ts_pid_t             *pat = &p_sys->pid[0];msg_Dbg( p_demux, "PATCallBack called" );if( ( pat->psi->i_pat_version != -1 &&( !p_pat->b_current_next ||p_pat->i_version == pat->psi->i_pat_version ) ) ||p_sys->b_user_pmt ){dvbpsi_DeletePAT( p_pat );return;}msg_Dbg( p_demux, "new PAT ts_id=%d version=%d current_next=%d",p_pat->i_ts_id, p_pat->i_version, p_pat->b_current_next );
  ....../* now create programs */for( p_program = p_pat->p_first_program; p_program != NULL;p_program = p_program->p_next ){// 依次输出PAT表中的节目,包括其number和PIDmsg_Dbg( p_demux, "  * number=%d pid=%d", p_program->i_number,p_program->i_pid );if( p_program->i_number == 0 )continue;// 节目的PID就是它所对应的PMT,这里获取该节目对应的PMTts_pid_t *pmt = &p_sys->pid[p_program->i_pid];ValidateDVBMeta( p_demux, p_program->i_pid );if( pmt->b_valid ){bool b_add = true;for( int i_prg = 0; b_add && i_prg < pmt->psi->i_prg; i_prg++ )if( pmt->psi->prg[i_prg]->i_number == p_program->i_number )b_add = false;if( !b_add )continue;}else{TAB_APPEND( (ts_pid_t **), p_sys->i_pmt, p_sys->pmt, pmt );            // sunqueen modify}PIDInit( pmt, true, pat->psi );ts_prg_psi_t *prg = pmt->psi->prg[pmt->psi->i_prg-1];    ......// 注册PMT回调函数prg->handle = dvbpsi_AttachPMT( p_program->i_number, PMTCallBack, p_demux );prg->i_number = p_program->i_number;prg->i_pid_pmt = p_program->i_pid;/* Now select PID at access level */if( ProgramIsSelected( p_demux, p_program->i_number ) ){if( p_sys->i_current_program == 0 )p_sys->i_current_program = p_program->i_number;if( SetPIDFilter( p_demux, p_program->i_pid, true ) )p_sys->b_access_control = false;}}pat->psi->i_pat_version = p_pat->i_version;dvbpsi_DeletePAT( p_pat );
}

3. PMTCallBack与PATCallBack类似,主要是循环读取某一节目的码流列表及及其PID,不再赘述。

4. Demux分离,使用GatherData收集一帧。

static int Demux( demux_t *p_demux )
{demux_sys_t *p_sys = p_demux->p_sys;bool b_wait_es = p_sys->i_pmt_es <= 0;/* We read at most 100 TS packet or until a frame is completed */for( int i_pkt = 0; i_pkt < p_sys->i_ts_read; i_pkt++ ){bool         b_frame = false;block_t     *p_pkt;if( !(p_pkt = ReadTSPacket( p_demux )) ){return 0;}if( p_sys->b_start_record ){/* Enable recording once synchronized */stream_Control( p_demux->s, STREAM_SET_RECORD_STATE, true, "ts" );p_sys->b_start_record = false;}if( p_sys->b_udp_out ){memcpy( &p_sys->buffer[i_pkt * p_sys->i_packet_size],p_pkt->p_buffer, p_sys->i_packet_size );}/* Parse the TS packet */ts_pid_t *p_pid = &p_sys->pid[PIDGet( p_pkt )];// 这里可以把接收到的包的pid打出来,方便调试msg_Dbg( p_demux, "received pkt pid[%d]", PIDGet( p_pkt ) );if( p_pid->b_valid ){// 如果当前包是PSI信息,使用dvbpsi库解表if( p_pid->psi ){// 如果是PAT或者0x11 0x12等if( p_pid->i_pid == 0 || ( p_sys->b_dvb_meta && ( p_pid->i_pid == 0x11 || p_pid->i_pid == 0x12 || p_pid->i_pid == 0x14 ) ) ){dvbpsi_PushPacket( p_pid->psi->handle, p_pkt->p_buffer );}else{for( int i_prg = 0; i_prg < p_pid->psi->i_prg; i_prg++ ){dvbpsi_PushPacket( p_pid->psi->prg[i_prg]->handle,p_pkt->p_buffer );}}block_Release( p_pkt );}       // 如果当前的包是数据,则收集 else if( !p_sys->b_udp_out ){// 返回值为是否已经获取了一整帧的数据b_frame = GatherData( p_demux, p_pid, p_pkt );}else{PCRHandle( p_demux, p_pid, p_pkt );block_Release( p_pkt );}}else{if( !p_pid->b_seen ){msg_Dbg( p_demux, "pid[%d] unknown", p_pid->i_pid );}/* We have to handle PCR if present */PCRHandle( p_demux, p_pid, p_pkt );block_Release( p_pkt );}p_pid->b_seen = true;if( b_frame || ( b_wait_es && p_sys->i_pmt_es > 0 ) )break;}if( p_sys->b_udp_out ){/* Send the complete block */net_Write( p_demux, p_sys->fd, NULL, p_sys->buffer,p_sys->i_ts_read * p_sys->i_packet_size );}return 1;
}

5. GatherData解析TS包

// 解析TS包
static bool GatherData( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk )
{const uint8_t *p = p_bk->p_buffer;// 第一个字节(8位)为同步字节,此处没有获取const bool b_unit_start = p[1]&0x40;// payload_unit_start_indicator是否被置位const bool b_scrambled  = p[3]&0x80;// 是否加扰const bool b_adaptation = p[3]&0x20;// 是否有调整字段控制const bool b_payload    = p[3]&0x10;// 有无payloadconst int  i_cc         = p[3]&0x0f; /* continuity counter */// 获取连续的计数bool       b_discontinuity = false;  /* discontinuity *//* transport_scrambling_control is ignored */int         i_skip = 0;bool        i_ret  = false;/* For now, ignore additional error correction* TODO: handle Reed-Solomon 204,188 error correction */// 目前暂时忽略扩展错误纠正的情况p_bk->i_buffer = TS_PACKET_SIZE_188;// 至少发生了1位错误,且不可纠正if( p[1]&0x80 ){msg_Dbg( p_demux, "transport_error_indicator set (pid=%d)",pid->i_pid );if( pid->es->p_data ) //&& pid->es->fmt.i_cat == VIDEO_ES )pid->es->p_data->i_flags |= BLOCK_FLAG_CORRUPTED;}if( p_demux->p_sys->csa ){vlc_mutex_lock( &p_demux->p_sys->csa_lock );csa_Decrypt( p_demux->p_sys->csa, p_bk->p_buffer, p_demux->p_sys->i_csa_pkt_size );vlc_mutex_unlock( &p_demux->p_sys->csa_lock );}if( !b_adaptation ){/* We don't have any adaptation_field, so payload starts* immediately after the 4 byte TS header */// 没有调整字段,所以有效净荷紧跟着4个字节的TS头i_skip = 4;}else{/* p[4] is adaptation_field_length minus one */// 如果有调整字段,那么TS头后的第一个字节就是调整字段的长度i_skip = 5 + p[4];if( p[4] > 0 ){/* discontinuity indicator found in stream */b_discontinuity = (p[5]&0x80) ? true : false;if( b_discontinuity && pid->es->p_data ){msg_Warn( p_demux, "discontinuity indicator (pid=%d) ",pid->i_pid );/* pid->es->p_data->i_flags |= BLOCK_FLAG_DISCONTINUITY; */}}}/* Test continuity counter *//* continuous when (one of this):* diff == 1* diff == 0 and payload == 0* diff == 0 and duplicate packet (playload != 0) <- should we*   test the content ?*/const int i_diff = ( i_cc - pid->i_cc )&0x0f;if( b_payload && i_diff == 1 ){pid->i_cc = ( pid->i_cc + 1 ) & 0xf;}else{if( pid->i_cc == 0xff ){msg_Warn( p_demux, "first packet for pid=%d cc=0x%x",pid->i_pid, i_cc );pid->i_cc = i_cc;}else if( i_diff != 0 && !b_discontinuity ){msg_Warn( p_demux, "discontinuity received 0x%x instead of 0x%x (pid=%d)",i_cc, ( pid->i_cc + 1 )&0x0f, pid->i_pid );pid->i_cc = i_cc;if( pid->es->p_data && pid->es->fmt.i_cat != VIDEO_ES ){/* Small video artifacts are usually better than* dropping full frames */pid->es->p_data->i_flags |= BLOCK_FLAG_CORRUPTED;}}}// 校正PCRPCRHandle( p_demux, pid, p_bk );if( i_skip >= 188 || pid->es->id == NULL || p_demux->p_sys->b_udp_out ){block_Release( p_bk );return i_ret;}/* */if( !pid->b_scrambled != !b_scrambled ){msg_Warn( p_demux, "scrambled state changed on pid %d (%d->%d)",pid->i_pid, pid->b_scrambled, b_scrambled );pid->b_scrambled = b_scrambled;for( int i = 0; i < pid->i_extra_es; i++ ){es_out_Control( p_demux->out, ES_OUT_SET_ES_SCRAMBLED_STATE,pid->extra_es[i]->id, b_scrambled );}es_out_Control( p_demux->out, ES_OUT_SET_ES_SCRAMBLED_STATE,pid->es->id, b_scrambled );}/* We have to gather it */p_bk->p_buffer += i_skip;p_bk->i_buffer -= i_skip;if( b_unit_start ){if( pid->es->data_type == TS_ES_DATA_TABLE_SECTION && p_bk->i_buffer > 0 ){int i_pointer_field = __MIN( p_bk->p_buffer[0], p_bk->i_buffer - 1 );block_t *p = block_Duplicate( p_bk );if( p ){p->i_buffer = i_pointer_field;p->p_buffer++;block_ChainLastAppend( &pid->es->pp_last, p );}p_bk->i_buffer -= 1 + i_pointer_field;p_bk->p_buffer += 1 + i_pointer_field;}if( pid->es->p_data ){ParseData( p_demux, pid );i_ret = true;}block_ChainLastAppend( &pid->es->pp_last, p_bk );if( pid->es->data_type == TS_ES_DATA_PES ){if( p_bk->i_buffer > 6 ){// 一个PES包含一帧图像,此时获取PES包的长度// 当收集到的数据大于等于i_data_size时,将接收到的一整帧图像放入block中,// 等待解码线程解码// PES包头为24位(3个字节),流ID为8位(1个字节),故PES包长度从第5个字节开始pid->es->i_data_size = GetWBE( &p_bk->p_buffer[4] );if( pid->es->i_data_size > 0 ){pid->es->i_data_size += 6;}}}else if( pid->es->data_type == TS_ES_DATA_TABLE_SECTION ){if( p_bk->i_buffer > 3 && p_bk->p_buffer[0] != 0xff ){pid->es->i_data_size = 3 + (((p_bk->p_buffer[1] & 0xf) << 8) | p_bk->p_buffer[2]);}}pid->es->i_data_gathered += p_bk->i_buffer;if( pid->es->i_data_size > 0 &&pid->es->i_data_gathered >= pid->es->i_data_size ){ParseData( p_demux, pid );i_ret = true;}}else{if( pid->es->p_data == NULL ){/* msg_Dbg( p_demux, "broken packet" ); */block_Release( p_bk );}else{block_ChainLastAppend( &pid->es->pp_last, p_bk );pid->es->i_data_gathered += p_bk->i_buffer;if( pid->es->i_data_size > 0 &&pid->es->i_data_gathered >= pid->es->i_data_size ){// 认为此时已经获取了一整帧数据,那么push到block的队列中等待解码线程解码ParseData( p_demux, pid );i_ret = true;}}}return i_ret;
}

  附:

  配置好的Windows版vlc工程下载:https://github.com/jiayayao/vlc_2.1.0-vs_2010,下载后使用vs2010可以直接编译运行,调试学习非常方便。

转载于:https://www.cnblogs.com/stnlcd/p/7202101.html

vlc源码分析(四) 调用libts接收TS流相关推荐

  1. Spring 源码分析(四) ——MVC(二)概述

    随时随地技术实战干货,获取项目源码.学习资料,请关注源代码社区公众号(ydmsq666) from:Spring 源码分析(四) --MVC(二)概述 - 水门-kay的个人页面 - OSCHINA ...

  2. 【投屏】Scrcpy源码分析四(最终章 - Server篇)

    Scrcpy源码分析系列 [投屏]Scrcpy源码分析一(编译篇) [投屏]Scrcpy源码分析二(Client篇-连接阶段) [投屏]Scrcpy源码分析三(Client篇-投屏阶段) [投屏]Sc ...

  3. gSOAP 源码分析(四)

    gSOAP 源码分析(四) 2012-6-2 邵盛松 前言 本文主要说明gSOAP中对Client的认证分析 gSOAP中包含了HTTP基本认证,NTLM认证等,还可以自定义SOAP Heard实现认 ...

  4. ABP源码分析四十七:ABP中的异常处理

    ABP源码分析四十七:ABP中的异常处理 参考文章: (1)ABP源码分析四十七:ABP中的异常处理 (2)https://www.cnblogs.com/1zhk/p/5538983.html (3 ...

  5. 【源码分析】storm拓扑运行全流程源码分析

    [源码分析]storm拓扑运行全流程源码分析 @(STORM)[storm] 源码分析storm拓扑运行全流程源码分析 一拓扑提交流程 一stormpy 1storm jar 2def jar 3ex ...

  6. 谷歌chrome浏览器的源码分析(四)

    上一次说到需要调用这个OpenURLFromTab函数,那么这个函数是做什么的呢?从名称上可能猜到它是打开网页,但是是从目前TAB页里打开呢?还是新建一个?或者使用每个TAB页一个进程呢?这些疑惑,只 ...

  7. 【转】ABP源码分析四十七:ABP中的异常处理

    ABP 中异常处理的思路是很清晰的.一共五种类型的异常类. AbpInitializationException用于封装ABP初始化过程中出现的异常,只要抛出AbpInitializationExce ...

  8. 【转】ABP源码分析四:Configuration

    核心模块的配置 Configuration是ABP中设计比较巧妙的地方.其通过AbpStartupConfiguration,Castle的依赖注入,Dictionary对象和扩展方法很巧妙的实现了配 ...

  9. wireshark协议解析器 源码分析 封装调用

    源码分析 Wireshark启动时,所有解析器进行初始化和注册.要注册的信息包括协议名称.各个字段的信息.过滤用的关键字.要关联的下层协议与端口(handoff)等.在解析过程,每个解析器负责解析自己 ...

最新文章

  1. 全國身份證查詢系統nciis
  2. C语言 大小写字符转换
  3. VC++连接Mysql
  4. Unity教程之-UGUI美术字体的制作与使用
  5. 前端---JavaScript基础3
  6. SAP社区改版之前的一些截图,怀旧啊
  7. 前端学习(1885)vue之电商管理系统电商系统之首页路由的重定向
  8. LeetCode 862. 和至少为 K 的最短子数组(前缀和+deque单调栈)
  9. 怎么把html转换成jpg6,html转为图片(六):xhtmlrenderer
  10. 我如何开始使用Linux
  11. vue2项目中给echarts地图设置背景图和打点
  12. mysql外键设置sql语句_数据库sql语句如何设置外键
  13. 电视台工作计算机管理,电视台工作岗位有哪些
  14. C#网络编程 (二) 数据流的类型和应用
  15. 关于GoldWave给Vegas视频添加音频叠加的教程分享
  16. ZJM 与生日礼物(字典树)
  17. Python编程:itertools库排列组合
  18. 《重新定义团队》读书笔记及阅读感想2600字
  19. 使用python requests+re库+curl.trillworks.com神器 实现淘宝页面信息爬取
  20. 内幕!5条技巧教你识别不靠谱IT外包公司,向坑骗说byebye!

热门文章

  1. 短期逾期影响贷款吗?
  2. 通俗易懂的语言解释下股票、基金、证券、债券、信托、期货、国债、外汇?
  3. 软件就像大教堂:我们建造它们——然后祈祷
  4. Qt4_内置的窗口部件类和对话框类
  5. 从git上clone项目到IDEA
  6. Liunux 编程遇到的SIGBUS信号
  7. SQL Server中的查询优化技术:基础
  8. sql azure 语法_使用Azure Data Studio开发SQL Server数据库
  9. 在django中使用vue.js需要注意的地方
  10. centos7.2 使用rpm安装jdk8