[Mplayer-cvslog] CVS: main/libmp1e/systems libsystems.h,NONE,1.1 mpeg.h,NONE,1.1 mpeg1.c,NONE,1.1 mpeg2.c,NONE,1.1 output.h,NONE,1.1 rte_output.c,NONE,1.1 systems.c,NONE,1.1 systems.h,NONE,1.1 vcd.c,NONE,1.1

David Holm mswitch at mplayer.dev.hu
Wed Dec 5 00:57:36 CET 2001


Update of /cvsroot/mplayer/main/libmp1e/systems
In directory mplayer:/var/tmp.root/cvs-serv18658

Added Files:
	libsystems.h mpeg.h mpeg1.c mpeg2.c output.h rte_output.c 
	systems.c systems.h vcd.c 
Log Message:


--- NEW FILE ---
/*
 *  MPEG-1 Real Time Encoder
 *
 *  Copyright (C) 1999-2000 Michael H. Schimek
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  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.
 */

/* $Id: libsystems.h,v 1.1 2001/12/04 23:57:32 mswitch Exp $ */

#ifndef LIBSYSTEMS_H
#define LIBSYSTEMS_H

#include "../common/fifo.h"

typedef struct multiplexer multiplexer; /* opaque */

extern long long		bytes_out;

extern multiplexer *		mux_alloc(void *user_data);
extern void			mux_free(multiplexer *mux);
extern fifo *			mux_add_input_stream(multiplexer *mux,
					int stream_id, char *name,
					int max_size, int buffers,
					double frame_rate, int bit_rate);

extern void *			stream_sink(void *mux);
extern void *			mpeg1_system_mux(void *mux);
extern void *			mpeg2_program_stream_mux(void *mux);
extern void *			elementary_stream_bypass(void *mux);
extern void *			vcd_system_mux(void *mux);

extern double			get_idle(void);

extern bool			init_output_stdout(void);
extern void *                   output_thread(void * unused);
extern int                      output_init(void);
extern void                     output_end(void);

extern char *			mpeg_header_name(unsigned int code);

#endif

--- NEW FILE ---
/*
 *  MPEG-1 Real Time Encoder
 *
 *  Copyright (C) 1999-2000 Michael H. Schimek
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  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.
 */

/* $Id: mpeg.h,v 1.1 2001/12/04 23:57:32 mswitch Exp $ */

#define PACKET_START_CODE		0x00000100L	// + stream_id
#define ISO_END_CODE			0x000001B9L
#define MPEG_PROGRAM_END_CODE		0x000001B9L
#define PACK_START_CODE			0x000001BAL
#define SYSTEM_HEADER_CODE		0x000001BBL

#define ALL_AUDIO_STREAMS		0xB8
#define ALL_VIDEO_STREAMS		0xB9

#define PROGRAM_STREAM_MAP		0xBC
#define PRIVATE_STREAM_1		0xBD
#define PADDING_STREAM			0xBE
#define PRIVATE_STREAM_2		0xBF
#define AUDIO_STREAM			0xC0		// 0xC0 ... 0xDF
#define VIDEO_STREAM			0xE0		// 0xE0 ... 0xEF
#define ECM_STREAM			0xF0
#define EMM_STREAM			0xF1
#define DSM_CC_STREAM			0xF2
#define ISO_13522_STREAM		0xF3
/* RESERVED_STREAM 0xF4 ... 0xFE */
#define PROGRAM_STREAM_DIRECTORY	0xFF

#define IS_AUDIO_STREAM(stream_id) (((unsigned int)(stream_id) & (~0x1F)) == AUDIO_STREAM)
#define IS_VIDEO_STREAM(stream_id) (((unsigned int)(stream_id) & (~0x0F)) == VIDEO_STREAM)

#define MARKER_SCR			2
#define MARKER_DTS			1
#define MARKER_PTS_ONLY			2
#define MARKER_PTS			3

#define SYSTEM_TICKS			90000

--- NEW FILE ---
/*
 *  MPEG-1 Real Time Encoder
 *
 *  Copyright (C) 1999-2000 Michael H. Schimek
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  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.
 */

/* $Id: mpeg1.c,v 1.1 2001/12/04 23:57:32 mswitch Exp $ */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <limits.h>
#include "../video/mpeg.h"
#include "../video/video.h"
#include "../audio/libaudio.h"
#include "../common/log.h"
#include "../common/fifo.h"
#include "../common/math.h"
#include "../options.h"
#include "mpeg.h"
#include "systems.h"

#define put(p, val, bytes)						\
do {									\
	unsigned int v = val;						\
									\
	switch (bytes) {						\
	case 4:								\
		*((unsigned int *)(p)) = swab32(v);			\
		break;							\
	case 3:	(p)[bytes - 3] = v >> 16;				\
	case 2:	(p)[bytes - 2] = v >> 8;				\
	case 1:	(p)[bytes - 1] = v;					\
	}								\
} while (0)

static inline unsigned char *
time_stamp(unsigned char *p, int marker, double system_time)
{
	long long ts = llroundn(system_time);

	p[0] = (marker << 4) + ((ts >> 29) & 0xE) + 1;
	p[1] = ((long) ts >> 22);
	p[2] = ((long) ts >> 14) | 1;
	p[3] = ((long) ts >> 7);
	p[4] = (long) ts * 2 + 1;
	/*
	 *  marker [4], TS [32..30], marker_bit,
	 *  TS [29..15], marker_bit,
	 *  TS [14..0], marker_bit
	 */

	return p + 5;
}

#define PACK_HEADER_SIZE 12
#define SCR_offset 8

static inline unsigned char *
pack_header(unsigned char *p, double scr, double system_rate)
{
	int mux_rate = ((unsigned int) ceil(system_rate + 49)) / 50;

	// assert(mux_rate > 0);

	put(p, PACK_START_CODE, 4);
	time_stamp(p + 4, MARKER_SCR, scr);
	put(p + 9, (mux_rate >> 15) | 0x80, 1);
	put(p + 10, mux_rate >> 7, 1);
	put(p + 11, (mux_rate << 1) | 1, 1);
	/*
	 *  pack_start_code [32], system_clock_reference [40],
	 *  marker_bit, program_mux_rate, marker_bit
	 */

	return p + 12;
}

#define SYSTEM_HEADER_SIZE(nstr) (12 + (nstr) * 3)

static inline unsigned char *
system_header(multiplexer *mux, unsigned char *p, double system_rate_bound)
{
	int mux_rate_bound = ((unsigned int) ceil(system_rate_bound + 49)) / 50;
	int audio_bound = 0;
	int video_bound = 0;
	unsigned char *ph;
	stream *str;

	// assert(mux_rate_bound > 0);

	ph = p;
	p += 12;

	for_all_nodes (str, &mux->streams, fifo.node) {
		put(p, str->stream_id, 1);

		if (IS_VIDEO_STREAM(str->stream_id)) {
			put(p + 1, (0x3 << 14) + (1 << 13) + ((40960 + 1023) >> 10), 2);
			video_bound++;
		} else if (IS_AUDIO_STREAM(str->stream_id)) {
			put(p + 1, (0x3 << 14) + (0 << 13) + ((4096 + 127) >> 7), 2);
			audio_bound++;
		} else if (str->stream_id == PRIVATE_STREAM_1) {
			put(p + 1, (0x3 << 14) + (0 << 13) + ((1504 + 127) >> 7), 2);
		} else if (0 < (1 << (13 + 7))) {
			put(p + 1, (0x3 << 14) + (0 << 13) + ((0 + 127) >> 7), 2);
		} else
			put(p + 1, (0x3 << 14) + (1 << 13) + ((0 + 1023) >> 10), 2);
			/* '11', buffer_bound_scale, buffer_size_bound [13] */

		p += 3;
	}

	put(ph, SYSTEM_HEADER_CODE, 4);
	put(ph + 4, p - ph - 6, 2);
	put(ph + 6, (mux_rate_bound >> 15) | 0x80, 1);
	put(ph + 7, mux_rate_bound >> 7, 1);
	put(ph + 8, (mux_rate_bound << 1) | 1, 1);
	put(ph + 9, (audio_bound << 2) + (0 << 1) + (0 << 0), 1);
	put(ph + 10, (0 << 7) + (0 << 6) + (0x1 << 5) + (video_bound << 0), 1);
	put(ph + 11, 0xFF, 1);
	/*
	 *  system_header_start_code [32], header_length [16],
	 *  marker_bit, rate_bound [22], marker_bit,
	 *  audio_bound [6], fixed_flag, CSPS_flag,
	 *  system_audio_lock_flag, system_video_lock_flag,
	 *  marker_bit, video_bound [5], reserved_byte [8]
	 */

	return p;
}

