[Mplayer-cvslog] CVS: main/libmp1e/common alloc.c,NONE,1.1 alloc.h,NONE,1.1 bstream.c,NONE,1.1 bstream.h,NONE,1.1 bstream_mmx.s,NONE,1.1 errstr.c,NONE,1.1 errstr.h,NONE,1.1 fifo.c,NONE,1.1 fifo.h,NONE,1.1 list.h,NONE,1.1 log.h,NONE,1.1 math.h,NONE,1.1 mmx.c,NONE,1.1 mmx.h,NONE,1.1 profile.c,NONE,1.1 profile.h,NONE,1.1 sync.c,NONE,1.1 sync.h,NONE,1.1 threads.h,NONE,1.1 types.h,NONE,1.1

David Holm mswitch at mplayer.dev.hu
Wed Dec 5 00:56:47 CET 2001


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

Added Files:
	alloc.c alloc.h bstream.c bstream.h bstream_mmx.s errstr.c 
	errstr.h fifo.c fifo.h list.h log.h math.h mmx.c mmx.h 
	profile.c profile.h sync.c sync.h threads.h types.h 
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: alloc.c,v 1.1 2001/12/04 23:56:43 mswitch Exp $ */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdlib.h>
#include "math.h"

#ifndef HAVE_MEMALIGN

void *
alloc_aligned(size_t size, int align, bool clear)
{
	void *p, *b;

	if (align < sizeof(void *))
		align = sizeof(void *);

	if (!(b = malloc(size + align)))
		return NULL;

	p = (void *)(((long)((char *) b + align)) & -align);

	((void **) p)[-1] = b;

	if (clear)
		memset(p, 0, size);

	return p;
}

#endif // !HAVE_MEMALIGN

--- 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: alloc.h,v 1.1 2001/12/04 23:56:43 mswitch Exp $ */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#ifndef ALLOC_H
#define ALLOC_H

#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include "types.h"

#ifdef HAVE_MEMALIGN
#define free_aligned(p) free(p)
#else
#define free_aligned(p) free(((void **) p)[-1])
#endif

extern void *alloc_aligned(size_t, int, bool);

static inline void *
malloc_aligned(size_t size, int align)
{
	void *p;

#ifdef HAVE_MEMALIGN
	p = (void *) memalign(align, size);
#else
	p = alloc_aligned(size, align, FALSE);
/*
	if ((p = malloc(size + align)))
		(char *) p += align - ((int) p & (align - 1));
 */
#endif
	return p;
}

static inline void *
calloc_aligned(size_t size, int align)
{
	void *p;

#ifdef HAVE_MEMALIGN
	if ((p = (void *) memalign(align, size)))
		memset(p, 0, size);
#else
	p = alloc_aligned(size, align, TRUE);
/*
	if ((p = calloc(1, size + align)))
		(char *) p += align - ((int) p & (align - 1));
 */
#endif
	return p;
}

#endif // ALLOC_H

--- 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: bstream.c,v 1.1 2001/12/04 23:56:43 mswitch Exp $ */

#include <stdio.h>
#include "bstream.h"

void
binit_write(struct bs_rec *b)
{
	b->n		= 0;
	b->buf.uq	= 0;
	b->uq64.uq	= 64ULL;
}

/*
 *  Returns the number of bits encoded since bstart,
 *  granularity 64 bits
 */
int
bflush(struct bs_rec *b)
{
	// Bits are shifted to msb already, filled up with padding
	// zeroes. Store as mmx.uq to maintain frame alignment.

	((unsigned int *) b->p)[0] = swab32(b->buf.ud[1]);
	((unsigned int *) b->p)[1] = swab32(b->buf.ud[0]);

	b->p++;
	b->n = 0;
	b->buf.uq = 0;

	return (b->p - b->p1) * 64;
}

--- 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: bstream.h,v 1.1 2001/12/04 23:56:43 mswitch Exp $ */

#ifndef BITSTREAM_H
#define BITSTREAM_H

#include "mmx.h"

struct bs_rec {
	int		n;	/* Number of bits accumulated */
	mmx_t *		p;	/* Output buffer */
	mmx_t		buf;	/* Accumulated bits (usually cached in mm7), msb justified */
	mmx_t		uq64;	/* 64ULL */
	mmx_t *		p1;
	int		pad;
};

extern void		binit_write(struct bs_rec *b);
#define			bstart(b, p0) ((b)->p1 = (b)->p = (mmx_t *)(p0));
#define bepilog(b)	__asm__ __volatile__ (" movq %0,%%mm7\n" :: "m" ((b)->buf))
#define bprolog(b)	__asm__ __volatile__ (" movq %%mm7,%0\n" :: "m" ((b)->buf))
/* Encode rightmost n bits in v, with v < (1 << n) and 0 < n <= 32 */
#define bputl(b, v, n)  mmx_bputl(b, n, v) /* to match reg assignment of bputq */
extern void		mmx_bputl(struct bs_rec *b, int n, unsigned int v) __attribute__ ((regparm (3)));
/* Encode rightmost n bits in mm0.uq, with mm0.uq < (1 << n) and 0 < n <= 64 */
#define bputq		mmx_bputq
extern void		mmx_bputq(struct bs_rec *b, int n) __attribute__ ((regparm (2)));
#define			bwritten(b) ((((char *)(b)->p - (char *)(b)->p1) * 8) + ((b)->n))
extern int		bflush(struct bs_rec *b);
#define			brewind(bd, bs) (*(bd) = *(bs))

static inline void
bstartq(unsigned int v)
{
	__asm__ __volatile__ ("\tmovd %0,%%mm0;\n"
		:: "rm" (v) : "cc" FPU_REGS);
}

#define bcatq(v, n)							\
do {									\
	if (__builtin_constant_p(n))					\
		__asm__ __volatile__ (					\
			"\tpsllq %0,%%mm0;\n"				\
			:: "im" ((n)) : "cc" FPU_REGS);			\
			/* never m but suppress warning */		\
	else								\
		__asm__ __volatile__ (					\
			"\tmovd %0,%%mm2;\n"				\
			"\tpsllq %%mm2,%%mm0;\n"			\
			:: "rm" ((unsigned int)(n)) : "cc" FPU_REGS);	\
									\
	if (0&&__builtin_constant_p(v))					\
		__asm__ __volatile__ ("\tpor %0,%%mm0;\n"		\
			:: "m" ((unsigned long long)(v))		\
			: "cc" FPU_REGS);				\
	else								\
		__asm__ __volatile__ (					\
			"\tmovd %0,%%mm1;\n"				\
			"\tpor %%mm1,%%mm0;\n"				\
			:: "rm" ((unsigned int)(v)) : "cc" FPU_REGS);	\
} while (0)

#endif /* BITSTREAM_H */

--- 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: bstream_mmx.s,v 1.1 2001/12/04 23:56:43 mswitch Exp $
	.text
	.align		16
	.globl		mmx_bputl
	.globl		mmx_bputq

# void
# mmx_bputl(struct bs_rec *b [eax], int n [edx], unsigned int v [ecx])

mmx_bputl:
	movd		%ecx,%mm0;	// value
mmx_bputq:
	addl		(%eax),%edx;	// bs_rec->n + nbits
	movl		$64,%ecx;
	subl		%edx,%ecx;
	movd		%ecx,%mm1;
	jle		1f;
	psllq		%mm1,%mm0;
	movl		%edx,(%eax);	// bs_rec->n
	por		%mm0,%mm7;
	ret
