Mailing List archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Alpha Version of an avpes2mpeg Conversion Utility



    [ The following text is in the "UNKNOWN-8BIT" character set. ]
    [ Your display is set for the "ISO-8859-1" character set.  ]
    [ Some characters may be displayed incorrectly. ]

Hi,

Here is a little utility which converts an avpes stream to mpeg2 system
stream. I am releasing it under GPL, any improvements are welcome.
It is a bit too complex (and suffers from a lot of misunderstanding of
mpeg2 system streams on my side), but the results seem to be fairly ok.

Usage:
avpes2mpeg <avpesfile >mpegfile
or
avpes2mpeg start_hr start_min start_sec <avpesfile >mpegfile
(The starttime is the video time, taken from GOPs)

Since it doesn´t use seeks, you can also use it to directly record mpeg2:
avpes2mpeg </dev/video >mpegfile

Todo:
- Clean it up
- Support for cutting (at least at GOPs, with a little help from external
programs maybe even at picture level)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

unsigned int start_hr, start_min, start_sec;
unsigned int valid=0;

#define DEBUG_AVPES_SYNC 0x0001
#define DEBUG_APES_SYNC 0x0002
#define DEBUG_VPES_SYNC 0x0004
#define DEBUG_AUDIO_SYNC 0x0008
#define DEBUG_VIDEO_SYNC 0x0010
#define DEBUG_SYNC 0x00ff

#define DEBUG_AVPES_HEADER 0x0100
#define DEBUG_APES_HEADER 0x0200
#define DEBUG_VPES_HEADER 0x0400
#define DEBUG_AUDIO_HEADER 0x0800
#define DEBUG_VIDEO_HEADER 0x1000

#define DEBUG_ALL -1

int debug=DEBUG_SYNC;

#define TS_CLOCK 90000	/* Timestamp Clock Frequency */

static unsigned int bitrate_index [3][16] =
    {{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0},
     {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0},
     {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0}};

static double frequency_index [4] = {44.1, 48, 32, 0};
static unsigned int slots [4] = {12, 144, 0, 0};
static unsigned int samples [4] = {384, 1152, 0, 0};
              
#define BUFFSIZE 2048

unsigned long long init_time;

struct buffer {
	char buffer[BUFFSIZE];
	int wpos;
	int init;
	int acu_time;
	int pesh_pos;
	unsigned long long time;
	unsigned long long written;
} video,audio;

unsigned long long
pts2time(unsigned char *pts)
{
	unsigned long long time;
	unsigned char t0;
	unsigned short t1,t2;
	t0=pts[0];
	t1=(pts[1] << 8) | pts[2];
	t2=(pts[3] << 8) | pts[4];
	time=(t0 >> 1) & 0x7;
	time <<= 15;
	time |= t1 >> 1;
	time <<= 15;
	time |= t2 >> 1;
	return time;
}

void
time2pts(unsigned long long time, char *pts)
{
	unsigned char t0;
	unsigned short t1,t2;
	t2=(time << 1) | 1;
	time >>= 15;
	t1=(time << 1) | 1;
 	time >>= 15;
 	t0=((time & 0x7)<< 1) | 0x21;
	pts[0]=t0;
	pts[1]=t1 >> 8;
	pts[2]=t1 & 0xff;
	pts[3]=t2 >> 8;
	pts[4]=t2 & 0xff;
}

void
time2hhmmssff(unsigned long long time)
{
	unsigned long frm_time=time/(TS_CLOCK/25);
	int hr,min,sec,frm;
	frm=frm_time % 25;
	frm_time/=25;
	sec=frm_time % 60;
	frm_time/=60;
	min=frm_time % 60;
	frm_time/=60;
	hr=frm_time;
	fprintf(stderr,"%02d:%02d:%02d.%02d\n", hr, min, sec, frm);
}