#define PACKET_HEADER_SIZE 16

static inline unsigned char *
packet_header(unsigned char *p, stream *str)
{
	put(p, PACKET_START_CODE + str->stream_id, 4);
	put(p + 4, 0xFFFFFFFF, 4);
	put(p + 8, 0xFFFFFFFF, 4);
	put(p + 12, 0xFFFFFF0F, 4);

	if (str->stream_id != PRIVATE_STREAM_1)
		return p + PACKET_HEADER_SIZE;
	else {
		/* TS alignment not applicable (how?) */
		put(p + 16, 0x10, 1); // data_identifier: EBU data

		return p + PACKET_HEADER_SIZE + 1;
	}
}

#define CDLOG 0

#if CDLOG
static FILE *cdlog;
#endif

#define Rvid (1.0 / 1024)
#define Raud (0.0)

static inline bool
next_access_unit(stream *str, double *ppts, unsigned char *ph)
{
	buffer *buf;

	str->buf = buf = wait_full_buffer(&str->cons);

	str->ptr  = buf->data;
	str->left = buf->used;

	if (buf->used <= 0) {
		str->left = 0; /* XXX */
		return FALSE;
	}

	if (!IS_AUDIO_STREAM(str->stream_id))
		str->eff_bit_rate +=
			((buf->used * 8 * str->frame_rate)
			 - str->eff_bit_rate) * Rvid;

#if CDLOG
	if (IS_VIDEO_STREAM(str->stream_id)) {
		double stime = str->cap_t0 +
			(str->frame_count + buf->offset - 1) / str->frame_rate;

		fprintf(cdlog, "%02x: I%15.10f S%15.10f %+15.10f\n",
			str->stream_id, buf->time, stime, buf->time - stime);

		str->frame_count++;
	} else if (IS_AUDIO_STREAM(str->stream_id)) {
		double stime = str->cap_t0 + str->frame_count / str->frame_rate;

		fprintf(cdlog, "%02x: I%15.10f S%15.10f %+15.10f\n",
			str->stream_id, buf->time, stime, buf->time - stime);

		str->frame_count++;
	}
#endif

	if (ph) {
		/*
		 *  Add DTS/PTS of first access unit
		 *  commencing in packet.
		 */
		if (IS_VIDEO_STREAM(str->stream_id)) {
			/*
			 *  d/o  IBBPBBIBBPBBP
			 *  c/o  IPBBIBBPBBPBB
			 *  DTS  0123456789ABC
			 *  PTS  1423756A89DBC
			 */
			switch (buf->type) {
			case I_TYPE:
			case P_TYPE:
				*ppts = str->dts + str->ticks_per_frame * buf->offset; // reorder delay
				time_stamp(ph +  6, MARKER_PTS, *ppts);
				time_stamp(ph + 11, MARKER_DTS, str->dts);
				break;
			
			case B_TYPE:
				*ppts = str->dts; // delay always 0
				time_stamp(ph +  6, MARKER_PTS, *ppts);
				time_stamp(ph + 11, MARKER_DTS, str->dts);
				break;

			default:
				; /* no time stamp */
			}
		} else {
			*ppts = str->dts + str->pts_offset;
			time_stamp(ph + 11, MARKER_PTS_ONLY, *ppts);
		}
	}


	str->ticks_per_byte = str->ticks_per_frame / str->left;

	return TRUE;
}

#define LARGE_DTS (DBL_MAX / 2.0)

static inline stream *
schedule(multiplexer *mux)
{
	double dtsi_min = LARGE_DTS;
	stream *s, *str;

	str = NULL;

	for_all_nodes (s, &mux->streams, fifo.node) {
		double dtsi = s->dts;

		if (s->buf)
			dtsi += (s->ptr - s->buf->data) * s->ticks_per_byte;

		if (dtsi < dtsi_min) {
			str = s;
			dtsi_min = dtsi;
		}
	}

	return str;
}