1:
	movd		16(%eax),%mm3;	// bs_rec->uq64 (64ULL)
	pxor		%mm2,%mm2;
	movq		%mm0,%mm4;
	movl		4(%eax),%ecx;	// bs_rec->p
	paddd		%mm1,%mm3;
	psubd		%mm1,%mm2;
	psllq		%mm3,%mm0;
	leal		8(%ecx),%edx;
	psrlq		%mm2,%mm4;
	movd		%mm2,(%eax);	// bs_rec->n
	por		%mm7,%mm4;
	movl		%edx,4(%eax);	// bs_rec->p
	movd		%mm4,%edx;
	psrlq		$32,%mm4;
	bswap		%edx;
	movd		%mm4,%eax;
	movl		%edx,4(%ecx)
	movq		%mm0,%mm7;
	bswap		%eax;
	movl		%eax,(%ecx);
	ret

--- NEW FILE ---
/*
 *  Copyright (C) 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 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.
 */

/* $Id: errstr.c,v 1.1 2001/12/04 23:56:43 mswitch Exp $ */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <stdarg.h>
#include <assert.h>

#include "errstr.h"

static pthread_key_t errstr_key;

struct errstr_rec {
	char *			message;
	void			(* free)(void *);
};

/**
 * reset_errstr:
 * 
 * Reset the thread specific errstr object, freeing a stored
 * error message as desired. Subsequent calls to get_errstr
 * will return %NULL. All newly created threads have a reset
 * errstr.
 **/
void
reset_errstr(void)
{
	struct errstr_rec *e;

	if ((e = pthread_getspecific(errstr_key))) {
		if (e->message && e->free)
			e->free(e->message);
		free(e);
		pthread_setspecific(errstr_key, NULL);
	}
}

/**
 * set_errstr:
 * @message: Error message, any zero terminated string.
 *   Can be %NULL, which has the same effect as calling reset_errstr().
 * @free_func: Pointer to a function to free the error message
 *   (eg. 'free'), can be %NULL for a static error message.
 * 
 * Set the thread specific errstr to @message, freeing any previously
 * stored message. When this function fails, subsequent calls to
 * get_errstr will return %NULL. The error message is freed when
 * calling reset_errstr(), set_errstr() and at thread termination.
 **/
void
set_errstr(char *message, void (*free_func)(void *))
{
	struct errstr_rec *e;

	if ((e = pthread_getspecific(errstr_key))) {
		if (e->message && e->free)
			e->free(e->message);
	} else if (!(e = malloc(sizeof(*e))))
		return;

	e->message = message;
	e->free = free_func;

	if (pthread_setspecific(errstr_key, e) != 0) {
		if (e->message && e->free)
			e->free(e->message);
		free(e);
	}
}

void
set_errstr_printf(char *templ, ...)
{
	va_list ap;
	char *msg;

	va_start(ap, templ);
	vasprintf(&msg, templ, ap);
	va_end(ap);

	set_errstr(msg, free);
}

/**
 * get_errstr:
 *
 * Reads the thread specific errstr object, this is equivalent
 * to reading the 'errstr' pseudo variable (char *). Note only
 * set_errstr() can write 'errstr'.
 * 
 * Return value:
 * Error message (char *) or %NULL if not set.
 **/
char *
get_errstr(void)
{
	struct errstr_rec *e;

	if ((e = pthread_getspecific(errstr_key)))
		return e->message;

	return NULL;
}

static void
uninit_errstr(void *p)
{
	struct errstr_rec *e = p;

	if (e) {
		if (e->message && e->free)
			e->free(e->message);
		free(e);
	}
}

static void init_errstr(void) __attribute__ ((constructor));

static void
init_errstr(void)
{
	assert(pthread_key_create(&errstr_key, uninit_errstr) == 0);
}

--- NEW FILE ---
/*
 *  Copyright (C) 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 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.
 */

/* $Id: errstr.h,v 1.1 2001/12/04 23:56:43 mswitch Exp $ */

#ifndef ERRSTR_H
#define ERRSTR_H

#ifndef NO_ERRSTR
#define errstr (get_errstr())
#endif

extern void		reset_errstr(void);
extern void		set_errstr(char *, void (*)(void *));
extern char *		get_errstr(void);

extern void		set_errstr_printf(char *, ...);

#endif /* ERRSTR_H */

--- NEW FILE ---
/*
 *  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: fifo.c,v 1.1 2001/12/04 23:56:43 mswitch Exp $ */

[...1132 lines suppressed...]
	    && f->eof_count <= list_members(&f->consumers))
		add_tail(&f->consumers, &c->node);
	else
		c = NULL;

	pthread_mutex_unlock(&f->producer->mutex);
	pthread_mutex_unlock(&f->consumer->mutex);

	return c;
}

/*
    TODO:
    * test callbacks
    * stealing buffers & callbacks ok?

    * add_p/c shall make a fifo callback
    * error ignores mp-fifo, in data direction only
    * add wait timeout (optional)
 */

--- NEW FILE ---
/*
 *  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: fifo.h,v 1.1 2001/12/04 23:56:43 mswitch Exp $ */

#ifndef FIFO_H
#define FIFO_H

#include <stdlib.h>
#include <sys/time.h>

#include "list.h"
#include "threads.h"

typedef struct fifo fifo;
typedef struct buffer buffer;
typedef struct consumer consumer;
typedef struct producer producer;

struct buffer {
	node 			node;		/* fifo->full/empty */
	fifo *			fifo;

	/*
	 *  These fields are used for the "virtual full queue".
	 *
	 *  consumer->next points to the next buffer in fifo->full
	 *  to be dequeued, NULL if all buffers have been consumed.
	 *  (b->refcount used to count the number of these
	 *   references but it turned out redundant.)
	 *
	 *  consumers is their number at send_full time, always > 0
	 *  because without consumers the buffer is instantly
	 *  send_emptied. We can't use fifo->consumers.members
	 *  because new consumers shall not dequeue old buffers.
	 *
	 *  dequeued and enqueued count the wait_full and send_empty
	 *  calls, respectively. A buffer is actually send_emptied
	 *  when enqueued >= consumers, and can be "recycled" when
	 *  dequeued == 0. enqueued > dequeued is already implied by
	 *  enqueued > consumers and no steady state.
	 *
	 *  For empty buffers b->refcount is n/a, consumers always 0 and
	 *  dequeued counts the wait_empty calls. enqueued is n/a
	 *  because send_full transfers the buffer immediately to
	 *  the full queue.
	 *
	 *  See rem_buffer for scheduled removal.
	 */
	int			consumers;

	int			dequeued;
	int			enqueued;

	bool			remove;

	/* Consumer read only, see send_full_buffer() */

	int			type;		/* application specific */
	int			offset;
	double			time;

	unsigned char *		data;		/* mandatory */
	ssize_t			used;

	int			error;		/* copy of errno */
	char *			errorstr;	/* gettext()ized, may be NULL */

	/* Owner private */

	node			added;		/* fifo->buffers */

	unsigned char *		allocated;
	ssize_t			size;

	void			(* destroy)(buffer *);

	void *			user_data;

	/* XXX? */
	int			rte_flags;	/* rte internal use */
};

struct fifo {
	node			node;		/* owner private */

