Mailing List archive

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

Program Stream to AV_PES converter



Terry Hardie wrote:
> 
> I have several hardware MPEG2 decoders (Creative PC-DVD Encore, and a
> Matrox RT2000) and tried lots of software decoders. They all say
> unrecognised format. mpg2mpg is very broken (I looked at the source) and I
> can't find any reference to oms anywhere. Can you give me a real
> reference?

I have attached a little program, that converts an MPEG2 Program 
Stream into the AV-PES format. You can pipe the converted program 
stream directly into /dev/video:

  ps2avpes <VTS_01_1.VOB >/dev/video

If you define DEBUG during compile, it outputs some information 
on stderr.


But as far as i have understood your problem, you want to convert 
the other way around, right? This is really tricky, since some 
header information (eg. the SCR in the pack header) has to be 
restored from either external information, or from the PTS in 
the elementary streams, wich is not trivial. I am currently 
working on that problem. I already have a converter that assembles 
the 2 PES streams from the AV-PES stream. Only the pack headers are 
missing now.


Ralph, any chance to read the SCR (or PCR), mux_rate etc. from 
the TS headers and retreive them along with AV-PES stream?

Or, wich would be much easier, does the DVB standard require 
PES packets to have their pack headers in the PES header? 
(PES_extension_flag = 1, pack_header_field_flag = 1)

  Christian.

-- 
http://www.scarabaeus.org/  mailto:scarabaeus@scarabaeus.org
check out   http://betalounge.com/    http://www.linuxtv.org
PGP Fingerprint:B871 358C 3F10 A5ED C41C B1DB B9F9 3C44/2048

--

/*
 * ps2avpes: converts MPEG program streams into the Siemens AV-PES format
 * Copyright (C) 1999 Christian Wolff for convergence integrated media GmbH
 * 
 * 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 scarabaeus@convergence.de, 
 * the project's page is at http://linuxtv.org/dvd/
 */
 
// Version: 2000-03-21 - preliminary - for dvb mailing list

#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

struct AV_PES {
	int v_prebytes;
	unsigned char v_pre[3];
	int v_counter;
	int a_prebytes;
	unsigned char a_pre[3];
	int a_counter;
	unsigned char buffer[2048];
	FILE *devvideo;
} avpes;

int InitAV_PES(struct AV_PES *avpes) 
{
	avpes->v_prebytes=0;
	avpes->a_prebytes=0;
	avpes->v_counter=0;
	avpes->a_counter=0;
	return 0;
}

int FlushAV_PES(void) {
	int postbytes;

	avpes.buffer[0] = 'A';
	avpes.buffer[1] = 'V';
	avpes.buffer[4] = 0x55;

	avpes.v_prebytes &= 0x03; 
	if (avpes.v_prebytes) {
		avpes.buffer[2] = 0x01;
		avpes.buffer[3] = (avpes.v_counter++) & 0xFF;
  	memcpy(&avpes.buffer[8], avpes.v_pre, avpes.v_prebytes);
	  postbytes = 0;
	  memset(&avpes.buffer[8 + avpes.v_prebytes], 0, postbytes);
		avpes.buffer[5] = ((postbytes << 2) & 0x0C) | (avpes.v_prebytes & 0x03);
		avpes.buffer[6] = 0x00;
		avpes.buffer[7] = avpes.v_prebytes;
		fwrite(avpes.buffer, 12, 1, stdout);
#ifdef DEBUG
		fprintf(stderr, "V %d-%d ", avpes.v_prebytes, postbytes);
#endif
	}
	
	avpes.a_prebytes &= 0x03; 
	if (avpes.a_prebytes) {
		avpes.buffer[2] = 0x02;
		avpes.buffer[3] = (avpes.a_counter++) & 0xFF;
  	memcpy(&avpes.buffer[8], avpes.a_pre, avpes.a_prebytes);
	  postbytes = 0;
	  memset(&avpes.buffer[8 + avpes.a_prebytes], 0, postbytes);
		avpes.buffer[5] = ((postbytes << 2) & 0x0C) | (avpes.a_prebytes & 0x03);
		avpes.buffer[6] = 0x00;
		avpes.buffer[7] = avpes.a_prebytes;
		fwrite(avpes.buffer, 12, 1, stdout);
#ifdef DEBUG
		fprintf(stderr, "A %d-%d ", avpes.a_prebytes, postbytes);
#endif
	}
	return 0;
}

