mirror of
https://projects.vdr-developer.org/git/vdr-plugin-streamdev.git
synced 2023-10-10 19:16:51 +02:00
2682 lines
56 KiB
C
2682 lines
56 KiB
C
/*
|
|
* dvb-mpegtools for the Siemens Fujitsu DVB PCI card
|
|
*
|
|
* Copyright (C) 2000, 2001 Marcus Metzler
|
|
* for convergence integrated media GmbH
|
|
* Copyright (C) 2002 Marcus Metzler
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
|
|
*
|
|
|
|
* The author can be reached at marcus@convergence.de,
|
|
|
|
* the project's page is at http://linuxtv.org/dvb/
|
|
*/
|
|
|
|
|
|
#include "transform.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "ctools.h"
|
|
|
|
static uint8_t tspid0[TS_SIZE] = {
|
|
0x47, 0x40, 0x00, 0x10, 0x00, 0x00, 0xb0, 0x11,
|
|
0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0x00, 0xe0,
|
|
0x10, 0x00, 0x01, 0xe4, 0x00, 0x2a, 0xd6, 0x1a,
|
|
0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff
|
|
};
|
|
|
|
static uint8_t tspid1[TS_SIZE] = {
|
|
0x47, 0x44, 0x00, 0x10, 0x00, 0x02, 0xb0, 0x1c,
|
|
0x00, 0x01, 0xcb, 0x00, 0x00, 0xe0, 0xa0, 0xf0,
|
|
0x05, 0x48, 0x03, 0x01, 0x00, 0x00, 0x02, 0xe0,
|
|
0xa0, 0xf0, 0x00, 0x03, 0xe0, 0x50, 0xf0, 0x00,
|
|
0xae, 0xea, 0x4e, 0x48, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff
|
|
};
|
|
|
|
// CRC32 lookup table for polynomial 0x04c11db7
|
|
static const uint32_t crc_table[256] = {
|
|
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
|
|
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
|
|
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
|
|
0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
|
|
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
|
|
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
|
|
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
|
|
0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
|
|
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
|
|
0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
|
|
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
|
|
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
|
|
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
|
|
0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
|
|
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
|
|
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
|
|
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
|
|
0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
|
|
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
|
|
0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
|
|
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
|
|
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
|
|
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
|
|
0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
|
|
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
|
|
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
|
|
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
|
|
0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
|
|
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
|
|
0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
|
|
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
|
|
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
|
|
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
|
|
0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
|
|
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
|
|
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
|
|
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
|
|
0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
|
|
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
|
|
0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
|
|
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
|
|
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
|
|
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
|
|
};
|
|
|
|
static uint32_t
|
|
calc_crc32 (const uint8_t *sec, uint8_t len)
|
|
{
|
|
int i;
|
|
uint32_t crc = 0xffffffff;
|
|
|
|
for (i = 0; i < len; i++)
|
|
crc = (crc << 8) ^ crc_table[((crc >> 24) ^ *sec++) & 0xff];
|
|
|
|
return crc;
|
|
}
|
|
|
|
|
|
uint64_t trans_pts_dts(uint8_t *pts)
|
|
{
|
|
uint64_t wts;
|
|
|
|
wts = ((uint64_t)((pts[0] & 0x0E) << 5) |
|
|
((pts[1] & 0xFC) >> 2)) << 24;
|
|
wts |= (((pts[1] & 0x03) << 6) |
|
|
((pts[2] & 0xFC) >> 2)) << 16;
|
|
wts |= (((pts[2] & 0x02) << 6) |
|
|
((pts[3] & 0xFE) >> 1)) << 8;
|
|
wts |= (((pts[3] & 0x01) << 7) |
|
|
((pts[4] & 0xFE) >> 1));
|
|
return wts;
|
|
}
|
|
|
|
|
|
void get_pespts(uint8_t *av_pts,uint8_t *pts)
|
|
{
|
|
|
|
pts[0] = 0x21 |
|
|
((av_pts[0] & 0xC0) >>5);
|
|
pts[1] = ((av_pts[0] & 0x3F) << 2) |
|
|
((av_pts[1] & 0xC0) >> 6);
|
|
pts[2] = 0x01 | ((av_pts[1] & 0x3F) << 2) |
|
|
((av_pts[2] & 0x80) >> 6);
|
|
pts[3] = ((av_pts[2] & 0x7F) << 1) |
|
|
((av_pts[3] & 0x80) >> 7);
|
|
pts[4] = 0x01 | ((av_pts[3] & 0x7F) << 1);
|
|
}
|
|
|
|
uint16_t get_pid(uint8_t *pid)
|
|
{
|
|
uint16_t pp = 0;
|
|
|
|
pp = (pid[0] & PID_MASK_HI)<<8;
|
|
pp |= pid[1];
|
|
|
|
return pp;
|
|
}
|
|
|
|
int write_ts_header(uint16_t pid, uint8_t *counter, int pes_start,
|
|
uint8_t *buf, uint8_t length)
|
|
{
|
|
int i;
|
|
int c = 0;
|
|
int fill;
|
|
uint8_t tshead[4] = { 0x47, 0x00, 0x00, 0x10};
|
|
|
|
|
|
fill = TS_SIZE-4-length;
|
|
if (pes_start) tshead[1] = 0x40;
|
|
if (fill) tshead[3] = 0x30;
|
|
tshead[1] |= (uint8_t)((pid & 0x1F00) >> 8);
|
|
tshead[2] |= (uint8_t)(pid & 0x00FF);
|
|
tshead[3] |= ((*counter)++ & 0x0F) ;
|
|
memcpy(buf,tshead,4);
|
|
c+=4;
|
|
|
|
|
|
if (fill){
|
|
buf[4] = fill-1;
|
|
c++;
|
|
if (fill >1){
|
|
buf[5] = 0x00;
|
|
c++;
|
|
}
|
|
for ( i = 6; i < fill+4; i++){
|
|
buf[i] = 0xFF;
|
|
c++;
|
|
}
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
int write_pes_header(uint8_t id,int length , long PTS, uint8_t *obuf,
|
|
int stuffing)
|
|
{
|
|
uint8_t le[2];
|
|
uint8_t dummy[3];
|
|
uint8_t *pts;
|
|
uint8_t ppts[5];
|
|
long lpts;
|
|
int c;
|
|
uint8_t headr[3] = {0x00, 0x00, 0x01};
|
|
|
|
lpts = htonl(PTS);
|
|
pts = (uint8_t *) &lpts;
|
|
|
|
get_pespts(pts,ppts);
|
|
|
|
c = 0;
|
|
memcpy(obuf+c,headr,3);
|
|
c += 3;
|
|
memcpy(obuf+c,&id,1);
|
|
c++;
|
|
|
|
le[0] = 0;
|
|
le[1] = 0;
|
|
length -= 6+stuffing;
|
|
|
|
le[0] |= ((uint8_t)(length >> 8) & 0xFF);
|
|
le[1] |= ((uint8_t)(length) & 0xFF);
|
|
memcpy(obuf+c,le,2);
|
|
c += 2;
|
|
|
|
if (id == PADDING_STREAM){
|
|
memset(obuf+c,0xff,length);
|
|
c+= length;
|
|
return c;
|
|
}
|
|
|
|
dummy[0] = 0x80;
|
|
dummy[1] = 0;
|
|
dummy[2] = 0;
|
|
if (PTS){
|
|
dummy[1] |= PTS_ONLY;
|
|
dummy[2] = 5+stuffing;
|
|
}
|
|
memcpy(obuf+c,dummy,3);
|
|
c += 3;
|
|
memset(obuf+c,0xFF,stuffing);
|
|
|
|
if (PTS){
|
|
memcpy(obuf+c,ppts,5);
|
|
c += 5;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
void init_p2p(p2p *p, void (*func)(uint8_t *buf, int count, void *p),
|
|
int repack){
|
|
p->found = 0;
|
|
p->cid = 0;
|
|
p->mpeg = 0;
|
|
memset(p->buf,0,MMAX_PLENGTH);
|
|
p->done = 0;
|
|
p->fd1 = -1;
|
|
p->func = func;
|
|
p->bigend_repack = 0;
|
|
p->repack = 0;
|
|
if ( repack < MAX_PLENGTH && repack > 265 ){
|
|
p->repack = repack-6;
|
|
p->bigend_repack = (uint16_t)htons((short)
|
|
((repack-6) & 0xFFFF));
|
|
} else {
|
|
fprintf(stderr, "Repack size %d is out of range\n",repack);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void pes_repack(p2p *p)
|
|
{
|
|
int count = 0;
|
|
int repack = p->repack;
|
|
int rest = p->plength;
|
|
uint8_t buf[MAX_PLENGTH];
|
|
int bfill = 0;
|
|
int diff;
|
|
uint16_t length;
|
|
|
|
if (rest < 0) {
|
|
fprintf(stderr,"Error in repack\n");
|
|
return;
|
|
}
|
|
|
|
if (!repack){
|
|
fprintf(stderr,"forgot to set repack size\n");
|
|
return;
|
|
}
|
|
|
|
if (p->plength == repack){
|
|
memcpy(p->buf+4,(char *)&p->bigend_repack,2);
|
|
p->func(p->buf, repack+6, p);
|
|
return;
|
|
}
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x00;
|
|
buf[2] = 0x01;
|
|
buf[3] = p->cid;
|
|
memcpy(buf+4,(char *)&p->bigend_repack,2);
|
|
memset(buf+6,0,MAX_PLENGTH-6);
|
|
|
|
if (p->mpeg == 2){
|
|
|
|
if ( rest > repack){
|
|
memcpy(p->buf+4,(char *)&p->bigend_repack,2);
|
|
p->func(p->buf, repack+6, p);
|
|
count += repack+6;
|
|
rest -= repack;
|
|
} else {
|
|
memcpy(buf,p->buf,9+p->hlength);
|
|
bfill = p->hlength;
|
|
count += 9+p->hlength;
|
|
rest -= p->hlength+3;
|
|
}
|
|
|
|
while (rest >= repack-3){
|
|
memset(buf+6,0,MAX_PLENGTH-6);
|
|
buf[6] = 0x80;
|
|
buf[7] = 0x00;
|
|
buf[8] = 0x00;
|
|
memcpy(buf+9,p->buf+count,repack-3);
|
|
rest -= repack-3;
|
|
count += repack-3;
|
|
p->func(buf, repack+6, p);
|
|
}
|
|
|
|
if (rest){
|
|
diff = repack - 3 - rest - bfill;
|
|
if (!bfill){
|
|
buf[6] = 0x80;
|
|
buf[7] = 0x00;
|
|
buf[8] = 0x00;
|
|
}
|
|
|
|
if ( diff < PES_MIN){
|
|
length = rest+ diff + bfill+3;
|
|
buf[4] = (uint8_t)((length & 0xFF00) >> 8);
|
|
buf[5] = (uint8_t)(length & 0x00FF);
|
|
buf[8] = (uint8_t)(bfill+diff);
|
|
memset(buf+9+bfill,0xFF,diff);
|
|
memcpy(buf+9+bfill+diff,p->buf+count,rest);
|
|
} else {
|
|
length = rest+ bfill+3;
|
|
buf[4] = (uint8_t)((length & 0xFF00) >> 8);
|
|
buf[5] = (uint8_t)(length & 0x00FF);
|
|
memcpy(buf+9+bfill,p->buf+count,rest);
|
|
bfill += rest+9;
|
|
write_pes_header( PADDING_STREAM, diff, 0,
|
|
buf+bfill, 0);
|
|
}
|
|
p->func(buf, repack+6, p);
|
|
}
|
|
}
|
|
|
|
if (p->mpeg == 1){
|
|
|
|
if ( rest > repack){
|
|
memcpy(p->buf+4,(char *)&p->bigend_repack,2);
|
|
p->func(p->buf, repack+6, p);
|
|
count += repack+6;
|
|
rest -= repack;
|
|
} else {
|
|
memcpy(buf,p->buf,6+p->hlength);
|
|
bfill = p->hlength;
|
|
count += 6;
|
|
rest -= p->hlength;
|
|
}
|
|
|
|
while (rest >= repack-1){
|
|
memset(buf+6,0,MAX_PLENGTH-6);
|
|
buf[6] = 0x0F;
|
|
memcpy(buf+7,p->buf+count,repack-1);
|
|
rest -= repack-1;
|
|
count += repack-1;
|
|
p->func(buf, repack+6, p);
|
|
}
|
|
|
|
|
|
if (rest){
|
|
diff = repack - 1 - rest - bfill;
|
|
|
|
if ( diff < PES_MIN){
|
|
length = rest+ diff + bfill+1;
|
|
buf[4] = (uint8_t)((length & 0xFF00) >> 8);
|
|
buf[5] = (uint8_t)(length & 0x00FF);
|
|
memset(buf+6,0xFF,diff);
|
|
if (!bfill){
|
|
buf[6+diff] = 0x0F;
|
|
}
|
|
memcpy(buf+7+diff,p->buf+count,rest+bfill);
|
|
} else {
|
|
length = rest+ bfill+1;
|
|
buf[4] = (uint8_t)((length & 0xFF00) >> 8);
|
|
buf[5] = (uint8_t)(length & 0x00FF);
|
|
if (!bfill){
|
|
buf[6] = 0x0F;
|
|
memcpy(buf+7,p->buf+count,rest);
|
|
bfill = rest+7;
|
|
} else {
|
|
memcpy(buf+6,p->buf+count,rest+bfill);
|
|
bfill += rest+6;
|
|
}
|
|
write_pes_header( PADDING_STREAM, diff, 0,
|
|
buf+bfill, 0);
|
|
}
|
|
p->func(buf, repack+6, p);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int filter_pes (uint8_t *buf, int count, p2p *p, int (*func)(p2p *p))
|
|
{
|
|
|
|
int l;
|
|
unsigned short *pl;
|
|
int c=0;
|
|
int ret = 1;
|
|
|
|
uint8_t headr[3] = { 0x00, 0x00, 0x01} ;
|
|
|
|
while (c < count && (p->mpeg == 0 ||
|
|
(p->mpeg == 1 && p->found < 7) ||
|
|
(p->mpeg == 2 && p->found < 9))
|
|
&& (p->found < 5 || !p->done)){
|
|
switch ( p->found ){
|
|
case 0:
|
|
case 1:
|
|
if (buf[c] == 0x00) p->found++;
|
|
else {
|
|
if (p->fd1 >= 0)
|
|
write(p->fd1,buf+c,1);
|
|
p->found = 0;
|
|
}
|
|
c++;
|
|
break;
|
|
case 2:
|
|
if (buf[c] == 0x01) p->found++;
|
|
else if (buf[c] == 0){
|
|
p->found = 2;
|
|
} else {
|
|
if (p->fd1 >= 0)
|
|
write(p->fd1,buf+c,1);
|
|
p->found = 0;
|
|
}
|
|
c++;
|
|
break;
|
|
case 3:
|
|
p->cid = 0;
|
|
switch (buf[c]){
|
|
case PROG_STREAM_MAP:
|
|
case PRIVATE_STREAM2:
|
|
case PROG_STREAM_DIR:
|
|
case ECM_STREAM :
|
|
case EMM_STREAM :
|
|
case PADDING_STREAM :
|
|
case DSM_CC_STREAM :
|
|
case ISO13522_STREAM:
|
|
if (p->fd1 >= 0)
|
|
write(p->fd1,buf+c,1);
|
|
p->done = 1;
|
|
case PRIVATE_STREAM1:
|
|
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
|
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
|
p->found++;
|
|
p->cid = buf[c];
|
|
c++;
|
|
break;
|
|
default:
|
|
if (p->fd1 >= 0)
|
|
write(p->fd1,buf+c,1);
|
|
p->found = 0;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case 4:
|
|
if (count-c > 1){
|
|
pl = (unsigned short *) (buf+c);
|
|
p->plength = ntohs(*pl);
|
|
p->plen[0] = buf[c];
|
|
c++;
|
|
p->plen[1] = buf[c];
|
|
c++;
|
|
p->found+=2;
|
|
} else {
|
|
p->plen[0] = buf[c];
|
|
p->found++;
|
|
return 1;
|
|
}
|
|
break;
|
|
case 5:
|
|
p->plen[1] = buf[c];
|
|
c++;
|
|
pl = (unsigned short *) p->plen;
|
|
p->plength = ntohs(*pl);
|
|
p->found++;
|
|
break;
|
|
|
|
|
|
case 6:
|
|
if (!p->done){
|
|
p->flag1 = buf[c];
|
|
c++;
|
|
p->found++;
|
|
if ( (p->flag1 & 0xC0) == 0x80 ) p->mpeg = 2;
|
|
else {
|
|
p->hlength = 0;
|
|
p->which = 0;
|
|
p->mpeg = 1;
|
|
p->flag2 = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 7:
|
|
if ( !p->done && p->mpeg == 2){
|
|
p->flag2 = buf[c];
|
|
c++;
|
|
p->found++;
|
|
}
|
|
break;
|
|
|
|
case 8:
|
|
if ( !p->done && p->mpeg == 2){
|
|
p->hlength = buf[c];
|
|
c++;
|
|
p->found++;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!p->plength) p->plength = MMAX_PLENGTH-6;
|
|
|
|
|
|
if ( p->done || ((p->mpeg == 2 && p->found >= 9) ||
|
|
(p->mpeg == 1 && p->found >= 7)) ){
|
|
switch (p->cid){
|
|
|
|
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
|
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
|
case PRIVATE_STREAM1:
|
|
|
|
memcpy(p->buf, headr, 3);
|
|
p->buf[3] = p->cid;
|
|
memcpy(p->buf+4,p->plen,2);
|
|
|
|
if (p->mpeg == 2 && p->found == 9){
|
|
p->buf[6] = p->flag1;
|
|
p->buf[7] = p->flag2;
|
|
p->buf[8] = p->hlength;
|
|
}
|
|
|
|
if (p->mpeg == 1 && p->found == 7){
|
|
p->buf[6] = p->flag1;
|
|
}
|
|
|
|
|
|
if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) &&
|
|
p->found < 14){
|
|
while (c < count && p->found < 14){
|
|
p->pts[p->found-9] = buf[c];
|
|
p->buf[p->found] = buf[c];
|
|
c++;
|
|
p->found++;
|
|
}
|
|
if (c == count) return 1;
|
|
}
|
|
|
|
if (p->mpeg == 1 && p->which < 2000){
|
|
|
|
if (p->found == 7) {
|
|
p->check = p->flag1;
|
|
p->hlength = 1;
|
|
}
|
|
|
|
while (!p->which && c < count &&
|
|
p->check == 0xFF){
|
|
p->check = buf[c];
|
|
p->buf[p->found] = buf[c];
|
|
c++;
|
|
p->found++;
|
|
p->hlength++;
|
|
}
|
|
|
|
if ( c == count) return 1;
|
|
|
|
if ( (p->check & 0xC0) == 0x40 && !p->which){
|
|
p->check = buf[c];
|
|
p->buf[p->found] = buf[c];
|
|
c++;
|
|
p->found++;
|
|
p->hlength++;
|
|
|
|
p->which = 1;
|
|
if ( c == count) return 1;
|
|
p->check = buf[c];
|
|
p->buf[p->found] = buf[c];
|
|
c++;
|
|
p->found++;
|
|
p->hlength++;
|
|
p->which = 2;
|
|
if ( c == count) return 1;
|
|
}
|
|
|
|
if (p->which == 1){
|
|
p->check = buf[c];
|
|
p->buf[p->found] = buf[c];
|
|
c++;
|
|
p->found++;
|
|
p->hlength++;
|
|
p->which = 2;
|
|
if ( c == count) return 1;
|
|
}
|
|
|
|
if ( (p->check & 0x30) && p->check != 0xFF){
|
|
p->flag2 = (p->check & 0xF0) << 2;
|
|
p->pts[0] = p->check;
|
|
p->which = 3;
|
|
}
|
|
|
|
if ( c == count) return 1;
|
|
if (p->which > 2){
|
|
if ((p->flag2 & PTS_DTS_FLAGS)
|
|
== PTS_ONLY){
|
|
while (c < count &&
|
|
p->which < 7){
|
|
p->pts[p->which-2] =
|
|
buf[c];
|
|
p->buf[p->found] =
|
|
buf[c];
|
|
c++;
|
|
p->found++;
|
|
p->which++;
|
|
p->hlength++;
|
|
}
|
|
if ( c == count) return 1;
|
|
} else if ((p->flag2 & PTS_DTS_FLAGS)
|
|
== PTS_DTS){
|
|
while (c < count &&
|
|
p->which< 12){
|
|
if (p->which< 7)
|
|
p->pts[p->which
|
|
-2] =
|
|
buf[c];
|
|
p->buf[p->found] =
|
|
buf[c];
|
|
c++;
|
|
p->found++;
|
|
p->which++;
|
|
p->hlength++;
|
|
}
|
|
if ( c == count) return 1;
|
|
}
|
|
p->which = 2000;
|
|
}
|
|
|
|
}
|
|
|
|
while (c < count && p->found < p->plength+6){
|
|
l = count -c;
|
|
if (l+p->found > p->plength+6)
|
|
l = p->plength+6-p->found;
|
|
memcpy(p->buf+p->found, buf+c, l);
|
|
p->found += l;
|
|
c += l;
|
|
}
|
|
if(p->found == p->plength+6){
|
|
if (func(p)){
|
|
if (p->fd1 >= 0){
|
|
write(p->fd1,p->buf,
|
|
p->plength+6);
|
|
}
|
|
} else ret = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
if ( p->done ){
|
|
if( p->found + count - c < p->plength+6){
|
|
p->found += count-c;
|
|
c = count;
|
|
} else {
|
|
c += p->plength+6 - p->found;
|
|
p->found = p->plength+6;
|
|
}
|
|
}
|
|
|
|
if (p->plength && p->found == p->plength+6) {
|
|
p->found = 0;
|
|
p->done = 0;
|
|
p->plength = 0;
|
|
memset(p->buf, 0, MAX_PLENGTH);
|
|
if (c < count)
|
|
return filter_pes(buf+c, count-c, p, func);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
#define SIZE 4096
|
|
|
|
|
|
int audio_pes_filt(p2p *p)
|
|
{
|
|
uint8_t off;
|
|
|
|
switch(p->cid){
|
|
case PRIVATE_STREAM1:
|
|
if ( p->cid == p->filter) {
|
|
off = 9+p->buf[8];
|
|
if (p->buf[off] == p->subid){
|
|
return 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
|
if ( p->cid == p->filter)
|
|
return 1;
|
|
break;
|
|
|
|
default:
|
|
return 1;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void filter_audio_from_pes(int fdin, int fdout, uint8_t id, uint8_t subid)
|
|
{
|
|
p2p p;
|
|
int count = 1;
|
|
uint8_t buf[2048];
|
|
|
|
init_p2p(&p, NULL, 2048);
|
|
p.fd1 = -1;
|
|
p.filter = id;
|
|
p.subid = subid;
|
|
|
|
while (count > 0){
|
|
count = read(fdin,buf,2048);
|
|
if(filter_pes(buf,count,&p,audio_pes_filt))
|
|
write(fdout,buf,2048);
|
|
}
|
|
}
|
|
|
|
|
|
void pes_filt(p2p *p)
|
|
{
|
|
int factor = p->mpeg-1;
|
|
|
|
if ( p->cid == p->filter) {
|
|
if (p->es)
|
|
write(p->fd1,p->buf+p->hlength+6+3*factor,
|
|
p->plength-p->hlength-3*factor);
|
|
else
|
|
write(p->fd1,p->buf,p->plength+6);
|
|
}
|
|
}
|
|
|
|
void extract_from_pes(int fdin, int fdout, uint8_t id, int es)
|
|
{
|
|
p2p p;
|
|
int count = 1;
|
|
uint8_t buf[SIZE];
|
|
|
|
init_p2p(&p, NULL, 2048);
|
|
p.fd1 = fdout;
|
|
p.filter = id;
|
|
p.es = es;
|
|
|
|
while (count > 0){
|
|
count = read(fdin,buf,SIZE);
|
|
get_pes(buf,count,&p,pes_filt);
|
|
}
|
|
}
|
|
|
|
|
|
void pes_dfilt(p2p *p)
|
|
{
|
|
int factor = p->mpeg-1;
|
|
int fd =0;
|
|
int head=0;
|
|
int type = NOPES;
|
|
int streamid;
|
|
int c = 6+p->hlength+3*factor;
|
|
|
|
|
|
switch ( p->cid ) {
|
|
case PRIVATE_STREAM1:
|
|
streamid = p->buf[c];
|
|
head = 4;
|
|
if ((streamid & 0xF8) == 0x80+p->es-1){
|
|
fd = p->fd1;
|
|
type = AC3;
|
|
}
|
|
break;
|
|
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
|
fd = p->fd1;
|
|
type = AUDIO;
|
|
break;
|
|
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
|
fd = p->fd2;
|
|
type = VIDEO;
|
|
break;
|
|
}
|
|
|
|
if (p->es && !p->startv && type == VIDEO){
|
|
int found = 0;
|
|
|
|
if ( p->flag2 & PTS_DTS )
|
|
p->vpts = trans_pts_dts(p->pts);
|
|
else return;
|
|
|
|
while ( !found && c+3 < p->plength+6 ){
|
|
if ( p->buf[c] == 0x00 &&
|
|
p->buf[c+1] == 0x00 &&
|
|
p->buf[c+2] == 0x01 &&
|
|
p->buf[c+3] == 0xb3)
|
|
found = 1;
|
|
else c++;
|
|
}
|
|
if (found){
|
|
p->startv = 1;
|
|
write(fd, p->buf+c, p->plength+6-c);
|
|
}
|
|
fd = 0;
|
|
}
|
|
|
|
|
|
if ( p->es && !p->starta && type == AUDIO){
|
|
int found = 0;
|
|
if ( p->flag2 & PTS_DTS )
|
|
p->apts = trans_pts_dts(p->pts);
|
|
else return;
|
|
|
|
if (p->startv)
|
|
while ( !found && c+1 < p->plength+6){
|
|
if ( p->buf[c] == 0xFF &&
|
|
(p->buf[c+1] & 0xF8) == 0xF8)
|
|
found = 1;
|
|
else c++;
|
|
}
|
|
if (found){
|
|
p->starta = 1;
|
|
write(fd, p->buf+c, p->plength+6-c);
|
|
}
|
|
fd = 0;
|
|
}
|
|
|
|
if ( p->es && !p->starta && type == AC3){
|
|
if ( p->flag2 & PTS_DTS )
|
|
p->apts = trans_pts_dts(p->pts);
|
|
else return;
|
|
|
|
if (p->startv){
|
|
c+= ((p->buf[c+2] << 8)| p->buf[c+3]);
|
|
p->starta = 1;
|
|
write(fd, p->buf+c, p->plength+6-c);
|
|
}
|
|
fd = 0;
|
|
}
|
|
|
|
|
|
if (fd){
|
|
if (p->es)
|
|
write(fd,p->buf+p->hlength+6+3*factor+head,
|
|
p->plength-p->hlength-3*factor-head);
|
|
else
|
|
write(fd,p->buf,p->plength+6);
|
|
}
|
|
}
|
|
|
|
int64_t pes_dmx( int fdin, int fdouta, int fdoutv, int es)
|
|
{
|
|
p2p p;
|
|
int count = 1;
|
|
uint8_t buf[SIZE];
|
|
uint64_t length = 0;
|
|
uint64_t l = 0;
|
|
int verb = 0;
|
|
int percent, oldPercent = -1;
|
|
|
|
init_p2p(&p, NULL, 2048);
|
|
p.fd1 = fdouta;
|
|
p.fd2 = fdoutv;
|
|
p.es = es;
|
|
p.startv = 0;
|
|
p.starta = 0;
|
|
p.apts=-1;
|
|
p.vpts=-1;
|
|
|
|
if (fdin != STDIN_FILENO) verb = 1;
|
|
|
|
if (verb) {
|
|
length = lseek(fdin, 0, SEEK_END);
|
|
lseek(fdin,0,SEEK_SET);
|
|
}
|
|
|
|
while (count > 0){
|
|
count = read(fdin,buf,SIZE);
|
|
l += count;
|
|
if (verb){
|
|
percent = 100 * l / length;
|
|
|
|
if (percent != oldPercent) {
|
|
fprintf(stderr, "Demuxing %d %%\r", percent);
|
|
oldPercent = percent;
|
|
}
|
|
}
|
|
get_pes(buf,count,&p,pes_dfilt);
|
|
}
|
|
|
|
return (int64_t)p.vpts - (int64_t)p.apts;
|
|
|
|
}
|
|
|
|
|
|
/* SV: made non-static */
|
|
void pes_in_ts(p2p *p)
|
|
{
|
|
int l, pes_start;
|
|
uint8_t obuf[TS_SIZE];
|
|
long int c = 0;
|
|
int length = p->plength+6;
|
|
uint16_t pid;
|
|
uint8_t *counter;
|
|
pes_start = 1;
|
|
switch ( p->cid ) {
|
|
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
|
pid = p->pida;
|
|
counter = &p->acounter;
|
|
break;
|
|
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
|
pid = p->pidv;
|
|
counter = &p->acounter;
|
|
|
|
tspid0[3] |= (p->count0++)
|
|
& 0x0F ;
|
|
tspid1[3] |= (p->count1++)
|
|
& 0x0F ;
|
|
|
|
tspid1[24] = p->pidv;
|
|
tspid1[23] |= (p->pidv >> 8) & 0x3F;
|
|
tspid1[29] = p->pida;
|
|
tspid1[28] |= (p->pida >> 8) & 0x3F;
|
|
|
|
p->func(tspid0,188,p);
|
|
p->func(tspid1,188,p);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
while ( c < length ){
|
|
memset(obuf,0,TS_SIZE);
|
|
if (length - c >= TS_SIZE-4){
|
|
l = write_ts_header(pid, counter, pes_start
|
|
, obuf, TS_SIZE-4);
|
|
memcpy(obuf+l, p->buf+c, TS_SIZE-l);
|
|
c += TS_SIZE-l;
|
|
} else {
|
|
l = write_ts_header(pid, counter, pes_start
|
|
, obuf, length-c);
|
|
memcpy(obuf+l, p->buf+c, TS_SIZE-l);
|
|
c = length;
|
|
}
|
|
p->func(obuf,188,p);
|
|
pes_start = 0;
|
|
}
|
|
}
|
|
|
|
static
|
|
void write_out(uint8_t *buf, int count,void *p)
|
|
{
|
|
write(STDOUT_FILENO, buf, count);
|
|
}
|
|
|
|
|
|
void pes_to_ts2( int fdin, int fdout, uint16_t pida, uint16_t pidv)
|
|
{
|
|
p2p p;
|
|
int count = 1;
|
|
uint8_t buf[SIZE];
|
|
uint64_t length = 0;
|
|
uint64_t l = 0;
|
|
int verb = 0;
|
|
|
|
init_p2p(&p, NULL, 2048);
|
|
p.fd1 = fdout;
|
|
p.pida = pida;
|
|
p.pidv = pidv;
|
|
p.acounter = 0;
|
|
p.vcounter = 0;
|
|
p.count1 = 0;
|
|
p.count0 = 0;
|
|
p.func = write_out;
|
|
|
|
if (fdin != STDIN_FILENO) verb = 1;
|
|
|
|
if (verb) {
|
|
length = lseek(fdin, 0, SEEK_END);
|
|
lseek(fdin,0,SEEK_SET);
|
|
}
|
|
|
|
while (count > 0){
|
|
count = read(fdin,buf,SIZE);
|
|
l += count;
|
|
if (verb)
|
|
fprintf(stderr,"Writing TS %2.2f %%\r",
|
|
100.*l/length);
|
|
|
|
get_pes(buf,count,&p,pes_in_ts);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
#define IN_SIZE TS_SIZE*10
|
|
void find_avpids(int fd, uint16_t *vpid, uint16_t *apid)
|
|
{
|
|
uint8_t buf[IN_SIZE];
|
|
int count;
|
|
int i;
|
|
int off =0;
|
|
|
|
while ( *apid == 0 || *vpid == 0){
|
|
count = read(fd, buf, IN_SIZE);
|
|
for (i = 0; i < count-7; i++){
|
|
if (buf[i] == 0x47){
|
|
if (buf[i+1] & 0x40){
|
|
off = 0;
|
|
if ( buf[3+i] & 0x20)//adapt field?
|
|
off = buf[4+i] + 1;
|
|
switch(buf[i+7+off]){
|
|
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
|
*vpid = get_pid(buf+i+1);
|
|
break;
|
|
case PRIVATE_STREAM1:
|
|
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
|
*apid = get_pid(buf+i+1);
|
|
break;
|
|
}
|
|
}
|
|
i += 187;
|
|
}
|
|
if (*apid != 0 && *vpid != 0) break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void find_bavpids(uint8_t *buf, int count, uint16_t *vpid, uint16_t *apid)
|
|
{
|
|
int i;
|
|
int founda = 0;
|
|
int foundb = 0;
|
|
int off = 0;
|
|
|
|
*vpid = 0;
|
|
*apid = 0;
|
|
for (i = 0; i < count-7; i++){
|
|
if (buf[i] == 0x47){
|
|
if ((buf[i+1] & 0xF0) == 0x40){
|
|
off = 0;
|
|
if ( buf[3+i] & 0x20) // adaptation field?
|
|
off = buf[4+i] + 1;
|
|
|
|
if (buf[off+i+4] == 0x00 &&
|
|
buf[off+i+5] == 0x00 &&
|
|
buf[off+i+6] == 0x01){
|
|
switch(buf[off+i+7]){
|
|
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
|
*vpid = get_pid(buf+i+1);
|
|
foundb=1;
|
|
break;
|
|
case PRIVATE_STREAM1:
|
|
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
|
*apid = get_pid(buf+i+1);
|
|
founda=1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
i += 187;
|
|
}
|
|
if (founda && foundb) break;
|
|
}
|
|
}
|
|
|
|
|
|
void ts_to_pes( int fdin, uint16_t pida, uint16_t pidv, int ps)
|
|
{
|
|
|
|
uint8_t buf[IN_SIZE];
|
|
uint8_t mbuf[TS_SIZE];
|
|
int i;
|
|
int count = 1;
|
|
uint16_t pid;
|
|
uint16_t dummy;
|
|
ipack pa, pv;
|
|
ipack *p;
|
|
|
|
if (fdin != STDIN_FILENO && (!pida || !pidv))
|
|
find_avpids(fdin, &pidv, &pida);
|
|
|
|
init_ipack(&pa, IPACKS,write_out, ps);
|
|
init_ipack(&pv, IPACKS,write_out, ps);
|
|
|
|
if ((count = save_read(fdin,mbuf,TS_SIZE))<0)
|
|
perror("reading");
|
|
|
|
for ( i = 0; i < 188 ; i++){
|
|
if ( mbuf[i] == 0x47 ) break;
|
|
}
|
|
if ( i == 188){
|
|
fprintf(stderr,"Not a TS\n");
|
|
return;
|
|
} else {
|
|
memcpy(buf,mbuf+i,TS_SIZE-i);
|
|
if ((count = save_read(fdin,mbuf,i))<0)
|
|
perror("reading");
|
|
memcpy(buf+TS_SIZE-i,mbuf,i);
|
|
i = 188;
|
|
}
|
|
count = 1;
|
|
while (count > 0){
|
|
if ((count = save_read(fdin,buf+i,IN_SIZE-i)+i)<0)
|
|
perror("reading");
|
|
|
|
|
|
if (!pidv){
|
|
find_bavpids(buf+i, IN_SIZE-i, &pidv, &dummy);
|
|
if (pidv) fprintf(stderr, "vpid %d (0x%02x)\n",
|
|
pidv,pidv);
|
|
}
|
|
|
|
if (!pida){
|
|
find_bavpids(buf+i, IN_SIZE-i, &dummy, &pida);
|
|
if (pida) fprintf(stderr, "apid %d (0x%02x)\n",
|
|
pida,pida);
|
|
}
|
|
|
|
|
|
for( i = 0; i < count; i+= TS_SIZE){
|
|
uint8_t off = 0;
|
|
|
|
if ( count - i < TS_SIZE) break;
|
|
|
|
pid = get_pid(buf+i+1);
|
|
if (!(buf[3+i]&0x10)) // no payload?
|
|
continue;
|
|
if ( buf[1+i]&0x80){
|
|
fprintf(stderr,"Error in TS for PID: %d\n",
|
|
pid);
|
|
}
|
|
if (pid == pidv){
|
|
p = &pv;
|
|
} else {
|
|
if (pid == pida){
|
|
p = &pa;
|
|
} else continue;
|
|
}
|
|
|
|
if ( buf[1+i]&0x40) {
|
|
if (p->plength == MMAX_PLENGTH-6){
|
|
p->plength = p->found-6;
|
|
p->found = 0;
|
|
send_ipack(p);
|
|
reset_ipack(p);
|
|
}
|
|
}
|
|
|
|
if ( buf[3+i] & 0x20) { // adaptation field?
|
|
off = buf[4+i] + 1;
|
|
}
|
|
|
|
instant_repack(buf+4+off+i, TS_SIZE-4-off, p);
|
|
}
|
|
i = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
#define INN_SIZE 2*IN_SIZE
|
|
void insert_pat_pmt( int fdin, int fdout)
|
|
{
|
|
|
|
uint8_t buf[INN_SIZE];
|
|
uint8_t mbuf[TS_SIZE];
|
|
int i;
|
|
int count = 1;
|
|
uint16_t pida = 0;
|
|
uint16_t pidv = 0;
|
|
int written,c;
|
|
uint8_t c0 = 0;
|
|
uint8_t c1 = 0;
|
|
uint8_t pmt_len;
|
|
uint32_t crc32;
|
|
|
|
|
|
find_avpids(fdin, &pidv, &pida);
|
|
|
|
count = save_read(fdin,mbuf,TS_SIZE);
|
|
for ( i = 0; i < 188 ; i++){
|
|
if ( mbuf[i] == 0x47 ) break;
|
|
}
|
|
if ( i == 188){
|
|
fprintf(stderr,"Not a TS\n");
|
|
return;
|
|
} else {
|
|
memcpy(buf,mbuf+i,TS_SIZE-i);
|
|
count = save_read(fdin,mbuf,i);
|
|
memcpy(buf+TS_SIZE-i,mbuf,i);
|
|
i = 188;
|
|
}
|
|
|
|
count = 1;
|
|
/* length is not correct, but we only create a very small
|
|
* PMT, so it doesn't matter :-)
|
|
*/
|
|
pmt_len = tspid1[7] + 3;
|
|
while (count > 0){
|
|
tspid1[24] = pidv;
|
|
tspid1[23] |= (pidv >> 8) & 0x3F;
|
|
tspid1[29] = pida;
|
|
tspid1[28] |= (pida >> 8) & 0x3F;
|
|
crc32 = calc_crc32 (&tspid1[5], pmt_len - 4);
|
|
tspid1[5 + pmt_len - 4] = (crc32 & 0xff000000) >> 24;
|
|
tspid1[5 + pmt_len - 3] = (crc32 & 0x00ff0000) >> 16;
|
|
tspid1[5 + pmt_len - 2] = (crc32 & 0x0000ff00) >> 8;
|
|
tspid1[5 + pmt_len - 1] = (crc32 & 0x000000ff) >> 0;
|
|
|
|
write(fdout,tspid0,188);
|
|
write(fdout,tspid1,188);
|
|
|
|
count = save_read(fdin,buf+i,INN_SIZE-i);
|
|
|
|
written = 0;
|
|
while (written < IN_SIZE){
|
|
c = write(fdout,buf,INN_SIZE);
|
|
if (c>0) written += c;
|
|
}
|
|
tspid0[3] &= 0xF0 ;
|
|
tspid0[3] |= (c0++)& 0x0F ;
|
|
|
|
tspid1[3] &= 0xF0 ;
|
|
tspid1[3] |= (c1++)& 0x0F ;
|
|
|
|
i=0;
|
|
}
|
|
|
|
}
|
|
|
|
void get_pes (uint8_t *buf, int count, p2p *p, void (*func)(p2p *p))
|
|
{
|
|
|
|
int l;
|
|
unsigned short *pl;
|
|
int c=0;
|
|
|
|
uint8_t headr[3] = { 0x00, 0x00, 0x01} ;
|
|
|
|
while (c < count && (p->mpeg == 0 ||
|
|
(p->mpeg == 1 && p->found < 7) ||
|
|
(p->mpeg == 2 && p->found < 9))
|
|
&& (p->found < 5 || !p->done)){
|
|
switch ( p->found ){
|
|
case 0:
|
|
case 1:
|
|
if (buf[c] == 0x00) p->found++;
|
|
else p->found = 0;
|
|
c++;
|
|
break;
|
|
case 2:
|
|
if (buf[c] == 0x01) p->found++;
|
|
else if (buf[c] == 0){
|
|
p->found = 2;
|
|
} else p->found = 0;
|
|
c++;
|
|
break;
|
|
case 3:
|
|
p->cid = 0;
|
|
switch (buf[c]){
|
|
case PROG_STREAM_MAP:
|
|
case PRIVATE_STREAM2:
|
|
case PROG_STREAM_DIR:
|
|
case ECM_STREAM :
|
|
case EMM_STREAM :
|
|
case PADDING_STREAM :
|
|
case DSM_CC_STREAM :
|
|
case ISO13522_STREAM:
|
|
p->done = 1;
|
|
case PRIVATE_STREAM1:
|
|
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
|
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
|
p->found++;
|
|
p->cid = buf[c];
|
|
c++;
|
|
break;
|
|
default:
|
|
p->found = 0;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case 4:
|
|
if (count-c > 1){
|
|
pl = (unsigned short *) (buf+c);
|
|
p->plength = ntohs(*pl);
|
|
p->plen[0] = buf[c];
|
|
c++;
|
|
p->plen[1] = buf[c];
|
|
c++;
|
|
p->found+=2;
|
|
} else {
|
|
p->plen[0] = buf[c];
|
|
p->found++;
|
|
return;
|
|
}
|
|
break;
|
|
case 5:
|
|
p->plen[1] = buf[c];
|
|
c++;
|
|
pl = (unsigned short *) p->plen;
|
|
p->plength = ntohs(*pl);
|
|
p->found++;
|
|
break;
|
|
|
|
|
|
case 6:
|
|
if (!p->done){
|
|
p->flag1 = buf[c];
|
|
c++;
|
|
p->found++;
|
|
if ( (p->flag1 & 0xC0) == 0x80 ) p->mpeg = 2;
|
|
else {
|
|
p->hlength = 0;
|
|
p->which = 0;
|
|
p->mpeg = 1;
|
|
p->flag2 = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 7:
|
|
if ( !p->done && p->mpeg == 2){
|
|
p->flag2 = buf[c];
|
|
c++;
|
|
p->found++;
|
|
}
|
|
break;
|
|
|
|
case 8:
|
|
if ( !p->done && p->mpeg == 2){
|
|
p->hlength = buf[c];
|
|
c++;
|
|
p->found++;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!p->plength) p->plength = MMAX_PLENGTH-6;
|
|
|
|
|
|
if ( p->done || ((p->mpeg == 2 && p->found >= 9) ||
|
|
(p->mpeg == 1 && p->found >= 7)) ){
|
|
switch (p->cid){
|
|
|
|
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
|
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
|
case PRIVATE_STREAM1:
|
|
|
|
memcpy(p->buf, headr, 3);
|
|
p->buf[3] = p->cid;
|
|
memcpy(p->buf+4,p->plen,2);
|
|
|
|
if (p->mpeg == 2 && p->found == 9){
|
|
p->buf[6] = p->flag1;
|
|
p->buf[7] = p->flag2;
|
|
p->buf[8] = p->hlength;
|
|
}
|
|
|
|
if (p->mpeg == 1 && p->found == 7){
|
|
p->buf[6] = p->flag1;
|
|
}
|
|
|
|
|
|
if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) &&
|
|
p->found < 14){
|
|
while (c < count && p->found < 14){
|
|
p->pts[p->found-9] = buf[c];
|
|
p->buf[p->found] = buf[c];
|
|
c++;
|
|
p->found++;
|
|
}
|
|
if (c == count) return;
|
|
}
|
|
|
|
if (p->mpeg == 1 && p->which < 2000){
|
|
|
|
if (p->found == 7) {
|
|
p->check = p->flag1;
|
|
p->hlength = 1;
|
|
}
|
|
|
|
while (!p->which && c < count &&
|
|
p->check == 0xFF){
|
|
p->check = buf[c];
|
|
p->buf[p->found] = buf[c];
|
|
c++;
|
|
p->found++;
|
|
p->hlength++;
|
|
}
|
|
|
|
if ( c == count) return;
|
|
|
|
if ( (p->check & 0xC0) == 0x40 && !p->which){
|
|
p->check = buf[c];
|
|
p->buf[p->found] = buf[c];
|
|
c++;
|
|
p->found++;
|
|
p->hlength++;
|
|
|
|
p->which = 1;
|
|
if ( c == count) return;
|
|
p->check = buf[c];
|
|
p->buf[p->found] = buf[c];
|
|
c++;
|
|
p->found++;
|
|
p->hlength++;
|
|
p->which = 2;
|
|
if ( c == count) return;
|
|
}
|
|
|
|
if (p->which == 1){
|
|
p->check = buf[c];
|
|
p->buf[p->found] = buf[c];
|
|
c++;
|
|
p->found++;
|
|
p->hlength++;
|
|
p->which = 2;
|
|
if ( c == count) return;
|
|
}
|
|
|
|
if ( (p->check & 0x30) && p->check != 0xFF){
|
|
p->flag2 = (p->check & 0xF0) << 2;
|
|
p->pts[0] = p->check;
|
|
p->which = 3;
|
|
}
|
|
|
|
if ( c == count) return;
|
|
if (p->which > 2){
|
|
if ((p->flag2 & PTS_DTS_FLAGS)
|
|
== PTS_ONLY){
|
|
while (c < count &&
|
|
p->which < 7){
|
|
p->pts[p->which-2] =
|
|
buf[c];
|
|
p->buf[p->found] =
|
|
buf[c];
|
|
c++;
|
|
p->found++;
|
|
p->which++;
|
|
p->hlength++;
|
|
}
|
|
if ( c == count) return;
|
|
} else if ((p->flag2 & PTS_DTS_FLAGS)
|
|
== PTS_DTS){
|
|
while (c < count &&
|
|
p->which< 12){
|
|
if (p->which< 7)
|
|
p->pts[p->which
|
|
-2] =
|
|
buf[c];
|
|
p->buf[p->found] =
|
|
buf[c];
|
|
c++;
|
|
p->found++;
|
|
p->which++;
|
|
p->hlength++;
|
|
}
|
|
if ( c == count) return;
|
|
}
|
|
p->which = 2000;
|
|
}
|
|
|
|
}
|
|
|
|
while (c < count && p->found < p->plength+6){
|
|
l = count -c;
|
|
if (l+p->found > p->plength+6)
|
|
l = p->plength+6-p->found;
|
|
memcpy(p->buf+p->found, buf+c, l);
|
|
p->found += l;
|
|
c += l;
|
|
}
|
|
if(p->found == p->plength+6)
|
|
func(p);
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
if ( p->done ){
|
|
if( p->found + count - c < p->plength+6){
|
|
p->found += count-c;
|
|
c = count;
|
|
} else {
|
|
c += p->plength+6 - p->found;
|
|
p->found = p->plength+6;
|
|
}
|
|
}
|
|
|
|
if (p->plength && p->found == p->plength+6) {
|
|
p->found = 0;
|
|
p->done = 0;
|
|
p->plength = 0;
|
|
memset(p->buf, 0, MAX_PLENGTH);
|
|
if (c < count)
|
|
get_pes(buf+c, count-c, p, func);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
void setup_pes2ts( p2p *p, uint32_t pida, uint32_t pidv,
|
|
void (*ts_write)(uint8_t *buf, int count, void *p))
|
|
{
|
|
init_p2p( p, ts_write, 2048);
|
|
p->pida = pida;
|
|
p->pidv = pidv;
|
|
p->acounter = 0;
|
|
p->vcounter = 0;
|
|
p->count1 = 0;
|
|
p->count0 = 0;
|
|
}
|
|
|
|
void kpes_to_ts( p2p *p,uint8_t *buf ,int count )
|
|
{
|
|
get_pes(buf,count, p,pes_in_ts);
|
|
}
|
|
|
|
|
|
void setup_ts2pes( p2p *pa, p2p *pv, uint32_t pida, uint32_t pidv,
|
|
void (*pes_write)(uint8_t *buf, int count, void *p))
|
|
{
|
|
init_p2p( pa, pes_write, 2048);
|
|
init_p2p( pv, pes_write, 2048);
|
|
pa->pid = pida;
|
|
pv->pid = pidv;
|
|
}
|
|
|
|
void kts_to_pes( p2p *p, uint8_t *buf) // don't need count (=188)
|
|
{
|
|
uint8_t off = 0;
|
|
uint16_t pid = 0;
|
|
|
|
if (!(buf[3]&PAYLOAD)) // no payload?
|
|
return;
|
|
|
|
pid = get_pid(buf+1);
|
|
|
|
if (pid != p->pid) return;
|
|
if ( buf[1]&0x80){
|
|
fprintf(stderr,"Error in TS for PID: %d\n",
|
|
pid);
|
|
}
|
|
|
|
if ( buf[1]&PAY_START) {
|
|
if (p->plength == MMAX_PLENGTH-6){
|
|
p->plength = p->found-6;
|
|
p->found = 0;
|
|
pes_repack(p);
|
|
}
|
|
}
|
|
|
|
if ( buf[3] & ADAPT_FIELD) { // adaptation field?
|
|
off = buf[4] + 1;
|
|
if (off+4 > 187) return;
|
|
}
|
|
|
|
get_pes(buf+4+off, TS_SIZE-4-off, p , pes_repack);
|
|
}
|
|
|
|
|
|
|
|
|
|
// instant repack
|
|
|
|
|
|
void reset_ipack(ipack *p)
|
|
{
|
|
p->found = 0;
|
|
p->cid = 0;
|
|
p->plength = 0;
|
|
p->flag1 = 0;
|
|
p->flag2 = 0;
|
|
p->hlength = 0;
|
|
p->mpeg = 0;
|
|
p->check = 0;
|
|
p->which = 0;
|
|
p->done = 0;
|
|
p->count = 0;
|
|
p->size = p->size_orig;
|
|
}
|
|
|
|
void init_ipack(ipack *p, int size,
|
|
void (*func)(uint8_t *buf, int size, void *priv), int ps)
|
|
{
|
|
if ( !(p->buf = malloc(size)) ){
|
|
fprintf(stderr,"Couldn't allocate memory for ipack\n");
|
|
exit(1);
|
|
}
|
|
p->ps = ps;
|
|
p->size_orig = size;
|
|
p->func = func;
|
|
reset_ipack(p);
|
|
p->has_ai = 0;
|
|
p->has_vi = 0;
|
|
p->start = 0;
|
|
}
|
|
|
|
void free_ipack(ipack * p)
|
|
{
|
|
if (p->buf) free(p->buf);
|
|
}
|
|
|
|
|
|
|
|
int get_vinfo(uint8_t *mbuf, int count, VideoInfo *vi, int pr)
|
|
{
|
|
uint8_t *headr;
|
|
int found = 0;
|
|
int sw;
|
|
int form = -1;
|
|
int c = 0;
|
|
|
|
while (found < 4 && c+4 < count){
|
|
uint8_t *b;
|
|
|
|
b = mbuf+c;
|
|
if ( b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01
|
|
&& b[3] == 0xb3) found = 4;
|
|
else {
|
|
c++;
|
|
}
|
|
}
|
|
|
|
if (! found) return -1;
|
|
c += 4;
|
|
if (c+12 >= count) return -1;
|
|
headr = mbuf+c;
|
|
|
|
vi->horizontal_size = ((headr[1] &0xF0) >> 4) | (headr[0] << 4);
|
|
vi->vertical_size = ((headr[1] &0x0F) << 8) | (headr[2]);
|
|
|
|
sw = (int)((headr[3]&0xF0) >> 4) ;
|
|
|
|
switch( sw ){
|
|
case 1:
|
|
if (pr)
|
|
fprintf(stderr,"Videostream: ASPECT: 1:1");
|
|
vi->aspect_ratio = 100;
|
|
break;
|
|
case 2:
|
|
if (pr)
|
|
fprintf(stderr,"Videostream: ASPECT: 4:3");
|
|
vi->aspect_ratio = 133;
|
|
break;
|
|
case 3:
|
|
if (pr)
|
|
fprintf(stderr,"Videostream: ASPECT: 16:9");
|
|
vi->aspect_ratio = 177;
|
|
break;
|
|
case 4:
|
|
if (pr)
|
|
fprintf(stderr,"Videostream: ASPECT: 2.21:1");
|
|
vi->aspect_ratio = 221;
|
|
break;
|
|
|
|
case 5 ... 15:
|
|
if (pr)
|
|
fprintf(stderr,"Videostream: ASPECT: reserved");
|
|
vi->aspect_ratio = 0;
|
|
break;
|
|
|
|
default:
|
|
vi->aspect_ratio = 0;
|
|
return -1;
|
|
}
|
|
|
|
if (pr)
|
|
fprintf(stderr," Size = %dx%d",vi->horizontal_size,
|
|
vi->vertical_size);
|
|
|
|
sw = (int)(headr[3]&0x0F);
|
|
|
|
switch ( sw ) {
|
|
case 1:
|
|
if (pr)
|
|
fprintf(stderr," FRate: 23.976 fps");
|
|
vi->framerate = 24000/1001.;
|
|
form = -1;
|
|
break;
|
|
case 2:
|
|
if (pr)
|
|
fprintf(stderr," FRate: 24 fps");
|
|
vi->framerate = 24;
|
|
form = -1;
|
|
break;
|
|
case 3:
|
|
if (pr)
|
|
fprintf(stderr," FRate: 25 fps");
|
|
vi->framerate = 25;
|
|
form = VIDEO_MODE_PAL;
|
|
break;
|
|
case 4:
|
|
if (pr)
|
|
fprintf(stderr," FRate: 29.97 fps");
|
|
vi->framerate = 30000/1001.;
|
|
form = VIDEO_MODE_NTSC;
|
|
break;
|
|
case 5:
|
|
if (pr)
|
|
fprintf(stderr," FRate: 30 fps");
|
|
vi->framerate = 30;
|
|
form = VIDEO_MODE_NTSC;
|
|
break;
|
|
case 6:
|
|
if (pr)
|
|
fprintf(stderr," FRate: 50 fps");
|
|
vi->framerate = 50;
|
|
form = VIDEO_MODE_PAL;
|
|
break;
|
|
case 7:
|
|
if (pr)
|
|
fprintf(stderr," FRate: 60 fps");
|
|
vi->framerate = 60;
|
|
form = VIDEO_MODE_NTSC;
|
|
break;
|
|
}
|
|
|
|
vi->bit_rate = 400*(((headr[4] << 10) & 0x0003FC00UL)
|
|
| ((headr[5] << 2) & 0x000003FCUL) |
|
|
(((headr[6] & 0xC0) >> 6) & 0x00000003UL));
|
|
|
|
if (pr){
|
|
fprintf(stderr," BRate: %.2f Mbit/s",(vi->bit_rate)/1000000.);
|
|
fprintf(stderr,"\n");
|
|
}
|
|
vi->video_format = form;
|
|
|
|
vi->off = c-4;
|
|
return c-4;
|
|
}
|
|
|
|
extern unsigned int bitrates[3][16];
|
|
extern uint32_t freq[4];
|
|
|
|
int get_ainfo(uint8_t *mbuf, int count, AudioInfo *ai, int pr)
|
|
{
|
|
uint8_t *headr;
|
|
int found = 0;
|
|
int c = 0;
|
|
int fr =0;
|
|
|
|
while (!found && c < count){
|
|
uint8_t *b = mbuf+c;
|
|
|
|
if ( b[0] == 0xff && (b[1] & 0xf8) == 0xf8)
|
|
found = 1;
|
|
else {
|
|
c++;
|
|
}
|
|
}
|
|
|
|
if (!found) return -1;
|
|
|
|
if (c+3 >= count) return -1;
|
|
headr = mbuf+c;
|
|
|
|
ai->layer = (headr[1] & 0x06) >> 1;
|
|
|
|
if (pr)
|
|
fprintf(stderr,"Audiostream: Layer: %d", 4-ai->layer);
|
|
|
|
|
|
ai->bit_rate = bitrates[(3-ai->layer)][(headr[2] >> 4 )]*1000;
|
|
|
|
if (pr){
|
|
if (ai->bit_rate == 0)
|
|
fprintf (stderr," Bit rate: free");
|
|
else if (ai->bit_rate == 0xf)
|
|
fprintf (stderr," BRate: reserved");
|
|
else
|
|
fprintf (stderr," BRate: %d kb/s", ai->bit_rate/1000);
|
|
}
|
|
|
|
fr = (headr[2] & 0x0c ) >> 2;
|
|
ai->frequency = freq[fr]*100;
|
|
|
|
if (pr){
|
|
if (ai->frequency == 3)
|
|
fprintf (stderr, " Freq: reserved\n");
|
|
else
|
|
fprintf (stderr," Freq: %2.1f kHz\n",
|
|
ai->frequency/1000.);
|
|
}
|
|
ai->off = c;
|
|
return c;
|
|
}
|
|
|
|
unsigned int ac3_bitrates[32] =
|
|
{32,40,48,56,64,80,96,112,128,160,192,224,256,320,384,448,512,576,640,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0};
|
|
|
|
uint32_t ac3_freq[4] = {480, 441, 320, 0};
|
|
uint32_t ac3_frames[3][32] =
|
|
{{64,80,96,112,128,160,192,224,256,320,384,448,512,640,768,896,1024,
|
|
1152,1280,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
{69,87,104,121,139,174,208,243,278,348,417,487,557,696,835,975,1114,
|
|
1253,1393,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
|
{96,120,144,168,192,240,288,336,384,480,576,672,768,960,1152,1344,
|
|
1536,1728,1920,0,0,0,0,0,0,0,0,0,0,0,0,0}};
|
|
|
|
int get_ac3info(uint8_t *mbuf, int count, AudioInfo *ai, int pr)
|
|
{
|
|
uint8_t *headr;
|
|
int found = 0;
|
|
int c = 0;
|
|
uint8_t frame;
|
|
int fr = 0;
|
|
|
|
while ( !found && c < count){
|
|
uint8_t *b = mbuf+c;
|
|
if ( b[0] == 0x0b && b[1] == 0x77 )
|
|
found = 1;
|
|
else {
|
|
c++;
|
|
}
|
|
}
|
|
|
|
|
|
if (!found){
|
|
return -1;
|
|
}
|
|
ai->off = c;
|
|
|
|
if (c+5 >= count) return -1;
|
|
|
|
ai->layer = 0; // 0 for AC3
|
|
headr = mbuf+c+2;
|
|
|
|
frame = (headr[2]&0x3f);
|
|
ai->bit_rate = ac3_bitrates[frame>>1]*1000;
|
|
|
|
if (pr) fprintf (stderr," BRate: %d kb/s", ai->bit_rate/1000);
|
|
|
|
fr = (headr[2] & 0xc0 ) >> 6;
|
|
ai->frequency = freq[fr]*100;
|
|
if (pr) fprintf (stderr," Freq: %d Hz\n", ai->frequency);
|
|
|
|
ai->framesize = ac3_frames[fr][frame >> 1];
|
|
if ((frame & 1) && (fr == 1)) ai->framesize++;
|
|
ai->framesize = ai->framesize << 1;
|
|
if (pr) fprintf (stderr," Framesize %d\n", ai->framesize);
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
void ps_pes(ipack *p)
|
|
{
|
|
int check;
|
|
uint8_t pbuf[PS_HEADER_L2];
|
|
static int muxr = 0;
|
|
static int ai = 0;
|
|
static int vi = 0;
|
|
static int start = 0;
|
|
static uint32_t SCR = 0;
|
|
|
|
if (p->mpeg == 2){
|
|
switch(p->buf[3]){
|
|
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
|
if (!p->has_vi){
|
|
if(get_vinfo(p->buf, p->count, &p->vi,1) >=0) {
|
|
p->has_vi = 1;
|
|
vi = p->vi.bit_rate;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
|
if (!p->has_ai){
|
|
if(get_ainfo(p->buf, p->count, &p->ai,1) >=0) {
|
|
p->has_ai = 1;
|
|
ai = p->ai.bit_rate;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (p->has_vi && vi && !muxr){
|
|
muxr = (vi+ai)/400;
|
|
}
|
|
|
|
if ( start && muxr && (p->buf[7] & PTS_ONLY) && (p->has_ai ||
|
|
p->buf[9+p->buf[8]+4] == 0xb3)){
|
|
SCR = trans_pts_dts(p->pts)-3600;
|
|
|
|
check = write_ps_header(pbuf,
|
|
SCR,
|
|
muxr, 1, 0, 0, 1, 1, 1,
|
|
0, 0, 0, 0, 0, 0);
|
|
|
|
p->func(pbuf, check , p->data);
|
|
}
|
|
|
|
if (muxr && !start && vi){
|
|
SCR = trans_pts_dts(p->pts)-3600;
|
|
check = write_ps_header(pbuf,
|
|
SCR,
|
|
muxr, 1, 0, 0, 1, 1, 1,
|
|
0xC0, 0, 64, 0xE0, 1, 460);
|
|
start = 1;
|
|
p->func(pbuf, check , p->data);
|
|
}
|
|
|
|
if (start)
|
|
p->func(p->buf, p->count, p->data);
|
|
}
|
|
}
|
|
|
|
void send_ipack(ipack *p)
|
|
{
|
|
int streamid=0;
|
|
int off;
|
|
int ac3_off = 0;
|
|
AudioInfo ai;
|
|
int nframes= 0;
|
|
int f=0;
|
|
|
|
if (p->count < 10) return;
|
|
p->buf[3] = p->cid;
|
|
p->buf[4] = (uint8_t)(((p->count-6) & 0xFF00) >> 8);
|
|
p->buf[5] = (uint8_t)((p->count-6) & 0x00FF);
|
|
|
|
|
|
if (p->cid == PRIVATE_STREAM1){
|
|
|
|
off = 9+p->buf[8];
|
|
streamid = p->buf[off];
|
|
if ((streamid & 0xF8) == 0x80){
|
|
ai.off = 0;
|
|
ac3_off = ((p->buf[off+2] << 8)| p->buf[off+3]);
|
|
if (ac3_off < p->count)
|
|
f=get_ac3info(p->buf+off+3+ac3_off,
|
|
p->count-ac3_off, &ai,0);
|
|
if ( !f ){
|
|
nframes = (p->count-off-3-ac3_off)/
|
|
ai.framesize + 1;
|
|
p->buf[off+1] = nframes;
|
|
p->buf[off+2] = (ac3_off >> 8)& 0xFF;
|
|
p->buf[off+3] = (ac3_off)& 0xFF;
|
|
|
|
ac3_off += nframes * ai.framesize - p->count;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p->ps) ps_pes(p);
|
|
else p->func(p->buf, p->count, p->data);
|
|
|
|
switch ( p->mpeg ){
|
|
case 2:
|
|
|
|
p->buf[6] = 0x80;
|
|
p->buf[7] = 0x00;
|
|
p->buf[8] = 0x00;
|
|
p->count = 9;
|
|
|
|
if (p->cid == PRIVATE_STREAM1 && (streamid & 0xF8)==0x80 ){
|
|
p->count += 4;
|
|
p->buf[9] = streamid;
|
|
p->buf[10] = 0;
|
|
p->buf[11] = (ac3_off >> 8)& 0xFF;
|
|
p->buf[12] = (ac3_off)& 0xFF;
|
|
}
|
|
|
|
break;
|
|
case 1:
|
|
p->buf[6] = 0x0F;
|
|
p->count = 7;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static void write_ipack(ipack *p, uint8_t *data, int count)
|
|
{
|
|
AudioInfo ai;
|
|
uint8_t headr[3] = { 0x00, 0x00, 0x01} ;
|
|
int diff =0;
|
|
|
|
if (p->count < 6){
|
|
if (trans_pts_dts(p->pts) > trans_pts_dts(p->last_pts))
|
|
memcpy(p->last_pts, p->pts, 5);
|
|
p->count = 0;
|
|
memcpy(p->buf+p->count, headr, 3);
|
|
p->count += 6;
|
|
}
|
|
if ( p->size == p->size_orig && p->plength &&
|
|
(diff = 6+p->plength - p->found + p->count +count) > p->size &&
|
|
diff < 3*p->size/2){
|
|
|
|
p->size = diff/2;
|
|
// fprintf(stderr,"size: %d \n",p->size);
|
|
}
|
|
|
|
if (p->cid == PRIVATE_STREAM1 && p->count == p->hlength+9){
|
|
if ((data[0] & 0xF8) != 0x80){
|
|
int ac3_off;
|
|
|
|
ac3_off = get_ac3info(data, count, &ai,0);
|
|
if (ac3_off>=0 && ai.framesize){
|
|
p->buf[p->count] = 0x80;
|
|
p->buf[p->count+1] = (p->size - p->count
|
|
- 4 - ac3_off)/
|
|
ai.framesize + 1;
|
|
p->buf[p->count+2] = (ac3_off >> 8)& 0xFF;
|
|
p->buf[p->count+3] = (ac3_off)& 0xFF;
|
|
p->count+=4;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p->count + count < p->size){
|
|
memcpy(p->buf+p->count, data, count);
|
|
p->count += count;
|
|
} else {
|
|
int rest = p->size - p->count;
|
|
if (rest < 0) rest = 0;
|
|
memcpy(p->buf+p->count, data, rest);
|
|
p->count += rest;
|
|
// fprintf(stderr,"count: %d \n",p->count);
|
|
send_ipack(p);
|
|
if (count - rest > 0)
|
|
write_ipack(p, data+rest, count-rest);
|
|
}
|
|
}
|
|
|
|
void instant_repack (uint8_t *buf, int count, ipack *p)
|
|
{
|
|
|
|
int l;
|
|
unsigned short *pl;
|
|
int c=0;
|
|
|
|
while (c < count && (p->mpeg == 0 ||
|
|
(p->mpeg == 1 && p->found < 7) ||
|
|
(p->mpeg == 2 && p->found < 9))
|
|
&& (p->found < 5 || !p->done)){
|
|
switch ( p->found ){
|
|
case 0:
|
|
case 1:
|
|
if (buf[c] == 0x00) p->found++;
|
|
else p->found = 0;
|
|
c++;
|
|
break;
|
|
case 2:
|
|
if (buf[c] == 0x01) p->found++;
|
|
else if (buf[c] == 0){
|
|
p->found = 2;
|
|
} else p->found = 0;
|
|
c++;
|
|
break;
|
|
case 3:
|
|
p->cid = 0;
|
|
switch (buf[c]){
|
|
case PROG_STREAM_MAP:
|
|
case PRIVATE_STREAM2:
|
|
case PROG_STREAM_DIR:
|
|
case ECM_STREAM :
|
|
case EMM_STREAM :
|
|
case PADDING_STREAM :
|
|
case DSM_CC_STREAM :
|
|
case ISO13522_STREAM:
|
|
p->done = 1;
|
|
case PRIVATE_STREAM1:
|
|
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
|
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
|
p->found++;
|
|
p->cid = buf[c];
|
|
c++;
|
|
break;
|
|
default:
|
|
p->found = 0;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case 4:
|
|
if (count-c > 1){
|
|
pl = (unsigned short *) (buf+c);
|
|
p->plength = ntohs(*pl);
|
|
p->plen[0] = buf[c];
|
|
c++;
|
|
p->plen[1] = buf[c];
|
|
c++;
|
|
p->found+=2;
|
|
} else {
|
|
p->plen[0] = buf[c];
|
|
p->found++;
|
|
return;
|
|
}
|
|
break;
|
|
case 5:
|
|
p->plen[1] = buf[c];
|
|
c++;
|
|
pl = (unsigned short *) p->plen;
|
|
p->plength = ntohs(*pl);
|
|
p->found++;
|
|
break;
|
|
|
|
|
|
case 6:
|
|
if (!p->done){
|
|
p->flag1 = buf[c];
|
|
c++;
|
|
p->found++;
|
|
if ( (p->flag1 & 0xC0) == 0x80 ) p->mpeg = 2;
|
|
else {
|
|
p->hlength = 0;
|
|
p->which = 0;
|
|
p->mpeg = 1;
|
|
p->flag2 = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 7:
|
|
if ( !p->done && p->mpeg == 2){
|
|
p->flag2 = buf[c];
|
|
c++;
|
|
p->found++;
|
|
}
|
|
break;
|
|
|
|
case 8:
|
|
if ( !p->done && p->mpeg == 2){
|
|
p->hlength = buf[c];
|
|
c++;
|
|
p->found++;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (c == count) return;
|
|
|
|
if (!p->plength) p->plength = MMAX_PLENGTH-6;
|
|
|
|
|
|
if ( p->done || ((p->mpeg == 2 && p->found >= 9) ||
|
|
(p->mpeg == 1 && p->found >= 7)) ){
|
|
switch (p->cid){
|
|
|
|
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
|
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
|
case PRIVATE_STREAM1:
|
|
|
|
if (p->mpeg == 2 && p->found == 9){
|
|
write_ipack(p, &p->flag1, 1);
|
|
write_ipack(p, &p->flag2, 1);
|
|
write_ipack(p, &p->hlength, 1);
|
|
}
|
|
|
|
if (p->mpeg == 1 && p->found == 7){
|
|
write_ipack(p, &p->flag1, 1);
|
|
}
|
|
|
|
|
|
if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) &&
|
|
p->found < 14){
|
|
while (c < count && p->found < 14){
|
|
p->pts[p->found-9] = buf[c];
|
|
write_ipack(p, buf+c, 1);
|
|
c++;
|
|
p->found++;
|
|
}
|
|
if (c == count) return;
|
|
}
|
|
|
|
if (p->mpeg == 1 && p->which < 2000){
|
|
|
|
if (p->found == 7) {
|
|
p->check = p->flag1;
|
|
p->hlength = 1;
|
|
}
|
|
|
|
while (!p->which && c < count &&
|
|
p->check == 0xFF){
|
|
p->check = buf[c];
|
|
write_ipack(p, buf+c, 1);
|
|
c++;
|
|
p->found++;
|
|
p->hlength++;
|
|
}
|
|
|
|
if ( c == count) return;
|
|
|
|
if ( (p->check & 0xC0) == 0x40 && !p->which){
|
|
p->check = buf[c];
|
|
write_ipack(p, buf+c, 1);
|
|
c++;
|
|
p->found++;
|
|
p->hlength++;
|
|
|
|
p->which = 1;
|
|
if ( c == count) return;
|
|
p->check = buf[c];
|
|
write_ipack(p, buf+c, 1);
|
|
c++;
|
|
p->found++;
|
|
p->hlength++;
|
|
p->which = 2;
|
|
if ( c == count) return;
|
|
}
|
|
|
|
if (p->which == 1){
|
|
p->check = buf[c];
|
|
write_ipack(p, buf+c, 1);
|
|
c++;
|
|
p->found++;
|
|
p->hlength++;
|
|
p->which = 2;
|
|
if ( c == count) return;
|
|
}
|
|
|
|
if ( (p->check & 0x30) && p->check != 0xFF){
|
|
p->flag2 = (p->check & 0xF0) << 2;
|
|
p->pts[0] = p->check;
|
|
p->which = 3;
|
|
}
|
|
|
|
if ( c == count) return;
|
|
if (p->which > 2){
|
|
if ((p->flag2 & PTS_DTS_FLAGS)
|
|
== PTS_ONLY){
|
|
while (c < count &&
|
|
p->which < 7){
|
|
p->pts[p->which-2] =
|
|
buf[c];
|
|
write_ipack(p,buf+c,1);
|
|
c++;
|
|
p->found++;
|
|
p->which++;
|
|
p->hlength++;
|
|
}
|
|
if ( c == count) return;
|
|
} else if ((p->flag2 & PTS_DTS_FLAGS)
|
|
== PTS_DTS){
|
|
while (c < count &&
|
|
p->which< 12){
|
|
if (p->which< 7)
|
|
p->pts[p->which
|
|
-2] =
|
|
buf[c];
|
|
write_ipack(p,buf+c,1);
|
|
c++;
|
|
p->found++;
|
|
p->which++;
|
|
p->hlength++;
|
|
}
|
|
if ( c == count) return;
|
|
}
|
|
p->which = 2000;
|
|
}
|
|
|
|
}
|
|
|
|
while (c < count && p->found < p->plength+6){
|
|
l = count -c;
|
|
if (l+p->found > p->plength+6)
|
|
l = p->plength+6-p->found;
|
|
write_ipack(p, buf+c, l);
|
|
p->found += l;
|
|
c += l;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
if ( p->done ){
|
|
if( p->found + count - c < p->plength+6){
|
|
p->found += count-c;
|
|
c = count;
|
|
} else {
|
|
c += p->plength+6 - p->found;
|
|
p->found = p->plength+6;
|
|
}
|
|
}
|
|
|
|
if (p->plength && p->found == p->plength+6) {
|
|
send_ipack(p);
|
|
reset_ipack(p);
|
|
if (c < count)
|
|
instant_repack(buf+c, count-c, p);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
void write_out_es(uint8_t *buf, int count,void *priv)
|
|
{
|
|
ipack *p = (ipack *) priv;
|
|
uint8_t payl = buf[8]+9+p->start-1;
|
|
|
|
write(p->fd, buf+payl, count-payl);
|
|
p->start = 1;
|
|
}
|
|
|
|
void write_out_pes(uint8_t *buf, int count,void *priv)
|
|
{
|
|
ipack *p = (ipack *) priv;
|
|
write(p->fd, buf, count);
|
|
}
|
|
|
|
|
|
|
|
int64_t ts_demux(int fdin, int fdv_out,int fda_out,uint16_t pida,
|
|
uint16_t pidv, int es)
|
|
{
|
|
uint8_t buf[IN_SIZE];
|
|
uint8_t mbuf[TS_SIZE];
|
|
int i;
|
|
int count = 1;
|
|
uint16_t pid;
|
|
ipack pa, pv;
|
|
ipack *p;
|
|
uint8_t *sb;
|
|
int64_t apts=0;
|
|
int64_t vpts=0;
|
|
int verb = 0;
|
|
uint64_t length =0;
|
|
uint64_t l=0;
|
|
int perc =0;
|
|
int last_perc =0;
|
|
|
|
if (fdin != STDIN_FILENO) verb = 1;
|
|
|
|
if (verb) {
|
|
length = lseek(fdin, 0, SEEK_END);
|
|
lseek(fdin,0,SEEK_SET);
|
|
}
|
|
|
|
if (!pida || !pidv)
|
|
find_avpids(fdin, &pidv, &pida);
|
|
|
|
if (es){
|
|
init_ipack(&pa, IPACKS,write_out_es, 0);
|
|
init_ipack(&pv, IPACKS,write_out_es, 0);
|
|
} else {
|
|
init_ipack(&pa, IPACKS,write_out_pes, 0);
|
|
init_ipack(&pv, IPACKS,write_out_pes, 0);
|
|
}
|
|
pa.fd = fda_out;
|
|
pv.fd = fdv_out;
|
|
pa.data = (void *)&pa;
|
|
pv.data = (void *)&pv;
|
|
|
|
count = save_read(fdin,mbuf,TS_SIZE);
|
|
if (count) l+=count;
|
|
for ( i = 0; i < 188 ; i++){
|
|
if ( mbuf[i] == 0x47 ) break;
|
|
}
|
|
if ( i == 188){
|
|
fprintf(stderr,"Not a TS\n");
|
|
return 0;
|
|
} else {
|
|
memcpy(buf,mbuf+i,TS_SIZE-i);
|
|
count = save_read(fdin,mbuf,i);
|
|
if (count) l+=count;
|
|
memcpy(buf+TS_SIZE-i,mbuf,i);
|
|
i = 188;
|
|
}
|
|
|
|
count = 1;
|
|
while (count > 0){
|
|
count = save_read(fdin,buf+i,IN_SIZE-i)+i;
|
|
if (count) l+=count;
|
|
if (verb && perc >last_perc){
|
|
perc = (100*l)/length;
|
|
fprintf(stderr,"Reading TS %d %%\r",perc);
|
|
last_perc = perc;
|
|
}
|
|
|
|
for( i = 0; i < count; i+= TS_SIZE){
|
|
uint8_t off = 0;
|
|
|
|
if ( count - i < TS_SIZE) break;
|
|
|
|
pid = get_pid(buf+i+1);
|
|
if (!(buf[3+i]&0x10)) // no payload?
|
|
continue;
|
|
if ( buf[1+i]&0x80){
|
|
fprintf(stderr,"Error in TS for PID: %d\n",
|
|
pid);
|
|
}
|
|
if (pid == pidv){
|
|
p = &pv;
|
|
} else {
|
|
if (pid == pida){
|
|
p = &pa;
|
|
} else continue;
|
|
}
|
|
|
|
if ( buf[3+i] & 0x20) { // adaptation field?
|
|
off = buf[4+i] + 1;
|
|
}
|
|
|
|
if ( buf[1+i]&0x40) {
|
|
if (p->plength == MMAX_PLENGTH-6){
|
|
p->plength = p->found-6;
|
|
p->found = 0;
|
|
send_ipack(p);
|
|
reset_ipack(p);
|
|
}
|
|
sb = buf+4+off+i;
|
|
if( es &&
|
|
!p->start && (sb[7] & PTS_DTS_FLAGS)){
|
|
uint8_t *pay = sb+sb[8]+9;
|
|
int l = TS_SIZE - 13 - off - sb[8];
|
|
if ( pid == pidv &&
|
|
(p->start =
|
|
get_vinfo( pay, l,&p->vi,1)+1) >0
|
|
){
|
|
vpts = trans_pts_dts(sb+9);
|
|
printf("vpts : %fs\n",
|
|
vpts/90000.);
|
|
}
|
|
if ( pid == pida && es==1 &&
|
|
(p->start =
|
|
get_ainfo( pay, l,&p->ai,1)+1) >0
|
|
){
|
|
apts = trans_pts_dts(sb+9);
|
|
printf("apts : %fs\n",
|
|
apts/90000.);
|
|
}
|
|
if ( pid == pida && es==2 &&
|
|
(p->start =
|
|
get_ac3info( pay, l,&p->ai,1)+1) >0
|
|
){
|
|
apts = trans_pts_dts(sb+9);
|
|
printf("apts : %fs\n",
|
|
apts/90000.);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p->start)
|
|
instant_repack(buf+4+off+i, TS_SIZE-4-off, p);
|
|
}
|
|
i = 0;
|
|
|
|
}
|
|
|
|
return (vpts-apts);
|
|
}
|
|
|
|
void ts2es_opt(int fdin, uint16_t pidv, ipack *p, int verb)
|
|
{
|
|
uint8_t buf[IN_SIZE];
|
|
uint8_t mbuf[TS_SIZE];
|
|
int i;
|
|
int count = 1;
|
|
uint64_t length =0;
|
|
uint64_t l=0;
|
|
int perc =0;
|
|
int last_perc =0;
|
|
uint16_t pid;
|
|
|
|
if (verb) {
|
|
length = lseek(fdin, 0, SEEK_END);
|
|
lseek(fdin,0,SEEK_SET);
|
|
}
|
|
|
|
count = save_read(fdin,mbuf,TS_SIZE);
|
|
if (count) l+=count;
|
|
for ( i = 0; i < 188 ; i++){
|
|
if ( mbuf[i] == 0x47 ) break;
|
|
}
|
|
if ( i == 188){
|
|
fprintf(stderr,"Not a TS\n");
|
|
return;
|
|
} else {
|
|
memcpy(buf,mbuf+i,TS_SIZE-i);
|
|
count = save_read(fdin,mbuf,i);
|
|
if (count) l+=count;
|
|
memcpy(buf+TS_SIZE-i,mbuf,i);
|
|
i = 188;
|
|
}
|
|
|
|
count = 1;
|
|
while (count > 0){
|
|
count = save_read(fdin,buf+i,IN_SIZE-i)+i;
|
|
if (count) l+=count;
|
|
if (verb && perc >last_perc){
|
|
perc = (100*l)/length;
|
|
fprintf(stderr,"Reading TS %d %%\r",perc);
|
|
last_perc = perc;
|
|
}
|
|
|
|
for( i = 0; i < count; i+= TS_SIZE){
|
|
uint8_t off = 0;
|
|
|
|
if ( count - i < TS_SIZE) break;
|
|
|
|
pid = get_pid(buf+i+1);
|
|
if (!(buf[3+i]&0x10)) // no payload?
|
|
continue;
|
|
if ( buf[1+i]&0x80){
|
|
fprintf(stderr,"Error in TS for PID: %d\n",
|
|
pid);
|
|
}
|
|
if (pid != pidv){
|
|
continue;
|
|
}
|
|
|
|
if ( buf[3+i] & 0x20) { // adaptation field?
|
|
off = buf[4+i] + 1;
|
|
}
|
|
|
|
if ( buf[1+i]&0x40) {
|
|
if (p->plength == MMAX_PLENGTH-6){
|
|
p->plength = p->found-6;
|
|
p->found = 0;
|
|
send_ipack(p);
|
|
reset_ipack(p);
|
|
}
|
|
}
|
|
|
|
instant_repack(buf+4+off+i, TS_SIZE-4-off, p);
|
|
}
|
|
i = 0;
|
|
|
|
}
|
|
}
|
|
|
|
void ts2es(int fdin, uint16_t pidv)
|
|
{
|
|
ipack p;
|
|
int verb = 0;
|
|
|
|
init_ipack(&p, IPACKS,write_out_es, 0);
|
|
p.fd = STDOUT_FILENO;
|
|
p.data = (void *)&p;
|
|
|
|
if (fdin != STDIN_FILENO) verb = 1;
|
|
|
|
ts2es_opt(fdin, pidv, &p, verb);
|
|
}
|
|
|
|
|
|
void change_aspect(int fdin, int fdout, int aspect)
|
|
{
|
|
ps_packet ps;
|
|
pes_packet pes;
|
|
int neof,i;
|
|
|
|
do {
|
|
init_ps(&ps);
|
|
neof = read_ps(fdin,&ps);
|
|
write_ps(fdout,&ps);
|
|
for (i = 0; i < ps.npes; i++){
|
|
uint8_t *buf;
|
|
int c = 0;
|
|
int l;
|
|
|
|
init_pes(&pes);
|
|
read_pes(fdin, &pes);
|
|
|
|
buf = pes.pes_pckt_data;
|
|
|
|
switch (pes.stream_id){
|
|
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
|
l=pes.length;
|
|
break;
|
|
default:
|
|
l = 0;
|
|
break;
|
|
}
|
|
while ( c < l - 6){
|
|
if (buf[c] == 0x00 &&
|
|
buf[c+1] == 0x00 &&
|
|
buf[c+2] == 0x01 &&
|
|
buf[c+3] == 0xB3) {
|
|
c += 4;
|
|
buf[c+3] &= 0x0f;
|
|
buf[c+3] |= aspect;
|
|
}
|
|
else c++;
|
|
}
|
|
write_pes(fdout,&pes);
|
|
}
|
|
} while( neof > 0 );
|
|
}
|