下面针对解复用程序详细分析一下PAT,PMT和SDT三类表格的格式.

PAT---Program Association Table,节目关联表。PAT表携带以下信息:
(1) TS流ID--- transport_stream_id,该ID标志唯一的流ID
(2)节目频道号-- program_number,该号码标志TS流中的一个频道,该频道可以包含很多的节目(即可以包含多个Video PID和Audio PID)
(3) PMT的PID--- program_map_PID,表示本频道使用的哪个PID做为PMT的PID,因为可以有很多的频道,因此DVB规定PMT的PID可以由用户自己定义.

PAT表定义如下: 各字段含义如下:

table_id:8 bits,标志本表格的类型,应该是0x00

section_syntax_indicator:1 bit,段语法标志,应该是''1'' ''0'':固定的''0'',这是为了防止和ISO13818Video流格式中的控制字冲突而设置的.

Reserved:保留的2bits,保留位一般都是''0''

section_length:12bits的段大小,单位是Bytes.

transport_stream_id:16bits的当前流ID,DVB内唯一.(事实上很多都是自定义的TS ID) version_number:5bits版本号码,标注当前节目的版本.这是个非常有用的参数,当检测到这个字段改变时,说明TS流中的节目已经变化了,程序必须重新搜索节目.

current_next_indicator:1bit:当前还是未来使用标志符,一般情况下为''0''

section_number:8bits当前段号码

last_section_number:8bits最后段号码(section_number和 last_section_number的功能是当PAT内容>184字节时,PAT表会分成多个段(sections),解复用程序必须在全部接收完成后再进行PAT的分析)

从for()开始,就是描述了当前流中的频道数目(N),每一个频道对应的PMT PID是什么.解复用程序需要和上图类似的循环来接收所有的频道号码和对应的PMT PID,并把这些信息在缓冲区中保存起来.在后部的处理中需要使用到PMT PID.

CRC_32:本段的CRC校验值,一般是会忽略的.N是一个变量,计算方法是N=(section_length-9)/4.

从以上分析我们可以发现,PAT表主要包含频道号码和每一个频道对应的PMT的PID号码,这些信息我们在处理PAT表格的时候会保存起来,以后会使用到这些数据.例如我们可以定义这样的数据结构保存这些信息:

typedef struct

{

int channel_number;

int pmt_pid;

}PMT_ITEM;

PMT_ITEM pmt[64];

PMT, Program Map Table,节目影射表如果一个TS流中含有多个频道,
那么就会包含多个PID不同的PMT表.检测是否PMT的伪代码如下:
void Process_Packet(unsigned char*buff)
{ int I;

int PID=GETPID(buff);

if(PID==0x0000)

{

Process_PAT(buff+4);

}

else if(PID==.....)
{ }

else

{ for(i=0;i<64;i++)
{ if(PID==pmt[i].pmt_pid)

{ Process_PMT(buff+4); break; }
} } }
PMT表中包含的数据如下:

(1) 当前频道中包含的所有Video数据的PID

(2) 当前频道中包含的所有Audio数据的PID

(3) 和当前频道关联在一起的其他数据的PID(如数字广播,数据通讯等使用的PID)

PMT定义如下: 各字段含义如下:

table_id:8bits的ID,应该是0x02

section_syntax_indicator:1bit的段语法标志,应该是''1'' ''0'':固定是''0'',如果不是说明数据有错.

reserved:2bits保留位,应该是''00''

section_length:16bits段长度,从program_number开始,到CRC_32(包含)的字节总数. program_number:16bits的频道号码,表示当前的PMT关联到的频道.换句话就是说,当前描述的是program_number频道的信息.

reserved:2bits保留位,应该是''00''

version_number:版本号码,如果PMT内容有更新,则version_number会递增1通知解复用程序需要重新接收节目信息,否则 version_number是固定不变的.

current_next_indicator:当前未来标志符,一般是0

section_number:当前段号码

last_section_number:最后段号码,含义和PAT中的对应字段相同,请参考PAT部分. reserved:3bits保留位,一般是''000''.

PCR_PID:13bits的PCR PID,具体请参考ISO13818-1,解复用程序不使用该参数.

