/* parsempeg.c 
 *   by Nathan Laredo (laredo@gnu.org)
 *
 * Stradis 4:2:2 MPEG-2 Decoder Driver
 * Copyright (C) 1999 Nathan Laredo <laredo@gnu.org>
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <linux/types.h>
#include <linux/videodev.h>

int videofd;
int f;

static lastwritemode = VID_WRITE_MPEG_VID;

int curpos = 0;
int lastpos = 0;
unsigned char inbuf[8192];
unsigned char fluff[32768];
unsigned char sequence_error[4] = { 0x00, 0x00, 0x01, 0xb4 };

#define dump_current(a) \
	fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x %02x " \
		"%02x %02x %02x %02x %02x %02x %02x %02x\n", \
		a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], \
		a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]);

/* count = 0 will peek at current byte without throwing it */
/* count - peek bytes will be thrown away */
unsigned char *nextbytes(int count, int peek)
{
	unsigned char *tmp = NULL;
	int left;

	if (count > 8192)
		return NULL;
	if (lastpos - curpos > count) {
		tmp = &inbuf[curpos];
		curpos += count - peek;
		if (curpos == lastpos)
			curpos = lastpos = 0;
		return tmp;
	}
	if (curpos > 0 && 8192 - curpos < count) {
		/* hopefully this won't happen TOO much */
		memcpy(inbuf, &inbuf[curpos], lastpos - curpos);
		lastpos -= curpos;
		curpos = 0;
	}
	tmp = &inbuf[curpos];
	curpos += count - peek;

	/* only load n*1024 bytes */
	if ((left = (8192 - lastpos) & 0xffc00))
		lastpos += read(f, &inbuf[lastpos], left);
	if (curpos == lastpos)
		curpos = lastpos = 0;
	if (curpos <= lastpos)
		return tmp;

	/* should VERY RARELY get to this point, maybe near EOF only */
	if (8192 - lastpos > 0)
		lastpos += read(f, &inbuf[lastpos], 8192 - lastpos);
	if (curpos == lastpos)
		curpos = lastpos = 0;
	if (curpos <= lastpos)
		return tmp;
	return NULL;
}

void decodedata(char *buf, int length, int mode)
{

	if (mode != lastwritemode) {
		ioctl(videofd, VIDIOCSWRITEMODE, &mode);
		lastwritemode = mode;
	}
#if 0
	if (mode == VID_WRITE_MPEG_VID)
#endif
	write(videofd, buf, length);
}

void parsempeg(void)
{
	int pid = 0, i, pesbytes;
	int ac3flag = 0, offset;
	unsigned char *tmp, *t2;
	tmp = nextbytes(8, 8);
	if (tmp[0] == 0 && tmp[1] == 0 && tmp[2] == 1 &&
	    tmp[3] == 0xba && (tmp[4] & 0xc0) == 0x40)
		fprintf(stderr, "sending program stream\n");
	else
		goto badfile;
	while(tmp = nextbytes(8, 8)) {
		while (tmp = nextbytes(8, 8))  {
			if (tmp[0] == 0 && tmp[1] == 0 && tmp[2] == 1 &&
				tmp[3] > 0xba)
				break;
			tmp = nextbytes(4, 0);
		}
		pid = tmp[3];
		pesbytes = (tmp[4]<<8) + tmp[5] + 6;
		tmp = nextbytes(pesbytes, 0);
		if ((pid & 0xf0) == 0xe0) {
			decodedata(tmp, pesbytes, VID_WRITE_MPEG_VID);
		} else if ((pid & 0xe0) == 0xc0) {
			decodedata(tmp, pesbytes, VID_WRITE_MPEG_AUD);
		} else if (pid == 0xbd) {
			if (!ac3flag)
				fprintf(stderr, "AC-3\n");
				ac3flag++;
		}
	}
	return;
badfile:
	if (nextbytes(0, 0)) {
		unsigned char *tmp;
		if(tmp = nextbytes(16, 0)) {
			/* DUMP SEMI-USEFUL DEBUGGING INFO */
			fprintf(stderr, "Parse error at location:\n");
			dump_current(tmp);
			if (tmp = nextbytes(16, 0))
				dump_current(tmp);
			if (tmp = nextbytes(16, 0))
				dump_current(tmp);
			if (tmp = nextbytes(16, 0))
				dump_current(tmp);
			if (tmp = nextbytes(16, 0))
				dump_current(tmp);
			if (tmp = nextbytes(16, 0))
				dump_current(tmp);
			if (tmp = nextbytes(16, 0))
				dump_current(tmp);
			if (tmp = nextbytes(16, 0))
				dump_current(tmp);
		}
	}
}

int main(int argc, char **argv)
{
	int d = 0, s = 0;
	char outf[25];
	struct video_play_mode p;
	struct stat st;

	if (argc < 3) {
		fprintf(stderr,
			"usage: %s mpegfile"
			" device [dopal]\n", argv[0]);
		exit(1);
	}
	if ((f = open(argv[1], O_RDONLY)) < 0) {
		perror(argv[1]);
		exit(1);
	}
	d = atoi(argv[2]);
	if (argc > 3)
		s = atoi(argv[3]);

	/* prime the input file buffer */
	lastpos = read(f, inbuf, 8192);
	if (lastpos < 0) {
		perror(argv[1]);
		exit(1);
	}

	memset(fluff, 0x00, 32768);
	memcpy(fluff, sequence_error, 4);
	sprintf(outf, "/dev/video%d", d);
	if ((videofd = open(outf, O_RDWR)) < 0) {
		perror(outf);
		exit(1);
	}
	p.mode = VID_PLAY_VID_OUT_MODE;
	p.p1 = VIDEO_MODE_NTSC;
	if (s == 1)
		p.p1 = VIDEO_MODE_PAL;
	ioctl(videofd, VIDIOCSPLAYMODE, &p);
	parsempeg();
	/* flush buffers */
	decodedata(fluff, 4096, VID_WRITE_MPEG_AUD);
	decodedata(fluff, 32768, VID_WRITE_MPEG_VID);
	p.mode = VID_PLAY_END_MARK;
	ioctl(videofd, VIDIOCSPLAYMODE, &p);
	close(f);
	exit(0);
}				/* main */