void
packet_header_write(struct buffer *buff)
{
	char *header=buff->buffer+buff->wpos;
	char *ptr=header;

	*ptr++=0;
	*ptr++=0;
	*ptr++=1;
	*ptr++=0xba;

	*ptr++=0;
	*ptr++=0;
	*ptr++=0;
	*ptr++=0;
	*ptr++=0;
	*ptr++=0;

	*ptr++=0x01;
	*ptr++=0x89;
	*ptr++=0xc3;
	*ptr++=0xf8;
	
	buff->wpos+=ptr-header;
}

void
packet_header_update(struct buffer *buff)
{
#if 1
	static unsigned long scr;
#else
	static unsigned long scr=1;
#endif
	unsigned long tmp1;
	unsigned short tmp2;
	
	char *header=buff->buffer+buff->wpos;
	char *ptr=header;

	ptr+=4;		/* skip 0x00 0x00 0x01 0xba */
/* Clock Ref 3 + 4 + 4 + 4 + 3 + 4 + 1
   10XX 0XXX  XXXX XXXX  XXXX 0XXX  XXXX X0YY YYYY YYYY0  
*/

	tmp1=0x40000000 |
	     ((scr >> 3) & 0x30000000) | 
	     ((scr >> 4) & 0x03fff800) |
	     ((scr >> 5) & 0x000003ff);
	tmp2=(scr << 11) & 0xf800;
#if 1
	scr+=146;
#else
	scr <<=1;
#endif
	*ptr++=tmp1 >> 24;
	*ptr++=tmp1 >> 16;
	*ptr++=tmp1 >> 8;
	*ptr++=tmp1;
	*ptr++=tmp2 >> 8;
	*ptr++=tmp2;

}

void
system_header_write(struct buffer *buff)
{
	char header[]={ 0x00,0x00,0x01,0xbb,0x00,0x12,0x80,0xc4,0xe1,0x04,0xe1,
		      0x7f,0xe0,0xe0,0xe8,0xc0,0xc0,0x20,0xbd,0xe0,0x3a,0xbf,0xe0,0x02};
	memcpy(buff->buffer+buff->wpos, header, sizeof(header));
	buff->wpos+=sizeof(header);
}


void
pes_header_write(int chan, struct buffer *buff, int len,
		 unsigned long long pt, unsigned long long dt)
{
	char *ptri,*ptr;
	char pts_buf[5];
	
#if 0
	fprintf(stderr,"pes_header_write %x, %Ld, %Ld\n", chan, pt, dt);
#endif
	ptr=buff->buffer+buff->wpos;
	buff->pesh_pos=buff->wpos;
	ptri=ptr;

	*ptr++=0;
	*ptr++=0;
	*ptr++=1;
	*ptr++=chan;
	*ptr++=len >> 8;
	*ptr++=len;
	*ptr++=0x81;	
	*ptr++=((pt != -1)?0x80:0x00)|((dt != -1)?0x40:0x00); 
	*ptr++=((pt != -1)?5:0)+((dt != -1)?5:0); 
	if (pt != -1) {
		time2pts(pt, pts_buf);
		*ptr++=pts_buf[0];
		*ptr++=pts_buf[1];
		*ptr++=pts_buf[2];
		*ptr++=pts_buf[3];
		*ptr++=pts_buf[4];
	}
	if (dt != -1) {
		time2pts(dt, pts_buf);
		*ptr++=pts_buf[0];
		*ptr++=pts_buf[1];
		*ptr++=pts_buf[2];
		*ptr++=pts_buf[3];
		*ptr++=pts_buf[4];
	}
	buff->wpos+=ptr-ptri;
}

void
pes_header_update(struct buffer *buff, int len)
{
	char *ptr;

	if (! buff->pesh_pos)
		return;	
	ptr=buff->buffer+buff->pesh_pos;
	
	ptr[4]=len >> 8;
	ptr[5]=len;
}

unsigned long long pad_written;

void
pes_pad(struct buffer *buff, int len)
{
	char *ptr,*ptri;

	ptr=buff->buffer+buff->wpos;
	ptri=ptr;
	len-=6;
	pad_written+=len;
	*ptr++=0;
	*ptr++=0;
	*ptr++=1;
	*ptr++=0xbe;
	*ptr++=len >> 8;
	*ptr++=len;
	while (len-- > 0) {
		*ptr++=0xff;
	}
	buff->wpos+=ptr-ptri;
}