reserved:4bits保留位,一般是''0000''

program_info_length:节目信息长度(之后的是N个描述符结构,一般可以忽略掉,这个字段就代表描述符总的长度,单位是Bytes) 紧接着就是频道内部包含的节目类型和对应的PID号码了.

stream_type:8bits流类型,标志是Video还是Audio还是其他数据.

reserved:3 bits保留位.

elementary_PID:13bits对应的数据PID号码(如果stream_type是Video,那么这个PID就是Video PID,如果stream_type标志是Audio,那么这个PID就是Audio PID)

reserved:4 bits保留位.

ES_info_length:和program_info_length类似的信息长度(其后是N2个描述符号) CRC_32:32bits段末尾是本段的CRC校验值,一般忽略.

从以上的分析可以看出,只要我们处理了PMT,那么我们就可以获取频道中所有的PID信息,例如当前频道包含多少个Video,共多少个Audio,和其他数据,还能知道每种数据对应的PID分别是什么. 这样如果我们要选择其中一个Video和Audio收看,那么只需要把要收看的节目的Video PID和Audio PID保存起来,在处理Packet的时候进行过滤即可实现. 比较全面实现解复用的伪代码如下:

int Video_PID=0x07e5,Audio_PID=0x07e6;

void Process_Packet(unsigned char*buff)

{ int I; int PID=GETPID(buff);

if(PID==0x0000) { Process_PAT(buff+4); }

else if(PID==Video_PID) { SaveToVideoBuffer(buff+4); }

else if(PID==Audio_PID) { SaveToAudioBuffer(buff+4); }

else

{ for( i=0;i<64;i++)

{ if(PID==pmt[i].pmt_pid) { Process_PMT(buff+4); Break; }

} } }

以上伪代码可以实现基本的解复用:检测所有的频道,检测所有stream的PID,选择特定的节目进行播放.只要读取每个Packet的188字节的内容,然后每次都调用Process_Packet()即可实现简单的解复用. 介绍到这里,我们就可以总结一下DVB搜台的原理了.(好!洗耳恭听!) 机顶盒先调整高频头到一个固定的频率(如498MHZ),如果此频率有数字信号,则COFDM芯片(如MT352)会自动把TS流数据传送给MPEG- 2 decoder. MPEG-2 decoder先进行数据的同步,也就是等待完整的Packet的到来.然后循环查找是否出现PID== 0x0000的Packet,如果出现了,则马上进入分析PAT的处理,获取了所有的PMT的PID.接着循环查找是否出现PMT,如果发现了,则自动进入PMT分析,获取该频段所有的频道数据并保存.如果没有发现PAT或者没有发现PMT,说明该频段没有信号,进入下一个频率扫描. 从以上描述可以看出,机顶盒搜索频率是随机发生的,要使每次机顶盒都能搜索到信号,则要求TS流每隔一段时间就发送一次PAT和PMT.事实上DVB传输系统就是这么做的.因此无论何时接入终端系统,系统都能马上搜索到节目并正确解复用实现播放.不仅仅如此,其他数据也都是交替传送的.比如第一个Packet可能是PAT,第二个Packet可能是PMT,而第三个Packet可能是Video 1,第四个Packet可能是Video 2, 只要系统传输速度足够快(就是称之为"码率"的东东),实现实时播放是没有任何问题的. 到这里虽然实现了解复用,但可以看出,使用的PID都是枯燥的数字,如果调台要用户自己输入数字那可是太麻烦了,而且还容易输入错误,操作非常不直观,即使做成一个菜单让用户选择也是非常的呆板.针对这个问题,DVB系统提出了一个SDT表格,该表格标志一个节目的名称,并且能和 PMT中的PID联系起来,这样用户就可以通过直接选择节目名称来选择节目了.

SDT, Service description section,服务描述段

SDT可以提供的信息包括:

(1) 该节目是否在播放中

(2) 该节目是否被加密

(3) 该节目的名称

SDT定义如下: 各字段定义如下:

table_id:8bits的ID,可以是0x42,表示描述的是当前流的信息,也可以是0x46,表示是其他流的信息(EPG使用此参数)

section_syntax_indicator:段语法标志,一般是''1''

