X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=synfig-core%2Ftags%2Fsynfig_0_61_05%2Fsynfig-core%2Fsrc%2Fmodules%2Fmod_libavcodec%2Flibavformat%2Fmpeg.c;fp=synfig-core%2Ftags%2Fsynfig_0_61_05%2Fsynfig-core%2Fsrc%2Fmodules%2Fmod_libavcodec%2Flibavformat%2Fmpeg.c;h=98103a6b285c4dfc14fe8947b99842185d0522ca;hb=299aecad571ca490ce017004a0d7e555d6df0520;hp=0000000000000000000000000000000000000000;hpb=42861dc634bef4059ca95e5292033315a0b9ce30;p=synfig.git diff --git a/synfig-core/tags/synfig_0_61_05/synfig-core/src/modules/mod_libavcodec/libavformat/mpeg.c b/synfig-core/tags/synfig_0_61_05/synfig-core/src/modules/mod_libavcodec/libavformat/mpeg.c new file mode 100644 index 0000000..98103a6 --- /dev/null +++ b/synfig-core/tags/synfig_0_61_05/synfig-core/src/modules/mod_libavcodec/libavformat/mpeg.c @@ -0,0 +1,710 @@ +/* + * MPEG1/2 mux/demux + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "avformat.h" + +#define MAX_PAYLOAD_SIZE 4096 +#define NB_STREAMS 2 + +typedef struct { + uint8_t buffer[MAX_PAYLOAD_SIZE]; + int buffer_ptr; + uint8_t id; + int max_buffer_size; /* in bytes */ + int packet_number; + int64_t start_pts; +} StreamInfo; + +typedef struct { + int packet_size; /* required packet size */ + int packet_data_max_size; /* maximum data size inside a packet */ + 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 audio_bound; + int video_bound; + int is_mpeg2; + int is_vcd; +} MpegMuxContext; + +#define PACK_START_CODE ((unsigned int)0x000001ba) +#define SYSTEM_HEADER_START_CODE ((unsigned int)0x000001bb) +#define SEQUENCE_END_CODE ((unsigned int)0x000001b7) +#define PACKET_START_CODE_MASK ((unsigned int)0xffffff00) +#define PACKET_START_CODE_PREFIX ((unsigned int)0x00000100) +#define ISO_11172_END_CODE ((unsigned int)0x000001b9) + +/* mpeg2 */ +#define PROGRAM_STREAM_MAP 0x1bc +#define PRIVATE_STREAM_1 0x1bd +#define PADDING_STREAM 0x1be +#define PRIVATE_STREAM_2 0x1bf + + +#define AUDIO_ID 0xc0 +#define VIDEO_ID 0xe0 + +extern AVOutputFormat mpeg1system_mux; +extern AVOutputFormat mpeg1vcd_mux; +extern AVOutputFormat mpeg2vob_mux; + +static int put_pack_header(AVFormatContext *ctx, + uint8_t *buf, int64_t timestamp) +{ + MpegMuxContext *s = ctx->priv_data; + PutBitContext pb; + + init_put_bits(&pb, buf, 128, NULL, NULL); + + put_bits(&pb, 32, PACK_START_CODE); + if (s->is_mpeg2) { + put_bits(&pb, 2, 0x1); + } else { + put_bits(&pb, 4, 0x2); + } + put_bits(&pb, 3, (uint32_t)((timestamp >> 30) & 0x07)); + put_bits(&pb, 1, 1); + put_bits(&pb, 15, (uint32_t)((timestamp >> 15) & 0x7fff)); + put_bits(&pb, 1, 1); + put_bits(&pb, 15, (uint32_t)((timestamp) & 0x7fff)); + put_bits(&pb, 1, 1); + if (s->is_mpeg2) { + /* clock extension */ + put_bits(&pb, 9, 0); + put_bits(&pb, 1, 1); + } + put_bits(&pb, 1, 1); + put_bits(&pb, 22, s->mux_rate); + put_bits(&pb, 1, 1); + if (s->is_mpeg2) { + put_bits(&pb, 5, 0x1f); /* reserved */ + put_bits(&pb, 3, 0); /* stuffing length */ + } + flush_put_bits(&pb); + return pbBufPtr(&pb) - pb.buf; +} + +static int put_system_header(AVFormatContext *ctx, uint8_t *buf) +{ + MpegMuxContext *s = ctx->priv_data; + int size, rate_bound, i, private_stream_coded, id; + 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 */ + put_bits(&pb, 6, s->audio_bound); + + put_bits(&pb, 1, 1); /* variable bitrate */ + put_bits(&pb, 1, 1); /* non constrainted bit stream */ + + put_bits(&pb, 1, 0); /* audio locked */ + put_bits(&pb, 1, 0); /* video locked */ + put_bits(&pb, 1, 1); /* marker */ + + put_bits(&pb, 5, s->video_bound); + put_bits(&pb, 8, 0xff); /* reserved byte */ + + /* audio stream info */ + private_stream_coded = 0; + for(i=0;inb_streams;i++) { + StreamInfo *stream = ctx->streams[i]->priv_data; + id = stream->id; + if (id < 0xc0) { + /* special case for private streams (AC3 use that) */ + if (private_stream_coded) + continue; + private_stream_coded = 1; + id = 0xbd; + } + put_bits(&pb, 8, id); /* stream ID */ + put_bits(&pb, 2, 3); + if (id < 0xe0) { + /* audio */ + put_bits(&pb, 1, 0); + put_bits(&pb, 13, stream->max_buffer_size / 128); + } else { + /* video */ + put_bits(&pb, 1, 1); + put_bits(&pb, 13, stream->max_buffer_size / 1024); + } + } + flush_put_bits(&pb); + size = pbBufPtr(&pb) - pb.buf; + /* patch packet size */ + buf[4] = (size - 6) >> 8; + buf[5] = (size - 6) & 0xff; + + return size; +} + +static int mpeg_mux_init(AVFormatContext *ctx) +{ + MpegMuxContext *s = ctx->priv_data; + int bitrate, i, mpa_id, mpv_id, ac3_id; + AVStream *st; + StreamInfo *stream; + + s->packet_number = 0; + s->is_vcd = (ctx->oformat == &mpeg1vcd_mux); + s->is_mpeg2 = (ctx->oformat == &mpeg2vob_mux); + + if (s->is_vcd) + s->packet_size = 2324; /* VCD packet size */ + else + s->packet_size = 2048; + + /* startcode(4) + length(2) + flags(1) */ + s->packet_data_max_size = s->packet_size - 7; + s->audio_bound = 0; + s->video_bound = 0; + mpa_id = AUDIO_ID; + ac3_id = 0x80; + mpv_id = VIDEO_ID; + for(i=0;inb_streams;i++) { + st = ctx->streams[i]; + stream = av_mallocz(sizeof(StreamInfo)); + if (!stream) + goto fail; + st->priv_data = stream; + + switch(st->codec.codec_type) { + case CODEC_TYPE_AUDIO: + if (st->codec.codec_id == CODEC_ID_AC3) + stream->id = ac3_id++; + else + stream->id = mpa_id++; + stream->max_buffer_size = 4 * 1024; + s->audio_bound++; + break; + case CODEC_TYPE_VIDEO: + stream->id = mpv_id++; + stream->max_buffer_size = 46 * 1024; + s->video_bound++; + break; + default: + av_abort(); + } + } + + /* we increase slightly the bitrate to take into account the + headers. XXX: compute it exactly */ + bitrate = 2000; + for(i=0;inb_streams;i++) { + st = ctx->streams[i]; + bitrate += st->codec.bit_rate; + } + s->mux_rate = (bitrate + (8 * 50) - 1) / (8 * 50); + + if (s->is_vcd || s->is_mpeg2) + /* every packet */ + s->pack_header_freq = 1; + else + /* every 2 seconds */ + s->pack_header_freq = 2 * bitrate / s->packet_size / 8; + + if (s->is_mpeg2) + /* every 200 packets. Need to look at the spec. */ + s->system_header_freq = s->pack_header_freq * 40; + else if (s->is_vcd) + /* every 40 packets, this is my invention */ + s->system_header_freq = s->pack_header_freq * 40; + else + s->system_header_freq = s->pack_header_freq * 5; + + for(i=0;inb_streams;i++) { + stream = ctx->streams[i]->priv_data; + stream->buffer_ptr = 0; + stream->packet_number = 0; + stream->start_pts = -1; + } + return 0; + fail: + for(i=0;inb_streams;i++) { + av_free(ctx->streams[i]->priv_data); + } + return -ENOMEM; +} + +/* flush the packet on stream stream_index */ +static void flush_packet(AVFormatContext *ctx, int stream_index) +{ + MpegMuxContext *s = ctx->priv_data; + StreamInfo *stream = ctx->streams[stream_index]->priv_data; + uint8_t *buf_ptr; + int size, payload_size, startcode, id, len, stuffing_size, i, header_len; + int64_t timestamp; + uint8_t buffer[128]; + + id = stream->id; + timestamp = stream->start_pts; + +#if 0 + printf("packet ID=%2x PTS=%0.3f\n", + id, timestamp / 90000.0); +#endif + + buf_ptr = buffer; + if (((s->packet_number % s->pack_header_freq) == 0)) { + /* output pack and systems header if needed */ + size = put_pack_header(ctx, buf_ptr, timestamp); + buf_ptr += size; + if ((s->packet_number % s->system_header_freq) == 0) { + size = put_system_header(ctx, buf_ptr); + buf_ptr += size; + } + } + size = buf_ptr - buffer; + put_buffer(&ctx->pb, buffer, size); + + /* packet header */ + if (s->is_mpeg2) { + header_len = 8; + } else { + header_len = 5; + } + payload_size = s->packet_size - (size + 6 + header_len); + if (id < 0xc0) { + startcode = PRIVATE_STREAM_1; + payload_size -= 4; + } else { + startcode = 0x100 + id; + } + stuffing_size = payload_size - stream->buffer_ptr; + if (stuffing_size < 0) + stuffing_size = 0; + + put_be32(&ctx->pb, startcode); + + put_be16(&ctx->pb, payload_size + header_len); + /* stuffing */ + for(i=0;ipb, 0xff); + + if (s->is_mpeg2) { + put_byte(&ctx->pb, 0x80); /* mpeg2 id */ + put_byte(&ctx->pb, 0x80); /* flags */ + put_byte(&ctx->pb, 0x05); /* header len (only pts is included) */ + } + put_byte(&ctx->pb, + (0x02 << 4) | + (((timestamp >> 30) & 0x07) << 1) | + 1); + put_be16(&ctx->pb, (uint16_t)((((timestamp >> 15) & 0x7fff) << 1) | 1)); + put_be16(&ctx->pb, (uint16_t)((((timestamp) & 0x7fff) << 1) | 1)); + + if (startcode == PRIVATE_STREAM_1) { + put_byte(&ctx->pb, id); + if (id >= 0x80 && id <= 0xbf) { + /* XXX: need to check AC3 spec */ + put_byte(&ctx->pb, 1); + put_byte(&ctx->pb, 0); + put_byte(&ctx->pb, 2); + } + } + + /* output data */ + put_buffer(&ctx->pb, stream->buffer, payload_size - stuffing_size); + put_flush_packet(&ctx->pb); + + /* preserve remaining data */ + len = stream->buffer_ptr - payload_size; + if (len < 0) + len = 0; + memmove(stream->buffer, stream->buffer + stream->buffer_ptr - len, len); + stream->buffer_ptr = len; + + s->packet_number++; + stream->packet_number++; + stream->start_pts = -1; +} + +static int mpeg_mux_write_packet(AVFormatContext *ctx, int stream_index, + const uint8_t *buf, int size, int64_t pts) +{ + MpegMuxContext *s = ctx->priv_data; + AVStream *st = ctx->streams[stream_index]; + StreamInfo *stream = st->priv_data; + int len; + + while (size > 0) { + /* set pts */ + if (stream->start_pts == -1) { + stream->start_pts = pts; + } + len = s->packet_data_max_size - stream->buffer_ptr; + if (len > size) + len = size; + memcpy(stream->buffer + stream->buffer_ptr, buf, len); + stream->buffer_ptr += len; + buf += len; + size -= len; + while (stream->buffer_ptr >= s->packet_data_max_size) { + /* output the packet */ + if (stream->start_pts == -1) + stream->start_pts = pts; + flush_packet(ctx, stream_index); + } + } + return 0; +} + +static int mpeg_mux_end(AVFormatContext *ctx) +{ + StreamInfo *stream; + int i; + + /* flush each packet */ + for(i=0;inb_streams;i++) { + stream = ctx->streams[i]->priv_data; + if (stream->buffer_ptr > 0) { + flush_packet(ctx, i); + } + } + + /* End header according to MPEG1 systems standard. We do not write + it as it is usually not needed by decoders and because it + complicates MPEG stream concatenation. */ + //put_be32(&ctx->pb, ISO_11172_END_CODE); + //put_flush_packet(&ctx->pb); + + for(i=0;inb_streams;i++) + av_freep(&ctx->streams[i]->priv_data); + + return 0; +} + +/*********************************************/ +/* demux code */ + +#define MAX_SYNC_SIZE 100000 + +static int mpegps_probe(AVProbeData *p) +{ + int code, c, i; + + code = 0xff; + /* we search the first start code. If it is a packet start code, + then we decide it is mpeg ps. We do not send highest value to + give a chance to mpegts */ + /* NOTE: the search range was restricted to avoid too many false + detections */ + + if (p->buf_size < 6) + return 0; + + for (i = 0; i < 20; i++) { + c = p->buf[i]; + code = (code << 8) | c; + if ((code & 0xffffff00) == 0x100) { + if (code == PACK_START_CODE || + code == SYSTEM_HEADER_START_CODE || + (code >= 0x1e0 && code <= 0x1ef) || + (code >= 0x1c0 && code <= 0x1df) || + code == PRIVATE_STREAM_2 || + code == PROGRAM_STREAM_MAP || + code == PRIVATE_STREAM_1 || + code == PADDING_STREAM) + return AVPROBE_SCORE_MAX - 2; + else + return 0; + } + } + return 0; +} + + +typedef struct MpegDemuxContext { + int header_state; +} MpegDemuxContext; + +static int find_start_code(ByteIOContext *pb, int *size_ptr, + uint32_t *header_state) +{ + unsigned int state, v; + int val, n; + + state = *header_state; + n = *size_ptr; + while (n > 0) { + if (url_feof(pb)) + break; + v = get_byte(pb); + n--; + if (state == 0x000001) { + state = ((state << 8) | v) & 0xffffff; + val = state; + goto found; + } + state = ((state << 8) | v) & 0xffffff; + } + val = -1; + found: + *header_state = state; + *size_ptr = n; + return val; +} + +static int mpegps_read_header(AVFormatContext *s, + AVFormatParameters *ap) +{ + MpegDemuxContext *m = s->priv_data; + m->header_state = 0xff; + /* no need to do more */ + return 0; +} + +static int64_t get_pts(ByteIOContext *pb, int c) +{ + int64_t pts; + int val; + + if (c < 0) + c = get_byte(pb); + pts = (int64_t)((c >> 1) & 0x07) << 30; + val = get_be16(pb); + pts |= (int64_t)(val >> 1) << 15; + val = get_be16(pb); + pts |= (int64_t)(val >> 1); + return pts; +} + +static int mpegps_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + MpegDemuxContext *m = s->priv_data; + AVStream *st; + int len, size, startcode, i, c, flags, header_len, type, codec_id; + int64_t pts, dts; + + /* next start code (should be immediately after) */ + redo: + m->header_state = 0xff; + size = MAX_SYNC_SIZE; + startcode = find_start_code(&s->pb, &size, &m->header_state); + //printf("startcode=%x pos=0x%Lx\n", startcode, url_ftell(&s->pb)); + if (startcode < 0) + return -EIO; + if (startcode == PACK_START_CODE) + goto redo; + if (startcode == SYSTEM_HEADER_START_CODE) + goto redo; + if (startcode == PADDING_STREAM || + startcode == PRIVATE_STREAM_2) { + /* skip them */ + len = get_be16(&s->pb); + url_fskip(&s->pb, len); + goto redo; + } + /* find matching stream */ + if (!((startcode >= 0x1c0 && startcode <= 0x1df) || + (startcode >= 0x1e0 && startcode <= 0x1ef) || + (startcode == 0x1bd))) + goto redo; + + len = get_be16(&s->pb); + pts = AV_NOPTS_VALUE; + dts = AV_NOPTS_VALUE; + /* stuffing */ + for(;;) { + c = get_byte(&s->pb); + len--; + /* XXX: for mpeg1, should test only bit 7 */ + if (c != 0xff) + break; + } + if ((c & 0xc0) == 0x40) { + /* buffer scale & size */ + get_byte(&s->pb); + c = get_byte(&s->pb); + len -= 2; + } + if ((c & 0xf0) == 0x20) { + pts = get_pts(&s->pb, c); + len -= 4; + } else if ((c & 0xf0) == 0x30) { + pts = get_pts(&s->pb, c); + dts = get_pts(&s->pb, -1); + len -= 9; + } else if ((c & 0xc0) == 0x80) { + /* mpeg 2 PES */ + if ((c & 0x30) != 0) { + fprintf(stderr, "Encrypted multiplex not handled\n"); + return -EIO; + } + flags = get_byte(&s->pb); + header_len = get_byte(&s->pb); + len -= 2; + if (header_len > len) + goto redo; + if ((flags & 0xc0) == 0x80) { + pts = get_pts(&s->pb, -1); + header_len -= 5; + len -= 5; + } if ((flags & 0xc0) == 0xc0) { + pts = get_pts(&s->pb, -1); + dts = get_pts(&s->pb, -1); + header_len -= 10; + len -= 10; + } + len -= header_len; + while (header_len > 0) { + get_byte(&s->pb); + header_len--; + } + } + if (startcode == 0x1bd) { + startcode = get_byte(&s->pb); + len--; + if (startcode >= 0x80 && startcode <= 0xbf) { + /* audio: skip header */ + get_byte(&s->pb); + get_byte(&s->pb); + get_byte(&s->pb); + len -= 3; + } + } + + /* now find stream */ + for(i=0;inb_streams;i++) { + st = s->streams[i]; + if (st->id == startcode) + goto found; + } + if (startcode >= 0x1e0 && startcode <= 0x1ef) { + type = CODEC_TYPE_VIDEO; + codec_id = CODEC_ID_MPEG1VIDEO; + } else if (startcode >= 0x1c0 && startcode <= 0x1df) { + type = CODEC_TYPE_AUDIO; + codec_id = CODEC_ID_MP2; + } else if (startcode >= 0x80 && startcode <= 0x9f) { + type = CODEC_TYPE_AUDIO; + codec_id = CODEC_ID_AC3; + } else if (startcode >= 0xa0 && startcode <= 0xbf) { + type = CODEC_TYPE_AUDIO; + codec_id = CODEC_ID_PCM_S16BE; + } else { + skip: + /* skip packet */ + url_fskip(&s->pb, len); + goto redo; + } + /* no stream found: add a new stream */ + st = av_new_stream(s, startcode); + if (!st) + goto skip; + st->codec.codec_type = type; + st->codec.codec_id = codec_id; + found: + if (startcode >= 0xa0 && startcode <= 0xbf) { + int b1, freq; + static const int lpcm_freq_tab[4] = { 48000, 96000, 44100, 32000 }; + + /* for LPCM, we just skip the header and consider it is raw + audio data */ + if (len <= 3) + goto skip; + get_byte(&s->pb); /* emphasis (1), muse(1), reserved(1), frame number(5) */ + b1 = get_byte(&s->pb); /* quant (2), freq(2), reserved(1), channels(3) */ + get_byte(&s->pb); /* dynamic range control (0x80 = off) */ + len -= 3; + freq = (b1 >> 4) & 3; + st->codec.sample_rate = lpcm_freq_tab[freq]; + st->codec.channels = 1 + (b1 & 7); + st->codec.bit_rate = st->codec.channels * st->codec.sample_rate * 2; + } + av_new_packet(pkt, len); + //printf("\nRead Packet ID: %x PTS: %f Size: %d", startcode, + // (float)pts/90000, len); + get_buffer(&s->pb, pkt->data, pkt->size); + pkt->pts = pts; + pkt->stream_index = st->index; + return 0; +} + +static int mpegps_read_close(AVFormatContext *s) +{ + return 0; +} + +static AVOutputFormat mpeg1system_mux = { + "mpeg", + "MPEG1 System format", + "video/mpeg", + "mpg,mpeg", + sizeof(MpegMuxContext), + CODEC_ID_MP2, + CODEC_ID_MPEG1VIDEO, + mpeg_mux_init, + mpeg_mux_write_packet, + mpeg_mux_end, +}; + +static AVOutputFormat mpeg1vcd_mux = { + "vcd", + "MPEG1 System format (VCD)", + "video/mpeg", + NULL, + sizeof(MpegMuxContext), + CODEC_ID_MP2, + CODEC_ID_MPEG1VIDEO, + mpeg_mux_init, + mpeg_mux_write_packet, + mpeg_mux_end, +}; + +static AVOutputFormat mpeg2vob_mux = { + "vob", + "MPEG2 PS format (VOB)", + "video/mpeg", + "vob", + sizeof(MpegMuxContext), + CODEC_ID_MP2, + CODEC_ID_MPEG1VIDEO, + mpeg_mux_init, + mpeg_mux_write_packet, + mpeg_mux_end, +}; + +AVInputFormat mpegps_demux = { + "mpeg", + "MPEG PS format", + sizeof(MpegDemuxContext), + mpegps_probe, + mpegps_read_header, + mpegps_read_packet, + mpegps_read_close, + .flags = AVFMT_NOHEADER, +}; + +int mpegps_init(void) +{ + av_register_output_format(&mpeg1system_mux); + av_register_output_format(&mpeg1vcd_mux); + av_register_output_format(&mpeg2vob_mux); + av_register_input_format(&mpegps_demux); + return 0; +}