void
buffer_write(struct buffer *buff)
{
	buff->wpos=0;
	packet_header_update(buff);
	write(1, buff->buffer, BUFFSIZE);
}


void
new_acu(int chan, unsigned long long pt, unsigned long long dt, int force)
{
	struct buffer *buff;
	int diff;
	
#if 0
	fprintf(stderr, "ACU %x %Ld %Ld %d\n", chan, pt, dt, force);
#endif
#if 0
	if (! init_time)
		init_time=pt-1;
#endif
#if 0
	fprintf(stderr, "ACU: time %d\n", pt-init_time);
#endif
	if (chan >=0xe0)
		buff=&video;
	else {
		buff=&audio;
		if ((!buff->pesh_pos && !init_time) || pt < init_time) {
			return ;
		}
	}
	diff=BUFFSIZE-buff->wpos;
	if ((diff < 256 || !buff->pesh_pos || force) && diff > 8) {
		if (buff->pesh_pos) {
			pes_header_update(buff, buff->wpos-buff->pesh_pos-6);
			pes_pad(buff, diff);
			buffer_write(buff);
		} 						
		if (force && !init_time)
			init_time=pt-1;
		buff->acu_time=0;
		packet_header_write(buff);
		if (pt != -1) {
			if (pt <= init_time)
				pt=1;
			else
				pt-=init_time;
		}
		if (dt != -1) {
			if (dt <= init_time)
				dt=1;
			else
				dt-=init_time;
		}
		pes_header_write(chan, buff, BUFFSIZE-buff->wpos-6, pt, dt);
	}
#if 0
	else
		fprintf(stderr, "SKIP_ACU %x\n", chan); 
#endif
}


void
do_write(int chan, char *addr, int len)
{
	int free,to_write;
	struct buffer *buff;
	
	if (chan >=0xe0)
		buff=&video;
	else
		buff=&audio;

	if (! buff->pesh_pos) {
#if 0
		fprintf(stderr, "Write skip %x\n", chan);
#endif
		return;
	}
	buff->written+=len;	
	while (len) {
		free=BUFFSIZE-buff->wpos;
		to_write=len;
		if (to_write > free)
			to_write=free;
		memcpy(buff->buffer+buff->wpos, addr, to_write);
		buff->wpos+=to_write;
		len-=to_write;
		addr+=to_write;		
		if (buff->wpos == BUFFSIZE) {
			buffer_write(buff);
			packet_header_write(buff);
			pes_header_write(chan, buff, BUFFSIZE-buff->wpos-6, -1, -1);
		}
	}
		
}