	char			name[64];	/* for debug messages */

	mucon			pro, con;

	list			full;		/* FIFO */
	list			empty;		/* LIFO */

	list			producers;
	list			consumers;

	int			p_reentry;
	int			c_reentry;

	int			eof_count;	/* counts p->eof_sent */

	/* Owner private */

	mucon *			producer;	/* -> pro */
	mucon *			consumer;	/* -> con */

	list			buffers;	/* add/rem_buffer */

	bool			unlink_full_buffers; /* default true */

	void			(* wait_empty)(fifo *);
	void			(* send_full)(producer *, buffer *);

	void			(* wait_full)(fifo *);
	void			(* send_empty)(consumer *, buffer *);

	bool			(* start)(fifo *);
	void			(* stop)(fifo *);

	void			(* destroy)(fifo *);

	void *			user_data;

	buffer *		(* alloc_buffer)(ssize_t);
};

struct producer {
	node			node;		/* fifo->producers */
	fifo *			fifo;

	int			dequeued;	/* bookkeeping */
	bool			eof_sent;
};

struct consumer {
	node			node;		/* fifo->consumers */
	fifo *			fifo;

	buffer *		next_buffer;	/* virtual pointer */
	int			dequeued;	/* bookkeeping */
};

/**
 * current_time:
 * 
 * Buffer time is usually noted in seconds TOD. Unfortunately too
 * many interfaces provide no interrupt time so we have no choice
 * but to query the system clock.
 *
 * Return value:
 * gettimeofday() in seconds and fractions, double.
 **/
static inline double
current_time(void)
{
	struct timeval tv;

	gettimeofday(&tv, NULL);

	return tv.tv_sec + tv.tv_usec * (1 / 1e6);
	/* rezp. mult is faster, not auto optimized for accuracy */
}

/**
 * destroy_buffer:
 * @b: buffer *
 * 
 * Free all resources associated with the buffer. This is a
 * low-level function, don't call it for buffers which have
 * been added to a fifo. No op when @b is %NULL.
 **/
static inline void
destroy_buffer(buffer *b)
{
	if (b && b->destroy)
		b->destroy(b);
}

extern buffer *		init_buffer(buffer *, ssize_t);
extern buffer *		alloc_buffer(ssize_t);

/**
 * destroy_fifo:
 * @f: fifo *
 * 
 * Free all resources associated with the fifo, including all
 * buffers. Make sure no threads are using the fifo and no
 * producers or consumers can be added.
 *
 * Removing all producers and consumers before calling this
 * function is not necessary, for example when a consumer
 * aborted before detaching himself from the fifo.
 *
 * No op when @f is %NULL.
 **/
static inline void
destroy_fifo(fifo *f)
{
	if (f && f->destroy)
		f->destroy(f);
}

/**
 * recv_full_buffer:
 * @c: consumer *
 * 
 * Dequeues the next buffer in production order from the consumer's fifo's
 * full queue. Remind callback (unbuffered) fifos do not fill automatically
 * but only when the consumer calls wait_full_buffer(). In this case
 * recv_full_buffer() returns a buffer only after unget_full_buffer() or
 * when the producer enqueued more than one full buffer at the last
 * wait_full_buffer().
 *
 * Buffers must be returned with send_empty_buffer() as soon as possible
 * for re-use by the producer, including those with buffer.used == 0
 * (end of stream) or buffer.used < 0 (error). You can dequeue more buffers
 * before returning and buffers need not be returned in order. All buffer
 * contents are read-only for consumers.
 *
 * None of the fifo functions depend on the identity of the calling thread
 * (thread_t), however we assume the consumer object is not shared. 
 *
 * Return value:
 * Buffer pointer, or %NULL if the full queue is empty.
 **/
static inline buffer *
recv_full_buffer(consumer *c)
{
	fifo *f = c->fifo;
	buffer *b;

	pthread_mutex_lock(&f->consumer->mutex);

	if ((b = c->next_buffer)->node.succ) {
		c->next_buffer = (buffer *) b->node.succ;
		b->dequeued++;
		c->dequeued++;
	} else
		b = NULL;

	pthread_mutex_unlock(&f->consumer->mutex);

	return b;
}

extern buffer *		wait_full_buffer(consumer *c);
extern buffer *		wait_full_buffer_timeout(consumer *c, struct
						 timespec *timeout);

/**
 * unget_full_buffer:
 * @c: consumer *
 * @b: buffer *
 * 
 * Put buffer @b, dequeued with wait_full_buffer() or recv_full_buffer()
 * and not yet returned with send_empty_buffer(), back on the full queue
 * of its fifo, to be dequeued again later. You can unget more than one
 * buffer, in reverse order of dequeuing, starting with the most
 * recently dequeued.
 **/
static inline void
unget_full_buffer(consumer *c, buffer *b)
{
	fifo *f = c->fifo;

	/* Migration prohibited */
	assert(c->fifo == b->fifo);

	c->dequeued--;

	pthread_mutex_lock(&f->consumer->mutex);

	assert(c->next_buffer == (buffer *) b->node.succ);

	b->dequeued--;

	c->next_buffer = b;

	pthread_mutex_unlock(&f->consumer->mutex);
}

/**
 * send_empty_buffer:
 * @c: consumer *
 * @b: buffer *
 * 
 * Consumers call this function when done with a previously dequeued
 * full buffer. Dereferencing the buffer pointer after sending the
 * buffer is not permitted.
 **/
static inline void
send_empty_buffer(consumer *c, buffer *b)
{
	/* Migration prohibited */
	assert(c->fifo == b->fifo);

	c->dequeued--;

	c->fifo->send_empty(c, b);
}

/* XXX rethink */
extern void			send_empty_buffered(consumer *c, buffer *b);

/**
 * recv_empty_buffer:
 * @p: producer *
 * 
 * Dequeues an empty buffer, no particular order, from the producer's fifo's
 * empty queue. Remind callback (unbuffered) fifos do not fill automatically
 * but only when the producer calls wait_empty_buffer().
 * 
 * Send filled buffers with send_full_buffer(). You can dequeue more buffers
 * before sending and buffers need not be sent in dequeuing order.
 *
 * None of the fifo functions depend on the identity of the calling thread
 * (thread_t), however we assume the producer object is not shared. 
 *
 * Return value:
 * Buffer pointer, or %NULL if the empty queue is empty.
 **/
static inline buffer *
recv_empty_buffer(producer *p)
{
	fifo *f = p->fifo;
	buffer *b;

	pthread_mutex_lock(&f->producer->mutex);

	b = PARENT(rem_head(&f->empty), buffer, node);

	pthread_mutex_unlock(&f->producer->mutex);

	if (b) {
		b->dequeued = 1;
		p->dequeued++;
	}

	return b;
}

extern buffer *		wait_empty_buffer(producer *p);

/**
 * unget_empty_buffer:
 * @p: producer *
 * @b: buffer *
 * 
 * Put buffer @b, dequeued with wait_empty_buffer() or recv_empty_buffer()
 * and not yet enqueued with send_full_buffer(), back on the empty queue
 * of its fifo, to be dequeued again later. You can unget more than one
 * buffer, in any order, but not the same buffer twice.
 *
 * It may happen another producer grabs the ungot buffer, so a subsequent
 * recv_empty_buffer() will not necessarily succeed.
 **/