// Converts MPEG2 Program stream into Siemens AV_PES format for DVB card
// Only audio type supported is MPEG
int SendAV_PES(unsigned char *data) 
{
	static unsigned char startcode[]={0x00,0x00,0x01};
	unsigned int len, overlen, medialen;
	int i=0, PTSpresent, startpos, postbytes, extractES, datastart;
	unsigned long int PTS;
	int *prebytes;
	unsigned char *pre;

	if (memcmp(&data[i], startcode, 3)) return 1;

	// initialize parameters
	startpos = 8;
	PTSpresent = 0;
	extractES = 0;
	avpes.buffer[0] = 'A';
	avpes.buffer[1] = 'V';
	avpes.buffer[4] = 0x55;
	avpes.buffer[5] = 0x00;

	if ((data[i+3] & 0xF0) == 0xE0) {  // video packet (0x000001EX)
		avpes.buffer[2] = 0x01;
		avpes.buffer[3] = (avpes.v_counter++) & 0xFF;
		prebytes = &(avpes.v_prebytes);
		pre = avpes.v_pre;
		if ((PTSpresent = (data[i+7]&0x80))) startpos += 4;
		extractES = 1;
	} else if ((data[i+3] & 0xE0) == 0xC0) {  // audio packet (0x000001CX or DX)
		avpes.buffer[2] = 0x02;
		avpes.buffer[3] = (avpes.a_counter++) & 0xFF;
		prebytes = &(avpes.a_prebytes);
		pre = avpes.a_pre;
	} else {  // wrong startcode, skip
		i += 4;
		return 1;
	}
	
	// calculate length of new packet
	datastart = i;
	overlen = ((data[i+4] & 0xFF) << 8) | (data[i+5] & 0xFF);  // PES packet length
	if (extractES) {
		overlen -= (3 + data[i+8]);  // minus PES header = payload length
		datastart += (9 + data[i+8]);
	} else {
		overlen += 6;  // plus PES header = payload length
	}

	while (overlen) {

	  // append carry bytes from last block
		*prebytes &= 0x03;
		len = overlen + *prebytes;
	  if (*prebytes) {
	  	memcpy(&avpes.buffer[startpos], pre, *prebytes);
			avpes.buffer[5] |= (*prebytes & 0x03);
			startpos += *prebytes;
		}

		// insert PTS
		if (PTSpresent) {
			PTS = (data[i+9]>>1)&0x03ULL;
			PTS = (PTS << 8) | (data[i + 10] & 0xFFULL);
			PTS = (PTS << 7) | ((data[i + 11] >> 1) & 0x7FULL);
			PTS = (PTS << 8) | (data[i + 12] & 0xFFULL);
			PTS = (PTS << 7) | ((data[i + 13] >> 1) & 0x7FULL);
			avpes.buffer[5] |= 0x10;
			avpes.buffer[8] = (PTS >> 24) & 0xFF;
			avpes.buffer[9] = (PTS >> 16) & 0xFF;
			avpes.buffer[10] = (PTS >> 8) & 0xFF;
			avpes.buffer[11] = PTS & 0xFF;
			len += 4;
		}

		if (len > 6136) {
			overlen -= (6136 - *prebytes - (PTSpresent ? 4 : 0));
			if (overlen < 0) overlen = 0;
			len = 6136;
			if (overlen < 4) {
				len += overlen;
				overlen = 0;
			}
		} else {
			overlen = 0;
		}

		// store media data
		medialen = len - *prebytes - (PTSpresent ? 4 : 0);

		// cut off carry bytes and store
		postbytes = (len & 0x03);
		if (postbytes) {
			len -= postbytes;
			medialen -= postbytes;
	  	memcpy(pre, &data[datastart + medialen], postbytes);
			avpes.buffer[5] |= ((postbytes << 2) & 0x0C);
		}

		// remind carry bytes
		*prebytes = postbytes;

		// fill in the overall length
		avpes.buffer[6] = (len >> 8) & 0xFF;
		avpes.buffer[7] = len & 0xFF;

		//fwrite(avpes.buffer, len + 8, 1, stdout);
		fwrite(avpes.buffer, startpos, 1, stdout);
		fwrite(&data[datastart], medialen, 1, stdout);
		datastart += medialen;

		PTSpresent = 0;
		startpos = 8;
		avpes.buffer[5] = 0x00;
	}
	return 0;
}