void
mpeg2_video_write(unsigned char *buffer, int len, unsigned long pts)
{
	static int state=0;
	int i;
	
#if 0
	fprintf(stderr,"VIDEO: buffer %x len %d\n", buffer, len);
#endif
	while (len) {
		switch(state) {
		case 0:
			len--;
			if (*buffer++ == 0)
				state=1;
			break;
		case 1:
			len--;
			if (*buffer++ == 0)
				state=2;
			else
				state=0;
			break;
		case 2:
			len--;
			if (*buffer++ == 1)
				state=3;
			else
				state=0;
			break;
		case 3:
			len--;
			if (*buffer++ == 0xb3 && pts) {
				char header[4]={0x00,0x00,0x01,0xb3};
				if (valid) {
					int goppos=-1;

					for (i = 0 ; i < len-3 ; i++) {
						if (buffer[i] == 0x0 && buffer[i+1] == 0x0 && buffer[i+2] == 0x1 && buffer[i+3] == 0xb8) {
							goppos=i;
							break;
						}
					}
					if (goppos != -1 ) {
						unsigned long x;
						unsigned int hr,min,sec,frame;
        	      				x=(buffer[goppos+4] << 24) | (buffer[goppos+5] << 16) |
						  (buffer[goppos+6] << 8) | buffer[goppos+7];
						hr=(x >> 26) & 0x1f;
						min=(x >> 20) & 0x3f;
						sec=(x >> 13) & 0x3f;
						frame=(x >> 7) & 0x3f; 					
						fprintf(stderr,"%02d:%02d:%02d.%02d\n",hr,min,sec,frame); 
						if (hr != start_hr || min != start_min || sec != start_sec) {
							state=0;
							break;
						}
						else
							fprintf(stderr, "Start found\n");
					}
					else {
						fprintf(stderr, "no GOP %x %x %x %x\n", buffer[158], buffer[159], buffer[160], buffer[161]);
						state=0;
						break;
					}
				}
				new_acu(0xe0, pts, pts-10800, 1);
				do_write(0xe0, header, 4);		
				state=4;
			}
			else
				state=0;
			break;
		case 4:
			if (pts && len > 7) {
				for (i = 0 ; i < len-4 ; i++) {
					if (buffer[i+0] == 0 && buffer[i+1] == 0 && buffer[i+2] == 1
						&& buffer[i+3] == 0xb3) {
						if (i) {
							do_write(0xe0, buffer, i);
							buffer+=i;
							len-=i;
						}
						new_acu(0xe0, pts, pts-10800, 1);
						break;
					}		
				}
			}
			do_write(0xe0, buffer, len);
			buffer+=len;
			len=0;
			break;
		}
	}
}