static inline void
unget_empty_buffer(producer *p, buffer *b)
{
	fifo *f = p->fifo;

	/* Migration prohibited, don't use this to add buffers to the fifo */
	assert(p->fifo == b->fifo && b->dequeued == 1);

	p->dequeued--;
	b->dequeued = 0;

	pthread_mutex_lock(&f->producer->mutex);

	add_tail(&f->empty, &b->node);

	pthread_mutex_unlock(&f->producer->mutex);
}

extern void			send_full_buffer(producer *p, buffer *b);

extern void			rem_buffer(buffer *b);
extern bool			add_buffer(fifo *f, buffer *b);

extern int			init_buffered_fifo(fifo *f, char *name, int num_buffers, ssize_t buffer_size);
extern int			init_callback_fifo(fifo *f, char *name, void (* custom_wait_empty)(fifo *), void (* custom_send_full)(producer *, buffer *), void (* custom_wait_full)(fifo *), void (* custom_send_empty)(consumer *, buffer *), int num_buffers, ssize_t buffer_size);

extern void			rem_producer(producer *p);
extern producer *		add_producer(fifo *f, producer *p);

extern void			rem_consumer(consumer *c);
extern consumer	*		add_consumer(fifo *f, consumer *c);

/* XXX TBD */
/* start only *after* adding a consumer? */
/* mp-fifos? */
static inline bool
start_fifo(fifo *f)
{
	return f->start(f);
}

#endif /* FIFO_H */

--- NEW FILE ---
/*
 *  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: list.h,v 1.1 2001/12/04 23:56:43 mswitch Exp $ */

#ifndef LIST_H
#define LIST_H

#include <assert.h>
#include <pthread.h>
#include "types.h"

/*
 *  Your familiar doubly linked list type, plus member
 *  count for fast resource accounting and optional rwlock.
 *
 *  Warning: No verification of the validity of list and
 *  node parameters. The number of members must not
 *  exceed INT_MAX.
 *
 *  Traverse:
 *  for (node = list->head; node->succ; node = node->succ)
 *
 *  Remove:
 *  for (n1 = list->head; (n2 = n1->succ); n1 = n2)
 *  	rem_node(n1);
 */

typedef struct node node;
typedef struct list list;
typedef struct xlist xlist; /* Xclusive access */

struct node {
	node *			succ;
	node *			pred;
};

struct list {
	node *			head;
	node *			null;
	node *			tail;
	int			members;
};

struct xlist {
	node *			head;
	node *			null;
	node *			tail;
	int			members;
	pthread_rwlock_t	rwlock;
};

/*
 * list foo_list;
 * struct foo { int baz; node bar; }, *foop;
 *
 * for_all_nodes(foop, &foo_list, bar)
 *   foop->baz = 0;
 *
 * Not useful to delete list members.
 */
#define for_all_nodes(p, l, _node_)					\
for ((p) = PARENT((l)->head, typeof(*(p)), _node_);			\
     (p)->_node_.succ;							\
     (p) = PARENT((p)->_node_.succ, typeof(*(p)), _node_))

/**
 * destroy_list:
 * 
 * Free all resources associated with the list,
 * you must pair this with an init_list() call.
 *
 * Does not free the list object or any nodes.
 **/
static inline void
destroy_xlist(xlist *l)
{
	assert(l->members == 0);
	assert(pthread_rwlock_destroy(&l->rwlock) == 0);
}

static inline void
destroy_list(list *l)
{
}

static inline void
destroy_invalid_xlist(xlist *l)
{
	pthread_rwlock_destroy(&l->rwlock);
}

/**
 * init_list:
 * @l: list * 
 * 
 * Return value:
 * The list pointer.
 **/
static inline list *
init_list(list *l)
{
	l->head = (node *) &l->null;
	l->null = (node *) 0;
	l->tail = (node *) &l->head;

	l->members = 0;

	return l;
}

static inline xlist *
init_xlist(xlist *l)
{
	assert(pthread_rwlock_init(&l->rwlock, NULL) == 0);

	l->head = (node *) &l->null;
	l->null = (node *) 0;
	l->tail = (node *) &l->head;

	l->members = 0;

	return l;
}

/**
 * list_members:
 * @l: list *
 * 
 * Return value:
 * Number of nodes linked in the list. You can read
 * l->members directly when the rwlock is unused.
 **/
static inline unsigned int
list_members(list *l)
{
	return l->members;
}

static inline unsigned int
list_xmembers(xlist *l)
{
	int members;

	pthread_rwlock_rdlock(&l->rwlock);
	members = l->members;
	pthread_rwlock_unlock(&l->rwlock);

	return members;
}

/**
 * empty_list:
 * @l: list *
 * 
 * Return value:
 * 1 if the list is empty, 0 otherwise. You can read
 * l->members directly when the rwlock is unused.
 **/
static inline int
empty_list(list *l)
{
	return l->members == 0;
}

static inline int
empty_xlist(xlist *l)
{
	return list_xmembers(l) == 0;
}

/**
 * add_head:
 * @l: list *
 * @n: node *
 * 
 * Add node at the head of the list.
 *
 * Return value:
 * The node pointer.
 **/
static inline node *
add_head(list *l, node *n)
{
	n->pred = (node *) &l->head;
	n->succ = l->head;
	l->head->pred = n;
	l->head = n;
	l->members++;

	return n;
}

static inline node *
add_xhead(xlist *l, node *n)
{
	pthread_rwlock_wrlock(&l->rwlock);
	n = add_head((list *) l, n);
	pthread_rwlock_unlock(&l->rwlock);

	return n;
}

/**
 * add_tail:
 * @l: list *
 * @n: node *
 * 
 * Add node at the end of the list.
 * 
 * Return value: 
 * The node pointer.
 **/
static inline node *
add_tail(list *l, node *n)
{
	n->succ = (node *) &l->null;
	n->pred = l->tail;
	l->tail->succ = n;
	l->tail = n;
	l->members++;

	return n;
}

static inline node *
add_xtail(xlist *l, node *n)
{
	pthread_rwlock_wrlock(&l->rwlock);
	n = add_tail((list *) l, n);
	pthread_rwlock_unlock(&l->rwlock);

	return n;
}

/**
 * rem_head:
 * @l: list *
 * 
 * Remove first node of the list.
 * 
 * Return value: 
 * Node pointer, or NULL if the list is empty.
 **/
static inline node *
rem_head(list *l)
{
	node *n = l->head, *s = n->succ;

	if (s) {
		s->pred = (node *) &l->head;
		l->head = s;
		l->members--;
		return n;
	}

	return NULL;
}

static inline node *
rem_xhead(xlist *l)
{
	node *n;

	pthread_rwlock_wrlock(&l->rwlock);
	n = rem_head((list *) l);
	pthread_rwlock_unlock(&l->rwlock);

	return n;
}

/**
 * rem_tail:
 * @l: list *
 * 
 * Remove last node of the list.
 * 
 * Return value: 
 * Node pointer, or NULL if the list is empty.
 **/
static inline node *
rem_tail(list *l)
{
	node *n = l->tail, *p = n->pred;

	if (p) {
		p->succ = (node *) &l->null;
		l->tail = p;
		l->members--;
		return n;
	}

	return NULL;
}

static inline node *
rem_xtail(xlist *l)
{
	node *n;

	pthread_rwlock_wrlock(&l->rwlock);
	n = rem_tail((list *) l);
	pthread_rwlock_unlock(&l->rwlock);

	return n;
}