void *
mpeg1_system_mux(void *muxp)
{
	multiplexer *mux = muxp;
	unsigned char *p, *ph, *ps, *pl, *px;
	unsigned long long bytes_out = 0;
	unsigned int pack_packet_count = PACKETS_PER_PACK;
	unsigned long long packet_count = 0;
	unsigned long long pack_count = 0;
	double system_rate, system_rate_bound;
	double system_overhead;
	double ticks_per_pack;
	double scr, pts, front_pts = 0.0;
	buffer *buf;
	stream *str;

	pthread_cleanup_push((void (*)(void *)) pthread_rwlock_unlock, (void *) &mux->streams.rwlock);
	assert(pthread_rwlock_rdlock(&mux->streams.rwlock) == 0);

#if CDLOG
	if ((cdlog = fopen("cdlog", "w"))) {
		fprintf(cdlog, "Clock drift\n\n");
	}
#endif

	{
		double preload_delay;
		double video_frame_rate = DBL_MAX;
		int nstreams = 0;
		int preload = 0;
		int bit_rate = 0;

		for_all_nodes (str, &mux->streams, fifo.node) {
			str->buf = NULL;
			bit_rate += str->bit_rate;
			str->eff_bit_rate = str->bit_rate;
			str->ticks_per_frame = (double) SYSTEM_TICKS / str->frame_rate;

			if (IS_VIDEO_STREAM(str->stream_id) && str->frame_rate < video_frame_rate)
				video_frame_rate = frame_rate;

			buf = wait_full_buffer(&str->cons);

			if (buf->used <= 0) // XXX
				FAIL("Premature end of file / error");

			str->cap_t0 = buf->time;
	    		preload += buf->used;

			unget_full_buffer(&str->cons, buf);

			nstreams++;
		}

		buf = mux_output(mux, NULL);

		assert(buf && buf->size >= 512
		           && buf->size <= 32768);

		system_overhead = mux->packet_size / (mux->packet_size
			- (SYSTEM_HEADER_SIZE(nstreams) + PACK_HEADER_SIZE / PACKETS_PER_PACK));

		system_rate = system_rate_bound = bit_rate * system_overhead / 8;

		/* Frames must arrive completely before decoding starts */
		preload_delay = (preload * system_overhead + PACK_HEADER_SIZE)
			/ system_rate_bound * SYSTEM_TICKS;

		scr = SCR_offset / system_rate_bound * SYSTEM_TICKS;
		ticks_per_pack = (mux->packet_size * PACKETS_PER_PACK) / system_rate_bound * SYSTEM_TICKS;

		for_all_nodes (str, &mux->streams, fifo.node) {
			/* Video PTS is delayed by one frame */
			if (!IS_VIDEO_STREAM(str->stream_id)) {
				str->pts_offset = (double) SYSTEM_TICKS / (video_frame_rate * 1.0);
				/* + 0.1 to schedule video frames first */
				str->dts = preload_delay + 0.1;
			} else
				str->dts = preload_delay;
		}
	}

	/* Packet loop */

	for (;;) {
		p = buf->data;
		px = p + buf->size;

		/* Pack header, system header */

		if (pack_packet_count >= PACKETS_PER_PACK) {
			printv(4, "Pack #%lld, scr=%f\n", pack_count++, scr);

			p = pack_header(p, scr, system_rate);
			p = system_header(mux, p, system_rate_bound);

			if (0)
				scr += ticks_per_pack;
			else {
				int bit_rate = 0;

				for_all_nodes (str, &mux->streams, fifo.node)
					bit_rate += str->eff_bit_rate;

				system_rate = bit_rate * system_overhead / 8;
				scr += (mux->packet_size * PACKETS_PER_PACK) / system_rate * SYSTEM_TICKS;
			}

			pack_packet_count = 0;
		}

reschedule:
		if (!(str = schedule(mux)))
			break;

		ph = p;
		ps = p;

		pl = p = packet_header(p, str);

		if (str->stream_id == PRIVATE_STREAM_1)
			px -= (px - p) % 184;

		/* Packet fill loop */

		pts = -1;

		while (p < px) {
			int n;

			if (str->left == 0) {
				if (!next_access_unit(str, &pts, ph)) {
					str->dts = LARGE_DTS * 2.0; // don't schedule stream

					if (pl == p) {
						/* no payload */
						p = ps;
						goto reschedule;
					}

					if (PAD_PACKETS || str->stream_id == PRIVATE_STREAM_1) {
						memset(p, 0, px - p);
						p = px;
					}

					break;
				}

				ph = NULL;
			}

			n = MIN(str->left, px - p);

			memcpy(p, str->ptr, n);

			str->left -= n;

			if (!str->left) {
				send_empty_buffer(&str->cons, str->buf);

				str->buf = NULL;
				str->dts += str->ticks_per_frame;
			}

			p += n;

			str->ptr += n;
		}

		printv(4, "Packet #%lld %s, pts=%f\n",
			packet_count, mpeg_header_name(str->stream_id), pts);

		((unsigned short *) ps)[2] = swab16(p - ps - 6);

		bytes_out += buf->used = p - buf->data;

		buf = mux_output(mux, buf);

		assert(buf && buf->size >= 512
			   && buf->size <= 32768);

		packet_count++;
		pack_packet_count++;

		if (pts > front_pts)
			front_pts = pts;

		if (verbose > 0 && (packet_count & 3) == 0) {
			double system_load = 1.0 - get_idle();
			int min, sec;

			sec = front_pts / SYSTEM_TICKS;
			min = sec / 60;
			sec -= min * 60;

			printv(1, "%d:%02d (%.1f MB), system load %4.1f %%",
				min, sec, bytes_out / (double)(1 << 20),
				100.0 * system_load);

			if (video_frames_dropped > 0)
				printv(1, ", %5.2f %% dropped",
					100.0 * video_frames_dropped / video_frame_count);


#if 0 /* garetxe: num_buffers_queued doesn't exist any longer */
			printv(1, ", fifo v=%5.2f%% a=%5.2f%%",
			       100.0 * num_buffers_queued(video_fifo) / video_fifo->num_buffers,
			       100.0 * num_buffers_queued(audio_fifo) / audio_fifo->num_buffers);
#endif
			printv(1, (verbose > 3) ? "\n" : "  \r");

			fflush(stderr);
		}
	}

	p = buf->data;

	*((unsigned int *) p) = swab32(ISO_END_CODE);

	if (PAD_PACKETS) {
		memset(p + 4, 0, buf->size - 4);
		buf->used = buf->size;
	} else
		buf->used = 4;

	mux_output(mux, buf);

	pthread_cleanup_pop(1);

	return NULL;
}

--- NEW FILE ---
/*
 *  MPEG-1 Real Time Encoder
 *
 *  Copyright (C) 1999-2000 Michael H. Schimek
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  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.
 */

/* $Id: mpeg2.c,v 1.1 2001/12/04 23:57:32 mswitch Exp $ */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <limits.h>
#include "../video/mpeg.h"
#include "../video/video.h"
#include "../audio/libaudio.h"
#include "../common/log.h"
#include "../common/fifo.h"
#include "../common/math.h"
#include "../options.h"
#include "mpeg.h"
#include "systems.h"

#define put(p, val, bytes)						\
do {									\
	unsigned int v = val;						\
									\
	switch (bytes) {						\
	case 4:								\
		*((unsigned int *)(p)) = swab32(v);			\
		break;							\
	case 3:	(p)[bytes - 3] = v >> 16;				\
	case 2:	(p)[bytes - 2] = v >> 8;				\
	case 1:	(p)[bytes - 1] = v;					\
	}								\
} while (0)

static inline unsigned char *
time_stamp(unsigned char *p, int marker, double system_time)
{
	long long ts = llroundn(system_time); // 90 kHz

	p[0] = (marker << 4) + ((ts >> 29) & 0xE) + 1;
	p[1] = ((long) ts >> 22);
	p[2] = ((long) ts >> 14) | 1;
	p[3] = ((long) ts >> 7);
	p[4] = (long) ts * 2 + 1;
	/*
	 *  marker [4], TS [32..30], marker_bit,
	 *  TS [29..15], marker_bit,
	 *  TS [14..0], marker_bit
	 */

	return p + 5;
}

static inline unsigned char *
time_stamp_ext(unsigned char *p, int marker, double system_time)
{
	static const int ext = 0;
	long long ts = llroundn(system_time); // 90 kHz

	p[0] = (marker << 6) + ((ts >> 27) & 0x38) + 4 + (((long) ts >> 28) & 3);
	p[1] = ((long) ts >> 20);
	p[2] = (((long) ts >> 12) & 0xF8) + 4 + (((long) ts >> 13) & 3);
	p[3] = ((long) ts >> 5);
	p[4] = ((long) ts << 3) + 4 + (ext >> 7);
	p[5] = ext * 2 + 1;
	/*
	 *  marker [2], TS [32..30], marker_bit,
	 *  TS [29..15], marker_bit,
	 *  TS [14..0], marker_bit,
	 *  extension [9], marker_bit
	 */

	return p + 6;
}

#define PS_PACK_HEADER_SIZE 14
#define PS_SCR_offset 9

static inline unsigned char *
PS_pack_header(unsigned char *p, double scr, double system_rate)
{
	int mux_rate = ((unsigned int) ceil(system_rate + 49)) / 50;

	// assert(mux_rate > 0);

	put(p, PACK_START_CODE, 4);
	time_stamp_ext(p + 4, 1, scr);
	put(p + 10, mux_rate >> 6, 2);
	put(p + 12, (mux_rate << 2) | 3, 1);
	put(p + 13, (0x1F << 3) | 0 /* psl */, 1);
	/*
	 *  pack_start_code [32], system_clock_reference [48],
	 *  marker_bit, program_mux_rate [22], marker_bit,
	 *  reserved [5], pack_stuffing_length [3]
	 */

	return p + PS_PACK_HEADER_SIZE;
}

#define PS_SYSTEM_HEADER_SIZE(nstr) (12 + (nstr) * 3)