void
mpeg2_audio_write(unsigned char *buffer, int len)
{
	static int state=0;
	static int skiph=0,skipb=0,skip=0,plen=0,flen=0;
	int to_go=0;
	static unsigned char header[32];
	
	if (debug & DEBUG_APES_HEADER) {
		fprintf(stderr, "APES: New Block with len %d\n", len);
	}
	while (len) {
		header[state]=*buffer;
		switch(state) {
		case 0:			/* 00 */
			len--;
			if (*buffer++ == 0)
				state=1;
			else {
				if (debug & DEBUG_APES_SYNC) {
					if (! skiph) 
						fprintf(stderr, "APES: Syncing Header... ");
					skiph++;
				}
			}
			break;
		case 1:			/* 00 */
			len--;
			if (*buffer++ == 0)
				state=2;
			else {
				if (debug & DEBUG_APES_SYNC) {
					if (! skiph)
						fprintf(stderr, "APES: Syncing Header... ");
					skiph++;
				}
				state=0;
			}
			break;
		case 2:			/* 01 */
			len--;
			if (*buffer++ == 1)
				state=3;
			else {
				if (debug & DEBUG_APES_SYNC) {
					if (! skiph)
						fprintf(stderr, "APES: Syncing Header... ");
					skiph++;
				}
				state=0;
			}
			break;
		case 3:			/* cx/dx */
			len--;
			if (*buffer >= 0xc0 && *buffer < 0xe0) {
				buffer++;
				state=4;
			}
			else {
				if (debug & DEBUG_APES_SYNC) {
					if (! skiph)
						fprintf(stderr, "APES: Syncing Header... ");
					skiph++;
				}
				buffer++;
				state=0;
			}
			break;
		case 4:			/* lenh */
			if (debug & DEBUG_APES_SYNC) {
				if (skiph) {
					fprintf(stderr, "%d APES bytes skipped\n", skiph);
					skiph=0;
				}
			}
			len--;
			plen=(*buffer++) << 8;
			state=5;
			break;
		case 5:			/* lenl */
			len--;
			plen|=*buffer++;
			skip=8;
			state=6;
			break;
		case 6:		/* 10 PSC PESP DAI CY OOCF */
		case 7:		/* PTSF DTSF ESCRF ESRF DSMTMF ACIF PESCRCF PESHDL */
		case 8:		/* Header Length */
		case 9:		/* PTS */
		case 10:
		case 11:
		case 12:
			len--;
			plen--;
			buffer++;
			state++;			
			break;
		case 13:	/* PTS Ende */
			len--;
			plen--;
			buffer++;
			state++;			
			audio.time=pts2time(header+9);			
			if (debug & DEBUG_APES_HEADER) {
				fprintf(stderr,"APES: Header Magic:0x%02x 0x%02x 0x%02x 0x%02x",
						header[0], header[1], header[2], header[3]);
				fprintf(stderr," len:%d flags:0x%02x 0x%02x hlen:%d pts:%Ld\n",
						(header[4] << 8) | header[5], header[6], header[7],
						header[8], audio.time);
			}
			break;
		case 14:	/* MPEG Audio Header */
			len--;
			plen--;
			if (*buffer++ == 0xff) {
				if (skipb) {
					fprintf(stderr, "Audio: Skip1: Skipped %d bytes\n", skipb);
					skipb=0;
				}
				state++;
			}
			else {
				if (! skipb) {
					fprintf(stderr, "Audio: Skip1 %x != 0xff len %d\n",buffer[-1],plen);
				}
				skipb++;
				if (plen <= 0)
					state=0;
			}
			break;
		case 15:
			len--;
			plen--;
			if ((*buffer++ & 0xf0) == 0xf0)
				state++;
			else {
				fprintf(stderr, "Audio: Skip2\n");
				if (plen <= 0)
					state=0;
				else
					state=14;
			}
			break;
		case 16:
			len--;
			plen--;
			buffer++;
			state++;
			break;
		case 17:	/* MPEG Audio Header End */	
			len--;
			plen--;
			buffer++;
			state++;
			new_acu(0xc0, audio.time, -1, 0);
			do_write(0xc0, header+14, 4); 
			{
			unsigned int sync_word;
			unsigned int layer;
			unsigned int protection;
			unsigned int bit_rate;
			unsigned int frequency;
			unsigned int padding;
			unsigned int mode;
			unsigned int mode_extension;
			unsigned int copyright;
			unsigned int original_copy;
			unsigned int emphasis;
			unsigned int framesize;
			sync_word=(header[14] << 8) | header[15];
			if ((sync_word & 0xfff0) != 0xfff0) {
				fprintf(stderr, "Audio: Out of Sync %x len %d\n",
				sync_word, plen);
			}
			layer=(sync_word >> 1) &3;
			protection=sync_word & 0x1;
			bit_rate=(header[16] >> 4) & 0xf;
			frequency=(header[16] >> 2) & 0x3;
			padding=(header[16] >> 1) & 0x1;
			mode=(header[17] >> 6) & 0x3;
			mode_extension=(header[17] >> 4) & 0x3;
			copyright=(header[17] >> 3) & 0x1;
			original_copy=(header[17] >> 2) & 0x1;
			emphasis=header[17] & 0x3;
			framesize=bitrate_index[3-layer][bit_rate] /
				  frequency_index[frequency] * slots [3-layer];
#if 0
			fprintf(stderr, "framesize %d plen %d len %d\n",
					framesize,plen,len);
#endif
			flen=framesize-4;
			audio.time+=TS_CLOCK*samples[3-layer]/
				(frequency_index[frequency]*1000);
			}
			break;
		case 18:
			to_go=flen;
			if (to_go > len)
				to_go=len;
			do_write(0xc0, buffer, to_go);
			len -= to_go;
			buffer += to_go;
			plen -= to_go;
			flen -= to_go;
			if (! flen)
				state=14;
			if (plen <= 0)
				state=0;
			break;
		}
	}
}

