非常实用的解析TS流源码
废话不说,直接上传源码。
TsParser.cpp:
#include "stdafx.h"
#include "crc.h"
#include "log.h"
#include "xstring.h"
#include "Xml.h"
#include "osfile.h"
#include "TsParser.h"
#include "OcgMem.h"
#include "MptsTransfer.h"
#include "main.h"
#include "osfile.h"
#include "osdir.h"
/* TS流分析器*/
CTsParser::CTsParser()
{
m_pat_filter = NULL;
m_pat_buf = NULL;
m_pat_size = 0;
m_sdt_filter = NULL;
m_bufsize = 12*188*1024;
m_head = 0;
m_tail = 0;
m_buf = (unsigned char*)OCG_MALLOC( m_bufsize ); }
CTsParser::~CTsParser()
{
uint16_t pmt_pid, serv_id;
SProgInfo spi;
TSSDTInfo sdt;
void *position;
if( m_pat_filter )
{
m_demux.DestroyFilter( m_pat_filter );
m_pat_filter = NULL;
}
if( m_pat_buf )
{
OCG_FREE( m_pat_buf );
m_pat_buf = NULL;
}
if( m_sdt_filter )
{
m_demux.DestroyFilter( m_sdt_filter );
m_sdt_filter = NULL;
}
if( m_buf )
{
OCG_FREE( m_buf );
m_buf = NULL;
}
position = m_pmtmap.GetFirst( &pmt_pid, &spi );
while( position )
{
if( spi.filter )
m_demux.DestroyFilter( spi.filter );
if( spi.pmt_buf )
OCG_FREE( spi.pmt_buf );
position = m_pmtmap.GetNext( &pmt_pid, &spi, position );
}
m_pmtmap.RemoveAll();
/* 清除SDT信息*/
position = m_sdtmap.GetFirst( &serv_id, &sdt );
while( position ) {
if( sdt.sdt_service_node ) {
TSSDTServiceInfo *serv_del, *serv_tmp = sdt.sdt_service_node;
while( serv_tmp ) {
serv_del = serv_tmp;
serv_tmp = serv_tmp->next;
serv_del->next = NULL;
OCG_FREE( serv_del );
}
}
if( sdt.sdt_multilingual_node ) {
TSSDTMultilingualInfo *multi_del, *multi_tmp = sdt.sdt_multilingual_node;
while( multi_tmp ) {
multi_del = multi_tmp;
multi_tmp = multi_tmp->next;
multi_del->next = NULL;
OCG_FREE( multi_del );
}
}
position = m_sdtmap.GetNext( &serv_id, &sdt, position );
}
m_sdtmap.RemoveAll();
}
/* PA T/PMT-SECTION输出回调函数*/
void CTsParser::FilterProc( void* filter, unsigned char *buf, int32_t size, uint32_t lParam ) {
if( filter == ((CTsParser*)lParam)->m_pat_filter )
((CTsParser*)lParam)->OnPatSection( filter, buf, size );
else if( filter == ((CTsParser*)lParam)->m_sdt_filter )
((CTsParser*)lParam)->OnSdtSection( filter, buf, size );
else
((CTsParser*)lParam)->OnPmtSection( filter, buf, size );
}
/* SDT-SECTION处理函数*/
bool CTsParser::OnSdtSection( void* filter, unsigned char *buf, int32_t size )
{
unsigned char *p, *q, *q1;
int32_t len, mutili_len;
uint16_t service_id, sdt_service_id;
uint32_t crc32, ival;
unsigned char version_number, section_number, last_section_number;
void *position;
TSSDTInfo* sdt_info = NULL;
TSSDTServiceInfo* sdt_service_des = NULL, *serv_tmp = NULL;
TSSDTMultilingualInfo* sdt_multilingual_des = NULL, *multi_tmp = NULL;
/* pat/pmt的尺寸不可能超过1024字节*/
if( 1024 < size || filter != m_sdt_filter )
return false;
/* CRC校验*/
crc32 = GetCrc32( buf, size-4 );
memcpy( &ival, buf+size-4, 4 );
if( ival != htonl(crc32) )
return false;
version_number = (buf[5] >> 1) & 0x1f;
section_number = buf[6];
last_section_number = buf[7];
if( last_section_number < section_number )
return false;
p = buf;
p += 11;
while( p < buf + (size - 4) )
{
/* service_id */
service_id = (p[0]<<8) | p[1];
p += 2;
/* EIT... */
p += 1;
/* descriptor_loop_length */
len = ((p[0]&0xf)<<8) + p[1];
p += 2;
position = m_sdtmap.GetFirst( &sdt_service_id, NULL );
while( position )
{
sdt_info = m_sdtmap.GetV alue( position );
if( sdt_info->sdt_service_id == service_id ) {
sdt_info->bIsSucceed = true;
break;
}
else {
sdt_info = NULL;
}
position = m_sdtmap.GetNext( &sdt_service_id, NULL, position );
}
/* 或许当前pat信息还没解析出来,存储结构还未建立好*/
if( !sdt_info )
return false;
/* descriptors: descriptor_tag(1)+descriptor_length(1)+... */
q = p;
while( p < q + len && p < buf + (size - 4) )
{
/* descriptor_tag */
switch( p[0] )
{
case 0x48:/* service_descriptor */
sdt_service_des = (TSSDTServiceInfo*)OCG_MALLOC( sizeof(TSSDTServiceInfo) );
if( !sdt_service_des )
return false;
memset( sdt_service_des, 0, sizeof(TSSDTServiceInfo) );
p += 2;
/* 获取service_descriptor 信息*/
sdt_service_des->sdt_service_type = *p;
p += 1;
memcpy( &sdt_service_des->sdt_service_provider_name, p + 1, *p );
sdt_service_des->sdt_service_provider_name[ *p ] = 0;
p += *p;
p += 1;
memcpy( &sdt_service_des->sdt_service_name, p + 1, *p );
sdt_service_des->sdt_service_name[ *p ] = 0;
p += *p;
p += 1;
/* 建立链表关系*/
if( !sdt_info->sdt_service_node ) {
sdt_info->sdt_service_node = sdt_service_des;
}
else {
serv_tmp = sdt_info->sdt_service_node;
while( serv_tmp ) {
if( !serv_tmp->next )
break;
serv_tmp = serv_tmp->next;
}
serv_tmp->next = sdt_service_des;
serv_tmp->next->next = NULL;
}
break;
case 0x5d:/* multilingul_service_name_descriptor */
mutili_len = p[1];
p += 2;
q1 = p;
while( p < q1 + mutili_len && p < buf + size ) {
sdt_multilingual_des = (TSSDTMultilingualInfo*)OCG_MALLOC( sizeof(TSSDTMultilingualInfo) );
if( !sdt_multilingual_des )
return false;
memset( sdt_multilingual_des, 0, sizeof(TSSDTMultilingualInfo) );
/* 获取multilingual_name_descriptor */
memcpy( &sdt_multilingual_des->sdt_ISO_639_language_code, p, 4 );
sdt_multilingual_des->sdt_ISO_639_language_code <<= 8;
sdt_multilingual_des->sdt_ISO_639_language_code = htonl( sdt_multilingual_des->sdt_ISO_639_language_code );
p += 3;
memcpy( &sdt_multilingual_des->sdt_service_provider_name, p + 1, *p );
sdt_multilingual_des->sdt_service_provider_name[ *p ] = 0;
p += *p;
p += 1;
memcpy( &sdt_multilingual_des->sdt_service_name, p + 1, *p );
sdt_multilingual_des->sdt_service_name[ *p ] = 0;
p += *p;
p += 1;
/* 建立链表关系*/
if( !sdt_info->sdt_multilingual_node ) {
sdt_info->sdt_multilingual_node = sdt_multilingual_des;
}
else {
multi_tmp = sdt_info->sdt_multilingual_node;
while( multi_tmp ) {
if( !multi_tmp->next )
break;
multi_tmp = multi_tmp->next;
}
multi_tmp->next = sdt_multilingual_des;
multi_tmp->next->next = NULL;
}
}
break;
default:
/* others descriptor_tag */
p += 2 + p[1];
break;
}
}
}
return true;
}
/* PA T-SECTION处理函数*/
bool CTsParser::OnPatSection( void* filter, unsigned char *buf, int32_t size )
{
unsigned char *p, *q;
uint16_t service_id;
uint16_t pmt_pid;
SProgInfo spi;
void *position;
uint32_t crc32, ival;
unsigned char version_number, section_number, last_section_number;
TSSDTInfo sdt;
/* pat/pmt的尺寸不可能超过1024字节*/
if( 1024 < size || filter != m_pat_filter )
return false;
/* CRC校验*/
crc32 = GetCrc32( buf, size-4 );
memcpy( &ival, buf+size-4, 4 );
if( ival != htonl(crc32) )
return false;
version_number = (buf[5] >> 1) & 0x1f;
section_number = buf[6];
last_section_number = buf[7];
if( last_section_number < section_number )
return false;
/* 如果PA T版本号改变,则删除所有PA T/PMT记录,重新生成所有信息*/ if( m_pat_buf && ( version_number != ((m_pat_buf[5] >> 1) & 0x1f) || last_section_number != m_pat_buf[7] ) )
{
OCG_FREE( m_pat_buf );
m_pat_buf = NULL;
m_pat_size = 0;
position = m_pmtmap.GetFirst( &pmt_pid, &spi );
while( position )
{
if( spi.filter )
m_demux.DestroyFilter( spi.filter );
if( spi.pmt_buf )
OCG_FREE( spi.pmt_buf );
position = m_pmtmap.GetNext( &pmt_pid, &spi, position );
}
m_pmtmap.RemoveAll();
}
if( m_pat_buf == NULL )
{
m_pat_size = ( last_section_number + 1 ) * 1024;
m_pat_buf = (unsigned char*)OCG_MALLOC( m_pat_size );
if( m_pat_buf == NULL )
return false;
memset( m_pat_buf, 0xff, m_pat_size );
}
/* 如果SECTION已经存在,则不必再次处理*/
p = m_pat_buf;
while( *p != 0xff && p < m_pat_buf + m_pat_size )
{
if( p[6] == section_number )
return false;
p += 3 + ((p[1]&0xf)<<8) + p[2];
}
/* BUFFER不够大: 不可能出现*/
if( m_pat_buf + m_pat_size < p + size )
{
OCG_ASSERT( false );
return false;
}
/* 保存PA T-SECTION */
memcpy( p, buf, size );
/* 提取PMT信息*/
q = p;
p += 8;
while( p + 4 <= q + size - 4 )
{
service_id = (p[0]<<8) | p[1];
p += 2;
if( service_id != 0 )
{
pmt_pid = ((p[0]&0x1f)<<8) | p[1];
/* 如果是第一次找到该PMT,则添加*/
if( m_pmtmap.Search( pmt_pid, &spi ) == NULL )
{
memset( &spi, 0, sizeof(spi) );
/* 开始过滤PMT */
spi.filter = m_demux.CreateFilterExt( pmt_pid, 0x02, 0xff,
service_id, 0xffff, 1024, CTsParser::FilterProc, (uint32_t)this );
/* 添加*/
m_pmtmap.Insert( pmt_pid, spi );
}
/* 如果是第一次找到该SDT,则添加*/
if( NULL == m_sdtmap.Search( service_id, &sdt ) )
{
memset( &sdt, 0, sizeof(sdt) );
sdt.sdt_service_id = service_id;
sdt.bIsSucceed = false;
/* 添加*/
m_sdtmap.Insert( service_id, sdt );
}
}
p += 2;
}
return true;
}
/* PMT-SECTION处理函数*/
bool CTsParser::OnPmtSection( void* filter, unsigned char *buf, int32_t size )
{
uint16_t pmt_pid;
SProgInfo *pspi;
void *position;
uint32_t crc32, ival;
int32_t len;
unsigned char *p;
/* pat/pmt的尺寸不可能超过1024字节*/
if( 1024 < size || buf[0] != 0x02 )
return false;
/* CRC校验*/
crc32 = GetCrc32( buf, size-4 );
memcpy( &ival, buf+size-4, 4 );
if( ival != htonl(crc32) )
{
log_error("mpts pmt crc error!");
return false;
}
position = m_pmtmap.GetFirst( &pmt_pid, NULL );
while( position )
{
pspi = m_pmtmap.GetV alue( position );
if( pspi->filter == filter )
{
/* 保存*/
if( pspi->pmt_buf && pspi->pmt_size < size )
{
OCG_FREE( pspi->pmt_buf );
pspi->pmt_buf = NULL;
}
if( pspi->pmt_buf == NULL )
{
pspi->pmt_size = size + 1024;
pspi->pmt_buf = (unsigned char *)OCG_MALLOC( pspi->pmt_size );
}
memcpy( pspi->pmt_buf, buf, size );
p = pspi->pmt_buf;
len = 3 + ((p[1]&0xf)<<8) + p[2];
if( size < len || pspi->pmt_size < len )
{
OCG_ASSERT( false );
break;
}
break;
}
position = m_pmtmap.GetNext( &pmt_pid, NULL, position );
}
return true;
}
/* 获得MessageGateway的带宽*/
int32_t CTsParser::ParseMsgGtwInfo( char *url, char *buf, int size, int32_t usetime ) {
void* pf = NULL;
int32_t len, val = 0;
char data[8] = {0};
char path[256] = {0};
char* dst;
if( NULL == url )
goto ERROR_EXIT;
memset( path, 0, sizeof(path) );
sprintf( path, "%s/%s", g_root_dir, url);
os_dir_trim_path( path );
pf = g_filecache->OpenFile( path, OS_FILE_READ_ONLY );
if( NULL == pf )
goto ERROR_EXIT;
g_filecache->SeekFile( pf, -8, SEEK_END );
len = g_filecache->ReadFile( pf, data, 4 );
g_filecache->CloseFile(pf);
memcpy( &val, data, 4 );
val = htonl(val);
dst = buf;
dst += sprintf( dst, "\r\n" );
dst += sprintf( dst, "
dst += sprintf( dst, "\r\n" );
return ( dst - buf );
ERROR_EXIT:
return -1;
}
/* 读取数据并分析PSI/SI */
int32_t CTsParser::Parse( char *url, char *buf, int size, int32_t usetime )
{
unsigned char *p = NULL;
char *dst;
uint16_t pid = 0, pmt_pid = 0, service_id = 0, serv_id = 0;;
void *position/*, *sdt_position*/;
SProgInfo spi;
uint32_t now, start_tick;
bool over;
int32_t len, timeout = 2001, pcr_complete = 0;
void *receiver = NULL;
double pcr_val = 0, TotalBitrate = 0, DataBitrate = 0;
TSPCRInfo pcr_info;
TSSDTInfo sdt;
if( (dst = strstr( url, ".obj" )) != NULL ) {
return ParseMsgGtwInfo( url, buf, size, usetime );
}
if( m_pat_buf )
{
OCG_FREE( m_pat_buf );
m_pat_buf = NULL;
}
position = m_pmtmap.GetFirst( &pmt_pid, &spi );
while( position )
{
if( spi.filter )
m_demux.DestroyFilter( spi.filter );
if( spi.pmt_buf )
OCG_FREE( spi.pmt_buf );
position = m_pmtmap.GetNext( &pmt_pid, &spi, position );
}
m_pmtmap.RemoveAll();
/* 清除SDT信息*/
position = m_sdtmap.GetFirst( &serv_id, &sdt );
while( position ) {
if( sdt.sdt_service_node ) {
TSSDTServiceInfo *serv_del, *serv_tmp = sdt.sdt_service_node;
while( serv_tmp ) {
serv_del = serv_tmp;
serv_tmp = serv_tmp->next;
serv_del->next = NULL;
OCG_FREE( serv_del );
}
sdt.sdt_service_node = NULL;
}
if( sdt.sdt_multilingual_node ) {
TSSDTMultilingualInfo *multi_del, *multi_tmp = sdt.sdt_multilingual_node;
while( multi_tmp ) {
multi_del = multi_tmp;
multi_tmp = multi_tmp->next;
multi_del->next = NULL;
OCG_FREE( multi_del );
}
sdt.sdt_multilingual_node = NULL;
}
position = m_sdtmap.GetNext( &serv_id, &sdt, position );
}
m_sdtmap.RemoveAll();
serv_id = 0;
pmt_pid = 0;
m_head = 0;
m_tail = 0;
/* 刚开始或协议切换,构造RECEIVER */
if( receiver )
g_render->Stop( receiver );
receiver = g_render->Rebuild( receiver, url );
if( receiver == NULL )
{
log_error( "set parse receiver failed: %s\n", url );
goto ERROR_EXIT;
}
if( g_render->Start( receiver ) != 0 )
goto ERROR_EXIT;
os_sys_usleep( 50 );
now = 0;
start_tick = os_sys_get_time();
/* 分析*/
while( now == 0 || now <= start_tick + timeout )
{
/* 将剩余数据移到缓冲区头部*/
if( 0 < m_head && m_bufsize < m_tail + 188 )
{
m_tail -= m_head;
if( 0 < m_tail )
memmove( m_buf, m_buf+m_head, m_tail );
m_head = 0;
}
/* 读新的数据*/
size = g_render->ReadData( receiver, (char*)m_buf+m_tail, m_bufsize-m_tail );
/* http等速度比较慢,解析时间从取得数据算起*/
if( 0 < size && 2001 == timeout ) {
now = 0;
start_tick = os_sys_get_time();
timeout = usetime;
}
/* 文件结束*/
if( size < 0 )
break;
m_tail += size;
/* 定位TS头部*/
p = m_buf + m_head;
while( p + 188 <= m_buf + m_tail )
{
if( p + 376 < m_buf + m_tail )
{
if( *p == 0x47 && *(p+188) == 0x47 && *(p+376) == 0x47 )
break;
}
else if( p + 188 < m_buf + m_tail )
{
if( *p == 0x47 && *(p+188) == 0x47 )
break;
}
else if( *p == 0x47 )
break;
p++;
}
/* 处理TS包: 过滤SI/PSI,更改PID */
while( p + 188 <= m_buf + m_tail )
{
if( *p != 0x47 )
{
while( *p != 0x47 && p + 188 <= m_buf + m_tail )
p++;
continue;
}
pid = ((p[1]&0x1f)<<8) | p[2];
/* 填充包*/
if( pid == 0x1fff )
{
}
/* PA T-ts */
else if( pid == 0 )
{
/* 创建PA T-SECTION过滤器*/
if( m_pat_filter == NULL )
{
m_pat_filter = m_demux.CreateFilterExt(
0, 0, 0xff, 0, 0, 1024, CTsParser::FilterProc, (uint32_t)this );
}
m_demux.OnData( p, 188 );
}
/* PMT-ts */
else if( m_pmtmap.Search( pid, NULL ) )
{
m_demux.OnData( p, 188 );
}
/* SDT, table_id=0x42: actual_transport_stream, 0x46:other_transport_stream */
else if( pid == 0x11 )
{
#if 0 //暂时注释掉解析sdt
/* 检查sdt的完整性*/
if( !sdt_compl ) {
sdt_position = m_sdtmap.GetFirst( &service_id, &sdt );
while( sdt_position )
{
if( !sdt.bIsSucceed )
{
sdt_compl = false;
break;
}
sdt_compl = true;
sdt_position = m_sdtmap.GetNext( &service_id, &sdt, sdt_position );
}
if( !sdt_compl ) {
/* 创建SDT-SECTION过滤器*/
if( m_sdt_filter == NULL )
{
m_sdt_filter = m_demux.CreateFilterExt(
0x11, 0x42, 0xff, 0, 0, 1024, CTsParser::FilterProc, (uint32_t)this );
}
m_demux.OnData( p, 188 );
}
}
#endif
}
if( !pcr_complete ) {
/* 检查当前package是否有pcr */
pcr_complete = GetPCRInfo( p, 188, &pcr_info );
if( -1 == pcr_complete ) {
pcr_complete = 0;
log_error("analyze pcr error\n");
}
else if( 1 == pcr_complete ) {
pcr_complete = 0;
}
else if( 2 == pcr_complete ) {
/* 由于变量字节的限制,运算偶尔会导致内存溢出,故把27M 的1000提到分子*/
pcr_val = ((pcr_info.pcr_back_base - pcr_info.pcr_first_base) * 300 * 2 + (pcr_info.pcr_back_ext - pcr_info.pcr_first_ext)) / (27 * 1000);
if( 0 != pcr_val ) {
TotalBitrate = (pcr_info.pack_num * 188 * 8 * 1000) / pcr_val;
DataBitrate = (pcr_info.data_pack_num * 188 * 8 * 1000) / pcr_val;
}
pcr_complete = 0;
}
else if( 3 == pcr_complete ) {
pcr_val = ((pcr_info.pcr_back_base - pcr_info.pcr_first_base) * 300 * 2 + (pcr_info.pcr_back_ext - pcr_info.pcr_first_ext)) / (27 * 1000);
if( 0 != pcr_val ) {
TotalBitrate = (pcr_info.pack_num * 188 * 8 * 1000) / pcr_val;
DataBitrate = (pcr_info.data_pack_num * 188 * 8 * 1000) / pcr_val;
}
}
}
p += 188;
/* 检查PMT的完整性,如果PMT完整就可以输出了*/
over = true;
position = m_pmtmap.GetFirst( &pmt_pid, &spi );
if( position == NULL )
{
over = false;
}
else
{
while( position )
{
if( spi.pmt_buf == NULL )
{
over = false;
break;
}
position = m_pmtmap.GetNext( &pmt_pid, &spi, position );
}
}
if( over ) {
if( pcr_info.bIsFindPCR ) {
if( 3 == pcr_complete ) {
goto PARSE_END;
}
else {
/* pmt完成,pcr没找足够,改变条件继续找*/
timeout = usetime / 2;
}
}
else {
/* pmt完成但一个pcr都没找到,改变条件继续找*/
timeout = usetime / 2;
}
}
#if 0 //暂时注释掉有sdt的结束判断
if( over ) {
if( sdt_compl ) {
if( pcr_info.bIsFindPCR ) {
if( 3 == pcr_complete ) {
goto PARSE_END;
}
else {
/* pmt sdt完成,pcr没找足够,改变条件继续找*/
timeout = usetime / 2;
}
}
else {
/* pmt sdt完成但一个pcr都没找到,改变条件继续找*/
timeout = usetime / 2;
}
}
else {
/* pmt完成,改变条件继续找*/
timeout = usetime / 2;
}
}
#endif
}
m_head = p - m_buf;
now = os_sys_get_time();
if( now < start_tick )
now = start_tick;
}
PARSE_END:
dst = buf;
dst += sprintf( dst, "\r\n" );
if( 0 < TotalBitrate ) {
dst += sprintf( dst, "
}
else {
dst += sprintf( dst, "
}
/* 输出*/
position = m_pmtmap.GetFirst( &pmt_pid, &spi );
while( position )
{
if( spi.pmt_buf )
{
p = spi.pmt_buf;
len = 3 + ( ((p[1]&0xf)<<8) | p[2] );
service_id = (p[3]<<8) | p[4];
pid = ((p[8]&0x1f)<<8) | p[9];
dst += sprintf( dst, "\t
service_id, pmt_pid, pid );
p += 12 + (((p[10]&0xf)<<8) | p[11]);
while( p < spi.pmt_buf + len - 4 )
{
pid = ((p[1]&0x1f)<<8) | p[2];
dst += sprintf( dst, "\t\t
p += 5 + (((p[3]&0xf)<<8) | p[4]);
}
dst += sprintf( dst, "\t\r\n" );
}
position = m_pmtmap.GetNext( &pmt_pid, &spi, position );
}
#if 0 //有sdt的信息暂时注释掉
while( position )
{
if( spi.pmt_buf )
{
p = spi.pmt_buf;
len = 3 + ( ((p[1]&0xf)<<8) | p[2] );
service_id = (p[3]<<8) | p[4];
pid = ((p[8]&0x1f)<<8) | p[9];
dst += sprintf( dst, "\t
service_id, pmt_pid, pid );
p += 12 + (((p[10]&0xf)<<8) | p[11]);
while( p < spi.pmt_buf + len - 4 )
{
pid = ((p[1]&0x1f)<<8) | p[2];
dst += sprintf( dst, "\t\t
p += 5 + (((p[3]&0xf)<<8) | p[4]);
}
/* 加入sdt信息*/
sdt_position = m_sdtmap.GetFirst( &serv_id, &sdt );
while( sdt_position )
{
if( service_id == sdt.sdt_service_id ) {
/* service_descriptor */
sdt_serv_out = sdt.sdt_service_node;
while( sdt_serv_out ) {
dst += sprintf( dst, "\t\t
sdt_serv_out->sdt_service_type,
sdt_serv_out->sdt_service_provider_name, sdt_serv_out->sdt_service_name );
sdt_serv_out = sdt_serv_out->next;
}
/* mutilingual_name_descriptor */
sdt_multi_out = sdt.sdt_multilingual_node;
while( sdt_multi_out ) {
dst += sprintf( dst, "\t\t
sdt_multi_out->sdt_ISO_639_language_code,
sdt_multi_out->sdt_service_provider_name, sdt_multi_out->sdt_service_name );
sdt_multi_out = sdt_multi_out->next;
}
}
sdt_position = m_sdtmap.GetNext( &serv_id, &sdt, sdt_position );
}
dst += sprintf( dst, "\t\r\n" );
}
position = m_pmtmap.GetNext( &pmt_pid, &spi, position );
}
#endif
dst += sprintf( dst, "\r\n" );
*dst = 0;
dst++;
HLS,Http Live Streaming是由Apple公司定义的用于实时流传输的协议,HLS基于HTTP 协议实现,传输内容包括两部分,一是M3U8描述文件,二是TS媒体文件。 1、M3U8文件 用文本方式对媒体文件进行描述,由一系列标签组成。 #EXTM3U #EXT-X-TARGETDURATION:5 #EXTINF:5, ./0.ts #EXTINF:5, ./1.ts #EXTM3U:每个M3U8文件第一行必须是这个tag。 #EXT-X-TARGETDURATION:指定最大的媒体段时间长度(秒),#EXTINF中指定的时间长度必须小于或等于这个最大值。该值只能出现一次。 #EXTINF:描述单个媒体文件的长度。后面为媒体文件,如./0.ts 2、ts文件 ts文件为传输流文件,视频编码主要格式h264/mpeg4,音频为acc/MP3。 ts文件分为三层:ts层Transport Stream、pes层 Packet Elemental Stream、es层 Elementary Stream. es层就是音视频数据,pes层是在音视频数据上加了时间戳等对数据帧的说明信息,ts层就是在pes层加入数据流的识别和传输必须的信息
注:详解如下 (1)ts层ts包大小固定为188字节,ts层分为三个部分:ts header、adaptation field、payload。ts header固定4个字节;adaptation field可能存在也可能不存在,主要作用是给不足188字节的数据做填充;payload是pes 数据。 ts header
TS流也是由一个或多个PES组合而来的,他们可以具有相同的时间基准,也可以不同。其基本的复用思想是,对具有相同时间基准[color="#000000"]的多个PES现进行节目复用,然后再对相互有独立时间基准的各个PS进行传输复用,最终产生出TS。TS包由包头和包数据2部分组成,其中包头还可以包括扩展的自适用区。包头长度占4bytes,自使用区和包数据共占184bytes,整个TS包长度相当于4个ATM包长。TS包的包头由如下图摘录所示的同步字节、传输误码指示符、有效载荷单元起始指示符、传输优先、包识别(PID-Packet Identification)、传输加扰控制、自适应区控制和连续计数器8个部分组成。 其中,可用同步字节位串的自动相关特性,检测数据流中的包限制,建立包同步;传输误码指示符,是指有不能消除误码时,采用误码校正解码器可表示1bit 的误码,但无法校正;有效载荷单元起始指示符,表示该数据包是否存在确定的起始信息;传输优先,是给TS包分配优先权;PID值是由用户确定的,解码器根据PID将TS上从不同ES来的TS包区别出来,以重建原来的ES;传输加扰控制,可指示数据包内容是否加扰,但包头和自适应区永远不加扰;自适应区控制,用2 bit表示有否自适应区,即(01)表示有有用信息无自适应区,(10)表示无有用信息有自适应区,(11)表示有有用信息有自适应区,(00)无定义;连续计数器可对PID包传送顺序计数,据计数器读数,接收端可判断是否有包丢失及包传送顺序错误。显然,包头对TS包具有同步、识别、检错及加密功能。 TS包自适应区由自适应区长、各种标志指示符、与插入标志有关的信息和填充数据4部分组成。其中标志部分由间断指示符、随机存取指示符、ES优化指示符、PCR标志、接点标志、传输专用数据标志、原始PCR标志、自适应区扩展标志8个部分组成。重要的是标志部分的PCR字段,可给编解码器的27MHz时钟提供同步资料,进行同步。其过程是,通过PLL,用解码时本地用PCR相位与输入的瞬时PCR相位锁相比较,确定解码过程是否同步,若不同步,则用这个瞬时PCR调整时钟频率。因为,数字图像采用了复杂而不同的压缩编码算法,造成每幅图像的数据各不相同,使直接从压缩编码图像数据的开始部分获取时钟信息成为不可能。为此,选择了某些(而非全部)TS包的自适应区来传送定时信息。于是,被选中的TS包的自适应区,可用于测定包信息的控制bit和重要的控制信息。自适应区无须伴随每个包都发送,发送多少主要由选中的TS包的传输专用时标参数决定。标志中的随机存取指示符和接点标志,在节目变动时,为随机进入I帧压缩的数据流提供随机进入点,也
transport stream MPEG组织于1994年推出MPEG-2压缩标准,以实现视/音频服务与应用互操作的可能性,MPEG-2标准是针对标准数字电视和高清晰度电视在各种应用下的压缩方案和系统层的详细规定。对应于不同的应用,符合MPEG-2标准的码流又分为传送流和程序流,本文主要讲解了传送流有关的部分数据结构,从实际应用的传送流码流中截取了部分码流做了说明,并给出了部分解析传送流码流的实例程序。 在MPEG-II标准中,为了将一个或更多的音频、视频或其他的基本数据流合成单个或多个数据流,以适应于存储和传送,必须对其重新进行打包编码,在码流中还需插入各种时间标记、系统控制等信息,最后送到信道编码与调制器。这样可以形成两种数据流——传送流(TS)和程序流(PS),分别适用于不同的应用,图1给出了单路节目的视音频数据流的复用框图。 传送流(Transport Stream)简称TS流,它是根据ITU-T Rec.H.222.0|ISO/IEC 13818-2 和ISO/IEC 13818-3协议而定义的一种数据流,其目的是为了在有可能发生严重错误的情况下进行一道或多道程序编码数据的传送和存储。这种错误表现为比特值错误或分组丢失。传送流由一道或多道节目组成,每道节目由一个或多个原始流和一些其他流复合在一起,包括视频流、音频流、节目特殊信息流(PSI)和其他数据包。其中PSI表有4种类型:节目关联表(PAT)、节目映射表(PMT)、网络信息表和条件访问表。传送流应用比较广泛,如视音频资料的保存、电视节目的非线性编辑系统及其网络等。在开发机顶盒以及视频设备时有时需要对码流的编码知识有比较清楚地了解,这样才能在遇到问题时做出全面的分析。 TS流结构分析 如图2所示,TS包的长度是固定的,为188字节。包括同步字节(sync_byte)0x47和数据包识别号PID等。PID为13位字段,指示存储于分组有效负载中数据的类型,PID值0x0000为程序关联表保留,而0x0001为条件访问表保留,0x1FFF为空分组保留。从PID可以判断其后面负载的数据类型是视频流、音频流、PSI还是其他数据包。 PSI描述说明 在MPEG-II中定义了节目特定信息(PSI),PSI用来描述传送流的组成结构,在MPEG-II系统中担任极其重要的角色,在多路复用中尤为重要的是PAT表和PMT表。PAT表给出了一路MPEG-II码流中有多少套节目,以及它与PMT表PID之间的对应关系;PMT表给出了一套节目的具体组成情况与其视频、音频等PID对应关系。PSI提供了使接收机能够自动配置的信息,用于对复用流中的不同节目流进行解复用和解码。PSI信息由以下几种类型表组成: ◆节目关联表(PAT Program Association Table) PAT表用MPEG指定的PID(00)标明,通常用PID=0表示。它的主要作用是针对复用的每一路传输流,提供传输流中包含哪些节目、节目的编号以及对应节目的节目映射表(PMT)的位置,即PMT的TS包的包标识符(PID)的值,同时还提供网络信息表(NIT)的位置,即NIT 的TS包的包标识符(PID)的值。 ◆条件接收表(CAT Conditional Access Table) CAT表用MPEG指定的PID(01)标明,通常用PID=1表示。它提供了在复用流中条件接收系统的有关信息,指定CA系统与它们相应的授权管理信息(EMM))之间的联系,指定EMM 的PID,以及相关的参数。 ◆节目映射表(PMT Program Map Table) 节目映射表指明该节目包含的内容,即该节目由哪些流组成,这些流的类型(音频、视频、数据),以及组成该节目的流的位置,即对应的TS包的PID值,每路节目的节目时钟参考(PCR)
HLS,Http Live Streaming 是由Apple公司定义的用于实时流传输的协议,HLS基于HTTP 协议实现,传输内容包括两部分,一是M3U8描述文件,二是TS媒体文件。 1、M3U8文件 用文本方式对媒体文件进行描述,由一系列标签组成。 #EXTM3U #EXT-X-TARGETDURATION:5 #EXTINF:5, ./0.ts #EXTINF:5, ./1.ts #EXTM3U:每个M3U8文件第一行必须是这个tag。 #EXT-X-TARGETDURATION:指定最大的媒体段时间长度(秒),#EXTINF中指定的时间长度必须小于或等于这个最大值。该值只能出现一次。 #EXTINF:描述单个媒体文件的长度。后面为媒体文件,如./0.ts 2、ts文件 ts文件为传输流文件,视频编码主要格式h264/mpeg4,音频为acc/MP3。 ts文件分为三层:ts层Transport Stream、pes层 Packet Elemental Stream、es层 Elementary Stream. es层就是音视频数据,pes层是在音视频数据上加了时间戳等对数据帧的说明信息,ts层就是在pes层加入数据流的识别和传输必须的信息
注:详解如下 (1)ts层 ts包大小固定为188字节,ts层分为三个部分:ts header、adaptation field、payload。ts header固定4个字节;adaptation field可能存在也可能不存在,主要作用是给不足188字节的数据做填充;payload是pes数据。 ts header sync_byte 8b 同步字节,固定为0x47 transport_error_indicator 1b 传输错误指示符,表明在ts头的adapt域后由一个无用字节,通常都为0,这个字节算在adapt域长度内 payload_unit_start_indicator 1b 负载单元起始标示符,一个完整的数据包开始时标记为1 transport_priority 1b 传输优先级,0为低优先级,1为高优先级,通常取
#include
MPEG-2 TS 码流编辑的原理与应用 在当今数字媒体不断发展、新媒体业务不断涌现的前提下,实践证明襁褓中的新媒体只有两种经营方略可供选择:或是购买并集成整套节目,或是低成本深加工新节目,再不可能去按照传统生产模式去自采自编。低成本的节目生产制作与发布,不仅成为数字媒体经营的主要手段,也成为传统媒体“改革工作流程”的重要举措,进而促成了对新型工作母机和简捷快速流程的迫切需求。 在辽宁新媒体多业务综合服务平台上,先于国际和国内应用了MPEG-2传输流快速剪辑编辑系统(以下简称码流快编)。这项由辽宁电视台与深圳奥维迅公司在2003年10月联合开发的新技术,为数字媒体低成本节目的制作、推广和运营提供了高效生产工作母机。尽管担负此项目源代码开发的奥维迅公司出现了经营问题,在技术推广的中间环节发生梗塞,但并不能说明此项技术走到了尽头。回顾3年的应用实践及研发成果,需要的不是扬弃,而是演进的升级,否则就是对可调控资源的莫大浪费。特别是针对第二代信源编解码国标AVS-P2的更新换代,很可能成为多业务内容整合的新一代产品的突破口。 一工作原理 1. 功能目标 码流快编的应用目标是,通过对开放视频的采集,将DVB-S或C的传输流(Transport Stream,TS)节目作为信源,直接进行剪辑处理,再经过人工创意后,整合为新主题内容的新节目,以便直接进入频道集成或编辑频道节目播出,快速实现数据层的内容整合,不仅简捷了采集制作的工作流程,而且为丰富媒体内容资产开辟了一条捷径。因为码流快编的工作流程无需先以解码后的视频记录于磁带,再以磁带上载编辑机,经编辑后再下载成为磁带,再编码复用成为新内容的新节目。即便数字化完成以后,视频数据流仍不能用于经复用的数字传输,还需编码、转码、打包等传输格式化以后,才能在数字信道上传输。而采用码流快编以后,不仅避免了解码后再采样编码所形成的视频损耗,还避免了在1∶1时间的上下载中所造成的效率损耗。更重要的是在视频内容整合中,一次性完成音/视频同步剪切、字幕处理和音/视频数据打包复用等连续作业。所以,它能够提高生产效率60%以上。必要时还可进行节目包装的特技编辑,直接创建数据级和文件级的互联互通内容交换平台,在媒体资产管理下,顺利实现网络化与智能化的节目配送与发布。 由于码流快编是针对以TS为信源的再编辑系统,所以实行“高来高走,低来低走”,或是“高来低走”的应用策略,即高码率对应高码率(包括兼容高清),低码率对应低码率,但码率连续可调,以适应高码率对应低码率的应用。理论和应用都说明,对比源节目和成品节目,经剪切和编辑处理的图像保持了同等的视频质量,成为不劣化图像的创新工作流程和新型工作母机。 2. 设计特征
H265封装成TS流 #define STREAM_TYPE_VIDEO_MPEG1 0x01 #define STREAM_TYPE_VIDEO_MPEG2 0x02 #define STREAM_TYPE_AUDIO_MPEG1 0x03 #define STREAM_TYPE_AUDIO_MPEG2 0x04 #define STREAM_TYPE_PRIVATE_SECTION 0x05 #define STREAM_TYPE_PRIVATE_DATA 0x06 #define STREAM_TYPE_AUDIO_AAC 0x0f #define STREAM_TYPE_AUDIO_AAC_LATM 0x11 #define STREAM_TYPE_VIDEO_MPEG4 0x10 #define STREAM_TYPE_VIDEO_H264 0x1b
#define STREAM_TYPE_VIDEO_HEVC 0x24 //Definition of 0x24 HEVC video MPEG TS stream type #define STREAM_TYPE_VIDEO_CAVS 0x42 #define STREAM_TYPE_VIDEO_VC1 0xea #define STREAM_TYPE_VIDEO_DIRAC 0xd1#define STREAM_TYPE_AUDIO_AC3 0x81 #define STREAM_TYPE_AUDIO_DTS 0x82 #define STREAM_TYPE_AUDIO_TRUEHD 0x83
TS流解析之PMT表格解析 2010-12-14 08:44 TS流解析之PMT表格解析 PMT结构定义: typedef struct TS_PMT_Stream { unsigned stream_type : 8; //指示特定PID的节目元素包的类型。该处PID由elementary PID指定 unsigned elementary_PID : 13; //该域指示TS包的PID 值。这些TS包含有相关的节目元素 unsigned ES_info_length : 12; //前两位bit为00。该域指示跟随其后的描述相关节目元素的byte数 unsigned descriptor; }TS_PMT_Stream; //PMT 表结构体 typedef struct TS_PMT { unsigned table_id : 8; //固定为0x02, 表示PMT表 unsigned section_syntax_indicator : 1; //固定为0x01 unsigned zero : 1; //0x01 unsigned reserved_1 : 2; //0x03 unsigned section_length : 12;//首先两位bit置为00,它指示段的byte数,由段长度域开始,包含CRC。 unsigned program_number : 16;// 指出该节目对应于可应用的Program map PID unsigned reserved_2 : 2; //0x03 unsigned version_number : 5; //指出TS流中Program map section的版本号 unsigned current_next_indicator : 1; //当该位置1时,当前传送的Program map section可用; //当该位置0时,指示当前传送的Program map section不可用,下一个TS 流的Program map section有效。 unsigned section_number : 8; //固定为0x00 unsigned last_section_number : 8; //固定为0x00 unsigned reserved_3 : 3; //0x07 unsigned PCR_PID : 13; //指明TS包的PID值,该TS包含有PCR域, //该PCR值对应于由节目号指定的对应节目。 //如果对于私有数据流的节目定义与PCR无关,这个域的值将为
码流录制盒播放盒(DVB-C-S-T-TH) 码流录制盒是北京迪未数视最新推出的一款码流录制盒,可以从数字电视网中录制遵循DVB标准的MPEG-2 TS码流。软件操作简单,界面友好。兼备录制与播放DVB传输流为一身的专业数字电视工具。可以完成播放和录制功能。该设备是网口输出,直接接PC网口,TS流就可以通过网络接口录制到电脑硬盘上。 专业的数字电视码流采集设备,免安装驱动,支持各种操作系统,即插即用,既可以实时播放数字电视节目,又可以录制TS码流并保存到电脑上。 支持多种数字电视方式:DVB-C,DVB- S/S2,DVB-T/T2欧标,DTMB-TH国标。 特性: *支持DVB-C\DVB-T\DVB-S\DTMB-TH输入,符合MPEG2系统层规范 *支持100Base-TX网口,符合IEEE 802.3规范 *支持以网口输出,可以不用直接连接pc,直接连接与pc在同一个局域网的网络设备 *在PC端使用免安装的控制软件,不需要安装软件 *支持VLC播放,可以随时对传输流进行数据分析 *支持实时播放,实时录制和预约录制 *提供API,支持二次开发 *支持远程在线升级 *支持各种操作系统:Microsoft Windows 98/ME、2000、XP、Server2003、win7,linux等等
优势之处: 1.尺寸小,与家用路由器相当,携带方便。 2.安装简单,免安装驱动,只要你有信号源和电脑,录制节目流就不是问题。 3.方便保存,录制下来的节目直接保存在电脑硬盘里面,方便使用。 本产品采用以太网接口输出,可以远距离操作。 采用本产品录制TS流时,用户不需要在电脑上面安装任何软件,只需要运行一个免安装的很小的控制软件即可。 本设备体积小、重量轻,价格便宜。当STB厂商也可以把这个设备寄给您的客户,让客户帮忙录制TS。本设备友好的人机界面,使您的用户基本不需要了解数字电视相关的知识,只要会操作电脑就会录制TS。 技术规范: *以太网接口 -网络标准:IEEE 802.3 -接口介质:100Base-TX -传输模式:100Mbps全双工 *射频输入 -接口类型:长F头输入,IEC公头环路输出 -输入频率:50~862MHz -输入电平:45dBuV~80dBuV -输入阻抗:75Ω *电源适配器 -输入:100-240VAC -输出:12VDC 1A *外形尺寸 - 110 x 90 x 24 mm 产品连接 1.电视信号连接 用铜轴线将录制盒的射频输入与数字电视接收信号连接。 2.网络连接 用网线将录制盒的网口与电脑的网卡直接连接。 注意:需要手动配置电脑的网卡(与录制盒连接的网口)的IP地址。
MPEG组织于1994年推出MPEG-2压缩标准,以实现视/音频服务与应用互操作的可能性,MPEG-2标准是针对标准数字电视和高清晰度电视在各种应用下的压缩方案和系统层的详细规定。对应于不同的应用,符合MPEG-2标准的码流又分为传送流和程序流,本文主要讲解了传送流有关的部分数据结构,从实际应用的传送流码流中截取了部分码流做了说明,并给出了部分解析传送流码流的实例程序。 在MPEG-II标准中,为了将一个或更多的音频、视频或其他的基本数据流合成单个或多个数据流,以适应于存储和传送,必须对其重新进行打包编码,在码流中还需插入各种时间标记、系统控制等信息,最后送到信道编码与调制器。这样可以形成两种数据流——传送流(TS)和程序流(PS),分别适用于不同的应用,图1给出了单路节目的视音频数据流的复用框图。 传送流(Transport Stream)简称TS流,它是根据ITU-T Rec.H.222.0|ISO/IEC 13818-2 和ISO/IEC 13818-3协议而定义的一种数据流,其目的是为了在有可能发生严重错误的情况下进行一道或多道程序编码数据的传送和存储。这种错误表现为比特值错误或分组丢失。传送流由一道或多道节目组成,每道节目由一个或多个原始流和一些其他流复合在一起,包括视频流、音频流、节目特殊信息流(PSI)和其他数据包。其中PSI表有4种类型:节目关联表(PAT)、节目映射表(PMT)、网络信息表和条件访问表。传送流应用比较广泛,如视音频资料的保存、电视节目的非线性编辑系统及其网络等。在开发机顶盒以及视频设备时有时需要对码流的编码知识有比较清楚地了解,这样才能在遇到问题时做出全面的分析。 TS流结构分析 如图2所示,TS包的长度是固定的,为188字节。包括同步字节(sync_byte)0x47和数据包识别号PID 等。PID为13位字段,指示存储于分组有效负载中数据的类型,PID值0x0000为程序关联表保留,而0x0001为条件访问表保留,0x1FFF为空分组保留。从PID可以判断其后面负载的数据类型是视频流、音频流、PSI 还是其他数据包。 PSI描述说明 在MPEG-II中定义了节目特定信息(PSI),PSI用来描述传送流的组成结构,在MPEG-II系统中担任极其重要的角色,在多路复用中尤为重要的是PAT表和PMT表。PAT表给出了一路MPEG-II码流中有多少套节目,以及它与PMT表PID之间的对应关系;PMT表给出了一套节目的具体组成情况与其视频、音频等PID对应关系。PSI提供了使接收机能够自动配置的信息,用于对复用流中的不同节目流进行解复用和解码。PSI信息由以下几种类型表组成: ◆节目关联表(PAT Program Association Table) PAT表用MPEG指定的PID(00)标明,通常用PID=0表示。它的主要作用是针对复用的每一路传输流,提供传输流中包含哪些节目、节目的编号以及对应节目的节目映射表(PMT)的位置,即PMT的TS 包的包标识符(PID)的值,同时还提供网络信息表(NIT)的位置,即NIT的TS包的包标识符(PID)的值。 ◆条件接收表(CAT Conditional Access Table) CAT表用MPEG指定的PID(01)标明,通常用PID=1表示。它提供了在复用流中条件接收系统的有关信息,指定CA系统与它们相应的授权管理信息(EMM))之间的联系,指定EMM的PID,以及相关的参数。 ◆节目映射表(PMT Program Map Table) 节目映射表指明该节目包含的内容,即该节目由哪些流组成,这些流的类型(音频、视频、数据),以及组成该节目的流的位置,即对应的TS包的PID值,每路节目的节目时钟参考(PCR)字段的位置。 ◆网络信息表(NIT Nerwork Information Table) 网络信息表提供关于多组传输流和传输网络相关的信息,其中包含传输流描述符、通道频率、卫星发射器号码、调制特性等信息。 ◆传输流描述表(TSDT Transport Stream Description Table) 传输流描述表由PID为2的TS包传送,提供传输流的一些主要参数。 ◆专用段(private_section)
传输流(TS) 将具有共同时间基准或具有独立时间基准的一个或多个PES组合而成的单一的数据流称为传输流(Transport Stream)。TS实际是面向数字化分配媒介(有线、卫星、地面网)的传输层接口。对具有共同时间基准的两个以上的PES 先进行节目复用,然后再对相互可有独立时间基准的各个PS进行传输复用,即将每个PES再细分为更小的TS包 TS包由包头、自适应区和包数据3部分组成。每个包长度为固定的188B,包头长度占4 B,自适应区和包数据长度占184B。184B为有用信息空间,用于传送已编码的视音频数据流。当节目时钟基准(PCR-Program Clock Reference)存在时,包头还包括可变长度的自适应区,包头的长度就会大于4B。考虑到与通信的关系,整个传输包固定长度应相当于4个ATM包。考虑到加密是按照8B 顺序加扰的,代表有用信息的自适应区和包数据的长度应该是8B的整数倍,即自适应区和包数据为23×8B =184B。 TS包的包头由如图所示的同步字节、传输误码指示符、有效载荷单元起始指示符、传输优先、包识别(PID-Packet Identification)、传输加扰控制、自适应区控制和连续计数器8个部分组成。其中,可用同步字节位串的自动相关特性,检测数据流中的包限制,建立包同步;传输误码指示符,是指有不能消除误码时,采用误码校正解码器可表示1bit 的误码,但无法校正;有效载荷单元起始指示符,表示该数据包是否存在确定的起始信息;传输优先,是给TS包分配优先权;PID值是由用户确定的,解码器根据PID将TS上从不同ES来的TS包区别出来,以重建原来的ES;传输加扰控制,可指示数据包内容是否加扰,但包头和自适应区永远不加扰;自适应区控制,用2 bit表示有否自适应区,即(01)表示有有用信息无自适应区,(10)表示无有用信息有自适应区,(11)表示有有用信息有自适应区,(00)无定义;连续计数器可对PID包传送顺序计数,据计数器读数,接收端可判断是否有包丢失及包传送顺序错误。显然,包头对TS 包具有同步、识别、检错及加密功能。 TS包自适应区由自适应区长、各种标志指示符、与插入标志有关的信息和填充数据4部分组成。其中标志部分由间断指示符、随机存取指示符、ES优化指示符、PCR标志、接点标志、传输专用数据标志、原始PCR标志、自适应区扩展标志8个部分组成。 TS包语法结构如下:
DVB传输流采集盒是符合MPEG-II/DVB标准的DVB码流盒,提供完备的API接口函数,是数字电视设备理想的硬件开发平台。DVB码流盒主要用于将各种数字电视设备输出的传输流数据采集到计算机的硬盘上。 专业的数字电视码流采集设备,免安装驱动,支持各种操作系统,即插即用,既可以实时播放数字电视节目,又可以录制TS码流并保存到电脑上。 支持多种数字电视方式:DVB-C,DVB- S/S2,DVB-T/T2欧标,DTMB-TH国标。 特性: *支持DVB-C\DVB-T\DVB-S\DTMB-TH输入,符合MPEG2系统层规范 *支持100Base-TX网口,符合IEEE 802.3规范 *支持以网口输出,可以不用直接连接pc,直接连接与pc在同一个局域网的网络设备 *在PC端使用免安装的控制软件,不需要安装软件 *支持VLC播放,可以随时对传输流进行数据分析 *支持实时播放,实时录制和预约录制 *提供API,支持二次开发 *支持远程在线升级 *支持各种操作系统:Microsoft Windows 98/ME、2000、XP、Server2003、win7,linux等等
优势之处: 1.尺寸小,与家用路由器相当,携带方便。 2.安装简单,免安装驱动,只要你有信号源和电脑,录制节目流就不是问题。 3.方便保存,录制下来的节目直接保存在电脑硬盘里面,方便使用。 本产品采用以太网接口输出,可以远距离操作。 采用本产品录制TS流时,用户不需要在电脑上面安装任何软件,只需要运行一个免安装的很小的控制软件即可。 本产品特别适合STB工程师外出采集码流。STB工程师外出采集码流的时候,利用本设备在普通用户家庭或酒店就可以采集实际环境的码流。本设备支持全频搜索,采集工程师不需要了解当地的网络情况。为了获取最原始的码流,本设备支持全频点录制;为了节省存储空间,本设备也支持单节目录制。 本设备体积小、重量轻,价格便宜。当STB厂商也可以把这个设备寄给您的客户,让客户帮忙录制TS。本设备友好的人机界面,使您的用户基本不需要了解数字电视相关的知识,只要会操作电脑就会录制TS。 技术规范: *以太网接口 -网络标准:IEEE 802.3 -接口介质:100Base-TX -传输模式:100Mbps全双工 *射频输入 -接口类型:长F头输入,IEC公头环路输出 -输入频率:50~862MHz -输入电平:45dBuV~80dBuV -输入阻抗:75Ω *电源适配器 -输入:100-240VAC -输出:12VDC 1A *外形尺寸 - 110 x 90 x 24 mm 产品连接
COSHIP ITV TS流规范 文档作者:宋小刚日期:2007-08-01 项目经理:日期: 审核:日期: 批准:日期: 深圳同洲视讯传媒有限公司
文档历史发放及记录
目录 1传输流(TRANSPORT STREAM)要求 (6) 1.1TS流必须是符合ISO/IEC13818-1T RANSPORT S TREAM(MPEG-2的传输流)标准和DVB-C 的相关标准; (6) 1.2TS流中只包含一个节目即SPTS流; (6) 1.3TS流由长度为188字节的包组成,由一个完整传送包开始并且包含整数个传送包; 6 1.4TS流中的内容不能被加密或加扰; (6) 1.5在TS流中,节目关联表(PAT)和节目映射表(PMT)须同时成对出现,且PAT在PMT前; (6) 1.6PAT和PMT以0.5秒左右的间隔重复出现; (6) 1.7一个节目可含一个视频流和不多于16个的其他流(音频或私有数据); .6 1.8节目程序参考时钟(P ROGRAM C LOCK R EFERENCE,PCR)的PID(标识符)即PCR_PID 与节目视频流的PID一致; (6) 1.9PCR时间最好是在每个视频帧的开始,且PCR与PCR之间的时间间隔保证在40毫秒左右,波动不能太大;(建议项) (6) 1.10TS流中最好包含10%以内的空包以便片段之间的拼接;(建议项) (6) 1.11视频流中每帧的数据单独成一个视频PES包,即每个视频PES中仅包含一个视频帧的数据;(建议项) (6) 1.12包含视频图片系列(S EQUENCE H EADER,S EQUENCE E XTENSION和G ROUP OF P ICTURES H EADER)开始的包必须带有程序参考时钟(PCR)的时间标记;(建议项) (6) 1.13TS流中的第一个程序参考时钟(PCR)的不连续标记(DISCONTINUITY_INDICATOR FLAG)值须设置为“1”;(建议项) (6) 1.14总码率:标清MPEG2码率3.75M BPS,高清MPEG2码率15M BPS,标清H264码率1.6M BPS,高清H264码率6M BPS;(建议项) (6) 1.15相关PID建议参考如下值:(建议项)1)PAT表中的TRANSPORT_STREAM_ID=1,只有一个节目号PROGRAM_NUMBER=2,PID=32;2)PMT表中的PCR_PID=33,视频流PID=33,音频流PID=34,其他流累加。 (6) 2视频编码要求 (7) 2.1视频流编码格式:MPEG-2格式需符合ISO/IEC13818-2编码标准;MPEG-4格式需符合ISO/IEC14496-2编码标准;H264格式需符合ISO/IEC14496-10编码标准; 7 2.2视频流必须为PAL制式,即帧率25帧/秒,普通画面大小为720*576,高清画面大小为1280*720,全高清画面大小为1920*1080; (7) 2.3MPEG-2格式相关 (7) 2.4H264格式相关 (7) 3音频编码要求 (8) 3.1音频流必须根据ISO/IEC11172-3(A UDIO部分)中MPEG-1L AYER2的标准进行编码; 8 3.2采样率:48K H Z;(建议项) (8) 3.3比特率:单声道96KBPS、双声道192KBPS;(建议项) (8) 3.4单声道、双声道的选择与片源保持一致;(建议项) (8)
龙源期刊网 https://www.doczj.com/doc/e28179564.html, TS流分析软件EasyICE使用方法简介 作者:陈晓军 来源:《卫星电视与宽带多媒体》2012年第09期 TS流分析软件EasyICE使用方法简介 笔者以深圳生产的DVBWorld 2102S USB多媒体数据接收盒为例向大家介绍详细使用过程,将接收盒硬件和软件全部安装好,然后依次点击“开始”→“所有程序”→“DVBWorld” →“TsCapture”项,打开TS流录制程序,如,点击“ LOCK”按钮可设置待录制TS流的下行频率、极化方式和符码率等参数,“Max File Size”是修改保存文件的大小,通过“Set Path”按钮设置好保存的文件名和位置(注意:保存文件的扩展名可以更改为TS,如图2),然后点击“Start Capture”便开始保存码流文件了,单击“Stop Capture”按钮可以停止录制。 接下来就是分析TS流文件的操作了。通过菜单栏或工具栏打开一个 TS 文件或将文件拖动到 EasyICE内,可根据实际所需调协范围:整个文件或抽样分析,通常选择整个文件来分析,虽然多花点时间,但分析效果会更好。当文件分析完毕后,软件主界面上会出现播放器、MediaInfo、PSI/SI、PID、图表和数据包四个选项标签,其中的播放器窗口会被初始化并处于暂停状态,单击播放按钮便开始播放节目(注:若TS流包含多路节目的话,则必须先在右上角选定某路节目才能播放),如图3所示。播控按钮依次为:播放、暂停、停止、降低播放速度、加大播放速度及逐帧播放,播放速度的调整分七个级别:1/4 速、1/3 速、1/2 速、正常速度、2 倍速、3 倍速和四倍速。当前播放速度会在“质量”区显示,“节目”区列出了当前流中所 含有的节目,展开可以看到节目的视频、音频和PCR三个PID;“质量”区显示了当前播放媒体的简单信息,如播放速度等,不过由于软件问题有些数据获取不到或存在问题,请以“媒体信息”栏检测结果为准。特别声名,软件首先会查找流中的 PSI/SI 信息,如果存在将按照 PSI/SI 信息解析,如果不存在软件将自己尝试检测视音频 PID 及 PCRPID,如果没有看到播放器画面,表明软件没有找到视频流或没有找到 PCR。 MediaInfo媒体信息标签模块显示了包括图像大小、编码格式、码率大小、彩色制式及场频等几乎所有的音视频详细参数,见图4所示。 PSI/SI标签模块对所有的 PSI/SI 进行了解析,当流中存在 PSI/SI 数据时,会在此模块会一一列出,在视图中点击鼠标右键,可以展开/折叠所有节点,见图5所示。 PID标签模块以统计的方式列出当前 TS 流中各个 PID 出现的数量及占用百分比及所属类型等,如图6所示。 图表标签模块。当流中存在多路节目时,图标界面会显示一节节目选择对话框,单击可以显示相应节目图表。包括:1、时间戳信息。时间戳图表显示的是DTS、PTS 出现时与 PCR 的采样。与“PCR 抖动” 图表相同的是,时间戳以 PCR 时钟为基准。DTS与PTS值取自视频流中
ps流与ts流 在MPEG-2系统中,信息复合/分离的过程称为系统复接/分接,由视频,音频的ES流和辅助数据复接生成的用于实际传输的标准信息流称为MPEG-2传送流(TS:TransportStream)。据传输媒体的质量不同,MPEG-2中定义了两种复合信息流:传送流(TS)和节目流(PS:ProgramStream)TS流与PS流的区别在于TS流的包结构是固定长度的,而PS流的包结构是可变长度的。 PS包与TS包在结构上的这种差异,导致了它们对传输误码具有不同的抵抗能力,因而应用的环境也有所不同。TS码流由于采用了固定长度的包结构,当传输误码破坏了某一TS包的同步信息时,接收机可在固定的位置检测它后面包中的同步信息,从而恢复同步,避免了信息丢失。而PS包由于长度是变化的,一旦某一PS包的同步信息丢失,接收机无法确定下一包的同步位置,就会造成失步,导致严重的信息丢失。因此,在信道环境较为恶劣,传输误码较高时,一般采用TS码流;而在信道环境较好,传输误码较低时,一般采用PS码流如DVD等等。由于TS码流具有较强的抵抗传输误码的能力,因此目前在传输媒体中进行传输的MPEG-2码流基本上都采用了TS码流。 简单说就是ps流(主要用在DVD上)如是中间丢了一断码流,后面的都没法播了;而TS流(DVB-T,DMB-TH 等)如果断了码流,后面的随时可以再开始解码怎么看都行。DVB-T,DVB-H,DMB-TH 主要指的是调制解调(信道编码和解码)方式为COFDM,信源编解码采用的都是MPEG-2,TS流。目前地面波数字电视标准中只有日本的ISDB 采用MPEG-4(H.264)编解码。清晰度方面,DVB-T,DMB-TH标准都可以达到高清标准,DVB-H主要面向手持设备,接收终端的解析度有限。 ps码流:dvd等本地文件 ts码流:rtp网络传输等 ==================================================== pes,ts,ps ts流是由很多不同种类的包所组成的,这些数据包都是188个字节大小,这188个字节包含两部分,包头和负载,包头包括同步信息,包信息等等,而负载则是传输的数据,而这些负载则可以组成PES流或者私有流 等等数据流. 举例说,一个TS流包括100个包,其中PSI信息包占20个,PES数据包80个,此TS流中只有一套节目流,不含有私有流,所以从这80个PES包中的负载连接在一起,就是2个PES流(视频,音频),如果每个PES 包的负载长度为100字节,则这两个PES流一共长度为8000个字节.假设其中视频的PES流长度为6000字节.则视频的6000字节的PES流,是由PES包组成的.PES包没有固定的长度,而是由包头部的数据给出.而PS也是类似TS流分解的方式,逆向的由PES包封装成包,其中要添加 PACKET_HEAD,SYSTEM_HEAD等信息.所以上次所做的程序,并不是TS->PS的转换,而是从一个复杂 的TS流中,过滤去一套节目,构造出一个简单的TS流的过程. mpeg-ts,mpeg-ps的转换 mpeg2文件都是以数据包传递的,同样都是188个字节为一个包,但是作为传输流和节目流,包的组织结构还是不太一样的,作为传输流来说,其包含的包的种类比较多(其实不是包的种类,而是包含不同用途的数 据的种类比较多),比如有PID为0x0000的PAT,EIT,TDT,TOT,还有PMT,等等不同的表或包.而节目流所
#include