static inline unsigned char *
PS_system_header(multiplexer *mux, unsigned char *p, double system_rate_bound)
{
	int mux_rate_bound = ((unsigned int) ceil(system_rate_bound + 49)) / 50;
	int audio_bound = 0;
	int video_bound = 0;
	unsigned char *ph;
	stream *str;

	// assert(mux_rate_bound > 0);

	ph = p;
	p += 12;

	for_all_nodes (str, &mux->streams, fifo.node) {
		put(p, str->stream_id, 1);

		if (IS_VIDEO_STREAM(str->stream_id)) {
			put(p + 1, (0x3 << 14) + (1 << 13) + ((40960 + 1023) >> 10), 2);
			video_bound++;
		} else if (IS_AUDIO_STREAM(str->stream_id)) {
			put(p + 1, (0x3 << 14) + (0 << 13) + ((4096 + 127) >> 7), 2);
			audio_bound++;
		} else if (str->stream_id == PRIVATE_STREAM_1) {
			put(p + 1, (0x3 << 14) + (0 << 13) + ((1504 + 127) >> 7), 2);
		} else if (0 < (1 << (13 + 7))) {
			put(p + 1, (0x3 << 14) + (0 << 13) + ((0 + 127) >> 7), 2);
		} else
			put(p + 1, (0x3 << 14) + (1 << 13) + ((0 + 1023) >> 10), 2);
			/*
			 *  '11', P-STD_buffer_bound_scale, 
			 *  P-STD_buffer_size_bound [13]
			 */

		p += 3;
	}

	put(ph, SYSTEM_HEADER_CODE, 4);
	put(ph + 4, p - ph - 6, 2);
	put(ph + 6, (mux_rate_bound >> 15) | 0x80, 1);
	put(ph + 7, mux_rate_bound >> 7, 1);
	put(ph + 8, (mux_rate_bound << 1) | 1, 1);
	put(ph + 9, (audio_bound << 2) + (0 << 1) + (0 << 0), 1);
	put(ph + 10, (0 << 7) + (0 << 6) + (0x1 << 5) + (video_bound << 0), 1);
	put(ph + 11, 0xFF, 1);
	/*
	 *  system_header_start_code [32], header_length [16],
	 *  marker_bit, rate_bound [22], marker_bit,
	 *  audio_bound [6], fixed_flag, CSPS_flag,
	 *  system_audio_lock_flag, system_video_lock_flag,
	 *  marker_bit, video_bound [5], reserved_byte [8]
	 */

	return p;
}

#define PES_PACKET_HEADER_SIZE 19

static inline unsigned char *
PES_packet_header(unsigned char *p, stream *str)
{
	put(p, PACKET_START_CODE + str->stream_id, 4);
	put(p + 4, 0, 2); // |->

	if (str->stream_id == PRIVATE_STREAM_1) {
		put(p + 6, (2 << 6) + (0 << 4) + (0 << 3) + (1 << 2) + (0 << 1) + (0 << 0), 1);
		put(p + 7, (0 << 6) + (0 << 5) + (0 << 4) + (0 << 3) + (0 << 2) + (0 << 1) + (0 << 0), 1);
		put(p + 8, 0x00, 1);
		memset(p + 9, 0xFF, 36);
		put(p + 45, 0x10, 1); // data_identifier: EBU data

		return p + 46;
	} else {
		put(p + 6, (2 << 6) + (0 << 4) + (0 << 3) + (0 << 2) + (0 << 1) + (0 << 0), 1);
		put(p + 7, (0 << 6) + (0 << 5) + (0 << 4) + (0 << 3) + (0 << 2) + (0 << 1) + (0 << 0), 1);
		put(p + 8, 0x00FFFFFF, 4);
		put(p + 12, 0xFFFFFFFF, 4);
		put(p + 16, 0xFFFFFF, 3);

		return p + PES_PACKET_HEADER_SIZE;
	}
	/*
	 *  packet_start_code_prefix [24], stream_id [8];
	 *  PES_packet_length [16];
	 *  '10', PES_scrambling_control [2], PES_priority,
	 *  data_alignment_indicator, copyright, original_or_copy;
	 *  PTS_DTS_flags [2], ESCR_flag, ES_rate_flag, DSM_trick_mode_flag,
	 *  additional_copy_info_flag, PES_CRC_flag, PES_extension_flag;
	 *  PES_header_data_length [8]
	 */
}

#define Rvid (1.0 / 1024)
#define Raud (0.0)

static inline bool
next_access_unit(stream *str, double *ppts, unsigned char *ph)
{
	buffer *buf;

	str->buf = buf = wait_full_buffer(&str->cons);

	str->ptr  = buf->data;
	str->left = buf->used;

	if (buf->used <= 0) {
		str->left = 0; /* XXX */
		return FALSE;
	}

	if (!IS_AUDIO_STREAM(str->stream_id))
		str->eff_bit_rate +=
			((buf->used * 8 * str->frame_rate)
			 - str->eff_bit_rate) * Rvid;

	if (ph) {
		if (IS_VIDEO_STREAM(str->stream_id)) {
			switch (buf->type) {
			case I_TYPE:
			case P_TYPE:
				*ppts = str->dts + str->ticks_per_frame * buf->offset;
				ph[7] |= MARKER_PTS << 6;
				time_stamp(ph +  9, MARKER_PTS, *ppts);
				time_stamp(ph + 14, MARKER_DTS, str->dts);
				break;
			
			case B_TYPE:
				*ppts = str->dts;
				ph[7] |= MARKER_PTS << 6;
				time_stamp(ph +  9, MARKER_PTS, *ppts);
				time_stamp(ph + 14, MARKER_DTS, str->dts);
				break;

			default:
				; /* no time stamp */
			}			
		} else {
			*ppts = str->dts;
			ph[7] |= MARKER_PTS_ONLY << 6;
			time_stamp(ph + 9, MARKER_PTS_ONLY, *ppts);
		}
	}

	str->ticks_per_byte = str->ticks_per_frame / str->left;

	return TRUE;
}

#define LARGE_DTS (DBL_MAX / 2.0)

static inline stream *
schedule(multiplexer *mux)
{
	double dtsi_min = LARGE_DTS;
	stream *s, *str;

	str = NULL;

	for_all_nodes (s, &mux->streams, fifo.node) {
		double dtsi = s->dts;

		if (s->buf)
			dtsi += (s->ptr - s->buf->data) * s->ticks_per_byte;

		if (dtsi < dtsi_min) {
			str = s;
			dtsi_min = dtsi;
		}
	}

	return str;
}

