/*
Copyright (C) 2009 Anton Burdinuk
clark15b@gmail.com
*/
#include "ts.h"
// TODO: join TS
// 1) M2TS timecode (0-1073741823)
/*
The extra 4-byte header is composed of two fields. The upper 2 bits are the copy_permission_indicator and the lower 30 bits are the arrival_time_stamp. The arrival_time_stamp is equal to the lower 30 bits of the 27 MHz STC at the 0x47 byte of the Transport packet. In a packet that contains a PCR, the PCR will be a few ticks later than the arrival_time_stamp. The exact difference between the arrival_time_stamp and the PCR (and the number of bits between them) indicates the intended fixed bitrate of the variable rate Transport Stream.
The primary function is to allow the variable rate Transport Stream to be converted to a fixed rate stream before decoding (since only fixed rate Transport Streams fit into the T-STD buffering model).
It doesn't really help for random access. 30 bits at 27 MHz only represents 39.77 seconds.
*/
// 2) TS Continuity counter (0-15..0-15..)
// 3) PES PTS/DTS
namespace ts
{
void get_prefix_name_by_filename(const std::string& s,std::string& name);
#ifdef _WIN32
void my_strptime(const char* s,tm* t);
#endif
}
ts::stream::~stream(void)
{
if(timecodes)
fclose(timecodes);
}
void ts::get_prefix_name_by_filename(const std::string& s,std::string& name)
{
int ll=s.length();
const char* p=s.c_str();
while(ll>0)
{
if(p[ll-1]=='/' || p[ll-1]=='\\')
break;
ll--;
}
p+=ll;
int cn=0;
const char* pp=strchr(p,'.');
if(pp)
name.assign(p,pp-p);
}
ts::file::~file(void)
{
close();
}
bool ts::file::open(int mode,const char* fmt,...)
{
filename.clear();
char name[512];
va_list ap;
va_start(ap,fmt);
vsprintf(name,fmt,ap);
va_end(ap);
int flags=0;
switch(mode)
{
case in:
flags=O_LARGEFILE|O_BINARY|O_RDONLY;
break;
case out:
flags=O_CREAT|O_TRUNC|O_LARGEFILE|O_BINARY|O_WRONLY;
break;
}
fd=::open(name,flags,0644);
if(fd!=-1)
{
filename=name;
len=offset=0;
return true;
}
fprintf(stderr,"can`t open file %s\n",name);
return false;
}
void ts::file::close(void)
{
if(fd!=-1)
{
flush();
::close(fd);
fd=-1;
}
len=0;
offset=0;
}
int ts::file::flush(void)
{
int l=0;
while(l<len)
{
int n=::write(fd,buf+l,len-l);
if(!n || n==-1)
break;
l+=n;
}
len=0;
return l;
}
int ts::file::write(const char* p,int l)
{
int rc=l;
while(l>0)
{
if(len>=max_buf_len)
flush();
int n=max_buf_len-len;
if(n>l)
n=l;
memcpy(buf+len,p,n);
len+=n;
p+=n;
l-=n;
}
return rc;
}
int ts::file::read(char* p,int l)
{
const char* tmp=p;
while(l>0)
{
int n=len-offset;
if(n>0)
{
if(n>l)
n=l;
memcpy(p,buf+offset,n);
p+=n;
offset+=n;
l-=n;
}else
{
int m=::read(fd,buf,max_buf_len);
if(m==-1 || !m)
break;
len=m;
offset=0;
}
}
return p-tmp;
}
bool ts::demuxer::validate_type(u_int8_t type)
{
if(av_only)
return strchr("\x01\x02\x80\x1b\xea\x81\x06\x83\x03\x04\x82\x86\x8a",type)?true:false;
return true;
}
int ts::demuxer::get_stream_type(u_int8_t type)
{
switch(type)
{
case 0x01:
case 0x02:
return stream_type::mpeg2_video;
case 0x80:
return hdmv?stream_type::lpcm_audio:stream_type::mpeg2_video;
case 0x1b:
return stream_type::h264_video;
case 0xea:
return stream_type::vc1_video;
case 0x81:
case 0x06:
case 0x83:
return stream_type::ac3_audio;
case 0x03:
case 0x04:
return stream_type::mpeg2_audio;
case 0x82:
case 0x86:
case 0x8a:
return stream_type::dts_audio;
}
return stream_type::data;
}
const char* ts::demuxer::get_stream_ext(u_int8_t type_id)
{
static const char* list[8]= { "sup", "m2v", "264", "vc1", "ac3", "m2a", "pcm", "dts" };
if(type_id<0 || type_id>=8)
type_id=0;
return list[type_id];
}
u_int64_t ts::demuxer::decode_pts(const char* ptr)
{
const unsigned char* p=(const unsigned char*)ptr;
u_int64_t pts=((p[0]&0xe)<<29);
pts|=((p[1]&0xff)<<22);
pts|=((p[2]&0xfe)<<14);
pts|=((p[3]&0xff)<<7);
pts|=((p[4]&0xfe)>>1);
return pts;
}
int ts::demuxer::demux_ts_packet(const char* ptr)
{
u_int32_t timecode=0;
if(hdmv)
{
timecode=to_int32(ptr)&0x3fffffff;
ptr+=4;
}
const char* end_ptr=ptr+188;
if(ptr[0]!=0x47) // ts sync byte
return -1;
u_int16_t pid=to_int(ptr+1);
u_int8_t flags=to_byte(ptr+3);
bool transport_error=pid&0x8000;
bool payload_unit_start_indicator=pid&0x4000;
bool adaptation_field_exist=flags&0x20;
bool payload_data_exist=flags&0x10;
u_int8_t continuity_counter=flags&0x0f;
pid&=0x1fff;
if(transport_error)
return -2;
if(pid==0x1fff || !payload_data_exist)
return 0;
ptr+=4;
// skip adaptation field
if(adaptation_field_exist)
{
ptr+=to_byte(ptr)+1;
if(ptr>=end_ptr)
return -3;
}
if(dump==1)
printf("%.4x: [%c%c%c%c] %u.%i\n",
pid,
transport_error?'e':'-',
payload_data_exist?'p':'-',
payload_unit_start_indicator?'s':'-',
adaptation_field_exist?'a':'-',
timecode,
continuity_counter
);
stream& s=streams[pid];
if(!pid || (s.channel!=0xffff && s.type==0xff))
{
// PSI
if(payload_unit_start_indicator)
{
// begin of PSI table
ptr++;
if(ptr>=end_ptr)
return -4;
if(*ptr!=0x00 && *ptr!=0x02)
return 0;
if(end_ptr-ptr<3)
return -5;
u_int16_t l=to_int(ptr+1);
if(l&0x3000!=0x3000)
return -6;
l&=0x0fff;
ptr+=3;
int len=end_ptr-ptr;
if(l>len)
{
if(l>ts::table::max_buf_len)
return -7;
s.psi.reset();
memcpy(s.psi.buf,ptr,len);
s.psi.offset+=len;
s.psi.len=l;
return 0;
}else
end_ptr=ptr+l;
}else
{
// next part of PSI
if(!s.psi.offset)
return -8;
int len=end_ptr-ptr;
if(len>ts::table::max_buf_len-s.psi.offset)
return -9;
memcpy(s.psi.buf+s.psi.offset,ptr,len);
s.psi.offset+=len;
if(s.psi.offset<s.psi.len)
return 0;
else
{
ptr=s.psi.buf;
end_ptr=ptr+s.psi.len;
}
}
if(!pid)
{
// PAT
ptr+=5;
if(ptr>=end_ptr)
return -10;
int len=end_ptr-ptr-4;
if(len<0 || len%4)
return -11;
int n=len/4;
for(int i=0;i<n;i++,ptr+=4)
{
u_int16_t channel=to_int(ptr);
u_int16_t pid=to_int(ptr+2);
if(pid&0xe000!=0xe000)
return -12;
pid&=0x1fff;
if(!demuxer::channel || demuxer::channel==channel)
{
stream& ss=streams[pid];
ss.channel=channel;
ss.type=0xff;
}
}
}else
{
// PMT
pt
- 1
- 2
前往页