reserved_future_used:2bits保留未来使用

reserved:1bit保留位,防止控制字冲突,一般是''0'',也有可能是''1''

section_length:12bits的段长度,单位是Bytes,从transport_stream_id开始,到CRC_32结束(包含)

transport_stream_id:16bits当前描述的流ID

reserved:2bits保留位

version_number:5bits的版本号码,如果数据更新则此字段递增1

current_next_indicator:当前未来标志,一般是''0'',表示当前马上使用. original_netword_id:16bits的原始网络ID号

reserved_future_use:8bits保留未来使用位接下来是N个节目信息的循环:

service_id:16 bits的服务器ID,实际上就是PMT段中的program_number. reserved_future_used:6bits保留未来使用位

EIT_schedule_flag:1bit的EIT信息,1表示当前流实现了该节目的EIT传送EIT_present_following_flag:1bits的EIT信息,1表示当前流实现了该节目的EIT传送running_status:3bits的运行状态信息:1-还未播放 2-几分钟后马上开始,3-被暂停播出,4-正在播放,其他---保留

free_CA_mode:1bits的加密信息,''1''表示该节目被加密. 紧接着的是描述符,一般是Service descriptor,分析此描述符可以获取servive_id指定的节目的节目名称.具体格式请参考 EN300468中的Service descriptor部分.

分析完毕,则节目名称和节目号码已经联系起来了.机顶盒程序就可以用这些节目名称代替 PID让用户选择,从而实现比较友好的用户界面! 下面参考一下<>中的界面和显示信息. 上图是<>打开三个不同的码流文件(*.ts)形成的PID信息和节目名称.用户可以通过切换节目名称的下拉列表框切换节目,也可以通过"视频流"和"音频流"下拉列表框切换Video和Audio!这些数据都是通过分析PAT, PMT和SDT得到的.

http://blog.csdn.net/blackboyofsnp/article/details/4665409

对TS流的分析, 涉及到对PAT,PMT等等的分析, 具体内容见iso/iec 13838系列文档,

本文只给出自己写的一些和TS分析想关的代码, 简单的分析足够了, 不考虑复杂情况:

//
//
//  TS流结构定义
//
//
#ifndef ZZQ_TS_H_
#define ZZQ_TS_H_
#include <vector>
using namespace std;
typedef  unsigned char  byte;
typedef  unsigned int   bits;
#define  TSPKT_LENGTH  188
//-----------------------------------------------------------------------------
// 结构体
//-----------------------------------------------------------------------------
// TS包构成伪代码
/*
transport_packet()
{
sync_byte
transport_error_indicator
payload_unit_start_indicator
transport_priority
PID
transport_scrambling_control
adaptation_field_control
continuity_counter
if(adaptation_field_control=='10' || adaptation_field_control=='11')
{
adaptation_field()
}
if(adaptation_field_control=='01' || adaptation_field_control=='11')
{
for (i=0;i<N;i++)
{
data_byte
}
}
}
*/
// TS包头部
// WARNING!!!  暂时没有考虑修改区以及修改区内可选字段的处理
struct ts_header
{
bits  sync_byte                    : 8;
bits  transport_error_indicator    : 1;
bits  payload_unit_start_indicator : 1;
bits  transport_priority           : 1;
bits  PID                          : 13;
bits  transport_scrambling_control : 2;
bits  adaptation_field_control     : 2;
bits  continuity_counter           : 4;
};
// TS包中的修改字段(adaptation field)
struct ts_adaptation_field
{
bits  adaptation_field_length : 8;
bits  discontinuity_idicator  : 1;
bits  random_access_indicator : 1;
bits  elementary_stream_priority_indicator : 1;
bits  flags                   : 5; // 5 flags
};
// PAT中的可变段
struct ts_pat_section
{
bits  program_number : 16;
bits  reserved       : 3;
bits  PID            : 13;
};
typedef vector<ts_pat_section>  vec_pat_section;
// PAT表, P138-PAT的结构, 文档P61
struct ts_pat
{
bits  table_id                 : 8;  // 0x00
bits  section_syntax_indicator : 1;  // 1
bits  zero                     : 1;  // 0
bits  reserved_1               : 2;  // 11
bits  section_length           : 12;
bits  transport_stream_id      : 16; // 传输数据流识别
bits  reserved_2               : 2;  // "11"
bits  version_number           : 5;
bits  current_next_indicator   : 1;
bits  section_number           : 8;
bits  last_section_number      : 8;
// .... TODO
vec_pat_section network_section;
vec_pat_section pmt_section;
bits  crc32                    : 32;
};
// Stream_type分配表, P85
#define  ST_Reserved        0x00 // ITU-T | ISO/IEC Reserved
#define  ST_VIDEO_1         0x01 // ISO/IEC 11172 Video
#define  ST_VIDEO_2         0x02 // ITU-T Rec. H.262 | ISO/IEC 13818-2 Video or ISO/IEC 11172-2 constrained parameter video stream
#define  ST_AUDIO_1         0x03 // ISO/IEC 11172 Audio
#define  ST_AUDIO_2         0x04 // ISO/IEC 13818-3 Audio
#define  ST_PRIVATE_SECTIONS        0x05 // ITU-T Rec. H.222.0 | ISO/IEC 13818-1 private_sections
#define  ST_PES_PKT_PRIVATE_DATA    0x06 // ITU-T Rec. H.222.0 | ISO/IEC 13818-1 PES packets containing private data
#define  ST_MHEG            0x07 // ISO/IEC 13522 MHEG
#define  ST_DSMCC           0x08 // ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A DSM CC
#define  ST_H2221           0x09 // ITU-T Rec. H.222.1
#define  ST_TYPEA           0x0A // ISO/IEC 13818-6 type A
#define  ST_TYPEB           0x0B // ISO/IEC 13818-6 type B
#define  ST_TYPEC           0x0C // ISO/IEC 13818-6 type C
#define  ST_TYPED           0x0D // ISO/IEC 13818-6 type D
#define  ST_AUXILIARY       0x0E // ISO/IEC 13818-1 auxiliary
// 0x0F-0x7F: ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved
// 0x80-0xFF: User Private
// PMT表中的ES段
struct ts_pmt_es_section
{
bits  stream_type              : 8;
bits  reserved_1               : 3;
bits  elementary_PID           : 13;
bits  reserved_2               : 4;
bits  ES_info_length           : 12;
};
typedef vector<ts_pmt_es_section>  vec_pmt_section;
// PMT表, 书P138 - PMT的结构
struct ts_pmt
{
bits  table_id                 : 8;  // 0x02
bits  section_syntax_indicator : 1;  // 1
bits  zero                     : 1;  // 0
bits  reserved_1               : 2;  // 11
bits  section_length           : 12;
bits  program_number           : 16;
bits  reserved_2               : 2;  // 11
bits  version_number           : 5;
bits  current_next_indicator   : 1;
bits  section_number           : 8;
bits  last_section_number      : 8;
bits  reserved_3               : 3;  // 111
bits  PCR_PID                  : 13;
bits  reserved_4               : 4;  // 1111
bits  program_info_length      : 12;
vec_pmt_section  es_section;
bits  crc32                    : 32;
};
// PES包构成伪代码  P43
/*
太长,略
*/
// Steam ID分配表,  文档P47
#define  SID_PROGRAM_STREAM_MAP     0xBC
#define  SID_PRIVATE_STREAM_1       0xBD
#define  SID_PADDING_STREAM         0xBE
#define  SID_PRIVATE_STREAM_2       0xBF
#define  SID_AUDIO_STREAM           0xC0 // 110x xxxx, stream number x xxxx
#define  SID_VIDEO_STREAM           0xE0 // 1110 xxxx, stream number xxxx
#define  SID_ECM_STREAM             0xF0
#define  SID_EMM_STREAM             0xF1
#define  SID_DSMCC_STREAM           0xF2
#define  SID_13522_STREAM           0xF3
#define  SID_TYPEA                  0xF4
#define  SID_TYPEB                  0xF5
#define  SID_TYPEC                  0xF6
#define  SID_TYPED                  0xF7
#define  SID_TYPEE                  0xF8
#define  SID_ANCILLARY_STREAM       0xF9
// 1111 1010 ~ 1111 1110, reserved data stream
#define  SID_PROGRAM_STREAM_DIRECTORY   0xFF
// PES包中的可选包头部
struct optional_pes_header
{
bits  prefix                   : 2; // "10"
bits  pes_scrambling_control   : 2;
bits  pes_priority             : 1;
bits  data_alignment_indicator : 1;
bits  copyright                : 1;
bits  original_or_copy         : 1;
// 7 flags start
bits  PTS_DTS_flags            : 2;
bits  ESCR_flag                : 1;
bits  ES_rate_flag             : 1;
bits  DSM_trick_mode_flag      : 1;
bits  additional_copy_info_flag: 1;
bits  PES_CRC_flag             : 1;
bits  PES_extension_flag       : 1;
// 7 flags end
bits  pes_header_data_length   : 8;
// WARNING!!! 目前只使用 PTS, DTS
bits  PTS_H                    : 3;  // 32..30
bits  PTS_M                    : 15; // 29..15
bits  PTS_L                    : 15; // 14..0
bits  DTS_H                    : 3;  // 32..30
bits  DTS_M                    : 15; // 29..15
bits  DTS_L                    : 15; // 14..0
};
// PES包头部, 文档P43, 书P131
struct pes_header
{
bits  packet_start_code_prefix : 24;
bits  stream_id                : 8;
bits  pes_packet_length        : 16;
// 之后是optional PES header, 即任意包头, 它的有无由stream_id决定
bool  ophdr_flag;
optional_pes_header ophdr;  // 如果声明为指针, 会带来一些内存问题
//byte*  es_data;   // 实际数据指针
int   offset;   // 实际数据偏移
pes_header()
{
ophdr_flag = false;
offset = -1;
}
~pes_header()
{}
};
//-----------------------------------------------------------------------------
// 位段结构调整函数
//-----------------------------------------------------------------------------
int adjust_ts_header(ts_header* pkt, byte* buff);
int adjust_ts_pat(ts_pat* pkt, byte* buff);
int adjust_ts_pmt(ts_pmt* pkt, byte* buff);
int adjust_ts_pes_header(pes_header* pkt, byte* buff);
__int64  get_pts_from_pes(pes_header& pkt);
__int64  get_dts_from_pes(pes_header& pkt);
#endif
//
//
//  TS流相关函数
//
//
#include <stdio.h>
#include "zzq_TS.h"
int adjust_ts_header(ts_header* pkt, byte* buff)
{
if(pkt == NULL || buff == NULL)
{   return 1;   }
pkt->sync_byte = buff[0];
pkt->transport_error_indicator = buff[1] >> 7;
pkt->payload_unit_start_indicator = buff[1] >> 6 & 0x01;
pkt->transport_priority = buff[1] >> 5 & 0x01;
pkt->PID = (buff[1] & 0x1f)<<8 | buff[2];
pkt->transport_scrambling_control = buff[3] >> 6;
pkt->adaptation_field_control = buff[3] >> 4 & 0x03;
pkt->continuity_counter = buff[3] & 0x03;
return 0;
}
int adjust_ts_pat(ts_pat* pkt, byte* buff)
{
if(pkt == NULL || buff == NULL)
{   return 1;   }
pkt->table_id = buff[0];
pkt->section_syntax_indicator = buff[1] >> 7;
pkt->zero = buff[1] >> 6 & 0x1;
pkt->reserved_1 = buff[1] >> 4 & 0x3;
pkt->section_length = (buff[1] & 0x0f) << 8 | buff[2];
pkt->transport_stream_id = buff[3] << 8 | buff[4];
pkt->reserved_2 = buff[5] >> 6;
pkt->version_number = buff[5] >> 1 & 0x1f;
pkt->current_next_indicator = (buff[5] << 7) >> 7;
pkt->section_number = buff[6];
pkt->last_section_number = buff[7];
// get crc32
int len = 3 + pkt->section_length;
pkt->crc32 = (buff[len-4] & 0x000000ff) << 24 |
(buff[len-3] & 0x000000ff) << 16 |
(buff[len-2] & 0x000000ff) << 8  |
(buff[len-1] & 0x000000ff);
// get variable section
for(int i=0; i<pkt->section_length-4-8; i+=4 )
{
ts_pat_section sec;
sec.program_number = buff[8+i] << 8 | buff[9];
sec.reserved = buff[10+i] >> 5;
sec.PID = (buff[10+i] << 3) << 5 | buff[11+i];
if(sec.program_number == 0x0)
pkt->network_section.push_back(sec);
else
pkt->pmt_section.push_back(sec);
}
return 0;
}
int adjust_ts_pmt(ts_pmt* pkt, byte* buff)
{
if(pkt == NULL || buff == NULL)
{   return 1;   }
pkt->table_id = buff[0];
pkt->section_syntax_indicator = buff[1] >> 7;
pkt->zero = buff[1] >> 6;
pkt->reserved_1 = buff[1] >> 4;
pkt->section_length = (buff[1] & 0x0f) << 8 | buff[2];
pkt->program_number = buff[3] << 8 | buff[4];
pkt->reserved_2 = buff[5] >> 6;
pkt->version_number = buff[5] >> 1 & 0x1f;
pkt->current_next_indicator = (buff[5] << 7) >> 7;
pkt->section_number = buff[6];
pkt->last_section_number = buff[7];
pkt->reserved_3 = buff[8] >> 5;
pkt->PCR_PID = ((buff[8] << 8) | buff[9]) & 0x1fff;
pkt->reserved_4 = buff[10] >> 4;
pkt->program_info_length = (buff[10] & 0x0f) << 8 | buff[11];
// get crc32
int len = pkt->section_length + 3;
pkt->crc32 = (buff[len-4] & 0x000000ff) << 24 |
(buff[len-3] & 0x000000ff) << 16 |
(buff[len-2] & 0x000000ff) << 8  |
(buff[len-1] & 0x000000ff);
// skip program description
int pos = 12 + pkt->program_info_length;
for( ; pos <= (pkt->section_length-4); pos+=5)
{
ts_pmt_es_section sec;
sec.stream_type = buff[pos];
sec.reserved_1 = buff[pos+1] >> 5;
sec.elementary_PID = ((buff[pos+1] << 8) | buff[pos+2]) & 0x1fff;
sec.ES_info_length = (buff[pos+3] & 0x0f) << 8 | buff[pos+4];
pkt->es_section.push_back(sec);
pos += sec.ES_info_length;
}
return 0;
}
int adjust_ts_pes_header(pes_header* pkt, byte* buff)
{
if(pkt == NULL || buff == NULL)
{   return 1;   }
// 0000 0000 0000 0000 0000 0001 : 0x000001
pkt->packet_start_code_prefix = (buff[0] & 0x0000ff) << 16 |
(buff[1] & 0x0000ff) << 8  |
(buff[2] & 0x0000ff);
//if(pkt->packet_start_code_prefix != 0x000001) return 2;
byte sid = buff[3]; // aux
pkt->stream_id = buff[3];
pkt->pes_packet_length = buff[4] << 8 | buff[5];
if(sid != SID_PROGRAM_STREAM_MAP &&
sid != SID_PADDING_STREAM &&
sid != SID_PRIVATE_STREAM_2 &&
sid != SID_ECM_STREAM &&
sid != SID_EMM_STREAM &&
sid != SID_PROGRAM_STREAM_DIRECTORY &&
sid != SID_DSMCC_STREAM &&
sid != SID_TYPEE
)
{
pkt->ophdr_flag = true;
pkt->ophdr.prefix = buff[6] >> 6; // "10" : 2
//if(pkt->ophdr->prefix != 2)    return 2;
pkt->ophdr.pes_scrambling_control = buff[6] >> 4 & 0x03;
pkt->ophdr.pes_priority = buff[6] >> 3 & 0x01;
pkt->ophdr.data_alignment_indicator = buff[6] >> 2 & 0x01;
pkt->ophdr.copyright = buff[6] >> 1 & 0x01;
pkt->ophdr.original_or_copy = buff[6] & 0x01;
pkt->ophdr.PTS_DTS_flags = buff[7] >> 6 & 0x03;
pkt->ophdr.ESCR_flag = buff[7] >> 5 & 0x01;
pkt->ophdr.ES_rate_flag = buff[7] >> 4 & 0x01;
pkt->ophdr.DSM_trick_mode_flag = buff[7] >> 3 & 0x01;
pkt->ophdr.additional_copy_info_flag = buff[7] >> 2 & 0x01;
pkt->ophdr.PES_CRC_flag = buff[7] >> 1 & 0x01;
pkt->ophdr.PES_extension_flag  = buff[7] & 0x01;
pkt->ophdr.pes_header_data_length = buff[8];
if(pkt->ophdr.PTS_DTS_flags == 0x2) // "10"
{
// 以下移位操作, 都将各位串靠左对齐
pkt->ophdr.PTS_H = buff[9] << 3 & 0xe0; // 3 bits
pkt->ophdr.PTS_M = (buff[10] << 8) | (buff[11] & 0xfe); // 15 bits
pkt->ophdr.PTS_L = (buff[12] << 8) | (buff[13] & 0xfe); // 15 bits
}
else if(pkt->ophdr.PTS_DTS_flags == 0x3) // "11"
{
// 以下移位操作, 都将各位串靠左对齐
pkt->ophdr.PTS_H = buff[9] << 3 & 0xe0; // 3 bits
pkt->ophdr.PTS_M = (buff[10] << 8) | (buff[11] & 0xfe); // 15 bits
pkt->ophdr.PTS_L = (buff[12] << 8) | (buff[13] & 0xfe); // 15 bits
pkt->ophdr.DTS_H = buff[14] << 3 & 0xe0;
pkt->ophdr.DTS_M = (buff[15] << 8) | (buff[16] & 0xfe); // 15 bits
pkt->ophdr.DTS_L = (buff[17] << 8) | (buff[18] & 0xfe); // 15 bits
}
//
//  ESCR, ES_rate之类的目前不处理
// 6 + 3: (24+8+16) + (2+2+1+1+1+1+8+8)
pkt->offset = 6 + 3 + pkt->ophdr.pes_header_data_length;
}
else if(sid == SID_PROGRAM_STREAM_MAP ||
sid == SID_PRIVATE_STREAM_2 ||
sid == SID_ECM_STREAM ||
sid == SID_EMM_STREAM ||
sid == SID_PROGRAM_STREAM_DIRECTORY ||
sid == SID_DSMCC_STREAM ||
sid == SID_TYPEE
)
{
pkt->offset = 6;
}
else if(sid == SID_PADDING_STREAM)
{
pkt->offset = -1;
}
return 0;
}
__int64  get_pts_from_pes(pes_header& pkt)
{
if(pkt.ophdr_flag == false)   return -1;
if(pkt.ophdr.PTS_DTS_flags != 0x2 && pkt.ophdr.PTS_DTS_flags != 0x3)
return -1;
__int64 H = 0, M = 0, L = 0;
L = pkt.ophdr.PTS_L >> 1 | pkt.ophdr.PTS_M << 14;
M = pkt.ophdr.PTS_M >> 2 | pkt.ophdr.PTS_H << 13;
L = pkt.ophdr.PTS_H >> 3;
return (H<<32) | (M<<16) | L;
}
__int64  get_dts_from_pes(pes_header& pkt)
{
if(pkt.ophdr_flag == false)   return -1;
if(pkt.ophdr.PTS_DTS_flags != 0x3)
return -1;
__int64 H = 0, M = 0, L = 0;
L = pkt.ophdr.DTS_L >> 1 | pkt.ophdr.DTS_M << 14;
M = pkt.ophdr.DTS_M >> 2 | pkt.ophdr.DTS_H << 13;
L = pkt.ophdr.DTS_H >> 3;
return (H<<32) | (M<<16) | L;
}