void *
mpeg2_program_stream_mux(void *muxp)
{
	multiplexer *mux = muxp;
	unsigned char *p, *ph, *ps, *pl, *px;
	unsigned long long bytes_out = 0;
	unsigned int pack_packet_count = PACKETS_PER_PACK;
	unsigned long long packet_count = 0;
	unsigned long long pack_count = 0;
	double system_rate, system_rate_bound;
	double system_overhead;
	double ticks_per_pack;
	double scr, pts, front_pts = 0.0;
	buffer *buf;
	stream *str;

	pthread_cleanup_push((void (*)(void *)) pthread_rwlock_unlock, (void *) &mux->streams.rwlock);
	assert(pthread_rwlock_rdlock(&mux->streams.rwlock) == 0);

	{
		double preload_delay;
		double video_frame_rate = DBL_MAX;
		int nstreams = 0;
		int preload = 0;
		int bit_rate = 0;

		for_all_nodes (str, &mux->streams, fifo.node) {
			str->buf = NULL;
			bit_rate += str->bit_rate;
			str->eff_bit_rate = str->bit_rate;
			str->ticks_per_frame = (double) SYSTEM_TICKS / str->frame_rate;

			if (IS_VIDEO_STREAM(str->stream_id) && str->frame_rate < video_frame_rate)
				video_frame_rate = frame_rate;

			buf = wait_full_buffer(&str->cons);

			if (buf->used <= 0)
				FAIL("Premature end of file / error");

			str->cap_t0 = buf->time;
	    		preload += buf->used;

			unget_full_buffer(&str->cons, buf);

			nstreams++;
		}

		buf = mux_output(mux, NULL);

		assert(buf && buf->size >= 512
		           && buf->size <= 32768);

		system_overhead = mux->packet_size / (mux->packet_size
			- (PS_SYSTEM_HEADER_SIZE(nstreams) + PS_PACK_HEADER_SIZE / PACKETS_PER_PACK));

		system_rate = system_rate_bound = bit_rate * system_overhead / 8;

		preload_delay = (preload * system_overhead + PS_PACK_HEADER_SIZE)
			/ system_rate_bound * SYSTEM_TICKS;

		scr = PS_SCR_offset / system_rate_bound * SYSTEM_TICKS;
		ticks_per_pack = (mux->packet_size * PACKETS_PER_PACK) / system_rate_bound * SYSTEM_TICKS;

		for_all_nodes (str, &mux->streams, fifo.node) {
			if (!IS_VIDEO_STREAM(str->stream_id)) {
				str->pts_offset = (double) SYSTEM_TICKS / (video_frame_rate * 1.0);
				/* + 0.1 to schedule video frames first */
				str->dts = preload_delay + 0.1;
			} else
				str->dts = preload_delay;
		}
	}

	/* Packet loop */

	for (;;) {
		p = buf->data;
		px = p + buf->size;

		/* Packet header, system header */

		if (pack_packet_count >= PACKETS_PER_PACK) {
			printv(4, "Pack #%lld, scr=%f\n", pack_count++, scr);

			p = PS_pack_header(p, scr, system_rate);
			p = PS_system_header(mux, p, system_rate_bound);

			if (0)
				scr += ticks_per_pack;
			else {
				int bit_rate = 0;

				for_all_nodes (str, &mux->streams, fifo.node)
					bit_rate += str->eff_bit_rate;

				system_rate = bit_rate * system_overhead / 8;
				scr += (mux->packet_size * PACKETS_PER_PACK) / system_rate * SYSTEM_TICKS;
			}

			pack_packet_count = 0;
		}

reschedule:
		if (!(str = schedule(mux)))
			break;

		ph = p;
		ps = p;

		if (str->stream_id == PRIVATE_STREAM_1)
			px -= (px - p) % 184;

		pl = p = PES_packet_header(p, str);

		/* Packet fill loop */

		pts = -1;

		while (p < px) {
			int n;

			if (str->left == 0) {
				if (!next_access_unit(str, &pts, ph)) {
					str->dts = LARGE_DTS * 2.0; // don't schedule stream

					if (pl == p) {
						/* no payload */
						p = ps;
						goto reschedule;
					}

					if (PAD_PACKETS || str->stream_id == PRIVATE_STREAM_1) {
						memset(p, 0, px - p);
						p = px;
					}

					break;
				}

				ph = NULL;
			}

			n = MIN(str->left, px - p);

			memcpy(p, str->ptr, n);

			str->left -= n;

			if (!str->left) {
				send_empty_buffer(&str->cons, str->buf);

				str->buf = NULL;
				str->dts += str->ticks_per_frame;
			}

			p += n;

			str->ptr += n;
		}

		printv(4, "Packet #%lld %s, pts=%f\n",
			packet_count, mpeg_header_name(str->stream_id), pts);

		((unsigned short *) ps)[2] = swab16(p - ps - 6);

		bytes_out += buf->used = p - buf->data;

		buf = mux_output(mux, buf);

		assert(buf && buf->size >= 512
		           && buf->size <= 32768);

		packet_count++;
		pack_packet_count++;

		if (pts > front_pts)
			front_pts = pts;

		if (verbose > 0 && (packet_count & 3) == 0) {
			double system_load = 1.0 - get_idle();
			int min, sec;

			sec = front_pts / SYSTEM_TICKS;
			min = sec / 60;
			sec -= min * 60;

			if (video_frames_dropped > 0)
				printv(1, "%d:%02d (%.1f MB), %.2f %% dropped, system load %.1f %%  %c",
					min, sec, bytes_out / (double)(1 << 20),
					100.0 * video_frames_dropped / video_frame_count,
					100.0 * system_load, (verbose > 3) ? '\n' : '\r');
			else
				printv(1, "%d:%02d (%.1f MB), system load %.1f %%  %c",
					min, sec, bytes_out / (double)(1 << 20),
					100.0 * system_load, (verbose > 3) ? '\n' : '\r');

			fflush(stderr);
		}
	}

	p = buf->data;

	*((unsigned int *) p) = swab32(MPEG_PROGRAM_END_CODE);
	if (PAD_PACKETS) {
		memset(p + 4, 0, buf->size - 4);
		buf->used = buf->size;
	} else
		buf->used = 4;

	mux_output(mux, buf);

	pthread_cleanup_pop(1);

	return NULL;
}

--- NEW FILE ---
/*
 *  MPEG-1 Real Time Encoder
 *
 *  Copyright (C) 1999-2000 Michael H. Schimek
 * 
 *  Modified by Iñaki G.E.
 *
 *  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.
 */

#ifndef __OUTPUT_H__
#define __OUTPUT_H__

extern bool                     output_init(void);
extern void                     output_end(void);

#endif // OUTPUT.H

--- NEW FILE ---
/*
 *  MPEG-1 Real Time Encoder
 *
 *  Copyright (C) 1999-2000 Michael H. Schimek
 *
 *  Modified by Iñaki G.E.
 *
 *  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 <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include "../video/mpeg.h"
#include "../video/video.h"
#include "../audio/mpeg.h"
#include "../options.h"
#include "../common/fifo.h"
#include "../common/log.h"
#include "systems.h"
#include "../rtepriv.h"

buffer *		(* mux_output)(struct multiplexer *mux,
				       buffer *b);

static buffer		mux_buffer;

static buffer *
output(struct multiplexer *mux,
       buffer *mbuf)
{
	rte_context *context = (rte_context*)mux->user_data;

	if (!mbuf)
		return &mux_buffer;

	/* rte_global_context sanity checks */
	if ((!context) || (!context->private) ||
	    (!context->private->encode_callback)) {
		rte_error(NULL, "sanity check failed");
		return mbuf;
	}

	context->private->bytes_out += mbuf->used;

	context->private->
		encode_callback(context,
				mbuf->data,
				mbuf->used,
				context->private->user_data);


	return mbuf; /* any previously entered */
}

int
output_init( void )
{
	if (!init_buffer(&mux_buffer, PACKET_SIZE))
		return FALSE;

	mux_output = output;

	return TRUE;
}

void
output_end ( void )
{
	destroy_buffer(&mux_buffer);
}

--- NEW FILE ---
/*
 *  MPEG-1 Real Time Encoder
 *
 *  Copyright (C) 1999-2000 Michael H. Schimek
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  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.
 */

/* $Id: systems.c,v 1.1 2001/12/04 23:57:32 mswitch Exp $ */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include "../video/mpeg.h"
#include "../audio/libaudio.h"
#include "../video/video.h"
#include "../common/math.h"
#include "../options.h"
#include "mpeg.h"
#include "systems.h"

void
mux_free(multiplexer *mux)
{
	stream *str;

	asserts(mux != NULL);

	while ((str = PARENT(rem_xhead(&mux->streams),
			     stream, fifo.node))) {
		destroy_fifo(&str->fifo);
		free(str);
	}

	destroy_xlist(&mux->streams);

	memset(mux, 0, sizeof(*mux));

	free(mux);
}

multiplexer *
mux_alloc(void *user_data)
{
	multiplexer *mux;

	if (!(mux = calloc(1, sizeof(*mux))))
		return NULL;

	init_xlist(&mux->streams);

	/* a) should be an argument, 
	   b) this func should init the output fifo */
	mux->packet_size = (mux_syn == 4) ? 2324 /* VCD */ : 2048;

	assert(mux->packet_size >= 512 && mux->packet_size <= 32768);

	mux->user_data = user_data;

	return mux;
}

/*
 *  stream_id	VIDEO_STREAM
 *  max_size	frame buffer
 *  buffers	1++
 *  frame_rate	24 Hz
 *  bit_rate	upper bound
 */

fifo *
mux_add_input_stream(multiplexer *mux, int stream_id, char *name,
	int max_size, int buffers, double frame_rate, int bit_rate)
{
	stream *str;
	int new_buffers, r;

	/*
	 *  Returns EBUSY while the mux runs.
	 */
	if ((r = pthread_rwlock_trywrlock(&mux->streams.rwlock)) != 0) {
		errno = r;
		return NULL;
	}

	if (!(str = calloc(1, sizeof(stream)))) {
		return NULL;
	}

	str->stream_id = stream_id;
	str->frame_rate = frame_rate;
	str->bit_rate = bit_rate;

	new_buffers = init_buffered_fifo(&str->fifo, name, buffers, max_size);

	if (new_buffers < buffers) {
		free(str);
		return NULL;
	}

	add_consumer(&str->fifo, &str->cons);

	add_tail((list *) &mux->streams, &str->fifo.node);

	pthread_rwlock_unlock(&mux->streams.rwlock);

	return &str->fifo;
}

