/* * 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 #include #include #include #include #include #include #include #include #include #include #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;inb_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;inb_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;istreams[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, };