1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-12-23 12:43:46 +02:00
FFmpeg/mpegmux.c
Fabrice Bellard 9aeeeb63f7 Initial revision
Originally committed as revision 2 to svn://svn.ffmpeg.org/ffmpeg/trunk
2000-12-20 00:02:47 +00:00

302 lines
8.5 KiB
C

/*
* Output a MPEG1 multiplexed video/audio stream
* Copyright (c) 2000 Gerard Lantau.
*
* 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 <stdlib.h>
#include <stdio.h>
#include <netinet/in.h>
#include <linux/videodev.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <errno.h>
#include <sys/time.h>
#include <getopt.h>
#include "mpegenc.h"
#include "mpegvideo.h"
#include "mpegaudio.h"
#define MAX_PAYLOAD_SIZE 4096
#define NB_STREAMS 2
typedef struct {
UINT8 buffer[MAX_PAYLOAD_SIZE];
int buffer_ptr;
UINT8 id;
int max_buffer_size;
int packet_number;
AVEncodeContext *enc;
float pts;
} StreamInfo;
typedef struct {
int packet_size; /* required packet size */
int packet_number;
int pack_header_freq; /* frequency (in packets^-1) at which we send pack headers */
int system_header_freq;
int mux_rate; /* bitrate in units of 50 bytes/s */
/* stream info */
int nb_streams;
StreamInfo streams[NB_STREAMS];
AVFormatContext *ctx;
} MpegMuxContext;
#define PACK_START_CODE ((unsigned int)0x000001ba)
#define SYSTEM_HEADER_START_CODE ((unsigned int)0x000001bb)
#define PACKET_START_CODE_MASK ((unsigned int)0xffffff00)
#define PACKET_START_CODE_PREFIX ((unsigned int)0x00000100)
#define ISO_11172_END_CODE ((unsigned int)0x000001b9)
#define AUDIO_ID 0xc0
#define VIDEO_ID 0xe0
static int put_pack_header(MpegMuxContext *s, UINT8 *buf, long long timestamp)
{
PutBitContext pb;
init_put_bits(&pb, buf, 128, NULL, NULL);
put_bits(&pb, 32, PACK_START_CODE);
put_bits(&pb, 4, 0x2);
put_bits(&pb, 3, (timestamp >> 30) & 0x07);
put_bits(&pb, 1, 1);
put_bits(&pb, 15, (timestamp >> 15) & 0x7fff);
put_bits(&pb, 1, 1);
put_bits(&pb, 15, (timestamp) & 0x7fff);
put_bits(&pb, 1, 1);
put_bits(&pb, 1, 1);
put_bits(&pb, 22, s->mux_rate);
flush_put_bits(&pb);
return pb.buf_ptr - pb.buf;
}
static int put_system_header(MpegMuxContext *s, UINT8 *buf)
{
int audio_bound, video_bound;
int size, rate_bound, i;
PutBitContext pb;
init_put_bits(&pb, buf, 128, NULL, NULL);
put_bits(&pb, 32, SYSTEM_HEADER_START_CODE);
put_bits(&pb, 16, 0);
put_bits(&pb, 1, 1);
rate_bound = s->mux_rate; /* maximum bit rate of the multiplexed stream */
put_bits(&pb, 22, rate_bound);
put_bits(&pb, 1, 1); /* marker */
audio_bound = 1; /* at most one audio stream */
put_bits(&pb, 6, audio_bound);
put_bits(&pb, 1, 0); /* variable bitrate */
put_bits(&pb, 1, 0); /* non constrainted bit stream */
put_bits(&pb, 1, 1); /* audio locked */
put_bits(&pb, 1, 1); /* video locked */
put_bits(&pb, 1, 1); /* marker */
video_bound = 1; /* at most one video stream */
put_bits(&pb, 5, video_bound);
put_bits(&pb, 8, 0xff); /* reserved byte */
/* audio stream info */
for(i=0;i<s->nb_streams;i++) {
put_bits(&pb, 8, s->streams[i].id); /* stream ID */
put_bits(&pb, 2, 3);
put_bits(&pb, 1, 1); /* buffer bound scale = 1024 */
put_bits(&pb, 13, s->streams[i].max_buffer_size); /* max buffer size */
}
/* no more streams */
put_bits(&pb, 1, 0);
flush_put_bits(&pb);
size = pb.buf_ptr - pb.buf;
/* patch packet size */
buf[4] = (size - 6) >> 8;
buf[5] = (size - 6) & 0xff;
return size;
}
/* Format a packet header for a total size of 'total_size'. Return the
header size */
static int put_packet_header(MpegMuxContext *s,
int id, long long timestamp,
UINT8 *buffer, int total_size)
{
UINT8 *buf_ptr;
PutBitContext pb;
int size, payload_size;
#if 0
printf("packet ID=%2x PTS=%0.3f size=%d\n",
id, timestamp / 90000.0, total_size);
#endif
buf_ptr = buffer;
if ((s->packet_number % s->pack_header_freq) == 0) {
/* output pack and systems header */
size = put_pack_header(s, buf_ptr, timestamp);
buf_ptr += size;
if ((s->packet_number % s->system_header_freq) == 0) {
size = put_system_header(s, buf_ptr);
buf_ptr += size;
}
}
payload_size = total_size - ((buf_ptr - buffer) + 6 + 5);
/* packet header */
init_put_bits(&pb, buf_ptr, 128, NULL, NULL);
put_bits(&pb, 32, PACKET_START_CODE_PREFIX + id);
put_bits(&pb, 16, payload_size + 5);
/* presentation time stamp */
put_bits(&pb, 4, 0x02);
put_bits(&pb, 3, (timestamp >> 30) & 0x07);
put_bits(&pb, 1, 1);
put_bits(&pb, 15, (timestamp >> 15) & 0x7fff);
put_bits(&pb, 1, 1);
put_bits(&pb, 15, (timestamp) & 0x7fff);
put_bits(&pb, 1, 1);
flush_put_bits(&pb);
s->packet_number++;
return pb.buf_ptr - buffer;
}
int mpeg_mux_init(AVFormatContext *ctx)
{
MpegMuxContext *s;
int bitrate, i;
s = malloc(sizeof(MpegMuxContext));
if (!s)
return -1;
memset(s, 0, sizeof(MpegMuxContext));
ctx->priv_data = s;
s->ctx = ctx;
s->packet_number = 0;
/* XXX: hardcoded */
s->packet_size = 2048;
s->nb_streams = 2;
s->streams[0].id = AUDIO_ID;
s->streams[0].max_buffer_size = 10; /* in KBytes */
s->streams[0].enc = ctx->audio_enc;
s->streams[1].id = VIDEO_ID;
s->streams[1].max_buffer_size = 50; /* in KBytes */
s->streams[1].enc = ctx->video_enc;
/* we increase slightly the bitrate to take into account the
headers. XXX: compute it exactly */
bitrate = 2000;
for(i=0;i<s->nb_streams;i++) {
bitrate += s->streams[i].enc->bit_rate;
}
s->mux_rate = (bitrate + (8 * 50) - 1) / (8 * 50);
/* every 2 seconds */
s->pack_header_freq = 2 * bitrate / s->packet_size / 8;
/* every 10 seconds */
s->system_header_freq = s->pack_header_freq * 5;
for(i=0;i<NB_STREAMS;i++) {
s->streams[i].buffer_ptr = 0;
s->streams[i].packet_number = 0;
s->streams[i].pts = 0;
}
return 0;
}
int mpeg_mux_end(AVFormatContext *ctx)
{
PutBitContext pb;
UINT8 buffer[128];
/* write the end header */
init_put_bits(&pb, buffer, sizeof(buffer), NULL, NULL);
put_bits(&pb, 32, ISO_11172_END_CODE);
put_buffer(&ctx->pb, buffer, pb.buf_ptr - buffer);
put_flush_packet(&ctx->pb);
return 0;
}
static void write_stream(MpegMuxContext *s, StreamInfo *stream, UINT8 *buf, int size)
{
int len, len1, header_size;
long long pts;
while (size > 0) {
if (stream->buffer_ptr == 0) {
pts = stream->pts * 90000.0;
header_size = put_packet_header(s, stream->id, pts, stream->buffer, s->packet_size);
stream->buffer_ptr = header_size;
}
len = size;
len1 = s->packet_size - stream->buffer_ptr;
if (len > len1)
len = len1;
memcpy(stream->buffer + stream->buffer_ptr, buf, len);
stream->buffer_ptr += len;
if (stream->buffer_ptr == s->packet_size) {
/* output the packet */
put_buffer(&s->ctx->pb, stream->buffer, s->packet_size);
put_flush_packet(&s->ctx->pb);
stream->buffer_ptr = 0;
stream->packet_number++;
}
buf += len;
size -= len;
}
}
static int mpeg_mux_write_audio(AVFormatContext *ctx, UINT8 *buf, int size)
{
MpegMuxContext *s = ctx->priv_data;
write_stream(s, &s->streams[0], buf, size);
s->streams[0].pts += (float)s->streams[0].enc->frame_size / s->streams[0].enc->rate;
return 0;
}
int mpeg_mux_write_video(AVFormatContext *ctx, UINT8 *buf, int size)
{
MpegMuxContext *s = ctx->priv_data;
write_stream(s, &s->streams[1], buf, size);
s->streams[1].pts += 1.0 / (float)s->streams[1].enc->rate;
return 0;
}
AVFormat mpeg_mux_format = {
"mpeg1",
"MPEG1 multiplex format",
"video/mpeg",
"mpg,mpeg",
CODEC_ID_MP2,
CODEC_ID_MPEG1VIDEO,
mpeg_mux_init,
mpeg_mux_write_audio,
mpeg_mux_write_video,
mpeg_mux_end,
};