void *
stream_sink(void *muxp)
{
	multiplexer *mux = muxp;
	unsigned long long bytes_out = 0;
	int num_streams;
	buffer *buf = NULL;
	stream *str;

	pthread_cleanup_push((void (*)(void *)) pthread_rwlock_unlock, (void *) &mux->streams.rwlock);
	assert(pthread_rwlock_rdlock(&mux->streams.rwlock) == 0);

	for_all_nodes (str, &mux->streams, fifo.node)
		str->left = 1;

	num_streams = list_members((list *) &mux->streams);

	while (num_streams > 0) {
		for_all_nodes (str, &mux->streams, fifo.node)
			if (str->left && (buf = recv_full_buffer(&str->cons)))
				break;

		if (buf->used <= 0) {
			/* XXX EOF/error */
			str->left = 0;
			num_streams--;
			continue;
		}

		bytes_out += buf->used;

		send_empty_buffer(&str->cons, buf);

		if (verbose > 0) {
			double system_load = 1.0 - get_idle();

			printv(1, "%.3f MB >0, %.2f %% dropped, system load %.1f %%  %c",
				bytes_out / (double)(1 << 20),
				video_frame_count ? 100.0 * video_frames_dropped / video_frame_count : 0.0,
				100.0 * system_load, (verbose > 3) ? '\n' : '\r');

			fflush(stderr);
		}
	}

	pthread_cleanup_pop(1);

	return NULL;
}

/*
 *  NB Use MPEG-2 mux to create an elementary VBI stream.
 */

void *
elementary_stream_bypass(void *muxp)
{
	multiplexer *mux = muxp;
	unsigned long long bytes_out = 0;
	unsigned long long frame_count = 0;
	double system_load;
	stream *str;

	pthread_cleanup_push((void (*)(void *)) pthread_rwlock_unlock, (void *) &mux->streams.rwlock);
	assert(pthread_rwlock_rdlock(&mux->streams.rwlock) == 0);

	assert(list_members((list *) &mux->streams) == 1);

	str = PARENT(mux->streams.head, stream, fifo.node);

	for (;;) {
		buffer *buf;

		buf = wait_full_buffer(&str->cons);

		if (buf->used <= 0) // EOF / error
			break;

		frame_count++;
		bytes_out += buf->used;

		buf = mux_output(mux, buf);

		send_empty_buffer(&str->cons, buf);

		if (verbose > 0) {
			int min, sec;
			long long frame;

			system_load = 1.0 - get_idle();

			if (IS_VIDEO_STREAM(str->stream_id)) {
				frame = lroundn(frame_count * frame_rate / str->frame_rate);
				// exclude padding frames

				sec = frame / str->frame_rate;
				frame -= sec * str->frame_rate;
				min = sec / 60;
				sec -= min * 60;

				if (video_frames_dropped > 0)
					printv(1, "%d:%02d.%02d (%.1f MB), %.2f %% dropped, system load %.1f %%  %c",
						min, sec, (int) frame, bytes_out / (double)(1 << 20),
						100.0 * video_frames_dropped / video_frame_count,
						100.0 * system_load, (verbose > 3) ? '\n' : '\r');
				else
					printv(1, "%d:%02d.%02d (%.1f MB), system load %.1f %%    %c",
						min, sec, (int) frame, bytes_out / (double)(1 << 20),
						100.0 * system_load, (verbose > 3) ? '\n' : '\r');
			} else {
				sec = lroundn(frame_count / str->frame_rate);
				min = sec / 60;
				sec -= min * 60;

				printv(1, "%d:%02d (%.3f MB), system load %.1f %%    %c",
					min, sec, bytes_out / (double)(1 << 20),
					100.0 * system_load, (verbose > 3) ? '\n' : '\r');
			}

			fflush(stderr);
		}
	}

	pthread_cleanup_pop(1);

	return NULL;
}

double
get_idle(void)
{
	static double last_uptime = -1, last_idle;
	static double system_idle = 0.0;
	static int upd_idle = 15;
	double uptime, idle, period;
	char buf[80];
	ssize_t r;
	int fd;

	if (--upd_idle > 0)
		return system_idle;

	upd_idle = 15;

	if ((fd = open("/proc/uptime", O_RDONLY)) < 0)
		return system_idle;

	r = read(fd, buf, sizeof(buf) - 1);

	close(fd);

	if (r == -1)
		return system_idle;

	buf[r] = 0;

	sscanf(buf, "%lf %lf", &uptime, &idle);

	period = uptime - last_uptime;

	if (period > 0.5) {
		if (last_uptime >= 0.0)
			system_idle = 0.5 * (system_idle + (idle - last_idle) / period);

		last_idle = idle;
		last_uptime = uptime;
	}

	return system_idle;
}

char *
mpeg_header_name(unsigned int code)
{
	static char buf[40];

	switch (code |= 0x00000100) {
	case PICTURE_START_CODE:
		return "picture_start";
		break;

	case SLICE_START_CODE + 0 ...
	     SLICE_START_CODE + 174:
		sprintf(buf, "slice_start_%d", code & 0xFF);
		return buf;
		break;

	case 0x000001B0:
	case 0x000001B1:
	case 0x000001B6:
		sprintf(buf, "reserved_%02x", code & 0xFF);
		return buf;
		break;

	case USER_DATA_START_CODE:
		return "user_data";
		break;

	case SEQUENCE_HEADER_CODE:
		return "sequence_header";
		break;

	case SEQUENCE_ERROR_CODE:
		return "sequence_error";
		break;

	case EXTENSION_START_CODE:
		return "extension_start";
		break;

	case SEQUENCE_END_CODE:
		return "sequence_end";
		break;

	case GROUP_START_CODE:
		return "group_start";
		break;

	/* system start codes */

	case ISO_END_CODE:
		return "iso_end";
		break;

	case PACK_START_CODE:
		return "pack_start";
		break;

	case SYSTEM_HEADER_CODE:
		return "system_header";
		break;

	case PACKET_START_CODE + PROGRAM_STREAM_MAP:
		return "program_stream_map";
		break;

	case PACKET_START_CODE + PRIVATE_STREAM_1:
		return "private_stream_1";
		break;

	case PACKET_START_CODE + PADDING_STREAM:
		return "padding_stream";
		break;

	case PACKET_START_CODE + PRIVATE_STREAM_2:
		return "private_stream_2";
		break;

	case PACKET_START_CODE + AUDIO_STREAM ... 
	     PACKET_START_CODE + AUDIO_STREAM + 0x1F:
		sprintf(buf, "audio_stream_%d", code & 0x1F);
		return buf;
		break;

	case PACKET_START_CODE + VIDEO_STREAM ... 
	     PACKET_START_CODE + VIDEO_STREAM + 0x0F:
		sprintf(buf, "video_stream_%d", code & 0x0F);
		return buf;
		break;

	case PACKET_START_CODE + ECM_STREAM:
		return "ecm_stream";
		break;

	case PACKET_START_CODE + EMM_STREAM:
		return "emm_stream";
		break;

	case PACKET_START_CODE + DSM_CC_STREAM:
		return "dsm_cc_stream";
		break;

	case PACKET_START_CODE + ISO_13522_STREAM:
		return "iso_13522_stream";
		break;

	case PACKET_START_CODE + 0xF4 ...
	     PACKET_START_CODE + 0xFE:
		sprintf(buf, "reserved_stream_%02x", code & 0xFF);
		return buf;
		break;

	case PACKET_START_CODE + PROGRAM_STREAM_DIRECTORY:
		return "program_stream_directory";
		break;

	default:
		return "invalid";
		break;	
	}
}