/**
 * unlink_node:
 * @l: list *
 * @n: node *
 * 
 * Remove the node from its list. The node must
 * be a member of the list, not verified.
 * 
 * Return value: 
 * The node pointer.
 **/
static inline node *
unlink_node(list *l, node *n)
{
	n->pred->succ = n->succ;
	n->succ->pred = n->pred;
	l->members--;

	return n;
}

static inline node *
unlink_xnode(xlist *l, node *n)
{
	pthread_rwlock_wrlock(&l->rwlock);
	n = unlink_node((list *) l, n);
	pthread_rwlock_unlock(&l->rwlock);

	return n;
}

/**
 * rem_node:
 * @l: list *
 * @n: node *
 * 
 * Remove the node if member of the list.
 * 
 * Return value: 
 * The node pointer or NULL if the node is not
 * member of the list.
 **/
static inline node *
rem_node(list *l, node *n)
{
	node *q;

	for (q = l->head; q->succ; q = q->succ)
		if (n == q) {
			unlink_node(l, n);
			return n;
		}

	return NULL;
}

static inline node *
rem_xnode(xlist *l, node *n)
{
	pthread_rwlock_wrlock(&l->rwlock);
	n = rem_node((list *) l, n);
	pthread_rwlock_unlock(&l->rwlock);

	return n;
}

#endif /* LIST_H */

--- 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.
 */

#ifndef LOG_H
#define LOG_H

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#ifndef HAVE_PROGRAM_INVOCATION_NAME
extern char *		program_invocation_name;
extern char *		program_invocation_short_name;
#endif

extern int		verbose;

#define ISTF2(x) #x
#define ISTF1(x) ISTF2(x)

// mp1e:log.h:35: Failed to explain this (3, No such process)

