[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
- Previous message: [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
- Next message: [Mplayer-cvslog] CVS: main/libmp1e/video dct.c,NONE,1.1 dct.h,NONE,1.1 dct_ieee.h,NONE,1.1 dct_mmx.s,NONE,1.1 dct_ref.c,NONE,1.1 filter.c,NONE,1.1 filter_mmx.s,NONE,1.1 libvideo.h,NONE,1.1 mblock.c,NONE,1.1 mblock.h,NONE,1.1 motion.c,NONE,1.1 motion.h,NONE,1.1 motion_mmx.s,NONE,1.1 motion_sse2.s,NONE,1.1 mpeg.h,NONE,1.1 mpeg1.c,NONE,1.1 tables.c,NONE,1.1 video.h,NONE,1.1 vlc.c,NONE,1.1 vlc.h,NONE,1.1 vlc_mmx.s,NONE,1.1
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
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;
}
- Previous message: [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
- Next message: [Mplayer-cvslog] CVS: main/libmp1e/video dct.c,NONE,1.1 dct.h,NONE,1.1 dct_ieee.h,NONE,1.1 dct_mmx.s,NONE,1.1 dct_ref.c,NONE,1.1 filter.c,NONE,1.1 filter_mmx.s,NONE,1.1 libvideo.h,NONE,1.1 mblock.c,NONE,1.1 mblock.h,NONE,1.1 motion.c,NONE,1.1 motion.h,NONE,1.1 motion_mmx.s,NONE,1.1 motion_sse2.s,NONE,1.1 mpeg.h,NONE,1.1 mpeg1.c,NONE,1.1 tables.c,NONE,1.1 video.h,NONE,1.1 vlc.c,NONE,1.1 vlc.h,NONE,1.1 vlc_mmx.s,NONE,1.1
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
More information about the MPlayer-cvslog
mailing list