--- NEW FILE ---
/*
 *  MPEG-1 Real Time Encoder
 *
 *  Copyright (C) 1999-2000 Michael H. Schimek
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  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.
 */

/* $Id: systems.h,v 1.1 2001/12/04 23:57:32 mswitch Exp $ */

#include "libsystems.h"

#include "../common/log.h"
#include "../common/types.h"
#include "../common/fifo.h"

#define PACKET_SIZE		2048
#define PACKETS_PER_PACK	16
#define PAD_PACKETS		FALSE
#define CONST_BIT_RATE		FALSE
#define PAYLOAD_ALIGNMENT	1

typedef struct stream stream;

struct stream {
	fifo			fifo;
	consumer		cons;

	int			stream_id;

	int			bit_rate;
	double			frame_rate;

	buffer *		buf;
	unsigned char *		ptr;
	int			left;

	double			dts;
	double			pts_offset;

	double			eff_bit_rate;

	double			ticks_per_frame;
	double			ticks_per_byte;

	double			cap_t0;
	long long		frame_count;
};

struct multiplexer {
	xlist			streams;

	fifo *			output;
	producer 		prod;

	int			packet_size;

	void *			user_data;
};

extern buffer *		(* mux_output)(struct multiplexer *mux, buffer *b);

--- NEW FILE ---
/*
 *  MPEG-1 Real Time Encoder
 *
 *  Copyright (C) 1999-2001 Michael H. Schimek
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  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.
 */

/* $Id: vcd.c,v 1.1 2001/12/04 23:57:32 mswitch Exp $ */

/*
 *  This code creates a stream suitable for mkvcdfs as vcdmplex
 *  does, both part of vcdtools by Rainer Johanni.
 *
 *  Disclaimer: I don't have the standard documents (ISO 11172-1,
 *  VCD "White Book") and, quote: "[vcdtools] where created mainly
 *  by reverse engineering the content of a VCD", so nobody can
 *  guarantee the files will work.
 *
 *  (IMHO there are better choices for archiving than VCD and mp1e
 *  at 1.152 or 2.3 Mbit/s, you will get what you asked for.)
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <limits.h>
#include "../video/mpeg.h"
#include "../video/video.h"
#include "../audio/libaudio.h"
#include "../common/log.h"
#include "../common/fifo.h"
#include "../common/math.h"
#include "../options.h"
#include "mpeg.h"
#include "systems.h"

#define put(p, val, bytes)						\
do {									\
	unsigned int v = val;						\
									\
	switch (bytes) {						\
	case 4:								\
		*((unsigned int *)(p)) = swab32(v);			\
		break;							\
	case 3:	(p)[bytes - 3] = v >> 16;				\
	case 2:	(p)[bytes - 2] = v >> 8;				\
	case 1:	(p)[bytes - 1] = v;					\
	}								\
} while (0)

static inline unsigned char *
time_stamp(unsigned char *p, int marker, double system_time)
{
	long long ts = llroundn(system_time);

	p[0] = (marker << 4) + ((ts >> 29) & 0xE) + 1;
	p[1] = ((long) ts >> 22);
	p[2] = ((long) ts >> 14) | 1;
	p[3] = ((long) ts >> 7);
	p[4] = (long) ts * 2 + 1;
	/*
	 *  marker [4], TS [32..30], marker_bit,
	 *  TS [29..15], marker_bit,
	 *  TS [14..0], marker_bit
	 */

	return p + 5;
}

#define PACK_HEADER_SIZE 12
#define SCR_offset 8

static inline unsigned char *
pack_header(unsigned char *p, double scr, double system_rate)
{
	int mux_rate = ((unsigned int) ceil(system_rate + 49)) / 50;

	assert(mux_rate > 0);

	put(p, PACK_START_CODE, 4);
	time_stamp(p + 4, MARKER_SCR, scr);
	put(p + 9, (mux_rate >> 15) | 0x80, 1);
	put(p + 10, mux_rate >> 7, 1);
	put(p + 11, (mux_rate << 1) | 1, 1);
	/*
	 *  pack_start_code [32], system_clock_reference [40],
	 *  marker_bit, program_mux_rate, marker_bit
	 */

	return p + 12;
}

#define SYSTEM_HEADER_SIZE 15

static inline unsigned char *
system_header(unsigned char *p, stream *str, double system_rate_bound)
{
	int mux_rate_bound = ((unsigned int) ceil(system_rate_bound + 49)) / 50;
	int audio_bound = 0;
	int video_bound = 0;

	assert(mux_rate_bound > 0);

	put(p + 12, str->stream_id, 1);

	if (str->stream_id == VIDEO_STREAM + 0) {
		put(p + 13, (0x3 << 14) + (1 << 13) + (47104 >> 10), 2);
		video_bound++;
	} else if (str->stream_id == AUDIO_STREAM + 0) {
		put(p + 13, (0x3 << 14) + (0 << 13) + (4096 >> 7), 2);
		audio_bound++;
	} else {
		FAIL("Stream id mismatch\n");
	}

	put(p, SYSTEM_HEADER_CODE, 4);
	put(p + 4, 9, 2);
	put(p + 6, (mux_rate_bound >> 15) | 0x80, 1);
	put(p + 7, mux_rate_bound >> 7, 1);
	put(p + 8, (mux_rate_bound << 1) | 1, 1);
	put(p + 9, (audio_bound << 2) + (0 << 1) + (0 << 0), 1);
	put(p + 10, (0 << 7) + (0 << 6) + (0x1 << 5) + (video_bound << 0), 1);
	put(p + 11, 0xFF, 1);
	/*
	 *  system_header_start_code [32], header_length [16],
	 *  marker_bit, rate_bound [22], marker_bit,
	 *  audio_bound [6], fixed_flag, CSPS_flag,
	 *  system_audio_lock_flag, system_video_lock_flag,
	 *  marker_bit, video_bound [5], reserved_byte [8]
	 */

	return p + 15;
}

static inline unsigned char *
padding_packet(unsigned char *p, int size)
{
	if (size < 8)
		return p;

	put(p, PACKET_START_CODE + PADDING_STREAM, 4);
	put(p + 4, size - 6, 2);
	put(p + 6, 0x0F, 1);

	memset(p + 7, 0xFF, size - 7);

	return p + size;
}

static inline bool
next_access_unit(stream *str, double *ppts, unsigned char *ph)
{
	buffer *buf;

	str->buf = buf = wait_full_buffer(&str->cons);

	str->ptr  = buf->data;
	str->left = buf->used;

	if (str->left <= 0) {
		str->left = 0; /* XXX */
		return FALSE;
	}

	if (ph) {
		/*
		 *  Add DTS/PTS of first access unit
		 *  commencing in packet.
		 */
		if (str->stream_id == VIDEO_STREAM + 0) {
			/*
			 *  d/o  IBBPBBIBBPBBP
			 *  c/o  IPBBIBBPBBPBB
			 *  DTS  0123456789ABC
			 *  PTS  1423756A89DBC
			 */
			switch (buf->type) {
			case I_TYPE:
			case P_TYPE:
				*ppts = str->dts + str->ticks_per_frame * buf->offset; // reorder delay
				put(ph + 6, (0x1 << 14) + (1 << 13) + (47104 >> 10), 2);
				time_stamp(ph +  8, MARKER_PTS, *ppts);
				time_stamp(ph + 13, MARKER_DTS, str->dts);
				break;

			case B_TYPE:
				*ppts = str->dts; // delay always 0
				time_stamp(ph +  8, MARKER_PTS, *ppts);
				time_stamp(ph + 13, MARKER_DTS, str->dts);
				break;

			default:
				; /* no time stamp */
			}
		} else {
			*ppts = str->dts + str->pts_offset;
			put(ph + 6, (0x1 << 14) + (0 << 13) + (4096 >> 7), 2);
			time_stamp(ph + 8, MARKER_PTS_ONLY, *ppts);
		}
	}

	str->ticks_per_byte = str->ticks_per_frame / str->left;

	return TRUE;
}