void
avpes_read(unsigned char *buffer, int len)
{
	static int state=0;
	static unsigned char header[32];
	static int gap1,plen;
	static int to_write;
	static int sync=0;
	int curr;
	unsigned long long pts=0;
	
	while (len) {
		header[state]=*buffer;
		switch(state) {
		case 0: /* 0x41 */
			len--;
			if (*buffer++ == 0x41)
				state=1;
			else {
				if (debug & DEBUG_AVPES_SYNC) {
					if (! sync)
						fprintf(stderr, "AVPES: Syncing Header... ");
					sync++;
				}
			}
			break;
		case 1: /* 0x56 */
			len--;
			if (*buffer++ == 0x56)
				state=2;
			else
				state=0;
			break;
		case 2: /* type */
			len--;
			buffer++;
			state=3;
			break;
		case 3: /* sequence */
			len--;
			buffer++;
			state=4;
			break;
		case 4: /* 0x55 */
			len--;
			if (*buffer++ == 0x55)
				state=5;
			else
				state=0;
			break;
		case 5:	/* gap */
		case 6: /* lenhi */
		case 7: /* lenlo */
			len--;
			buffer++;
			state++;
			break;
		case 8:
			if (debug & DEBUG_AVPES_SYNC) {
				if (sync)
					fprintf(stderr, "%d AVPES bytes skipped\n",sync);
				sync=0;
			}
			gap1=(header[5] >> 2) & 0x3;
			plen=header[6]*256+header[7];
			gap1=0;
			if (debug & DEBUG_AVPES_HEADER) {
				fprintf(stderr, "AVPES: Header Magic:0x%02x 0x%02x Type:0x%02x",
						header[0], header[1], header[2]);
				fprintf(stderr, " Sequence:0x%02x Magic:0x%02x Gap:0x%02x Len:%d",
						header[3], header[4], header[5], plen);
				
			}
			state=9;
			break;
		case 9:
			pts=0;
			if (header[5] & 0x10) {
				if (header[2]==2) {
					if (debug & DEBUG_AVPES_HEADER) {
						fprintf(stderr, " NO PTS FOR AUDIO");
					}
					to_write=plen-gap1;
					state=14;
					break;
				}
				state=10;
			} else {
				to_write=plen-gap1;
				state=14;
			} 
			break;
		case 10:	/* pts 1 */
		case 11:	/* pts 2 */
		case 12:	/* pts 3 */
			plen--;
			len--;
			buffer++;
			state++;
			break;
		case 13:	/* pts 4 */
			plen--;
			len--;
			buffer++;
			state++;
			pts=(header[10] << 24) |
			 	(header[11] << 16) |
			    	(header[12] << 8) |
			    	header[13];
			to_write=plen-gap1;
			break;
		case 14:
			if (debug & DEBUG_AVPES_HEADER) {
				if (pts)
					fprintf(stderr," PTS:%Ld\n", pts);
				else
					fprintf(stderr,"\n");				
			}
			curr=to_write;
			if (curr > len)
				curr=len;			
			if (header[2] == 1)
				mpeg2_video_write(buffer, curr, pts);
			if (header[2] == 2)
				mpeg2_audio_write(buffer, curr);
			buffer+=curr;
			len-=curr;
			plen-=curr;
			to_write-=curr;
			if (! to_write)
				state=15;
			break;
		case 15:
			if (len >= gap1) {
				len-=gap1;
				buffer+=gap1;
				gap1=0;
				state=0;
			}
			else {
				gap1-=len;
				buffer+=len;
				len=0;
			}
			break;
		}
	}
}

unsigned long long av_read;
unsigned long last;
int
main(int argc, char **argv)
{
#define BUFFER_SIZE 65536*4
	unsigned char buffer[BUFFER_SIZE];
	int len;
	if (argc > 3) {
		start_hr=atoi(argv[1]);
		start_min=atoi(argv[2]);
		start_sec=atoi(argv[3]);
		valid=1;
	}

	while ( (len=read(0, buffer, BUFFER_SIZE)) ) {
		av_read+=len;
		last+=len;
		if (last >= 10*1024*1024) {
			last=0;
			fprintf(stderr,"AVPES: %Ld MB read VIDEO: %Ld MB AUDIO: %Ld MB PAD: %Ld MB written\n",
				av_read/1024/1024, video.written/1024/1024,
				audio.written/1024/1024, pad_written/1024/1024);
		} 
		avpes_read(buffer, len);		
	}
	return(0);
}



-- 
Martin (martin@smurf.franken.de)


Home | Main Index | Thread Index