PAT、PMT、SDT详解 MPEG2-TS流的分析相关推荐

  1. PAT、PMT、SDT详解

    http://blog.chinaunix.NET/uid-24322243-id-2620180.html 下面针对解复用程序详细分析一下PAT,PMT和SDT三类表格的格式. PAT---Prog ...

  2. RTSP协议详解与实时流视频预览-第6/11季视频课程-海思-朱有鹏-专题视频课程

    RTSP协议详解与实时流视频预览-第6/11季视频课程-海思-383人已学习 课程介绍         本季详细讲解RTSP协议的技术细节,并且编程实现基于RTSP协议的实时视频流传输,在局域网内浏览 ...

  3. FPGA学习之路—接口(3)—SPI详解及Verilog源码分析

    FPGA学习之路--SPI详解及Verilog源码分析 概述 SPI = Serial Peripheral Interface,是串行外围设备接口,是一种高速,全双工,同步的通信总线. 优点 支持全 ...

  4. python数据挖掘课程】二十一.朴素贝叶斯分类器详解及中文文本舆情分析

    #2018-04-06 13:52:30 April Friday the 14 week, the 096 day SZ SSMR python数据挖掘课程]二十一.朴素贝叶斯分类器详解及中文文本舆 ...

  5. PID详解3(摄像头循迹分析)

    PID详解3(摄像头循迹分析) 看了那么多成功的案例和大佬们的分享.讲解,终于轮到我们自己来设计PID了. 首先需要分析的是,想要用PID,我们得先知道,我们需要通过传感器拿到哪些参数,我们处理后要得 ...

  6. MPEG2 -TS流结构详细浅析

    一.概述 MPEG-2是MPEG(Moving Picture Experts Group,运动图像专家组)组织制定的视频和音频有损压缩标准之一,它的正式名称为"基于数字存储媒体运动图像和语 ...

  7. 详解API Gateway流控实现,揭开ROMA平台高性能秒级流控的技术细节

    摘要:ROMA平台的核心系统ROMA Connect源自华为流程IT的集成平台,在华为内部有超过15年的企业业务集成经验. 本文分享自华为云社区<ROMA集成关键技术(1)-API流控技术详解& ...

  8. RTSP协议详解与实时流视频预览(1)

    目录 一.本季核心技术点 二.H264编码原理和基本概念 1.h.264编码原理 2.h.264编码相关的一些概念 三.H264的NAL单元详解 1.VCL和NAL的关系 2.H.264视频流分析工具 ...

  9. tomcat使用详解(week4_day2)--技术流ken

    tomcat简介 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache.Sun和其他一些公司及个人共同开发 ...