#define LARGE_DTS (DBL_MAX / 2.0)

static inline stream *
schedule(multiplexer *mux)
{
	double dtsi_min = LARGE_DTS;
	stream *s, *str;

	str = NULL;

	for_all_nodes (s, &mux->streams, fifo.node) {
		double dtsi = s->dts;

		if (s->buf)
			dtsi += (s->ptr - s->buf->data) * s->ticks_per_byte;

		if (dtsi < dtsi_min) {
			str = s;
			dtsi_min = dtsi;
		}
	}

	return str;
}

void *
vcd_system_mux(void *muxp)
{
	multiplexer *mux = muxp;
	unsigned char *p, *ph, *ps, *pl, *px;
	unsigned long long bytes_out = 0;
	unsigned long long packet_count;
	double system_rate, system_rate_bound;
	double system_overhead;
	double ticks_per_pack, tscr;
	double scr, pts, front_pts = 0.0;
	stream *str, *video_stream = NULL;
	stream *audio_stream = NULL;
	bool do_pad = TRUE;
	buffer *buf;

	pthread_cleanup_push((void (*)(void *)) pthread_rwlock_unlock, (void *) &mux->streams.rwlock);
	assert(pthread_rwlock_rdlock(&mux->streams.rwlock) == 0);

	{
		double preload_delay;
		double video_frame_rate = DBL_MAX;
		int nstreams = 0;
		int preload = 0;
		int bit_rate = 0;

		for_all_nodes (str, &mux->streams, fifo.node) {
			str->buf = NULL;
			bit_rate += str->bit_rate;
			str->eff_bit_rate = str->bit_rate;
			str->ticks_per_frame = (double) SYSTEM_TICKS / str->frame_rate;

			if (IS_VIDEO_STREAM(str->stream_id) && str->frame_rate < video_frame_rate) {
				video_stream = str;
				video_frame_rate = frame_rate;
				if (abs(str->bit_rate - 1152000) > 20000)
					do_pad = FALSE;
			} else {
				audio_stream = str;
				if (abs(str->bit_rate - 224000) > 5000)
					do_pad = FALSE;
			}

			buf = wait_full_buffer(&str->cons);

			if (buf->used <= 0) /* XXX */
				FAIL("Premature end of file / error");

	    		preload += buf->used;

			unget_full_buffer(&str->cons, buf);

			nstreams++;
		}

		if (!video_stream || !audio_stream || nstreams != 2)
			FAIL("One video, one audio stream required");

		buf = mux_output(mux, NULL);

		assert(buf && buf->size >= 512
		           && buf->size <= 32768);

		system_overhead = mux->packet_size / (mux->packet_size - SYSTEM_HEADER_SIZE - PACK_HEADER_SIZE);
		system_rate = system_rate_bound = bit_rate * system_overhead / 8;

		/* Frames must arrive completely before decoding starts */
		preload_delay = (preload * system_overhead + PACK_HEADER_SIZE)
			/ system_rate_bound * SYSTEM_TICKS;

		scr = 36000;
		ticks_per_pack = mux->packet_size / system_rate_bound * SYSTEM_TICKS;

		for_all_nodes (str, &mux->streams, fifo.node) {
			/* Video PTS is delayed by one frame */
			if (!IS_VIDEO_STREAM(str->stream_id)) {
				str->pts_offset = (double) SYSTEM_TICKS / (video_frame_rate * 1.0);
				/* + 0.1 to schedule video frames first */
				str->dts = preload_delay + 0.1;
			} else
				str->dts = preload_delay;
		}
	}

	/* Appears suspicious to me, but this is what vcdmplex does. */

	p = buf->data;
	p = pack_header(p, scr, system_rate);
	p = system_header(p, audio_stream, system_rate_bound);

	if (do_pad)
		p = padding_packet(p, buf->data + buf->size - p);

	bytes_out += buf->used = p - buf->data;
	buf = mux_output(mux, buf);

	scr += ticks_per_pack;

	p = buf->data;
	p = pack_header(p, scr, system_rate);
	p = system_header(p, video_stream, system_rate_bound);

	if (do_pad)
		p = padding_packet(p, buf->data + buf->size - p);

	bytes_out += buf->used = p - buf->data;
	buf = mux_output(mux, buf);

	tscr = scr;

	packet_count = 2;

	/* Packet loop */

	for (;;) {
		int n, m;

		p = buf->data;
		px = p + buf->size;
		p = pack_header(p, scr, system_rate);

		scr += ticks_per_pack;

		/* XXX target system rate 75 * 2324 B/s */
reschedule:
		if (!(str = schedule(mux)))
			break;

		pts = -1;

		ph = p;
		ps = p;

		n = px - p - 7;
		m = (str->stream_id == VIDEO_STREAM + 0) ? 11 : 6;

		put(p, PACKET_START_CODE + str->stream_id, 4);
		put(p + 4, 0xFFFFFFFF, 4);
		put(p + 8, 0xFFFFFFFF, 4);
		put(p + 12, 0xFFFFFFFF, 4);
		put(p + 16, 0xFFFF, 2);

		if (str->left >= (n - m)) {
			m = MIN(str->left, n);
			p += n - m + 6;

			put(p, 0x0F, 1);
			memcpy(p + 1, str->ptr, m);

			p = px;
			str->ptr += m;
			str->left -= m;

			if (!str->left) {
				send_empty_buffer(&str->cons, str->buf);

				str->buf = NULL;
				str->dts += str->ticks_per_frame;
			}
		} else {
			put(p + m + 6, 0x0F, 1);
			pl = p += m + 7;

			while (p < px) {
				if (str->left <= 0) {
					if (!next_access_unit(str, &pts, ph)) {
						str->dts = LARGE_DTS * 2.0; // don't schedule stream

						if (pl == p) {
							/* no payload */
							p = ps;
							goto reschedule;
						}

						if (do_pad) {
							memset(p, 0, px - p);
							p = px;
						}

						break;
					}

					ph = NULL;
				}

				n = MIN(str->left, px - p);

				memcpy(p, str->ptr, n);

				str->left -= n;

				if (!str->left) {
					send_empty_buffer(&str->cons, str->buf);

					str->buf = NULL;
					str->dts += str->ticks_per_frame;
				}

				p += n;

				str->ptr += n;
			}
		}

		printv(4, "Packet #%lld %s, pts=%f\n",
			packet_count, mpeg_header_name(str->stream_id), pts);

		((unsigned short *) ps)[2] = swab16(p - ps - 6);

		bytes_out += buf->used = p - buf->data;

		buf = mux_output(mux, buf);

		assert(buf && buf->size >= 512
			   && buf->size <= 32768);

		packet_count++;

		if (pts > front_pts)
			front_pts = pts;

		if (verbose > 0 && (packet_count & 3) == 0) {
			double system_load = 1.0 - get_idle();
			int min, sec;

			sec = front_pts / SYSTEM_TICKS;
			min = sec / 60;
			sec -= min * 60;

			printv(1, "%d:%02d (%.1f MB), system load %4.1f %%",
				min, sec, bytes_out / (double)(1 << 20), 100.0 * system_load);

			if (video_frames_dropped > 0)
				printv(1, ", %5.2f %% dropped",
					100.0 * video_frames_dropped / video_frame_count);

			printv(1, (verbose > 3) ? "\n" : "  \r");

			fflush(stderr);
		}
	}

	p = buf->data;

	*((unsigned int *) p) = swab32(ISO_END_CODE);
	buf->used = 4;

	mux_output(mux, buf);

	pthread_cleanup_pop(1);

	return NULL;
}




More information about the MPlayer-cvslog mailing list