int main(void) 
{
	int i,packetsize;
	static unsigned char startcode[]={0x00,0x00,0x01};
	unsigned char data[65542];  // 64k + 6 byte
	

	InitAV_PES(&avpes);
	while (!feof(stdin) && !ferror(stdin)) {
		i=0;
		while ((i < 3) && ! feof(stdin) && ! ferror(stdin)) {
			if (fread(&data[i], 1, 1, stdin) <= 0) {
#ifdef DEBUG
				fprintf(stderr, "[%d]", i);
#endif
				continue;
			}
			if (data[i]==startcode[i]) i++;
			else i = 0;
		}
		if (i < 3) {
#ifdef DEBUG
			fprintf(stderr, "[S]");
#endif
			continue;
		}

		// Read stream ID
		if (fread(&data[i++], 1, 1, stdin) <= 0) {
#ifdef DEBUG
			fprintf(stderr, "[ID]");
#endif
			continue;
		}

		// pack header or stream packet?
		if (data[3] == 0xBA) {  // pack header
			if (fread(&data[i], 10, 1, stdin)<=0) {
#ifdef DEBUG
				fprintf(stderr, "[h]");
#endif
				continue;
			}
			i+=10;
			if (data[13] & 0x07) {
				if (fread(&data[i], data[13] & 0x07, 1, stdin) <= 0 ) {
#ifdef DEBUG
					fprintf(stderr, "[d%d]", data[13] & 0x07);
#endif
					continue;
				}
				i += (data[13] & 0x07);
				// TODO: SavePackHeader(data, i);
			}
		} else if (data[3] >= 0xBB) {  // stream packet
			if (fread(&data[i], 2, 1, stdin) <= 0) {
#ifdef DEBUG
				fprintf(stderr, "[H]");
#endif
				continue; 
			}
			i += 2;
			packetsize = ((data[4] & 0x00FF) << 8) | (data[5] & 0x00FF);
			if (fread(&data[i], packetsize, 1, stdin) <= 0) {
#ifdef DEBUG
				fprintf(stderr, "[D%d]", packetsize);
#endif
				continue;
			}
			i += packetsize;
			if ((data[3] >= 0xC0) && (data[3] <= 0xEF)) {
#ifdef DEBUG
				if ((data[3] & 0xF0) == 0xE0) {  // video
					fprintf(stderr, "v");
				} else if ((data[3] & 0xF0) == 0xC0) {  // mpeg audio
					fprintf(stderr, "m");
				} else if ((data[3] & 0xF0) == 0xD0) {  // mpeg audio extension
					fprintf(stderr, "M");
				}
#endif
				// TODO: optionally, save the audio pack header and insert into PES header
				if (SendAV_PES(data)) {
#ifdef DEBUG
					fprintf(stderr, "[-]");
#endif
				}
			} else if (data[3] == 0xBB) {
#ifdef DEBUG
				fprintf(stderr, "s");
#endif
			} else {
#ifdef DEBUG
				fprintf(stderr, "S%02X", data[3]);
#endif
			}
		}
	}
	FlushAV_PES();
#ifdef DEBUG
	fprintf(stderr, "\n");
#endif
	return 0;
}


Home | Main Index | Thread Index