最新文章

  1. 代码片段管理工具_VS代码片段:提高编码效率的最强大工具
  2. 安卓gridview 网格,多行多列实现
  3. C# 数组 二维数组
  4. DL框架之MXNet :深度学习框架之MXNet 的简介、安装、使用方法、应用案例之详细攻略
  5. Vue+Openlayers+Draw实现画笔切换功能,切换画笔为点、线、面
  6. 无障碍开发(一)之初认识
  7. scala学习笔记-Array、ArrayBuffer以及遍历数组(7)
  8. 在openstack环境中安装rackspace private cloud --1 环境准备
  9. Entity Framework 批量插入
  10. 代码的坏味道之一——译自《重构》
  11. 下载丨 MySQL运维管理+编程开发大全
  12. ES6入门之对象的扩展
  13. JSK-383 是否闰年(课后)【入门】
  14. form表单target实现当前页表单提交而不进行跳转刷新
  15. TFTP服务器的使用
  16. 最近火爆全网的猫猫回收站教程,小七给你们搞来了
  17. 百度地图 大头针设置本地图片与网络图片
  18. 详解强大的SQL注入工具——SQLMAP
  19. python 基础语法--print,input,open的内置函数的操作
  20. 从山景城看,Android看起来像什么? 关于Google I / O的见解

热门文章

  1. scope hosting_VPS Hosting 101 –虚拟专用服务器入门指南
  2. 全阶滑模观测器程序_感应电动机全局高阶滑模观测器
  3. 免费快递查询接口不限量对接
  4. Linux Top command
  5. Neo4j数据模型设计
  6. SnowflakeIdWorker类中SystemUtils.getHostName()在Mac环境下为空,导致空指针异常
  7. Telegram Android源码问题汇总 持续更新
  8. GitHub Copilot的下载使用方法(2022最新)
  9. 经典java面试题(持续更新)
  10. 中国芯,第1篇:一颗“四端口 LVDS转 HDMI2.0 与音频”的芯片