#define ASSERT(what, cond, args...)					\
do {									\
	if (!(cond)) {							\
		fprintf(stderr,	"%s:" __FILE__ ":" ISTF1(__LINE__) ": "	\
			"Failed to " what " (%d, %s)\n",		\
			program_invocation_short_name			\
			 ,##args, errno, strerror(errno));		\
		exit(EXIT_FAILURE);					\
	} else if (0) {							\
		fprintf(stderr,	"%s:" __FILE__ ":" ISTF1(__LINE__) ": "	\
			what " - ok\n",	program_invocation_short_name	\
			 ,##args);					\
	}								\
} while (0)

// mp1e:log.h:47: Failed to explain this

#define ASSERTX(what, cond, args...)					\
do {									\
	if (!(cond)) {							\
		fprintf(stderr,	"%s:" __FILE__ ":" ISTF1(__LINE__) ": "	\
			"Failed to " what "\n",				\
			program_invocation_short_name ,##args);		\
		exit(EXIT_FAILURE);					\
	}								\
} while (0)

/* glib-ish g_return_if_fail */
#define CHECK(what, cond, args...)				\
do {								\
	if (!(cond)) {						\
		fprintf(stderr, "%s (" __FILE__ "@%d): "	\
			"Failed to " what " (%d, %s)\n",	\
			program_invocation_short_name,		\
			__LINE__ ,##args,			\
			errno, strerror(errno));		\
			return;					\
	}							\
} while (0)

// mp1e:log.h:71: Elvis lives

#define FAIL(why, args...)						\
do {									\
	fprintf(stderr,	"%s:" __FILE__ ":" ISTF1(__LINE__) ": "		\
		why "\n", program_invocation_short_name ,##args);	\
	exit(EXIT_FAILURE);						\
} while (0)

#define DUMP(array, from, to)						\
do {									\
	int i;								\
									\
	fprintf(stderr, __FILE__ "@%d:\n", __LINE__);			\
	for (i = (from); i < (to); i++)					\
		fprintf(stderr, #array "[%d]=%f\n",			\
			i, (double)((array)[i]));			\
} while (0)

/* Trace execution, except where void or prohibited by law. */

#define printv(level, format, args...)					\
    ((verbose >= level) ? fprintf(stderr,				\
	/* "%s: " */ format						\
	/*, program_invocation_short_name */ ,##args) : 0)

/* mp1e:log.h:106: User error message */

#define ERRMSG(templ, args...)						\
	set_errstr_printf("mp1e:" __FILE__ ":" ISTF1(__LINE__) ": "	\
		templ ,##args)

#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: math.h,v 1.1 2001/12/04 23:56:43 mswitch Exp $ */

#ifndef MATH_H
#define MATH_H

#include <math.h>
#include <float.h>

#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))

#define swap(a, b)						\
do {								\
	__typeof__ (a) _t = (b);				\
	(b) = (a);						\
	(a) = _t;						\
} while (0)

static inline int
saturate(int val, int min, int max)
{
#ifdef __i686__
	/* 2 x cmp cmov, typ. both evaluated */
	if (val < min)
		val = min;
	if (val > max)
		val = max;
#else
	/* 1-2 branches */
	if (val < min)
		val = min;
	else if (val > max)
		val = max;
#endif

	return val;
}

/*
 *  Integer sign, equv to saturate(val, -1, +1)
 */
#define sign(val) (((int)(val) >> 31) | ((int)(val) > 0))

/*
 *  Round to nearest int, halfway cases to the nearest even integer
 */
#ifdef __i386__
/* rtn is the default mode */
#define lroundn(val) ({ long res; asm volatile ("fistpl %0" : "=m" (res) : "t" (val) : "st"); res; })
#define llroundn(val) ({ long long res; asm volatile ("fistpll %0" : "=m" (res) : "t" (val) : "st"); res; })
#else
#define lroundn(val) ((long)(((val) < 0.0) ? -floor(0.5 - (val)) : floor((val) + 0.5)))
#define llroundn(val) ((long long)(((val) < 0.0) ? -floor(0.5 - (val)) : floor((val) + 0.5)))
#endif

/*
 *  Round integer v up to nearest integer multiple of a
 *  ?
 */
// #define lalign(v, a) ((v) + (a) - (int)(v) % (a))

/*
 *  Absolute value w/o a branch
 */
static inline unsigned int
nbabs(register int n)
{
	register int t = n;

        t >>= 31;
	n ^= t;
	n -= t;

	return n;
}

/*
 *  Find first set bit, starting at msb.
 *
 *  0x89abcdef -> 31, 0x456789ab -> 30
 *  0x00000001 -> 0, 0x00000000 undefined
 */
static inline int
ffsr(unsigned int n)
{
	int r;

	asm volatile ("\tbsrl %1,%0\n": "=r" (r) : "r" (n));

	return r;
}

/*
 *  Number of set bits
 */
static inline unsigned int
popcnt(unsigned int v)
{
	v -= ((v >> 1) & 0x55555555);
	v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
	v = (v + (v >> 4)) & 0x0F0F0F0F;

	return (v * 0x01010101) >> 24;
}

#endif // MATH_H

--- 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: mmx.c,v 1.1 2001/12/04 23:56:43 mswitch Exp $ */

#include <stdlib.h>
#include "log.h"
#include "mmx.h"
#include "math.h"

#if #cpu (i386)

/*
 *  References
 *
 *  "Intel Processor Identification and the CPUID Instruction",
 *  Application Note AP-485, Nov 2000, order no. 241618-016,
 *  http://developer.intel.com
 *
 *  "AMD Processor Recognition Application Note",
 *  publication # 20734, Rev. R, June 2000.
 *  http://www.amd.com/products/cpg/athlon/techdocs/index.html
 *
 *  "Cyrix CPU Detection Guide",
 *  Application Note 112, Rev. 1.9, July 21, 1998
 *  formerly available from http://www.cyrix.com/html/developers/index.htm
 *  when Cyrix was part of National Semiconductor.
 *  VIA has no similar document available as of Jan 2001.
 */

typedef union {
	unsigned char		s[16];
	struct {
		unsigned int		eax;
		unsigned int		ebx;
		unsigned int		edx;
		unsigned int		ecx;
	}			r;
} cpuid_t;

static inline int
toggle_eflags_id(void)
{
	int success;

	__asm__ __volatile__ (
		" pushfl	\n"
		" popl		%%ecx\n"
		" movl		%%ecx,%%eax\n"
		" xorl		$0x200000,%%eax\n"
		" pushl		%%eax\n"
		" popfl		\n"
		" pushfl	\n"
		" popl		%%eax\n"
		" pushl		%%ecx\n"
		" popfl		\n"
		" xorl		%%ecx,%%eax\n"
		" andl		$0x200000,%%eax\n"
		" jz		1f\n"
		" movl		$1,%%eax\n"
		"1:\n"
	: "=a" (success) :: "ecx", "cc");

	return success;
}

static /*inline*/ unsigned int
cpuid(cpuid_t *buf, unsigned int level)
{
	unsigned int eax;

	/* ARRRR */
	__asm__ __volatile__ (
		" pushl		%%ebx\n"
		" pushl		%%ecx\n"
		" pushl		%%edx\n"
		" cpuid		\n"
		" movl		%%eax,(%%edi)\n"
		" movl		%%ebx,4(%%edi)\n"
		" movl		%%edx,8(%%edi)\n"
		" movl		%%ecx,12(%%edi)\n"
		" popl		%%edx\n"
		" popl		%%ecx\n"
		" popl		%%ebx\n"
	: "=a" (eax) : "D" (buf), "a" (level) /*: "ebx", "ecx", "edx", "cc", "memory"*/);

	return eax;
}

/* XXX check kernel version before advertising SSE */
#define INTEL_CMOV	(1 << 15)
#define INTEL_MMX	(1 << 23)
#define INTEL_SSE	(1 << 25)
#define INTEL_SSE2	(1 << 26)

#define AMD_MMXEXT	(1 << 22)
#define AMD_MMX		(1 << 23)
#define AMD_SSE		(1 << 25)
#define AMD_3DNOWEXT	(1 << 30)
#define AMD_3DNOW	(1 << 31)

#define CYRIX_MMX	(1 << 23)
#define CYRIX_MMXEXT	(1 << 24)
#define CYRIX_3DNOW	(1 << 31)

#define FEATURE(bits)	((c.r.edx & (bits)) == (bits))

int
cpu_detection(void)
{
	cpuid_t c;

	if (!toggle_eflags_id()) {
		ASSERT("identify CPU", 0);
		return CPU_UNKNOWN;
	}

	cpuid(&c, 0);

	if (!strncmp(c.s + 4, "GenuineIntel", 12)) {
		cpuid(&c, 1);

		if (FEATURE(INTEL_MMX | INTEL_CMOV | INTEL_SSE | INTEL_SSE2))
			return CPU_PENTIUM_4;
		if (FEATURE(INTEL_MMX | INTEL_CMOV | INTEL_SSE))
			return CPU_PENTIUM_III;
		if (FEATURE(INTEL_MMX | INTEL_CMOV))
			return CPU_PENTIUM_II;
		if (FEATURE(INTEL_MMX))
			return CPU_PENTIUM_MMX;
	} else if (!strncmp(c.s + 4, "AuthenticAMD", 12)) {
		if (cpuid(&c, 0x80000000) > 0x80000000) {
			cpuid(&c, 0x80000001);

			if (FEATURE(AMD_MMX | AMD_MMXEXT | AMD_3DNOW | AMD_3DNOWEXT))
				return CPU_ATHLON;
			if (FEATURE(AMD_MMX | AMD_3DNOW))
				return CPU_K6_2;
		}
	} else if (!strncmp(c.s + 4, "CyrixInstead", 12)) {
		if (cpuid(&c, 0x80000000) > 0x80000000) {
			cpuid(&c, 0x80000001);

			if (FEATURE(CYRIX_MMX | CYRIX_MMXEXT | CYRIX_3DNOW))
				return CPU_CYRIX_III;
		} else {
			cpuid(&c, 1);

			if (FEATURE(CYRIX_MMX))
				return CPU_CYRIX_MII;
		}
	}

	ASSERT("identify CPU", 0);

	return CPU_UNKNOWN;
}

#else

int
cpu_detection(void)
{
	return 0;
}

#endif /* !cpu x86 */

--- 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.
 */

#ifndef MMX_H
#define MMX_H

#include <stdlib.h>

typedef	union {
	long long		q;
	unsigned long long	uq;
	int			d[2];
	unsigned int		ud[2];
	short			w[4];
	unsigned short		uw[4];
	char			b[8];
	unsigned char		ub[8];
} __attribute__ ((aligned (8))) mmx_t;

#define MMXQ(a) ((mmx_t)((long long)(a)))
#define MMXD(a,b) ((mmx_t)(((((unsigned long long)(b))&0xFFFFFFFFULL)<<32)|((((unsigned long long)(a))&0xFFFFFFFFULL))))
#define MMXW(a,b,c,d) ((mmx_t)(((((unsigned long long)(d))&0xFFFFULL)<<48)|((((unsigned long long)(c))&0xFFFFULL)<<32)|((((unsigned long long)(b))&0xFFFFULL)<<16)|((((unsigned long long)(a))&0xFFFFULL))))
#define MMXB(a,b,c,d,e,f,g,h) ((mmx_t)(((((unsigned long long)(h))&0xFFULL)<<56)|((((unsigned long long)(g))&0xFFULL)<<48)|((((unsigned long long)(f))&0xFFULL)<<40)|((((unsigned long long)(e))&0xFFULL)<<32)|((((unsigned long long)(d))&0xFFULL)<<24)|((((unsigned long long)(c))&0xFFULL)<<16)|((((unsigned long long)(b))&0xFFULL)<<8)|((((unsigned long long)(a))&0xFFULL))))

#define MMXRD(a) MMXD(a,a)
#define MMXRW(a) MMXW(a,a,a,a)
#define MMXRB(a) MMXB(a,a,a,a,a,a,a,a)

/*
 *  These are optimization classes rather than
 *  identifying the actual model or brand name.
 *  Keep order (options.c)
 */
#define CPU_UNKNOWN		0	/* no MMX */
#define	CPU_PENTIUM_MMX		1	/* MMX; late P5 core */
#define CPU_PENTIUM_II		2	/* MMX, CMOV; any P6 core */
#define	CPU_PENTIUM_III		3	/* MMX, CMOV, SSE; any P6 core and Itanium x86 */
#define	CPU_PENTIUM_4		4	/* MMX, CMOV, SSE, SSE2; any P8 core */
#define CPU_K6_2		5	/* MMX, 3DNOW; K6-2/K6-III */
#define CPU_ATHLON		6	/* MMX, 3DNOW, AMD 3DNOW ext, CMOV, SSE int; K7 core */
#define CPU_CYRIX_MII		7	/* MMX, CMOV */
#define CPU_CYRIX_III		8	/* MMX, Cyrix MMX ext, 3DNOW, CMOV */
/* AMD Palomino? MMX, 3DNOW, AMD 3DNOW ext, CMOV, SSE int, SSE flt; ? */
/* AMD Hammer family, presumably Athlon + SSE2; K8 core */

extern int cpu_detection(void);

#if __GNUC__ == 3
# if __GNUC_MINOR__ > 0
#  warning Compilation with your version of gcc is untested,
#  warning may fail or create incorrect code.
# endif
# define FPU_REGS , "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)"
# define SECTION(x) section (x), 
# define emms() do { asm volatile ("\temms\n" ::: "cc" FPU_REGS); } while(0)
#elif __GNUC__ == 2
# if __GNUC_MINOR__ < 90 /* gcc [2.7.2.3] */
#  define FPU_REGS
#  define SECTION(x)
#  define emms() asm("\temms\n")
#  define __builtin_expect(exp, c) (exp)
# elif __GNUC_MINOR__ < 95 /* egcs [2.91.66] */
#  define FPU_REGS
#  define SECTION(x)
#  define emms() asm("\temms\n")
#  define __builtin_expect(exp, c) (exp)
# else /* egcs [2.95.2] */
#  define FPU_REGS , "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)"
#  define SECTION(x) section (x), 
#  define emms() do { asm volatile ("\temms\n" ::: "cc" FPU_REGS); } while(0)
#  define __builtin_expect(exp, c) (exp)
# endif
#else
# error Sorry, your GCC does not exist.
#endif

#define CACHE_LINE 32 // power of two

static inline const unsigned int
swab32(unsigned int x)
{
	if (__builtin_constant_p(x))
		return (((x) & 0xFFUL) << 24) | (((x) & 0xFF00UL) << 8)
			| (((x) & 0xFF0000UL) >> 8) | (((x) & 0xFF000000UL) >> 24);

	asm volatile ("bswap %0" : "=r" (x) : "0" (x));

	return x;
}

static inline const unsigned short
swab16(unsigned short x)
{
	if (__builtin_constant_p(x))
		return (((x) & 0xFFUL) << 8) | (((x) & 0xFF00UL) >> 8);

	asm volatile ("xchgb %b0,%h0" : "=q" (x) : "0" (x));

	return x;
}

#endif // MMX_H

--- NEW FILE ---
/*
 *  MPEG Real Time Encoder
 *  Simple Profiling
 *
 *  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: profile.c,v 1.1 2001/12/04 23:56:43 mswitch Exp $ */

#include "../site_def.h"

#if PROFILING

#include <stdio.h>

typedef long long int tsc_t;

// Keep in mind TSC counts real time, not process time

tsc_t rdtsc(void)
{
    tsc_t tsc;

    asm ("\trdtsc\n" : "=A" (tsc));

    return tsc;
}                                                                                         

#define COUNTERS 80

static char *labels[COUNTERS];
static tsc_t start[COUNTERS];
static long long sum[COUNTERS];
static int count[COUNTERS];

void
pr_start(int n, char *label)
{
	if (n > COUNTERS)
		return;

	labels[n] = label;
	start[n] = rdtsc();
}

void
pr_event(int n, char *label)
{
	if (n > COUNTERS)
		return;

	labels[n] = label;
	count[n]++;
}

void
pr_end(int n)
{
	if (n > COUNTERS)
		return;

	sum[n] += rdtsc() - start[n];
	count[n]++;
}

void
pr_report(void)
{
	int i;

	for (i = 0; i < COUNTERS; i++)
		if (count[i]) {
			if (sum[i] > 0)
				fprintf(stderr, "%25s %02d: %10lld cycles %8d iterations (%4lld %06lld)\n",
					labels[i], i, sum[i] / count[i], count[i], sum[i] / 1000000, sum[i] % 1000000);
			else
				fprintf(stderr, "%25s %02d:                   %8d iterations\n", labels[i], i, count[i]);
		}
}

#endif /* PROFILING */

--- 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.
 */

#include "../site_def.h"

#if PROFILING

extern void pr_start(int n, char *label);
extern void pr_end(int n);
extern void pr_event(int n, char *label);
extern void pr_report(void);

#else

#define pr_start(n, label)
#define pr_end(n)
#define pr_event(n, label)
#define pr_report()

#endif

--- 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: sync.c,v 1.1 2001/12/04 23:56:43 mswitch Exp $ */

#include "../common/log.h"
#include "sync.h"
#include "math.h"

struct synchr synchr;

void
mp1e_sync_init(unsigned int modules, unsigned int time_base)
{
	mucon_init(&synchr.mucon);

	synchr.start_time = DBL_MAX;
	synchr.stop_time = DBL_MAX;

	synchr.front_time = -1;

	synchr.modules = modules;
	synchr.vote = 0;

	assert(popcnt(time_base) <= 1);

	synchr.time_base = time_base;

	synchr.ref_warp = 1.0;
}

/**
 * mp1e_sync_start:
 * @time: 
 * 
 * Trigger initial synchronization at @time.
 * 
 * Return value: 
 **/
bool
mp1e_sync_start(double time)
{
	pthread_mutex_lock(&synchr.mucon.mutex);

	if (synchr.modules == synchr.vote) {
		pthread_mutex_unlock(&synchr.mucon.mutex);
		return FALSE;
	}

	synchr.start_time = time;

	pthread_cond_broadcast(&synchr.mucon.cond);

	pthread_mutex_unlock(&synchr.mucon.mutex);

	return TRUE;
}

/**
 * mp1e_sync_stop:
 * @time: 
 * 
 * Stop encoding at @time.
 * 
 * Return value: 
 **/
bool
mp1e_sync_stop(double time)
{
	pthread_mutex_lock(&synchr.mucon.mutex);

	if (synchr.modules != synchr.vote ||
	    synchr.stop_time < DBL_MAX) {
		pthread_mutex_unlock(&synchr.mucon.mutex);
		return FALSE;
	}

	synchr.stop_time = MAX(time, synchr.front_time);

	printv(4, "sync_stop at %f\n", synchr.stop_time);

	pthread_mutex_unlock(&synchr.mucon.mutex);

	return TRUE;
}

/**
 * mp1e_sync_run_in:
 * @str: 
 * @c: 
 * @frame_frac: 
 * 
 * Initial synchronization for *encoding modules*.
 * 
 * Return value: 
 **/
bool
mp1e_sync_run_in(synchr_stream *str, consumer *c, int *frame_frac)
{
	double first_time, last_time = -1;
	double frame_period;
	buffer *b;

	c->fifo->start(c->fifo);

	b = wait_full_buffer(c);

	if (b->used <= 0)
		FAIL("Premature end of file (%s)", c->fifo->name);

	pthread_mutex_lock(&synchr.mucon.mutex);

	first_time = b->time;

	if (frame_frac)
		*frame_frac = 0;

	for (;;) {
		if (b->time == 0.0) { // offline
			if (synchr.start_time < DBL_MAX) {
				printv(4, "SRI %02x: accept start_time %f for %f, voted %02x/%02x\n",
					str->this_module, synchr.start_time, b->time,
					synchr.vote, synchr.modules);
				if ((synchr.vote |= str->this_module) == synchr.modules)
					break;
				pthread_cond_broadcast(&synchr.mucon.cond);
			}
			pthread_cond_wait(&synchr.mucon.cond, &synchr.mucon.mutex);
			continue;
		}

		// XXX return FALSE
		if (0) // because rte sends in duplicate b->time, but that's ok.
		if (b->time <= last_time)
			FAIL("Invalid timestamps from %s: ..., %f, %f\n",
				c->fifo->name, last_time, b->time);
		if ((b->time - first_time) > 2.0)
			FAIL("Unable to sync %s after %f secs\n",
				c->fifo->name, b->time - first_time);

		last_time = b->time;

		if (synchr.start_time < b->time) {
			printv(4, "SRI %02x: propose start_time %f, was %f\n",
				str->this_module, b->time, synchr.start_time);
			synchr.start_time = b->time;
			synchr.vote = str->this_module;
			if (str->this_module == synchr.modules)
				break;
			pthread_cond_broadcast(&synchr.mucon.cond);
			pthread_cond_wait(&synchr.mucon.cond, &synchr.mucon.mutex);
			continue;
		}

		frame_period = str->frame_period + b->used * str->byte_period;

		if (synchr.start_time < b->time + frame_period) {
			printv(4, "SRI %02x: accept start_time %f for %f, voted %02x/%02x\n",
				str->this_module, synchr.start_time, b->time,
				synchr.vote, synchr.modules);
			if ((synchr.vote |= str->this_module) == synchr.modules) {
				if (frame_frac && str->byte_period > 0.0) {
					double tfrac = synchr.start_time - b->time;
					int sfrac = tfrac / str->byte_period;

					*frame_frac = saturate(sfrac & -str->bytes_per_sample, 0, b->used - 1);
				}

				break;
			}
		}

		printv(4, "SRI %02x: disagree start_time %f, discard %f\n",
			str->this_module, synchr.start_time, b->time);

		synchr.vote &= ~str->this_module;

		pthread_mutex_unlock(&synchr.mucon.mutex);

		send_empty_buffer(c, b);

		b = wait_full_buffer(c);

		if (b->used <= 0)
			FAIL("Capture failure");

		pthread_mutex_lock(&synchr.mucon.mutex);
	}

	str->start_ref = b->time;

	pthread_mutex_unlock(&synchr.mucon.mutex);
	pthread_cond_broadcast(&synchr.mucon.cond);

	unget_full_buffer(c, b);

	return TRUE;
}

--- 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: sync.h,v 1.1 2001/12/04 23:56:43 mswitch Exp $ */

#ifndef SYNC_H
#define SYNC_H

#include "threads.h"
#include "fifo.h"
#include "log.h"

typedef unsigned int sync_module;

struct synchr {
	mucon			mucon;

	double			start_time;
	double			stop_time;
	double			front_time;

	sync_module		modules;
	sync_module		vote;

	double			ref_warp;
	sync_module		time_base;
};

typedef struct synchr_stream {
	sync_module		this_module;

	double			start_ref;

	double			byte_period;
	double			frame_period;

	int			bytes_per_sample;	/* power of 2 */
} synchr_stream;

/* GGG */
extern struct synchr synchr;

extern void	mp1e_sync_init(unsigned int modules, unsigned int time_base);
extern bool	mp1e_sync_start(double time);
extern bool	mp1e_sync_stop(double time);
extern bool	mp1e_sync_run_in(synchr_stream *str, consumer *c, int *frame_frac);

static inline int
mp1e_sync_break(synchr_stream *str, double time)
{
	pthread_mutex_lock(&synchr.mucon.mutex);

	if (time >= synchr.stop_time) {
		pthread_mutex_unlock(&synchr.mucon.mutex);

		printv(4, "sync_break %08x, %f, stop_time %f\n",
		       str->this_module, time, synchr.stop_time);

		return TRUE;
	}

	if (time > synchr.front_time)
		synchr.front_time = time;

	pthread_mutex_unlock(&synchr.mucon.mutex);

	return FALSE;
}

static inline double
mp1e_sync_drift(synchr_stream *str, double time, double elapsed)
{
	double drift;

	pthread_mutex_lock(&synchr.mucon.mutex);

	printv(4, "SD%02d %f i%f o%f d%f\n", str->this_module,
	       time, (time - str->start_ref), elapsed,
	       (time - str->start_ref) - elapsed);

	if (str->this_module == synchr.time_base) {
		if (elapsed >= 0.5)
			synchr.ref_warp = (time - str->start_ref) / elapsed;

		drift = 0.0;

		printv(4, "SD%02d ref_warp %f, %f s\n",
		       str->this_module, synchr.ref_warp,
		       (time - str->start_ref) - elapsed);
	} else {
		double ref_time = (time - str->start_ref) * synchr.ref_warp;

		drift = elapsed - ref_time;

		printv(4, "SD%02d ref %f o%f, drift %f s, %f ppm, %f units\n",
		       str->this_module, ref_time, elapsed, drift,
		       elapsed * 1e6 / ref_time - 1e6,
		       drift / (str->frame_period + str->byte_period));
	}

	pthread_mutex_unlock(&synchr.mucon.mutex);

	return drift;
}

#endif /* SYNC_H */

--- NEW FILE ---
/*
 *  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: threads.h,v 1.1 2001/12/04 23:56:43 mswitch Exp $ */

#ifndef THREADS_H
#define THREADS_H

#undef _GNU_SOURCE
#define _GNU_SOURCE 1
/* XXX for rwlocks, but the parent file
   may have included pthread.h already */

#include <pthread.h>
#include "list.h"

typedef struct {
	pthread_mutex_t		mutex;		/* attn: fast mutex */
	pthread_cond_t		cond;
	list			list;
} mucon;

static inline void
mucon_init(mucon *m)
{
	pthread_mutex_init(&m->mutex, NULL);
	pthread_cond_init(&m->cond, NULL);
	init_list(&m->list);
}

static inline void
mucon_destroy(mucon *m)
{
	pthread_mutex_destroy(&m->mutex);
	pthread_cond_destroy(&m->cond);
}

static inline void
wait_mucon(mucon *m)
{
	pthread_mutex_lock(&m->mutex);
	pthread_cond_wait(&m->cond, &m->mutex);
	pthread_mutex_unlock(&m->mutex);
}

#endif /* THREADS_H */

--- NEW FILE ---
/*
 *  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: types.h,v 1.1 2001/12/04 23:56:43 mswitch Exp $ */

#ifndef TYPES_H
#define TYPES_H

#include <stddef.h>

#undef TRUE
#undef FALSE

enum { FALSE, TRUE };

typedef unsigned char bool;

#define PARENT(ptr, type, member) \
	((type *)(((char *) ptr) - offsetof(type, member)))

/*
 *  Same as libc assert, but also reports the caller.
 */
#ifdef	NDEBUG
# define asserts(expr) ((void) 0)
#else
extern void asserts_fail(char *assertion, char *file, unsigned int line, char *function, void *caller);
#define asserts(expr)							\
	((void) ((expr) ? 0 : asserts_fail(#expr, __FILE__, __LINE__,	\
		__PRETTY_FUNCTION__, __builtin_return_address(0))))
#endif

#endif /* TYPES_H */




More information about the MPlayer-cvslog mailing list