aboutsummaryrefslogtreecommitdiffstats
path: root/libavformat
diff options
context:
space:
mode:
authorFabrice Bellard <fabrice@bellard.org>2002-11-25 19:07:40 +0000
committerFabrice Bellard <fabrice@bellard.org>2002-11-25 19:07:40 +0000
commitabac6175916c57f59ddb51ffe41bfd1d9851fe38 (patch)
tree8fc5b70a143f0f74468c353afbf043d7e104cdc0 /libavformat
parent57fc25764261abe71b6cf7571eca182f8acf5795 (diff)
downloadffmpeg-abac6175916c57f59ddb51ffe41bfd1d9851fe38.tar.gz
renamed libav to libavformat
Originally committed as revision 1276 to svn://svn.ffmpeg.org/ffmpeg/trunk
Diffstat (limited to 'libavformat')
-rw-r--r--libavformat/.cvsignore6
-rw-r--r--libavformat/Makefile72
-rw-r--r--libavformat/allformats.c74
-rw-r--r--libavformat/asf.c1256
-rw-r--r--libavformat/au.c214
-rw-r--r--libavformat/audio.c330
-rw-r--r--libavformat/avformat.h351
-rw-r--r--libavformat/avi.h28
-rw-r--r--libavformat/avidec.c256
-rw-r--r--libavformat/avienc.c432
-rw-r--r--libavformat/avio.c156
-rw-r--r--libavformat/avio.h153
-rw-r--r--libavformat/aviobuf.c687
-rw-r--r--libavformat/barpainet.c25
-rw-r--r--libavformat/barpainet.h23
-rw-r--r--libavformat/beosaudio.cpp449
-rw-r--r--libavformat/crc.c111
-rw-r--r--libavformat/cutils.c110
-rw-r--r--libavformat/dv.c134
-rw-r--r--libavformat/ffm.c684
-rw-r--r--libavformat/file.c130
-rw-r--r--libavformat/framehook.c102
-rw-r--r--libavformat/framehook.h19
-rw-r--r--libavformat/gif.c375
-rw-r--r--libavformat/grab.c829
-rw-r--r--libavformat/http.c290
-rw-r--r--libavformat/img.c945
-rw-r--r--libavformat/jpeg.c266
-rw-r--r--libavformat/mov.c1347
-rw-r--r--libavformat/mpeg.c685
-rw-r--r--libavformat/mpegts.c316
-rw-r--r--libavformat/ogg.c269
-rw-r--r--libavformat/raw.c509
-rw-r--r--libavformat/rm.c773
-rw-r--r--libavformat/rtp.c687
-rw-r--r--libavformat/rtp.h40
-rw-r--r--libavformat/rtpproto.c300
-rw-r--r--libavformat/rtsp.c1163
-rw-r--r--libavformat/rtsp.h86
-rw-r--r--libavformat/rtspcodes.h11
-rw-r--r--libavformat/strptime.c1004
-rw-r--r--libavformat/strptime.h32
-rw-r--r--libavformat/swf.c571
-rw-r--r--libavformat/tcp.c176
-rw-r--r--libavformat/udp.c272
-rw-r--r--libavformat/utils.c1280
-rw-r--r--libavformat/wav.c326
47 files changed, 18354 insertions, 0 deletions
diff --git a/libavformat/.cvsignore b/libavformat/.cvsignore
new file mode 100644
index 0000000000..0cc425cc33
--- /dev/null
+++ b/libavformat/.cvsignore
@@ -0,0 +1,6 @@
+config.h
+config.mak
+*ffmpeg
+ffserver
+Makefile.*
+.depend
diff --git a/libavformat/Makefile b/libavformat/Makefile
new file mode 100644
index 0000000000..1051aa6543
--- /dev/null
+++ b/libavformat/Makefile
@@ -0,0 +1,72 @@
+#
+# libavformat Makefile
+# (c) 2000, 2001, 2002 Fabrice Bellard
+#
+include ../config.mak
+
+VPATH=$(SRC_PATH)/libav
+
+CFLAGS= $(OPTFLAGS) -Wall -g -I.. -I$(SRC_PATH) -I$(SRC_PATH)/libavcodec -DHAVE_AV_CONFIG_H -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_GNU_SOURCE
+
+OBJS= utils.o cutils.o allformats.o
+
+# mux and demuxes
+OBJS+=mpeg.o mpegts.o ffm.o crc.o img.o raw.o rm.o asf.o \
+ avienc.o avidec.o wav.o swf.o au.o gif.o mov.o jpeg.o dv.o framehook.o
+# file I/O
+OBJS+= avio.o aviobuf.o file.o
+
+ifeq ($(BUILD_STRPTIME),yes)
+OBJS+= strptime.o
+endif
+
+ifeq ($(CONFIG_VIDEO4LINUX),yes)
+OBJS+= grab.o
+endif
+
+ifeq ($(CONFIG_AUDIO_OSS),yes)
+OBJS+= audio.o
+endif
+
+ifeq ($(CONFIG_AUDIO_BEOS),yes)
+OBJS+= beosaudio.o
+endif
+
+ifeq ($(CONFIG_NETWORK),yes)
+OBJS+= udp.o tcp.o http.o rtsp.o rtp.o rtpproto.o
+# BeOS network stuff
+ifeq ($(CONFIG_BEOS_NETSERVER),yes)
+OBJS+= barpainet.o
+endif
+endif
+
+ifeq ($(CONFIG_VORBIS),yes)
+OBJS+= ogg.o
+endif
+
+LIB= libavformat.a
+
+all: $(LIB)
+
+$(LIB): $(OBJS)
+ rm -f $@
+ $(AR) rc $@ $(OBJS)
+ $(RANLIB) $@
+
+installlib: all
+ install -m 644 $(LIB) $(prefix)/lib
+ mkdir -p $(prefix)/include/ffmpeg
+ install -m 644 $(SRC_PATH)/libav/avformat.h $(SRC_PATH)/libav/avio.h \
+ $(SRC_PATH)/libav/rtp.h $(SRC_PATH)/libav/rtsp.h \
+ $(SRC_PATH)/libav/rtspcodes.h \
+ $(prefix)/include/ffmpeg
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+# BeOS: remove -Wall to get rid of all the "multibyte constant" warnings
+%.o: %.cpp
+ g++ $(subst -Wall,,$(CFLAGS)) -c -o $@ $<
+
+clean:
+ rm -f *.o *~ *.a
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
new file mode 100644
index 0000000000..8a65638e88
--- /dev/null
+++ b/libavformat/allformats.c
@@ -0,0 +1,74 @@
+/*
+ * Register all the formats and protocols
+ * 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"
+
+/* If you do not call this function, then you can select exactly which
+ formats you want to support */
+
+/**
+ * Initialize libavcodec and register all the codecs and formats.
+ */
+void av_register_all(void)
+{
+ avcodec_init();
+ avcodec_register_all();
+
+ mpegps_init();
+ mpegts_init();
+ crc_init();
+ img_init();
+ raw_init();
+ rm_init();
+ asf_init();
+ avienc_init();
+ avidec_init();
+ wav_init();
+ swf_init();
+ au_init();
+ gif_init();
+ mov_init();
+ jpeg_init();
+ dv_init();
+
+#ifdef CONFIG_VORBIS
+ ogg_init();
+#endif
+
+#ifndef CONFIG_WIN32
+ ffm_init();
+#endif
+#ifdef CONFIG_VIDEO4LINUX
+ video_grab_init();
+#endif
+#if defined(CONFIG_AUDIO_OSS) || defined(CONFIG_AUDIO_BEOS)
+ audio_init();
+#endif
+
+ /* file protocols */
+ register_protocol(&file_protocol);
+ register_protocol(&pipe_protocol);
+#ifdef CONFIG_NETWORK
+ rtsp_init();
+ rtp_init();
+ register_protocol(&udp_protocol);
+ register_protocol(&rtp_protocol);
+ register_protocol(&tcp_protocol);
+ register_protocol(&http_protocol);
+#endif
+}
diff --git a/libavformat/asf.c b/libavformat/asf.c
new file mode 100644
index 0000000000..2f1ce12dc3
--- /dev/null
+++ b/libavformat/asf.c
@@ -0,0 +1,1256 @@
+/*
+ * ASF compatible encoder and decoder.
+ * Copyright (c) 2000, 2001 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"
+#include "avi.h"
+#include "mpegaudio.h"
+
+#define PACKET_SIZE 3200
+#define PACKET_HEADER_SIZE 12
+#define FRAME_HEADER_SIZE 17
+
+typedef struct {
+ int num;
+ int seq;
+ /* use for reading */
+ AVPacket pkt;
+ int frag_offset;
+ int timestamp;
+ INT64 duration;
+
+ int ds_span; /* descrambling */
+ int ds_packet_size;
+ int ds_chunk_size;
+ int ds_data_size;
+ int ds_silence_data;
+
+} ASFStream;
+
+typedef struct {
+ UINT32 v1;
+ UINT16 v2;
+ UINT16 v3;
+ UINT8 v4[8];
+} GUID;
+
+typedef struct __attribute__((packed)) {
+ GUID guid; // generated by client computer
+ uint64_t file_size; // in bytes
+ // invalid if broadcasting
+ uint64_t create_time; // time of creation, in 100-nanosecond units since 1.1.1601
+ // invalid if broadcasting
+ uint64_t packets_count; // how many packets are there in the file
+ // invalid if broadcasting
+ uint64_t play_time; // play time, in 100-nanosecond units
+ // invalid if broadcasting
+ uint64_t send_time; // time to send file, in 100-nanosecond units
+ // invalid if broadcasting (could be ignored)
+ uint32_t preroll; // timestamp of the first packet, in milliseconds
+ // if nonzero - substract from time
+ uint32_t ignore; // preroll is 64bit - but let's just ignore it
+ uint32_t flags; // 0x01 - broadcast
+ // 0x02 - seekable
+ // rest is reserved should be 0
+ uint32_t min_pktsize; // size of a data packet
+ // invalid if broadcasting
+ uint32_t max_pktsize; // shall be the same as for min_pktsize
+ // invalid if broadcasting
+ uint32_t max_bitrate; // bandwith of stream in bps
+ // should be the sum of bitrates of the
+ // individual media streams
+} ASFMainHeader;
+
+
+typedef struct {
+ int seqno;
+ int packet_size;
+ int is_streamed;
+ int asfid2avid[128]; /* conversion table from asf ID 2 AVStream ID */
+ ASFStream streams[128]; /* it's max number and it's not that big */
+ /* non streamed additonnal info */
+ INT64 nb_packets;
+ INT64 duration; /* in 100ns units */
+ /* packet filling */
+ int packet_size_left;
+ int packet_timestamp_start;
+ int packet_timestamp_end;
+ int packet_nb_frames;
+ UINT8 packet_buf[PACKET_SIZE];
+ ByteIOContext pb;
+ /* only for reading */
+ uint64_t data_offset; /* begining of the first data packet */
+
+ ASFMainHeader hdr;
+
+ int packet_flags;
+ int packet_property;
+ int packet_timestamp;
+ int packet_segsizetype;
+ int packet_segments;
+ int packet_seq;
+ int packet_replic_size;
+ int packet_key_frame;
+ int packet_padsize;
+ int packet_frag_offset;
+ int packet_frag_size;
+ int packet_frag_timestamp;
+ int packet_multi_size;
+ int packet_obj_size;
+ int packet_time_delta;
+ int packet_time_start;
+
+ int stream_index;
+ ASFStream* asf_st; /* currently decoded stream */
+} ASFContext;
+
+static const GUID asf_header = {
+ 0x75B22630, 0x668E, 0x11CF, { 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C },
+};
+
+static const GUID file_header = {
+ 0x8CABDCA1, 0xA947, 0x11CF, { 0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 },
+};
+
+static const GUID stream_header = {
+ 0xB7DC0791, 0xA9B7, 0x11CF, { 0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 },
+};
+
+static const GUID audio_stream = {
+ 0xF8699E40, 0x5B4D, 0x11CF, { 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B },
+};
+
+static const GUID audio_conceal_none = {
+ // 0x49f1a440, 0x4ece, 0x11d0, { 0xa3, 0xac, 0x00, 0xa0, 0xc9, 0x03, 0x48, 0xf6 },
+ // New value lifted from avifile
+ 0x20fb5700, 0x5b55, 0x11cf, { 0xa8, 0xfd, 0x00, 0x80, 0x5f, 0x5c, 0x44, 0x2b },
+};
+
+static const GUID video_stream = {
+ 0xBC19EFC0, 0x5B4D, 0x11CF, { 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B },
+};
+
+static const GUID video_conceal_none = {
+ 0x20FB5700, 0x5B55, 0x11CF, { 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B },
+};
+
+
+static const GUID comment_header = {
+ 0x75b22633, 0x668e, 0x11cf, { 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c },
+};
+
+static const GUID codec_comment_header = {
+ 0x86D15240, 0x311D, 0x11D0, { 0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6 },
+};
+static const GUID codec_comment1_header = {
+ 0x86d15241, 0x311d, 0x11d0, { 0xa3, 0xa4, 0x00, 0xa0, 0xc9, 0x03, 0x48, 0xf6 },
+};
+
+static const GUID data_header = {
+ 0x75b22636, 0x668e, 0x11cf, { 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c },
+};
+
+static const GUID index_guid = {
+ 0x33000890, 0xe5b1, 0x11cf, { 0x89, 0xf4, 0x00, 0xa0, 0xc9, 0x03, 0x49, 0xcb },
+};
+
+static const GUID head1_guid = {
+ 0x5fbf03b5, 0xa92e, 0x11cf, { 0x8e, 0xe3, 0x00, 0xc0, 0x0c, 0x20, 0x53, 0x65 },
+};
+
+static const GUID head2_guid = {
+ 0xabd3d211, 0xa9ba, 0x11cf, { 0x8e, 0xe6, 0x00, 0xc0, 0x0c, 0x20, 0x53, 0x65 },
+};
+
+/* I am not a number !!! This GUID is the one found on the PC used to
+ generate the stream */
+static const GUID my_guid = {
+ 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+static void put_guid(ByteIOContext *s, const GUID *g)
+{
+ int i;
+
+ put_le32(s, g->v1);
+ put_le16(s, g->v2);
+ put_le16(s, g->v3);
+ for(i=0;i<8;i++)
+ put_byte(s, g->v4[i]);
+}
+
+static void put_str16(ByteIOContext *s, const char *tag)
+{
+ int c;
+
+ put_le16(s,strlen(tag) + 1);
+ for(;;) {
+ c = (UINT8)*tag++;
+ put_le16(s, c);
+ if (c == '\0')
+ break;
+ }
+}
+
+static void put_str16_nolen(ByteIOContext *s, const char *tag)
+{
+ int c;
+
+ for(;;) {
+ c = (UINT8)*tag++;
+ put_le16(s, c);
+ if (c == '\0')
+ break;
+ }
+}
+
+static INT64 put_header(ByteIOContext *pb, const GUID *g)
+{
+ INT64 pos;
+
+ pos = url_ftell(pb);
+ put_guid(pb, g);
+ put_le64(pb, 24);
+ return pos;
+}
+
+/* update header size */
+static void end_header(ByteIOContext *pb, INT64 pos)
+{
+ INT64 pos1;
+
+ pos1 = url_ftell(pb);
+ url_fseek(pb, pos + 16, SEEK_SET);
+ put_le64(pb, pos1 - pos);
+ url_fseek(pb, pos1, SEEK_SET);
+}
+
+/* write an asf chunk (only used in streaming case) */
+static void put_chunk(AVFormatContext *s, int type, int payload_length, int flags)
+{
+ ASFContext *asf = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ int length;
+
+ length = payload_length + 8;
+ put_le16(pb, type);
+ put_le16(pb, length);
+ put_le32(pb, asf->seqno);
+ put_le16(pb, flags); /* unknown bytes */
+ put_le16(pb, length);
+ asf->seqno++;
+}
+
+/* convert from unix to windows time */
+static INT64 unix_to_file_time(int ti)
+{
+ INT64 t;
+
+ t = ti * INT64_C(10000000);
+ t += INT64_C(116444736000000000);
+ return t;
+}
+
+/* write the header (used two times if non streamed) */
+static int asf_write_header1(AVFormatContext *s, INT64 file_size, INT64 data_chunk_size)
+{
+ ASFContext *asf = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ int header_size, n, extra_size, extra_size2, wav_extra_size, file_time;
+ int has_title;
+ AVCodecContext *enc;
+ INT64 header_offset, cur_pos, hpos;
+ int bit_rate;
+
+ has_title = (s->title[0] || s->author[0] || s->copyright[0] || s->comment[0]);
+
+ bit_rate = 0;
+ for(n=0;n<s->nb_streams;n++) {
+ enc = &s->streams[n]->codec;
+
+ bit_rate += enc->bit_rate;
+ }
+
+ if (asf->is_streamed) {
+ put_chunk(s, 0x4824, 0, 0xc00); /* start of stream (length will be patched later) */
+ }
+
+ put_guid(pb, &asf_header);
+ put_le64(pb, -1); /* header length, will be patched after */
+ put_le32(pb, 3 + has_title + s->nb_streams); /* number of chunks in header */
+ put_byte(pb, 1); /* ??? */
+ put_byte(pb, 2); /* ??? */
+
+ /* file header */
+ header_offset = url_ftell(pb);
+ hpos = put_header(pb, &file_header);
+ put_guid(pb, &my_guid);
+ put_le64(pb, file_size);
+ file_time = 0;
+ put_le64(pb, unix_to_file_time(file_time));
+ put_le64(pb, asf->nb_packets); /* number of packets */
+ put_le64(pb, asf->duration); /* end time stamp (in 100ns units) */
+ put_le64(pb, asf->duration); /* duration (in 100ns units) */
+ put_le32(pb, 0); /* start time stamp */
+ put_le32(pb, 0); /* ??? */
+ put_le32(pb, asf->is_streamed ? 1 : 0); /* ??? */
+ put_le32(pb, asf->packet_size); /* packet size */
+ put_le32(pb, asf->packet_size); /* packet size */
+ put_le32(pb, bit_rate); /* Nominal data rate in bps */
+ end_header(pb, hpos);
+
+ /* unknown headers */
+ hpos = put_header(pb, &head1_guid);
+ put_guid(pb, &head2_guid);
+ put_le32(pb, 6);
+ put_le16(pb, 0);
+ end_header(pb, hpos);
+
+ /* title and other infos */
+ if (has_title) {
+ hpos = put_header(pb, &comment_header);
+ put_le16(pb, 2 * (strlen(s->title) + 1));
+ put_le16(pb, 2 * (strlen(s->author) + 1));
+ put_le16(pb, 2 * (strlen(s->copyright) + 1));
+ put_le16(pb, 2 * (strlen(s->comment) + 1));
+ put_le16(pb, 0);
+ put_str16_nolen(pb, s->title);
+ put_str16_nolen(pb, s->author);
+ put_str16_nolen(pb, s->copyright);
+ put_str16_nolen(pb, s->comment);
+ end_header(pb, hpos);
+ }
+
+ /* stream headers */
+ for(n=0;n<s->nb_streams;n++) {
+ INT64 es_pos;
+ // ASFStream *stream = &asf->streams[n];
+
+ enc = &s->streams[n]->codec;
+ asf->streams[n].num = n + 1;
+ asf->streams[n].seq = 0;
+
+ switch(enc->codec_type) {
+ case CODEC_TYPE_AUDIO:
+ wav_extra_size = 0;
+ extra_size = 18 + wav_extra_size;
+ extra_size2 = 0;
+ break;
+ default:
+ case CODEC_TYPE_VIDEO:
+ wav_extra_size = 0;
+ extra_size = 0x33;
+ extra_size2 = 0;
+ break;
+ }
+
+ hpos = put_header(pb, &stream_header);
+ if (enc->codec_type == CODEC_TYPE_AUDIO) {
+ put_guid(pb, &audio_stream);
+ put_guid(pb, &audio_conceal_none);
+ } else {
+ put_guid(pb, &video_stream);
+ put_guid(pb, &video_conceal_none);
+ }
+ put_le64(pb, 0); /* ??? */
+ es_pos = url_ftell(pb);
+ put_le32(pb, extra_size); /* wav header len */
+ put_le32(pb, extra_size2); /* additional data len */
+ put_le16(pb, n + 1); /* stream number */
+ put_le32(pb, 0); /* ??? */
+
+ if (enc->codec_type == CODEC_TYPE_AUDIO) {
+ /* WAVEFORMATEX header */
+ int wavsize = put_wav_header(pb, enc);
+
+ if (wavsize < 0)
+ return -1;
+ if (wavsize != extra_size) {
+ cur_pos = url_ftell(pb);
+ url_fseek(pb, es_pos, SEEK_SET);
+ put_le32(pb, wavsize); /* wav header len */
+ url_fseek(pb, cur_pos, SEEK_SET);
+ }
+ } else {
+ put_le32(pb, enc->width);
+ put_le32(pb, enc->height);
+ put_byte(pb, 2); /* ??? */
+ put_le16(pb, 40); /* size */
+
+ /* BITMAPINFOHEADER header */
+ put_bmp_header(pb, enc, codec_bmp_tags, 1);
+ }
+ end_header(pb, hpos);
+ }
+
+ /* media comments */
+
+ hpos = put_header(pb, &codec_comment_header);
+ put_guid(pb, &codec_comment1_header);
+ put_le32(pb, s->nb_streams);
+ for(n=0;n<s->nb_streams;n++) {
+ AVCodec *p;
+
+ enc = &s->streams[n]->codec;
+ p = avcodec_find_encoder(enc->codec_id);
+
+ put_le16(pb, asf->streams[n].num);
+ put_str16(pb, p ? p->name : enc->codec_name);
+ put_le16(pb, 0); /* no parameters */
+ /* id */
+ if (enc->codec_type == CODEC_TYPE_AUDIO) {
+ put_le16(pb, 2);
+ put_le16(pb, codec_get_tag(codec_wav_tags, enc->codec_id));
+ } else {
+ put_le16(pb, 4);
+ put_le32(pb, codec_get_tag(codec_bmp_tags, enc->codec_id));
+ }
+ }
+ end_header(pb, hpos);
+
+ /* patch the header size fields */
+
+ cur_pos = url_ftell(pb);
+ header_size = cur_pos - header_offset;
+ if (asf->is_streamed) {
+ header_size += 8 + 30 + 50;
+
+ url_fseek(pb, header_offset - 10 - 30, SEEK_SET);
+ put_le16(pb, header_size);
+ url_fseek(pb, header_offset - 2 - 30, SEEK_SET);
+ put_le16(pb, header_size);
+
+ header_size -= 8 + 30 + 50;
+ }
+ header_size += 24 + 6;
+ url_fseek(pb, header_offset - 14, SEEK_SET);
+ put_le64(pb, header_size);
+ url_fseek(pb, cur_pos, SEEK_SET);
+
+ /* movie chunk, followed by packets of packet_size */
+ asf->data_offset = cur_pos;
+ put_guid(pb, &data_header);
+ put_le64(pb, data_chunk_size);
+ put_guid(pb, &my_guid);
+ put_le64(pb, asf->nb_packets); /* nb packets */
+ put_byte(pb, 1); /* ??? */
+ put_byte(pb, 1); /* ??? */
+ return 0;
+}
+
+static int asf_write_header(AVFormatContext *s)
+{
+ ASFContext *asf = s->priv_data;
+
+ av_set_pts_info(s, 32, 1, 1000); /* 32 bit pts in ms */
+
+ asf->packet_size = PACKET_SIZE;
+ asf->nb_packets = 0;
+
+ if (asf_write_header1(s, 0, 50) < 0) {
+ //av_free(asf);
+ return -1;
+ }
+
+ put_flush_packet(&s->pb);
+
+ asf->packet_nb_frames = 0;
+ asf->packet_timestamp_start = -1;
+ asf->packet_timestamp_end = -1;
+ asf->packet_size_left = asf->packet_size - PACKET_HEADER_SIZE;
+ init_put_byte(&asf->pb, asf->packet_buf, asf->packet_size, 1,
+ NULL, NULL, NULL, NULL);
+
+ return 0;
+}
+
+static int asf_write_stream_header(AVFormatContext *s)
+{
+ ASFContext *asf = s->priv_data;
+
+ asf->is_streamed = 1;
+
+ return asf_write_header(s);
+}
+
+/* write a fixed size packet */
+static int put_packet(AVFormatContext *s,
+ unsigned int timestamp, unsigned int duration,
+ int nb_frames, int padsize)
+{
+ ASFContext *asf = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ int flags;
+
+ if (asf->is_streamed) {
+ put_chunk(s, 0x4424, asf->packet_size, 0);
+ }
+
+ put_byte(pb, 0x82);
+ put_le16(pb, 0);
+
+ flags = 0x01; /* nb segments present */
+ if (padsize > 0) {
+ if (padsize < 256)
+ flags |= 0x08;
+ else
+ flags |= 0x10;
+ }
+ put_byte(pb, flags); /* flags */
+ put_byte(pb, 0x5d);
+ if (flags & 0x10)
+ put_le16(pb, padsize - 2);
+ if (flags & 0x08)
+ put_byte(pb, padsize - 1);
+ put_le32(pb, timestamp);
+ put_le16(pb, duration);
+ put_byte(pb, nb_frames | 0x80);
+
+ return PACKET_HEADER_SIZE + ((flags & 0x18) >> 3);
+}
+
+static void flush_packet(AVFormatContext *s)
+{
+ ASFContext *asf = s->priv_data;
+ int hdr_size, ptr;
+
+ hdr_size = put_packet(s, asf->packet_timestamp_start,
+ asf->packet_timestamp_end - asf->packet_timestamp_start,
+ asf->packet_nb_frames, asf->packet_size_left);
+
+ /* Clear out the padding bytes */
+ ptr = asf->packet_size - hdr_size - asf->packet_size_left;
+ memset(asf->packet_buf + ptr, 0, asf->packet_size_left);
+
+ put_buffer(&s->pb, asf->packet_buf, asf->packet_size - hdr_size);
+
+ put_flush_packet(&s->pb);
+ asf->nb_packets++;
+ asf->packet_nb_frames = 0;
+ asf->packet_timestamp_start = -1;
+ asf->packet_timestamp_end = -1;
+ asf->packet_size_left = asf->packet_size - PACKET_HEADER_SIZE;
+ init_put_byte(&asf->pb, asf->packet_buf, asf->packet_size, 1,
+ NULL, NULL, NULL, NULL);
+}
+
+static void put_frame_header(AVFormatContext *s, ASFStream *stream, int timestamp,
+ int payload_size, int frag_offset, int frag_len)
+{
+ ASFContext *asf = s->priv_data;
+ ByteIOContext *pb = &asf->pb;
+ int val;
+
+ val = stream->num;
+ if (s->streams[val - 1]->codec.key_frame /* && frag_offset == 0 */)
+ val |= 0x80;
+ put_byte(pb, val);
+ put_byte(pb, stream->seq);
+ put_le32(pb, frag_offset); /* fragment offset */
+ put_byte(pb, 0x08); /* flags */
+ put_le32(pb, payload_size);
+ put_le32(pb, timestamp);
+ put_le16(pb, frag_len);
+}
+
+
+/* Output a frame. We suppose that payload_size <= PACKET_SIZE.
+
+ It is there that you understand that the ASF format is really
+ crap. They have misread the MPEG Systems spec !
+ */
+static void put_frame(AVFormatContext *s, ASFStream *stream, int timestamp,
+ UINT8 *buf, int payload_size)
+{
+ ASFContext *asf = s->priv_data;
+ int frag_pos, frag_len, frag_len1;
+
+ frag_pos = 0;
+ while (frag_pos < payload_size) {
+ frag_len = payload_size - frag_pos;
+ frag_len1 = asf->packet_size_left - FRAME_HEADER_SIZE;
+ if (frag_len1 > 0) {
+ if (frag_len > frag_len1)
+ frag_len = frag_len1;
+ put_frame_header(s, stream, timestamp+1, payload_size, frag_pos, frag_len);
+ put_buffer(&asf->pb, buf, frag_len);
+ asf->packet_size_left -= (frag_len + FRAME_HEADER_SIZE);
+ asf->packet_timestamp_end = timestamp;
+ if (asf->packet_timestamp_start == -1)
+ asf->packet_timestamp_start = timestamp;
+ asf->packet_nb_frames++;
+ } else {
+ frag_len = 0;
+ }
+ frag_pos += frag_len;
+ buf += frag_len;
+ /* output the frame if filled */
+ if (asf->packet_size_left <= FRAME_HEADER_SIZE)
+ flush_packet(s);
+ }
+ stream->seq++;
+}
+
+
+static int asf_write_packet(AVFormatContext *s, int stream_index,
+ UINT8 *buf, int size, int timestamp)
+{
+ ASFContext *asf = s->priv_data;
+ ASFStream *stream;
+ INT64 duration;
+ AVCodecContext *codec;
+
+ codec = &s->streams[stream_index]->codec;
+ stream = &asf->streams[stream_index];
+
+ if (codec->codec_type == CODEC_TYPE_AUDIO) {
+ duration = (codec->frame_number * codec->frame_size * INT64_C(10000000)) /
+ codec->sample_rate;
+ } else {
+ duration = codec->frame_number *
+ ((INT64_C(10000000) * FRAME_RATE_BASE) / codec->frame_rate);
+ }
+ if (duration > asf->duration)
+ asf->duration = duration;
+
+ put_frame(s, stream, timestamp, buf, size);
+ return 0;
+}
+
+static int asf_write_trailer(AVFormatContext *s)
+{
+ ASFContext *asf = s->priv_data;
+ INT64 file_size;
+
+ /* flush the current packet */
+ if (asf->pb.buf_ptr > asf->pb.buffer)
+ flush_packet(s);
+
+ if (asf->is_streamed) {
+ put_chunk(s, 0x4524, 0, 0); /* end of stream */
+ } else {
+ /* rewrite an updated header */
+ file_size = url_ftell(&s->pb);
+ url_fseek(&s->pb, 0, SEEK_SET);
+ asf_write_header1(s, file_size, file_size - asf->data_offset);
+ }
+
+ put_flush_packet(&s->pb);
+ return 0;
+}
+
+/**********************************/
+/* decoding */
+
+//#define DEBUG
+
+#ifdef DEBUG
+static void print_guid(const GUID *g)
+{
+ int i;
+ printf("0x%08x, 0x%04x, 0x%04x, {", g->v1, g->v2, g->v3);
+ for(i=0;i<8;i++)
+ printf(" 0x%02x,", g->v4[i]);
+ printf("}\n");
+}
+#endif
+
+static void get_guid(ByteIOContext *s, GUID *g)
+{
+ int i;
+
+ g->v1 = get_le32(s);
+ g->v2 = get_le16(s);
+ g->v3 = get_le16(s);
+ for(i=0;i<8;i++)
+ g->v4[i] = get_byte(s);
+}
+
+#if 0
+static void get_str16(ByteIOContext *pb, char *buf, int buf_size)
+{
+ int len, c;
+ char *q;
+
+ len = get_le16(pb);
+ q = buf;
+ while (len > 0) {
+ c = get_le16(pb);
+ if ((q - buf) < buf_size - 1)
+ *q++ = c;
+ len--;
+ }
+ *q = '\0';
+}
+#endif
+
+static void get_str16_nolen(ByteIOContext *pb, int len, char *buf, int buf_size)
+{
+ int c;
+ char *q;
+
+ q = buf;
+ while (len > 0) {
+ c = get_le16(pb);
+ if ((q - buf) < buf_size - 1)
+ *q++ = c;
+ len-=2;
+ }
+ *q = '\0';
+}
+
+static int asf_probe(AVProbeData *pd)
+{
+ GUID g;
+ const unsigned char *p;
+ int i;
+
+ /* check file header */
+ if (pd->buf_size <= 32)
+ return 0;
+ p = pd->buf;
+ g.v1 = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+ p += 4;
+ g.v2 = p[0] | (p[1] << 8);
+ p += 2;
+ g.v3 = p[0] | (p[1] << 8);
+ p += 2;
+ for(i=0;i<8;i++)
+ g.v4[i] = *p++;
+
+ if (!memcmp(&g, &asf_header, sizeof(GUID)))
+ return AVPROBE_SCORE_MAX;
+ else
+ return 0;
+}
+
+static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
+{
+ ASFContext *asf = s->priv_data;
+ GUID g;
+ ByteIOContext *pb = &s->pb;
+ AVStream *st;
+ ASFStream *asf_st;
+ int size, i;
+ INT64 gsize;
+
+ av_set_pts_info(s, 32, 1, 1000); /* 32 bit pts in ms */
+
+ get_guid(pb, &g);
+ if (memcmp(&g, &asf_header, sizeof(GUID)))
+ goto fail;
+ get_le64(pb);
+ get_le32(pb);
+ get_byte(pb);
+ get_byte(pb);
+ memset(&asf->asfid2avid, -1, sizeof(asf->asfid2avid));
+ for(;;) {
+ get_guid(pb, &g);
+ gsize = get_le64(pb);
+#ifdef DEBUG
+ printf("%08Lx: ", url_ftell(pb) - 24);
+ print_guid(&g);
+ printf(" size=0x%Lx\n", gsize);
+#endif
+ if (gsize < 24)
+ goto fail;
+ if (!memcmp(&g, &file_header, sizeof(GUID))) {
+ get_guid(pb, &asf->hdr.guid);
+ asf->hdr.file_size = get_le64(pb);
+ asf->hdr.create_time = get_le64(pb);
+ asf->hdr.packets_count = get_le64(pb);
+ asf->hdr.play_time = get_le64(pb);
+ asf->hdr.send_time = get_le64(pb);
+ asf->hdr.preroll = get_le32(pb);
+ asf->hdr.ignore = get_le32(pb);
+ asf->hdr.flags = get_le32(pb);
+ asf->hdr.min_pktsize = get_le32(pb);
+ asf->hdr.max_pktsize = get_le32(pb);
+ asf->hdr.max_bitrate = get_le32(pb);
+ asf->packet_size = asf->hdr.max_pktsize;
+ asf->nb_packets = asf->hdr.packets_count;
+ } else if (!memcmp(&g, &stream_header, sizeof(GUID))) {
+ int type, total_size;
+ unsigned int tag1;
+ INT64 pos1, pos2;
+
+ pos1 = url_ftell(pb);
+
+ st = av_mallocz(sizeof(AVStream));
+ if (!st)
+ goto fail;
+ s->streams[s->nb_streams] = st;
+ asf_st = av_mallocz(sizeof(ASFStream));
+ if (!asf_st)
+ goto fail;
+ st->priv_data = asf_st;
+ st->time_length = (asf->hdr.send_time - asf->hdr.preroll) / 10; // us
+ get_guid(pb, &g);
+ if (!memcmp(&g, &audio_stream, sizeof(GUID))) {
+ type = CODEC_TYPE_AUDIO;
+ } else if (!memcmp(&g, &video_stream, sizeof(GUID))) {
+ type = CODEC_TYPE_VIDEO;
+ } else {
+ goto fail;
+ }
+ get_guid(pb, &g);
+ total_size = get_le64(pb);
+ get_le32(pb);
+ get_le32(pb);
+ st->id = get_le16(pb) & 0x7f; /* stream id */
+ // mapping of asf ID to AV stream ID;
+ asf->asfid2avid[st->id] = s->nb_streams++;
+
+ get_le32(pb);
+ st->codec.codec_type = type;
+ st->codec.frame_rate = 15 * s->pts_den / s->pts_num; // 15 fps default
+ if (type == CODEC_TYPE_AUDIO) {
+ get_wav_header(pb, &st->codec, 1);
+ /* We have to init the frame size at some point .... */
+ pos2 = url_ftell(pb);
+ if (gsize > (pos2 + 8 - pos1 + 24)) {
+ asf_st->ds_span = get_byte(pb);
+ asf_st->ds_packet_size = get_le16(pb);
+ asf_st->ds_chunk_size = get_le16(pb);
+ asf_st->ds_data_size = get_le16(pb);
+ asf_st->ds_silence_data = get_byte(pb);
+ }
+ //printf("Descrambling: ps:%d cs:%d ds:%d s:%d sd:%d\n",
+ // asf_st->ds_packet_size, asf_st->ds_chunk_size,
+ // asf_st->ds_data_size, asf_st->ds_span, asf_st->ds_silence_data);
+ if (asf_st->ds_span > 1) {
+ if (!asf_st->ds_chunk_size
+ || (asf_st->ds_packet_size/asf_st->ds_chunk_size <= 1))
+ asf_st->ds_span = 0; // disable descrambling
+ }
+ switch (st->codec.codec_id) {
+ case CODEC_ID_MP3LAME:
+ st->codec.frame_size = MPA_FRAME_SIZE;
+ break;
+ case CODEC_ID_PCM_S16LE:
+ case CODEC_ID_PCM_S16BE:
+ case CODEC_ID_PCM_U16LE:
+ case CODEC_ID_PCM_U16BE:
+ case CODEC_ID_PCM_S8:
+ case CODEC_ID_PCM_U8:
+ case CODEC_ID_PCM_ALAW:
+ case CODEC_ID_PCM_MULAW:
+ st->codec.frame_size = 1;
+ break;
+ default:
+ /* This is probably wrong, but it prevents a crash later */
+ st->codec.frame_size = 1;
+ break;
+ }
+ } else {
+ get_le32(pb);
+ get_le32(pb);
+ get_byte(pb);
+ size = get_le16(pb); /* size */
+ get_le32(pb); /* size */
+ st->codec.width = get_le32(pb);
+ st->codec.height = get_le32(pb);
+ /* not available for asf */
+ get_le16(pb); /* panes */
+ get_le16(pb); /* depth */
+ tag1 = get_le32(pb);
+ url_fskip(pb, 20);
+ if (size > 40) {
+ st->codec.extradata_size = size - 40;
+ st->codec.extradata = av_mallocz(st->codec.extradata_size);
+ get_buffer(pb, st->codec.extradata, st->codec.extradata_size);
+ }
+ st->codec.codec_tag = st->codec.fourcc = tag1;
+ st->codec.codec_id = codec_get_id(codec_bmp_tags, tag1);
+ }
+ pos2 = url_ftell(pb);
+ url_fskip(pb, gsize - (pos2 - pos1 + 24));
+ } else if (!memcmp(&g, &data_header, sizeof(GUID))) {
+ break;
+ } else if (!memcmp(&g, &comment_header, sizeof(GUID))) {
+ int len1, len2, len3, len4, len5;
+
+ len1 = get_le16(pb);
+ len2 = get_le16(pb);
+ len3 = get_le16(pb);
+ len4 = get_le16(pb);
+ len5 = get_le16(pb);
+ get_str16_nolen(pb, len1, s->title, sizeof(s->title));
+ get_str16_nolen(pb, len2, s->author, sizeof(s->author));
+ get_str16_nolen(pb, len3, s->copyright, sizeof(s->copyright));
+ get_str16_nolen(pb, len4, s->comment, sizeof(s->comment));
+ url_fskip(pb, len5);
+#if 0
+ } else if (!memcmp(&g, &head1_guid, sizeof(GUID))) {
+ int v1, v2;
+ get_guid(pb, &g);
+ v1 = get_le32(pb);
+ v2 = get_le16(pb);
+ } else if (!memcmp(&g, &codec_comment_header, sizeof(GUID))) {
+ int len, v1, n, num;
+ char str[256], *q;
+ char tag[16];
+
+ get_guid(pb, &g);
+ print_guid(&g);
+
+ n = get_le32(pb);
+ for(i=0;i<n;i++) {
+ num = get_le16(pb); /* stream number */
+ get_str16(pb, str, sizeof(str));
+ get_str16(pb, str, sizeof(str));
+ len = get_le16(pb);
+ q = tag;
+ while (len > 0) {
+ v1 = get_byte(pb);
+ if ((q - tag) < sizeof(tag) - 1)
+ *q++ = v1;
+ len--;
+ }
+ *q = '\0';
+ }
+#endif
+ } else if (url_feof(pb)) {
+ goto fail;
+ } else {
+ url_fseek(pb, gsize - 24, SEEK_CUR);
+ }
+ }
+ get_guid(pb, &g);
+ get_le64(pb);
+ get_byte(pb);
+ get_byte(pb);
+ if (url_feof(pb))
+ goto fail;
+ asf->data_offset = url_ftell(pb);
+ asf->packet_size_left = 0;
+
+ return 0;
+
+ fail:
+ for(i=0;i<s->nb_streams;i++) {
+ AVStream *st = s->streams[i];
+ if (st) {
+ av_free(st->priv_data);
+ av_free(st->codec.extradata);
+ }
+ av_free(st);
+ }
+ return -1;
+}
+
+#define DO_2BITS(bits, var, defval) \
+ switch (bits & 3) \
+ { \
+ case 3: var = get_le32(pb); rsize += 4; break; \
+ case 2: var = get_le16(pb); rsize += 2; break; \
+ case 1: var = get_byte(pb); rsize++; break; \
+ default: var = defval; break; \
+ }
+
+static int asf_get_packet(AVFormatContext *s)
+{
+ ASFContext *asf = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ uint32_t packet_length, padsize;
+ int rsize = 11;
+ int c = get_byte(pb);
+ if (c != 0x82) {
+ if (!url_feof(pb))
+ printf("ff asf bad header %x at:%Ld\n", c, url_ftell(pb));
+ return -EIO;
+ }
+ if ((c & 0x0f) == 2) { // always true for now
+ if (get_le16(pb) != 0) {
+ if (!url_feof(pb))
+ printf("ff asf bad non zero\n");
+ return -EIO;
+ }
+ }
+
+ asf->packet_flags = get_byte(pb);
+ asf->packet_property = get_byte(pb);
+
+ DO_2BITS(asf->packet_flags >> 5, packet_length, asf->packet_size);
+ DO_2BITS(asf->packet_flags >> 1, padsize, 0); // sequence ignored
+ DO_2BITS(asf->packet_flags >> 3, padsize, 0); // padding length
+
+ asf->packet_timestamp = get_le32(pb);
+ get_le16(pb); /* duration */
+ // rsize has at least 11 bytes which have to be present
+
+ if (asf->packet_flags & 0x01) {
+ asf->packet_segsizetype = get_byte(pb); rsize++;
+ asf->packet_segments = asf->packet_segsizetype & 0x3f;
+ } else {
+ asf->packet_segments = 1;
+ asf->packet_segsizetype = 0x80;
+ }
+ asf->packet_size_left = packet_length - padsize - rsize;
+ if (packet_length < asf->hdr.min_pktsize)
+ padsize += asf->hdr.min_pktsize - packet_length;
+ asf->packet_padsize = padsize;
+#ifdef DEBUG
+ printf("packet: size=%d padsize=%d left=%d\n", asf->packet_size, asf->packet_padsize, asf->packet_size_left);
+#endif
+ return 0;
+}
+
+static int asf_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ ASFContext *asf = s->priv_data;
+ ASFStream *asf_st = 0;
+ ByteIOContext *pb = &s->pb;
+ //static int pc = 0;
+ for (;;) {
+ int rsize = 0;
+ if (asf->packet_size_left < FRAME_HEADER_SIZE
+ || asf->packet_segments < 1) {
+ //asf->packet_size_left <= asf->packet_padsize) {
+ int ret = asf->packet_size_left + asf->packet_padsize;
+ //printf("PacketLeftSize:%d Pad:%d Pos:%Ld\n", asf->packet_size_left, asf->packet_padsize, url_ftell(pb));
+ /* fail safe */
+ url_fskip(pb, ret);
+ ret = asf_get_packet(s);
+ //printf("READ ASF PACKET %d r:%d c:%d\n", ret, asf->packet_size_left, pc++);
+ if (ret < 0 || url_feof(pb))
+ return -EIO;
+ asf->packet_time_start = 0;
+ continue;
+ }
+ if (asf->packet_time_start == 0) {
+ /* read frame header */
+ int num = get_byte(pb);
+ asf->packet_segments--;
+ rsize++;
+ asf->packet_key_frame = (num & 0x80) >> 7;
+ asf->stream_index = asf->asfid2avid[num & 0x7f];
+ // sequence should be ignored!
+ DO_2BITS(asf->packet_property >> 4, asf->packet_seq, 0);
+ DO_2BITS(asf->packet_property >> 2, asf->packet_frag_offset, 0);
+ DO_2BITS(asf->packet_property, asf->packet_replic_size, 0);
+
+ if (asf->packet_replic_size > 1) {
+ // it should be always at least 8 bytes - FIXME validate
+ asf->packet_obj_size = get_le32(pb);
+ asf->packet_frag_timestamp = get_le32(pb); // timestamp
+ if (asf->packet_replic_size > 8)
+ url_fskip(pb, asf->packet_replic_size - 8);
+ rsize += asf->packet_replic_size; // FIXME - check validity
+ } else {
+ // multipacket - frag_offset is begining timestamp
+ asf->packet_time_start = asf->packet_frag_offset;
+ asf->packet_frag_offset = 0;
+ asf->packet_frag_timestamp = asf->packet_timestamp;
+
+ if (asf->packet_replic_size == 1) {
+ asf->packet_time_delta = get_byte(pb);
+ rsize++;
+ }
+ }
+ if (asf->packet_flags & 0x01) {
+ DO_2BITS(asf->packet_segsizetype >> 6, asf->packet_frag_size, 0); // 0 is illegal
+#undef DO_2BITS
+ //printf("Fragsize %d\n", asf->packet_frag_size);
+ } else {
+ asf->packet_frag_size = asf->packet_size_left - rsize;
+ //printf("Using rest %d %d %d\n", asf->packet_frag_size, asf->packet_size_left, rsize);
+ }
+ if (asf->packet_replic_size == 1) {
+ asf->packet_multi_size = asf->packet_frag_size;
+ if (asf->packet_multi_size > asf->packet_size_left) {
+ asf->packet_segments = 0;
+ continue;
+ }
+ }
+ asf->packet_size_left -= rsize;
+ //printf("___objsize____ %d %d rs:%d\n", asf->packet_obj_size, asf->packet_frag_offset, rsize);
+
+ if (asf->stream_index < 0) {
+ asf->packet_time_start = 0;
+ /* unhandled packet (should not happen) */
+ url_fskip(pb, asf->packet_frag_size);
+ asf->packet_size_left -= asf->packet_frag_size;
+ printf("ff asf skip %d %d\n", asf->packet_frag_size, num & 0x7f);
+ continue;
+ }
+ asf->asf_st = s->streams[asf->stream_index]->priv_data;
+ }
+ asf_st = asf->asf_st;
+
+ if ((asf->packet_frag_offset != asf_st->frag_offset
+ || (asf->packet_frag_offset
+ && asf->packet_seq != asf_st->seq)) // seq should be ignored
+ ) {
+ /* cannot continue current packet: free it */
+ // FIXME better check if packet was already allocated
+ printf("ff asf parser skips: %d - %d o:%d - %d %d %d fl:%d\n",
+ asf_st->pkt.size,
+ asf->packet_obj_size,
+ asf->packet_frag_offset, asf_st->frag_offset,
+ asf->packet_seq, asf_st->seq, asf->packet_frag_size);
+ if (asf_st->pkt.size)
+ av_free_packet(&asf_st->pkt);
+ asf_st->frag_offset = 0;
+ if (asf->packet_frag_offset != 0) {
+ url_fskip(pb, asf->packet_frag_size);
+ printf("ff asf parser skiping %db\n", asf->packet_frag_size);
+ asf->packet_size_left -= asf->packet_frag_size;
+ continue;
+ }
+ }
+ if (asf->packet_replic_size == 1) {
+ // frag_offset is here used as the begining timestamp
+ asf->packet_frag_timestamp = asf->packet_time_start;
+ asf->packet_time_start += asf->packet_time_delta;
+ asf->packet_obj_size = asf->packet_frag_size = get_byte(pb);
+ asf->packet_size_left--;
+ asf->packet_multi_size--;
+ if (asf->packet_multi_size < asf->packet_obj_size)
+ {
+ asf->packet_time_start = 0;
+ url_fskip(pb, asf->packet_multi_size);
+ asf->packet_size_left -= asf->packet_multi_size;
+ continue;
+ }
+ asf->packet_multi_size -= asf->packet_obj_size;
+ //printf("COMPRESS size %d %d %d ms:%d\n", asf->packet_obj_size, asf->packet_frag_timestamp, asf->packet_size_left, asf->packet_multi_size);
+ }
+ if (asf_st->frag_offset == 0) {
+ /* new packet */
+ av_new_packet(&asf_st->pkt, asf->packet_obj_size);
+ asf_st->seq = asf->packet_seq;
+ asf_st->pkt.pts = asf->packet_frag_timestamp - asf->hdr.preroll;
+ asf_st->pkt.stream_index = asf->stream_index;
+ if (asf->packet_key_frame)
+ asf_st->pkt.flags |= PKT_FLAG_KEY;
+ }
+
+ /* read data */
+ //printf("READ PACKET s:%d os:%d o:%d,%d l:%d DATA:%p\n",
+ // asf->packet_size, asf_st->pkt.size, asf->packet_frag_offset,
+ // asf_st->frag_offset, asf->packet_frag_size, asf_st->pkt.data);
+ asf->packet_size_left -= asf->packet_frag_size;
+ if (asf->packet_size_left < 0)
+ continue;
+ get_buffer(pb, asf_st->pkt.data + asf->packet_frag_offset,
+ asf->packet_frag_size);
+ asf_st->frag_offset += asf->packet_frag_size;
+ /* test if whole packet is read */
+ if (asf_st->frag_offset == asf_st->pkt.size) {
+ /* return packet */
+ if (asf_st->ds_span > 1) {
+ /* packet descrambling */
+ char* newdata = av_malloc(asf_st->pkt.size);
+ if (newdata) {
+ int offset = 0;
+ while (offset < asf_st->pkt.size) {
+ int off = offset / asf_st->ds_chunk_size;
+ int row = off / asf_st->ds_span;
+ int col = off % asf_st->ds_span;
+ int idx = row + col * asf_st->ds_packet_size / asf_st->ds_chunk_size;
+ //printf("off:%d row:%d col:%d idx:%d\n", off, row, col, idx);
+ memcpy(newdata + offset,
+ asf_st->pkt.data + idx * asf_st->ds_chunk_size,
+ asf_st->ds_chunk_size);
+ offset += asf_st->ds_chunk_size;
+ }
+ av_free(asf_st->pkt.data);
+ asf_st->pkt.data = newdata;
+ }
+ }
+ asf_st->frag_offset = 0;
+ memcpy(pkt, &asf_st->pkt, sizeof(AVPacket));
+ //printf("packet %d %d\n", asf_st->pkt.size, asf->packet_frag_size);
+ asf_st->pkt.size = 0;
+ asf_st->pkt.data = 0;
+ break; // packet completed
+ }
+ }
+ return 0;
+}
+
+static int asf_read_close(AVFormatContext *s)
+{
+ int i;
+
+ for(i=0;i<s->nb_streams;i++) {
+ AVStream *st = s->streams[i];
+ av_free(st->priv_data);
+ av_free(st->codec.extradata);
+ }
+ return 0;
+}
+
+static int asf_read_seek(AVFormatContext *s, int64_t pts)
+{
+ printf("SEEK TO %Ld", pts);
+ return -1;
+}
+
+static AVInputFormat asf_iformat = {
+ "asf",
+ "asf format",
+ sizeof(ASFContext),
+ asf_probe,
+ asf_read_header,
+ asf_read_packet,
+ asf_read_close,
+ asf_read_seek,
+};
+
+static AVOutputFormat asf_oformat = {
+ "asf",
+ "asf format",
+ "application/octet-stream",
+ "asf,wmv",
+ sizeof(ASFContext),
+#ifdef CONFIG_MP3LAME
+ CODEC_ID_MP3LAME,
+#else
+ CODEC_ID_MP2,
+#endif
+ CODEC_ID_MSMPEG4V3,
+ asf_write_header,
+ asf_write_packet,
+ asf_write_trailer,
+};
+
+static AVOutputFormat asf_stream_oformat = {
+ "asf_stream",
+ "asf format",
+ "application/octet-stream",
+ "asf,wmv",
+ sizeof(ASFContext),
+#ifdef CONFIG_MP3LAME
+ CODEC_ID_MP3LAME,
+#else
+ CODEC_ID_MP2,
+#endif
+ CODEC_ID_MSMPEG4V3,
+ asf_write_stream_header,
+ asf_write_packet,
+ asf_write_trailer,
+};
+
+int asf_init(void)
+{
+ av_register_input_format(&asf_iformat);
+ av_register_output_format(&asf_oformat);
+ av_register_output_format(&asf_stream_oformat);
+ return 0;
+}
diff --git a/libavformat/au.c b/libavformat/au.c
new file mode 100644
index 0000000000..8c3d62a595
--- /dev/null
+++ b/libavformat/au.c
@@ -0,0 +1,214 @@
+/*
+ * AU encoder and decoder
+ * Copyright (c) 2001 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
+ */
+
+/*
+ * First version by Francois Revol revol@free.fr
+ *
+ * Reference documents:
+ * http://www.opengroup.org/public/pubs/external/auformat.html
+ * http://www.goice.co.jp/member/mo/formats/au.html
+ */
+
+#include "avformat.h"
+#include "avi.h"
+
+/* if we don't know the size in advance */
+#define AU_UNKOWN_SIZE ((UINT32)(~0))
+
+/* The ffmpeg codecs we support, and the IDs they have in the file */
+static const CodecTag codec_au_tags[] = {
+ { CODEC_ID_PCM_MULAW, 1 },
+ { CODEC_ID_PCM_S16BE, 3 },
+ { CODEC_ID_PCM_ALAW, 27 },
+ { 0, 0 },
+};
+
+/* AUDIO_FILE header */
+static int put_au_header(ByteIOContext *pb, AVCodecContext *enc)
+{
+ int tag;
+
+ tag = codec_get_tag(codec_au_tags, enc->codec_id);
+ if (tag == 0)
+ return -1;
+ put_tag(pb, ".snd"); /* magic number */
+ put_be32(pb, 24); /* header size */
+ put_be32(pb, AU_UNKOWN_SIZE); /* data size */
+ put_be32(pb, (UINT32)tag); /* codec ID */
+ put_be32(pb, enc->sample_rate);
+ put_be32(pb, (UINT32)enc->channels);
+ return 0;
+}
+
+static int au_write_header(AVFormatContext *s)
+{
+ ByteIOContext *pb = &s->pb;
+
+ s->priv_data = NULL;
+
+ /* format header */
+ if (put_au_header(pb, &s->streams[0]->codec) < 0) {
+ return -1;
+ }
+
+ put_flush_packet(pb);
+
+ return 0;
+}
+
+static int au_write_packet(AVFormatContext *s, int stream_index_ptr,
+ UINT8 *buf, int size, int force_pts)
+{
+ ByteIOContext *pb = &s->pb;
+ put_buffer(pb, buf, size);
+ return 0;
+}
+
+static int au_write_trailer(AVFormatContext *s)
+{
+ ByteIOContext *pb = &s->pb;
+ offset_t file_size;
+
+ if (!url_is_streamed(&s->pb)) {
+
+ /* update file size */
+ file_size = url_ftell(pb);
+ url_fseek(pb, 8, SEEK_SET);
+ put_be32(pb, (UINT32)(file_size - 24));
+ url_fseek(pb, file_size, SEEK_SET);
+
+ put_flush_packet(pb);
+ }
+
+ return 0;
+}
+
+static int au_probe(AVProbeData *p)
+{
+ /* check file header */
+ if (p->buf_size <= 24)
+ return 0;
+ if (p->buf[0] == '.' && p->buf[1] == 's' &&
+ p->buf[2] == 'n' && p->buf[3] == 'd')
+ return AVPROBE_SCORE_MAX;
+ else
+ return 0;
+}
+
+/* au input */
+static int au_read_header(AVFormatContext *s,
+ AVFormatParameters *ap)
+{
+ int size;
+ unsigned int tag;
+ ByteIOContext *pb = &s->pb;
+ unsigned int id, codec, channels, rate;
+ AVStream *st;
+
+ /* check ".snd" header */
+ tag = get_le32(pb);
+ if (tag != MKTAG('.', 's', 'n', 'd'))
+ return -1;
+ size = get_be32(pb); /* header size */
+ get_be32(pb); /* data size */
+
+ id = get_be32(pb);
+ rate = get_be32(pb);
+ channels = get_be32(pb);
+
+ codec = codec_get_id(codec_au_tags, id);
+
+ if (size >= 24) {
+ /* skip unused data */
+ url_fseek(pb, size - 24, SEEK_CUR);
+ }
+
+ /* now we are ready: build format streams */
+ st = av_malloc(sizeof(AVStream));
+ if (!st)
+ return -1;
+ s->nb_streams = 1;
+ s->streams[0] = st;
+
+ st->id = 0;
+
+ st->codec.codec_type = CODEC_TYPE_AUDIO;
+ st->codec.codec_tag = id;
+ st->codec.codec_id = codec;
+ st->codec.channels = channels;
+ st->codec.sample_rate = rate;
+ return 0;
+}
+
+#define MAX_SIZE 4096
+
+static int au_read_packet(AVFormatContext *s,
+ AVPacket *pkt)
+{
+ int ret;
+
+ if (url_feof(&s->pb))
+ return -EIO;
+ if (av_new_packet(pkt, MAX_SIZE))
+ return -EIO;
+ pkt->stream_index = 0;
+
+ ret = get_buffer(&s->pb, pkt->data, pkt->size);
+ if (ret < 0)
+ av_free_packet(pkt);
+ /* note: we need to modify the packet size here to handle the last
+ packet */
+ pkt->size = ret;
+ return 0;
+}
+
+static int au_read_close(AVFormatContext *s)
+{
+ return 0;
+}
+
+static AVInputFormat au_iformat = {
+ "au",
+ "SUN AU Format",
+ 0,
+ au_probe,
+ au_read_header,
+ au_read_packet,
+ au_read_close,
+};
+
+static AVOutputFormat au_oformat = {
+ "au",
+ "SUN AU Format",
+ "audio/basic",
+ "au",
+ 0,
+ CODEC_ID_PCM_S16BE,
+ CODEC_ID_NONE,
+ au_write_header,
+ au_write_packet,
+ au_write_trailer,
+};
+
+int au_init(void)
+{
+ av_register_input_format(&au_iformat);
+ av_register_output_format(&au_oformat);
+ return 0;
+}
diff --git a/libavformat/audio.c b/libavformat/audio.c
new file mode 100644
index 0000000000..4fa155c85d
--- /dev/null
+++ b/libavformat/audio.c
@@ -0,0 +1,330 @@
+/*
+ * Linux audio play and grab interface
+ * Copyright (c) 2000, 2001 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"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/soundcard.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+const char *audio_device = "/dev/dsp";
+
+#define AUDIO_BLOCK_SIZE 4096
+
+typedef struct {
+ int fd;
+ int sample_rate;
+ int channels;
+ int frame_size; /* in bytes ! */
+ int codec_id;
+ int flip_left : 1;
+ UINT8 buffer[AUDIO_BLOCK_SIZE];
+ int buffer_ptr;
+} AudioData;
+
+static int audio_open(AudioData *s, int is_output)
+{
+ int audio_fd;
+ int tmp, err;
+ char *flip = getenv("AUDIO_FLIP_LEFT");
+
+ /* open linux audio device */
+ if (is_output)
+ audio_fd = open(audio_device, O_WRONLY);
+ else
+ audio_fd = open(audio_device, O_RDONLY);
+ if (audio_fd < 0) {
+ perror(audio_device);
+ return -EIO;
+ }
+
+ if (flip && *flip == '1') {
+ s->flip_left = 1;
+ }
+
+ /* non blocking mode */
+ if (!is_output)
+ fcntl(audio_fd, F_SETFL, O_NONBLOCK);
+
+ s->frame_size = AUDIO_BLOCK_SIZE;
+#if 0
+ tmp = (NB_FRAGMENTS << 16) | FRAGMENT_BITS;
+ err = ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &tmp);
+ if (err < 0) {
+ perror("SNDCTL_DSP_SETFRAGMENT");
+ }
+#endif
+
+ /* select format : favour native format */
+ err = ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &tmp);
+
+#ifdef WORDS_BIGENDIAN
+ if (tmp & AFMT_S16_BE) {
+ tmp = AFMT_S16_BE;
+ } else if (tmp & AFMT_S16_LE) {
+ tmp = AFMT_S16_LE;
+ } else {
+ tmp = 0;
+ }
+#else
+ if (tmp & AFMT_S16_LE) {
+ tmp = AFMT_S16_LE;
+ } else if (tmp & AFMT_S16_BE) {
+ tmp = AFMT_S16_BE;
+ } else {
+ tmp = 0;
+ }
+#endif
+
+ switch(tmp) {
+ case AFMT_S16_LE:
+ s->codec_id = CODEC_ID_PCM_S16LE;
+ break;
+ case AFMT_S16_BE:
+ s->codec_id = CODEC_ID_PCM_S16BE;
+ break;
+ default:
+ fprintf(stderr, "Soundcard does not support 16 bit sample format\n");
+ close(audio_fd);
+ return -EIO;
+ }
+ err=ioctl(audio_fd, SNDCTL_DSP_SETFMT, &tmp);
+ if (err < 0) {
+ perror("SNDCTL_DSP_SETFMT");
+ goto fail;
+ }
+
+ tmp = (s->channels == 2);
+ err = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp);
+ if (err < 0) {
+ perror("SNDCTL_DSP_STEREO");
+ goto fail;
+ }
+ if (tmp)
+ s->channels = 2;
+
+ tmp = s->sample_rate;
+ err = ioctl(audio_fd, SNDCTL_DSP_SPEED, &tmp);
+ if (err < 0) {
+ perror("SNDCTL_DSP_SPEED");
+ goto fail;
+ }
+ s->sample_rate = tmp; /* store real sample rate */
+ s->fd = audio_fd;
+
+ return 0;
+ fail:
+ close(audio_fd);
+ return -EIO;
+}
+
+static int audio_close(AudioData *s)
+{
+ close(s->fd);
+ return 0;
+}
+
+/* sound output support */
+static int audio_write_header(AVFormatContext *s1)
+{
+ AudioData *s = s1->priv_data;
+ AVStream *st;
+ int ret;
+
+ st = s1->streams[0];
+ s->sample_rate = st->codec.sample_rate;
+ s->channels = st->codec.channels;
+ ret = audio_open(s, 1);
+ if (ret < 0) {
+ return -EIO;
+ } else {
+ return 0;
+ }
+}
+
+static int audio_write_packet(AVFormatContext *s1, int stream_index,
+ UINT8 *buf, int size, int force_pts)
+{
+ AudioData *s = s1->priv_data;
+ int len, ret;
+
+ while (size > 0) {
+ len = AUDIO_BLOCK_SIZE - s->buffer_ptr;
+ if (len > size)
+ len = size;
+ memcpy(s->buffer + s->buffer_ptr, buf, len);
+ s->buffer_ptr += len;
+ if (s->buffer_ptr >= AUDIO_BLOCK_SIZE) {
+ for(;;) {
+ ret = write(s->fd, s->buffer, AUDIO_BLOCK_SIZE);
+ if (ret > 0)
+ break;
+ if (ret < 0 && (errno != EAGAIN && errno != EINTR))
+ return -EIO;
+ }
+ s->buffer_ptr = 0;
+ }
+ buf += len;
+ size -= len;
+ }
+ return 0;
+}
+
+static int audio_write_trailer(AVFormatContext *s1)
+{
+ AudioData *s = s1->priv_data;
+
+ audio_close(s);
+ return 0;
+}
+
+/* grab support */
+
+static int audio_read_header(AVFormatContext *s1, AVFormatParameters *ap)
+{
+ AudioData *s = s1->priv_data;
+ AVStream *st;
+ int ret;
+
+ if (!ap || ap->sample_rate <= 0 || ap->channels <= 0)
+ return -1;
+
+ st = av_new_stream(s1, 0);
+ if (!st) {
+ return -ENOMEM;
+ }
+ s->sample_rate = ap->sample_rate;
+ s->channels = ap->channels;
+
+ ret = audio_open(s, 0);
+ if (ret < 0) {
+ av_free(st);
+ return -EIO;
+ }
+
+ /* take real parameters */
+ st->codec.codec_type = CODEC_TYPE_AUDIO;
+ st->codec.codec_id = s->codec_id;
+ st->codec.sample_rate = s->sample_rate;
+ st->codec.channels = s->channels;
+
+ av_set_pts_info(s1, 48, 1, 1000000); /* 48 bits pts in us */
+ return 0;
+}
+
+static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt)
+{
+ AudioData *s = s1->priv_data;
+ int ret, bdelay;
+ int64_t cur_time;
+ struct audio_buf_info abufi;
+
+ if (av_new_packet(pkt, s->frame_size) < 0)
+ return -EIO;
+ for(;;) {
+ ret = read(s->fd, pkt->data, pkt->size);
+ if (ret > 0)
+ break;
+ if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
+ av_free_packet(pkt);
+ pkt->size = 0;
+ return 0;
+ }
+ if (!(ret == 0 || (ret == -1 && (errno == EAGAIN || errno == EINTR)))) {
+ av_free_packet(pkt);
+ return -EIO;
+ }
+ }
+ pkt->size = ret;
+
+ /* compute pts of the start of the packet */
+ cur_time = av_gettime();
+ bdelay = ret;
+ if (ioctl(s->fd, SNDCTL_DSP_GETISPACE, &abufi) == 0) {
+ bdelay += abufi.bytes;
+ }
+ /* substract time represented by the number of bytes in the audio fifo */
+ cur_time -= (bdelay * 1000000LL) / (s->sample_rate * s->channels);
+
+ /* convert to wanted units */
+ pkt->pts = cur_time & ((1LL << 48) - 1);
+
+ if (s->flip_left && s->channels == 2) {
+ int i;
+ short *p = (short *) pkt->data;
+
+ for (i = 0; i < ret; i += 4) {
+ *p = ~*p;
+ p += 2;
+ }
+ }
+ return 0;
+}
+
+static int audio_read_close(AVFormatContext *s1)
+{
+ AudioData *s = s1->priv_data;
+
+ audio_close(s);
+ return 0;
+}
+
+static AVInputFormat audio_in_format = {
+ "audio_device",
+ "audio grab and output",
+ sizeof(AudioData),
+ NULL,
+ audio_read_header,
+ audio_read_packet,
+ audio_read_close,
+ .flags = AVFMT_NOFILE,
+};
+
+static AVOutputFormat audio_out_format = {
+ "audio_device",
+ "audio grab and output",
+ "",
+ "",
+ sizeof(AudioData),
+ /* XXX: we make the assumption that the soundcard accepts this format */
+ /* XXX: find better solution with "preinit" method, needed also in
+ other formats */
+#ifdef WORDS_BIGENDIAN
+ CODEC_ID_PCM_S16BE,
+#else
+ CODEC_ID_PCM_S16LE,
+#endif
+ CODEC_ID_NONE,
+ audio_write_header,
+ audio_write_packet,
+ audio_write_trailer,
+ .flags = AVFMT_NOFILE,
+};
+
+int audio_init(void)
+{
+ av_register_input_format(&audio_in_format);
+ av_register_output_format(&audio_out_format);
+ return 0;
+}
diff --git a/libavformat/avformat.h b/libavformat/avformat.h
new file mode 100644
index 0000000000..0ea10f7dfe
--- /dev/null
+++ b/libavformat/avformat.h
@@ -0,0 +1,351 @@
+#ifndef AVFORMAT_H
+#define AVFORMAT_H
+
+#define LIBAVFORMAT_VERSION_INT 0x000406
+#define LIBAVFORMAT_VERSION "0.4.6"
+#define LIBAVFORMAT_BUILD 4602
+
+#include "avcodec.h"
+
+#include "avio.h"
+
+/* packet functions */
+
+#define AV_NOPTS_VALUE 0
+
+typedef struct AVPacket {
+ INT64 pts; /* presentation time stamp in stream units (set av_set_pts_info) */
+ UINT8 *data;
+ int size;
+ int stream_index;
+ int flags;
+ int duration;
+#define PKT_FLAG_KEY 0x0001
+} AVPacket;
+
+int av_new_packet(AVPacket *pkt, int size);
+void av_free_packet(AVPacket *pkt);
+
+/*************************************************/
+/* fractional numbers for exact pts handling */
+
+/* the exact value of the fractional number is: 'val + num / den'. num
+ is assumed to be such as 0 <= num < den */
+typedef struct AVFrac {
+ INT64 val, num, den;
+} AVFrac;
+
+void av_frac_init(AVFrac *f, INT64 val, INT64 num, INT64 den);
+void av_frac_add(AVFrac *f, INT64 incr);
+void av_frac_set(AVFrac *f, INT64 val);
+
+/*************************************************/
+/* input/output formats */
+
+struct AVFormatContext;
+
+/* this structure contains the data a format has to probe a file */
+typedef struct AVProbeData {
+ char *filename;
+ unsigned char *buf;
+ int buf_size;
+} AVProbeData;
+
+#define AVPROBE_SCORE_MAX 100
+
+typedef struct AVFormatParameters {
+ int frame_rate;
+ int sample_rate;
+ int channels;
+ int width;
+ int height;
+ enum PixelFormat pix_fmt;
+} AVFormatParameters;
+
+#define AVFMT_NOFILE 0x0001 /* no file should be opened */
+#define AVFMT_NEEDNUMBER 0x0002 /* needs '%d' in filename */
+#define AVFMT_NOHEADER 0x0004 /* signal that no header is present
+ (streams are added dynamically) */
+#define AVFMT_SHOW_IDS 0x0008 /* show format stream IDs numbers */
+#define AVFMT_RGB24 0x0010 /* force RGB24 output for ppm (hack
+ - need better api) */
+#define AVFMT_RAWPICTURE 0x0020 /* format wants AVPicture structure for
+ raw picture data */
+
+typedef struct AVOutputFormat {
+ const char *name;
+ const char *long_name;
+ const char *mime_type;
+ const char *extensions; /* comma separated extensions */
+ /* size of private data so that it can be allocated in the wrapper */
+ int priv_data_size;
+ /* output support */
+ enum CodecID audio_codec; /* default audio codec */
+ enum CodecID video_codec; /* default video codec */
+ int (*write_header)(struct AVFormatContext *);
+ /* XXX: change prototype for 64 bit pts */
+ int (*write_packet)(struct AVFormatContext *,
+ int stream_index,
+ unsigned char *buf, int size, int force_pts);
+ int (*write_trailer)(struct AVFormatContext *);
+ /* can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER */
+ int flags;
+ /* private fields */
+ struct AVOutputFormat *next;
+} AVOutputFormat;
+
+typedef struct AVInputFormat {
+ const char *name;
+ const char *long_name;
+ /* size of private data so that it can be allocated in the wrapper */
+ int priv_data_size;
+ /* tell if a given file has a chance of being parsing by this format */
+ int (*read_probe)(AVProbeData *);
+ /* read the format header and initialize the AVFormatContext
+ structure. Return 0 if OK. 'ap' if non NULL contains
+ additionnal paramters. Only used in raw format right
+ now. 'av_new_stream' should be called to create new streams. */
+ int (*read_header)(struct AVFormatContext *,
+ AVFormatParameters *ap);
+ /* read one packet and put it in 'pkt'. pts and flags are also
+ set. 'av_new_stream' can be called only if the flag
+ AVFMT_NOHEADER is used. */
+ int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);
+ /* close the stream. The AVFormatContext and AVStreams are not
+ freed by this function */
+ int (*read_close)(struct AVFormatContext *);
+ /* seek at or before a given pts (given in microsecond). The pts
+ origin is defined by the stream */
+ int (*read_seek)(struct AVFormatContext *, INT64 pts);
+ /* can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_NOHEADER */
+ int flags;
+ /* if extensions are defined, then no probe is done. You should
+ usually not use extension format guessing because it is not
+ reliable enough */
+ const char *extensions;
+ /* general purpose read only value that the format can use */
+ int value;
+ /* private fields */
+ struct AVInputFormat *next;
+} AVInputFormat;
+
+typedef struct AVStream {
+ int index; /* stream index in AVFormatContext */
+ int id; /* format specific stream id */
+ AVCodecContext codec; /* codec context */
+ int r_frame_rate; /* real frame rate of the stream */
+ uint64_t time_length; /* real length of the stream in miliseconds */
+ void *priv_data;
+ /* internal data used in av_find_stream_info() */
+ int codec_info_state;
+ int codec_info_nb_repeat_frames;
+ int codec_info_nb_real_frames;
+ /* PTS generation when outputing stream */
+ AVFrac pts;
+ /* ffmpeg.c private use */
+ int stream_copy; /* if TRUE, just copy stream */
+} AVStream;
+
+#define MAX_STREAMS 20
+
+/* format I/O context */
+typedef struct AVFormatContext {
+ /* can only be iformat or oformat, not both at the same time */
+ struct AVInputFormat *iformat;
+ struct AVOutputFormat *oformat;
+ void *priv_data;
+ ByteIOContext pb;
+ int nb_streams;
+ AVStream *streams[MAX_STREAMS];
+ char filename[1024]; /* input or output filename */
+ /* stream info */
+ char title[512];
+ char author[512];
+ char copyright[512];
+ char comment[512];
+ int flags; /* format specific flags */
+ /* private data for pts handling (do not modify directly) */
+ int pts_wrap_bits; /* number of bits in pts (used for wrapping control) */
+ int pts_num, pts_den; /* value to convert to seconds */
+ /* This buffer is only needed when packets were already buffered but
+ not decoded, for example to get the codec parameters in mpeg
+ streams */
+ struct AVPacketList *packet_buffer;
+} AVFormatContext;
+
+typedef struct AVPacketList {
+ AVPacket pkt;
+ struct AVPacketList *next;
+} AVPacketList;
+
+extern AVInputFormat *first_iformat;
+extern AVOutputFormat *first_oformat;
+
+/* XXX: use automatic init with either ELF sections or C file parser */
+/* modules */
+
+/* mpeg.c */
+int mpegps_init(void);
+
+/* mpegts.c */
+extern AVInputFormat mpegts_demux;
+int mpegts_init(void);
+
+/* rm.c */
+int rm_init(void);
+
+/* crc.c */
+int crc_init(void);
+
+/* img.c */
+int img_init(void);
+
+/* asf.c */
+int asf_init(void);
+
+/* avienc.c */
+int avienc_init(void);
+
+/* avidec.c */
+int avidec_init(void);
+
+/* swf.c */
+int swf_init(void);
+
+/* mov.c */
+int mov_init(void);
+
+/* jpeg.c */
+int jpeg_init(void);
+
+/* gif.c */
+int gif_init(void);
+
+/* au.c */
+int au_init(void);
+
+/* wav.c */
+int wav_init(void);
+
+/* raw.c */
+int raw_init(void);
+
+/* ogg.c */
+int ogg_init(void);
+
+/* dv.c */
+int dv_init(void);
+
+/* ffm.c */
+int ffm_init(void);
+
+/* rtsp.c */
+extern AVInputFormat redir_demux;
+int redir_open(AVFormatContext **ic_ptr, ByteIOContext *f);
+
+#include "rtp.h"
+
+#include "rtsp.h"
+
+/* utils.c */
+#define MKTAG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
+#define MKBETAG(a,b,c,d) (d | (c << 8) | (b << 16) | (a << 24))
+
+void av_register_input_format(AVInputFormat *format);
+void av_register_output_format(AVOutputFormat *format);
+AVOutputFormat *guess_stream_format(const char *short_name,
+ const char *filename, const char *mime_type);
+AVOutputFormat *guess_format(const char *short_name,
+ const char *filename, const char *mime_type);
+
+void av_hex_dump(UINT8 *buf, int size);
+
+void av_register_all(void);
+
+typedef struct FifoBuffer {
+ UINT8 *buffer;
+ UINT8 *rptr, *wptr, *end;
+} FifoBuffer;
+
+int fifo_init(FifoBuffer *f, int size);
+void fifo_free(FifoBuffer *f);
+int fifo_size(FifoBuffer *f, UINT8 *rptr);
+int fifo_read(FifoBuffer *f, UINT8 *buf, int buf_size, UINT8 **rptr_ptr);
+void fifo_write(FifoBuffer *f, UINT8 *buf, int size, UINT8 **wptr_ptr);
+
+/* media file input */
+AVInputFormat *av_find_input_format(const char *short_name);
+AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened);
+int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
+ AVInputFormat *fmt,
+ int buf_size,
+ AVFormatParameters *ap);
+
+#define AVERROR_UNKNOWN (-1) /* unknown error */
+#define AVERROR_IO (-2) /* i/o error */
+#define AVERROR_NUMEXPECTED (-3) /* number syntax expected in filename */
+#define AVERROR_INVALIDDATA (-4) /* invalid data found */
+#define AVERROR_NOMEM (-5) /* not enough memory */
+#define AVERROR_NOFMT (-6) /* unknown format */
+
+int av_find_stream_info(AVFormatContext *ic);
+int av_read_packet(AVFormatContext *s, AVPacket *pkt);
+void av_close_input_file(AVFormatContext *s);
+AVStream *av_new_stream(AVFormatContext *s, int id);
+void av_set_pts_info(AVFormatContext *s, int pts_wrap_bits,
+ int pts_num, int pts_den);
+
+/* media file output */
+int av_write_header(AVFormatContext *s);
+int av_write_frame(AVFormatContext *s, int stream_index, const uint8_t *buf,
+ int size);
+int av_write_trailer(AVFormatContext *s);
+
+void dump_format(AVFormatContext *ic,
+ int index,
+ const char *url,
+ int is_output);
+int parse_image_size(int *width_ptr, int *height_ptr, const char *str);
+INT64 parse_date(const char *datestr, int duration);
+
+INT64 av_gettime(void);
+
+/* ffm specific for ffserver */
+#define FFM_PACKET_SIZE 4096
+offset_t ffm_read_write_index(int fd);
+void ffm_write_write_index(int fd, offset_t pos);
+void ffm_set_write_index(AVFormatContext *s, offset_t pos, offset_t file_size);
+
+int find_info_tag(char *arg, int arg_size, const char *tag1, const char *info);
+
+int get_frame_filename(char *buf, int buf_size,
+ const char *path, int number);
+int filename_number_test(const char *filename);
+
+/* grab specific */
+int video_grab_init(void);
+int audio_init(void);
+
+extern const char *v4l_device;
+extern const char *audio_device;
+
+#ifdef HAVE_AV_CONFIG_H
+int strstart(const char *str, const char *val, const char **ptr);
+int stristart(const char *str, const char *val, const char **ptr);
+void pstrcpy(char *buf, int buf_size, const char *str);
+char *pstrcat(char *buf, int buf_size, const char *s);
+
+struct in_addr;
+int resolve_host(struct in_addr *sin_addr, const char *hostname);
+
+void url_split(char *proto, int proto_size,
+ char *hostname, int hostname_size,
+ int *port_ptr,
+ char *path, int path_size,
+ const char *url);
+
+int match_ext(const char *filename, const char *extensions);
+
+#endif /* HAVE_AV_CONFIG_H */
+
+#endif /* AVFORMAT_H */
diff --git a/libavformat/avi.h b/libavformat/avi.h
new file mode 100644
index 0000000000..d1b5eb5789
--- /dev/null
+++ b/libavformat/avi.h
@@ -0,0 +1,28 @@
+
+#define AVIF_HASINDEX 0x00000010 // Index at end of file?
+#define AVIF_MUSTUSEINDEX 0x00000020
+#define AVIF_ISINTERLEAVED 0x00000100
+#define AVIF_TRUSTCKTYPE 0x00000800 // Use CKType to find key frames?
+#define AVIF_WASCAPTUREFILE 0x00010000
+#define AVIF_COPYRIGHTED 0x00020000
+
+offset_t start_tag(ByteIOContext *pb, const char *tag);
+void end_tag(ByteIOContext *pb, offset_t start);
+
+typedef struct CodecTag {
+ int id;
+ unsigned int tag;
+ unsigned int invalid_asf : 1;
+} CodecTag;
+
+void put_bmp_header(ByteIOContext *pb, AVCodecContext *enc, const CodecTag *tags, int for_asf);
+int put_wav_header(ByteIOContext *pb, AVCodecContext *enc);
+int wav_codec_get_id(unsigned int tag, int bps);
+void get_wav_header(ByteIOContext *pb, AVCodecContext *codec,
+ int has_extra_data);
+
+extern const CodecTag codec_bmp_tags[];
+extern const CodecTag codec_wav_tags[];
+
+unsigned int codec_get_tag(const CodecTag *tags, int id);
+int codec_get_id(const CodecTag *tags, unsigned int tag);
diff --git a/libavformat/avidec.c b/libavformat/avidec.c
new file mode 100644
index 0000000000..9646408cd1
--- /dev/null
+++ b/libavformat/avidec.c
@@ -0,0 +1,256 @@
+/*
+ * AVI decoder.
+ * Copyright (c) 2001 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"
+#include "avi.h"
+
+//#define DEBUG
+
+typedef struct AVIIndex {
+ unsigned char tag[4];
+ unsigned int flags, pos, len;
+ struct AVIIndex *next;
+} AVIIndex;
+
+typedef struct {
+ INT64 movi_end;
+ offset_t movi_list;
+ AVIIndex *first, *last;
+} AVIContext;
+
+#ifdef DEBUG
+static void print_tag(const char *str, unsigned int tag, int size)
+{
+ printf("%s: tag=%c%c%c%c size=0x%x\n",
+ str, tag & 0xff,
+ (tag >> 8) & 0xff,
+ (tag >> 16) & 0xff,
+ (tag >> 24) & 0xff,
+ size);
+}
+#endif
+
+static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
+{
+ AVIContext *avi = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ UINT32 tag, tag1;
+ int codec_type, stream_index, size, frame_period, bit_rate;
+ int i;
+ AVStream *st;
+
+ /* check RIFF header */
+ tag = get_le32(pb);
+
+ if (tag != MKTAG('R', 'I', 'F', 'F'))
+ return -1;
+ get_le32(pb); /* file size */
+ tag = get_le32(pb);
+ if (tag != MKTAG('A', 'V', 'I', ' '))
+ return -1;
+
+ /* first list tag */
+ stream_index = -1;
+ codec_type = -1;
+ frame_period = 0;
+ for(;;) {
+ if (url_feof(pb))
+ goto fail;
+ tag = get_le32(pb);
+ size = get_le32(pb);
+#ifdef DEBUG
+ print_tag("tag", tag, size);
+#endif
+
+ switch(tag) {
+ case MKTAG('L', 'I', 'S', 'T'):
+ /* ignored, except when start of video packets */
+ tag1 = get_le32(pb);
+#ifdef DEBUG
+ print_tag("list", tag1, 0);
+#endif
+ if (tag1 == MKTAG('m', 'o', 'v', 'i')) {
+ avi->movi_end = url_ftell(pb) + size - 4;
+#ifdef DEBUG
+ printf("movi end=%Lx\n", avi->movi_end);
+#endif
+ goto end_of_header;
+ }
+ break;
+ case MKTAG('a', 'v', 'i', 'h'):
+ /* avi header */
+ /* using frame_period is bad idea */
+ frame_period = get_le32(pb);
+ bit_rate = get_le32(pb) * 8;
+ url_fskip(pb, 4 * 4);
+ s->nb_streams = get_le32(pb);
+ for(i=0;i<s->nb_streams;i++) {
+ AVStream *st = av_mallocz(sizeof(AVStream));
+ if (!st)
+ goto fail;
+ s->streams[i] = st;
+ }
+ url_fskip(pb, size - 7 * 4);
+ break;
+ case MKTAG('s', 't', 'r', 'h'):
+ /* stream header */
+ stream_index++;
+ tag1 = get_le32(pb);
+ switch(tag1) {
+ case MKTAG('v', 'i', 'd', 's'):
+ codec_type = CODEC_TYPE_VIDEO;
+ get_le32(pb); /* codec tag */
+ get_le32(pb); /* flags */
+ get_le16(pb); /* priority */
+ get_le16(pb); /* language */
+ get_le32(pb); /* XXX: initial frame ? */
+ get_le32(pb); /* scale */
+ get_le32(pb); /* rate */
+ size -= 6 * 4;
+ break;
+ case MKTAG('a', 'u', 'd', 's'):
+ codec_type = CODEC_TYPE_AUDIO;
+ /* nothing really useful */
+ }
+ url_fskip(pb, size - 4);
+ break;
+ case MKTAG('s', 't', 'r', 'f'):
+ /* stream header */
+ if (stream_index >= s->nb_streams) {
+ url_fskip(pb, size);
+ } else {
+ st = s->streams[stream_index];
+ switch(codec_type) {
+ case CODEC_TYPE_VIDEO:
+ get_le32(pb); /* size */
+ st->codec.width = get_le32(pb);
+ st->codec.height = get_le32(pb);
+ if (frame_period)
+ st->codec.frame_rate = (INT64_C(1000000) * FRAME_RATE_BASE) / frame_period;
+ else
+ st->codec.frame_rate = 25 * FRAME_RATE_BASE;
+ get_le16(pb); /* panes */
+ get_le16(pb); /* depth */
+ tag1 = get_le32(pb);
+#ifdef DEBUG
+ print_tag("video", tag1, 0);
+#endif
+ st->codec.codec_type = CODEC_TYPE_VIDEO;
+ st->codec.codec_tag = tag1;
+ st->codec.codec_id = codec_get_id(codec_bmp_tags, tag1);
+ url_fskip(pb, size - 5 * 4);
+ break;
+ case CODEC_TYPE_AUDIO:
+ get_wav_header(pb, &st->codec, (size >= 18));
+ break;
+ default:
+ url_fskip(pb, size);
+ break;
+ }
+ }
+ break;
+ default:
+ /* skip tag */
+ size += (size & 1);
+ url_fskip(pb, size);
+ break;
+ }
+ }
+ end_of_header:
+ /* check stream number */
+ if (stream_index != s->nb_streams - 1) {
+ fail:
+ for(i=0;i<s->nb_streams;i++) {
+ av_freep(&s->streams[i]);
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+static int avi_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVIContext *avi = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ int n, d1, d2, size;
+
+ for(;;) {
+ if (url_feof(pb) || url_ftell(pb) >= avi->movi_end)
+ return -1;
+ d1 = get_byte(pb) - '0';
+ d2 = get_byte(pb) - '0';
+ if (d1 < 0 || d1 > 9 || d2 < 0 || d2 > 9)
+ continue;
+
+ n = d1 * 10 + d2;
+ if (n < 0 || n >= s->nb_streams)
+ continue;
+
+ d1 = get_byte(pb);
+ d2 = get_byte(pb);
+ if ((d1 == 'd' && d2 == 'c')
+ || (d1 == 'w' && d2 == 'b'))
+ break;
+ }
+ size = get_le32(pb);
+ av_new_packet(pkt, size);
+ pkt->stream_index = n;
+
+ get_buffer(pb, pkt->data, pkt->size);
+
+ if (size & 1)
+ get_byte(pb);
+
+ return 0;
+}
+
+static int avi_read_close(AVFormatContext *s)
+{
+ return 0;
+}
+
+static int avi_probe(AVProbeData *p)
+{
+ /* check file header */
+ if (p->buf_size <= 32)
+ return 0;
+ if (p->buf[0] == 'R' && p->buf[1] == 'I' &&
+ p->buf[2] == 'F' && p->buf[3] == 'F' &&
+ p->buf[8] == 'A' && p->buf[9] == 'V' &&
+ p->buf[10] == 'I' && p->buf[11] == ' ')
+ return AVPROBE_SCORE_MAX;
+ else
+ return 0;
+}
+
+static AVInputFormat avi_iformat = {
+ "avi",
+ "avi format",
+ sizeof(AVIContext),
+ avi_probe,
+ avi_read_header,
+ avi_read_packet,
+ avi_read_close,
+};
+
+int avidec_init(void)
+{
+ av_register_input_format(&avi_iformat);
+ return 0;
+}
diff --git a/libavformat/avienc.c b/libavformat/avienc.c
new file mode 100644
index 0000000000..b4298b0b39
--- /dev/null
+++ b/libavformat/avienc.c
@@ -0,0 +1,432 @@
+/*
+ * AVI encoder.
+ * Copyright (c) 2000 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"
+#include "avi.h"
+
+/*
+ * TODO:
+ * - fill all fields if non streamed (nb_frames for example)
+ */
+
+typedef struct AVIIndex {
+ unsigned char tag[4];
+ unsigned int flags, pos, len;
+ struct AVIIndex *next;
+} AVIIndex;
+
+typedef struct {
+ offset_t movi_list, frames_hdr_all, frames_hdr_strm[MAX_STREAMS];
+ int audio_strm_length[MAX_STREAMS];
+ AVIIndex *first, *last;
+} AVIContext;
+
+offset_t start_tag(ByteIOContext *pb, const char *tag)
+{
+ put_tag(pb, tag);
+ put_le32(pb, 0);
+ return url_ftell(pb);
+}
+
+void end_tag(ByteIOContext *pb, offset_t start)
+{
+ offset_t pos;
+
+ pos = url_ftell(pb);
+ url_fseek(pb, start - 4, SEEK_SET);
+ put_le32(pb, (UINT32)(pos - start));
+ url_fseek(pb, pos, SEEK_SET);
+}
+
+/* Note: when encoding, the first matching tag is used, so order is
+ important if multiple tags possible for a given codec. */
+const CodecTag codec_bmp_tags[] = {
+ { CODEC_ID_H263, MKTAG('H', '2', '6', '3') },
+ { CODEC_ID_H263P, MKTAG('H', '2', '6', '3') },
+ { CODEC_ID_H263I, MKTAG('I', '2', '6', '3') }, /* intel h263 */
+ { CODEC_ID_MJPEG, MKTAG('M', 'J', 'P', 'G') },
+ { CODEC_ID_MPEG4, MKTAG('D', 'I', 'V', 'X'), .invalid_asf = 1 },
+ { CODEC_ID_MPEG4, MKTAG('d', 'i', 'v', 'x'), .invalid_asf = 1 },
+ { CODEC_ID_MPEG4, MKTAG('D', 'X', '5', '0'), .invalid_asf = 1 },
+ { CODEC_ID_MPEG4, MKTAG('X', 'V', 'I', 'D'), .invalid_asf = 1 },
+ { CODEC_ID_MPEG4, MKTAG('x', 'v', 'i', 'd'), .invalid_asf = 1 },
+ { CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 's'), .invalid_asf = 1 },
+ { CODEC_ID_MPEG4, MKTAG('M', 'P', '4', 'S') },
+ { CODEC_ID_MPEG4, MKTAG('M', '4', 'S', '2') },
+ { CODEC_ID_MPEG4, MKTAG('m', '4', 's', '2') },
+ { CODEC_ID_MPEG4, MKTAG(0x04, 0, 0, 0) }, /* some broken avi use this */
+ { CODEC_ID_MSMPEG4V3, MKTAG('D', 'I', 'V', '3'), .invalid_asf = 1 }, /* default signature when using MSMPEG4 */
+ { CODEC_ID_MSMPEG4V3, MKTAG('d', 'i', 'v', '3'), .invalid_asf = 1 },
+ { CODEC_ID_MSMPEG4V3, MKTAG('M', 'P', '4', '3') },
+ { CODEC_ID_MSMPEG4V2, MKTAG('M', 'P', '4', '2') },
+ { CODEC_ID_MSMPEG4V1, MKTAG('M', 'P', 'G', '4') },
+ { CODEC_ID_WMV1, MKTAG('W', 'M', 'V', '1') },
+ { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 's', 'l') },
+ { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 's', 'd') },
+ { CODEC_ID_DVVIDEO, MKTAG('D', 'V', 'S', 'D') },
+ { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', 'd') },
+ { CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'g', '1') },
+ { CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'g', '2') },
+ { CODEC_ID_MPEG1VIDEO, MKTAG('P', 'I', 'M', '1') },
+ { CODEC_ID_MJPEG, MKTAG('M', 'J', 'P', 'G') },
+ { 0, 0 },
+};
+
+unsigned int codec_get_tag(const CodecTag *tags, int id)
+{
+ while (tags->id != 0) {
+ if (tags->id == id)
+ return tags->tag;
+ tags++;
+ }
+ return 0;
+}
+
+static unsigned int codec_get_asf_tag(const CodecTag *tags, int id)
+{
+ while (tags->id != 0) {
+ if (!tags->invalid_asf && tags->id == id)
+ return tags->tag;
+ tags++;
+ }
+ return 0;
+}
+
+int codec_get_id(const CodecTag *tags, unsigned int tag)
+{
+ while (tags->id != 0) {
+ if (tags->tag == tag)
+ return tags->id;
+ tags++;
+ }
+ return 0;
+}
+
+unsigned int codec_get_bmp_tag(int id)
+{
+ return codec_get_tag(codec_bmp_tags, id);
+}
+
+/* BITMAPINFOHEADER header */
+void put_bmp_header(ByteIOContext *pb, AVCodecContext *enc, const CodecTag *tags, int for_asf)
+{
+ put_le32(pb, 40); /* size */
+ put_le32(pb, enc->width);
+ put_le32(pb, enc->height);
+ put_le16(pb, 1); /* planes */
+ put_le16(pb, 24); /* depth */
+ /* compression type */
+ put_le32(pb, for_asf ? codec_get_asf_tag(tags, enc->codec_id) : codec_get_tag(tags, enc->codec_id));
+ put_le32(pb, enc->width * enc->height * 3);
+ put_le32(pb, 0);
+ put_le32(pb, 0);
+ put_le32(pb, 0);
+ put_le32(pb, 0);
+}
+
+static void parse_specific_params(AVCodecContext *stream, int *au_byterate, int *au_ssize, int *au_scale)
+{
+ switch(stream->codec_id) {
+ case CODEC_ID_PCM_S16LE:
+ *au_scale = *au_ssize = 2*stream->channels;
+ *au_byterate = *au_ssize * stream->sample_rate;
+ break;
+ case CODEC_ID_PCM_U8:
+ case CODEC_ID_PCM_ALAW:
+ case CODEC_ID_PCM_MULAW:
+ *au_scale = *au_ssize = stream->channels;
+ *au_byterate = *au_ssize * stream->sample_rate;
+ break;
+ case CODEC_ID_MP2:
+ *au_ssize = 1;
+ *au_scale = 1;
+ *au_byterate = stream->bit_rate / 8;
+ case CODEC_ID_MP3LAME:
+ *au_ssize = 1;
+ *au_scale = 1;
+ *au_byterate = stream->bit_rate / 8;
+ default:
+ *au_ssize = 1;
+ *au_scale = 1;
+ *au_byterate = stream->bit_rate / 8;
+ break;
+ }
+}
+
+static int avi_write_header(AVFormatContext *s)
+{
+ AVIContext *avi = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ int bitrate, n, i, nb_frames, au_byterate, au_ssize, au_scale;
+ AVCodecContext *stream, *video_enc;
+ offset_t list1, list2, strh, strf;
+
+ put_tag(pb, "RIFF");
+ put_le32(pb, 0); /* file length */
+ put_tag(pb, "AVI ");
+
+ /* header list */
+ list1 = start_tag(pb, "LIST");
+ put_tag(pb, "hdrl");
+
+ /* avi header */
+ put_tag(pb, "avih");
+ put_le32(pb, 14 * 4);
+ bitrate = 0;
+
+ video_enc = NULL;
+ for(n=0;n<s->nb_streams;n++) {
+ stream = &s->streams[n]->codec;
+ bitrate += stream->bit_rate;
+ if (stream->codec_type == CODEC_TYPE_VIDEO)
+ video_enc = stream;
+ }
+
+ if (!video_enc) {
+ av_free(avi);
+ return -1;
+ }
+ nb_frames = 0;
+
+ put_le32(pb, (UINT32)(INT64_C(1000000) * FRAME_RATE_BASE / video_enc->frame_rate));
+ put_le32(pb, bitrate / 8); /* XXX: not quite exact */
+ put_le32(pb, 0); /* padding */
+ put_le32(pb, AVIF_TRUSTCKTYPE | AVIF_HASINDEX | AVIF_ISINTERLEAVED); /* flags */
+ avi->frames_hdr_all = url_ftell(pb); /* remember this offset to fill later */
+ put_le32(pb, nb_frames); /* nb frames, filled later */
+ put_le32(pb, 0); /* initial frame */
+ put_le32(pb, s->nb_streams); /* nb streams */
+ put_le32(pb, 1024 * 1024); /* suggested buffer size */
+ put_le32(pb, video_enc->width);
+ put_le32(pb, video_enc->height);
+ put_le32(pb, 0); /* reserved */
+ put_le32(pb, 0); /* reserved */
+ put_le32(pb, 0); /* reserved */
+ put_le32(pb, 0); /* reserved */
+
+ /* stream list */
+ for(i=0;i<n;i++) {
+ list2 = start_tag(pb, "LIST");
+ put_tag(pb, "strl");
+
+ stream = &s->streams[i]->codec;
+
+ /* stream generic header */
+ strh = start_tag(pb, "strh");
+ switch(stream->codec_type) {
+ case CODEC_TYPE_VIDEO:
+ put_tag(pb, "vids");
+ put_le32(pb, codec_get_bmp_tag(stream->codec_id));
+ put_le32(pb, 0); /* flags */
+ put_le16(pb, 0); /* priority */
+ put_le16(pb, 0); /* language */
+ put_le32(pb, 0); /* initial frame */
+ put_le32(pb, 1000); /* scale */
+ put_le32(pb, (1000 * stream->frame_rate) / FRAME_RATE_BASE); /* rate */
+ put_le32(pb, 0); /* start */
+ avi->frames_hdr_strm[i] = url_ftell(pb); /* remember this offset to fill later */
+ put_le32(pb, nb_frames); /* length, XXX: fill later */
+ put_le32(pb, 1024 * 1024); /* suggested buffer size */
+ put_le32(pb, -1); /* quality */
+ put_le32(pb, stream->width * stream->height * 3); /* sample size */
+ put_le16(pb, 0);
+ put_le16(pb, 0);
+ put_le16(pb, stream->width);
+ put_le16(pb, stream->height);
+ break;
+ case CODEC_TYPE_AUDIO:
+ put_tag(pb, "auds");
+ put_le32(pb, 1); /* tag */
+ put_le32(pb, 0); /* flags */
+ put_le16(pb, 0); /* priority */
+ put_le16(pb, 0); /* language */
+ put_le32(pb, 0); /* initial frame */
+ parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale);
+ put_le32(pb, au_scale); /* scale */
+ put_le32(pb, au_byterate); /* rate */
+ put_le32(pb, 0); /* start */
+ avi->frames_hdr_strm[i] = url_ftell(pb); /* remember this offset to fill later */
+ put_le32(pb, 0); /* length, XXX: filled later */
+ put_le32(pb, 12 * 1024); /* suggested buffer size */
+ put_le32(pb, -1); /* quality */
+ put_le32(pb, au_ssize); /* sample size */
+ put_le32(pb, 0);
+ put_le32(pb, 0);
+ break;
+ default:
+ av_abort();
+ }
+ end_tag(pb, strh);
+
+ strf = start_tag(pb, "strf");
+ switch(stream->codec_type) {
+ case CODEC_TYPE_VIDEO:
+ put_bmp_header(pb, stream, codec_bmp_tags, 0);
+ break;
+ case CODEC_TYPE_AUDIO:
+ if (put_wav_header(pb, stream) < 0) {
+ av_free(avi);
+ return -1;
+ }
+ break;
+ default:
+ av_abort();
+ }
+ end_tag(pb, strf);
+ end_tag(pb, list2);
+ }
+
+ end_tag(pb, list1);
+
+ avi->movi_list = start_tag(pb, "LIST");
+ avi->first = NULL;
+ avi->last = NULL;
+ put_tag(pb, "movi");
+
+ put_flush_packet(pb);
+
+ return 0;
+}
+
+static int avi_write_packet(AVFormatContext *s, int stream_index,
+ UINT8 *buf, int size, int force_pts)
+{
+ AVIContext *avi = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ AVIIndex *idx;
+ unsigned char tag[5];
+ unsigned int flags;
+ AVCodecContext *enc;
+
+ enc = &s->streams[stream_index]->codec;
+
+ tag[0] = '0';
+ tag[1] = '0' + stream_index;
+ if (enc->codec_type == CODEC_TYPE_VIDEO) {
+ tag[2] = 'd';
+ tag[3] = 'c';
+ flags = enc->key_frame ? 0x10 : 0x00;
+ } else {
+ tag[2] = 'w';
+ tag[3] = 'b';
+ flags = 0x10;
+ }
+ if (enc->codec_type == CODEC_TYPE_AUDIO)
+ avi->audio_strm_length[stream_index] += size;
+
+ if (!url_is_streamed(&s->pb)) {
+ idx = av_malloc(sizeof(AVIIndex));
+ memcpy(idx->tag, tag, 4);
+ idx->flags = flags;
+ idx->pos = url_ftell(pb) - avi->movi_list;
+ idx->len = size;
+ idx->next = NULL;
+ if (!avi->last)
+ avi->first = idx;
+ else
+ avi->last->next = idx;
+ avi->last = idx;
+ }
+
+ put_buffer(pb, tag, 4);
+ put_le32(pb, size);
+ put_buffer(pb, buf, size);
+ if (size & 1)
+ put_byte(pb, 0);
+
+ put_flush_packet(pb);
+ return 0;
+}
+
+static int avi_write_trailer(AVFormatContext *s)
+{
+ ByteIOContext *pb = &s->pb;
+ AVIContext *avi = s->priv_data;
+ offset_t file_size, idx_chunk;
+ int n, nb_frames, au_byterate, au_ssize, au_scale;
+ AVCodecContext *stream;
+ AVIIndex *idx;
+
+ if (!url_is_streamed(&s->pb)) {
+ end_tag(pb, avi->movi_list);
+
+ idx_chunk = start_tag(pb, "idx1");
+ idx = avi->first;
+ while (idx != NULL) {
+ put_buffer(pb, idx->tag, 4);
+ put_le32(pb, idx->flags);
+ put_le32(pb, idx->pos);
+ put_le32(pb, idx->len);
+ idx = idx->next;
+ }
+ end_tag(pb, idx_chunk);
+
+ /* update file size */
+ file_size = url_ftell(pb);
+ url_fseek(pb, 4, SEEK_SET);
+ put_le32(pb, (UINT32)(file_size - 8));
+
+ /* Fill in frame/sample counters */
+ nb_frames = 0;
+ for(n=0;n<s->nb_streams;n++) {
+ if (avi->frames_hdr_strm[n] != 0) {
+ stream = &s->streams[n]->codec;
+ url_fseek(pb, avi->frames_hdr_strm[n], SEEK_SET);
+ if (stream->codec_type == CODEC_TYPE_VIDEO) {
+ put_le32(pb, stream->frame_number);
+ if (nb_frames < stream->frame_number)
+ nb_frames = stream->frame_number;
+ } else {
+ if (stream->codec_id == CODEC_ID_MP2 || stream->codec_id == CODEC_ID_MP3LAME) {
+ put_le32(pb, stream->frame_number);
+ nb_frames += stream->frame_number;
+ } else {
+ parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale);
+ put_le32(pb, avi->audio_strm_length[n] / au_ssize);
+ }
+ }
+ }
+ }
+ if (avi->frames_hdr_all != 0) {
+ url_fseek(pb, avi->frames_hdr_all, SEEK_SET);
+ put_le32(pb, nb_frames);
+ }
+ url_fseek(pb, file_size, SEEK_SET);
+ }
+ put_flush_packet(pb);
+ return 0;
+}
+
+static AVOutputFormat avi_oformat = {
+ "avi",
+ "avi format",
+ "video/x-msvideo",
+ "avi",
+ sizeof(AVIContext),
+ CODEC_ID_MP2,
+ CODEC_ID_MSMPEG4V3,
+ avi_write_header,
+ avi_write_packet,
+ avi_write_trailer,
+};
+
+int avienc_init(void)
+{
+ av_register_output_format(&avi_oformat);
+ return 0;
+}
diff --git a/libavformat/avio.c b/libavformat/avio.c
new file mode 100644
index 0000000000..37af56f9af
--- /dev/null
+++ b/libavformat/avio.c
@@ -0,0 +1,156 @@
+/*
+ * Unbuffered io for ffmpeg system
+ * Copyright (c) 2001 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"
+
+URLProtocol *first_protocol = NULL;
+
+int register_protocol(URLProtocol *protocol)
+{
+ URLProtocol **p;
+ p = &first_protocol;
+ while (*p != NULL) p = &(*p)->next;
+ *p = protocol;
+ protocol->next = NULL;
+ return 0;
+}
+
+int url_open(URLContext **puc, const char *filename, int flags)
+{
+ URLContext *uc;
+ URLProtocol *up;
+ const char *p;
+ char proto_str[128], *q;
+ int err;
+
+ p = filename;
+ q = proto_str;
+ while (*p != '\0' && *p != ':') {
+ if ((q - proto_str) < sizeof(proto_str) - 1)
+ *q++ = *p;
+ p++;
+ }
+ /* if the protocol has length 1, we consider it is a dos drive */
+ if (*p == '\0' || (q - proto_str) <= 1) {
+ strcpy(proto_str, "file");
+ } else {
+ *q = '\0';
+ }
+
+ up = first_protocol;
+ while (up != NULL) {
+ if (!strcmp(proto_str, up->name))
+ goto found;
+ up = up->next;
+ }
+ err = -ENOENT;
+ goto fail;
+ found:
+ uc = av_malloc(sizeof(URLContext));
+ if (!uc) {
+ err = -ENOMEM;
+ goto fail;
+ }
+ uc->prot = up;
+ uc->flags = flags;
+ uc->is_streamed = 0; /* default = not streamed */
+ uc->max_packet_size = 0; /* default: stream file */
+ err = up->url_open(uc, filename, flags);
+ if (err < 0) {
+ av_free(uc);
+ *puc = NULL;
+ return err;
+ }
+ *puc = uc;
+ return 0;
+ fail:
+ *puc = NULL;
+ return err;
+}
+
+int url_read(URLContext *h, unsigned char *buf, int size)
+{
+ int ret;
+ if (h->flags & URL_WRONLY)
+ return -EIO;
+ ret = h->prot->url_read(h, buf, size);
+ return ret;
+}
+
+int url_write(URLContext *h, unsigned char *buf, int size)
+{
+ int ret;
+ if (!(h->flags & (URL_WRONLY | URL_RDWR)))
+ return -EIO;
+ /* avoid sending too big packets */
+ if (h->max_packet_size && size > h->max_packet_size)
+ return -EIO;
+ ret = h->prot->url_write(h, buf, size);
+ return ret;
+}
+
+offset_t url_seek(URLContext *h, offset_t pos, int whence)
+{
+ offset_t ret;
+
+ if (!h->prot->url_seek)
+ return -EPIPE;
+ ret = h->prot->url_seek(h, pos, whence);
+ return ret;
+}
+
+int url_close(URLContext *h)
+{
+ int ret;
+
+ ret = h->prot->url_close(h);
+ av_free(h);
+ return ret;
+}
+
+int url_exist(const char *filename)
+{
+ URLContext *h;
+ if (url_open(&h, filename, URL_RDONLY) < 0)
+ return 0;
+ url_close(h);
+ return 1;
+}
+
+offset_t url_filesize(URLContext *h)
+{
+ offset_t pos, size;
+
+ pos = url_seek(h, 0, SEEK_CUR);
+ size = url_seek(h, 0, SEEK_END);
+ url_seek(h, pos, SEEK_SET);
+ return size;
+}
+
+/*
+ * Return the maximum packet size associated to packetized file
+ * handle. If the file is not packetized (stream like http or file on
+ * disk), then 0 is returned.
+ *
+ * @param h file handle
+ * @return maximum packet size in bytes
+ */
+int url_get_max_packet_size(URLContext *h)
+{
+ return h->max_packet_size;
+}
diff --git a/libavformat/avio.h b/libavformat/avio.h
new file mode 100644
index 0000000000..541eff5ae0
--- /dev/null
+++ b/libavformat/avio.h
@@ -0,0 +1,153 @@
+#ifndef AVIO_H
+#define AVIO_H
+
+/* output byte stream handling */
+
+typedef INT64 offset_t;
+
+/* unbuffered I/O */
+
+struct URLContext {
+ struct URLProtocol *prot;
+ int flags;
+ int is_streamed; /* true if streamed (no seek possible), default = false */
+ int max_packet_size; /* if non zero, the stream is packetized with this max packet size */
+ void *priv_data;
+};
+
+typedef struct URLContext URLContext;
+
+typedef struct URLPollEntry {
+ URLContext *handle;
+ int events;
+ int revents;
+} URLPollEntry;
+
+#define URL_RDONLY 0
+#define URL_WRONLY 1
+#define URL_RDWR 2
+
+int url_open(URLContext **h, const char *filename, int flags);
+int url_read(URLContext *h, unsigned char *buf, int size);
+int url_write(URLContext *h, unsigned char *buf, int size);
+offset_t url_seek(URLContext *h, offset_t pos, int whence);
+int url_close(URLContext *h);
+int url_exist(const char *filename);
+offset_t url_filesize(URLContext *h);
+int url_get_max_packet_size(URLContext *h);
+/* not implemented */
+int url_poll(URLPollEntry *poll_table, int n, int timeout);
+
+typedef struct URLProtocol {
+ const char *name;
+ int (*url_open)(URLContext *h, const char *filename, int flags);
+ int (*url_read)(URLContext *h, unsigned char *buf, int size);
+ int (*url_write)(URLContext *h, unsigned char *buf, int size);
+ offset_t (*url_seek)(URLContext *h, offset_t pos, int whence);
+ int (*url_close)(URLContext *h);
+ struct URLProtocol *next;
+} URLProtocol;
+
+extern URLProtocol *first_protocol;
+
+int register_protocol(URLProtocol *protocol);
+
+typedef struct {
+ unsigned char *buffer;
+ int buffer_size;
+ unsigned char *buf_ptr, *buf_end;
+ void *opaque;
+ int (*read_packet)(void *opaque, UINT8 *buf, int buf_size);
+ void (*write_packet)(void *opaque, UINT8 *buf, int buf_size);
+ int (*seek)(void *opaque, offset_t offset, int whence);
+ offset_t pos; /* position in the file of the current buffer */
+ int must_flush; /* true if the next seek should flush */
+ int eof_reached; /* true if eof reached */
+ int write_flag; /* true if open for writing */
+ int is_streamed;
+ int max_packet_size;
+} ByteIOContext;
+
+int init_put_byte(ByteIOContext *s,
+ unsigned char *buffer,
+ int buffer_size,
+ int write_flag,
+ void *opaque,
+ int (*read_packet)(void *opaque, UINT8 *buf, int buf_size),
+ void (*write_packet)(void *opaque, UINT8 *buf, int buf_size),
+ int (*seek)(void *opaque, offset_t offset, int whence));
+
+void put_byte(ByteIOContext *s, int b);
+void put_buffer(ByteIOContext *s, const unsigned char *buf, int size);
+void put_le64(ByteIOContext *s, UINT64 val);
+void put_be64(ByteIOContext *s, UINT64 val);
+void put_le32(ByteIOContext *s, unsigned int val);
+void put_be32(ByteIOContext *s, unsigned int val);
+void put_le16(ByteIOContext *s, unsigned int val);
+void put_be16(ByteIOContext *s, unsigned int val);
+void put_tag(ByteIOContext *s, const char *tag);
+
+void put_be64_double(ByteIOContext *s, double val);
+void put_strz(ByteIOContext *s, const char *buf);
+
+offset_t url_fseek(ByteIOContext *s, offset_t offset, int whence);
+void url_fskip(ByteIOContext *s, offset_t offset);
+offset_t url_ftell(ByteIOContext *s);
+int url_feof(ByteIOContext *s);
+
+#define URL_EOF (-1)
+int url_fgetc(ByteIOContext *s);
+int url_fprintf(ByteIOContext *s, const char *fmt, ...);
+char *url_fgets(ByteIOContext *s, char *buf, int buf_size);
+
+void put_flush_packet(ByteIOContext *s);
+
+int get_buffer(ByteIOContext *s, unsigned char *buf, int size);
+int get_byte(ByteIOContext *s);
+unsigned int get_le32(ByteIOContext *s);
+UINT64 get_le64(ByteIOContext *s);
+unsigned int get_le16(ByteIOContext *s);
+
+double get_be64_double(ByteIOContext *s);
+char *get_strz(ByteIOContext *s, char *buf, int maxlen);
+unsigned int get_be16(ByteIOContext *s);
+unsigned int get_be32(ByteIOContext *s);
+UINT64 get_be64(ByteIOContext *s);
+
+static inline int url_is_streamed(ByteIOContext *s)
+{
+ return s->is_streamed;
+}
+
+int url_fdopen(ByteIOContext *s, URLContext *h);
+int url_setbufsize(ByteIOContext *s, int buf_size);
+int url_fopen(ByteIOContext *s, const char *filename, int flags);
+int url_fclose(ByteIOContext *s);
+URLContext *url_fileno(ByteIOContext *s);
+int url_fget_max_packet_size(ByteIOContext *s);
+
+int url_open_buf(ByteIOContext *s, UINT8 *buf, int buf_size, int flags);
+int url_close_buf(ByteIOContext *s);
+
+int url_open_dyn_buf(ByteIOContext *s);
+int url_open_dyn_packet_buf(ByteIOContext *s, int max_packet_size);
+int url_close_dyn_buf(ByteIOContext *s, UINT8 **pbuffer);
+
+/* file.c */
+extern URLProtocol file_protocol;
+extern URLProtocol pipe_protocol;
+
+/* udp.c */
+extern URLProtocol udp_protocol;
+int udp_set_remote_url(URLContext *h, const char *uri);
+int udp_get_local_port(URLContext *h);
+int udp_get_file_handle(URLContext *h);
+
+/* tcp.c */
+extern URLProtocol tcp_protocol;
+
+/* http.c */
+extern URLProtocol http_protocol;
+
+#endif
+
diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c
new file mode 100644
index 0000000000..2e931bd849
--- /dev/null
+++ b/libavformat/aviobuf.c
@@ -0,0 +1,687 @@
+/*
+ * Buffered I/O for ffmpeg system
+ * Copyright (c) 2000,2001 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"
+#include <stdarg.h>
+
+#define IO_BUFFER_SIZE 32768
+
+int init_put_byte(ByteIOContext *s,
+ unsigned char *buffer,
+ int buffer_size,
+ int write_flag,
+ void *opaque,
+ int (*read_packet)(void *opaque, UINT8 *buf, int buf_size),
+ void (*write_packet)(void *opaque, UINT8 *buf, int buf_size),
+ int (*seek)(void *opaque, offset_t offset, int whence))
+{
+ s->buffer = buffer;
+ s->buffer_size = buffer_size;
+ s->buf_ptr = buffer;
+ s->write_flag = write_flag;
+ if (!s->write_flag)
+ s->buf_end = buffer;
+ else
+ s->buf_end = buffer + buffer_size;
+ s->opaque = opaque;
+ s->write_packet = write_packet;
+ s->read_packet = read_packet;
+ s->seek = seek;
+ s->pos = 0;
+ s->must_flush = 0;
+ s->eof_reached = 0;
+ s->is_streamed = 0;
+ s->max_packet_size = 0;
+ return 0;
+}
+
+
+static void flush_buffer(ByteIOContext *s)
+{
+ if (s->buf_ptr > s->buffer) {
+ if (s->write_packet)
+ s->write_packet(s->opaque, s->buffer, s->buf_ptr - s->buffer);
+ s->pos += s->buf_ptr - s->buffer;
+ }
+ s->buf_ptr = s->buffer;
+}
+
+void put_byte(ByteIOContext *s, int b)
+{
+ *(s->buf_ptr)++ = b;
+ if (s->buf_ptr >= s->buf_end)
+ flush_buffer(s);
+}
+
+void put_buffer(ByteIOContext *s, const unsigned char *buf, int size)
+{
+ int len;
+
+ while (size > 0) {
+ len = (s->buf_end - s->buf_ptr);
+ if (len > size)
+ len = size;
+ memcpy(s->buf_ptr, buf, len);
+ s->buf_ptr += len;
+
+ if (s->buf_ptr >= s->buf_end)
+ flush_buffer(s);
+
+ buf += len;
+ size -= len;
+ }
+}
+
+void put_flush_packet(ByteIOContext *s)
+{
+ flush_buffer(s);
+ s->must_flush = 0;
+}
+
+offset_t url_fseek(ByteIOContext *s, offset_t offset, int whence)
+{
+ offset_t offset1;
+
+ if (whence != SEEK_CUR && whence != SEEK_SET)
+ return -EINVAL;
+
+ if (s->write_flag) {
+ if (whence == SEEK_CUR) {
+ offset1 = s->pos + (s->buf_ptr - s->buffer);
+ if (offset == 0)
+ return offset1;
+ offset += offset1;
+ }
+ offset1 = offset - s->pos;
+ if (!s->must_flush &&
+ offset1 >= 0 && offset1 < (s->buf_end - s->buffer)) {
+ /* can do the seek inside the buffer */
+ s->buf_ptr = s->buffer + offset1;
+ } else {
+ if (!s->seek)
+ return -EPIPE;
+ flush_buffer(s);
+ s->must_flush = 1;
+ s->buf_ptr = s->buffer;
+ s->seek(s->opaque, offset, SEEK_SET);
+ s->pos = offset;
+ }
+ } else {
+ if (whence == SEEK_CUR) {
+ offset1 = s->pos - (s->buf_end - s->buffer) + (s->buf_ptr - s->buffer);
+ if (offset == 0)
+ return offset1;
+ offset += offset1;
+ }
+ offset1 = offset - (s->pos - (s->buf_end - s->buffer));
+ if (offset1 >= 0 && offset1 <= (s->buf_end - s->buffer)) {
+ /* can do the seek inside the buffer */
+ s->buf_ptr = s->buffer + offset1;
+ } else {
+ if (!s->seek)
+ return -EPIPE;
+ s->buf_ptr = s->buffer;
+ s->buf_end = s->buffer;
+ s->seek(s->opaque, offset, SEEK_SET);
+ s->pos = offset;
+ }
+ s->eof_reached = 0;
+ }
+ return offset;
+}
+
+void url_fskip(ByteIOContext *s, offset_t offset)
+{
+ url_fseek(s, offset, SEEK_CUR);
+}
+
+offset_t url_ftell(ByteIOContext *s)
+{
+ return url_fseek(s, 0, SEEK_CUR);
+}
+
+int url_feof(ByteIOContext *s)
+{
+ return s->eof_reached;
+}
+
+void put_le32(ByteIOContext *s, unsigned int val)
+{
+ put_byte(s, val);
+ put_byte(s, val >> 8);
+ put_byte(s, val >> 16);
+ put_byte(s, val >> 24);
+}
+
+void put_be32(ByteIOContext *s, unsigned int val)
+{
+ put_byte(s, val >> 24);
+ put_byte(s, val >> 16);
+ put_byte(s, val >> 8);
+ put_byte(s, val);
+}
+
+/* IEEE format is assumed */
+void put_be64_double(ByteIOContext *s, double val)
+{
+ union {
+ double d;
+ UINT64 ull;
+ } u;
+ u.d = val;
+ put_be64(s, u.ull);
+}
+
+void put_strz(ByteIOContext *s, const char *str)
+{
+ if (str)
+ put_buffer(s, (const unsigned char *) str, strlen(str) + 1);
+ else
+ put_byte(s, 0);
+}
+
+void put_le64(ByteIOContext *s, UINT64 val)
+{
+ put_le32(s, (UINT32)(val & 0xffffffff));
+ put_le32(s, (UINT32)(val >> 32));
+}
+
+void put_be64(ByteIOContext *s, UINT64 val)
+{
+ put_be32(s, (UINT32)(val >> 32));
+ put_be32(s, (UINT32)(val & 0xffffffff));
+}
+
+void put_le16(ByteIOContext *s, unsigned int val)
+{
+ put_byte(s, val);
+ put_byte(s, val >> 8);
+}
+
+void put_be16(ByteIOContext *s, unsigned int val)
+{
+ put_byte(s, val >> 8);
+ put_byte(s, val);
+}
+
+void put_tag(ByteIOContext *s, const char *tag)
+{
+ while (*tag) {
+ put_byte(s, *tag++);
+ }
+}
+
+/* Input stream */
+
+static void fill_buffer(ByteIOContext *s)
+{
+ int len;
+
+ /* no need to do anything if EOF already reached */
+ if (s->eof_reached)
+ return;
+ len = s->read_packet(s->opaque, s->buffer, s->buffer_size);
+ if (len <= 0) {
+ /* do not modify buffer if EOF reached so that a seek back can
+ be done without rereading data */
+ s->eof_reached = 1;
+ } else {
+ s->pos += len;
+ s->buf_ptr = s->buffer;
+ s->buf_end = s->buffer + len;
+ }
+}
+
+/* NOTE: return 0 if EOF, so you cannot use it if EOF handling is
+ necessary */
+/* XXX: put an inline version */
+int get_byte(ByteIOContext *s)
+{
+ if (s->buf_ptr < s->buf_end) {
+ return *s->buf_ptr++;
+ } else {
+ fill_buffer(s);
+ if (s->buf_ptr < s->buf_end)
+ return *s->buf_ptr++;
+ else
+ return 0;
+ }
+}
+
+/* NOTE: return URL_EOF (-1) if EOF */
+int url_fgetc(ByteIOContext *s)
+{
+ if (s->buf_ptr < s->buf_end) {
+ return *s->buf_ptr++;
+ } else {
+ fill_buffer(s);
+ if (s->buf_ptr < s->buf_end)
+ return *s->buf_ptr++;
+ else
+ return URL_EOF;
+ }
+}
+
+int get_buffer(ByteIOContext *s, unsigned char *buf, int size)
+{
+ int len, size1;
+
+ size1 = size;
+ while (size > 0) {
+ len = s->buf_end - s->buf_ptr;
+ if (len > size)
+ len = size;
+ if (len == 0) {
+ fill_buffer(s);
+ len = s->buf_end - s->buf_ptr;
+ if (len == 0)
+ break;
+ } else {
+ memcpy(buf, s->buf_ptr, len);
+ buf += len;
+ s->buf_ptr += len;
+ size -= len;
+ }
+ }
+ return size1 - size;
+}
+
+unsigned int get_le16(ByteIOContext *s)
+{
+ unsigned int val;
+ val = get_byte(s);
+ val |= get_byte(s) << 8;
+ return val;
+}
+
+unsigned int get_le32(ByteIOContext *s)
+{
+ unsigned int val;
+ val = get_byte(s);
+ val |= get_byte(s) << 8;
+ val |= get_byte(s) << 16;
+ val |= get_byte(s) << 24;
+ return val;
+}
+
+UINT64 get_le64(ByteIOContext *s)
+{
+ UINT64 val;
+ val = (UINT64)get_le32(s);
+ val |= (UINT64)get_le32(s) << 32;
+ return val;
+}
+
+unsigned int get_be16(ByteIOContext *s)
+{
+ unsigned int val;
+ val = get_byte(s) << 8;
+ val |= get_byte(s);
+ return val;
+}
+
+unsigned int get_be32(ByteIOContext *s)
+{
+ unsigned int val;
+ val = get_byte(s) << 24;
+ val |= get_byte(s) << 16;
+ val |= get_byte(s) << 8;
+ val |= get_byte(s);
+ return val;
+}
+
+double get_be64_double(ByteIOContext *s)
+{
+ union {
+ double d;
+ UINT64 ull;
+ } u;
+
+ u.ull = get_be64(s);
+ return u.d;
+}
+
+char *get_strz(ByteIOContext *s, char *buf, int maxlen)
+{
+ int i = 0;
+ char c;
+
+ while ((c = get_byte(s))) {
+ if (i < maxlen-1)
+ buf[i++] = c;
+ }
+
+ buf[i] = 0; /* Ensure null terminated, but may be truncated */
+
+ return buf;
+}
+
+UINT64 get_be64(ByteIOContext *s)
+{
+ UINT64 val;
+ val = (UINT64)get_be32(s) << 32;
+ val |= (UINT64)get_be32(s);
+ return val;
+}
+
+/* link with avio functions */
+
+void url_write_packet(void *opaque, UINT8 *buf, int buf_size)
+{
+ URLContext *h = opaque;
+ url_write(h, buf, buf_size);
+}
+
+int url_read_packet(void *opaque, UINT8 *buf, int buf_size)
+{
+ URLContext *h = opaque;
+ return url_read(h, buf, buf_size);
+}
+
+int url_seek_packet(void *opaque, INT64 offset, int whence)
+{
+ URLContext *h = opaque;
+ url_seek(h, offset, whence);
+ return 0;
+}
+
+int url_fdopen(ByteIOContext *s, URLContext *h)
+{
+ UINT8 *buffer;
+ int buffer_size, max_packet_size;
+
+
+ max_packet_size = url_get_max_packet_size(h);
+ if (max_packet_size) {
+ buffer_size = max_packet_size; /* no need to bufferize more than one packet */
+ } else {
+ buffer_size = IO_BUFFER_SIZE;
+ }
+ buffer = av_malloc(buffer_size);
+ if (!buffer)
+ return -ENOMEM;
+
+ if (init_put_byte(s, buffer, buffer_size,
+ (h->flags & URL_WRONLY) != 0, h,
+ url_read_packet, url_write_packet, url_seek_packet) < 0) {
+ av_free(buffer);
+ return -EIO;
+ }
+ s->is_streamed = h->is_streamed;
+ s->max_packet_size = max_packet_size;
+ return 0;
+}
+
+/* XXX: must be called before any I/O */
+int url_setbufsize(ByteIOContext *s, int buf_size)
+{
+ UINT8 *buffer;
+ buffer = av_malloc(buf_size);
+ if (!buffer)
+ return -ENOMEM;
+
+ av_free(s->buffer);
+ s->buffer = buffer;
+ s->buffer_size = buf_size;
+ s->buf_ptr = buffer;
+ if (!s->write_flag)
+ s->buf_end = buffer;
+ else
+ s->buf_end = buffer + buf_size;
+ return 0;
+}
+
+/* NOTE: when opened as read/write, the buffers are only used for
+ reading */
+int url_fopen(ByteIOContext *s, const char *filename, int flags)
+{
+ URLContext *h;
+ int err;
+
+ err = url_open(&h, filename, flags);
+ if (err < 0)
+ return err;
+ err = url_fdopen(s, h);
+ if (err < 0) {
+ url_close(h);
+ return err;
+ }
+ return 0;
+}
+
+int url_fclose(ByteIOContext *s)
+{
+ URLContext *h = s->opaque;
+
+ av_free(s->buffer);
+ memset(s, 0, sizeof(ByteIOContext));
+ return url_close(h);
+}
+
+URLContext *url_fileno(ByteIOContext *s)
+{
+ return s->opaque;
+}
+
+/* XXX: currently size is limited */
+int url_fprintf(ByteIOContext *s, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[4096];
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ put_buffer(s, buf, strlen(buf));
+ return ret;
+}
+
+/* note: unlike fgets, the EOL character is not returned and a whole
+ line is parsed. return NULL if first char read was EOF */
+char *url_fgets(ByteIOContext *s, char *buf, int buf_size)
+{
+ int c;
+ char *q;
+
+ c = url_fgetc(s);
+ if (c == EOF)
+ return NULL;
+ q = buf;
+ for(;;) {
+ if (c == EOF || c == '\n')
+ break;
+ if ((q - buf) < buf_size - 1)
+ *q++ = c;
+ c = url_fgetc(s);
+ }
+ if (buf_size > 0)
+ *q = '\0';
+ return buf;
+}
+
+/*
+ * Return the maximum packet size associated to packetized buffered file
+ * handle. If the file is not packetized (stream like http or file on
+ * disk), then 0 is returned.
+ *
+ * @param h buffered file handle
+ * @return maximum packet size in bytes
+ */
+int url_fget_max_packet_size(ByteIOContext *s)
+{
+ return s->max_packet_size;
+}
+
+/* buffer handling */
+int url_open_buf(ByteIOContext *s, UINT8 *buf, int buf_size, int flags)
+{
+ return init_put_byte(s, buf, buf_size,
+ (flags & URL_WRONLY) != 0, NULL, NULL, NULL, NULL);
+}
+
+/* return the written or read size */
+int url_close_buf(ByteIOContext *s)
+{
+ put_flush_packet(s);
+ return s->buf_ptr - s->buffer;
+}
+
+/* output in a dynamic buffer */
+
+typedef struct DynBuffer {
+ int pos, size, allocated_size;
+ UINT8 *buffer;
+ int io_buffer_size;
+ UINT8 io_buffer[1];
+} DynBuffer;
+
+static void dyn_buf_write(void *opaque, UINT8 *buf, int buf_size)
+{
+ DynBuffer *d = opaque;
+ int new_size, new_allocated_size;
+ UINT8 *new_buffer;
+
+ /* reallocate buffer if needed */
+ new_size = d->pos + buf_size;
+ new_allocated_size = d->allocated_size;
+ while (new_size > new_allocated_size) {
+ if (!new_allocated_size)
+ new_allocated_size = new_size;
+ else
+ new_allocated_size = (new_allocated_size * 3) / 2 + 1;
+ }
+
+ if (new_allocated_size > d->allocated_size) {
+ new_buffer = av_malloc(new_allocated_size);
+ if (!new_buffer)
+ return;
+ memcpy(new_buffer, d->buffer, d->size);
+ av_free(d->buffer);
+ d->buffer = new_buffer;
+ d->allocated_size = new_allocated_size;
+ }
+ memcpy(d->buffer + d->pos, buf, buf_size);
+ d->pos = new_size;
+ if (d->pos > d->size)
+ d->size = d->pos;
+}
+
+static void dyn_packet_buf_write(void *opaque, UINT8 *buf, int buf_size)
+{
+ unsigned char buf1[4];
+
+ /* packetized write: output the header */
+ buf1[0] = (buf_size >> 24);
+ buf1[1] = (buf_size >> 16);
+ buf1[2] = (buf_size >> 8);
+ buf1[3] = (buf_size);
+ dyn_buf_write(opaque, buf1, 4);
+
+ /* then the data */
+ dyn_buf_write(opaque, buf, buf_size);
+}
+
+static int dyn_buf_seek(void *opaque, offset_t offset, int whence)
+{
+ DynBuffer *d = opaque;
+
+ if (whence == SEEK_CUR)
+ offset += d->pos;
+ else if (whence == SEEK_END)
+ offset += d->size;
+ if (offset < 0 || offset > 0x7fffffffLL)
+ return -1;
+ d->pos = offset;
+ return 0;
+}
+
+static int url_open_dyn_buf_internal(ByteIOContext *s, int max_packet_size)
+{
+ DynBuffer *d;
+ int io_buffer_size, ret;
+
+ if (max_packet_size)
+ io_buffer_size = max_packet_size;
+ else
+ io_buffer_size = 1024;
+
+ d = av_malloc(sizeof(DynBuffer) + io_buffer_size);
+ if (!d)
+ return -1;
+ d->io_buffer_size = io_buffer_size;
+ d->buffer = NULL;
+ d->pos = 0;
+ d->size = 0;
+ d->allocated_size = 0;
+ ret = init_put_byte(s, d->io_buffer, io_buffer_size,
+ 1, d, NULL,
+ max_packet_size ? dyn_packet_buf_write : dyn_buf_write,
+ max_packet_size ? NULL : dyn_buf_seek);
+ if (ret == 0) {
+ s->max_packet_size = max_packet_size;
+ }
+ return ret;
+}
+
+/*
+ * Open a write only memory stream.
+ *
+ * @param s new IO context
+ * @return zero if no error.
+ */
+int url_open_dyn_buf(ByteIOContext *s)
+{
+ return url_open_dyn_buf_internal(s, 0);
+}
+
+/*
+ * Open a write only packetized memory stream with a maximum packet
+ * size of 'max_packet_size'. The stream is stored in a memory buffer
+ * with a big endian 4 byte header giving the packet size in bytes.
+ *
+ * @param s new IO context
+ * @param max_packet_size maximum packet size (must be > 0)
+ * @return zero if no error.
+ */
+int url_open_dyn_packet_buf(ByteIOContext *s, int max_packet_size)
+{
+ if (max_packet_size <= 0)
+ return -1;
+ return url_open_dyn_buf_internal(s, max_packet_size);
+}
+
+/*
+ * Return the written size and a pointer to the buffer. The buffer
+ * must be freed with av_free().
+ * @param s IO context
+ * @param pointer to a byte buffer
+ * @return the length of the byte buffer
+ */
+int url_close_dyn_buf(ByteIOContext *s, UINT8 **pbuffer)
+{
+ DynBuffer *d = s->opaque;
+ int size;
+
+ put_flush_packet(s);
+
+ *pbuffer = d->buffer;
+ size = d->size;
+ av_free(d);
+ return size;
+}
diff --git a/libavformat/barpainet.c b/libavformat/barpainet.c
new file mode 100644
index 0000000000..c1e8877718
--- /dev/null
+++ b/libavformat/barpainet.c
@@ -0,0 +1,25 @@
+
+#include <stdlib.h>
+#include <strings.h>
+#include "barpainet.h"
+
+int inet_aton (const char * str, struct in_addr * add) {
+ const char * pch = str;
+ unsigned int add1 = 0, add2 = 0, add3 = 0, add4 = 0;
+
+ add1 = atoi(pch);
+ pch = strpbrk(pch,".");
+ if (pch == 0 || ++pch == 0) goto done;
+ add2 = atoi(pch);
+ pch = strpbrk(pch,".");
+ if (pch == 0 || ++pch == 0) goto done;
+ add3 = atoi(pch);
+ pch = strpbrk(pch,".");
+ if (pch == 0 || ++pch == 0) goto done;
+ add4 = atoi(pch);
+
+done:
+ add->s_addr=(add4<<24)+(add3<<16)+(add2<<8)+add1;
+
+ return 1;
+}
diff --git a/libavformat/barpainet.h b/libavformat/barpainet.h
new file mode 100644
index 0000000000..461403b3fa
--- /dev/null
+++ b/libavformat/barpainet.h
@@ -0,0 +1,23 @@
+#ifndef BARPA_INET_H
+#define BARPA_INET_H
+
+#include "../config.h"
+
+#ifdef CONFIG_BEOS_NETSERVER
+
+# include <socket.h>
+int inet_aton (const char * str, struct in_addr * add);
+# define PF_INET AF_INET
+# define SO_SNDBUF 0x40000001
+
+/* fake */
+struct ip_mreq {
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_interface; /* local IP address of interface */
+};
+
+#else
+# include <arpa/inet.h>
+#endif
+
+#endif /* BARPA_INET_H */
diff --git a/libavformat/beosaudio.cpp b/libavformat/beosaudio.cpp
new file mode 100644
index 0000000000..a1ae0a53c8
--- /dev/null
+++ b/libavformat/beosaudio.cpp
@@ -0,0 +1,449 @@
+/*
+ * BeOS audio play interface
+ * Copyright (c) 2000, 2001 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 <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include <Application.h>
+#include <SoundPlayer.h>
+
+extern "C" {
+#include "avformat.h"
+}
+
+/* enable performance checks */
+//#define PERF_CHECK
+
+//const char *audio_device = "/dev/dsp";
+const char *audio_device = "beosaudio:";
+
+/* Pipes are 4k in BeOS IIRC */
+#define AUDIO_BLOCK_SIZE 4096
+//#define AUDIO_BLOCK_SIZE 2048
+#define AUDIO_BLOCK_COUNT 8
+
+#define AUDIO_BUFFER_SIZE (AUDIO_BLOCK_SIZE*AUDIO_BLOCK_COUNT)
+
+/* pipes suck for realtime */
+#define USE_RING_BUFFER 1
+
+typedef struct {
+ int fd;
+ int sample_rate;
+ int channels;
+ int frame_size; /* in bytes ! */
+ CodecID codec_id;
+ int flip_left : 1;
+ UINT8 buffer[AUDIO_BUFFER_SIZE];
+ int buffer_ptr;
+ int pipefd; /* the other end of the pipe */
+ /* ring buffer */
+ sem_id input_sem;
+ int input_index;
+ sem_id output_sem;
+ int output_index;
+ int queued;
+ BSoundPlayer *player;
+ int has_quit; /* signal callbacks not to wait */
+ volatile bigtime_t starve_time;
+} AudioData;
+
+static thread_id main_thid;
+static thread_id bapp_thid;
+static int own_BApp_created = 0;
+static int refcount = 0;
+
+/* create the BApplication and Run() it */
+static int32 bapp_thread(void *arg)
+{
+ new BApplication("application/x-vnd.ffmpeg");
+ own_BApp_created = 1;
+ be_app->Run();
+ /* kill the process group */
+// kill(0, SIGINT);
+// kill(main_thid, SIGHUP);
+ return B_OK;
+}
+
+/* create the BApplication only if needed */
+static void create_bapp_if_needed(void)
+{
+ if (refcount++ == 0) {
+ /* needed by libmedia */
+ if (be_app == NULL) {
+ bapp_thid = spawn_thread(bapp_thread, "ffmpeg BApplication", B_NORMAL_PRIORITY, NULL);
+ resume_thread(bapp_thid);
+ while (!own_BApp_created)
+ snooze(50000);
+ }
+ }
+}
+
+static void destroy_bapp_if_needed(void)
+{
+ if (--refcount == 0 && own_BApp_created) {
+ be_app->Lock();
+ be_app->Quit();
+ be_app = NULL;
+ }
+}
+
+/* called back by BSoundPlayer */
+static void audioplay_callback(void *cookie, void *buffer, size_t bufferSize, const media_raw_audio_format &format)
+{
+ AudioData *s;
+ size_t len, amount;
+ unsigned char *buf = (unsigned char *)buffer;
+
+ s = (AudioData *)cookie;
+ if (s->has_quit)
+ return;
+ while (bufferSize > 0) {
+#ifdef PERF_CHECK
+ bigtime_t t;
+ t = system_time();
+#endif
+#ifdef USE_RING_BUFFER
+ len = MIN(AUDIO_BLOCK_SIZE, bufferSize);
+ if (acquire_sem_etc(s->output_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK) {
+ s->has_quit = 1;
+ s->player->SetHasData(false);
+ return;
+ }
+ amount = MIN(len, (AUDIO_BUFFER_SIZE - s->output_index));
+ memcpy(buf, &s->buffer[s->output_index], amount);
+ s->output_index += amount;
+ if (s->output_index >= AUDIO_BUFFER_SIZE) {
+ s->output_index %= AUDIO_BUFFER_SIZE;
+ memcpy(buf + amount, &s->buffer[s->output_index], len - amount);
+ s->output_index += len-amount;
+ s->output_index %= AUDIO_BUFFER_SIZE;
+ }
+ release_sem_etc(s->input_sem, len, 0);
+#else
+ len = read(s->pipefd, buf, bufferSize);
+#endif
+#ifdef PERF_CHECK
+ t = system_time() - t;
+ s->starve_time = MAX(s->starve_time, t);
+#endif
+#ifndef USE_RING_BUFFER
+ if (len < B_OK) {
+ puts("EPIPE");
+ s->player->SetHasData(false);
+ snooze(100000);
+ return;
+ }
+ if (len == 0) {
+ s->player->SetHasData(false);
+ snooze(100000);
+ return;
+ }
+#endif
+ buf += len;
+ bufferSize -= len;
+ }
+}
+
+static int audio_open(AudioData *s, int is_output)
+{
+ int p[2];
+ int ret;
+ media_raw_audio_format format;
+
+ if (!is_output)
+ return -EIO; /* not for now */
+#ifdef USE_RING_BUFFER
+ s->input_sem = create_sem(AUDIO_BUFFER_SIZE, "ffmpeg_ringbuffer_input");
+// s->input_sem = create_sem(AUDIO_BLOCK_SIZE, "ffmpeg_ringbuffer_input");
+ if (s->input_sem < B_OK)
+ return -EIO;
+ s->output_sem = create_sem(0, "ffmpeg_ringbuffer_output");
+ if (s->output_sem < B_OK) {
+ delete_sem(s->input_sem);
+ return -EIO;
+ }
+ s->input_index = 0;
+ s->output_index = 0;
+ s->queued = 0;
+#else
+ ret = pipe(p);
+ if (ret < 0)
+ return -EIO;
+ s->fd = p[is_output?1:0];
+ s->pipefd = p[is_output?0:1];
+ if (s->fd < 0) {
+ perror(is_output?"audio out":"audio in");
+ return -EIO;
+ }
+#endif
+ create_bapp_if_needed();
+ /* non blocking mode */
+// fcntl(s->fd, F_SETFL, O_NONBLOCK);
+// fcntl(s->pipefd, F_SETFL, O_NONBLOCK);
+ s->frame_size = AUDIO_BLOCK_SIZE;
+ format = media_raw_audio_format::wildcard;
+ format.format = media_raw_audio_format::B_AUDIO_SHORT;
+ format.byte_order = B_HOST_IS_LENDIAN ? B_MEDIA_LITTLE_ENDIAN : B_MEDIA_BIG_ENDIAN;
+ format.channel_count = s->channels;
+ format.buffer_size = s->frame_size;
+ format.frame_rate = s->sample_rate;
+ s->player = new BSoundPlayer(&format, "ffmpeg output", audioplay_callback);
+ if (s->player->InitCheck() != B_OK) {
+ delete s->player;
+ s->player = NULL;
+#ifdef USE_RING_BUFFER
+ if (s->input_sem)
+ delete_sem(s->input_sem);
+ if (s->output_sem)
+ delete_sem(s->output_sem);
+#else
+ close(s->fd);
+ close(s->pipefd);
+#endif
+ return -EIO;
+ }
+ s->player->SetCookie(s);
+ s->player->SetVolume(1.0);
+ s->player->Start();
+ s->player->SetHasData(true);
+ /* bump up the priority (avoid realtime though) */
+ set_thread_priority(find_thread(NULL), B_DISPLAY_PRIORITY+1);
+ return 0;
+}
+
+static int audio_close(AudioData *s)
+{
+#ifdef USE_RING_BUFFER
+ if (s->input_sem)
+ delete_sem(s->input_sem);
+ if (s->output_sem)
+ delete_sem(s->output_sem);
+#endif
+ s->has_quit = 1;
+ if (s->player) {
+ s->player->Stop();
+ }
+ if (s->player)
+ delete s->player;
+#ifndef USE_RING_BUFFER
+ close(s->pipefd);
+ close(s->fd);
+#endif
+ destroy_bapp_if_needed();
+ return 0;
+}
+
+/* sound output support */
+static int audio_write_header(AVFormatContext *s1)
+{
+ AudioData *s = (AudioData *)s1->priv_data;
+ AVStream *st;
+ int ret;
+
+ st = s1->streams[0];
+ s->sample_rate = st->codec.sample_rate;
+ s->channels = st->codec.channels;
+ ret = audio_open(s, 1);
+ if (ret < 0)
+ return -EIO;
+ return 0;
+}
+
+static int audio_write_packet(AVFormatContext *s1, int stream_index,
+ UINT8 *buf, int size, int force_pts)
+{
+ AudioData *s = (AudioData *)s1->priv_data;
+ int len, ret;
+#ifdef PERF_CHECK
+ bigtime_t t = s->starve_time;
+ s->starve_time = 0;
+ printf("starve_time: %lld \n", t);
+#endif
+#ifdef USE_RING_BUFFER
+ while (size > 0) {
+ int amount;
+ len = MIN(size, AUDIO_BLOCK_SIZE);
+ if (acquire_sem_etc(s->input_sem, len, B_CAN_INTERRUPT, 0LL) < B_OK)
+ return -EIO;
+ amount = MIN(len, (AUDIO_BUFFER_SIZE - s->input_index));
+ memcpy(&s->buffer[s->input_index], buf, amount);
+ s->input_index += amount;
+ if (s->input_index >= AUDIO_BUFFER_SIZE) {
+ s->input_index %= AUDIO_BUFFER_SIZE;
+ memcpy(&s->buffer[s->input_index], buf + amount, len - amount);
+ s->input_index += len - amount;
+ }
+ release_sem_etc(s->output_sem, len, 0);
+ buf += len;
+ size -= len;
+ }
+#else
+ while (size > 0) {
+ len = AUDIO_BLOCK_SIZE - s->buffer_ptr;
+ if (len > size)
+ len = size;
+ memcpy(s->buffer + s->buffer_ptr, buf, len);
+ s->buffer_ptr += len;
+ if (s->buffer_ptr >= AUDIO_BLOCK_SIZE) {
+ for(;;) {
+//snooze(1000);
+ ret = write(s->fd, s->buffer, AUDIO_BLOCK_SIZE);
+ if (ret != 0)
+ break;
+ if (ret < 0 && (errno != EAGAIN && errno != EINTR))
+ return -EIO;
+ }
+ s->buffer_ptr = 0;
+ }
+ buf += len;
+ size -= len;
+ }
+#endif
+ return 0;
+}
+
+static int audio_write_trailer(AVFormatContext *s1)
+{
+ AudioData *s = (AudioData *)s1->priv_data;
+
+ audio_close(s);
+ return 0;
+}
+
+/* grab support */
+
+static int audio_read_header(AVFormatContext *s1, AVFormatParameters *ap)
+{
+ AudioData *s = (AudioData *)s1->priv_data;
+ AVStream *st;
+ int ret;
+
+ if (!ap || ap->sample_rate <= 0 || ap->channels <= 0)
+ return -1;
+
+ st = av_new_stream(s1, 0);
+ if (!st) {
+ return -ENOMEM;
+ }
+ s->sample_rate = ap->sample_rate;
+ s->channels = ap->channels;
+
+ ret = audio_open(s, 0);
+ if (ret < 0) {
+ av_free(st);
+ return -EIO;
+ } else {
+ /* take real parameters */
+ st->codec.codec_type = CODEC_TYPE_AUDIO;
+ st->codec.codec_id = s->codec_id;
+ st->codec.sample_rate = s->sample_rate;
+ st->codec.channels = s->channels;
+ return 0;
+ }
+}
+
+static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt)
+{
+ AudioData *s = (AudioData *)s1->priv_data;
+ int ret;
+
+ if (av_new_packet(pkt, s->frame_size) < 0)
+ return -EIO;
+ for(;;) {
+ ret = read(s->fd, pkt->data, pkt->size);
+ if (ret > 0)
+ break;
+ if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
+ av_free_packet(pkt);
+ pkt->size = 0;
+ return 0;
+ }
+ if (!(ret == 0 || (ret == -1 && (errno == EAGAIN || errno == EINTR)))) {
+ av_free_packet(pkt);
+ return -EIO;
+ }
+ }
+ pkt->size = ret;
+ if (s->flip_left && s->channels == 2) {
+ int i;
+ short *p = (short *) pkt->data;
+
+ for (i = 0; i < ret; i += 4) {
+ *p = ~*p;
+ p += 2;
+ }
+ }
+ return 0;
+}
+
+static int audio_read_close(AVFormatContext *s1)
+{
+ AudioData *s = (AudioData *)s1->priv_data;
+
+ audio_close(s);
+ return 0;
+}
+
+AVInputFormat audio_in_format = {
+ "audio_device",
+ "audio grab and output",
+ sizeof(AudioData),
+ NULL,
+ audio_read_header,
+ audio_read_packet,
+ audio_read_close,
+ NULL,
+ AVFMT_NOFILE,
+};
+
+AVOutputFormat audio_out_format = {
+ "audio_device",
+ "audio grab and output",
+ "",
+ "",
+ sizeof(AudioData),
+#ifdef WORDS_BIGENDIAN
+ CODEC_ID_PCM_S16BE,
+#else
+ CODEC_ID_PCM_S16LE,
+#endif
+ CODEC_ID_NONE,
+ audio_write_header,
+ audio_write_packet,
+ audio_write_trailer,
+ AVFMT_NOFILE,
+};
+
+extern "C" {
+
+int audio_init(void)
+{
+ main_thid = find_thread(NULL);
+ av_register_input_format(&audio_in_format);
+ av_register_output_format(&audio_out_format);
+ return 0;
+}
+
+} // "C"
+
diff --git a/libavformat/crc.c b/libavformat/crc.c
new file mode 100644
index 0000000000..553ab537cb
--- /dev/null
+++ b/libavformat/crc.c
@@ -0,0 +1,111 @@
+/*
+ * CRC decoder (for codec/format testing)
+ * Copyright (c) 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"
+
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#define BASE 65521L /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf) {s1 += *buf++; s2 += s1;}
+#define DO2(buf) DO1(buf); DO1(buf);
+#define DO4(buf) DO2(buf); DO2(buf);
+#define DO8(buf) DO4(buf); DO4(buf);
+#define DO16(buf) DO8(buf); DO8(buf);
+
+static UINT32 adler32(UINT32 adler, UINT8 *buf, unsigned int len)
+{
+ unsigned long s1 = adler & 0xffff;
+ unsigned long s2 = (adler >> 16) & 0xffff;
+ int k;
+
+ if (buf == NULL) return 1L;
+
+ while (len > 0) {
+ k = len < NMAX ? len : NMAX;
+ len -= k;
+ while (k >= 16) {
+ DO16(buf);
+ k -= 16;
+ }
+ if (k != 0) do {
+ DO1(buf);
+ } while (--k);
+ s1 %= BASE;
+ s2 %= BASE;
+ }
+ return (s2 << 16) | s1;
+}
+
+typedef struct CRCState {
+ UINT32 crcval;
+} CRCState;
+
+static int crc_write_header(struct AVFormatContext *s)
+{
+ CRCState *crc = s->priv_data;
+
+ /* init CRC */
+ crc->crcval = adler32(0, NULL, 0);
+
+ return 0;
+}
+
+static int crc_write_packet(struct AVFormatContext *s,
+ int stream_index,
+ unsigned char *buf, int size, int force_pts)
+{
+ CRCState *crc = s->priv_data;
+ crc->crcval = adler32(crc->crcval, buf, size);
+ return 0;
+}
+
+static int crc_write_trailer(struct AVFormatContext *s)
+{
+ CRCState *crc = s->priv_data;
+ char buf[64];
+
+ snprintf(buf, sizeof(buf), "CRC=%08x\n", crc->crcval);
+ put_buffer(&s->pb, buf, strlen(buf));
+ put_flush_packet(&s->pb);
+ return 0;
+}
+
+static AVOutputFormat crc_format = {
+ "crc",
+ "crc testing format",
+ NULL,
+ "",
+ sizeof(CRCState),
+ CODEC_ID_PCM_S16LE,
+ CODEC_ID_RAWVIDEO,
+ crc_write_header,
+ crc_write_packet,
+ crc_write_trailer,
+};
+
+int crc_init(void)
+{
+ av_register_output_format(&crc_format);
+ return 0;
+}
diff --git a/libavformat/cutils.c b/libavformat/cutils.c
new file mode 100644
index 0000000000..ce2c845226
--- /dev/null
+++ b/libavformat/cutils.c
@@ -0,0 +1,110 @@
+/*
+ * Various simple utilities for ffmpeg system
+ * 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"
+#include <ctype.h>
+
+#if !defined(CONFIG_NOCUTILS)
+/**
+ * Return TRUE if val is a prefix of str. If it returns TRUE, ptr is
+ * set to the next character in 'str' after the prefix.
+ *
+ * @param str input string
+ * @param val prefix to test
+ * @param ptr updated after the prefix in str in there is a match
+ * @return TRUE if there is a match
+ */
+int strstart(const char *str, const char *val, const char **ptr)
+{
+ const char *p, *q;
+ p = str;
+ q = val;
+ while (*q != '\0') {
+ if (*p != *q)
+ return 0;
+ p++;
+ q++;
+ }
+ if (ptr)
+ *ptr = p;
+ return 1;
+}
+
+/**
+ * Return TRUE if val is a prefix of str (case independent). If it
+ * returns TRUE, ptr is set to the next character in 'str' after the
+ * prefix.
+ *
+ * @param str input string
+ * @param val prefix to test
+ * @param ptr updated after the prefix in str in there is a match
+ * @return TRUE if there is a match */
+int stristart(const char *str, const char *val, const char **ptr)
+{
+ const char *p, *q;
+ p = str;
+ q = val;
+ while (*q != '\0') {
+ if (toupper(*(unsigned char *)p) != toupper(*(unsigned char *)q))
+ return 0;
+ p++;
+ q++;
+ }
+ if (ptr)
+ *ptr = p;
+ return 1;
+}
+
+/**
+ * Copy the string str to buf. If str length is bigger than buf_size -
+ * 1 then it is clamped to buf_size - 1.
+ * NOTE: this function does what strncpy should have done to be
+ * useful. NEVER use strncpy.
+ *
+ * @param buf destination buffer
+ * @param buf_size size of destination buffer
+ * @param str source string
+ */
+void pstrcpy(char *buf, int buf_size, const char *str)
+{
+ int c;
+ char *q = buf;
+
+ if (buf_size <= 0)
+ return;
+
+ for(;;) {
+ c = *str++;
+ if (c == 0 || q >= buf + buf_size - 1)
+ break;
+ *q++ = c;
+ }
+ *q = '\0';
+}
+
+/* strcat and truncate. */
+char *pstrcat(char *buf, int buf_size, const char *s)
+{
+ int len;
+ len = strlen(buf);
+ if (len < buf_size)
+ pstrcpy(buf + len, buf_size - len, s);
+ return buf;
+}
+
+#endif
diff --git a/libavformat/dv.c b/libavformat/dv.c
new file mode 100644
index 0000000000..1f152a2fba
--- /dev/null
+++ b/libavformat/dv.c
@@ -0,0 +1,134 @@
+/*
+ * Raw DV format
+ * Copyright (c) 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 NTSC_FRAME_SIZE 120000
+#define PAL_FRAME_SIZE 144000
+
+typedef struct DVDemuxContext {
+ int is_audio;
+} DVDemuxContext;
+
+/* raw input */
+static int dv_read_header(AVFormatContext *s,
+ AVFormatParameters *ap)
+{
+ AVStream *vst, *ast;
+
+ vst = av_new_stream(s, 0);
+ if (!vst)
+ return AVERROR_NOMEM;
+ vst->codec.codec_type = CODEC_TYPE_VIDEO;
+ vst->codec.codec_id = CODEC_ID_DVVIDEO;
+
+#if 0
+ ast = av_new_stream(s, 1);
+ if (!ast)
+ return AVERROR_NOMEM;
+
+ ast->codec.codec_type = CODEC_TYPE_AUDIO;
+ ast->codec.codec_id = CODEC_ID_DVAUDIO;
+#endif
+ return 0;
+}
+
+/* XXX: build fake audio stream when DV audio decoder will be finished */
+static int dv_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ int ret, size, dsf;
+ uint8_t buf[4];
+
+ ret = get_buffer(&s->pb, buf, 4);
+ if (ret <= 0)
+ return -EIO;
+ dsf = buf[3] & 0x80;
+ if (!dsf)
+ size = NTSC_FRAME_SIZE;
+ else
+ size = PAL_FRAME_SIZE;
+
+ if (av_new_packet(pkt, size) < 0)
+ return -EIO;
+
+ pkt->stream_index = 0;
+ memcpy(pkt->data, buf, 4);
+ ret = get_buffer(&s->pb, pkt->data + 4, size - 4);
+ if (ret <= 0) {
+ av_free_packet(pkt);
+ return -EIO;
+ }
+ return ret;
+}
+
+static int dv_read_close(AVFormatContext *s)
+{
+ return 0;
+}
+
+static AVInputFormat dv_iformat = {
+ "dv",
+ "DV video format",
+ sizeof(DVDemuxContext),
+ NULL,
+ dv_read_header,
+ dv_read_packet,
+ dv_read_close,
+ .extensions = "dv",
+};
+
+#if 0
+int dv_write_header(struct AVFormatContext *s)
+{
+ return 0;
+}
+
+int dv_write_packet(struct AVFormatContext *s,
+ int stream_index,
+ unsigned char *buf, int size, int force_pts)
+{
+ put_buffer(&s->pb, buf, size);
+ put_flush_packet(&s->pb);
+ return 0;
+}
+
+int dv_write_trailer(struct AVFormatContext *s)
+{
+ return 0;
+}
+
+AVOutputFormat dv_oformat = {
+ "dv",
+ "DV video format",
+ NULL,
+ "dv",
+ 0,
+ CODEC_ID_DVVIDEO,
+ CODEC_ID_DVAUDIO,
+ dv_write_header,
+ dv_write_packet,
+ dv_write_trailer,
+};
+#endif
+
+int dv_init(void)
+{
+ av_register_input_format(&dv_iformat);
+ // av_register_output_format(&dv_oformat);
+ return 0;
+}
diff --git a/libavformat/ffm.c b/libavformat/ffm.c
new file mode 100644
index 0000000000..c21599c69f
--- /dev/null
+++ b/libavformat/ffm.c
@@ -0,0 +1,684 @@
+/*
+ * FFM (ffserver live feed) encoder and decoder
+ * Copyright (c) 2001 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"
+#include <unistd.h>
+
+/* The FFM file is made of blocks of fixed size */
+#define FFM_HEADER_SIZE 14
+#define PACKET_ID 0x666d
+
+/* each packet contains frames (which can span several packets */
+#define FRAME_HEADER_SIZE 8
+#define FLAG_KEY_FRAME 0x01
+
+typedef struct FFMStream {
+ INT64 pts;
+} FFMStream;
+
+enum {
+ READ_HEADER,
+ READ_DATA,
+};
+
+typedef struct FFMContext {
+ /* only reading mode */
+ offset_t write_index, file_size;
+ int read_state;
+ UINT8 header[FRAME_HEADER_SIZE];
+
+ /* read and write */
+ int first_packet; /* true if first packet, needed to set the discontinuity tag */
+ int packet_size;
+ int frame_offset;
+ INT64 pts;
+ UINT8 *packet_ptr, *packet_end;
+ UINT8 packet[FFM_PACKET_SIZE];
+} FFMContext;
+
+/* disable pts hack for testing */
+int ffm_nopts = 0;
+
+static void flush_packet(AVFormatContext *s)
+{
+ FFMContext *ffm = s->priv_data;
+ int fill_size, h;
+ ByteIOContext *pb = &s->pb;
+
+ fill_size = ffm->packet_end - ffm->packet_ptr;
+ memset(ffm->packet_ptr, 0, fill_size);
+
+ /* put header */
+ put_be16(pb, PACKET_ID);
+ put_be16(pb, fill_size);
+ put_be64(pb, ffm->pts);
+ h = ffm->frame_offset;
+ if (ffm->first_packet)
+ h |= 0x8000;
+ put_be16(pb, h);
+ put_buffer(pb, ffm->packet, ffm->packet_end - ffm->packet);
+
+ /* prepare next packet */
+ ffm->frame_offset = 0; /* no key frame */
+ ffm->pts = 0; /* no pts */
+ ffm->packet_ptr = ffm->packet;
+ ffm->first_packet = 0;
+}
+
+/* 'first' is true if first data of a frame */
+static void ffm_write_data(AVFormatContext *s,
+ UINT8 *buf, int size,
+ INT64 pts, int first)
+{
+ FFMContext *ffm = s->priv_data;
+ int len;
+
+ if (first && ffm->frame_offset == 0)
+ ffm->frame_offset = ffm->packet_ptr - ffm->packet + FFM_HEADER_SIZE;
+ if (first && ffm->pts == 0)
+ ffm->pts = pts;
+
+ /* write as many packets as needed */
+ while (size > 0) {
+ len = ffm->packet_end - ffm->packet_ptr;
+ if (len > size)
+ len = size;
+ memcpy(ffm->packet_ptr, buf, len);
+
+ ffm->packet_ptr += len;
+ buf += len;
+ size -= len;
+ if (ffm->packet_ptr >= ffm->packet_end) {
+ /* special case : no pts in packet : we leave the current one */
+ if (ffm->pts == 0)
+ ffm->pts = pts;
+
+ flush_packet(s);
+ }
+ }
+}
+
+static int ffm_write_header(AVFormatContext *s)
+{
+ FFMContext *ffm = s->priv_data;
+ AVStream *st;
+ FFMStream *fst;
+ ByteIOContext *pb = &s->pb;
+ AVCodecContext *codec;
+ int bit_rate, i;
+
+ ffm->packet_size = FFM_PACKET_SIZE;
+
+ /* header */
+ put_tag(pb, "FFM1");
+ put_be32(pb, ffm->packet_size);
+ /* XXX: store write position in other file ? */
+ put_be64(pb, ffm->packet_size); /* current write position */
+
+ put_be32(pb, s->nb_streams);
+ bit_rate = 0;
+ for(i=0;i<s->nb_streams;i++) {
+ st = s->streams[i];
+ bit_rate += st->codec.bit_rate;
+ }
+ put_be32(pb, bit_rate);
+
+ /* list of streams */
+ for(i=0;i<s->nb_streams;i++) {
+ st = s->streams[i];
+ fst = av_mallocz(sizeof(FFMStream));
+ if (!fst)
+ goto fail;
+ st->priv_data = fst;
+
+ codec = &st->codec;
+ /* generic info */
+ put_be32(pb, codec->codec_id);
+ put_byte(pb, codec->codec_type);
+ put_be32(pb, codec->bit_rate);
+ put_be32(pb, codec->quality);
+ put_be32(pb, codec->flags);
+ /* specific info */
+ switch(codec->codec_type) {
+ case CODEC_TYPE_VIDEO:
+ put_be32(pb, (codec->frame_rate * 1000) / FRAME_RATE_BASE);
+ put_be16(pb, codec->width);
+ put_be16(pb, codec->height);
+ put_be16(pb, codec->gop_size);
+ put_byte(pb, codec->qmin);
+ put_byte(pb, codec->qmax);
+ put_byte(pb, codec->max_qdiff);
+ put_be16(pb, (int) (codec->qcompress * 10000.0));
+ put_be16(pb, (int) (codec->qblur * 10000.0));
+ put_be32(pb, codec->bit_rate_tolerance);
+ put_strz(pb, codec->rc_eq);
+ put_be32(pb, codec->rc_max_rate);
+ put_be32(pb, codec->rc_min_rate);
+ put_be32(pb, codec->rc_buffer_size);
+ put_be64_double(pb, codec->i_quant_factor);
+ put_be64_double(pb, codec->b_quant_factor);
+ put_be64_double(pb, codec->i_quant_offset);
+ put_be64_double(pb, codec->b_quant_offset);
+ put_be32(pb, codec->dct_algo);
+ break;
+ case CODEC_TYPE_AUDIO:
+ put_be32(pb, codec->sample_rate);
+ put_le16(pb, codec->channels);
+ put_le16(pb, codec->frame_size);
+ break;
+ default:
+ av_abort();
+ }
+ /* hack to have real time */
+ if (ffm_nopts)
+ fst->pts = 0;
+ else
+ fst->pts = av_gettime();
+ }
+
+ /* flush until end of block reached */
+ while ((url_ftell(pb) % ffm->packet_size) != 0)
+ put_byte(pb, 0);
+
+ put_flush_packet(pb);
+
+ /* init packet mux */
+ ffm->packet_ptr = ffm->packet;
+ ffm->packet_end = ffm->packet + ffm->packet_size - FFM_HEADER_SIZE;
+ ffm->frame_offset = 0;
+ ffm->pts = 0;
+ ffm->first_packet = 1;
+
+ return 0;
+ fail:
+ for(i=0;i<s->nb_streams;i++) {
+ st = s->streams[i];
+ av_freep(&st->priv_data);
+ }
+ return -1;
+}
+
+static int ffm_write_packet(AVFormatContext *s, int stream_index,
+ UINT8 *buf, int size, int force_pts)
+{
+ AVStream *st = s->streams[stream_index];
+ FFMStream *fst = st->priv_data;
+ INT64 pts;
+ UINT8 header[FRAME_HEADER_SIZE];
+ int duration;
+
+ if (st->codec.codec_type == CODEC_TYPE_AUDIO) {
+ duration = ((float)st->codec.frame_size / st->codec.sample_rate * 1000000.0);
+ } else {
+ duration = (1000000.0 * FRAME_RATE_BASE / (float)st->codec.frame_rate);
+ }
+
+ pts = fst->pts;
+ /* packet size & key_frame */
+ header[0] = stream_index;
+ header[1] = 0;
+ if (st->codec.key_frame)
+ header[1] |= FLAG_KEY_FRAME;
+ header[2] = (size >> 16) & 0xff;
+ header[3] = (size >> 8) & 0xff;
+ header[4] = size & 0xff;
+ header[5] = (duration >> 16) & 0xff;
+ header[6] = (duration >> 8) & 0xff;
+ header[7] = duration & 0xff;
+ ffm_write_data(s, header, FRAME_HEADER_SIZE, pts, 1);
+ ffm_write_data(s, buf, size, pts, 0);
+
+ fst->pts += duration;
+ return 0;
+}
+
+static int ffm_write_trailer(AVFormatContext *s)
+{
+ ByteIOContext *pb = &s->pb;
+ FFMContext *ffm = s->priv_data;
+ int i;
+
+ /* flush packets */
+ if (ffm->packet_ptr > ffm->packet)
+ flush_packet(s);
+
+ put_flush_packet(pb);
+
+ if (!url_is_streamed(pb)) {
+ INT64 size;
+ /* update the write offset */
+ size = url_ftell(pb);
+ url_fseek(pb, 8, SEEK_SET);
+ put_be64(pb, size);
+ put_flush_packet(pb);
+ }
+
+ for(i=0;i<s->nb_streams;i++)
+ av_freep(&s->streams[i]->priv_data);
+ return 0;
+}
+
+/* ffm demux */
+
+static int ffm_is_avail_data(AVFormatContext *s, int size)
+{
+ FFMContext *ffm = s->priv_data;
+ offset_t pos, avail_size;
+ int len;
+
+ len = ffm->packet_end - ffm->packet_ptr;
+ if (!ffm_nopts) {
+ /* XXX: I don't understand this test, so I disabled it for testing */
+ if (size <= len)
+ return 1;
+ }
+ pos = url_ftell(&s->pb);
+ if (pos == ffm->write_index) {
+ /* exactly at the end of stream */
+ return 0;
+ } else if (pos < ffm->write_index) {
+ avail_size = ffm->write_index - pos;
+ } else {
+ avail_size = (ffm->file_size - pos) + (ffm->write_index - FFM_PACKET_SIZE);
+ }
+ avail_size = (avail_size / ffm->packet_size) * (ffm->packet_size - FFM_HEADER_SIZE) + len;
+ if (size <= avail_size)
+ return 1;
+ else
+ return 0;
+}
+
+/* first is true if we read the frame header */
+static int ffm_read_data(AVFormatContext *s,
+ UINT8 *buf, int size, int first)
+{
+ FFMContext *ffm = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ int len, fill_size, size1, frame_offset;
+
+ size1 = size;
+ while (size > 0) {
+ redo:
+ len = ffm->packet_end - ffm->packet_ptr;
+ if (len > size)
+ len = size;
+ if (len == 0) {
+ if (url_ftell(pb) == ffm->file_size)
+ url_fseek(pb, ffm->packet_size, SEEK_SET);
+ retry_read:
+ get_be16(pb); /* PACKET_ID */
+ fill_size = get_be16(pb);
+ ffm->pts = get_be64(pb);
+ frame_offset = get_be16(pb);
+ get_buffer(pb, ffm->packet, ffm->packet_size - FFM_HEADER_SIZE);
+ ffm->packet_end = ffm->packet + (ffm->packet_size - FFM_HEADER_SIZE - fill_size);
+ /* if first packet or resynchronization packet, we must
+ handle it specifically */
+ if (ffm->first_packet || (frame_offset & 0x8000)) {
+ if (!frame_offset) {
+ /* This packet has no frame headers in it */
+ if (url_ftell(pb) >= ffm->packet_size * 3) {
+ url_fseek(pb, -ffm->packet_size * 2, SEEK_CUR);
+ goto retry_read;
+ }
+ /* This is bad, we cannot find a valid frame header */
+ return 0;
+ }
+ ffm->first_packet = 0;
+ if ((frame_offset & 0x7ffff) < FFM_HEADER_SIZE)
+ av_abort();
+ ffm->packet_ptr = ffm->packet + (frame_offset & 0x7fff) - FFM_HEADER_SIZE;
+ if (!first)
+ break;
+ } else {
+ ffm->packet_ptr = ffm->packet;
+ }
+ goto redo;
+ }
+ memcpy(buf, ffm->packet_ptr, len);
+ buf += len;
+ ffm->packet_ptr += len;
+ size -= len;
+ first = 0;
+ }
+ return size1 - size;
+}
+
+
+static int ffm_read_header(AVFormatContext *s, AVFormatParameters *ap)
+{
+ FFMContext *ffm = s->priv_data;
+ AVStream *st;
+ FFMStream *fst;
+ ByteIOContext *pb = &s->pb;
+ AVCodecContext *codec;
+ int i;
+ UINT32 tag;
+
+ /* header */
+ tag = get_le32(pb);
+ if (tag != MKTAG('F', 'F', 'M', '1'))
+ goto fail;
+ ffm->packet_size = get_be32(pb);
+ if (ffm->packet_size != FFM_PACKET_SIZE)
+ goto fail;
+ ffm->write_index = get_be64(pb);
+ /* get also filesize */
+ if (!url_is_streamed(pb)) {
+ ffm->file_size = url_filesize(url_fileno(pb));
+ } else {
+ ffm->file_size = (UINT64_C(1) << 63) - 1;
+ }
+
+ s->nb_streams = get_be32(pb);
+ get_be32(pb); /* total bitrate */
+ /* read each stream */
+ for(i=0;i<s->nb_streams;i++) {
+ char rc_eq_buf[128];
+
+ st = av_mallocz(sizeof(AVStream));
+ if (!st)
+ goto fail;
+ s->streams[i] = st;
+ fst = av_mallocz(sizeof(FFMStream));
+ if (!fst)
+ goto fail;
+ st->priv_data = fst;
+
+ codec = &st->codec;
+ /* generic info */
+ st->codec.codec_id = get_be32(pb);
+ st->codec.codec_type = get_byte(pb); /* codec_type */
+ codec->bit_rate = get_be32(pb);
+ codec->quality = get_be32(pb);
+ codec->flags = get_be32(pb);
+ /* specific info */
+ switch(codec->codec_type) {
+ case CODEC_TYPE_VIDEO:
+ codec->frame_rate = ((INT64)get_be32(pb) * FRAME_RATE_BASE) / 1000;
+ codec->width = get_be16(pb);
+ codec->height = get_be16(pb);
+ codec->gop_size = get_be16(pb);
+ codec->qmin = get_byte(pb);
+ codec->qmax = get_byte(pb);
+ codec->max_qdiff = get_byte(pb);
+ codec->qcompress = get_be16(pb) / 10000.0;
+ codec->qblur = get_be16(pb) / 10000.0;
+ codec->bit_rate_tolerance = get_be32(pb);
+ codec->rc_eq = strdup(get_strz(pb, rc_eq_buf, sizeof(rc_eq_buf)));
+ codec->rc_max_rate = get_be32(pb);
+ codec->rc_min_rate = get_be32(pb);
+ codec->rc_buffer_size = get_be32(pb);
+ codec->i_quant_factor = get_be64_double(pb);
+ codec->b_quant_factor = get_be64_double(pb);
+ codec->i_quant_offset = get_be64_double(pb);
+ codec->b_quant_offset = get_be64_double(pb);
+ codec->dct_algo = get_be32(pb);
+ break;
+ case CODEC_TYPE_AUDIO:
+ codec->sample_rate = get_be32(pb);
+ codec->channels = get_le16(pb);
+ codec->frame_size = get_le16(pb);
+ break;
+ default:
+ goto fail;
+ }
+
+ }
+
+ /* get until end of block reached */
+ while ((url_ftell(pb) % ffm->packet_size) != 0)
+ get_byte(pb);
+
+ /* init packet demux */
+ ffm->packet_ptr = ffm->packet;
+ ffm->packet_end = ffm->packet;
+ ffm->frame_offset = 0;
+ ffm->pts = 0;
+ ffm->read_state = READ_HEADER;
+ ffm->first_packet = 1;
+ return 0;
+ fail:
+ for(i=0;i<s->nb_streams;i++) {
+ st = s->streams[i];
+ if (st) {
+ av_freep(&st->priv_data);
+ av_free(st);
+ }
+ }
+ return -1;
+}
+
+/* return < 0 if eof */
+static int ffm_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ int size;
+ FFMContext *ffm = s->priv_data;
+ int duration;
+
+ switch(ffm->read_state) {
+ case READ_HEADER:
+ if (!ffm_is_avail_data(s, FRAME_HEADER_SIZE)) {
+ return -EAGAIN;
+ }
+#if 0
+ printf("pos=%08Lx spos=%Lx, write_index=%Lx size=%Lx\n",
+ url_ftell(&s->pb), s->pb.pos, ffm->write_index, ffm->file_size);
+#endif
+ if (ffm_read_data(s, ffm->header, FRAME_HEADER_SIZE, 1) !=
+ FRAME_HEADER_SIZE)
+ return -EAGAIN;
+#if 0
+ {
+ int i;
+ for(i=0;i<FRAME_HEADER_SIZE;i++)
+ printf("%02x ", ffm->header[i]);
+ printf("\n");
+ }
+#endif
+ ffm->read_state = READ_DATA;
+ /* fall thru */
+ case READ_DATA:
+ size = (ffm->header[2] << 16) | (ffm->header[3] << 8) | ffm->header[4];
+ if (!ffm_is_avail_data(s, size)) {
+ return -EAGAIN;
+ }
+
+ duration = (ffm->header[5] << 16) | (ffm->header[6] << 8) | ffm->header[7];
+
+ av_new_packet(pkt, size);
+ pkt->stream_index = ffm->header[0];
+ if (ffm->header[1] & FLAG_KEY_FRAME)
+ pkt->flags |= PKT_FLAG_KEY;
+
+ ffm->read_state = READ_HEADER;
+ if (ffm_read_data(s, pkt->data, size, 0) != size) {
+ /* bad case: desynchronized packet. we cancel all the packet loading */
+ av_free_packet(pkt);
+ return -EAGAIN;
+ }
+ pkt->pts = ffm->pts;
+ pkt->duration = duration;
+ break;
+ }
+ return 0;
+}
+
+//#define DEBUG_SEEK
+
+/* pos is between 0 and file_size - FFM_PACKET_SIZE. It is translated
+ by the write position inside this function */
+static void ffm_seek1(AVFormatContext *s, offset_t pos1)
+{
+ FFMContext *ffm = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ offset_t pos;
+
+ pos = pos1 + ffm->write_index;
+ if (pos >= ffm->file_size)
+ pos -= (ffm->file_size - FFM_PACKET_SIZE);
+#ifdef DEBUG_SEEK
+ printf("seek to %Lx -> %Lx\n", pos1, pos);
+#endif
+ url_fseek(pb, pos, SEEK_SET);
+}
+
+static INT64 get_pts(AVFormatContext *s, offset_t pos)
+{
+ ByteIOContext *pb = &s->pb;
+ INT64 pts;
+
+ ffm_seek1(s, pos);
+ url_fskip(pb, 4);
+ pts = get_be64(pb);
+#ifdef DEBUG_SEEK
+ printf("pts=%0.6f\n", pts / 1000000.0);
+#endif
+ return pts;
+}
+
+/* seek to a given time in the file. The file read pointer is
+ positionned at or before pts. XXX: the following code is quite
+ approximative */
+static int ffm_seek(AVFormatContext *s, INT64 wanted_pts)
+{
+ FFMContext *ffm = s->priv_data;
+ offset_t pos_min, pos_max, pos;
+ INT64 pts_min, pts_max, pts;
+ double pos1;
+
+#ifdef DEBUG_SEEK
+ printf("wanted_pts=%0.6f\n", wanted_pts / 1000000.0);
+#endif
+ /* find the position using linear interpolation (better than
+ dichotomy in typical cases) */
+ pos_min = 0;
+ pos_max = ffm->file_size - 2 * FFM_PACKET_SIZE;
+ while (pos_min <= pos_max) {
+ pts_min = get_pts(s, pos_min);
+ pts_max = get_pts(s, pos_max);
+ /* linear interpolation */
+ pos1 = (double)(pos_max - pos_min) * (double)(wanted_pts - pts_min) /
+ (double)(pts_max - pts_min);
+ pos = (((INT64)pos1) / FFM_PACKET_SIZE) * FFM_PACKET_SIZE;
+ if (pos <= pos_min)
+ pos = pos_min;
+ else if (pos >= pos_max)
+ pos = pos_max;
+ pts = get_pts(s, pos);
+ /* check if we are lucky */
+ if (pts == wanted_pts) {
+ goto found;
+ } else if (pts > wanted_pts) {
+ pos_max = pos - FFM_PACKET_SIZE;
+ } else {
+ pos_min = pos + FFM_PACKET_SIZE;
+ }
+ }
+ pos = pos_min;
+ if (pos > 0)
+ pos -= FFM_PACKET_SIZE;
+ found:
+ ffm_seek1(s, pos);
+ return 0;
+}
+
+offset_t ffm_read_write_index(int fd)
+{
+ UINT8 buf[8];
+ offset_t pos;
+ int i;
+
+ lseek(fd, 8, SEEK_SET);
+ read(fd, buf, 8);
+ pos = 0;
+ for(i=0;i<8;i++)
+ pos |= buf[i] << (56 - i * 8);
+ return pos;
+}
+
+void ffm_write_write_index(int fd, offset_t pos)
+{
+ UINT8 buf[8];
+ int i;
+
+ for(i=0;i<8;i++)
+ buf[i] = (pos >> (56 - i * 8)) & 0xff;
+ lseek(fd, 8, SEEK_SET);
+ write(fd, buf, 8);
+}
+
+void ffm_set_write_index(AVFormatContext *s, offset_t pos, offset_t file_size)
+{
+ FFMContext *ffm = s->priv_data;
+ ffm->write_index = pos;
+ ffm->file_size = file_size;
+}
+
+static int ffm_read_close(AVFormatContext *s)
+{
+ AVStream *st;
+ int i;
+
+ for(i=0;i<s->nb_streams;i++) {
+ st = s->streams[i];
+ av_freep(&st->priv_data);
+ }
+ return 0;
+}
+
+static int ffm_probe(AVProbeData *p)
+{
+ if (p->buf_size >= 4 &&
+ p->buf[0] == 'F' && p->buf[1] == 'F' && p->buf[2] == 'M' &&
+ p->buf[3] == '1')
+ return AVPROBE_SCORE_MAX + 1;
+ return 0;
+}
+
+static AVInputFormat ffm_iformat = {
+ "ffm",
+ "ffm format",
+ sizeof(FFMContext),
+ ffm_probe,
+ ffm_read_header,
+ ffm_read_packet,
+ ffm_read_close,
+ ffm_seek,
+};
+
+static AVOutputFormat ffm_oformat = {
+ "ffm",
+ "ffm format",
+ "",
+ "ffm",
+ sizeof(FFMContext),
+ /* not really used */
+ CODEC_ID_MP2,
+ CODEC_ID_MPEG1VIDEO,
+ ffm_write_header,
+ ffm_write_packet,
+ ffm_write_trailer,
+};
+
+int ffm_init(void)
+{
+ av_register_input_format(&ffm_iformat);
+ av_register_output_format(&ffm_oformat);
+ return 0;
+}
diff --git a/libavformat/file.c b/libavformat/file.c
new file mode 100644
index 0000000000..8206ff9255
--- /dev/null
+++ b/libavformat/file.c
@@ -0,0 +1,130 @@
+/*
+ * Buffered file io for ffmpeg system
+ * Copyright (c) 2001 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"
+#include <fcntl.h>
+#ifndef CONFIG_WIN32
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#else
+#include <io.h>
+#define open(fname,oflag,pmode) _open(fname,oflag,pmode)
+#endif /* CONFIG_WIN32 */
+
+
+/* standard file protocol */
+
+static int file_open(URLContext *h, const char *filename, int flags)
+{
+ int access;
+ int fd;
+
+ if (flags & URL_WRONLY) {
+ access = O_CREAT | O_TRUNC | O_WRONLY;
+ } else {
+ access = O_RDONLY;
+ }
+#ifdef CONFIG_WIN32
+ access |= O_BINARY;
+#endif
+ fd = open(filename, access, 0666);
+ if (fd < 0)
+ return -ENOENT;
+ h->priv_data = (void *)fd;
+ return 0;
+}
+
+static int file_read(URLContext *h, unsigned char *buf, int size)
+{
+ int fd = (int)h->priv_data;
+ return read(fd, buf, size);
+}
+
+static int file_write(URLContext *h, unsigned char *buf, int size)
+{
+ int fd = (int)h->priv_data;
+ return write(fd, buf, size);
+}
+
+/* XXX: use llseek */
+static offset_t file_seek(URLContext *h, offset_t pos, int whence)
+{
+ int fd = (int)h->priv_data;
+#ifdef CONFIG_WIN32
+ return _lseeki64(fd, pos, whence);
+#else
+ return lseek(fd, pos, whence);
+#endif
+}
+
+static int file_close(URLContext *h)
+{
+ int fd = (int)h->priv_data;
+ return close(fd);
+}
+
+URLProtocol file_protocol = {
+ "file",
+ file_open,
+ file_read,
+ file_write,
+ file_seek,
+ file_close,
+};
+
+/* pipe protocol */
+
+static int pipe_open(URLContext *h, const char *filename, int flags)
+{
+ int fd;
+
+ if (flags & URL_WRONLY) {
+ fd = 1;
+ } else {
+ fd = 0;
+ }
+ h->priv_data = (void *)fd;
+ return 0;
+}
+
+static int pipe_read(URLContext *h, unsigned char *buf, int size)
+{
+ int fd = (int)h->priv_data;
+ return read(fd, buf, size);
+}
+
+static int pipe_write(URLContext *h, unsigned char *buf, int size)
+{
+ int fd = (int)h->priv_data;
+ return write(fd, buf, size);
+}
+
+static int pipe_close(URLContext *h)
+{
+ return 0;
+}
+
+URLProtocol pipe_protocol = {
+ "pipe",
+ pipe_open,
+ pipe_read,
+ pipe_write,
+ NULL,
+ pipe_close,
+};
diff --git a/libavformat/framehook.c b/libavformat/framehook.c
new file mode 100644
index 0000000000..03ee32e188
--- /dev/null
+++ b/libavformat/framehook.c
@@ -0,0 +1,102 @@
+/*
+ * Video processing hooks
+ * Copyright (c) 2000, 2001 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 <errno.h>
+#include "config.h"
+#include "framehook.h"
+#include "avformat.h"
+
+#ifdef HAVE_VHOOK
+#include <dlfcn.h>
+#endif
+
+
+typedef struct _FrameHookEntry {
+ struct _FrameHookEntry *next;
+ FrameHookConfigureFn Configure;
+ FrameHookProcessFn Process;
+ void *ctx;
+} FrameHookEntry;
+
+static FrameHookEntry *first_hook;
+
+/* Returns 0 on OK */
+int frame_hook_add(int argc, char *argv[])
+{
+#ifdef HAVE_VHOOK
+ void *loaded;
+ FrameHookEntry *fhe, **fhep;
+
+ if (argc < 1) {
+ return ENOENT;
+ }
+
+ loaded = dlopen(argv[0], RTLD_NOW);
+ if (!loaded) {
+ fprintf(stderr, "%s\n", dlerror());
+ return -1;
+ }
+
+ fhe = av_mallocz(sizeof(*fhe));
+ if (!fhe) {
+ return errno;
+ }
+
+ fhe->Configure = dlsym(loaded, "Configure");
+ fhe->Process = dlsym(loaded, "Process");
+
+ if (!fhe->Process) {
+ fprintf(stderr, "Failed to find Process entrypoint in %s\n", argv[0]);
+ return -1;
+ }
+
+ if (!fhe->Configure && argc > 1) {
+ fprintf(stderr, "Failed to find Configure entrypoint in %s\n", argv[0]);
+ return -1;
+ }
+
+ if (argc > 1 || fhe->Configure) {
+ if (fhe->Configure(&fhe->ctx, argc, argv)) {
+ fprintf(stderr, "Failed to Configure %s\n", argv[0]);
+ return -1;
+ }
+ }
+
+ for (fhep = &first_hook; *fhep; fhep = &((*fhep)->next)) {
+ }
+
+ *fhep = fhe;
+
+ return 0;
+#else
+ fprintf(stderr, "Video hooking not compiled into this version\n");
+ return 1;
+#endif
+}
+
+void frame_hook_process(AVPicture *pict, enum PixelFormat pix_fmt, int width, int height)
+{
+ if (first_hook) {
+ FrameHookEntry *fhe;
+ INT64 pts = av_gettime();
+
+ for (fhe = first_hook; fhe; fhe = fhe->next) {
+ fhe->Process(fhe->ctx, pict, pix_fmt, width, height, pts);
+ }
+ }
+}
diff --git a/libavformat/framehook.h b/libavformat/framehook.h
new file mode 100644
index 0000000000..eb1a51f7e4
--- /dev/null
+++ b/libavformat/framehook.h
@@ -0,0 +1,19 @@
+#ifndef _FRAMEHOOK_H
+#define _FRAMEHOOK_H
+
+/*
+ * Prototypes for interface to .so that implement a video processing hook
+ */
+
+#include "avcodec.h"
+
+/* Function must be called 'Configure' */
+typedef int (*FrameHookConfigureFn)(void **ctxp, int argc, char *argv[]);
+
+/* Function must be called 'Process' */
+typedef void (*FrameHookProcessFn)(void *ctx, struct AVPicture *pict, enum PixelFormat pix_fmt, int width, int height, INT64 pts);
+
+extern int frame_hook_add(int argc, char *argv[]);
+extern void frame_hook_process(struct AVPicture *pict, enum PixelFormat pix_fmt, int width, int height);
+
+#endif
diff --git a/libavformat/gif.c b/libavformat/gif.c
new file mode 100644
index 0000000000..6becf52a49
--- /dev/null
+++ b/libavformat/gif.c
@@ -0,0 +1,375 @@
+/*
+ * Animated GIF encoder
+ * Copyright (c) 2000 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
+ */
+
+/*
+ * First version by Francois Revol revol@free.fr
+ *
+ * Features and limitations:
+ * - currently no compression is performed,
+ * in fact the size of the data is 9/8 the size of the image in 8bpp
+ * - uses only a global standard palette
+ * - tested with IE 5.0, Opera for BeOS, NetPositive (BeOS), and Mozilla (BeOS).
+ *
+ * Reference documents:
+ * http://www.goice.co.jp/member/mo/formats/gif.html
+ * http://astronomy.swin.edu.au/pbourke/dataformats/gif/
+ * http://www.dcs.ed.ac.uk/home/mxr/gfx/2d/GIF89a.txt
+ *
+ * this url claims to have an LZW algorithm not covered by Unisys patent:
+ * http://www.msg.net/utility/whirlgif/gifencod.html
+ * could help reduce the size of the files _a lot_...
+ * some sites mentions an RLE type compression also.
+ */
+
+#include "avformat.h"
+
+/* bitstream minipacket size */
+#define GIF_CHUNKS 100
+
+/* slows down the decoding (and some browsers doesn't like it) */
+/* #define GIF_ADD_APP_HEADER */
+
+typedef struct {
+ unsigned char r;
+ unsigned char g;
+ unsigned char b;
+} rgb_triplet;
+
+/* we use the standard 216 color palette */
+
+/* this script was used to create the palette:
+ * for r in 00 33 66 99 cc ff; do for g in 00 33 66 99 cc ff; do echo -n " "; for b in 00 33 66 99 cc ff; do
+ * echo -n "{ 0x$r, 0x$g, 0x$b }, "; done; echo ""; done; done
+ */
+
+static const rgb_triplet gif_clut[216] = {
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x33 }, { 0x00, 0x00, 0x66 }, { 0x00, 0x00, 0x99 }, { 0x00, 0x00, 0xcc }, { 0x00, 0x00, 0xff },
+ { 0x00, 0x33, 0x00 }, { 0x00, 0x33, 0x33 }, { 0x00, 0x33, 0x66 }, { 0x00, 0x33, 0x99 }, { 0x00, 0x33, 0xcc }, { 0x00, 0x33, 0xff },
+ { 0x00, 0x66, 0x00 }, { 0x00, 0x66, 0x33 }, { 0x00, 0x66, 0x66 }, { 0x00, 0x66, 0x99 }, { 0x00, 0x66, 0xcc }, { 0x00, 0x66, 0xff },
+ { 0x00, 0x99, 0x00 }, { 0x00, 0x99, 0x33 }, { 0x00, 0x99, 0x66 }, { 0x00, 0x99, 0x99 }, { 0x00, 0x99, 0xcc }, { 0x00, 0x99, 0xff },
+ { 0x00, 0xcc, 0x00 }, { 0x00, 0xcc, 0x33 }, { 0x00, 0xcc, 0x66 }, { 0x00, 0xcc, 0x99 }, { 0x00, 0xcc, 0xcc }, { 0x00, 0xcc, 0xff },
+ { 0x00, 0xff, 0x00 }, { 0x00, 0xff, 0x33 }, { 0x00, 0xff, 0x66 }, { 0x00, 0xff, 0x99 }, { 0x00, 0xff, 0xcc }, { 0x00, 0xff, 0xff },
+ { 0x33, 0x00, 0x00 }, { 0x33, 0x00, 0x33 }, { 0x33, 0x00, 0x66 }, { 0x33, 0x00, 0x99 }, { 0x33, 0x00, 0xcc }, { 0x33, 0x00, 0xff },
+ { 0x33, 0x33, 0x00 }, { 0x33, 0x33, 0x33 }, { 0x33, 0x33, 0x66 }, { 0x33, 0x33, 0x99 }, { 0x33, 0x33, 0xcc }, { 0x33, 0x33, 0xff },
+ { 0x33, 0x66, 0x00 }, { 0x33, 0x66, 0x33 }, { 0x33, 0x66, 0x66 }, { 0x33, 0x66, 0x99 }, { 0x33, 0x66, 0xcc }, { 0x33, 0x66, 0xff },
+ { 0x33, 0x99, 0x00 }, { 0x33, 0x99, 0x33 }, { 0x33, 0x99, 0x66 }, { 0x33, 0x99, 0x99 }, { 0x33, 0x99, 0xcc }, { 0x33, 0x99, 0xff },
+ { 0x33, 0xcc, 0x00 }, { 0x33, 0xcc, 0x33 }, { 0x33, 0xcc, 0x66 }, { 0x33, 0xcc, 0x99 }, { 0x33, 0xcc, 0xcc }, { 0x33, 0xcc, 0xff },
+ { 0x33, 0xff, 0x00 }, { 0x33, 0xff, 0x33 }, { 0x33, 0xff, 0x66 }, { 0x33, 0xff, 0x99 }, { 0x33, 0xff, 0xcc }, { 0x33, 0xff, 0xff },
+ { 0x66, 0x00, 0x00 }, { 0x66, 0x00, 0x33 }, { 0x66, 0x00, 0x66 }, { 0x66, 0x00, 0x99 }, { 0x66, 0x00, 0xcc }, { 0x66, 0x00, 0xff },
+ { 0x66, 0x33, 0x00 }, { 0x66, 0x33, 0x33 }, { 0x66, 0x33, 0x66 }, { 0x66, 0x33, 0x99 }, { 0x66, 0x33, 0xcc }, { 0x66, 0x33, 0xff },
+ { 0x66, 0x66, 0x00 }, { 0x66, 0x66, 0x33 }, { 0x66, 0x66, 0x66 }, { 0x66, 0x66, 0x99 }, { 0x66, 0x66, 0xcc }, { 0x66, 0x66, 0xff },
+ { 0x66, 0x99, 0x00 }, { 0x66, 0x99, 0x33 }, { 0x66, 0x99, 0x66 }, { 0x66, 0x99, 0x99 }, { 0x66, 0x99, 0xcc }, { 0x66, 0x99, 0xff },
+ { 0x66, 0xcc, 0x00 }, { 0x66, 0xcc, 0x33 }, { 0x66, 0xcc, 0x66 }, { 0x66, 0xcc, 0x99 }, { 0x66, 0xcc, 0xcc }, { 0x66, 0xcc, 0xff },
+ { 0x66, 0xff, 0x00 }, { 0x66, 0xff, 0x33 }, { 0x66, 0xff, 0x66 }, { 0x66, 0xff, 0x99 }, { 0x66, 0xff, 0xcc }, { 0x66, 0xff, 0xff },
+ { 0x99, 0x00, 0x00 }, { 0x99, 0x00, 0x33 }, { 0x99, 0x00, 0x66 }, { 0x99, 0x00, 0x99 }, { 0x99, 0x00, 0xcc }, { 0x99, 0x00, 0xff },
+ { 0x99, 0x33, 0x00 }, { 0x99, 0x33, 0x33 }, { 0x99, 0x33, 0x66 }, { 0x99, 0x33, 0x99 }, { 0x99, 0x33, 0xcc }, { 0x99, 0x33, 0xff },
+ { 0x99, 0x66, 0x00 }, { 0x99, 0x66, 0x33 }, { 0x99, 0x66, 0x66 }, { 0x99, 0x66, 0x99 }, { 0x99, 0x66, 0xcc }, { 0x99, 0x66, 0xff },
+ { 0x99, 0x99, 0x00 }, { 0x99, 0x99, 0x33 }, { 0x99, 0x99, 0x66 }, { 0x99, 0x99, 0x99 }, { 0x99, 0x99, 0xcc }, { 0x99, 0x99, 0xff },
+ { 0x99, 0xcc, 0x00 }, { 0x99, 0xcc, 0x33 }, { 0x99, 0xcc, 0x66 }, { 0x99, 0xcc, 0x99 }, { 0x99, 0xcc, 0xcc }, { 0x99, 0xcc, 0xff },
+ { 0x99, 0xff, 0x00 }, { 0x99, 0xff, 0x33 }, { 0x99, 0xff, 0x66 }, { 0x99, 0xff, 0x99 }, { 0x99, 0xff, 0xcc }, { 0x99, 0xff, 0xff },
+ { 0xcc, 0x00, 0x00 }, { 0xcc, 0x00, 0x33 }, { 0xcc, 0x00, 0x66 }, { 0xcc, 0x00, 0x99 }, { 0xcc, 0x00, 0xcc }, { 0xcc, 0x00, 0xff },
+ { 0xcc, 0x33, 0x00 }, { 0xcc, 0x33, 0x33 }, { 0xcc, 0x33, 0x66 }, { 0xcc, 0x33, 0x99 }, { 0xcc, 0x33, 0xcc }, { 0xcc, 0x33, 0xff },
+ { 0xcc, 0x66, 0x00 }, { 0xcc, 0x66, 0x33 }, { 0xcc, 0x66, 0x66 }, { 0xcc, 0x66, 0x99 }, { 0xcc, 0x66, 0xcc }, { 0xcc, 0x66, 0xff },
+ { 0xcc, 0x99, 0x00 }, { 0xcc, 0x99, 0x33 }, { 0xcc, 0x99, 0x66 }, { 0xcc, 0x99, 0x99 }, { 0xcc, 0x99, 0xcc }, { 0xcc, 0x99, 0xff },
+ { 0xcc, 0xcc, 0x00 }, { 0xcc, 0xcc, 0x33 }, { 0xcc, 0xcc, 0x66 }, { 0xcc, 0xcc, 0x99 }, { 0xcc, 0xcc, 0xcc }, { 0xcc, 0xcc, 0xff },
+ { 0xcc, 0xff, 0x00 }, { 0xcc, 0xff, 0x33 }, { 0xcc, 0xff, 0x66 }, { 0xcc, 0xff, 0x99 }, { 0xcc, 0xff, 0xcc }, { 0xcc, 0xff, 0xff },
+ { 0xff, 0x00, 0x00 }, { 0xff, 0x00, 0x33 }, { 0xff, 0x00, 0x66 }, { 0xff, 0x00, 0x99 }, { 0xff, 0x00, 0xcc }, { 0xff, 0x00, 0xff },
+ { 0xff, 0x33, 0x00 }, { 0xff, 0x33, 0x33 }, { 0xff, 0x33, 0x66 }, { 0xff, 0x33, 0x99 }, { 0xff, 0x33, 0xcc }, { 0xff, 0x33, 0xff },
+ { 0xff, 0x66, 0x00 }, { 0xff, 0x66, 0x33 }, { 0xff, 0x66, 0x66 }, { 0xff, 0x66, 0x99 }, { 0xff, 0x66, 0xcc }, { 0xff, 0x66, 0xff },
+ { 0xff, 0x99, 0x00 }, { 0xff, 0x99, 0x33 }, { 0xff, 0x99, 0x66 }, { 0xff, 0x99, 0x99 }, { 0xff, 0x99, 0xcc }, { 0xff, 0x99, 0xff },
+ { 0xff, 0xcc, 0x00 }, { 0xff, 0xcc, 0x33 }, { 0xff, 0xcc, 0x66 }, { 0xff, 0xcc, 0x99 }, { 0xff, 0xcc, 0xcc }, { 0xff, 0xcc, 0xff },
+ { 0xff, 0xff, 0x00 }, { 0xff, 0xff, 0x33 }, { 0xff, 0xff, 0x66 }, { 0xff, 0xff, 0x99 }, { 0xff, 0xff, 0xcc }, { 0xff, 0xff, 0xff },
+};
+
+/* The GIF format uses reversed order for bitstreams... */
+/* at least they don't use PDP_ENDIAN :) */
+/* so we 'extend' PutBitContext. hmmm, OOP :) */
+/* seems this thing changed slightly since I wrote it... */
+
+#ifdef ALT_BITSTREAM_WRITER
+# error no ALT_BITSTREAM_WRITER support for now
+#endif
+
+static void gif_put_bits_rev(PutBitContext *s, int n, unsigned int value)
+{
+ unsigned int bit_buf;
+ int bit_cnt;
+
+#ifdef STATS
+ st_out_bit_counts[st_current_index] += n;
+#endif
+ // printf("put_bits=%d %x\n", n, value);
+ assert(n == 32 || value < (1U << n));
+
+ bit_buf = s->bit_buf;
+ bit_cnt = 32 - s->bit_left; /* XXX:lazyness... was = s->bit_cnt; */
+
+ // printf("n=%d value=%x cnt=%d buf=%x\n", n, value, bit_cnt, bit_buf);
+ /* XXX: optimize */
+ if (n < (32-bit_cnt)) {
+ bit_buf |= value << (bit_cnt);
+ bit_cnt+=n;
+ } else {
+ bit_buf |= value << (bit_cnt);
+
+ *s->buf_ptr = bit_buf & 0xff;
+ s->buf_ptr[1] = (bit_buf >> 8) & 0xff;
+ s->buf_ptr[2] = (bit_buf >> 16) & 0xff;
+ s->buf_ptr[3] = (bit_buf >> 24) & 0xff;
+
+ //printf("bitbuf = %08x\n", bit_buf);
+ s->buf_ptr+=4;
+ if (s->buf_ptr >= s->buf_end)
+ puts("bit buffer overflow !!"); // should never happen ! who got rid of the callback ???
+// flush_buffer_rev(s);
+ bit_cnt=bit_cnt + n - 32;
+ if (bit_cnt == 0) {
+ bit_buf = 0;
+ } else {
+ bit_buf = value >> (n - bit_cnt);
+ }
+ }
+
+ s->bit_buf = bit_buf;
+ s->bit_left = 32 - bit_cnt;
+}
+
+/* pad the end of the output stream with zeros */
+static void gif_flush_put_bits_rev(PutBitContext *s)
+{
+ while (s->bit_left < 32) {
+ /* XXX: should test end of buffer */
+ *s->buf_ptr++=s->bit_buf & 0xff;
+ s->bit_buf>>=8;
+ s->bit_left+=8;
+ }
+// flush_buffer_rev(s);
+ s->bit_left=32;
+ s->bit_buf=0;
+}
+
+/* !RevPutBitContext */
+
+typedef struct {
+ UINT8 buffer[100]; /* data chunks */
+ INT64 time, file_time;
+} GIFContext;
+
+static int gif_write_header(AVFormatContext *s)
+{
+ GIFContext *gif = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ AVCodecContext *enc, *video_enc;
+ int i, width, height, rate;
+
+/* XXX: do we reject audio streams or just ignore them ?
+ if(s->nb_streams > 1)
+ return -1;
+*/
+ gif->time = 0;
+ gif->file_time = 0;
+
+ video_enc = NULL;
+ for(i=0;i<s->nb_streams;i++) {
+ enc = &s->streams[i]->codec;
+ if (enc->codec_type != CODEC_TYPE_AUDIO)
+ video_enc = enc;
+ }
+
+ if (!video_enc) {
+ av_free(gif);
+ return -1;
+ } else {
+ width = video_enc->width;
+ height = video_enc->height;
+ rate = video_enc->frame_rate;
+ }
+
+ /* XXX: is it allowed ? seems to work so far... */
+ video_enc->pix_fmt = PIX_FMT_RGB24;
+
+ /* GIF header */
+
+ put_tag(pb, "GIF");
+ put_tag(pb, "89a");
+ put_le16(pb, width);
+ put_le16(pb, height);
+
+ put_byte(pb, 0xf7); /* flags: global clut, 256 entries */
+ put_byte(pb, 0x1f); /* background color index */
+ put_byte(pb, 0); /* aspect ratio */
+
+ /* the global palette */
+
+ put_buffer(pb, (unsigned char *)gif_clut, 216*3);
+ for(i=0;i<((256-216)*3);i++)
+ put_byte(pb, 0);
+
+ /* application extension header */
+ /* XXX: not really sure what to put in here... */
+#ifdef GIF_ADD_APP_HEADER
+ put_byte(pb, 0x21);
+ put_byte(pb, 0xff);
+ put_byte(pb, 0x0b);
+ put_tag(pb, "NETSCAPE2.0");
+ put_byte(pb, 0x03);
+ put_byte(pb, 0x01);
+ put_byte(pb, 0x00);
+ put_byte(pb, 0x00);
+#endif
+
+ put_flush_packet(&s->pb);
+ return 0;
+}
+
+/* this is maybe slow, but allows for extensions */
+static inline unsigned char gif_clut_index(rgb_triplet *clut, UINT8 r, UINT8 g, UINT8 b)
+{
+ return ((((r)/47)%6)*6*6+(((g)/47)%6)*6+(((b)/47)%6));
+}
+
+/* chunk writer callback */
+/* !!! XXX:deprecated
+static void gif_put_chunk(void *pbctx, UINT8 *buffer, int count)
+{
+ ByteIOContext *pb = (ByteIOContext *)pbctx;
+ put_byte(pb, (UINT8)count);
+ put_buffer(pb, buffer, count);
+}
+*/
+
+static int gif_write_video(AVFormatContext *s,
+ AVCodecContext *enc, UINT8 *buf, int size)
+{
+ ByteIOContext *pb = &s->pb;
+ GIFContext *gif = s->priv_data;
+ int i, left, jiffies;
+ INT64 delay;
+ PutBitContext p;
+ UINT8 buffer[200]; /* 100 * 9 / 8 = 113 */
+
+
+ /* graphic control extension block */
+ put_byte(pb, 0x21);
+ put_byte(pb, 0xf9);
+ put_byte(pb, 0x04); /* block size */
+ put_byte(pb, 0x04); /* flags */
+
+ /* 1 jiffy is 1/70 s */
+ /* the delay_time field indicates the number of jiffies - 1 */
+ delay = gif->file_time - gif->time;
+
+ /* XXX: should use delay, in order to be more accurate */
+ /* instead of using the same rounded value each time */
+ /* XXX: don't even remember if I really use it for now */
+ jiffies = (70*FRAME_RATE_BASE/enc->frame_rate) - 1;
+
+ put_le16(pb, jiffies);
+
+ put_byte(pb, 0x1f); /* transparent color index */
+ put_byte(pb, 0x00);
+
+ /* image block */
+
+ put_byte(pb, 0x2c);
+ put_le16(pb, 0);
+ put_le16(pb, 0);
+ put_le16(pb, enc->width);
+ put_le16(pb, enc->height);
+ put_byte(pb, 0x00); /* flags */
+ /* no local clut */
+
+ put_byte(pb, 0x08);
+
+ left=size/3;
+
+ init_put_bits(&p, buffer, 130, NULL, NULL);
+
+/*
+ * the thing here is the bitstream is written as little packets, with a size byte before
+ * but it's still the same bitstream between packets (no flush !)
+ */
+
+ while(left>0) {
+
+ gif_put_bits_rev(&p, 9, 0x0100); /* clear code */
+
+ for(i=0;i<GIF_CHUNKS;i++) {
+ gif_put_bits_rev(&p, 9, gif_clut_index(NULL, *buf, buf[1], buf[2]));
+ buf+=3;
+ }
+
+ if(left<=GIF_CHUNKS) {
+ gif_put_bits_rev(&p, 9, 0x101); /* end of stream */
+ gif_flush_put_bits_rev(&p);
+ }
+ if(pbBufPtr(&p) - p.buf > 0) {
+ put_byte(pb, pbBufPtr(&p) - p.buf); /* byte count of the packet */
+ put_buffer(pb, p.buf, pbBufPtr(&p) - p.buf); /* the actual buffer */
+ p.data_out_size += pbBufPtr(&p) - p.buf;
+ p.buf_ptr = p.buf; /* dequeue the bytes off the bitstream */
+ }
+ if(left<=GIF_CHUNKS) {
+ put_byte(pb, 0x00); /* end of image block */
+ }
+
+ left-=GIF_CHUNKS;
+ }
+
+ put_flush_packet(&s->pb);
+ return 0;
+}
+
+static int gif_write_packet(AVFormatContext *s, int stream_index,
+ UINT8 *buf, int size, int force_pts)
+{
+ AVCodecContext *codec = &s->streams[stream_index]->codec;
+ if (codec->codec_type == CODEC_TYPE_AUDIO)
+ return 0; /* just ignore audio */
+ else
+ return gif_write_video(s, codec, buf, size);
+}
+
+static int gif_write_trailer(AVFormatContext *s)
+{
+ ByteIOContext *pb = &s->pb;
+
+ put_byte(pb, 0x3b);
+ put_flush_packet(&s->pb);
+ return 0;
+}
+
+static AVOutputFormat gif_oformat = {
+ "gif",
+ "GIF Animation",
+ "image/gif",
+ "gif",
+ sizeof(GIFContext),
+ CODEC_ID_NONE,
+ CODEC_ID_RAWVIDEO,
+ gif_write_header,
+ gif_write_packet,
+ gif_write_trailer,
+};
+
+int gif_init(void)
+{
+ av_register_output_format(&gif_oformat);
+ return 0;
+}
diff --git a/libavformat/grab.c b/libavformat/grab.c
new file mode 100644
index 0000000000..8173bac369
--- /dev/null
+++ b/libavformat/grab.c
@@ -0,0 +1,829 @@
+/*
+ * Linux video grab interface
+ * Copyright (c) 2000,2001 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"
+#include <linux/videodev.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <time.h>
+
+typedef struct {
+ int fd;
+ int frame_format; /* see VIDEO_PALETTE_xxx */
+ int use_mmap;
+ int width, height;
+ int frame_rate;
+ INT64 time_frame;
+ int frame_size;
+ struct video_capability video_cap;
+ struct video_audio audio_saved;
+ UINT8 *video_buf;
+ struct video_mbuf gb_buffers;
+ struct video_mmap gb_buf;
+ int gb_frame;
+
+ /* ATI All In Wonder specific stuff */
+ /* XXX: remove and merge in libavcodec/imgconvert.c */
+ int aiw_enabled;
+ int deint;
+ int halfw;
+ UINT8 *src_mem;
+ UINT8 *lum_m4_mem;
+} VideoData;
+
+static int aiw_init(VideoData *s);
+static int aiw_read_picture(VideoData *s, uint8_t *data);
+static int aiw_close(VideoData *s);
+
+const char *v4l_device = "/dev/video";
+
+static int grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
+{
+ VideoData *s = s1->priv_data;
+ AVStream *st;
+ int width, height;
+ int video_fd, frame_size;
+ int ret, frame_rate;
+ int desired_palette;
+ struct video_audio audio;
+
+ if (!ap || ap->width <= 0 || ap->height <= 0 || ap->frame_rate <= 0)
+ return -1;
+
+ width = ap->width;
+ height = ap->height;
+ frame_rate = ap->frame_rate;
+
+ st = av_new_stream(s1, 0);
+ if (!st)
+ return -ENOMEM;
+
+ s->width = width;
+ s->height = height;
+ s->frame_rate = frame_rate;
+
+ video_fd = open(v4l_device, O_RDWR);
+ if (video_fd < 0) {
+ perror(v4l_device);
+ goto fail;
+ }
+
+ if (ioctl(video_fd,VIDIOCGCAP, &s->video_cap) < 0) {
+ perror("VIDIOCGCAP");
+ goto fail;
+ }
+
+ if (!(s->video_cap.type & VID_TYPE_CAPTURE)) {
+ fprintf(stderr, "Fatal: grab device does not handle capture\n");
+ goto fail;
+ }
+
+ desired_palette = -1;
+ if (st->codec.pix_fmt == PIX_FMT_YUV420P) {
+ desired_palette = VIDEO_PALETTE_YUV420P;
+ } else if (st->codec.pix_fmt == PIX_FMT_YUV422) {
+ desired_palette = VIDEO_PALETTE_YUV422;
+ } else if (st->codec.pix_fmt == PIX_FMT_BGR24) {
+ desired_palette = VIDEO_PALETTE_RGB24;
+ }
+
+ /* unmute audio */
+ audio.audio = 0;
+ ioctl(video_fd, VIDIOCGAUDIO, &audio);
+ memcpy(&s->audio_saved, &audio, sizeof(audio));
+ audio.flags &= ~VIDEO_AUDIO_MUTE;
+ ioctl(video_fd, VIDIOCSAUDIO, &audio);
+
+ ret = ioctl(video_fd,VIDIOCGMBUF,&s->gb_buffers);
+ if (ret < 0) {
+ /* try to use read based access */
+ struct video_window win;
+ struct video_picture pict;
+ int val;
+
+ win.x = 0;
+ win.y = 0;
+ win.width = width;
+ win.height = height;
+ win.chromakey = -1;
+ win.flags = 0;
+
+ ioctl(video_fd, VIDIOCSWIN, &win);
+
+ ioctl(video_fd, VIDIOCGPICT, &pict);
+#if 0
+ printf("v4l: colour=%d hue=%d brightness=%d constrast=%d whiteness=%d\n",
+ pict.colour,
+ pict.hue,
+ pict.brightness,
+ pict.contrast,
+ pict.whiteness);
+#endif
+ /* try to choose a suitable video format */
+ pict.palette = desired_palette;
+ if (desired_palette == -1 || (ret = ioctl(video_fd, VIDIOCSPICT, &pict)) < 0) {
+ pict.palette=VIDEO_PALETTE_YUV420P;
+ ret = ioctl(video_fd, VIDIOCSPICT, &pict);
+ if (ret < 0) {
+ pict.palette=VIDEO_PALETTE_YUV422;
+ ret = ioctl(video_fd, VIDIOCSPICT, &pict);
+ if (ret < 0) {
+ pict.palette=VIDEO_PALETTE_RGB24;
+ ret = ioctl(video_fd, VIDIOCSPICT, &pict);
+ if (ret < 0)
+ goto fail1;
+ }
+ }
+ }
+
+ s->frame_format = pict.palette;
+
+ val = 1;
+ ioctl(video_fd, VIDIOCCAPTURE, &val);
+
+ s->time_frame = av_gettime();
+ s->use_mmap = 0;
+
+ /* ATI All In Wonder automatic activation */
+ if (!strcmp(s->video_cap.name, "Km")) {
+ if (aiw_init(s) < 0)
+ goto fail;
+ s->aiw_enabled = 1;
+ /* force 420P format because convertion from YUV422 to YUV420P
+ is done in this driver (ugly) */
+ s->frame_format = VIDEO_PALETTE_YUV420P;
+ }
+ } else {
+ s->video_buf = mmap(0,s->gb_buffers.size,PROT_READ|PROT_WRITE,MAP_SHARED,video_fd,0);
+ if ((unsigned char*)-1 == s->video_buf) {
+ perror("mmap");
+ goto fail;
+ }
+ s->gb_frame = 0;
+ s->time_frame = av_gettime();
+
+ /* start to grab the first frame */
+ s->gb_buf.frame = s->gb_frame % s->gb_buffers.frames;
+ s->gb_buf.height = height;
+ s->gb_buf.width = width;
+ s->gb_buf.format = desired_palette;
+
+ if (desired_palette == -1 || (ret = ioctl(video_fd, VIDIOCMCAPTURE, &s->gb_buf)) < 0) {
+ s->gb_buf.format = VIDEO_PALETTE_YUV420P;
+
+ ret = ioctl(video_fd, VIDIOCMCAPTURE, &s->gb_buf);
+ if (ret < 0 && errno != EAGAIN) {
+ /* try YUV422 */
+ s->gb_buf.format = VIDEO_PALETTE_YUV422;
+
+ ret = ioctl(video_fd, VIDIOCMCAPTURE, &s->gb_buf);
+ if (ret < 0 && errno != EAGAIN) {
+ /* try RGB24 */
+ s->gb_buf.format = VIDEO_PALETTE_RGB24;
+ ret = ioctl(video_fd, VIDIOCMCAPTURE, &s->gb_buf);
+ }
+ }
+ }
+ if (ret < 0) {
+ if (errno != EAGAIN) {
+ fail1:
+ fprintf(stderr, "Fatal: grab device does not support suitable format\n");
+ } else {
+ fprintf(stderr,"Fatal: grab device does not receive any video signal\n");
+ }
+ goto fail;
+ }
+ s->frame_format = s->gb_buf.format;
+ s->use_mmap = 1;
+ }
+
+ switch(s->frame_format) {
+ case VIDEO_PALETTE_YUV420P:
+ frame_size = (width * height * 3) / 2;
+ st->codec.pix_fmt = PIX_FMT_YUV420P;
+ break;
+ case VIDEO_PALETTE_YUV422:
+ frame_size = width * height * 2;
+ st->codec.pix_fmt = PIX_FMT_YUV422;
+ break;
+ case VIDEO_PALETTE_RGB24:
+ frame_size = width * height * 3;
+ st->codec.pix_fmt = PIX_FMT_BGR24; /* NOTE: v4l uses BGR24, not RGB24 ! */
+ break;
+ default:
+ goto fail;
+ }
+ s->fd = video_fd;
+ s->frame_size = frame_size;
+
+ st->codec.codec_type = CODEC_TYPE_VIDEO;
+ st->codec.codec_id = CODEC_ID_RAWVIDEO;
+ st->codec.width = width;
+ st->codec.height = height;
+ st->codec.frame_rate = frame_rate;
+
+ av_set_pts_info(s1, 48, 1, 1000000); /* 48 bits pts in us */
+
+ return 0;
+ fail:
+ if (video_fd >= 0)
+ close(video_fd);
+ av_free(st);
+ return -EIO;
+}
+
+static int v4l_mm_read_picture(VideoData *s, UINT8 *buf)
+{
+ UINT8 *ptr;
+
+ /* Setup to capture the next frame */
+ s->gb_buf.frame = (s->gb_frame + 1) % s->gb_buffers.frames;
+ if (ioctl(s->fd, VIDIOCMCAPTURE, &s->gb_buf) < 0) {
+ if (errno == EAGAIN)
+ fprintf(stderr,"Cannot Sync\n");
+ else
+ perror("VIDIOCMCAPTURE");
+ return -EIO;
+ }
+
+ while (ioctl(s->fd, VIDIOCSYNC, &s->gb_frame) < 0 &&
+ (errno == EAGAIN || errno == EINTR));
+
+ ptr = s->video_buf + s->gb_buffers.offsets[s->gb_frame];
+ memcpy(buf, ptr, s->frame_size);
+
+ /* This is now the grabbing frame */
+ s->gb_frame = s->gb_buf.frame;
+
+ return s->frame_size;
+}
+
+static int grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
+{
+ VideoData *s = s1->priv_data;
+ INT64 curtime, delay;
+ struct timespec ts;
+ INT64 per_frame = (INT64_C(1000000) * FRAME_RATE_BASE) / s->frame_rate;
+
+ /* Calculate the time of the next frame */
+ s->time_frame += per_frame;
+
+ /* wait based on the frame rate */
+ for(;;) {
+ curtime = av_gettime();
+ delay = s->time_frame - curtime;
+ if (delay <= 0) {
+ if (delay < -per_frame) {
+ /* printf("grabbing is %d frames late (dropping)\n", (int) -(delay / 16666)); */
+ s->time_frame += per_frame;
+ }
+ break;
+ }
+ ts.tv_sec = delay / 1000000;
+ ts.tv_nsec = (delay % 1000000) * 1000;
+ nanosleep(&ts, NULL);
+ }
+
+ if (av_new_packet(pkt, s->frame_size) < 0)
+ return -EIO;
+
+ pkt->pts = curtime & ((1LL << 48) - 1);
+
+ /* read one frame */
+ if (s->aiw_enabled) {
+ return aiw_read_picture(s, pkt->data);
+ } else if (s->use_mmap) {
+ return v4l_mm_read_picture(s, pkt->data);
+ } else {
+ if (read(s->fd, pkt->data, pkt->size) != pkt->size)
+ return -EIO;
+ return s->frame_size;
+ }
+}
+
+static int grab_read_close(AVFormatContext *s1)
+{
+ VideoData *s = s1->priv_data;
+
+ if (s->aiw_enabled)
+ aiw_close(s);
+
+ if (s->use_mmap)
+ munmap(s->video_buf, s->gb_buffers.size);
+
+ /* mute audio. we must force it because the BTTV driver does not
+ return its state correctly */
+ s->audio_saved.flags |= VIDEO_AUDIO_MUTE;
+ ioctl(s->fd, VIDIOCSAUDIO, &s->audio_saved);
+
+ close(s->fd);
+ return 0;
+}
+
+static AVInputFormat video_grab_device_format = {
+ "video_grab_device",
+ "video grab",
+ sizeof(VideoData),
+ NULL,
+ grab_read_header,
+ grab_read_packet,
+ grab_read_close,
+ .flags = AVFMT_NOFILE,
+};
+
+/* All in Wonder specific stuff */
+/* XXX: remove and merge in libavcodec/imgconvert.c */
+
+static int aiw_init(VideoData *s)
+{
+ int width, height;
+
+ width = s->width;
+ height = s->height;
+
+ if ((width == s->video_cap.maxwidth && height == s->video_cap.maxheight) ||
+ (width == s->video_cap.maxwidth && height == s->video_cap.maxheight*2) ||
+ (width == s->video_cap.maxwidth/2 && height == s->video_cap.maxheight)) {
+
+ s->deint=0;
+ s->halfw=0;
+ if (height == s->video_cap.maxheight*2) s->deint=1;
+ if (width == s->video_cap.maxwidth/2) s->halfw=1;
+ } else {
+ fprintf(stderr,"\nIncorrect Grab Size Supplied - Supported Sizes Are:\n");
+ fprintf(stderr," %dx%d %dx%d %dx%d\n\n",
+ s->video_cap.maxwidth,s->video_cap.maxheight,
+ s->video_cap.maxwidth,s->video_cap.maxheight*2,
+ s->video_cap.maxwidth/2,s->video_cap.maxheight);
+ goto fail;
+ }
+
+ if (s->halfw == 0) {
+ s->src_mem = av_malloc(s->width*2);
+ } else {
+ s->src_mem = av_malloc(s->width*4);
+ }
+ if (!s->src_mem) goto fail;
+
+ s->lum_m4_mem = av_malloc(s->width);
+ if (!s->lum_m4_mem)
+ goto fail;
+ return 0;
+ fail:
+ av_freep(&s->src_mem);
+ av_freep(&s->lum_m4_mem);
+ return -1;
+}
+
+#ifdef HAVE_MMX
+#include "../libavcodec/i386/mmx.h"
+
+#define LINE_WITH_UV \
+ movq_m2r(ptr[0],mm0); \
+ movq_m2r(ptr[8],mm1); \
+ movq_r2r(mm0, mm4); \
+ punpcklbw_r2r(mm1,mm0); \
+ punpckhbw_r2r(mm1,mm4); \
+ movq_r2r(mm0,mm5); \
+ punpcklbw_r2r(mm4,mm0); \
+ punpckhbw_r2r(mm4,mm5); \
+ movq_r2r(mm0,mm1); \
+ punpcklbw_r2r(mm5,mm1); \
+ movq_r2m(mm1,lum[0]); \
+ movq_m2r(ptr[16],mm2); \
+ movq_m2r(ptr[24],mm1); \
+ movq_r2r(mm2,mm4); \
+ punpcklbw_r2r(mm1,mm2); \
+ punpckhbw_r2r(mm1,mm4); \
+ movq_r2r(mm2,mm3); \
+ punpcklbw_r2r(mm4,mm2); \
+ punpckhbw_r2r(mm4,mm3); \
+ movq_r2r(mm2,mm1); \
+ punpcklbw_r2r(mm3,mm1); \
+ movq_r2m(mm1,lum[8]); \
+ punpckhdq_r2r(mm2,mm0); \
+ punpckhdq_r2r(mm3,mm5); \
+ movq_r2m(mm0,cb[0]); \
+ movq_r2m(mm5,cr[0]);
+
+#define LINE_NO_UV \
+ movq_m2r(ptr[0],mm0);\
+ movq_m2r(ptr[8],mm1);\
+ movq_r2r(mm0, mm4);\
+ punpcklbw_r2r(mm1,mm0); \
+ punpckhbw_r2r(mm1,mm4);\
+ movq_r2r(mm0,mm5);\
+ punpcklbw_r2r(mm4,mm0);\
+ punpckhbw_r2r(mm4,mm5);\
+ movq_r2r(mm0,mm1);\
+ punpcklbw_r2r(mm5,mm1);\
+ movq_r2m(mm1,lum[0]);\
+ movq_m2r(ptr[16],mm2);\
+ movq_m2r(ptr[24],mm1);\
+ movq_r2r(mm2,mm4);\
+ punpcklbw_r2r(mm1,mm2);\
+ punpckhbw_r2r(mm1,mm4);\
+ movq_r2r(mm2,mm3);\
+ punpcklbw_r2r(mm4,mm2);\
+ punpckhbw_r2r(mm4,mm3);\
+ movq_r2r(mm2,mm1);\
+ punpcklbw_r2r(mm3,mm1);\
+ movq_r2m(mm1,lum[8]);
+
+#define LINE_WITHUV_AVG \
+ movq_m2r(ptr[0], mm0);\
+ movq_m2r(ptr[8], mm1);\
+ movq_r2r(mm0, mm4);\
+ punpcklbw_r2r(mm1,mm0);\
+ punpckhbw_r2r(mm1,mm4);\
+ movq_r2r(mm0,mm5);\
+ punpcklbw_r2r(mm4,mm0);\
+ punpckhbw_r2r(mm4,mm5);\
+ movq_r2r(mm0,mm1);\
+ movq_r2r(mm5,mm2);\
+ punpcklbw_r2r(mm7,mm1);\
+ punpcklbw_r2r(mm7,mm2);\
+ paddw_r2r(mm6,mm1);\
+ paddw_r2r(mm2,mm1);\
+ psraw_i2r(1,mm1);\
+ packuswb_r2r(mm7,mm1);\
+ movd_r2m(mm1,lum[0]);\
+ movq_m2r(ptr[16],mm2);\
+ movq_m2r(ptr[24],mm1);\
+ movq_r2r(mm2,mm4);\
+ punpcklbw_r2r(mm1,mm2);\
+ punpckhbw_r2r(mm1,mm4);\
+ movq_r2r(mm2,mm3);\
+ punpcklbw_r2r(mm4,mm2);\
+ punpckhbw_r2r(mm4,mm3);\
+ movq_r2r(mm2,mm1);\
+ movq_r2r(mm3,mm4);\
+ punpcklbw_r2r(mm7,mm1);\
+ punpcklbw_r2r(mm7,mm4);\
+ paddw_r2r(mm6,mm1);\
+ paddw_r2r(mm4,mm1);\
+ psraw_i2r(1,mm1);\
+ packuswb_r2r(mm7,mm1);\
+ movd_r2m(mm1,lum[4]);\
+ punpckhbw_r2r(mm7,mm0);\
+ punpckhbw_r2r(mm7,mm2);\
+ paddw_r2r(mm6,mm0);\
+ paddw_r2r(mm2,mm0);\
+ psraw_i2r(1,mm0);\
+ packuswb_r2r(mm7,mm0);\
+ punpckhbw_r2r(mm7,mm5);\
+ punpckhbw_r2r(mm7,mm3);\
+ paddw_r2r(mm6,mm5);\
+ paddw_r2r(mm3,mm5);\
+ psraw_i2r(1,mm5);\
+ packuswb_r2r(mm7,mm5);\
+ movd_r2m(mm0,cb[0]);\
+ movd_r2m(mm5,cr[0]);
+
+#define LINE_NOUV_AVG \
+ movq_m2r(ptr[0],mm0);\
+ movq_m2r(ptr[8],mm1);\
+ pand_r2r(mm5,mm0);\
+ pand_r2r(mm5,mm1);\
+ pmaddwd_r2r(mm6,mm0);\
+ pmaddwd_r2r(mm6,mm1);\
+ packssdw_r2r(mm1,mm0);\
+ paddw_r2r(mm6,mm0);\
+ psraw_i2r(1,mm0);\
+ movq_m2r(ptr[16],mm2);\
+ movq_m2r(ptr[24],mm3);\
+ pand_r2r(mm5,mm2);\
+ pand_r2r(mm5,mm3);\
+ pmaddwd_r2r(mm6,mm2);\
+ pmaddwd_r2r(mm6,mm3);\
+ packssdw_r2r(mm3,mm2);\
+ paddw_r2r(mm6,mm2);\
+ psraw_i2r(1,mm2);\
+ packuswb_r2r(mm2,mm0);\
+ movq_r2m(mm0,lum[0]);
+
+#define DEINT_LINE_LUM(ptroff) \
+ movd_m2r(lum_m4[(ptroff)],mm0);\
+ movd_m2r(lum_m3[(ptroff)],mm1);\
+ movd_m2r(lum_m2[(ptroff)],mm2);\
+ movd_m2r(lum_m1[(ptroff)],mm3);\
+ movd_m2r(lum[(ptroff)],mm4);\
+ punpcklbw_r2r(mm7,mm0);\
+ movd_r2m(mm2,lum_m4[(ptroff)]);\
+ punpcklbw_r2r(mm7,mm1);\
+ punpcklbw_r2r(mm7,mm2);\
+ punpcklbw_r2r(mm7,mm3);\
+ punpcklbw_r2r(mm7,mm4);\
+ psllw_i2r(2,mm1);\
+ psllw_i2r(1,mm2);\
+ paddw_r2r(mm6,mm1);\
+ psllw_i2r(2,mm3);\
+ paddw_r2r(mm2,mm1);\
+ paddw_r2r(mm4,mm0);\
+ paddw_r2r(mm3,mm1);\
+ psubusw_r2r(mm0,mm1);\
+ psrlw_i2r(3,mm1);\
+ packuswb_r2r(mm7,mm1);\
+ movd_r2m(mm1,lum_m2[(ptroff)]);
+
+#else
+#include "../libavcodec/dsputil.h"
+
+#define LINE_WITH_UV \
+ lum[0]=ptr[0];lum[1]=ptr[2];lum[2]=ptr[4];lum[3]=ptr[6];\
+ cb[0]=ptr[1];cb[1]=ptr[5];\
+ cr[0]=ptr[3];cr[1]=ptr[7];\
+ lum[4]=ptr[8];lum[5]=ptr[10];lum[6]=ptr[12];lum[7]=ptr[14];\
+ cb[2]=ptr[9];cb[3]=ptr[13];\
+ cr[2]=ptr[11];cr[3]=ptr[15];\
+ lum[8]=ptr[16];lum[9]=ptr[18];lum[10]=ptr[20];lum[11]=ptr[22];\
+ cb[4]=ptr[17];cb[5]=ptr[21];\
+ cr[4]=ptr[19];cr[5]=ptr[23];\
+ lum[12]=ptr[24];lum[13]=ptr[26];lum[14]=ptr[28];lum[15]=ptr[30];\
+ cb[6]=ptr[25];cb[7]=ptr[29];\
+ cr[6]=ptr[27];cr[7]=ptr[31];
+
+#define LINE_NO_UV \
+ lum[0]=ptr[0];lum[1]=ptr[2];lum[2]=ptr[4];lum[3]=ptr[6];\
+ lum[4]=ptr[8];lum[5]=ptr[10];lum[6]=ptr[12];lum[7]=ptr[14];\
+ lum[8]=ptr[16];lum[9]=ptr[18];lum[10]=ptr[20];lum[11]=ptr[22];\
+ lum[12]=ptr[24];lum[13]=ptr[26];lum[14]=ptr[28];lum[15]=ptr[30];
+
+#define LINE_WITHUV_AVG \
+ sum=(ptr[0]+ptr[2]+1) >> 1;lum[0]=sum; \
+ sum=(ptr[4]+ptr[6]+1) >> 1;lum[1]=sum; \
+ sum=(ptr[1]+ptr[5]+1) >> 1;cb[0]=sum; \
+ sum=(ptr[3]+ptr[7]+1) >> 1;cr[0]=sum; \
+ sum=(ptr[8]+ptr[10]+1) >> 1;lum[2]=sum; \
+ sum=(ptr[12]+ptr[14]+1) >> 1;lum[3]=sum; \
+ sum=(ptr[9]+ptr[13]+1) >> 1;cb[1]=sum; \
+ sum=(ptr[11]+ptr[15]+1) >> 1;cr[1]=sum; \
+ sum=(ptr[16]+ptr[18]+1) >> 1;lum[4]=sum; \
+ sum=(ptr[20]+ptr[22]+1) >> 1;lum[5]=sum; \
+ sum=(ptr[17]+ptr[21]+1) >> 1;cb[2]=sum; \
+ sum=(ptr[19]+ptr[23]+1) >> 1;cr[2]=sum; \
+ sum=(ptr[24]+ptr[26]+1) >> 1;lum[6]=sum; \
+ sum=(ptr[28]+ptr[30]+1) >> 1;lum[7]=sum; \
+ sum=(ptr[25]+ptr[29]+1) >> 1;cb[3]=sum; \
+ sum=(ptr[27]+ptr[31]+1) >> 1;cr[3]=sum;
+
+#define LINE_NOUV_AVG \
+ sum=(ptr[0]+ptr[2]+1) >> 1;lum[0]=sum; \
+ sum=(ptr[4]+ptr[6]+1) >> 1;lum[1]=sum; \
+ sum=(ptr[8]+ptr[10]+1) >> 1;lum[2]=sum; \
+ sum=(ptr[12]+ptr[14]+1) >> 1;lum[3]=sum; \
+ sum=(ptr[16]+ptr[18]+1) >> 1;lum[4]=sum; \
+ sum=(ptr[20]+ptr[22]+1) >> 1;lum[5]=sum; \
+ sum=(ptr[24]+ptr[26]+1) >> 1;lum[6]=sum; \
+ sum=(ptr[28]+ptr[30]+1) >> 1;lum[7]=sum;
+
+#define DEINT_LINE_LUM(ptroff) \
+ sum=(-lum_m4[(ptroff)]+(lum_m3[(ptroff)]<<2)+(lum_m2[(ptroff)]<<1)+(lum_m1[(ptroff)]<<2)-lum[(ptroff)]); \
+ lum_m4[(ptroff)]=lum_m2[(ptroff)];\
+ lum_m2[(ptroff)]=cm[(sum+4)>>3];\
+ sum=(-lum_m4[(ptroff)+1]+(lum_m3[(ptroff)+1]<<2)+(lum_m2[(ptroff)+1]<<1)+(lum_m1[(ptroff)+1]<<2)-lum[(ptroff)+1]); \
+ lum_m4[(ptroff)+1]=lum_m2[(ptroff)+1];\
+ lum_m2[(ptroff)+1]=cm[(sum+4)>>3];\
+ sum=(-lum_m4[(ptroff)+2]+(lum_m3[(ptroff)+2]<<2)+(lum_m2[(ptroff)+2]<<1)+(lum_m1[(ptroff)+2]<<2)-lum[(ptroff)+2]); \
+ lum_m4[(ptroff)+2]=lum_m2[(ptroff)+2];\
+ lum_m2[(ptroff)+2]=cm[(sum+4)>>3];\
+ sum=(-lum_m4[(ptroff)+3]+(lum_m3[(ptroff)+3]<<2)+(lum_m2[(ptroff)+3]<<1)+(lum_m1[(ptroff)+3]<<2)-lum[(ptroff)+3]); \
+ lum_m4[(ptroff)+3]=lum_m2[(ptroff)+3];\
+ lum_m2[(ptroff)+3]=cm[(sum+4)>>3];
+
+#endif
+
+
+/* Read two fields separately. */
+static int aiw_read_picture(VideoData *s, uint8_t *data)
+{
+ UINT8 *ptr, *lum, *cb, *cr;
+ int h;
+#ifndef HAVE_MMX
+ int sum;
+#endif
+ UINT8* src = s->src_mem;
+ UINT8 *ptrend = &src[s->width*2];
+ lum=data;
+ cb=&lum[s->width*s->height];
+ cr=&cb[(s->width*s->height)/4];
+ if (s->deint == 0 && s->halfw == 0) {
+ while (read(s->fd,src,s->width*2) < 0) {
+ usleep(100);
+ }
+ for (h = 0; h < s->height-2; h+=2) {
+ for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) {
+ LINE_WITH_UV
+ }
+ read(s->fd,src,s->width*2);
+ for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16) {
+ LINE_NO_UV
+ }
+ read(s->fd,src,s->width*2);
+ }
+ /*
+ * Do last two lines
+ */
+ for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) {
+ LINE_WITH_UV
+ }
+ read(s->fd,src,s->width*2);
+ for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16) {
+ LINE_NO_UV
+ }
+ /* drop second field */
+ while (read(s->fd,src,s->width*2) < 0) {
+ usleep(100);
+ }
+ for (h = 0; h < s->height - 1; h++) {
+ read(s->fd,src,s->width*2);
+ }
+ } else if (s->halfw == 1) {
+#ifdef HAVE_MMX
+ mmx_t rounder;
+ mmx_t masker;
+ rounder.uw[0]=1;
+ rounder.uw[1]=1;
+ rounder.uw[2]=1;
+ rounder.uw[3]=1;
+ masker.ub[0]=0xff;
+ masker.ub[1]=0;
+ masker.ub[2]=0xff;
+ masker.ub[3]=0;
+ masker.ub[4]=0xff;
+ masker.ub[5]=0;
+ masker.ub[6]=0xff;
+ masker.ub[7]=0;
+ pxor_r2r(mm7,mm7);
+ movq_m2r(rounder,mm6);
+#endif
+ while (read(s->fd,src,s->width*4) < 0) {
+ usleep(100);
+ }
+ ptrend = &src[s->width*4];
+ for (h = 0; h < s->height-2; h+=2) {
+ for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=8, cb+=4, cr+=4) {
+ LINE_WITHUV_AVG
+ }
+ read(s->fd,src,s->width*4);
+#ifdef HAVE_MMX
+ movq_m2r(masker,mm5);
+#endif
+ for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=8) {
+ LINE_NOUV_AVG
+ }
+ read(s->fd,src,s->width*4);
+ }
+ /*
+ * Do last two lines
+ */
+ for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=8, cb+=4, cr+=4) {
+ LINE_WITHUV_AVG
+ }
+ read(s->fd,src,s->width*4);
+#ifdef HAVE_MMX
+ movq_m2r(masker,mm5);
+#endif
+ for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=8) {
+ LINE_NOUV_AVG
+ }
+ /* drop second field */
+ while (read(s->fd,src,s->width*4) < 0) {
+ usleep(100);
+ }
+ for (h = 0; h < s->height - 1; h++) {
+ read(s->fd,src,s->width*4);
+ }
+ } else {
+ UINT8 *lum_m1, *lum_m2, *lum_m3, *lum_m4;
+#ifdef HAVE_MMX
+ mmx_t rounder;
+ rounder.uw[0]=4;
+ rounder.uw[1]=4;
+ rounder.uw[2]=4;
+ rounder.uw[3]=4;
+ movq_m2r(rounder,mm6);
+ pxor_r2r(mm7,mm7);
+#else
+ UINT8 *cm = cropTbl + MAX_NEG_CROP;
+#endif
+
+ /* read two fields and deinterlace them */
+ while (read(s->fd,src,s->width*2) < 0) {
+ usleep(100);
+ }
+ for (h = 0; h < (s->height/2)-2; h+=2) {
+ for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) {
+ LINE_WITH_UV
+ }
+ read(s->fd,src,s->width*2);
+ /* skip a luminance line - will be filled in later */
+ lum += s->width;
+ for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) {
+ LINE_WITH_UV
+ }
+ /* skip a luminance line - will be filled in later */
+ lum += s->width;
+ read(s->fd,src,s->width*2);
+ }
+ /*
+ * Do last two lines
+ */
+ for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) {
+ LINE_WITH_UV
+ }
+ /* skip a luminance line - will be filled in later */
+ lum += s->width;
+ read(s->fd,src,s->width*2);
+ for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) {
+ LINE_WITH_UV
+ }
+ /*
+ *
+ * SECOND FIELD
+ *
+ */
+ lum=&data[s->width];
+ while (read(s->fd,src,s->width*2) < 0) {
+ usleep(10);
+ }
+ /* First (and last) two lines not interlaced */
+ for (h = 0; h < 2; h++) {
+ for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16) {
+ LINE_NO_UV
+ }
+ read(s->fd,src,s->width*2);
+ /* skip a luminance line */
+ lum += s->width;
+ }
+ lum_m1=&lum[-s->width];
+ lum_m2=&lum_m1[-s->width];
+ lum_m3=&lum_m2[-s->width];
+ memmove(s->lum_m4_mem,&lum_m3[-s->width],s->width);
+ for (; h < (s->height/2)-1; h++) {
+ lum_m4=s->lum_m4_mem;
+ for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16,lum_m1+=16,lum_m2+=16,lum_m3+=16,lum_m4+=16) {
+ LINE_NO_UV
+
+ DEINT_LINE_LUM(0)
+ DEINT_LINE_LUM(4)
+ DEINT_LINE_LUM(8)
+ DEINT_LINE_LUM(12)
+ }
+ read(s->fd,src,s->width*2);
+ /* skip a luminance line */
+ lum += s->width;
+ lum_m1 += s->width;
+ lum_m2 += s->width;
+ lum_m3 += s->width;
+ // lum_m4 += s->width;
+ }
+ /*
+ * Do last line
+ */
+ lum_m4=s->lum_m4_mem;
+ for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, lum_m1+=16, lum_m2+=16, lum_m3+=16, lum_m4+=16) {
+ LINE_NO_UV
+
+ DEINT_LINE_LUM(0)
+ DEINT_LINE_LUM(4)
+ DEINT_LINE_LUM(8)
+ DEINT_LINE_LUM(12)
+ }
+ }
+#ifdef HAVE_MMX
+ emms();
+#endif
+ return s->frame_size;
+}
+
+static int aiw_close(VideoData *s)
+{
+ av_freep(&s->lum_m4_mem);
+ av_freep(&s->src_mem);
+ return 0;
+}
+
+int video_grab_init(void)
+{
+ av_register_input_format(&video_grab_device_format);
+ return 0;
+}
diff --git a/libavformat/http.c b/libavformat/http.c
new file mode 100644
index 0000000000..7271a6da81
--- /dev/null
+++ b/libavformat/http.c
@@ -0,0 +1,290 @@
+/*
+ * HTTP protocol for ffmpeg client
+ * Copyright (c) 2000, 2001 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"
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#ifndef __BEOS__
+# include <arpa/inet.h>
+#else
+# include "barpainet.h"
+#endif
+#include <netdb.h>
+
+
+/* XXX: POST protocol is not completly implemented because ffmpeg use
+ only a subset of it */
+
+//#define DEBUG
+
+/* used for protocol handling */
+#define BUFFER_SIZE 1024
+#define URL_SIZE 4096
+
+typedef struct {
+ URLContext *hd;
+ unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
+ int line_count;
+ int http_code;
+ char location[URL_SIZE];
+} HTTPContext;
+
+static int http_connect(URLContext *h, const char *path, const char *hoststr);
+static int http_write(URLContext *h, UINT8 *buf, int size);
+
+
+/* return non zero if error */
+static int http_open(URLContext *h, const char *uri, int flags)
+{
+ const char *path, *proxy_path;
+ char hostname[1024], hoststr[1024];
+ char path1[1024];
+ char buf[1024];
+ int port, use_proxy, err;
+ HTTPContext *s;
+ URLContext *hd = NULL;
+
+ h->is_streamed = 1;
+
+ s = av_malloc(sizeof(HTTPContext));
+ if (!s) {
+ return -ENOMEM;
+ }
+ h->priv_data = s;
+
+ proxy_path = getenv("http_proxy");
+ use_proxy = (proxy_path != NULL) && !getenv("no_proxy") &&
+ strstart(proxy_path, "http://", NULL);
+
+ /* fill the dest addr */
+ redo:
+ /* needed in any case to build the host string */
+ url_split(NULL, 0, hostname, sizeof(hostname), &port,
+ path1, sizeof(path1), uri);
+ if (port > 0) {
+ snprintf(hoststr, sizeof(hoststr), "%s:%d", hostname, port);
+ } else {
+ pstrcpy(hoststr, sizeof(hoststr), hostname);
+ }
+
+ if (use_proxy) {
+ url_split(NULL, 0, hostname, sizeof(hostname), &port,
+ NULL, 0, proxy_path);
+ path = uri;
+ } else {
+ if (path1[0] == '\0')
+ path = "/";
+ else
+ path = path1;
+ }
+ if (port < 0)
+ port = 80;
+
+ snprintf(buf, sizeof(buf), "tcp://%s:%d", hostname, port);
+ err = url_open(&hd, buf, URL_RDWR);
+ if (err < 0)
+ goto fail;
+
+ s->hd = hd;
+ if (http_connect(h, path, hoststr) < 0)
+ goto fail;
+ if (s->http_code == 303 && s->location[0] != '\0') {
+ /* url moved, get next */
+ uri = s->location;
+ url_close(hd);
+ goto redo;
+ }
+ return 0;
+ fail:
+ if (hd)
+ url_close(hd);
+ av_free(s);
+ return -EIO;
+}
+
+static int http_getc(HTTPContext *s)
+{
+ int len;
+ if (s->buf_ptr >= s->buf_end) {
+ len = url_read(s->hd, s->buffer, BUFFER_SIZE);
+ if (len < 0) {
+ return -EIO;
+ } else if (len == 0) {
+ return -1;
+ } else {
+ s->buf_ptr = s->buffer;
+ s->buf_end = s->buffer + len;
+ }
+ }
+ return *s->buf_ptr++;
+}
+
+static int process_line(HTTPContext *s, char *line, int line_count)
+{
+ char *tag, *p;
+
+ /* end of header */
+ if (line[0] == '\0')
+ return 0;
+
+ p = line;
+ if (line_count == 0) {
+ while (!isspace(*p) && *p != '\0')
+ p++;
+ while (isspace(*p))
+ p++;
+ s->http_code = strtol(p, NULL, 10);
+#ifdef DEBUG
+ printf("http_code=%d\n", s->http_code);
+#endif
+ } else {
+ while (*p != '\0' && *p != ':')
+ p++;
+ if (*p != ':')
+ return 1;
+
+ *p = '\0';
+ tag = line;
+ p++;
+ while (isspace(*p))
+ p++;
+ if (!strcmp(tag, "Location")) {
+ strcpy(s->location, p);
+ }
+ }
+ return 1;
+}
+
+static int http_connect(URLContext *h, const char *path, const char *hoststr)
+{
+ HTTPContext *s = h->priv_data;
+ int post, err, ch;
+ char line[1024], *q;
+
+
+ /* send http header */
+ post = h->flags & URL_WRONLY;
+
+ snprintf(s->buffer, sizeof(s->buffer),
+ "%s %s HTTP/1.0\n"
+ "User-Agent: FFmpeg %s\n"
+ "Accept: */*\n"
+ "Host: %s\n"
+ "\n",
+ post ? "POST" : "GET",
+ path,
+ FFMPEG_VERSION,
+ hoststr);
+
+ if (http_write(h, s->buffer, strlen(s->buffer)) < 0)
+ return -EIO;
+
+ /* init input buffer */
+ s->buf_ptr = s->buffer;
+ s->buf_end = s->buffer;
+ s->line_count = 0;
+ s->location[0] = '\0';
+ if (post) {
+ sleep(1);
+ return 0;
+ }
+
+ /* wait for header */
+ q = line;
+ for(;;) {
+ ch = http_getc(s);
+ if (ch < 0)
+ return -EIO;
+ if (ch == '\n') {
+ /* process line */
+ if (q > line && q[-1] == '\r')
+ q--;
+ *q = '\0';
+#ifdef DEBUG
+ printf("header='%s'\n", line);
+#endif
+ err = process_line(s, line, s->line_count);
+ if (err < 0)
+ return err;
+ if (err == 0)
+ return 0;
+ s->line_count++;
+ q = line;
+ } else {
+ if ((q - line) < sizeof(line) - 1)
+ *q++ = ch;
+ }
+ }
+}
+
+
+static int http_read(URLContext *h, UINT8 *buf, int size)
+{
+ HTTPContext *s = h->priv_data;
+ int size1, len;
+
+ size1 = size;
+ while (size > 0) {
+ /* read bytes from input buffer first */
+ len = s->buf_end - s->buf_ptr;
+ if (len > 0) {
+ if (len > size)
+ len = size;
+ memcpy(buf, s->buf_ptr, len);
+ s->buf_ptr += len;
+ } else {
+ len = url_read (s->hd, buf, size);
+ if (len < 0) {
+ return len;
+ } else if (len == 0) {
+ break;
+ }
+ }
+ size -= len;
+ buf += len;
+ }
+ return size1 - size;
+}
+
+/* used only when posting data */
+static int http_write(URLContext *h, UINT8 *buf, int size)
+{
+ HTTPContext *s = h->priv_data;
+ return url_write(s->hd, buf, size);
+}
+
+static int http_close(URLContext *h)
+{
+ HTTPContext *s = h->priv_data;
+ url_close(s->hd);
+ av_free(s);
+ return 0;
+}
+
+URLProtocol http_protocol = {
+ "http",
+ http_open,
+ http_read,
+ http_write,
+ NULL, /* seek */
+ http_close,
+};
+
diff --git a/libavformat/img.c b/libavformat/img.c
new file mode 100644
index 0000000000..305cbb08f2
--- /dev/null
+++ b/libavformat/img.c
@@ -0,0 +1,945 @@
+/*
+ * Image format
+ * 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"
+
+extern AVInputFormat pgm_iformat;
+extern AVOutputFormat pgm_oformat;
+extern AVInputFormat pgmyuv_iformat;
+extern AVOutputFormat pgmyuv_oformat;
+extern AVInputFormat ppm_iformat;
+extern AVOutputFormat ppm_oformat;
+extern AVInputFormat imgyuv_iformat;
+extern AVOutputFormat imgyuv_oformat;
+extern AVInputFormat pgmpipe_iformat;
+extern AVOutputFormat pgmpipe_oformat;
+extern AVInputFormat pgmyuvpipe_iformat;
+extern AVOutputFormat pgmyuvpipe_oformat;
+extern AVInputFormat ppmpipe_iformat;
+extern AVOutputFormat ppmpipe_oformat;
+extern AVOutputFormat yuv4mpegpipe_oformat;
+
+#define IMGFMT_YUV 1
+#define IMGFMT_PGMYUV 2
+#define IMGFMT_PGM 3
+#define IMGFMT_PPM 4
+#define IMGFMT_YUV4MPEG 5
+
+#define Y4M_MAGIC "YUV4MPEG2"
+#define Y4M_FRAME_MAGIC "FRAME"
+#define Y4M_LINE_MAX 256
+
+typedef struct {
+ int width;
+ int height;
+ int img_number;
+ int img_size;
+ int img_fmt;
+ int is_pipe;
+ int header_written;
+ char path[1024];
+} VideoData;
+
+static inline int pnm_space(int c)
+{
+ return (c==' ' || c=='\n' || c=='\r' || c=='\t');
+}
+
+static void pnm_get(ByteIOContext *f, char *str, int buf_size)
+{
+ char *s;
+ int c;
+
+ do {
+ c=get_byte(f);
+ if (c=='#') {
+ do {
+ c=get_byte(f);
+ } while (c!='\n');
+ c=get_byte(f);
+ }
+ } while (pnm_space(c));
+
+ s=str;
+ do {
+ if (url_feof(f))
+ break;
+ if ((s - str) < buf_size - 1)
+ *s++=c;
+ c=get_byte(f);
+ } while (!pnm_space(c));
+ *s = '\0';
+}
+
+static int pgm_read(VideoData *s, ByteIOContext *f, UINT8 *buf, int size, int is_yuv)
+{
+ int width, height, i;
+ char buf1[32];
+ UINT8 *picture[3];
+
+ width = s->width;
+ height = s->height;
+
+ pnm_get(f, buf1, sizeof(buf1));
+ if (strcmp(buf1, "P5")) {
+ return -EIO;
+ }
+ pnm_get(f, buf1, sizeof(buf1));
+ pnm_get(f, buf1, sizeof(buf1));
+ pnm_get(f, buf1, sizeof(buf1));
+
+ picture[0] = buf;
+ picture[1] = buf + width * height;
+ picture[2] = buf + width * height + (width * height / 4);
+ get_buffer(f, picture[0], width * height);
+
+ height>>=1;
+ width>>=1;
+ if (is_yuv) {
+ for(i=0;i<height;i++) {
+ get_buffer(f, picture[1] + i * width, width);
+ get_buffer(f, picture[2] + i * width, width);
+ }
+ } else {
+ for(i=0;i<height;i++) {
+ memset(picture[1] + i * width, 128, width);
+ memset(picture[2] + i * width, 128, width);
+ }
+ }
+ return 0;
+}
+
+static int ppm_read(VideoData *s, ByteIOContext *f, UINT8 *buf, int size)
+{
+ int width, height;
+ char buf1[32];
+ UINT8 *picture[3];
+
+ width = s->width;
+ height = s->height;
+
+ pnm_get(f, buf1, sizeof(buf1));
+ if (strcmp(buf1, "P6")) {
+ return -EIO;
+ }
+
+ pnm_get(f, buf1, sizeof(buf1));
+ pnm_get(f, buf1, sizeof(buf1));
+ pnm_get(f, buf1, sizeof(buf1));
+
+ picture[0] = buf;
+ get_buffer(f, picture[0], width * height*3);
+
+ return 0;
+
+}
+
+static int yuv_read(VideoData *s, const char *filename, UINT8 *buf, int size1)
+{
+ ByteIOContext pb1, *pb = &pb1;
+ char fname[1024], *p;
+ int size;
+
+ size = s->width * s->height;
+
+ strcpy(fname, filename);
+ p = strrchr(fname, '.');
+ if (!p || p[1] != 'Y')
+ return -EIO;
+
+ if (url_fopen(pb, fname, URL_RDONLY) < 0)
+ return -EIO;
+
+ get_buffer(pb, buf, size);
+ url_fclose(pb);
+
+ p[1] = 'U';
+ if (url_fopen(pb, fname, URL_RDONLY) < 0)
+ return -EIO;
+
+ get_buffer(pb, buf + size, size / 4);
+ url_fclose(pb);
+
+ p[1] = 'V';
+ if (url_fopen(pb, fname, URL_RDONLY) < 0)
+ return -EIO;
+
+ get_buffer(pb, buf + size + (size / 4), size / 4);
+ url_fclose(pb);
+ return 0;
+}
+
+static int img_read_packet(AVFormatContext *s1, AVPacket *pkt)
+{
+ VideoData *s = s1->priv_data;
+ char filename[1024];
+ int ret;
+ ByteIOContext f1, *f;
+
+/*
+ This if-statement destroys pipes - I do not see why it is necessary
+ if (get_frame_filename(filename, sizeof(filename),
+ s->path, s->img_number) < 0)
+ return -EIO;
+*/
+ get_frame_filename(filename, sizeof(filename),
+ s->path, s->img_number);
+ if (!s->is_pipe) {
+ f = &f1;
+ if (url_fopen(f, filename, URL_RDONLY) < 0)
+ return -EIO;
+ } else {
+ f = &s1->pb;
+ if (url_feof(f))
+ return -EIO;
+ }
+
+ av_new_packet(pkt, s->img_size);
+ pkt->stream_index = 0;
+
+ switch(s->img_fmt) {
+ case IMGFMT_PGMYUV:
+ ret = pgm_read(s, f, pkt->data, pkt->size, 1);
+ break;
+ case IMGFMT_PGM:
+ ret = pgm_read(s, f, pkt->data, pkt->size, 0);
+ break;
+ case IMGFMT_YUV:
+ ret = yuv_read(s, filename, pkt->data, pkt->size);
+ break;
+ case IMGFMT_PPM:
+ ret = ppm_read(s, f, pkt->data, pkt->size);
+ break;
+ default:
+ return -EIO;
+ }
+
+ if (!s->is_pipe) {
+ url_fclose(f);
+ }
+
+ if (ret < 0) {
+ av_free_packet(pkt);
+ return -EIO; /* signal EOF */
+ } else {
+ pkt->pts = ((INT64)s->img_number * s1->pts_den * FRAME_RATE_BASE) / (s1->streams[0]->codec.frame_rate * s1->pts_num);
+ s->img_number++;
+ return 0;
+ }
+}
+
+static int sizes[][2] = {
+ { 640, 480 },
+ { 720, 480 },
+ { 720, 576 },
+ { 352, 288 },
+ { 352, 240 },
+ { 160, 128 },
+ { 512, 384 },
+ { 640, 352 },
+ { 640, 240 },
+};
+
+static int infer_size(int *width_ptr, int *height_ptr, int size)
+{
+ int i;
+
+ for(i=0;i<sizeof(sizes)/sizeof(sizes[0]);i++) {
+ if ((sizes[i][0] * sizes[i][1]) == size) {
+ *width_ptr = sizes[i][0];
+ *height_ptr = sizes[i][1];
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int img_read_header(AVFormatContext *s1, AVFormatParameters *ap)
+{
+ VideoData *s = s1->priv_data;
+ int i, h;
+ char buf[1024];
+ char buf1[32];
+ ByteIOContext pb1, *f = &pb1;
+ AVStream *st;
+
+ st = av_new_stream(s1, 0);
+ if (!st) {
+ av_free(s);
+ return -ENOMEM;
+ }
+
+ strcpy(s->path, s1->filename);
+ s->img_number = 0;
+
+ /* find format */
+ if (s1->iformat->flags & AVFMT_NOFILE)
+ s->is_pipe = 0;
+ else
+ s->is_pipe = 1;
+
+ if (s1->iformat == &pgmyuvpipe_iformat ||
+ s1->iformat == &pgmyuv_iformat)
+ s->img_fmt = IMGFMT_PGMYUV;
+ else if (s1->iformat == &pgmpipe_iformat ||
+ s1->iformat == &pgm_iformat)
+ s->img_fmt = IMGFMT_PGM;
+ else if (s1->iformat == &imgyuv_iformat)
+ s->img_fmt = IMGFMT_YUV;
+ else if (s1->iformat == &ppmpipe_iformat ||
+ s1->iformat == &ppm_iformat)
+ s->img_fmt = IMGFMT_PPM;
+ else
+ goto fail;
+
+ if (!s->is_pipe) {
+ /* try to find the first image */
+ for(i=0;i<5;i++) {
+ if (get_frame_filename(buf, sizeof(buf), s->path, s->img_number) < 0)
+ goto fail;
+ if (url_fopen(f, buf, URL_RDONLY) >= 0)
+ break;
+ s->img_number++;
+ }
+ if (i == 5)
+ goto fail;
+ } else {
+ f = &s1->pb;
+ }
+
+ /* find the image size */
+ /* XXX: use generic file format guessing, as mpeg */
+ switch(s->img_fmt) {
+ case IMGFMT_PGM:
+ case IMGFMT_PGMYUV:
+ case IMGFMT_PPM:
+ pnm_get(f, buf1, sizeof(buf1));
+ pnm_get(f, buf1, sizeof(buf1));
+ s->width = atoi(buf1);
+ pnm_get(f, buf1, sizeof(buf1));
+ h = atoi(buf1);
+ if (s->img_fmt == IMGFMT_PGMYUV)
+ h = (h * 2) / 3;
+ s->height = h;
+ if (s->width <= 0 ||
+ s->height <= 0 ||
+ (s->width % 2) != 0 ||
+ (s->height % 2) != 0) {
+ goto fail1;
+ }
+ break;
+ case IMGFMT_YUV:
+ /* infer size by using the file size. */
+ {
+ int img_size;
+ URLContext *h;
+
+ /* XXX: hack hack */
+ h = url_fileno(f);
+ img_size = url_seek(h, 0, SEEK_END);
+ if (infer_size(&s->width, &s->height, img_size) < 0) {
+ goto fail1;
+ }
+ }
+ break;
+ }
+
+
+ if (!s->is_pipe) {
+ url_fclose(f);
+ } else {
+ url_fseek(f, 0, SEEK_SET);
+ }
+
+
+ st->codec.codec_type = CODEC_TYPE_VIDEO;
+ st->codec.codec_id = CODEC_ID_RAWVIDEO;
+ st->codec.width = s->width;
+ st->codec.height = s->height;
+ if (s->img_fmt == IMGFMT_PPM) {
+ st->codec.pix_fmt = PIX_FMT_RGB24;
+ s->img_size = (s->width * s->height * 3);
+ } else {
+ st->codec.pix_fmt = PIX_FMT_YUV420P;
+ s->img_size = (s->width * s->height * 3) / 2;
+ }
+ if (!ap || !ap->frame_rate)
+ st->codec.frame_rate = 25 * FRAME_RATE_BASE;
+ else
+ st->codec.frame_rate = ap->frame_rate;
+
+ return 0;
+ fail1:
+ if (!s->is_pipe)
+ url_fclose(f);
+ fail:
+ av_free(s);
+ return -EIO;
+}
+
+static int img_read_close(AVFormatContext *s1)
+{
+ return 0;
+}
+
+/******************************************************/
+/* image output */
+
+static int pgm_save(AVPicture *picture, int width, int height, ByteIOContext *pb, int is_yuv)
+{
+ int i, h;
+ char buf[100];
+ UINT8 *ptr, *ptr1, *ptr2;
+
+ h = height;
+ if (is_yuv)
+ h = (height * 3) / 2;
+ snprintf(buf, sizeof(buf),
+ "P5\n%d %d\n%d\n",
+ width, h, 255);
+ put_buffer(pb, buf, strlen(buf));
+
+ ptr = picture->data[0];
+ for(i=0;i<height;i++) {
+ put_buffer(pb, ptr, width);
+ ptr += picture->linesize[0];
+ }
+
+ if (is_yuv) {
+ height >>= 1;
+ width >>= 1;
+ ptr1 = picture->data[1];
+ ptr2 = picture->data[2];
+ for(i=0;i<height;i++) {
+ put_buffer(pb, ptr1, width);
+ put_buffer(pb, ptr2, width);
+ ptr1 += picture->linesize[1];
+ ptr2 += picture->linesize[2];
+ }
+ }
+ put_flush_packet(pb);
+ return 0;
+}
+
+static int ppm_save(AVPicture *picture, int width, int height, ByteIOContext *pb)
+{
+ int i;
+ char buf[100];
+ UINT8 *ptr;
+
+ snprintf(buf, sizeof(buf),
+ "P6\n%d %d\n%d\n",
+ width, height, 255);
+ put_buffer(pb, buf, strlen(buf));
+
+ ptr = picture->data[0];
+ for(i=0;i<height;i++) {
+ put_buffer(pb, ptr, width * 3);
+ ptr += picture->linesize[0];
+ }
+
+ put_flush_packet(pb);
+ return 0;
+}
+
+static int yuv_save(AVPicture *picture, int width, int height, const char *filename)
+{
+ ByteIOContext pb1, *pb = &pb1;
+ char fname[1024], *p;
+ int i, j;
+ UINT8 *ptr;
+ static char *ext = "YUV";
+
+ strcpy(fname, filename);
+ p = strrchr(fname, '.');
+ if (!p || p[1] != 'Y')
+ return -EIO;
+
+ for(i=0;i<3;i++) {
+ if (i == 1) {
+ width >>= 1;
+ height >>= 1;
+ }
+
+ p[1] = ext[i];
+ if (url_fopen(pb, fname, URL_WRONLY) < 0)
+ return -EIO;
+
+ ptr = picture->data[i];
+ for(j=0;j<height;j++) {
+ put_buffer(pb, ptr, width);
+ ptr += picture->linesize[i];
+ }
+ put_flush_packet(pb);
+ url_fclose(pb);
+ }
+ return 0;
+}
+
+static int yuv4mpeg_save(AVPicture *picture, int width, int height, ByteIOContext *pb, int need_stream_header,
+ int is_yuv, int raten, int rated, int aspectn, int aspectd)
+{
+ int i, n, m;
+ char buf[Y4M_LINE_MAX+1], buf1[20];
+ UINT8 *ptr, *ptr1, *ptr2;
+
+ /* construct stream header, if this is the first frame */
+ if(need_stream_header) {
+ n = snprintf(buf, sizeof(buf), "%s W%d H%d F%d:%d I%s A%d:%d\n",
+ Y4M_MAGIC,
+ width,
+ height,
+ raten, rated,
+ "p", /* ffmpeg seems to only output progressive video */
+ aspectn, aspectd);
+ if (n < 0) {
+ fprintf(stderr, "Error. YUV4MPEG stream header write failed.\n");
+ } else {
+ fprintf(stderr, "YUV4MPEG stream header written. FPS is %d\n", raten);
+ put_buffer(pb, buf, strlen(buf));
+ }
+ }
+
+ /* construct frame header */
+ m = snprintf(buf1, sizeof(buf1), "%s \n", Y4M_FRAME_MAGIC);
+ if (m < 0) {
+ fprintf(stderr, "Error. YUV4MPEG frame header write failed.\n");
+ } else {
+ /* fprintf(stderr, "YUV4MPEG frame header written.\n"); */
+ put_buffer(pb, buf1, strlen(buf1));
+ }
+
+ ptr = picture->data[0];
+ for(i=0;i<height;i++) {
+ put_buffer(pb, ptr, width);
+ ptr += picture->linesize[0];
+ }
+
+ if (is_yuv) {
+ height >>= 1;
+ width >>= 1;
+ ptr1 = picture->data[1];
+ ptr2 = picture->data[2];
+ for(i=0;i<height;i++) { /* Cb */
+ put_buffer(pb, ptr1, width);
+ ptr1 += picture->linesize[1];
+ }
+ for(i=0;i<height;i++) { /* Cr */
+ put_buffer(pb, ptr2, width);
+ ptr2 += picture->linesize[2];
+ }
+ }
+ put_flush_packet(pb);
+ return 0;
+}
+
+static int img_write_header(AVFormatContext *s)
+{
+ VideoData *img = s->priv_data;
+
+ img->img_number = 1;
+ strcpy(img->path, s->filename);
+
+ /* find format */
+ if (s->oformat->flags & AVFMT_NOFILE)
+ img->is_pipe = 0;
+ else
+ img->is_pipe = 1;
+
+ if (s->oformat == &pgmyuvpipe_oformat ||
+ s->oformat == &pgmyuv_oformat) {
+ img->img_fmt = IMGFMT_PGMYUV;
+ } else if (s->oformat == &pgmpipe_oformat ||
+ s->oformat == &pgm_oformat) {
+ img->img_fmt = IMGFMT_PGM;
+ } else if (s->oformat == &imgyuv_oformat) {
+ img->img_fmt = IMGFMT_YUV;
+ } else if (s->oformat == &ppmpipe_oformat ||
+ s->oformat == &ppm_oformat) {
+ img->img_fmt = IMGFMT_PPM;
+ } else if (s->oformat == &yuv4mpegpipe_oformat) {
+ img->img_fmt = IMGFMT_YUV4MPEG;
+ img->header_written = 0;
+ } else {
+ goto fail;
+ }
+ return 0;
+ fail:
+ av_free(img);
+ return -EIO;
+}
+
+static int img_write_packet(AVFormatContext *s, int stream_index,
+ UINT8 *buf, int size, int force_pts)
+{
+ VideoData *img = s->priv_data;
+ AVStream *st = s->streams[stream_index];
+ ByteIOContext pb1, *pb;
+ AVPicture picture;
+ int width, height, need_stream_header, ret, size1, raten, rated, aspectn, aspectd, fps, fps1;
+ char filename[1024];
+
+ width = st->codec.width;
+ height = st->codec.height;
+
+ if (img->img_number == 1) {
+ need_stream_header = 1;
+ } else {
+ need_stream_header = 0;
+ }
+
+ fps = st->codec.frame_rate;
+ fps1 = (((float)fps / FRAME_RATE_BASE) * 1000);
+
+ /* Sorry about this messy code, but mpeg2enc is very picky about
+ * the framerates it accepts. */
+ switch(fps1) {
+ case 23976:
+ raten = 24000; /* turn the framerate into a ratio */
+ rated = 1001;
+ break;
+ case 29970:
+ raten = 30000;
+ rated = 1001;
+ break;
+ case 25000:
+ raten = 25;
+ rated = 1;
+ break;
+ case 30000:
+ raten = 30;
+ rated = 1;
+ break;
+ case 24000:
+ raten = 24;
+ rated = 1;
+ break;
+ case 50000:
+ raten = 50;
+ rated = 1;
+ break;
+ case 59940:
+ raten = 60000;
+ rated = 1001;
+ break;
+ case 60000:
+ raten = 60;
+ rated = 1;
+ break;
+ default:
+ raten = fps1; /* this setting should work, but often doesn't */
+ rated = 1000;
+ break;
+ }
+
+ aspectn = 1;
+ aspectd = 1; /* ffmpeg always uses a 1:1 aspect ratio */
+
+ switch(st->codec.pix_fmt) {
+ case PIX_FMT_YUV420P:
+ size1 = (width * height * 3) / 2;
+ if (size != size1)
+ return -EIO;
+
+ picture.data[0] = buf;
+ picture.data[1] = picture.data[0] + width * height;
+ picture.data[2] = picture.data[1] + (width * height) / 4;
+ picture.linesize[0] = width;
+ picture.linesize[1] = width >> 1;
+ picture.linesize[2] = width >> 1;
+ break;
+ case PIX_FMT_RGB24:
+ size1 = (width * height * 3);
+ if (size != size1)
+ return -EIO;
+ picture.data[0] = buf;
+ picture.linesize[0] = width * 3;
+ break;
+ default:
+ return -EIO;
+ }
+
+/*
+ This if-statement destroys pipes - I do not see why it is necessary
+ if (get_frame_filename(filename, sizeof(filename),
+ img->path, img->img_number) < 0)
+ return -EIO;
+*/
+ get_frame_filename(filename, sizeof(filename),
+ img->path, img->img_number);
+ if (!img->is_pipe) {
+ pb = &pb1;
+ if (url_fopen(pb, filename, URL_WRONLY) < 0)
+ return -EIO;
+ } else {
+ pb = &s->pb;
+ }
+ switch(img->img_fmt) {
+ case IMGFMT_PGMYUV:
+ ret = pgm_save(&picture, width, height, pb, 1);
+ break;
+ case IMGFMT_PGM:
+ ret = pgm_save(&picture, width, height, pb, 0);
+ break;
+ case IMGFMT_YUV:
+ ret = yuv_save(&picture, width, height, filename);
+ break;
+ case IMGFMT_PPM:
+ ret = ppm_save(&picture, width, height, pb);
+ break;
+ case IMGFMT_YUV4MPEG:
+ ret = yuv4mpeg_save(&picture, width, height, pb,
+ need_stream_header, 1, raten, rated, aspectn, aspectd);
+ break;
+ }
+ if (!img->is_pipe) {
+ url_fclose(pb);
+ }
+
+ img->img_number++;
+ return 0;
+}
+
+static int img_write_trailer(AVFormatContext *s)
+{
+ return 0;
+}
+
+static AVInputFormat pgm_iformat = {
+ "pgm",
+ "pgm image format",
+ sizeof(VideoData),
+ NULL,
+ img_read_header,
+ img_read_packet,
+ img_read_close,
+ NULL,
+ AVFMT_NOFILE | AVFMT_NEEDNUMBER,
+ .extensions = "pgm",
+};
+
+static AVOutputFormat pgm_oformat = {
+ "pgm",
+ "pgm image format",
+ "",
+ "pgm",
+ sizeof(VideoData),
+ CODEC_ID_NONE,
+ CODEC_ID_RAWVIDEO,
+ img_write_header,
+ img_write_packet,
+ img_write_trailer,
+ AVFMT_NOFILE | AVFMT_NEEDNUMBER,
+};
+
+static AVInputFormat pgmyuv_iformat = {
+ "pgmyuv",
+ "pgm with YUV content image format",
+ sizeof(VideoData),
+ NULL, /* no probe */
+ img_read_header,
+ img_read_packet,
+ img_read_close,
+ NULL,
+ AVFMT_NOFILE | AVFMT_NEEDNUMBER,
+};
+
+static AVOutputFormat pgmyuv_oformat = {
+ "pgmyuv",
+ "pgm with YUV content image format",
+ "",
+ "pgm",
+ sizeof(VideoData),
+ CODEC_ID_NONE,
+ CODEC_ID_RAWVIDEO,
+ img_write_header,
+ img_write_packet,
+ img_write_trailer,
+ AVFMT_NOFILE | AVFMT_NEEDNUMBER,
+};
+
+static AVInputFormat ppm_iformat = {
+ "ppm",
+ "ppm image format",
+ sizeof(VideoData),
+ NULL,
+ img_read_header,
+ img_read_packet,
+ img_read_close,
+ NULL,
+ AVFMT_NOFILE | AVFMT_NEEDNUMBER | AVFMT_RGB24,
+ .extensions = "ppm",
+};
+
+static AVOutputFormat ppm_oformat = {
+ "ppm",
+ "ppm image format",
+ "",
+ "ppm",
+ sizeof(VideoData),
+ CODEC_ID_NONE,
+ CODEC_ID_RAWVIDEO,
+ img_write_header,
+ img_write_packet,
+ img_write_trailer,
+ AVFMT_NOFILE | AVFMT_NEEDNUMBER | AVFMT_RGB24,
+};
+
+static AVInputFormat imgyuv_iformat = {
+ ".Y.U.V",
+ ".Y.U.V format",
+ sizeof(VideoData),
+ NULL,
+ img_read_header,
+ img_read_packet,
+ img_read_close,
+ NULL,
+ AVFMT_NOFILE | AVFMT_NEEDNUMBER,
+ .extensions = "Y",
+};
+
+static AVOutputFormat imgyuv_oformat = {
+ ".Y.U.V",
+ ".Y.U.V format",
+ "",
+ "Y",
+ sizeof(VideoData),
+ CODEC_ID_NONE,
+ CODEC_ID_RAWVIDEO,
+ img_write_header,
+ img_write_packet,
+ img_write_trailer,
+ AVFMT_NOFILE | AVFMT_NEEDNUMBER,
+};
+
+static AVInputFormat pgmpipe_iformat = {
+ "pgmpipe",
+ "PGM pipe format",
+ sizeof(VideoData),
+ NULL, /* no probe */
+ img_read_header,
+ img_read_packet,
+ img_read_close,
+ NULL,
+};
+
+static AVOutputFormat pgmpipe_oformat = {
+ "pgmpipe",
+ "PGM pipe format",
+ "",
+ "pgm",
+ sizeof(VideoData),
+ CODEC_ID_NONE,
+ CODEC_ID_RAWVIDEO,
+ img_write_header,
+ img_write_packet,
+ img_write_trailer,
+};
+
+static AVInputFormat pgmyuvpipe_iformat = {
+ "pgmyuvpipe",
+ "PGM YUV pipe format",
+ sizeof(VideoData),
+ NULL, /* no probe */
+ img_read_header,
+ img_read_packet,
+ img_read_close,
+ NULL,
+};
+
+static AVOutputFormat pgmyuvpipe_oformat = {
+ "pgmyuvpipe",
+ "PGM YUV pipe format",
+ "",
+ "pgm",
+ sizeof(VideoData),
+ CODEC_ID_NONE,
+ CODEC_ID_RAWVIDEO,
+ img_write_header,
+ img_write_packet,
+ img_write_trailer,
+};
+
+static AVInputFormat ppmpipe_iformat = {
+ "ppmpipe",
+ "PPM pipe format",
+ sizeof(VideoData),
+ NULL, /* no probe */
+ img_read_header,
+ img_read_packet,
+ img_read_close,
+ NULL,
+ .flags = AVFMT_RGB24,
+};
+
+static AVOutputFormat ppmpipe_oformat = {
+ "ppmpipe",
+ "PPM pipe format",
+ "",
+ "ppm",
+ sizeof(VideoData),
+ CODEC_ID_NONE,
+ CODEC_ID_RAWVIDEO,
+ img_write_header,
+ img_write_packet,
+ img_write_trailer,
+ .flags = AVFMT_RGB24,
+};
+
+
+static AVOutputFormat yuv4mpegpipe_oformat = {
+ "yuv4mpegpipe",
+ "YUV4MPEG pipe format",
+ "",
+ "yuv4mpeg",
+ sizeof(VideoData),
+ CODEC_ID_NONE,
+ CODEC_ID_RAWVIDEO,
+ img_write_header,
+ img_write_packet,
+ img_write_trailer,
+};
+
+
+int img_init(void)
+{
+ av_register_input_format(&pgm_iformat);
+ av_register_output_format(&pgm_oformat);
+
+ av_register_input_format(&pgmyuv_iformat);
+ av_register_output_format(&pgmyuv_oformat);
+
+ av_register_input_format(&ppm_iformat);
+ av_register_output_format(&ppm_oformat);
+
+ av_register_input_format(&imgyuv_iformat);
+ av_register_output_format(&imgyuv_oformat);
+
+ av_register_input_format(&pgmpipe_iformat);
+ av_register_output_format(&pgmpipe_oformat);
+
+ av_register_input_format(&pgmyuvpipe_iformat);
+ av_register_output_format(&pgmyuvpipe_oformat);
+
+ av_register_input_format(&ppmpipe_iformat);
+ av_register_output_format(&ppmpipe_oformat);
+
+ av_register_output_format(&yuv4mpegpipe_oformat);
+
+ return 0;
+}
diff --git a/libavformat/jpeg.c b/libavformat/jpeg.c
new file mode 100644
index 0000000000..6a19db6d67
--- /dev/null
+++ b/libavformat/jpeg.c
@@ -0,0 +1,266 @@
+/*
+ * JPEG based formats
+ * Copyright (c) 2000, 2001 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"
+
+/* Multipart JPEG */
+
+#define BOUNDARY_TAG "ffserver"
+
+static int mpjpeg_write_header(AVFormatContext *s)
+{
+ UINT8 buf1[256];
+
+ snprintf(buf1, sizeof(buf1), "--%s\n", BOUNDARY_TAG);
+ put_buffer(&s->pb, buf1, strlen(buf1));
+ put_flush_packet(&s->pb);
+ return 0;
+}
+
+static int mpjpeg_write_packet(AVFormatContext *s, int stream_index,
+ UINT8 *buf, int size, int force_pts)
+{
+ UINT8 buf1[256];
+
+ snprintf(buf1, sizeof(buf1), "Content-type: image/jpeg\n\n");
+ put_buffer(&s->pb, buf1, strlen(buf1));
+ put_buffer(&s->pb, buf, size);
+
+ snprintf(buf1, sizeof(buf1), "\n--%s\n", BOUNDARY_TAG);
+ put_buffer(&s->pb, buf1, strlen(buf1));
+ put_flush_packet(&s->pb);
+ return 0;
+}
+
+static int mpjpeg_write_trailer(AVFormatContext *s)
+{
+ return 0;
+}
+
+static AVOutputFormat mpjpeg_format = {
+ "mpjpeg",
+ "Mime multipart JPEG format",
+ "multipart/x-mixed-replace;boundary=" BOUNDARY_TAG,
+ "mjpg",
+ 0,
+ CODEC_ID_NONE,
+ CODEC_ID_MJPEG,
+ mpjpeg_write_header,
+ mpjpeg_write_packet,
+ mpjpeg_write_trailer,
+};
+
+
+/*************************************/
+/* single frame JPEG */
+
+static int single_jpeg_write_header(AVFormatContext *s)
+{
+ return 0;
+}
+
+static int single_jpeg_write_packet(AVFormatContext *s, int stream_index,
+ UINT8 *buf, int size, int force_pts)
+{
+ put_buffer(&s->pb, buf, size);
+ put_flush_packet(&s->pb);
+ return 1; /* no more data can be sent */
+}
+
+static int single_jpeg_write_trailer(AVFormatContext *s)
+{
+ return 0;
+}
+
+static AVOutputFormat single_jpeg_format = {
+ "singlejpeg",
+ "single JPEG image",
+ "image/jpeg",
+ NULL, /* note: no extension to favorize jpeg multiple images match */
+ 0,
+ CODEC_ID_NONE,
+ CODEC_ID_MJPEG,
+ single_jpeg_write_header,
+ single_jpeg_write_packet,
+ single_jpeg_write_trailer,
+};
+
+/*************************************/
+/* multiple jpeg images */
+
+typedef struct JpegContext {
+ char path[1024];
+ int img_number;
+} JpegContext;
+
+static int jpeg_write_header(AVFormatContext *s1)
+{
+ JpegContext *s;
+
+ s = av_mallocz(sizeof(JpegContext));
+ if (!s)
+ return -1;
+ s1->priv_data = s;
+ pstrcpy(s->path, sizeof(s->path), s1->filename);
+ s->img_number = 1;
+ return 0;
+}
+
+static int jpeg_write_packet(AVFormatContext *s1, int stream_index,
+ UINT8 *buf, int size, int force_pts)
+{
+ JpegContext *s = s1->priv_data;
+ char filename[1024];
+ ByteIOContext f1, *pb = &f1;
+
+ if (get_frame_filename(filename, sizeof(filename),
+ s->path, s->img_number) < 0)
+ return -EIO;
+ if (url_fopen(pb, filename, URL_WRONLY) < 0)
+ return -EIO;
+
+ put_buffer(pb, buf, size);
+ put_flush_packet(pb);
+
+ url_fclose(pb);
+ s->img_number++;
+
+ return 0;
+}
+
+static int jpeg_write_trailer(AVFormatContext *s1)
+{
+ return 0;
+}
+
+/***/
+
+static int jpeg_read_header(AVFormatContext *s1, AVFormatParameters *ap)
+{
+ JpegContext *s;
+ int i;
+ char buf[1024];
+ ByteIOContext pb1, *f = &pb1;
+ AVStream *st;
+
+ s = av_mallocz(sizeof(JpegContext));
+ if (!s)
+ return -1;
+ s1->priv_data = s;
+ pstrcpy(s->path, sizeof(s->path), s1->filename);
+
+ s1->nb_streams = 1;
+ st = av_mallocz(sizeof(AVStream));
+ if (!st) {
+ av_free(s);
+ return -ENOMEM;
+ }
+ s1->streams[0] = st;
+ s->img_number = 0;
+
+ /* try to find the first image */
+ for(i=0;i<5;i++) {
+ if (get_frame_filename(buf, sizeof(buf), s->path, s->img_number) < 0)
+ goto fail;
+ if (url_fopen(f, buf, URL_RDONLY) >= 0)
+ break;
+ s->img_number++;
+ }
+ if (i == 5)
+ goto fail;
+ url_fclose(f);
+ st->codec.codec_type = CODEC_TYPE_VIDEO;
+ st->codec.codec_id = CODEC_ID_MJPEG;
+
+ if (!ap || !ap->frame_rate)
+ st->codec.frame_rate = 25 * FRAME_RATE_BASE;
+ else
+ st->codec.frame_rate = ap->frame_rate;
+ return 0;
+ fail:
+ av_free(s);
+ return -EIO;
+}
+
+static int jpeg_read_packet(AVFormatContext *s1, AVPacket *pkt)
+{
+ JpegContext *s = s1->priv_data;
+ char filename[1024];
+ int size;
+ ByteIOContext f1, *f = &f1;
+
+ if (get_frame_filename(filename, sizeof(filename),
+ s->path, s->img_number) < 0)
+ return -EIO;
+
+ f = &f1;
+ if (url_fopen(f, filename, URL_RDONLY) < 0)
+ return -EIO;
+
+ size = url_seek(url_fileno(f), 0, SEEK_END);
+ url_seek(url_fileno(f), 0, SEEK_SET);
+
+ av_new_packet(pkt, size);
+ pkt->stream_index = 0;
+ get_buffer(f, pkt->data, size);
+
+ url_fclose(f);
+ s->img_number++;
+ return 0;
+}
+
+static int jpeg_read_close(AVFormatContext *s1)
+{
+ return 0;
+}
+
+static AVInputFormat jpeg_iformat = {
+ "jpeg",
+ "JPEG image",
+ sizeof(JpegContext),
+ NULL,
+ jpeg_read_header,
+ jpeg_read_packet,
+ jpeg_read_close,
+ NULL,
+ .flags = AVFMT_NOFILE | AVFMT_NEEDNUMBER,
+ .extensions = "jpg,jpeg",
+};
+
+static AVOutputFormat jpeg_oformat = {
+ "jpeg",
+ "JPEG image",
+ "image/jpeg",
+ "jpg,jpeg",
+ sizeof(JpegContext),
+ CODEC_ID_NONE,
+ CODEC_ID_MJPEG,
+ jpeg_write_header,
+ jpeg_write_packet,
+ jpeg_write_trailer,
+ .flags = AVFMT_NOFILE | AVFMT_NEEDNUMBER,
+};
+
+int jpeg_init(void)
+{
+ av_register_output_format(&mpjpeg_format);
+ av_register_output_format(&single_jpeg_format);
+ av_register_input_format(&jpeg_iformat);
+ av_register_output_format(&jpeg_oformat);
+ return 0;
+}
diff --git a/libavformat/mov.c b/libavformat/mov.c
new file mode 100644
index 0000000000..91c7155b08
--- /dev/null
+++ b/libavformat/mov.c
@@ -0,0 +1,1347 @@
+/*
+ * MOV decoder.
+ * Copyright (c) 2001 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"
+#include "avi.h"
+
+#ifdef CONFIG_ZLIB
+#include <zlib.h>
+#endif
+
+/*
+ * First version by Francois Revol revol@free.fr
+ *
+ * Features and limitations:
+ * - reads most of the QT files I have (at least the structure),
+ * the exceptions are .mov with zlib compressed headers ('cmov' section). It shouldn't be hard to implement.
+ * FIXED, Francois Revol, 07/17/2002
+ * - ffmpeg has nearly none of the usual QuickTime codecs,
+ * although I succesfully dumped raw and mp3 audio tracks off .mov files.
+ * Sample QuickTime files with mp3 audio can be found at: http://www.3ivx.com/showcase.html
+ * - .mp4 parsing is still hazardous, although the format really is QuickTime with some minor changes
+ * (to make .mov parser crash maybe ?), despite what they say in the MPEG FAQ at
+ * http://mpeg.telecomitalialab.com/faq.htm
+ * - the code is quite ugly... maybe I won't do it recursive next time :-)
+ *
+ * Funny I didn't know about http://sourceforge.net/projects/qt-ffmpeg/
+ * when coding this :) (it's a writer anyway)
+ *
+ * Reference documents:
+ * http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
+ * Apple:
+ * http://developer.apple.com/techpubs/quicktime/qtdevdocs/QTFF/qtff.html
+ * http://developer.apple.com/techpubs/quicktime/qtdevdocs/PDF/QTFileFormat.pdf
+ * QuickTime is a trademark of Apple (AFAIK :))
+ */
+
+//#define DEBUG
+
+/* allows chunk splitting - should work now... */
+/* in case you can't read a file, try commenting */
+#define MOV_SPLIT_CHUNKS
+
+#ifdef DEBUG
+/*
+ * XXX: static sux, even more in a multithreaded environment...
+ * Avoid them. This is here just to help debugging.
+ */
+static int debug_indent = 0;
+void print_atom(const char *str, UINT32 type, UINT64 offset, UINT64 size)
+{
+ unsigned int tag, i;
+ tag = (unsigned int) type;
+ i=debug_indent;
+ if(tag == 0) tag = MKTAG('N', 'U', 'L', 'L');
+ while(i--)
+ printf("|");
+ printf("parse:");
+ printf(" %s: tag=%c%c%c%c offset=0x%x size=0x%x\n",
+ str, tag & 0xff,
+ (tag >> 8) & 0xff,
+ (tag >> 16) & 0xff,
+ (tag >> 24) & 0xff,
+ (unsigned int)offset,
+ (unsigned int)size);
+}
+#endif
+
+/* some streams in QT (and in MP4 mostly) aren't either video nor audio */
+/* so we first list them as this, then clean up the list of streams we give back, */
+/* getting rid of these */
+#define CODEC_TYPE_MOV_OTHER 2
+
+static const CodecTag mov_video_tags[] = {
+/* { CODEC_ID_, MKTAG('c', 'v', 'i', 'd') }, *//* Cinepak */
+/* { CODEC_ID_JPEG, MKTAG('j', 'p', 'e', 'g') }, *//* JPEG */
+/* { CODEC_ID_H263, MKTAG('r', 'a', 'w', ' ') }, *//* Uncompressed RGB */
+/* { CODEC_ID_H263, MKTAG('Y', 'u', 'v', '2') }, *//* Uncompressed YUV422 */
+/* Graphics */
+/* Animation */
+/* Apple video */
+/* Kodak Photo CD */
+ { CODEC_ID_MJPEG, MKTAG('j', 'p', 'e', 'g') }, /* PhotoJPEG */
+ { CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'e', 'g') }, /* MPEG */
+ { CODEC_ID_MJPEG, MKTAG('m', 'j', 'p', 'a') }, /* Motion-JPEG (format A) */
+ { CODEC_ID_MJPEG, MKTAG('m', 'j', 'p', 'b') }, /* Motion-JPEG (format B) */
+/* { CODEC_ID_GIF, MKTAG('g', 'i', 'f', ' ') }, *//* embedded gif files as frames (usually one "click to play movie" frame) */
+/* Sorenson video */
+ { CODEC_ID_SVQ1, MKTAG('S', 'V', 'Q', '1') }, /* Sorenson Video v1 */
+ { CODEC_ID_SVQ1, MKTAG('s', 'v', 'q', '1') }, /* Sorenson Video v1 */
+ { CODEC_ID_SVQ1, MKTAG('s', 'v', 'q', 'i') }, /* Sorenson Video v1 (from QT specs)*/
+ { CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') },
+ { CODEC_ID_MPEG4, MKTAG('D', 'I', 'V', 'X') }, /* OpenDiVX *//* sample files at http://heroinewarrior.com/xmovie.php3 use this tag */
+/* { CODEC_ID_, MKTAG('I', 'V', '5', '0') }, *//* Indeo 5.0 */
+ { CODEC_ID_H263, MKTAG('h', '2', '6', '3') }, /* H263 */
+ { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', ' ') }, /* DV NTSC */
+ { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', 'p') }, /* DV PAL */
+ { 0, 0 },
+};
+
+static const CodecTag mov_audio_tags[] = {
+/* { CODEC_ID_PCM_S16BE, MKTAG('N', 'O', 'N', 'E') }, *//* uncompressed */
+ { CODEC_ID_PCM_S16BE, MKTAG('t', 'w', 'o', 's') }, /* 16 bits */
+ { CODEC_ID_PCM_S8, MKTAG('t', 'w', 'o', 's') }, /* 8 bits */
+ { CODEC_ID_PCM_U8, 0x20776172 }, /* 8 bits unsigned */
+ { CODEC_ID_PCM_S16LE, MKTAG('s', 'o', 'w', 't') }, /* */
+ { CODEC_ID_PCM_MULAW, MKTAG('u', 'l', 'a', 'w') }, /* */
+ { CODEC_ID_PCM_ALAW, MKTAG('a', 'l', 'a', 'w') }, /* */
+ { CODEC_ID_ADPCM_IMA_QT, MKTAG('i', 'm', 'a', '4') }, /* IMA-4 ADPCM */
+ { CODEC_ID_MACE3, MKTAG('M', 'A', 'C', '3') }, /* Macintosh Audio Compression and Expansion 3:1 */
+ { CODEC_ID_MACE6, MKTAG('M', 'A', 'C', '6') }, /* Macintosh Audio Compression and Expansion 6:1 */
+
+ { CODEC_ID_MP2, MKTAG('.', 'm', 'p', '3') }, /* MPEG layer 3 */ /* sample files at http://www.3ivx.com/showcase.html use this tag */
+ { CODEC_ID_MP2, 0x6D730055 }, /* MPEG layer 3 */
+ { CODEC_ID_MP2, 0x5500736D }, /* MPEG layer 3 *//* XXX: check endianness */
+/* { CODEC_ID_OGG_VORBIS, MKTAG('O', 'g', 'g', 'S') }, *//* sample files at http://heroinewarrior.com/xmovie.php3 use this tag */
+/* MP4 tags */
+/* { CODEC_ID_AAC, MKTAG('m', 'p', '4', 'a') }, *//* MPEG 4 AAC or audio ? */
+ /* The standard for mpeg4 audio is still not normalised AFAIK anyway */
+ { 0, 0 },
+};
+
+/* the QuickTime file format is quite convoluted...
+ * it has lots of index tables, each indexing something in another one...
+ * Here we just use what is needed to read the chunks
+ */
+
+typedef struct MOV_sample_to_chunk_tbl {
+ long first;
+ long count;
+ long id;
+} MOV_sample_to_chunk_tbl;
+
+typedef struct MOVStreamContext {
+ int ffindex; /* the ffmpeg stream id */
+ int is_ff_stream; /* Is this stream presented to ffmpeg ? i.e. is this an audio or video stream ? */
+ long next_chunk;
+ long chunk_count;
+ INT64 *chunk_offsets;
+ long sample_to_chunk_sz;
+ MOV_sample_to_chunk_tbl *sample_to_chunk;
+ long sample_to_chunk_index;
+ long sample_size;
+ long sample_count;
+ long *sample_sizes;
+ long time_scale;
+ long current_sample;
+ long left_in_chunk; /* how many samples before next chunk */
+ /* specific MPEG4 header which is added at the beginning of the stream */
+ int header_len;
+ uint8_t *header_data;
+} MOVStreamContext;
+
+typedef struct MOVContext {
+ int mp4; /* set to 1 as soon as we are sure that the file is an .mp4 file (even some header parsing depends on this) */
+ AVFormatContext *fc;
+ long time_scale;
+ int found_moov; /* when both 'moov' and 'mdat' sections has been found */
+ int found_mdat; /* we suppose we have enough data to read the file */
+ INT64 mdat_size;
+ INT64 mdat_offset;
+ int total_streams;
+ /* some streams listed here aren't presented to the ffmpeg API, since they aren't either video nor audio
+ * but we need the info to be able to skip data from those streams in the 'mdat' section
+ */
+ MOVStreamContext *streams[MAX_STREAMS];
+
+ INT64 next_chunk_offset;
+ int partial; /* != 0 : there is still to read in the current chunk (=id of the stream + 1) */
+} MOVContext;
+
+
+struct MOVParseTableEntry;
+
+/* XXX: it's the first time I make a recursive parser I think... sorry if it's ugly :P */
+
+/* those functions parse an atom */
+/* return code:
+ 1: found what I wanted, exit
+ 0: continue to parse next atom
+ -1: error occured, exit
+ */
+typedef int (*mov_parse_function)(const struct MOVParseTableEntry *parse_table,
+ ByteIOContext *pb,
+ UINT32 atom_type,
+ INT64 atom_offset, /* after the size and type field (and eventually the extended size) */
+ INT64 atom_size, /* total size (excluding the size and type fields) */
+ void *param);
+
+/* links atom IDs to parse functions */
+typedef struct MOVParseTableEntry {
+ UINT32 type;
+ mov_parse_function func;
+} MOVParseTableEntry;
+
+static int parse_leaf(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param)
+{
+#ifdef DEBUG
+ print_atom("leaf", atom_type, atom_offset, atom_size);
+#endif
+ if(atom_size>1)
+ url_fskip(pb, atom_size);
+/* url_seek(pb, atom_offset+atom_size, SEEK_SET); */
+ return 0;
+}
+
+
+static int parse_default(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param)
+{
+ UINT32 type, foo=0;
+ UINT64 offset, size;
+ UINT64 total_size = 0;
+ int i;
+ int err = 0;
+ foo=0;
+#ifdef DEBUG
+ print_atom("default", atom_type, atom_offset, atom_size);
+ debug_indent++;
+#endif
+
+ offset = atom_offset;
+
+ if(atom_size < 0)
+ atom_size = 0x0FFFFFFFFFFFFFFF;
+ while((total_size < atom_size) && !url_feof(pb) && !err) {
+ size=atom_size;
+ type=0L;
+ if(atom_size >= 8) {
+ size = get_be32(pb);
+ type = get_le32(pb);
+ }
+ total_size += 8;
+ offset+=8;
+// printf("type: %08lx sz: %08lx", type, size);
+ if(size == 1) { /* 64 bit extended size */
+ size = get_be64(pb);
+ offset+=8;
+ total_size+=8;
+ size-=8;
+ }
+ if(size == 0)
+ size = atom_size - total_size;
+ size-=8;
+ for(i=0; parse_table[i].type != 0L && parse_table[i].type != type; i++);
+
+// printf(" i=%ld\n", i);
+ if (parse_table[i].type == 0) { /* skip leaf atoms data */
+// url_seek(pb, atom_offset+atom_size, SEEK_SET);
+#ifdef DEBUG
+ print_atom("unknown", type, offset, size);
+#endif
+ url_fskip(pb, size);
+ } else
+ err = (parse_table[i].func)(parse_table, pb, type, offset, size, param);
+
+ offset+=size;
+ total_size+=size;
+ }
+
+#ifdef DEBUG
+ debug_indent--;
+#endif
+ return err;
+}
+
+static int parse_mvhd(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param)
+{
+ MOVContext *c;
+#ifdef DEBUG
+ print_atom("mvhd", atom_type, atom_offset, atom_size);
+#endif
+ c = (MOVContext *)param;
+
+ get_byte(pb); /* version */
+ get_byte(pb); get_byte(pb); get_byte(pb); /* flags */
+
+ get_be32(pb); /* creation time */
+ get_be32(pb); /* modification time */
+ c->time_scale = get_be32(pb); /* time scale */
+#ifdef DEBUG
+ printf("time scale = %li\n", c->time_scale);
+#endif
+ get_be32(pb); /* duration */
+ get_be32(pb); /* preferred scale */
+
+ get_be16(pb); /* preferred volume */
+
+ url_fskip(pb, 10); /* reserved */
+
+ url_fskip(pb, 36); /* display matrix */
+
+ get_be32(pb); /* preview time */
+ get_be32(pb); /* preview duration */
+ get_be32(pb); /* poster time */
+ get_be32(pb); /* selection time */
+ get_be32(pb); /* selection duration */
+ get_be32(pb); /* current time */
+ get_be32(pb); /* next track ID */
+
+ return 0;
+}
+
+/* this atom should contain all header atoms */
+static int parse_moov(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param)
+{
+ int err;
+ MOVContext *c;
+#ifdef DEBUG
+ print_atom("moov", atom_type, atom_offset, atom_size);
+#endif
+ c = (MOVContext *)param;
+
+ err = parse_default(parse_table, pb, atom_type, atom_offset, atom_size, param);
+ /* we parsed the 'moov' atom, we can terminate the parsing as soon as we find the 'mdat' */
+ /* so we don't parse the whole file if over a network */
+ c->found_moov=1;
+ if(c->found_mdat)
+ return 1; /* found both, just go */
+ return 0; /* now go for mdat */
+}
+
+/* this atom contains actual media data */
+static int parse_mdat(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param)
+{
+ MOVContext *c;
+#ifdef DEBUG
+ print_atom("mdat", atom_type, atom_offset, atom_size);
+#endif
+ c = (MOVContext *)param;
+
+ if(atom_size == 0) /* wrong one (MP4) */
+ return 0;
+ c->found_mdat=1;
+ c->mdat_offset = atom_offset;
+ c->mdat_size = atom_size;
+ if(c->found_moov)
+ return 1; /* found both, just go */
+ url_fskip(pb, atom_size);
+ return 0; /* now go for moov */
+}
+
+/* this atom should be null (from specs), but some buggy files put the 'moov' atom inside it... */
+/* like the files created with Adobe Premiere 5.0, for samples see */
+/* http://graphics.tudelft.nl/~wouter/publications/soundtests/ */
+static int parse_wide(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param)
+{
+ int err;
+ UINT32 type;
+#ifdef DEBUG
+ print_atom("wide", atom_type, atom_offset, atom_size);
+ debug_indent++;
+#endif
+ if (atom_size < 8)
+ return 0; /* continue */
+ if (get_be32(pb) != 0) { /* 0 sized mdat atom... use the 'wide' atom size */
+ url_fskip(pb, atom_size - 4);
+ return 0;
+ }
+ type = get_le32(pb);
+ if (type != MKTAG('m', 'd', 'a', 't')) {
+ url_fskip(pb, atom_size - 8);
+ return 0;
+ }
+ err = parse_mdat(parse_table, pb, type, atom_offset + 8, atom_size - 8, param);
+#ifdef DEBUG
+ debug_indent--;
+#endif
+ return err;
+}
+
+static int parse_trak(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param)
+{
+ MOVContext *c;
+ AVStream *st;
+ MOVStreamContext *sc;
+#ifdef DEBUG
+ print_atom("trak", atom_type, atom_offset, atom_size);
+#endif
+
+ c = (MOVContext *)param;
+ st = av_new_stream(c->fc, c->fc->nb_streams);
+ if (!st) return -2;
+ sc = av_malloc(sizeof(MOVStreamContext));
+ sc->sample_to_chunk_index = -1;
+ st->priv_data = sc;
+ st->codec.codec_type = CODEC_TYPE_MOV_OTHER;
+ c->streams[c->fc->nb_streams-1] = sc;
+ return parse_default(parse_table, pb, atom_type, atom_offset, atom_size, param);
+}
+
+static int parse_tkhd(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param)
+{
+ MOVContext *c;
+ AVStream *st;
+#ifdef DEBUG
+ print_atom("tkhd", atom_type, atom_offset, atom_size);
+#endif
+
+ c = (MOVContext *)param;
+ st = c->fc->streams[c->fc->nb_streams-1];
+
+ get_byte(pb); /* version */
+
+ get_byte(pb); get_byte(pb);
+ get_byte(pb); /* flags */
+ /*
+ MOV_TRACK_ENABLED 0x0001
+ MOV_TRACK_IN_MOVIE 0x0002
+ MOV_TRACK_IN_PREVIEW 0x0004
+ MOV_TRACK_IN_POSTER 0x0008
+ */
+
+ get_be32(pb); /* creation time */
+ get_be32(pb); /* modification time */
+ st->id = (int)get_be32(pb); /* track id (NOT 0 !)*/
+ get_be32(pb); /* reserved */
+ get_be32(pb); /* duration */
+ get_be32(pb); /* reserved */
+ get_be32(pb); /* reserved */
+
+ get_be16(pb); /* layer */
+ get_be16(pb); /* alternate group */
+ get_be16(pb); /* volume */
+ get_be16(pb); /* reserved */
+
+ url_fskip(pb, 36); /* display matrix */
+
+ /* those are fixed-point */
+ st->codec.width = get_be32(pb) >> 16; /* track width */
+ st->codec.height = get_be32(pb) >> 16; /* track height */
+
+ return 0;
+}
+
+static int parse_mdhd(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param)
+{
+ MOVContext *c;
+ AVStream *st;
+#ifdef DEBUG
+ print_atom("mdhd", atom_type, atom_offset, atom_size);
+#endif
+
+ c = (MOVContext *)param;
+ st = c->fc->streams[c->fc->nb_streams-1];
+
+ get_byte(pb); /* version */
+
+ get_byte(pb); get_byte(pb);
+ get_byte(pb); /* flags */
+
+ get_be32(pb); /* creation time */
+ get_be32(pb); /* modification time */
+
+ c->streams[c->total_streams]->time_scale = get_be32(pb);
+
+#ifdef DEBUG
+ printf("track[%i].time_scale = %li\n", c->fc->nb_streams-1, c->streams[c->total_streams]->time_scale); /* time scale */
+#endif
+ get_be32(pb); /* duration */
+
+ get_be16(pb); /* language */
+ get_be16(pb); /* quality */
+
+ return 0;
+}
+
+static int parse_hdlr(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param)
+{
+ MOVContext *c;
+ int len = 0;
+ char *buf;
+ UINT32 type;
+ AVStream *st;
+ UINT32 ctype;
+#ifdef DEBUG
+ print_atom("hdlr", atom_type, atom_offset, atom_size);
+#endif
+ c = (MOVContext *)param;
+ st = c->fc->streams[c->fc->nb_streams-1];
+
+ get_byte(pb); /* version */
+ get_byte(pb); get_byte(pb); get_byte(pb); /* flags */
+
+ /* component type */
+ ctype = get_le32(pb);
+ type = get_le32(pb); /* component subtype */
+
+#ifdef DEBUG
+ printf("ctype= %c%c%c%c (0x%08lx)\n", *((char *)&ctype), ((char *)&ctype)[1], ((char *)&ctype)[2], ((char *)&ctype)[3], (long) ctype);
+ printf("stype= %c%c%c%c\n", *((char *)&type), ((char *)&type)[1], ((char *)&type)[2], ((char *)&type)[3]);
+#endif
+#ifdef DEBUG
+/* XXX: yeah this is ugly... */
+ if(ctype == MKTAG('m', 'h', 'l', 'r')) { /* MOV */
+ if(type == MKTAG('v', 'i', 'd', 'e'))
+ puts("hdlr: vide");
+ else if(type == MKTAG('s', 'o', 'u', 'n'))
+ puts("hdlr: soun");
+ } else if(ctype == 0) { /* MP4 */
+ if(type == MKTAG('v', 'i', 'd', 'e'))
+ puts("hdlr: vide");
+ else if(type == MKTAG('s', 'o', 'u', 'n'))
+ puts("hdlr: soun");
+ else if(type == MKTAG('o', 'd', 's', 'm'))
+ puts("hdlr: odsm");
+ else if(type == MKTAG('s', 'd', 's', 'm'))
+ puts("hdlr: sdsm");
+ } else puts("hdlr: meta");
+#endif
+
+ if(ctype == MKTAG('m', 'h', 'l', 'r')) { /* MOV */
+ /* helps parsing the string hereafter... */
+ c->mp4 = 0;
+ if(type == MKTAG('v', 'i', 'd', 'e'))
+ st->codec.codec_type = CODEC_TYPE_VIDEO;
+ else if(type == MKTAG('s', 'o', 'u', 'n'))
+ st->codec.codec_type = CODEC_TYPE_AUDIO;
+ } else if(ctype == 0) { /* MP4 */
+ /* helps parsing the string hereafter... */
+ c->mp4 = 1;
+ if(type == MKTAG('v', 'i', 'd', 'e'))
+ st->codec.codec_type = CODEC_TYPE_VIDEO;
+ else if(type == MKTAG('s', 'o', 'u', 'n'))
+ st->codec.codec_type = CODEC_TYPE_AUDIO;
+ }
+ get_be32(pb); /* component manufacture */
+ get_be32(pb); /* component flags */
+ get_be32(pb); /* component flags mask */
+
+ if(atom_size <= 24)
+ return 0; /* nothing left to read */
+ /* XXX: MP4 uses a C string, not a pascal one */
+ /* component name */
+
+ if(c->mp4) {
+ /* .mp4: C string */
+ while(get_byte(pb) && (++len < (atom_size - 24)));
+ } else {
+ /* .mov: PASCAL string */
+ len = get_byte(pb);
+ buf = av_malloc(len+1);
+ get_buffer(pb, buf, len);
+ buf[len] = '\0';
+#ifdef DEBUG
+ printf("**buf='%s'\n", buf);
+#endif
+ av_free(buf);
+ }
+#if 0
+ len = get_byte(pb);
+ /* XXX: use a better heuristic */
+ if(len < 32) {
+ /* assume that it is a Pascal like string */
+ buf = av_malloc(len+1);
+ get_buffer(pb, buf, len);
+ buf[len] = '\0';
+#ifdef DEBUG
+ printf("**buf='%s'\n", buf);
+#endif
+ av_free(buf);
+ } else {
+ /* MP4 string */
+ for(;;) {
+ if (len == 0)
+ break;
+ len = get_byte(pb);
+ }
+ }
+#endif
+
+ return 0;
+}
+
+static int mp4_read_descr_len(ByteIOContext *pb)
+{
+ int c, len, count;
+
+ len = 0;
+ count = 0;
+ for(;;) {
+ c = get_byte(pb);
+ len = (len << 7) | (c & 0x7f);
+ if ((c & 0x80) == 0)
+ break;
+ if (++count == 4)
+ break;
+ }
+ return len;
+}
+
+static int mp4_read_descr(ByteIOContext *pb, int *tag)
+{
+ int len;
+ *tag = get_byte(pb);
+ len = mp4_read_descr_len(pb);
+#ifdef DEBUG
+ printf("MPEG4 description: tag=0x%02x len=%d\n", *tag, len);
+#endif
+ return len;
+}
+
+static int parse_stsd(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param)
+{
+ MOVContext *c;
+ int entries, size, samp_sz, frames_per_sample, id;
+ UINT32 format;
+ AVStream *st;
+ MOVStreamContext *sc;
+#ifdef DEBUG
+ print_atom("stsd", atom_type, atom_offset, atom_size);
+#endif
+ c = (MOVContext *)param;
+ st = c->fc->streams[c->fc->nb_streams-1];
+ sc = (MOVStreamContext *)st->priv_data;
+
+ get_byte(pb); /* version */
+ get_byte(pb); get_byte(pb); get_byte(pb); /* flags */
+
+ entries = get_be32(pb);
+
+ while(entries--) {
+ size = get_be32(pb); /* size */
+ format = get_le32(pb); /* data format */
+
+ get_be32(pb); /* reserved */
+ get_be16(pb); /* reserved */
+ get_be16(pb); /* index */
+
+ /* for MPEG4: set codec type by looking for it */
+ id = codec_get_id(mov_video_tags, format);
+ if (id >= 0) {
+ AVCodec *codec;
+ codec = avcodec_find_decoder(id);
+ if (codec)
+ st->codec.codec_type = codec->type;
+ }
+#ifdef DEBUG
+ printf("size=%d 4CC= %c%c%c%c codec_type=%d\n",
+ size,
+ (format >> 0) & 0xff,
+ (format >> 8) & 0xff,
+ (format >> 16) & 0xff,
+ (format >> 24) & 0xff,
+ st->codec.codec_type);
+#endif
+ if(st->codec.codec_type==CODEC_TYPE_VIDEO) {
+ st->codec.codec_tag = format;
+ st->codec.codec_id = codec_get_id(mov_video_tags, format);
+ get_be16(pb); /* version */
+ get_be16(pb); /* revision level */
+ get_be32(pb); /* vendor */
+ get_be32(pb); /* temporal quality */
+ get_be32(pb); /* spacial quality */
+ st->codec.width = get_be16(pb); /* width */
+ st->codec.height = get_be16(pb); /* height */
+#if 1
+ if (st->codec.codec_id == CODEC_ID_MPEG4) {
+ /* in some MPEG4 the width/height are not correct, so
+ we ignore this info */
+ st->codec.width = 0;
+ st->codec.height = 0;
+ }
+#endif
+ get_be32(pb); /* horiz resolution */
+ get_be32(pb); /* vert resolution */
+ get_be32(pb); /* data size, always 0 */
+ frames_per_sample = get_be16(pb); /* frame per samples */
+#ifdef DEBUG
+ printf("frames/samples = %d\n", frames_per_sample);
+#endif
+ url_fskip(pb, 32); /* codec name */
+
+ get_be16(pb); /* depth */
+ get_be16(pb); /* colortable id */
+
+ st->codec.frame_rate = 25 * FRAME_RATE_BASE;
+
+ size -= (16+8*4+2+32+2*2);
+ while (size >= 8) {
+ int atom_size, atom_type;
+ INT64 start_pos;
+
+ atom_size = get_be32(pb);
+ atom_type = get_le32(pb);
+ size -= 8;
+#ifdef DEBUG
+ printf("VIDEO: atom_type=%c%c%c%c atom_size=%d size_left=%d\n",
+ (atom_type >> 0) & 0xff,
+ (atom_type >> 8) & 0xff,
+ (atom_type >> 16) & 0xff,
+ (atom_type >> 24) & 0xff,
+ atom_size, size);
+#endif
+ start_pos = url_ftell(pb);
+
+ switch(atom_type) {
+ case MKTAG('e', 's', 'd', 's'):
+ {
+ int tag, len;
+ /* Well, broken but suffisant for some MP4 streams */
+ get_be32(pb); /* version + flags */
+ len = mp4_read_descr(pb, &tag);
+ if (tag == 0x03) {
+ /* MP4ESDescrTag */
+ get_be16(pb); /* ID */
+ get_byte(pb); /* priority */
+ len = mp4_read_descr(pb, &tag);
+ if (tag != 0x04)
+ goto fail;
+ /* MP4DecConfigDescrTag */
+ get_byte(pb); /* objectTypeId */
+ get_be32(pb); /* streamType + buffer size */
+ get_be32(pb); /* max bit rate */
+ get_be32(pb); /* avg bit rate */
+ len = mp4_read_descr(pb, &tag);
+ if (tag != 0x05)
+ goto fail;
+ /* MP4DecSpecificDescrTag */
+#ifdef DEBUG
+ printf("Specific MPEG4 header len=%d\n", len);
+#endif
+ sc->header_data = av_mallocz(len);
+ if (sc->header_data) {
+ get_buffer(pb, sc->header_data, len);
+ sc->header_len = len;
+ }
+ }
+ /* in any case, skip garbage */
+ }
+ break;
+ default:
+ break;
+ }
+ fail:
+ url_fskip(pb, (atom_size - 8) -
+ ((url_ftell(pb) - start_pos)));
+ size -= atom_size - 8;
+ }
+ if (size > 0) {
+ /* unknown extension */
+ url_fskip(pb, size);
+ }
+ } else {
+ st->codec.codec_tag = format;
+
+ get_be16(pb); /* version */
+ get_be16(pb); /* revision level */
+ get_be32(pb); /* vendor */
+
+ st->codec.channels = get_be16(pb);/* channel count */
+ samp_sz = get_be16(pb); /* sample size */
+#ifdef DEBUG
+ if(samp_sz != 16)
+ puts("!!! stsd: audio sample size is not 16 bit !");
+#endif
+ st->codec.codec_id = codec_get_id(mov_audio_tags, format);
+ /* handle specific s8 codec */
+ if (st->codec.codec_id == CODEC_ID_PCM_S16BE && samp_sz == 8)
+ st->codec.codec_id = CODEC_ID_PCM_S8;
+
+ get_be16(pb); /* compression id = 0*/
+ get_be16(pb); /* packet size = 0 */
+
+ st->codec.sample_rate = ((get_be32(pb) >> 16));
+ st->codec.bit_rate = 0;
+#if 0
+
+ get_be16(pb); get_be16(pb); /* */
+ get_be16(pb); /* */
+ get_be16(pb); /* */
+ get_be16(pb); /* */
+ get_be16(pb); /* */
+#endif
+ if(size > 16)
+ url_fskip(pb, size-(16+20));
+ }
+ }
+/*
+ if(len) {
+ buf = av_malloc(len+1);
+ get_buffer(pb, buf, len);
+ buf[len] = '\0';
+ puts(buf);
+ av_free(buf);
+ }
+*/
+ return 0;
+}
+
+static int parse_stco(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param)
+{
+ MOVContext *c;
+ int entries, i;
+ AVStream *st;
+ MOVStreamContext *sc;
+#ifdef DEBUG
+ print_atom("stco", atom_type, atom_offset, atom_size);
+#endif
+ c = (MOVContext *)param;
+ st = c->fc->streams[c->fc->nb_streams-1];
+ sc = (MOVStreamContext *)st->priv_data;
+
+ get_byte(pb); /* version */
+ get_byte(pb); get_byte(pb); get_byte(pb); /* flags */
+
+ entries = get_be32(pb);
+ sc->chunk_count = entries;
+ sc->chunk_offsets = av_malloc(entries * sizeof(INT64));
+ if(atom_type == MKTAG('s', 't', 'c', 'o')) {
+ for(i=0; i<entries; i++) {
+ sc->chunk_offsets[i] = get_be32(pb);
+ }
+ } else if(atom_type == MKTAG('c', 'o', '6', '4')) {
+ for(i=0; i<entries; i++) {
+ sc->chunk_offsets[i] = get_be64(pb);
+ }
+ } else
+ return -1;
+#ifdef DEBUG
+/*
+ for(i=0; i<entries; i++) {
+ printf("chunk offset=0x%Lx\n", sc->chunk_offsets[i]);
+ }
+*/
+#endif
+ return 0;
+}
+
+static int parse_stsc(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param)
+{
+ MOVContext *c;
+ int entries, i;
+ AVStream *st;
+ MOVStreamContext *sc;
+#ifdef DEBUG
+ print_atom("stsc", atom_type, atom_offset, atom_size);
+#endif
+ c = (MOVContext *)param;
+ st = c->fc->streams[c->fc->nb_streams-1];
+ sc = (MOVStreamContext *)st->priv_data;
+
+ get_byte(pb); /* version */
+ get_byte(pb); get_byte(pb); get_byte(pb); /* flags */
+
+ entries = get_be32(pb);
+#ifdef DEBUG
+printf("track[%i].stsc.entries = %i\n", c->fc->nb_streams-1, entries);
+#endif
+ sc->sample_to_chunk_sz = entries;
+ sc->sample_to_chunk = av_malloc(entries * sizeof(MOV_sample_to_chunk_tbl));
+ for(i=0; i<entries; i++) {
+ sc->sample_to_chunk[i].first = get_be32(pb);
+ sc->sample_to_chunk[i].count = get_be32(pb);
+ sc->sample_to_chunk[i].id = get_be32(pb);
+#ifdef DEBUG
+/* printf("sample_to_chunk first=%ld count=%ld, id=%ld\n", sc->sample_to_chunk[i].first, sc->sample_to_chunk[i].count, sc->sample_to_chunk[i].id); */
+#endif
+ }
+ return 0;
+}
+
+static int parse_stsz(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param)
+{
+ MOVContext *c;
+ int entries, i;
+ AVStream *st;
+ MOVStreamContext *sc;
+#ifdef DEBUG
+ print_atom("stsz", atom_type, atom_offset, atom_size);
+#endif
+ c = (MOVContext *)param;
+ st = c->fc->streams[c->fc->nb_streams-1];
+ sc = (MOVStreamContext *)st->priv_data;
+
+ get_byte(pb); /* version */
+ get_byte(pb); get_byte(pb); get_byte(pb); /* flags */
+
+ sc->sample_size = get_be32(pb);
+ entries = get_be32(pb);
+ sc->sample_count = entries;
+#ifdef DEBUG
+ printf("sample_size = %ld sample_count = %ld\n", sc->sample_size, sc->sample_count);
+#endif
+ if(sc->sample_size)
+ return 0; /* there isn't any table following */
+ sc->sample_sizes = av_malloc(entries * sizeof(long));
+ for(i=0; i<entries; i++) {
+ sc->sample_sizes[i] = get_be32(pb);
+#ifdef DEBUG
+/* printf("sample_sizes[]=%ld\n", sc->sample_sizes[i]); */
+#endif
+ }
+ return 0;
+}
+
+static int parse_stts(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param)
+{
+ MOVContext *c;
+ int entries, i;
+ AVStream *st;
+ MOVStreamContext *sc;
+#ifdef DEBUG
+ print_atom("stts", atom_type, atom_offset, atom_size);
+#endif
+ c = (MOVContext *)param;
+ st = c->fc->streams[c->fc->nb_streams-1];
+ sc = (MOVStreamContext *)st->priv_data;
+
+ get_byte(pb); /* version */
+ get_byte(pb); get_byte(pb); get_byte(pb); /* flags */
+ entries = get_be32(pb);
+#ifdef DEBUG
+printf("track[%i].stts.entries = %i\n", c->fc->nb_streams-1, entries);
+#endif
+ for(i=0; i<entries; i++) {
+ int sample_duration;
+
+ get_be32(pb);
+ sample_duration = get_be32(pb);
+
+ if (!i && st->codec.codec_type==CODEC_TYPE_VIDEO) {
+ st->codec.frame_rate = FRAME_RATE_BASE * c->streams[c->total_streams]->time_scale;
+ if (sample_duration)
+ st->codec.frame_rate /= sample_duration;
+#ifdef DEBUG
+ printf("VIDEO FRAME RATE= %i (sd= %i)\n", st->codec.frame_rate, sample_duration);
+#endif
+ }
+ }
+ return 0;
+}
+
+#ifdef CONFIG_ZLIB
+static int null_read_packet(void *opaque, UINT8 *buf, int buf_size)
+{
+ return -1;
+}
+
+static int parse_cmov(const MOVParseTableEntry *parse_table, ByteIOContext *pb, UINT32 atom_type, INT64 atom_offset, INT64 atom_size, void *param)
+{
+ MOVContext *c;
+ ByteIOContext ctx;
+ char *cmov_data;
+ unsigned char *moov_data; /* uncompressed data */
+ long cmov_len, moov_len;
+ int ret;
+#ifdef DEBUG
+ print_atom("cmov", atom_type, atom_offset, atom_size);
+#endif
+ c = (MOVContext *)param;
+
+ get_be32(pb); /* dcom atom */
+ if (get_le32(pb) != MKTAG( 'd', 'c', 'o', 'm' ))
+ return -1;
+ if (get_le32(pb) != MKTAG( 'z', 'l', 'i', 'b' )) {
+ puts("unknown compression for cmov atom !");
+ return -1;
+ }
+ get_be32(pb); /* cmvd atom */
+ if (get_le32(pb) != MKTAG( 'c', 'm', 'v', 'd' ))
+ return -1;
+ moov_len = get_be32(pb); /* uncompressed size */
+ cmov_len = atom_size - 6 * 4;
+
+ cmov_data = av_malloc(cmov_len);
+ if (!cmov_data)
+ return -1;
+ moov_data = av_malloc(moov_len);
+ if (!moov_data) {
+ av_free(cmov_data);
+ return -1;
+ }
+ get_buffer(pb, cmov_data, cmov_len);
+ if(uncompress (moov_data, &moov_len, (const Bytef *)cmov_data, cmov_len) != Z_OK)
+ return -1;
+ if(init_put_byte(&ctx, moov_data, moov_len, 0, NULL, null_read_packet, NULL, NULL) != 0)
+ return -1;
+ ctx.buf_end = ctx.buffer + moov_len;
+ ret = parse_default(parse_table, &ctx, MKTAG( 'm', 'o', 'o', 'v' ), 0, moov_len, param);
+ av_free(moov_data);
+ av_free(cmov_data);
+ return ret;
+}
+#endif
+
+static const MOVParseTableEntry mov_default_parse_table[] = {
+/* mp4 atoms */
+{ MKTAG( 'm', 'p', '4', 'a' ), parse_default },
+{ MKTAG( 'c', 'o', '6', '4' ), parse_stco },
+{ MKTAG( 's', 't', 'c', 'o' ), parse_stco },
+{ MKTAG( 'c', 'r', 'h', 'd' ), parse_default },
+{ MKTAG( 'c', 't', 't', 's' ), parse_leaf },
+{ MKTAG( 'c', 'p', 'r', 't' ), parse_default },
+{ MKTAG( 'u', 'r', 'l', ' ' ), parse_leaf },
+{ MKTAG( 'u', 'r', 'n', ' ' ), parse_leaf },
+{ MKTAG( 'd', 'i', 'n', 'f' ), parse_default },
+{ MKTAG( 'd', 'r', 'e', 'f' ), parse_leaf },
+{ MKTAG( 's', 't', 'd', 'p' ), parse_default },
+{ MKTAG( 'e', 's', 'd', 's' ), parse_default },
+{ MKTAG( 'e', 'd', 't', 's' ), parse_default },
+{ MKTAG( 'e', 'l', 's', 't' ), parse_leaf },
+{ MKTAG( 'u', 'u', 'i', 'd' ), parse_default },
+{ MKTAG( 'f', 'r', 'e', 'e' ), parse_leaf },
+{ MKTAG( 'h', 'd', 'l', 'r' ), parse_hdlr },
+{ MKTAG( 'h', 'm', 'h', 'd' ), parse_leaf },
+{ MKTAG( 'h', 'i', 'n', 't' ), parse_leaf },
+{ MKTAG( 'n', 'm', 'h', 'd' ), parse_leaf },
+{ MKTAG( 'm', 'p', '4', 's' ), parse_default },
+{ MKTAG( 'm', 'd', 'i', 'a' ), parse_default },
+{ MKTAG( 'm', 'd', 'a', 't' ), parse_mdat },
+{ MKTAG( 'm', 'd', 'h', 'd' ), parse_mdhd },
+{ MKTAG( 'm', 'i', 'n', 'f' ), parse_default },
+{ MKTAG( 'm', 'o', 'o', 'v' ), parse_moov },
+{ MKTAG( 'm', 'v', 'h', 'd' ), parse_mvhd },
+{ MKTAG( 'i', 'o', 'd', 's' ), parse_leaf },
+{ MKTAG( 'o', 'd', 'h', 'd' ), parse_default },
+{ MKTAG( 'm', 'p', 'o', 'd' ), parse_leaf },
+{ MKTAG( 's', 't', 's', 'd' ), parse_stsd },
+{ MKTAG( 's', 't', 's', 'z' ), parse_stsz },
+{ MKTAG( 's', 't', 'b', 'l' ), parse_default },
+{ MKTAG( 's', 't', 's', 'c' ), parse_stsc },
+{ MKTAG( 's', 'd', 'h', 'd' ), parse_default },
+{ MKTAG( 's', 't', 's', 'h' ), parse_default },
+{ MKTAG( 's', 'k', 'i', 'p' ), parse_default },
+{ MKTAG( 's', 'm', 'h', 'd' ), parse_leaf },
+{ MKTAG( 'd', 'p', 'n', 'd' ), parse_leaf },
+{ MKTAG( 's', 't', 's', 's' ), parse_leaf },
+{ MKTAG( 's', 't', 't', 's' ), parse_stts },
+{ MKTAG( 't', 'r', 'a', 'k' ), parse_trak },
+{ MKTAG( 't', 'k', 'h', 'd' ), parse_tkhd },
+{ MKTAG( 't', 'r', 'e', 'f' ), parse_default }, /* not really */
+{ MKTAG( 'u', 'd', 't', 'a' ), parse_leaf },
+{ MKTAG( 'v', 'm', 'h', 'd' ), parse_leaf },
+{ MKTAG( 'm', 'p', '4', 'v' ), parse_default },
+/* extra mp4 */
+{ MKTAG( 'M', 'D', 'E', 'S' ), parse_leaf },
+/* QT atoms */
+{ MKTAG( 'c', 'h', 'a', 'p' ), parse_leaf },
+{ MKTAG( 'c', 'l', 'i', 'p' ), parse_default },
+{ MKTAG( 'c', 'r', 'g', 'n' ), parse_leaf },
+{ MKTAG( 'k', 'm', 'a', 't' ), parse_leaf },
+{ MKTAG( 'm', 'a', 't', 't' ), parse_default },
+{ MKTAG( 'r', 'd', 'r', 'f' ), parse_leaf },
+{ MKTAG( 'r', 'm', 'd', 'a' ), parse_default },
+{ MKTAG( 'r', 'm', 'd', 'r' ), parse_leaf },
+//{ MKTAG( 'r', 'm', 'q', 'u' ), parse_leaf },
+{ MKTAG( 'r', 'm', 'r', 'a' ), parse_default },
+{ MKTAG( 's', 'c', 'p', 't' ), parse_leaf },
+{ MKTAG( 's', 'y', 'n', 'c' ), parse_leaf },
+{ MKTAG( 's', 's', 'r', 'c' ), parse_leaf },
+{ MKTAG( 't', 'c', 'm', 'd' ), parse_leaf },
+{ MKTAG( 'w', 'i', 'd', 'e' ), parse_wide }, /* place holder */
+#ifdef CONFIG_ZLIB
+{ MKTAG( 'c', 'm', 'o', 'v' ), parse_cmov },
+#else
+{ MKTAG( 'c', 'm', 'o', 'v' ), parse_leaf },
+#endif
+{ 0L, parse_leaf }
+};
+
+static void mov_free_stream_context(MOVStreamContext *sc)
+{
+ if(sc) {
+ av_free(sc->chunk_offsets);
+ av_free(sc->sample_to_chunk);
+ av_free(sc->header_data);
+ av_free(sc);
+ }
+}
+
+static uint32_t to_tag(uint8_t *buf)
+{
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+}
+
+static uint32_t to_be32(uint8_t *buf)
+{
+ return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+}
+
+/* XXX: is it suffisant ? */
+static int mov_probe(AVProbeData *p)
+{
+ unsigned int offset;
+ uint32_t tag;
+
+ /* check file header */
+ if (p->buf_size <= 12)
+ return 0;
+ offset = 0;
+ for(;;) {
+ /* ignore invalid offset */
+ if ((offset + 8) > (unsigned int)p->buf_size)
+ return 0;
+ tag = to_tag(p->buf + offset + 4);
+ switch(tag) {
+ case MKTAG( 'm', 'o', 'o', 'v' ):
+ case MKTAG( 'w', 'i', 'd', 'e' ):
+ case MKTAG( 'f', 'r', 'e', 'e' ):
+ case MKTAG( 'm', 'd', 'a', 't'):
+ return AVPROBE_SCORE_MAX;
+ case MKTAG( 'f', 't', 'y', 'p' ):
+ case MKTAG( 's', 'k', 'i', 'p' ):
+ offset = to_be32(p->buf) + offset;
+ break;
+ default:
+ /* unrecognized tag */
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap)
+{
+ MOVContext *mov = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ int i, j, nb, err;
+ INT64 size;
+
+ mov->fc = s;
+#if 0
+ /* XXX: I think we should auto detect */
+ if(s->iformat->name[1] == 'p')
+ mov->mp4 = 1;
+#endif
+ if(!url_is_streamed(pb)) /* .mov and .mp4 aren't streamable anyway (only progressive download if moov is before mdat) */
+ size = url_filesize(url_fileno(pb));
+ else
+ size = 0x7FFFFFFFFFFFFFFF;
+
+#ifdef DEBUG
+ printf("filesz=%Ld\n", size);
+#endif
+
+ /* check MOV header */
+ err = parse_default(mov_default_parse_table, pb, 0L, 0LL, size, mov);
+ if(err<0 || (!mov->found_moov || !mov->found_mdat)) {
+ puts("header not found !!!");
+ exit(1);
+ }
+#ifdef DEBUG
+ printf("on_parse_exit_offset=%d\n", (int) url_ftell(pb));
+#endif
+ /* some cleanup : make sure we are on the mdat atom */
+ if(!url_is_streamed(pb) && (url_ftell(pb) != mov->mdat_offset))
+ url_fseek(pb, mov->mdat_offset, SEEK_SET);
+
+ mov->next_chunk_offset = mov->mdat_offset; /* initialise reading */
+
+#ifdef DEBUG
+ printf("mdat_reset_offset=%d\n", (int) url_ftell(pb));
+#endif
+
+#ifdef DEBUG
+ printf("streams= %d\n", s->nb_streams);
+#endif
+ mov->total_streams = nb = s->nb_streams;
+
+#if 1
+ for(i=0; i<s->nb_streams;) {
+ if(s->streams[i]->codec.codec_type == CODEC_TYPE_MOV_OTHER) {/* not audio, not video, delete */
+ av_free(s->streams[i]);
+ for(j=i+1; j<s->nb_streams; j++)
+ s->streams[j-1] = s->streams[j];
+ s->nb_streams--;
+ } else
+ i++;
+ }
+ for(i=0; i<s->nb_streams;i++) {
+ MOVStreamContext *sc;
+ sc = (MOVStreamContext *)s->streams[i]->priv_data;
+ sc->ffindex = i;
+ sc->is_ff_stream = 1;
+ }
+#endif
+#ifdef DEBUG
+ printf("real streams= %d\n", s->nb_streams);
+#endif
+ return 0;
+}
+
+/* Yes, this is ugly... I didn't write the specs of QT :p */
+/* XXX:remove useless commented code sometime */
+static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ MOVContext *mov = s->priv_data;
+ MOVStreamContext *sc;
+ INT64 offset = 0x0FFFFFFFFFFFFFFF;
+ int i;
+ int st_id = 0, size;
+ size = 0x0FFFFFFF;
+
+#ifdef MOV_SPLIT_CHUNKS
+ if (mov->partial) {
+
+ int idx;
+
+ st_id = mov->partial - 1;
+ idx = mov->streams[st_id]->sample_to_chunk_index;
+ if (idx < 0) return 0;
+ size = mov->streams[st_id]->sample_sizes[mov->streams[st_id]->current_sample];
+
+ mov->streams[st_id]->current_sample++;
+ mov->streams[st_id]->left_in_chunk--;
+
+ if(mov->streams[st_id]->left_in_chunk <= 0)
+ mov->partial = 0;
+ offset = mov->next_chunk_offset;
+ /* extract the sample */
+
+ goto readchunk;
+ }
+#endif
+
+again:
+ for(i=0; i<mov->total_streams; i++) {
+ if((mov->streams[i]->next_chunk < mov->streams[i]->chunk_count)
+ && (mov->streams[i]->chunk_offsets[mov->streams[i]->next_chunk] < offset)) {
+ st_id = i;
+ offset = mov->streams[i]->chunk_offsets[mov->streams[i]->next_chunk];
+ }
+ }
+ mov->streams[st_id]->next_chunk++;
+ if(offset==0x0FFFFFFFFFFFFFFF)
+ return -1;
+
+ if(mov->next_chunk_offset < offset) { /* some meta data */
+ url_fskip(&s->pb, (offset - mov->next_chunk_offset));
+ mov->next_chunk_offset = offset;
+ }
+
+//printf("chunk: [%i] %lli -> %lli\n", st_id, mov->next_chunk_offset, offset);
+ if(!mov->streams[st_id]->is_ff_stream) {
+ url_fskip(&s->pb, (offset - mov->next_chunk_offset));
+ mov->next_chunk_offset = offset;
+ offset = 0x0FFFFFFFFFFFFFFF;
+ goto again;
+ }
+
+ /* now get the chunk size... */
+
+ for(i=0; i<mov->total_streams; i++) {
+ if((mov->streams[i]->next_chunk < mov->streams[i]->chunk_count)
+ && ((mov->streams[i]->chunk_offsets[mov->streams[i]->next_chunk] - offset) < size)) {
+ size = mov->streams[i]->chunk_offsets[mov->streams[i]->next_chunk] - offset;
+ }
+ }
+#ifdef MOV_SPLIT_CHUNKS
+ /* split chunks into samples */
+ if(mov->streams[st_id]->sample_size == 0) {
+ int idx;
+ idx = mov->streams[st_id]->sample_to_chunk_index;
+ if ((idx + 1 < mov->streams[st_id]->sample_to_chunk_sz)
+ && (mov->streams[st_id]->next_chunk >= mov->streams[st_id]->sample_to_chunk[idx + 1].first))
+ idx++;
+ mov->streams[st_id]->sample_to_chunk_index = idx;
+ if(idx >= 0 && mov->streams[st_id]->sample_to_chunk[idx].count != 1) {
+ mov->partial = st_id+1;
+ /* we'll have to get those samples before next chunk */
+ mov->streams[st_id]->left_in_chunk = (mov->streams[st_id]->sample_to_chunk[idx].count) - 1;
+ size = mov->streams[st_id]->sample_sizes[mov->streams[st_id]->current_sample];
+ }
+
+ mov->streams[st_id]->current_sample++;
+ }
+#endif
+
+readchunk:
+//printf("chunk: [%i] %lli -> %lli (%i)\n", st_id, offset, offset + size, size);
+ if(size == 0x0FFFFFFF)
+ size = mov->mdat_size + mov->mdat_offset - offset;
+ if(size < 0)
+ return -1;
+ if(size == 0)
+ return -1;
+ url_fseek(&s->pb, offset, SEEK_SET);
+ sc = mov->streams[st_id];
+ if (sc->header_len > 0) {
+ av_new_packet(pkt, size + sc->header_len);
+ memcpy(pkt->data, sc->header_data, sc->header_len);
+ get_buffer(&s->pb, pkt->data + sc->header_len, size);
+ /* free header */
+ av_freep(&sc->header_data);
+ sc->header_len = 0;
+ } else {
+ av_new_packet(pkt, size);
+ get_buffer(&s->pb, pkt->data, pkt->size);
+ }
+ pkt->stream_index = sc->ffindex;
+
+#ifdef DEBUG
+/*
+ printf("Packet (%d, %d, %ld) ", pkt->stream_index, st_id, pkt->size);
+ for(i=0; i<8; i++)
+ printf("%02x ", pkt->data[i]);
+ for(i=0; i<8; i++)
+ printf("%c ", (pkt->data[i]) & 0x7F);
+ puts("");
+*/
+#endif
+
+ mov->next_chunk_offset = offset + size;
+
+ return 0;
+}
+
+static int mov_read_close(AVFormatContext *s)
+{
+ int i;
+ MOVContext *mov = s->priv_data;
+ for(i=0; i<mov->total_streams; i++)
+ mov_free_stream_context(mov->streams[i]);
+ for(i=0; i<s->nb_streams; i++)
+ av_freep(&s->streams[i]);
+ return 0;
+}
+
+static AVInputFormat mov_iformat = {
+ "mov",
+ "QuickTime/MPEG4 format",
+ sizeof(MOVContext),
+ mov_probe,
+ mov_read_header,
+ mov_read_packet,
+ mov_read_close,
+};
+
+int mov_init(void)
+{
+ av_register_input_format(&mov_iformat);
+ return 0;
+}
diff --git a/libavformat/mpeg.c b/libavformat/mpeg.c
new file mode 100644
index 0000000000..748841881d
--- /dev/null
+++ b/libavformat/mpeg.c
@@ -0,0 +1,685 @@
+/*
+ * 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 buffer[MAX_PAYLOAD_SIZE];
+ int buffer_ptr;
+ UINT8 id;
+ int max_buffer_size; /* in bytes */
+ int packet_number;
+ INT64 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 *buf, INT64 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, 0x2);
+ } else {
+ put_bits(&pb, 4, 0x2);
+ }
+ put_bits(&pb, 3, (UINT32)((timestamp >> 30) & 0x07));
+ put_bits(&pb, 1, 1);
+ put_bits(&pb, 15, (UINT32)((timestamp >> 15) & 0x7fff));
+ put_bits(&pb, 1, 1);
+ put_bits(&pb, 15, (UINT32)((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 *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;i<ctx->nb_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;i<ctx->nb_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;i<ctx->nb_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;i<ctx->nb_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;i<ctx->nb_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, int last_pkt)
+{
+ MpegMuxContext *s = ctx->priv_data;
+ StreamInfo *stream = ctx->streams[stream_index]->priv_data;
+ UINT8 *buf_ptr;
+ int size, payload_size, startcode, id, len, stuffing_size, i, header_len;
+ INT64 timestamp;
+ UINT8 buffer[128];
+ int last = last_pkt ? 4 : 0;
+
+ 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 + last);
+ 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;i<stuffing_size;i++)
+ put_byte(&ctx->pb, 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)((((timestamp >> 15) & 0x7fff) << 1) | 1));
+ put_be16(&ctx->pb, (UINT16)((((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);
+ }
+ }
+
+ if (last_pkt) {
+ put_be32(&ctx->pb, ISO_11172_END_CODE);
+ }
+ /* 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,
+ UINT8 *buf, int size, int 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, 0);
+ }
+ }
+ return 0;
+}
+
+static int mpeg_mux_end(AVFormatContext *ctx)
+{
+ StreamInfo *stream;
+ int i;
+
+ /* flush each packet */
+ for(i=0;i<ctx->nb_streams;i++) {
+ stream = ctx->streams[i]->priv_data;
+ if (stream->buffer_ptr > 0) {
+ if (i == (ctx->nb_streams - 1))
+ flush_packet(ctx, i, 1);
+ else
+ flush_packet(ctx, i, 0);
+ }
+ }
+
+ /* write the end header */
+ //put_be32(&ctx->pb, ISO_11172_END_CODE);
+ //put_flush_packet(&ctx->pb);
+ 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 */
+ for(i=0;i<p->buf_size;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 - 1;
+ else
+ return 0;
+ }
+ }
+ return 0;
+}
+
+
+typedef struct MpegDemuxContext {
+ int header_state;
+} MpegDemuxContext;
+
+static int find_start_code(ByteIOContext *pb, int *size_ptr,
+ UINT32 *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 get_pts(ByteIOContext *pb, int c)
+{
+ INT64 pts;
+ int val;
+
+ if (c < 0)
+ c = get_byte(pb);
+ pts = (INT64)((c >> 1) & 0x07) << 30;
+ val = get_be16(pb);
+ pts |= (INT64)(val >> 1) << 15;
+ val = get_be16(pb);
+ pts |= (INT64)(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 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;i<s->nb_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 {
+ 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:
+ 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/x-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/x-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/x-mpeg",
+ "vob",
+ sizeof(MpegMuxContext),
+ CODEC_ID_MP2,
+ CODEC_ID_MPEG1VIDEO,
+ mpeg_mux_init,
+ mpeg_mux_write_packet,
+ mpeg_mux_end,
+};
+
+static 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;
+}
diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
new file mode 100644
index 0000000000..2947d960e8
--- /dev/null
+++ b/libavformat/mpegts.c
@@ -0,0 +1,316 @@
+/*
+ * MPEG2 transport stream (aka DVB) demux
+ * Copyright (c) 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 TS_FEC_PACKET_SIZE 204
+#define TS_PACKET_SIZE 188
+#define NB_PID_MAX 8192
+
+enum MpegTSState {
+ MPEGTS_HEADER = 0,
+ MPEGTS_PESHEADER_FILL,
+ MPEGTS_PESHEADER_FLAGS,
+ MPEGTS_PESHEADER_SIZE,
+ MPEGTS_PESHEADER_READ,
+ MPEGTS_PAYLOAD,
+ MPEGTS_SKIP,
+};
+
+/* enough for PES header + length */
+#define MAX_HEADER_SIZE 6
+
+typedef struct MpegTSStream {
+ int pid;
+ enum MpegTSState state;
+ int last_cc; /* last cc code (-1 if first packet) */
+ /* used to get the format */
+ int header_size;
+ int payload_size;
+ int pes_header_size;
+ AVStream *st;
+ unsigned char header[MAX_HEADER_SIZE];
+} MpegTSStream;
+
+typedef struct MpegTSContext {
+ int raw_packet_size; /* raw packet size, including FEC if present */
+ MpegTSStream *pids[NB_PID_MAX];
+} MpegTSContext;
+
+/* autodetect fec presence. Must have at least 1024 bytes */
+static int get_packet_size(const unsigned char *buf, int size)
+{
+ int i;
+
+ if (size < (TS_FEC_PACKET_SIZE * 5 + 1))
+ return -1;
+ for(i=0;i<5;i++) {
+ if (buf[i * TS_PACKET_SIZE] != 0x47)
+ goto try_fec;
+ }
+ return TS_PACKET_SIZE;
+ try_fec:
+ for(i=0;i<5;i++) {
+ if (buf[i * TS_FEC_PACKET_SIZE] != 0x47)
+ return -1;
+ }
+ return TS_FEC_PACKET_SIZE;
+}
+
+static int mpegts_probe(AVProbeData *p)
+{
+ int size;
+ size = get_packet_size(p->buf, p->buf_size);
+ if (size < 0)
+ return 0;
+ return AVPROBE_SCORE_MAX;
+}
+
+static int mpegts_read_header(AVFormatContext *s,
+ AVFormatParameters *ap)
+{
+ MpegTSContext *ts = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ unsigned char buf[1024];
+ int len;
+ INT64 pos;
+
+ /* read the first 1024 bytes to get packet size */
+ pos = url_ftell(pb);
+ len = get_buffer(pb, buf, sizeof(buf));
+ if (len != sizeof(buf))
+ goto fail;
+ ts->raw_packet_size = get_packet_size(buf, sizeof(buf));
+ if (ts->raw_packet_size <= 0)
+ goto fail;
+ /* go again to the start */
+ url_fseek(pb, pos, SEEK_SET);
+ return 0;
+ fail:
+ return -1;
+}
+
+/* return non zero if a packet could be constructed */
+static int mpegts_push_data(AVFormatContext *s, MpegTSStream *tss,
+ AVPacket *pkt,
+ const unsigned char *buf, int buf_size, int is_start)
+{
+ AVStream *st;
+ const unsigned char *p;
+ int len, code, codec_type, codec_id;
+
+ if (is_start) {
+ tss->state = MPEGTS_HEADER;
+ tss->header_size = 0;
+ }
+ p = buf;
+ while (buf_size > 0) {
+ len = buf_size;
+ switch(tss->state) {
+ case MPEGTS_HEADER:
+ if (len > MAX_HEADER_SIZE - tss->header_size)
+ len = MAX_HEADER_SIZE - tss->header_size;
+ memcpy(tss->header, p, len);
+ tss->header_size += len;
+ p += len;
+ buf_size -= len;
+ if (tss->header_size == MAX_HEADER_SIZE) {
+ /* we got all the PES or section header. We can now
+ decide */
+#if 0
+ av_hex_dump(tss->header, tss->header_size);
+#endif
+ if (tss->header[0] == 0x00 && tss->header[1] == 0x00 &&
+ tss->header[2] == 0x01) {
+ /* it must be an mpeg2 PES stream */
+ /* XXX: add AC3 support */
+ code = tss->header[3] | 0x100;
+ if (!((code >= 0x1c0 && code <= 0x1df) ||
+ (code >= 0x1e0 && code <= 0x1ef)))
+ goto skip;
+ if (!tss->st) {
+ /* allocate stream */
+ if (code >= 0x1c0 && code <= 0x1df) {
+ codec_type = CODEC_TYPE_AUDIO;
+ codec_id = CODEC_ID_MP2;
+ } else {
+ codec_type = CODEC_TYPE_VIDEO;
+ codec_id = CODEC_ID_MPEG1VIDEO;
+ }
+ st = av_new_stream(s, tss->pid);
+ if (st) {
+ st->priv_data = tss;
+ st->codec.codec_type = codec_type;
+ st->codec.codec_id = codec_id;
+ tss->st = st;
+ }
+ }
+ tss->state = MPEGTS_PESHEADER_FILL;
+ tss->payload_size = (tss->header[4] << 8) | tss->header[5];
+ if (tss->payload_size == 0)
+ tss->payload_size = 65536;
+ } else {
+ /* otherwise, it should be a table */
+ /* skip packet */
+ skip:
+ tss->state = MPEGTS_SKIP;
+ continue;
+ }
+ }
+ break;
+ /**********************************************/
+ /* PES packing parsing */
+ case MPEGTS_PESHEADER_FILL:
+ /* skip filling */
+ code = *p++;
+ buf_size--;
+ tss->payload_size--;
+ if (code != 0xff) {
+ if ((code & 0xc0) != 0x80)
+ goto skip;
+ tss->state = MPEGTS_PESHEADER_FLAGS;
+ }
+ break;
+ case MPEGTS_PESHEADER_FLAGS:
+ code = *p++;
+ buf_size--;
+ tss->payload_size--;
+ tss->state = MPEGTS_PESHEADER_SIZE;
+ break;
+ case MPEGTS_PESHEADER_SIZE:
+ tss->pes_header_size = *p++;
+ buf_size--;
+ tss->payload_size--;
+ tss->state = MPEGTS_PESHEADER_READ;
+ break;
+ case MPEGTS_PESHEADER_READ:
+ /* currently we do nothing except skipping */
+ if (len > tss->pes_header_size)
+ len = tss->pes_header_size;
+ p += len;
+ buf_size -= len;
+ tss->pes_header_size -= len;
+ tss->payload_size -= len;
+ if (tss->pes_header_size == 0)
+ tss->state = MPEGTS_PAYLOAD;
+ break;
+ case MPEGTS_PAYLOAD:
+ if (len > tss->payload_size)
+ len = tss->payload_size;
+ if (len > 0) {
+ if (tss->st && av_new_packet(pkt, buf_size) == 0) {
+ memcpy(pkt->data, p, buf_size);
+ pkt->stream_index = tss->st->index;
+ return 1;
+ }
+ tss->payload_size -= len;
+ }
+ buf_size = 0;
+ break;
+ case MPEGTS_SKIP:
+ buf_size = 0;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int mpegts_read_packet(AVFormatContext *s,
+ AVPacket *pkt)
+{
+ MpegTSContext *ts = s->priv_data;
+ MpegTSStream *tss;
+ ByteIOContext *pb = &s->pb;
+ unsigned char packet[TS_FEC_PACKET_SIZE];
+ int len, pid, cc, cc_ok, afc;
+ const unsigned char *p;
+
+ for(;;) {
+ len = get_buffer(pb, packet, ts->raw_packet_size);
+ if (len != ts->raw_packet_size)
+ return AVERROR_IO;
+ /* check paquet sync byte */
+ /* XXX: accept to resync ? */
+ if (packet[0] != 0x47)
+ return AVERROR_INVALIDDATA;
+
+ pid = ((packet[1] & 0x1f) << 8) | packet[2];
+ tss = ts->pids[pid];
+ if (tss == NULL) {
+ /* if no pid found, then add a pid context */
+ tss = av_mallocz(sizeof(MpegTSStream));
+ if (!tss)
+ continue;
+ ts->pids[pid] = tss;
+ tss->pid = pid;
+ tss->last_cc = -1;
+ // printf("new pid=0x%x\n", pid);
+ }
+
+ /* continuity check (currently not used) */
+ cc = (packet[3] & 0xf);
+ cc_ok = (tss->last_cc < 0) || ((((tss->last_cc + 1) & 0x0f) == cc));
+ tss->last_cc = cc;
+
+ /* skip adaptation field */
+ afc = (packet[3] >> 4) & 3;
+ p = packet + 4;
+ if (afc == 0) /* reserved value */
+ continue;
+ if (afc == 2) /* adaptation field only */
+ continue;
+ if (afc == 3) {
+ /* skip adapation field */
+ p += p[0] + 1;
+ }
+ /* if past the end of packet, ignore */
+ if (p >= packet + TS_PACKET_SIZE)
+ continue;
+
+ if (mpegts_push_data(s, tss, pkt, p, TS_PACKET_SIZE - (p - packet),
+ packet[1] & 0x40))
+ break;
+ }
+ return 0;
+}
+
+static int mpegts_read_close(AVFormatContext *s)
+{
+ MpegTSContext *ts = s->priv_data;
+ int i;
+ for(i=0;i<NB_PID_MAX;i++)
+ av_free(ts->pids[i]);
+ return 0;
+}
+
+AVInputFormat mpegts_demux = {
+ "mpegts",
+ "MPEG2 transport stream format",
+ sizeof(MpegTSContext),
+ mpegts_probe,
+ mpegts_read_header,
+ mpegts_read_packet,
+ mpegts_read_close,
+ .flags = AVFMT_NOHEADER | AVFMT_SHOW_IDS,
+};
+
+int mpegts_init(void)
+{
+ av_register_input_format(&mpegts_demux);
+ return 0;
+}
diff --git a/libavformat/ogg.c b/libavformat/ogg.c
new file mode 100644
index 0000000000..7c7c96c61f
--- /dev/null
+++ b/libavformat/ogg.c
@@ -0,0 +1,269 @@
+/*
+ * Ogg bitstream support
+ * Mark Hills <mark@pogo.org.uk>
+ *
+ * Uses libogg, but requires libvorbisenc to construct correct headers
+ * when containing Vorbis stream -- currently the only format supported
+ */
+
+#include <stdio.h>
+#include <time.h>
+
+#include <ogg/ogg.h>
+#include <vorbis/vorbisenc.h>
+
+#include "avformat.h"
+#include "oggvorbis.h"
+
+#define DECODER_BUFFER_SIZE 4096
+
+
+typedef struct OggContext {
+ /* output */
+ ogg_stream_state os ;
+ int header_handled ;
+ ogg_int64_t base_packet_no ;
+ ogg_int64_t base_granule_pos ;
+
+ /* input */
+ ogg_sync_state oy ;
+} OggContext ;
+
+
+static int ogg_write_header(AVFormatContext *avfcontext) {
+ OggContext *context ;
+ AVCodecContext *avccontext ;
+ vorbis_info vi ;
+ vorbis_dsp_state vd ;
+ vorbis_comment vc ;
+ vorbis_block vb ;
+ ogg_packet header, header_comm, header_code ;
+ int n ;
+
+ if(!(context = malloc(sizeof(OggContext))))
+ return -1 ;
+ avfcontext->priv_data = context ;
+
+ srand(time(NULL));
+ ogg_stream_init(&context->os, rand());
+
+ for(n = 0 ; n < avfcontext->nb_streams ; n++) {
+ avccontext = &avfcontext->streams[n]->codec ;
+
+ /* begin vorbis specific code */
+
+ vorbis_info_init(&vi) ;
+
+ /* code copied from libavcodec/oggvorbis.c */
+
+ if(oggvorbis_init_encoder(&vi, avccontext) < 0) {
+ fprintf(stderr, "ogg_write_header: init_encoder failed") ;
+ return -1 ;
+ }
+
+ vorbis_analysis_init(&vd, &vi) ;
+ vorbis_block_init(&vd, &vb) ;
+
+ vorbis_comment_init(&vc) ;
+ vorbis_comment_add_tag(&vc, "encoder", "ffmpeg") ;
+ if(*avfcontext->title)
+ vorbis_comment_add_tag(&vc, "title", avfcontext->title) ;
+
+ vorbis_analysis_headerout(&vd, &vc, &header,
+ &header_comm, &header_code) ;
+ ogg_stream_packetin(&context->os, &header) ;
+ ogg_stream_packetin(&context->os, &header_comm) ;
+ ogg_stream_packetin(&context->os, &header_code) ;
+
+ vorbis_comment_clear(&vc) ;
+
+ /* end of vorbis specific code */
+
+ context->header_handled = 0 ;
+ context->base_packet_no = 0 ;
+ }
+
+ return 0 ;
+}
+
+
+static int ogg_write_packet(AVFormatContext *avfcontext,
+ int stream_index,
+ unsigned char *buf, int size, int force_pts)
+{
+ OggContext *context = avfcontext->priv_data ;
+ ogg_packet *op ;
+ ogg_page og ;
+ int l = 0 ;
+
+ /* flush header packets so audio starts on a new page */
+
+ if(!context->header_handled) {
+ while(ogg_stream_flush(&context->os, &og)) {
+ put_buffer(&avfcontext->pb, og.header, og.header_len) ;
+ put_buffer(&avfcontext->pb, og.body, og.body_len) ;
+ put_flush_packet(&avfcontext->pb);
+ }
+ context->header_handled = 1 ;
+ }
+
+ while(l < size) {
+ op = (ogg_packet*)(buf + l) ;
+ op->packet = buf + l + sizeof(ogg_packet) ; /* fix data pointer */
+
+ if(!context->base_packet_no) { /* this is the first packet */
+ context->base_packet_no = op->packetno ;
+ context->base_granule_pos = op->granulepos ;
+ }
+
+ /* correct the fields in the packet -- essential for streaming */
+
+ op->packetno -= context->base_packet_no ;
+ op->granulepos -= context->base_granule_pos ;
+
+ ogg_stream_packetin(&context->os, op) ;
+ l += sizeof(ogg_packet) + op->bytes ;
+
+ while(ogg_stream_pageout(&context->os, &og)) {
+ put_buffer(&avfcontext->pb, og.header, og.header_len) ;
+ put_buffer(&avfcontext->pb, og.body, og.body_len) ;
+ put_flush_packet(&avfcontext->pb);
+ }
+ }
+
+ return 0;
+}
+
+
+static int ogg_write_trailer(AVFormatContext *avfcontext) {
+ OggContext *context = avfcontext->priv_data ;
+ ogg_page og ;
+
+ while(ogg_stream_flush(&context->os, &og)) {
+ put_buffer(&avfcontext->pb, og.header, og.header_len) ;
+ put_buffer(&avfcontext->pb, og.body, og.body_len) ;
+ put_flush_packet(&avfcontext->pb);
+ }
+
+ ogg_stream_clear(&context->os) ;
+ return 0 ;
+}
+
+
+static AVOutputFormat ogg_oformat = {
+ "ogg",
+ "Ogg Vorbis",
+ "audio/x-vorbis",
+ "ogg",
+ sizeof(OggContext),
+ CODEC_ID_VORBIS,
+ 0,
+ ogg_write_header,
+ ogg_write_packet,
+ ogg_write_trailer,
+} ;
+
+
+static int next_packet(AVFormatContext *avfcontext, ogg_packet *op) {
+ OggContext *context = avfcontext->priv_data ;
+ ogg_page og ;
+ char *buf ;
+
+ while(ogg_stream_packetout(&context->os, op) != 1) {
+
+ /* while no pages are available, read in more data to the sync */
+ while(ogg_sync_pageout(&context->oy, &og) != 1) {
+ buf = ogg_sync_buffer(&context->oy, DECODER_BUFFER_SIZE) ;
+ if(get_buffer(&avfcontext->pb, buf, DECODER_BUFFER_SIZE) <= 0)
+ return 1 ;
+ ogg_sync_wrote(&context->oy, DECODER_BUFFER_SIZE) ;
+ }
+
+ /* got a page. Feed it into the stream and get the packet */
+ if(ogg_stream_pagein(&context->os, &og) != 0)
+ return 1 ;
+ }
+
+ return 0 ;
+}
+
+
+static int ogg_read_header(AVFormatContext *avfcontext, AVFormatParameters *ap)
+{
+ OggContext *context ;
+ char *buf ;
+ ogg_page og ;
+ AVStream *ast ;
+
+ if(!(context = malloc(sizeof(OggContext)))) {
+ perror("malloc") ;
+ return -1 ;
+ }
+ avfcontext->priv_data = context ;
+
+ ogg_sync_init(&context->oy) ;
+ buf = ogg_sync_buffer(&context->oy, DECODER_BUFFER_SIZE) ;
+
+ if(get_buffer(&avfcontext->pb, buf, DECODER_BUFFER_SIZE) <= 0)
+ return -EIO ;
+
+ ogg_sync_wrote(&context->oy, DECODER_BUFFER_SIZE) ;
+ ogg_sync_pageout(&context->oy, &og) ;
+ ogg_stream_init(&context->os, ogg_page_serialno(&og)) ;
+ ogg_stream_pagein(&context->os, &og) ;
+
+ /* currently only one vorbis stream supported */
+
+ ast = av_new_stream(avfcontext, 0) ;
+ if(!ast)
+ return AVERROR_NOMEM ;
+
+ ast->codec.codec_type = CODEC_TYPE_AUDIO ;
+ ast->codec.codec_id = CODEC_ID_VORBIS ;
+
+ return 0 ;
+}
+
+
+static int ogg_read_packet(AVFormatContext *avfcontext, AVPacket *pkt) {
+ ogg_packet op ;
+
+ if(next_packet(avfcontext, &op))
+ return -EIO ;
+ if(av_new_packet(pkt, sizeof(ogg_packet) + op.bytes) < 0)
+ return -EIO ;
+ pkt->stream_index = 0 ;
+ memcpy(pkt->data, &op, sizeof(ogg_packet)) ;
+ memcpy(pkt->data + sizeof(ogg_packet), op.packet, op.bytes) ;
+
+ return sizeof(ogg_packet) + op.bytes ;
+}
+
+
+static int ogg_read_close(AVFormatContext *avfcontext) {
+ OggContext *context = avfcontext->priv_data ;
+
+ ogg_stream_clear(&context->os) ;
+ ogg_sync_clear(&context->oy) ;
+
+ return 0 ;
+}
+
+
+static AVInputFormat ogg_iformat = {
+ "ogg",
+ "Ogg Vorbis",
+ sizeof(OggContext),
+ NULL,
+ ogg_read_header,
+ ogg_read_packet,
+ ogg_read_close,
+ .extensions = "ogg",
+} ;
+
+
+int ogg_init(void) {
+ av_register_output_format(&ogg_oformat) ;
+ av_register_input_format(&ogg_iformat);
+ return 0 ;
+}
diff --git a/libavformat/raw.c b/libavformat/raw.c
new file mode 100644
index 0000000000..518206ea56
--- /dev/null
+++ b/libavformat/raw.c
@@ -0,0 +1,509 @@
+/*
+ * RAW encoder and decoder
+ * Copyright (c) 2001 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"
+
+/* simple formats */
+int raw_write_header(struct AVFormatContext *s)
+{
+ return 0;
+}
+
+int raw_write_packet(struct AVFormatContext *s,
+ int stream_index,
+ unsigned char *buf, int size, int force_pts)
+{
+ put_buffer(&s->pb, buf, size);
+ put_flush_packet(&s->pb);
+ return 0;
+}
+
+int raw_write_trailer(struct AVFormatContext *s)
+{
+ return 0;
+}
+
+/* raw input */
+static int raw_read_header(AVFormatContext *s,
+ AVFormatParameters *ap)
+{
+ AVStream *st;
+ int id;
+
+ st = av_new_stream(s, 0);
+ if (!st)
+ return AVERROR_NOMEM;
+ if (ap) {
+ id = s->iformat->value;
+ if (id == CODEC_ID_RAWVIDEO) {
+ st->codec.codec_type = CODEC_TYPE_VIDEO;
+ } else {
+ st->codec.codec_type = CODEC_TYPE_AUDIO;
+ }
+ st->codec.codec_id = id;
+
+ switch(st->codec.codec_type) {
+ case CODEC_TYPE_AUDIO:
+ st->codec.sample_rate = ap->sample_rate;
+ st->codec.channels = ap->channels;
+ break;
+ case CODEC_TYPE_VIDEO:
+ st->codec.frame_rate = ap->frame_rate;
+ st->codec.width = ap->width;
+ st->codec.height = ap->height;
+ break;
+ default:
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
+#define RAW_PACKET_SIZE 1024
+
+int raw_read_packet(AVFormatContext *s,
+ AVPacket *pkt)
+{
+ int ret, size;
+ AVStream *st = s->streams[0];
+
+ size= RAW_PACKET_SIZE;
+
+ if (av_new_packet(pkt, size) < 0)
+ return -EIO;
+
+ pkt->stream_index = 0;
+ ret = get_buffer(&s->pb, pkt->data, size);
+ if (ret <= 0) {
+ av_free_packet(pkt);
+ return -EIO;
+ }
+ /* note: we need to modify the packet size here to handle the last
+ packet */
+ pkt->size = ret;
+ return ret;
+}
+
+int raw_read_close(AVFormatContext *s)
+{
+ return 0;
+}
+
+/* mp3 read */
+static int mp3_read_header(AVFormatContext *s,
+ AVFormatParameters *ap)
+{
+ AVStream *st;
+
+ st = av_new_stream(s, 0);
+ if (!st)
+ return AVERROR_NOMEM;
+
+ st->codec.codec_type = CODEC_TYPE_AUDIO;
+ st->codec.codec_id = CODEC_ID_MP2;
+ /* the parameters will be extracted from the compressed bitstream */
+ return 0;
+}
+
+/* mpeg1/h263 input */
+static int video_read_header(AVFormatContext *s,
+ AVFormatParameters *ap)
+{
+ AVStream *st;
+
+ st = av_new_stream(s, 0);
+ if (!st)
+ return AVERROR_NOMEM;
+
+ st->codec.codec_type = CODEC_TYPE_VIDEO;
+ st->codec.codec_id = s->iformat->value;
+ /* for mjpeg, specify frame rate */
+ /* for mpeg4 specify it too (most mpeg4 streams dont have the fixed_vop_rate set ...)*/
+ if (st->codec.codec_id == CODEC_ID_MJPEG || st->codec.codec_id == CODEC_ID_MPEG4) {
+ if (ap) {
+ st->codec.frame_rate = ap->frame_rate;
+ } else {
+ st->codec.frame_rate = 25 * FRAME_RATE_BASE;
+ }
+ }
+ return 0;
+}
+
+#define SEQ_START_CODE 0x000001b3
+#define GOP_START_CODE 0x000001b8
+#define PICTURE_START_CODE 0x00000100
+
+/* XXX: improve that by looking at several start codes */
+static int mpegvideo_probe(AVProbeData *p)
+{
+ int code, c, i;
+ code = 0xff;
+
+ /* we search the first start code. If it is a sequence, gop or
+ picture start code then we decide it is an mpeg video
+ stream. We do not send highest value to give a chance to mpegts */
+ for(i=0;i<p->buf_size;i++) {
+ c = p->buf[i];
+ code = (code << 8) | c;
+ if ((code & 0xffffff00) == 0x100) {
+ if (code == SEQ_START_CODE ||
+ code == GOP_START_CODE ||
+ code == PICTURE_START_CODE)
+ return 50 - 1;
+ else
+ return 0;
+ }
+ }
+ return 0;
+}
+
+AVInputFormat mp3_iformat = {
+ "mp3",
+ "MPEG audio",
+ 0,
+ NULL,
+ mp3_read_header,
+ raw_read_packet,
+ raw_read_close,
+ .extensions = "mp2,mp3", /* XXX: use probe */
+};
+
+AVOutputFormat mp2_oformat = {
+ "mp2",
+ "MPEG audio layer 2",
+ "audio/x-mpeg",
+ "mp2,mp3",
+ 0,
+ CODEC_ID_MP2,
+ 0,
+ raw_write_header,
+ raw_write_packet,
+ raw_write_trailer,
+};
+
+
+AVInputFormat ac3_iformat = {
+ "ac3",
+ "raw ac3",
+ 0,
+ NULL,
+ raw_read_header,
+ raw_read_packet,
+ raw_read_close,
+ .extensions = "ac3",
+ .value = CODEC_ID_AC3,
+};
+
+AVOutputFormat ac3_oformat = {
+ "ac3",
+ "raw ac3",
+ "audio/x-ac3",
+ "ac3",
+ 0,
+ CODEC_ID_AC3,
+ 0,
+ raw_write_header,
+ raw_write_packet,
+ raw_write_trailer,
+};
+
+AVOutputFormat h263_oformat = {
+ "h263",
+ "raw h263",
+ "video/x-h263",
+ "h263",
+ 0,
+ 0,
+ CODEC_ID_H263,
+ raw_write_header,
+ raw_write_packet,
+ raw_write_trailer,
+};
+
+AVInputFormat m4v_iformat = {
+ "m4v",
+ "raw MPEG4 video format",
+ 0,
+ NULL /*mpegvideo_probe*/,
+ video_read_header,
+ raw_read_packet,
+ raw_read_close,
+ .extensions = "m4v", //FIXME remove after writing mpeg4_probe
+ .value = CODEC_ID_MPEG4,
+};
+
+AVOutputFormat m4v_oformat = {
+ "m4v",
+ "raw MPEG4 video format",
+ NULL,
+ "m4v",
+ 0,
+ CODEC_ID_NONE,
+ CODEC_ID_MPEG4,
+ raw_write_header,
+ raw_write_packet,
+ raw_write_trailer,
+};
+
+AVInputFormat mpegvideo_iformat = {
+ "mpegvideo",
+ "MPEG video",
+ 0,
+ mpegvideo_probe,
+ video_read_header,
+ raw_read_packet,
+ raw_read_close,
+ .value = CODEC_ID_MPEG1VIDEO,
+};
+
+AVOutputFormat mpeg1video_oformat = {
+ "mpeg1video",
+ "MPEG video",
+ "video/x-mpeg",
+ "mpg,mpeg",
+ 0,
+ 0,
+ CODEC_ID_MPEG1VIDEO,
+ raw_write_header,
+ raw_write_packet,
+ raw_write_trailer,
+};
+
+AVInputFormat mjpeg_iformat = {
+ "mjpeg",
+ "MJPEG video",
+ 0,
+ NULL,
+ video_read_header,
+ raw_read_packet,
+ raw_read_close,
+ .extensions = "mjpg,mjpeg",
+ .value = CODEC_ID_MJPEG,
+};
+
+AVOutputFormat mjpeg_oformat = {
+ "mjpeg",
+ "MJPEG video",
+ "video/x-mjpeg",
+ "mjpg,mjpeg",
+ 0,
+ 0,
+ CODEC_ID_MJPEG,
+ raw_write_header,
+ raw_write_packet,
+ raw_write_trailer,
+};
+
+/* pcm formats */
+
+#define PCMDEF(name, long_name, ext, codec) \
+AVInputFormat pcm_ ## name ## _iformat = {\
+ #name,\
+ long_name,\
+ 0,\
+ NULL,\
+ raw_read_header,\
+ raw_read_packet,\
+ raw_read_close,\
+ .extensions = ext,\
+ .value = codec,\
+};\
+\
+AVOutputFormat pcm_ ## name ## _oformat = {\
+ #name,\
+ long_name,\
+ NULL,\
+ ext,\
+ 0,\
+ codec,\
+ 0,\
+ raw_write_header,\
+ raw_write_packet,\
+ raw_write_trailer,\
+};
+
+#ifdef WORDS_BIGENDIAN
+#define BE_DEF(s) s
+#define LE_DEF(s) NULL
+#else
+#define BE_DEF(s) NULL
+#define LE_DEF(s) s
+#endif
+
+
+PCMDEF(s16le, "pcm signed 16 bit little endian format",
+ LE_DEF("sw"), CODEC_ID_PCM_S16LE)
+
+PCMDEF(s16be, "pcm signed 16 bit big endian format",
+ BE_DEF("sw"), CODEC_ID_PCM_S16BE)
+
+PCMDEF(u16le, "pcm unsigned 16 bit little endian format",
+ LE_DEF("uw"), CODEC_ID_PCM_U16LE)
+
+PCMDEF(u16be, "pcm unsigned 16 bit big endian format",
+ BE_DEF("uw"), CODEC_ID_PCM_U16BE)
+
+PCMDEF(s8, "pcm signed 8 bit format",
+ "sb", CODEC_ID_PCM_S8)
+
+PCMDEF(u8, "pcm unsigned 8 bit format",
+ "ub", CODEC_ID_PCM_U8)
+
+PCMDEF(mulaw, "pcm mu law format",
+ "ul", CODEC_ID_PCM_MULAW)
+
+PCMDEF(alaw, "pcm A law format",
+ "al", CODEC_ID_PCM_ALAW)
+
+int rawvideo_read_packet(AVFormatContext *s,
+ AVPacket *pkt)
+{
+ int packet_size, ret, width, height;
+ AVStream *st = s->streams[0];
+
+ width = st->codec.width;
+ height = st->codec.height;
+
+ switch(st->codec.pix_fmt) {
+ case PIX_FMT_YUV420P:
+ packet_size = (width * height * 3) / 2;
+ break;
+ case PIX_FMT_YUV422:
+ packet_size = (width * height * 2);
+ break;
+ case PIX_FMT_BGR24:
+ case PIX_FMT_RGB24:
+ packet_size = (width * height * 3);
+ break;
+ default:
+ av_abort();
+ break;
+ }
+
+ if (av_new_packet(pkt, packet_size) < 0)
+ return -EIO;
+
+ pkt->stream_index = 0;
+#if 0
+ /* bypass buffered I/O */
+ ret = url_read(url_fileno(&s->pb), pkt->data, pkt->size);
+#else
+ ret = get_buffer(&s->pb, pkt->data, pkt->size);
+#endif
+ if (ret != pkt->size) {
+ av_free_packet(pkt);
+ return -EIO;
+ } else {
+ return 0;
+ }
+}
+
+AVInputFormat rawvideo_iformat = {
+ "rawvideo",
+ "raw video format",
+ 0,
+ NULL,
+ raw_read_header,
+ rawvideo_read_packet,
+ raw_read_close,
+ .extensions = "yuv",
+ .value = CODEC_ID_RAWVIDEO,
+};
+
+AVOutputFormat rawvideo_oformat = {
+ "rawvideo",
+ "raw video format",
+ NULL,
+ "yuv",
+ 0,
+ CODEC_ID_NONE,
+ CODEC_ID_RAWVIDEO,
+ raw_write_header,
+ raw_write_packet,
+ raw_write_trailer,
+};
+
+static int null_write_packet(struct AVFormatContext *s,
+ int stream_index,
+ unsigned char *buf, int size, int force_pts)
+{
+ return 0;
+}
+
+AVOutputFormat null_oformat = {
+ "null",
+ "null video format",
+ NULL,
+ NULL,
+ 0,
+#ifdef WORDS_BIGENDIAN
+ CODEC_ID_PCM_S16BE,
+#else
+ CODEC_ID_PCM_S16LE,
+#endif
+ CODEC_ID_RAWVIDEO,
+ raw_write_header,
+ null_write_packet,
+ raw_write_trailer,
+ .flags = AVFMT_NOFILE | AVFMT_RAWPICTURE,
+};
+
+int raw_init(void)
+{
+ av_register_input_format(&mp3_iformat);
+ av_register_output_format(&mp2_oformat);
+
+ av_register_input_format(&ac3_iformat);
+ av_register_output_format(&ac3_oformat);
+
+ av_register_output_format(&h263_oformat);
+
+ av_register_input_format(&m4v_iformat);
+ av_register_output_format(&m4v_oformat);
+
+ av_register_input_format(&mpegvideo_iformat);
+ av_register_output_format(&mpeg1video_oformat);
+
+ av_register_input_format(&mjpeg_iformat);
+ av_register_output_format(&mjpeg_oformat);
+
+ av_register_input_format(&pcm_s16le_iformat);
+ av_register_output_format(&pcm_s16le_oformat);
+ av_register_input_format(&pcm_s16be_iformat);
+ av_register_output_format(&pcm_s16be_oformat);
+ av_register_input_format(&pcm_u16le_iformat);
+ av_register_output_format(&pcm_u16le_oformat);
+ av_register_input_format(&pcm_u16be_iformat);
+ av_register_output_format(&pcm_u16be_oformat);
+ av_register_input_format(&pcm_s8_iformat);
+ av_register_output_format(&pcm_s8_oformat);
+ av_register_input_format(&pcm_u8_iformat);
+ av_register_output_format(&pcm_u8_oformat);
+ av_register_input_format(&pcm_mulaw_iformat);
+ av_register_output_format(&pcm_mulaw_oformat);
+ av_register_input_format(&pcm_alaw_iformat);
+ av_register_output_format(&pcm_alaw_oformat);
+
+ av_register_input_format(&rawvideo_iformat);
+ av_register_output_format(&rawvideo_oformat);
+
+ av_register_output_format(&null_oformat);
+ return 0;
+}
diff --git a/libavformat/rm.c b/libavformat/rm.c
new file mode 100644
index 0000000000..be90a27c89
--- /dev/null
+++ b/libavformat/rm.c
@@ -0,0 +1,773 @@
+/*
+ * "Real" compatible mux and demux.
+ * Copyright (c) 2000, 2001 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"
+
+/* in ms */
+#define BUFFER_DURATION 0
+
+typedef struct {
+ int nb_packets;
+ int packet_total_size;
+ int packet_max_size;
+ /* codec related output */
+ int bit_rate;
+ float frame_rate;
+ int nb_frames; /* current frame number */
+ int total_frames; /* total number of frames */
+ int num;
+ AVCodecContext *enc;
+} StreamInfo;
+
+typedef struct {
+ StreamInfo streams[2];
+ StreamInfo *audio_stream, *video_stream;
+ int data_pos; /* position of the data after the header */
+ int nb_packets;
+} RMContext;
+
+static void put_str(ByteIOContext *s, const char *tag)
+{
+ put_be16(s,strlen(tag));
+ while (*tag) {
+ put_byte(s, *tag++);
+ }
+}
+
+static void put_str8(ByteIOContext *s, const char *tag)
+{
+ put_byte(s, strlen(tag));
+ while (*tag) {
+ put_byte(s, *tag++);
+ }
+}
+
+static void rv10_write_header(AVFormatContext *ctx,
+ int data_size, int index_pos)
+{
+ RMContext *rm = ctx->priv_data;
+ ByteIOContext *s = &ctx->pb;
+ StreamInfo *stream;
+ unsigned char *data_offset_ptr, *start_ptr;
+ const char *desc, *mimetype;
+ int nb_packets, packet_total_size, packet_max_size, size, packet_avg_size, i;
+ int bit_rate, v, duration, flags, data_pos;
+
+ start_ptr = s->buf_ptr;
+
+ put_tag(s, ".RMF");
+ put_be32(s,18); /* header size */
+ put_be16(s,0);
+ put_be32(s,0);
+ put_be32(s,4 + ctx->nb_streams); /* num headers */
+
+ put_tag(s,"PROP");
+ put_be32(s, 50);
+ put_be16(s, 0);
+ packet_max_size = 0;
+ packet_total_size = 0;
+ nb_packets = 0;
+ bit_rate = 0;
+ duration = 0;
+ for(i=0;i<ctx->nb_streams;i++) {
+ StreamInfo *stream = &rm->streams[i];
+ bit_rate += stream->bit_rate;
+ if (stream->packet_max_size > packet_max_size)
+ packet_max_size = stream->packet_max_size;
+ nb_packets += stream->nb_packets;
+ packet_total_size += stream->packet_total_size;
+ /* select maximum duration */
+ v = (int) (1000.0 * (float)stream->total_frames / stream->frame_rate);
+ if (v > duration)
+ duration = v;
+ }
+ put_be32(s, bit_rate); /* max bit rate */
+ put_be32(s, bit_rate); /* avg bit rate */
+ put_be32(s, packet_max_size); /* max packet size */
+ if (nb_packets > 0)
+ packet_avg_size = packet_total_size / nb_packets;
+ else
+ packet_avg_size = 0;
+ put_be32(s, packet_avg_size); /* avg packet size */
+ put_be32(s, nb_packets); /* num packets */
+ put_be32(s, duration); /* duration */
+ put_be32(s, BUFFER_DURATION); /* preroll */
+ put_be32(s, index_pos); /* index offset */
+ /* computation of data the data offset */
+ data_offset_ptr = s->buf_ptr;
+ put_be32(s, 0); /* data offset : will be patched after */
+ put_be16(s, ctx->nb_streams); /* num streams */
+ flags = 1 | 2; /* save allowed & perfect play */
+ if (url_is_streamed(s))
+ flags |= 4; /* live broadcast */
+ put_be16(s, flags);
+
+ /* comments */
+
+ put_tag(s,"CONT");
+ size = strlen(ctx->title) + strlen(ctx->author) + strlen(ctx->copyright) +
+ strlen(ctx->comment) + 4 * 2 + 10;
+ put_be32(s,size);
+ put_be16(s,0);
+ put_str(s, ctx->title);
+ put_str(s, ctx->author);
+ put_str(s, ctx->copyright);
+ put_str(s, ctx->comment);
+
+ for(i=0;i<ctx->nb_streams;i++) {
+ int codec_data_size;
+
+ stream = &rm->streams[i];
+
+ if (stream->enc->codec_type == CODEC_TYPE_AUDIO) {
+ desc = "The Audio Stream";
+ mimetype = "audio/x-pn-realaudio";
+ codec_data_size = 73;
+ } else {
+ desc = "The Video Stream";
+ mimetype = "video/x-pn-realvideo";
+ codec_data_size = 34;
+ }
+
+ put_tag(s,"MDPR");
+ size = 10 + 9 * 4 + strlen(desc) + strlen(mimetype) + codec_data_size;
+ put_be32(s, size);
+ put_be16(s, 0);
+
+ put_be16(s, i); /* stream number */
+ put_be32(s, stream->bit_rate); /* max bit rate */
+ put_be32(s, stream->bit_rate); /* avg bit rate */
+ put_be32(s, stream->packet_max_size); /* max packet size */
+ if (stream->nb_packets > 0)
+ packet_avg_size = stream->packet_total_size /
+ stream->nb_packets;
+ else
+ packet_avg_size = 0;
+ put_be32(s, packet_avg_size); /* avg packet size */
+ put_be32(s, 0); /* start time */
+ put_be32(s, BUFFER_DURATION); /* preroll */
+ /* duration */
+ if (url_is_streamed(s) || !stream->total_frames)
+ put_be32(s, (int)(3600 * 1000));
+ else
+ put_be32(s, (int)(stream->total_frames * 1000 / stream->frame_rate));
+ put_str8(s, desc);
+ put_str8(s, mimetype);
+ put_be32(s, codec_data_size);
+
+ if (stream->enc->codec_type == CODEC_TYPE_AUDIO) {
+ int coded_frame_size, fscode, sample_rate;
+ sample_rate = stream->enc->sample_rate;
+ coded_frame_size = (stream->enc->bit_rate *
+ stream->enc->frame_size) / (8 * sample_rate);
+ /* audio codec info */
+ put_tag(s, ".ra");
+ put_byte(s, 0xfd);
+ put_be32(s, 0x00040000); /* version */
+ put_tag(s, ".ra4");
+ put_be32(s, 0x01b53530); /* stream length */
+ put_be16(s, 4); /* unknown */
+ put_be32(s, 0x39); /* header size */
+
+ switch(sample_rate) {
+ case 48000:
+ case 24000:
+ case 12000:
+ fscode = 1;
+ break;
+ default:
+ case 44100:
+ case 22050:
+ case 11025:
+ fscode = 2;
+ break;
+ case 32000:
+ case 16000:
+ case 8000:
+ fscode = 3;
+ }
+ put_be16(s, fscode); /* codec additional info, for AC3, seems
+ to be a frequency code */
+ /* special hack to compensate rounding errors... */
+ if (coded_frame_size == 557)
+ coded_frame_size--;
+ put_be32(s, coded_frame_size); /* frame length */
+ put_be32(s, 0x51540); /* unknown */
+ put_be32(s, 0x249f0); /* unknown */
+ put_be32(s, 0x249f0); /* unknown */
+ put_be16(s, 0x01);
+ /* frame length : seems to be very important */
+ put_be16(s, coded_frame_size);
+ put_be32(s, 0); /* unknown */
+ put_be16(s, stream->enc->sample_rate); /* sample rate */
+ put_be32(s, 0x10); /* unknown */
+ put_be16(s, stream->enc->channels);
+ put_str8(s, "Int0"); /* codec name */
+ put_str8(s, "dnet"); /* codec name */
+ put_be16(s, 0); /* title length */
+ put_be16(s, 0); /* author length */
+ put_be16(s, 0); /* copyright length */
+ put_byte(s, 0); /* end of header */
+ } else {
+ /* video codec info */
+ put_be32(s,34); /* size */
+ put_tag(s,"VIDORV10");
+ put_be16(s, stream->enc->width);
+ put_be16(s, stream->enc->height);
+ put_be16(s, (int) stream->frame_rate); /* frames per seconds ? */
+ put_be32(s,0); /* unknown meaning */
+ put_be16(s, (int) stream->frame_rate); /* unknown meaning */
+ put_be32(s,0); /* unknown meaning */
+ put_be16(s, 8); /* unknown meaning */
+ /* Seems to be the codec version: only use basic H263. The next
+ versions seems to add a diffential DC coding as in
+ MPEG... nothing new under the sun */
+ put_be32(s,0x10000000);
+ //put_be32(s,0x10003000);
+ }
+ }
+
+ /* patch data offset field */
+ data_pos = s->buf_ptr - start_ptr;
+ rm->data_pos = data_pos;
+ data_offset_ptr[0] = data_pos >> 24;
+ data_offset_ptr[1] = data_pos >> 16;
+ data_offset_ptr[2] = data_pos >> 8;
+ data_offset_ptr[3] = data_pos;
+
+ /* data stream */
+ put_tag(s,"DATA");
+ put_be32(s,data_size + 10 + 8);
+ put_be16(s,0);
+
+ put_be32(s, nb_packets); /* number of packets */
+ put_be32(s,0); /* next data header */
+}
+
+static void write_packet_header(AVFormatContext *ctx, StreamInfo *stream,
+ int length, int key_frame)
+{
+ int timestamp;
+ ByteIOContext *s = &ctx->pb;
+
+ stream->nb_packets++;
+ stream->packet_total_size += length;
+ if (length > stream->packet_max_size)
+ stream->packet_max_size = length;
+
+ put_be16(s,0); /* version */
+ put_be16(s,length + 12);
+ put_be16(s, stream->num); /* stream number */
+ timestamp = (1000 * (float)stream->nb_frames) / stream->frame_rate;
+ put_be32(s, timestamp); /* timestamp */
+ put_byte(s, 0); /* reserved */
+ put_byte(s, key_frame ? 2 : 0); /* flags */
+}
+
+static int rm_write_header(AVFormatContext *s)
+{
+ RMContext *rm = s->priv_data;
+ StreamInfo *stream;
+ int n;
+ AVCodecContext *codec;
+
+ for(n=0;n<s->nb_streams;n++) {
+ s->streams[n]->id = n;
+ codec = &s->streams[n]->codec;
+ stream = &rm->streams[n];
+ memset(stream, 0, sizeof(StreamInfo));
+ stream->num = n;
+ stream->bit_rate = codec->bit_rate;
+ stream->enc = codec;
+
+ switch(codec->codec_type) {
+ case CODEC_TYPE_AUDIO:
+ rm->audio_stream = stream;
+ stream->frame_rate = (float)codec->sample_rate / (float)codec->frame_size;
+ /* XXX: dummy values */
+ stream->packet_max_size = 1024;
+ stream->nb_packets = 0;
+ stream->total_frames = stream->nb_packets;
+ break;
+ case CODEC_TYPE_VIDEO:
+ rm->video_stream = stream;
+ stream->frame_rate = (float)codec->frame_rate / (float)FRAME_RATE_BASE;
+ /* XXX: dummy values */
+ stream->packet_max_size = 4096;
+ stream->nb_packets = 0;
+ stream->total_frames = stream->nb_packets;
+ break;
+ default:
+ av_abort();
+ }
+ }
+
+ rv10_write_header(s, 0, 0);
+ put_flush_packet(&s->pb);
+ return 0;
+}
+
+static int rm_write_audio(AVFormatContext *s, UINT8 *buf, int size)
+{
+ UINT8 *buf1;
+ RMContext *rm = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ StreamInfo *stream = rm->audio_stream;
+ int i;
+
+ /* XXX: suppress this malloc */
+ buf1= (UINT8*) av_malloc( size * sizeof(UINT8) );
+
+ write_packet_header(s, stream, size, stream->enc->key_frame);
+
+ /* for AC3, the words seems to be reversed */
+ for(i=0;i<size;i+=2) {
+ buf1[i] = buf[i+1];
+ buf1[i+1] = buf[i];
+ }
+ put_buffer(pb, buf1, size);
+ put_flush_packet(pb);
+ stream->nb_frames++;
+ av_free(buf1);
+ return 0;
+}
+
+static int rm_write_video(AVFormatContext *s, UINT8 *buf, int size)
+{
+ RMContext *rm = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ StreamInfo *stream = rm->video_stream;
+ int key_frame = stream->enc->key_frame;
+
+ /* XXX: this is incorrect: should be a parameter */
+
+ /* Well, I spent some time finding the meaning of these bits. I am
+ not sure I understood everything, but it works !! */
+#if 1
+ write_packet_header(s, stream, size + 7, key_frame);
+ /* bit 7: '1' if final packet of a frame converted in several packets */
+ put_byte(pb, 0x81);
+ /* bit 7: '1' if I frame. bits 6..0 : sequence number in current
+ frame starting from 1 */
+ if (key_frame) {
+ put_byte(pb, 0x81);
+ } else {
+ put_byte(pb, 0x01);
+ }
+ put_be16(pb, 0x4000 | (size)); /* total frame size */
+ put_be16(pb, 0x4000 | (size)); /* offset from the start or the end */
+#else
+ /* full frame */
+ write_packet_header(s, size + 6);
+ put_byte(pb, 0xc0);
+ put_be16(pb, 0x4000 | size); /* total frame size */
+ put_be16(pb, 0x4000 + packet_number * 126); /* position in stream */
+#endif
+ put_byte(pb, stream->nb_frames & 0xff);
+
+ put_buffer(pb, buf, size);
+ put_flush_packet(pb);
+
+ stream->nb_frames++;
+ return 0;
+}
+
+static int rm_write_packet(AVFormatContext *s, int stream_index,
+ UINT8 *buf, int size, int force_pts)
+{
+ if (s->streams[stream_index]->codec.codec_type ==
+ CODEC_TYPE_AUDIO)
+ return rm_write_audio(s, buf, size);
+ else
+ return rm_write_video(s, buf, size);
+}
+
+static int rm_write_trailer(AVFormatContext *s)
+{
+ RMContext *rm = s->priv_data;
+ int data_size, index_pos, i;
+ ByteIOContext *pb = &s->pb;
+
+ if (!url_is_streamed(&s->pb)) {
+ /* end of file: finish to write header */
+ index_pos = url_fseek(pb, 0, SEEK_CUR);
+ data_size = index_pos - rm->data_pos;
+
+ /* index */
+ put_tag(pb, "INDX");
+ put_be32(pb, 10 + 10 * s->nb_streams);
+ put_be16(pb, 0);
+
+ for(i=0;i<s->nb_streams;i++) {
+ put_be32(pb, 0); /* zero indices */
+ put_be16(pb, i); /* stream number */
+ put_be32(pb, 0); /* next index */
+ }
+ /* undocumented end header */
+ put_be32(pb, 0);
+ put_be32(pb, 0);
+
+ url_fseek(pb, 0, SEEK_SET);
+ for(i=0;i<s->nb_streams;i++)
+ rm->streams[i].total_frames = rm->streams[i].nb_frames;
+ rv10_write_header(s, data_size, index_pos);
+ } else {
+ /* undocumented end header */
+ put_be32(pb, 0);
+ put_be32(pb, 0);
+ }
+ put_flush_packet(pb);
+ return 0;
+}
+
+/***************************************************/
+
+static void get_str(ByteIOContext *pb, char *buf, int buf_size)
+{
+ int len, i;
+ char *q;
+
+ len = get_be16(pb);
+ q = buf;
+ for(i=0;i<len;i++) {
+ if (i < buf_size - 1)
+ *q++ = get_byte(pb);
+ }
+ *q = '\0';
+}
+
+static void get_str8(ByteIOContext *pb, char *buf, int buf_size)
+{
+ int len, i;
+ char *q;
+
+ len = get_byte(pb);
+ q = buf;
+ for(i=0;i<len;i++) {
+ if (i < buf_size - 1)
+ *q++ = get_byte(pb);
+ }
+ *q = '\0';
+}
+
+static int rm_read_header(AVFormatContext *s, AVFormatParameters *ap)
+{
+ RMContext *rm = s->priv_data;
+ AVStream *st;
+ ByteIOContext *pb = &s->pb;
+ unsigned int tag, v;
+ int tag_size, size, codec_data_size, i;
+ INT64 codec_pos;
+ unsigned int h263_hack_version;
+ char buf[128];
+ int flags = 0;
+
+ if (get_le32(pb) != MKTAG('.', 'R', 'M', 'F'))
+ return -EIO;
+
+ get_be32(pb); /* header size */
+ get_be16(pb);
+ get_be32(pb);
+ get_be32(pb); /* number of headers */
+
+ for(;;) {
+ if (url_feof(pb))
+ goto fail;
+ tag = get_le32(pb);
+ tag_size = get_be32(pb);
+ get_be16(pb);
+#if 0
+ printf("tag=%c%c%c%c (%08x) size=%d\n",
+ (tag) & 0xff,
+ (tag >> 8) & 0xff,
+ (tag >> 16) & 0xff,
+ (tag >> 24) & 0xff,
+ tag,
+ tag_size);
+#endif
+ if (tag_size < 10)
+ goto fail;
+ switch(tag) {
+ case MKTAG('P', 'R', 'O', 'P'):
+ /* file header */
+ get_be32(pb); /* max bit rate */
+ get_be32(pb); /* avg bit rate */
+ get_be32(pb); /* max packet size */
+ get_be32(pb); /* avg packet size */
+ get_be32(pb); /* nb packets */
+ get_be32(pb); /* duration */
+ get_be32(pb); /* preroll */
+ get_be32(pb); /* index offset */
+ get_be32(pb); /* data offset */
+ get_be16(pb); /* nb streams */
+ flags = get_be16(pb); /* flags */
+ break;
+ case MKTAG('C', 'O', 'N', 'T'):
+ get_str(pb, s->title, sizeof(s->title));
+ get_str(pb, s->author, sizeof(s->author));
+ get_str(pb, s->copyright, sizeof(s->copyright));
+ get_str(pb, s->comment, sizeof(s->comment));
+ break;
+ case MKTAG('M', 'D', 'P', 'R'):
+ st = av_mallocz(sizeof(AVStream));
+ if (!st)
+ goto fail;
+ s->streams[s->nb_streams++] = st;
+ st->id = get_be16(pb);
+ get_be32(pb); /* max bit rate */
+ st->codec.bit_rate = get_be32(pb); /* bit rate */
+ get_be32(pb); /* max packet size */
+ get_be32(pb); /* avg packet size */
+ get_be32(pb); /* start time */
+ get_be32(pb); /* preroll */
+ get_be32(pb); /* duration */
+ get_str8(pb, buf, sizeof(buf)); /* desc */
+ get_str8(pb, buf, sizeof(buf)); /* mimetype */
+ codec_data_size = get_be32(pb);
+ codec_pos = url_ftell(pb);
+
+ v = get_be32(pb);
+ if (v == MKTAG(0xfd, 'a', 'r', '.')) {
+ /* ra type header */
+ get_be32(pb); /* version */
+ get_be32(pb); /* .ra4 */
+ get_be32(pb);
+ get_be16(pb);
+ get_be32(pb); /* header size */
+ get_be16(pb); /* add codec info */
+ get_be32(pb); /* coded frame size */
+ get_be32(pb); /* ??? */
+ get_be32(pb); /* ??? */
+ get_be32(pb); /* ??? */
+ get_be16(pb); /* 1 */
+ get_be16(pb); /* coded frame size */
+ get_be32(pb);
+ st->codec.sample_rate = get_be16(pb);
+ get_be32(pb);
+ st->codec.channels = get_be16(pb);
+ get_str8(pb, buf, sizeof(buf)); /* desc */
+ get_str8(pb, buf, sizeof(buf)); /* desc */
+ st->codec.codec_type = CODEC_TYPE_AUDIO;
+ if (!strcmp(buf, "dnet")) {
+ st->codec.codec_id = CODEC_ID_AC3;
+ } else {
+ st->codec.codec_id = CODEC_ID_NONE;
+ pstrcpy(st->codec.codec_name, sizeof(st->codec.codec_name),
+ buf);
+ }
+ } else {
+ if (get_le32(pb) != MKTAG('V', 'I', 'D', 'O')) {
+ fail1:
+ fprintf(stderr, "Unsupported video codec\n");
+ goto fail;
+ }
+ st->codec.codec_tag = get_le32(pb);
+ if (st->codec.codec_tag != MKTAG('R', 'V', '1', '0'))
+ goto fail1;
+ st->codec.width = get_be16(pb);
+ st->codec.height = get_be16(pb);
+ st->codec.frame_rate = get_be16(pb) * FRAME_RATE_BASE;
+ st->codec.codec_type = CODEC_TYPE_VIDEO;
+ get_be32(pb);
+ get_be16(pb);
+ get_be32(pb);
+ get_be16(pb);
+ /* modification of h263 codec version (!) */
+ h263_hack_version = get_be32(pb);
+ switch(h263_hack_version) {
+ case 0x10000000:
+ case 0x10003000:
+ case 0x10003001:
+ st->codec.sub_id = h263_hack_version;
+ st->codec.codec_id = CODEC_ID_RV10;
+ break;
+ default:
+ /* not handled */
+ st->codec.codec_id = CODEC_ID_NONE;
+ break;
+ }
+ }
+ /* skip codec info */
+ size = url_ftell(pb) - codec_pos;
+ url_fskip(pb, codec_data_size - size);
+ break;
+ case MKTAG('D', 'A', 'T', 'A'):
+ goto header_end;
+ default:
+ /* unknown tag: skip it */
+ url_fskip(pb, tag_size - 10);
+ break;
+ }
+ }
+ header_end:
+ rm->nb_packets = get_be32(pb); /* number of packets */
+ if (!rm->nb_packets && (flags & 4))
+ rm->nb_packets = 3600 * 25;
+ get_be32(pb); /* next data header */
+ return 0;
+
+ fail:
+ for(i=0;i<s->nb_streams;i++) {
+ av_free(s->streams[i]);
+ }
+ return -EIO;
+}
+
+static int get_num(ByteIOContext *pb, int *len)
+{
+ int n, n1;
+
+ n = get_be16(pb);
+ (*len)-=2;
+ if (n >= 0x4000) {
+ return n - 0x4000;
+ } else {
+ n1 = get_be16(pb);
+ (*len)-=2;
+ return (n << 16) | n1;
+ }
+}
+
+static int rm_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ RMContext *rm = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ AVStream *st;
+ int len, num, timestamp, i, tmp, j;
+ UINT8 *ptr;
+ int flags;
+
+ redo:
+ if (rm->nb_packets == 0)
+ return -EIO;
+ get_be16(pb);
+ len = get_be16(pb);
+ if (len < 12)
+ return -EIO;
+ num = get_be16(pb);
+ timestamp = get_be32(pb);
+ get_byte(pb); /* reserved */
+ flags = get_byte(pb); /* flags */
+ rm->nb_packets--;
+ len -= 12;
+
+ st = NULL;
+ for(i=0;i<s->nb_streams;i++) {
+ st = s->streams[i];
+ if (num == st->id)
+ break;
+ }
+ if (i == s->nb_streams) {
+ /* skip packet if unknown number */
+ url_fskip(pb, len);
+ goto redo;
+ }
+
+ if (st->codec.codec_type == CODEC_TYPE_VIDEO) {
+ int full_frame, h, pic_num;
+
+ h= get_byte(pb);
+ if ((h & 0xc0) == 0xc0) {
+ int len2, pos;
+ full_frame = 1;
+ len2= get_num(pb, &len);
+ pos = get_num(pb, &len);
+ //printf("pos:%d\n",len);
+ len -= 2;
+ } else {
+ int seq, frame_size, pos;
+ full_frame = 0;
+ seq = get_byte(pb);
+ frame_size = get_num(pb, &len);
+ pos = get_num(pb, &len);
+ //printf("seq:%d, size:%d, pos:%d\n",seq,frame_size,pos);
+ len -= 3;
+ }
+ /* picture number */
+ pic_num= get_byte(pb);
+
+ //XXX/FIXME/HACK, demuxer should be fixed to send complete frames ...
+ if(st->codec.slice_offset==NULL) st->codec.slice_offset= (int*)malloc(sizeof(int));
+ st->codec.slice_count= full_frame;
+ st->codec.slice_offset[0]= 0;
+ }
+
+ av_new_packet(pkt, len);
+ pkt->stream_index = i;
+ get_buffer(pb, pkt->data, len);
+
+ /* for AC3, needs to swap bytes */
+ if (st->codec.codec_id == CODEC_ID_AC3) {
+ ptr = pkt->data;
+ for(j=0;j<len;j+=2) {
+ tmp = ptr[0];
+ ptr[0] = ptr[1];
+ ptr[1] = tmp;
+ ptr += 2;
+ }
+ }
+ return 0;
+}
+
+static int rm_read_close(AVFormatContext *s)
+{
+ return 0;
+}
+
+static int rm_probe(AVProbeData *p)
+{
+ /* check file header */
+ if (p->buf_size <= 32)
+ return 0;
+ if (p->buf[0] == '.' && p->buf[1] == 'R' &&
+ p->buf[2] == 'M' && p->buf[3] == 'F' &&
+ p->buf[4] == 0 && p->buf[5] == 0)
+ return AVPROBE_SCORE_MAX;
+ else
+ return 0;
+}
+
+static AVInputFormat rm_iformat = {
+ "rm",
+ "rm format",
+ sizeof(RMContext),
+ rm_probe,
+ rm_read_header,
+ rm_read_packet,
+ rm_read_close,
+};
+
+static AVOutputFormat rm_oformat = {
+ "rm",
+ "rm format",
+ "audio/x-pn-realaudio",
+ "rm,ra",
+ sizeof(RMContext),
+ CODEC_ID_AC3,
+ CODEC_ID_RV10,
+ rm_write_header,
+ rm_write_packet,
+ rm_write_trailer,
+};
+
+int rm_init(void)
+{
+ av_register_input_format(&rm_iformat);
+ av_register_output_format(&rm_oformat);
+ return 0;
+}
diff --git a/libavformat/rtp.c b/libavformat/rtp.c
new file mode 100644
index 0000000000..f36da6d41d
--- /dev/null
+++ b/libavformat/rtp.c
@@ -0,0 +1,687 @@
+/*
+ * RTP input/output format
+ * Copyright (c) 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"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#ifndef __BEOS__
+# include <arpa/inet.h>
+#else
+# include "barpainet.h"
+#endif
+#include <netdb.h>
+
+//#define DEBUG
+
+
+/* TODO: - add RTCP statistics reporting (should be optional).
+
+ - add support for h263/mpeg4 packetized output : IDEA: send a
+ buffer to 'rtp_write_packet' contains all the packets for ONE
+ frame. Each packet should have a four byte header containing
+ the length in big endian format (same trick as
+ 'url_open_dyn_packet_buf')
+*/
+
+#define RTP_VERSION 2
+
+#define RTP_MAX_SDES 256 /* maximum text length for SDES */
+
+/* RTCP paquets use 0.5 % of the bandwidth */
+#define RTCP_TX_RATIO_NUM 5
+#define RTCP_TX_RATIO_DEN 1000
+
+typedef enum {
+ RTCP_SR = 200,
+ RTCP_RR = 201,
+ RTCP_SDES = 202,
+ RTCP_BYE = 203,
+ RTCP_APP = 204
+} rtcp_type_t;
+
+typedef enum {
+ RTCP_SDES_END = 0,
+ RTCP_SDES_CNAME = 1,
+ RTCP_SDES_NAME = 2,
+ RTCP_SDES_EMAIL = 3,
+ RTCP_SDES_PHONE = 4,
+ RTCP_SDES_LOC = 5,
+ RTCP_SDES_TOOL = 6,
+ RTCP_SDES_NOTE = 7,
+ RTCP_SDES_PRIV = 8,
+ RTCP_SDES_IMG = 9,
+ RTCP_SDES_DOOR = 10,
+ RTCP_SDES_SOURCE = 11
+} rtcp_sdes_type_t;
+
+enum RTPPayloadType {
+ RTP_PT_ULAW = 0,
+ RTP_PT_GSM = 3,
+ RTP_PT_G723 = 4,
+ RTP_PT_ALAW = 8,
+ RTP_PT_S16BE_STEREO = 10,
+ RTP_PT_S16BE_MONO = 11,
+ RTP_PT_MPEGAUDIO = 14,
+ RTP_PT_JPEG = 26,
+ RTP_PT_H261 = 31,
+ RTP_PT_MPEGVIDEO = 32,
+ RTP_PT_MPEG2TS = 33,
+ RTP_PT_H263 = 34, /* old H263 encapsulation */
+ RTP_PT_PRIVATE = 96,
+};
+
+typedef struct RTPContext {
+ int payload_type;
+ UINT32 ssrc;
+ UINT16 seq;
+ UINT32 timestamp;
+ UINT32 base_timestamp;
+ UINT32 cur_timestamp;
+ int max_payload_size;
+ /* rtcp sender statistics receive */
+ INT64 last_rtcp_ntp_time;
+ UINT32 last_rtcp_timestamp;
+ /* rtcp sender statistics */
+ unsigned int packet_count;
+ unsigned int octet_count;
+ unsigned int last_octet_count;
+ int first_packet;
+ /* buffer for output */
+ UINT8 buf[RTP_MAX_PACKET_LENGTH];
+ UINT8 *buf_ptr;
+} RTPContext;
+
+int rtp_get_codec_info(AVCodecContext *codec, int payload_type)
+{
+ switch(payload_type) {
+ case RTP_PT_ULAW:
+ codec->codec_id = CODEC_ID_PCM_MULAW;
+ codec->channels = 1;
+ codec->sample_rate = 8000;
+ break;
+ case RTP_PT_ALAW:
+ codec->codec_id = CODEC_ID_PCM_ALAW;
+ codec->channels = 1;
+ codec->sample_rate = 8000;
+ break;
+ case RTP_PT_S16BE_STEREO:
+ codec->codec_id = CODEC_ID_PCM_S16BE;
+ codec->channels = 2;
+ codec->sample_rate = 44100;
+ break;
+ case RTP_PT_S16BE_MONO:
+ codec->codec_id = CODEC_ID_PCM_S16BE;
+ codec->channels = 1;
+ codec->sample_rate = 44100;
+ break;
+ case RTP_PT_MPEGAUDIO:
+ codec->codec_id = CODEC_ID_MP2;
+ break;
+ case RTP_PT_JPEG:
+ codec->codec_id = CODEC_ID_MJPEG;
+ break;
+ case RTP_PT_MPEGVIDEO:
+ codec->codec_id = CODEC_ID_MPEG1VIDEO;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* return < 0 if unknown payload type */
+int rtp_get_payload_type(AVCodecContext *codec)
+{
+ int payload_type;
+
+ /* compute the payload type */
+ payload_type = -1;
+ switch(codec->codec_id) {
+ case CODEC_ID_PCM_MULAW:
+ payload_type = RTP_PT_ULAW;
+ break;
+ case CODEC_ID_PCM_ALAW:
+ payload_type = RTP_PT_ALAW;
+ break;
+ case CODEC_ID_PCM_S16BE:
+ if (codec->channels == 1) {
+ payload_type = RTP_PT_S16BE_MONO;
+ } else if (codec->channels == 2) {
+ payload_type = RTP_PT_S16BE_STEREO;
+ }
+ break;
+ case CODEC_ID_MP2:
+ case CODEC_ID_MP3LAME:
+ payload_type = RTP_PT_MPEGAUDIO;
+ break;
+ case CODEC_ID_MJPEG:
+ payload_type = RTP_PT_JPEG;
+ break;
+ case CODEC_ID_MPEG1VIDEO:
+ payload_type = RTP_PT_MPEGVIDEO;
+ break;
+ default:
+ break;
+ }
+ return payload_type;
+}
+
+static inline UINT32 decode_be32(const UINT8 *p)
+{
+ return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+}
+
+static inline UINT32 decode_be64(const UINT8 *p)
+{
+ return ((UINT64)decode_be32(p) << 32) | decode_be32(p + 4);
+}
+
+static int rtcp_parse_packet(AVFormatContext *s1, const unsigned char *buf, int len)
+{
+ RTPContext *s = s1->priv_data;
+
+ if (buf[1] != 200)
+ return -1;
+ s->last_rtcp_ntp_time = decode_be64(buf + 8);
+ s->last_rtcp_timestamp = decode_be32(buf + 16);
+ return 0;
+}
+
+/**
+ * Parse an RTP packet directly sent as raw data. Can only be used if
+ * 'raw' is given as input file
+ * @param s1 media file context
+ * @param pkt returned packet
+ * @param buf input buffer
+ * @param len buffer len
+ * @return zero if no error.
+ */
+int rtp_parse_packet(AVFormatContext *s1, AVPacket *pkt,
+ const unsigned char *buf, int len)
+{
+ RTPContext *s = s1->priv_data;
+ unsigned int ssrc, h;
+ int payload_type, seq, delta_timestamp;
+ AVStream *st;
+ UINT32 timestamp;
+
+ if (len < 12)
+ return -1;
+
+ if ((buf[0] & 0xc0) != (RTP_VERSION << 6))
+ return -1;
+ if (buf[1] >= 200 && buf[1] <= 204) {
+ rtcp_parse_packet(s1, buf, len);
+ return -1;
+ }
+ payload_type = buf[1] & 0x7f;
+ seq = (buf[2] << 8) | buf[3];
+ timestamp = decode_be32(buf + 4);
+ ssrc = decode_be32(buf + 8);
+
+ if (s->payload_type < 0) {
+ s->payload_type = payload_type;
+
+ if (payload_type == RTP_PT_MPEG2TS) {
+ /* XXX: special case : not a single codec but a whole stream */
+ return -1;
+ } else {
+ st = av_new_stream(s1, 0);
+ if (!st)
+ return -1;
+ rtp_get_codec_info(&st->codec, payload_type);
+ }
+ }
+
+ /* NOTE: we can handle only one payload type */
+ if (s->payload_type != payload_type)
+ return -1;
+#if defined(DEBUG) || 1
+ if (seq != ((s->seq + 1) & 0xffff)) {
+ printf("RTP: PT=%02x: bad cseq %04x expected=%04x\n",
+ payload_type, seq, ((s->seq + 1) & 0xffff));
+ }
+ s->seq = seq;
+#endif
+ len -= 12;
+ buf += 12;
+ st = s1->streams[0];
+ switch(st->codec.codec_id) {
+ case CODEC_ID_MP2:
+ /* better than nothing: skip mpeg audio RTP header */
+ if (len <= 4)
+ return -1;
+ h = decode_be32(buf);
+ len -= 4;
+ buf += 4;
+ av_new_packet(pkt, len);
+ memcpy(pkt->data, buf, len);
+ break;
+ case CODEC_ID_MPEG1VIDEO:
+ /* better than nothing: skip mpeg audio RTP header */
+ if (len <= 4)
+ return -1;
+ h = decode_be32(buf);
+ buf += 4;
+ len -= 4;
+ if (h & (1 << 26)) {
+ /* mpeg2 */
+ if (len <= 4)
+ return -1;
+ buf += 4;
+ len -= 4;
+ }
+ av_new_packet(pkt, len);
+ memcpy(pkt->data, buf, len);
+ break;
+ default:
+ av_new_packet(pkt, len);
+ memcpy(pkt->data, buf, len);
+ break;
+ }
+
+ if (s->last_rtcp_ntp_time != AV_NOPTS_VALUE) {
+ /* compute pts from timestamp with received ntp_time */
+ delta_timestamp = timestamp - s->last_rtcp_timestamp;
+ /* XXX: do conversion, but not needed for mpeg at 90 KhZ */
+ pkt->pts = s->last_rtcp_ntp_time + delta_timestamp;
+ }
+ return 0;
+}
+
+static int rtp_read_header(AVFormatContext *s1,
+ AVFormatParameters *ap)
+{
+ RTPContext *s = s1->priv_data;
+ s->payload_type = -1;
+ s->last_rtcp_ntp_time = AV_NOPTS_VALUE;
+ return 0;
+}
+
+static int rtp_read_packet(AVFormatContext *s1, AVPacket *pkt)
+{
+ char buf[RTP_MAX_PACKET_LENGTH];
+ int ret;
+
+ /* XXX: needs a better API for packet handling ? */
+ for(;;) {
+ ret = url_read(url_fileno(&s1->pb), buf, sizeof(buf));
+ if (ret < 0)
+ return AVERROR_IO;
+ if (rtp_parse_packet(s1, pkt, buf, ret) == 0)
+ break;
+ }
+ return 0;
+}
+
+static int rtp_read_close(AVFormatContext *s1)
+{
+ // RTPContext *s = s1->priv_data;
+ return 0;
+}
+
+static int rtp_probe(AVProbeData *p)
+{
+ if (strstart(p->filename, "rtp://", NULL))
+ return AVPROBE_SCORE_MAX;
+ return 0;
+}
+
+/* rtp output */
+
+static int rtp_write_header(AVFormatContext *s1)
+{
+ RTPContext *s = s1->priv_data;
+ int payload_type, max_packet_size;
+ AVStream *st;
+
+ if (s1->nb_streams != 1)
+ return -1;
+ st = s1->streams[0];
+
+ payload_type = rtp_get_payload_type(&st->codec);
+ if (payload_type < 0)
+ payload_type = RTP_PT_PRIVATE; /* private payload type */
+ s->payload_type = payload_type;
+
+ s->base_timestamp = random();
+ s->timestamp = s->base_timestamp;
+ s->ssrc = random();
+ s->first_packet = 1;
+
+ max_packet_size = url_fget_max_packet_size(&s1->pb);
+ if (max_packet_size <= 12)
+ return AVERROR_IO;
+ s->max_payload_size = max_packet_size - 12;
+
+ switch(st->codec.codec_id) {
+ case CODEC_ID_MP2:
+ case CODEC_ID_MP3LAME:
+ s->buf_ptr = s->buf + 4;
+ s->cur_timestamp = 0;
+ break;
+ case CODEC_ID_MPEG1VIDEO:
+ s->cur_timestamp = 0;
+ break;
+ default:
+ s->buf_ptr = s->buf;
+ break;
+ }
+
+ return 0;
+}
+
+/* send an rtcp sender report packet */
+static void rtcp_send_sr(AVFormatContext *s1, INT64 ntp_time)
+{
+ RTPContext *s = s1->priv_data;
+#if defined(DEBUG)
+ printf("RTCP: %02x %Lx %x\n", s->payload_type, ntp_time, s->timestamp);
+#endif
+ put_byte(&s1->pb, (RTP_VERSION << 6));
+ put_byte(&s1->pb, 200);
+ put_be16(&s1->pb, 6); /* length in words - 1 */
+ put_be32(&s1->pb, s->ssrc);
+ put_be64(&s1->pb, ntp_time);
+ put_be32(&s1->pb, s->timestamp);
+ put_be32(&s1->pb, s->packet_count);
+ put_be32(&s1->pb, s->octet_count);
+ put_flush_packet(&s1->pb);
+}
+
+/* send an rtp packet. sequence number is incremented, but the caller
+ must update the timestamp itself */
+static void rtp_send_data(AVFormatContext *s1, UINT8 *buf1, int len)
+{
+ RTPContext *s = s1->priv_data;
+
+#ifdef DEBUG
+ printf("rtp_send_data size=%d\n", len);
+#endif
+
+ /* build the RTP header */
+ put_byte(&s1->pb, (RTP_VERSION << 6));
+ put_byte(&s1->pb, s->payload_type & 0x7f);
+ put_be16(&s1->pb, s->seq);
+ put_be32(&s1->pb, s->timestamp);
+ put_be32(&s1->pb, s->ssrc);
+
+ put_buffer(&s1->pb, buf1, len);
+ put_flush_packet(&s1->pb);
+
+ s->seq++;
+ s->octet_count += len;
+ s->packet_count++;
+}
+
+/* send an integer number of samples and compute time stamp and fill
+ the rtp send buffer before sending. */
+static void rtp_send_samples(AVFormatContext *s1,
+ UINT8 *buf1, int size, int sample_size)
+{
+ RTPContext *s = s1->priv_data;
+ int len, max_packet_size, n;
+
+ max_packet_size = (s->max_payload_size / sample_size) * sample_size;
+ /* not needed, but who nows */
+ if ((size % sample_size) != 0)
+ av_abort();
+ while (size > 0) {
+ len = (max_packet_size - (s->buf_ptr - s->buf));
+ if (len > size)
+ len = size;
+
+ /* copy data */
+ memcpy(s->buf_ptr, buf1, len);
+ s->buf_ptr += len;
+ buf1 += len;
+ size -= len;
+ n = (s->buf_ptr - s->buf);
+ /* if buffer full, then send it */
+ if (n >= max_packet_size) {
+ rtp_send_data(s1, s->buf, n);
+ s->buf_ptr = s->buf;
+ /* update timestamp */
+ s->timestamp += n / sample_size;
+ }
+ }
+}
+
+/* NOTE: we suppose that exactly one frame is given as argument here */
+/* XXX: test it */
+static void rtp_send_mpegaudio(AVFormatContext *s1,
+ UINT8 *buf1, int size)
+{
+ RTPContext *s = s1->priv_data;
+ AVStream *st = s1->streams[0];
+ int len, count, max_packet_size;
+
+ max_packet_size = s->max_payload_size;
+
+ /* test if we must flush because not enough space */
+ len = (s->buf_ptr - s->buf);
+ if ((len + size) > max_packet_size) {
+ if (len > 4) {
+ rtp_send_data(s1, s->buf, s->buf_ptr - s->buf);
+ s->buf_ptr = s->buf + 4;
+ /* 90 KHz time stamp */
+ s->timestamp = s->base_timestamp +
+ (s->cur_timestamp * 90000LL) / st->codec.sample_rate;
+ }
+ }
+
+ /* add the packet */
+ if (size > max_packet_size) {
+ /* big packet: fragment */
+ count = 0;
+ while (size > 0) {
+ len = max_packet_size - 4;
+ if (len > size)
+ len = size;
+ /* build fragmented packet */
+ s->buf[0] = 0;
+ s->buf[1] = 0;
+ s->buf[2] = count >> 8;
+ s->buf[3] = count;
+ memcpy(s->buf + 4, buf1, len);
+ rtp_send_data(s1, s->buf, len + 4);
+ size -= len;
+ buf1 += len;
+ count += len;
+ }
+ } else {
+ if (s->buf_ptr == s->buf + 4) {
+ /* no fragmentation possible */
+ s->buf[0] = 0;
+ s->buf[1] = 0;
+ s->buf[2] = 0;
+ s->buf[3] = 0;
+ }
+ memcpy(s->buf_ptr, buf1, size);
+ s->buf_ptr += size;
+ }
+ s->cur_timestamp += st->codec.frame_size;
+}
+
+/* NOTE: a single frame must be passed with sequence header if
+ needed. XXX: use slices. */
+static void rtp_send_mpegvideo(AVFormatContext *s1,
+ UINT8 *buf1, int size)
+{
+ RTPContext *s = s1->priv_data;
+ AVStream *st = s1->streams[0];
+ int len, h, max_packet_size;
+ UINT8 *q;
+
+ max_packet_size = s->max_payload_size;
+
+ while (size > 0) {
+ /* XXX: more correct headers */
+ h = 0;
+ if (st->codec.sub_id == 2)
+ h |= 1 << 26; /* mpeg 2 indicator */
+ q = s->buf;
+ *q++ = h >> 24;
+ *q++ = h >> 16;
+ *q++ = h >> 8;
+ *q++ = h;
+
+ if (st->codec.sub_id == 2) {
+ h = 0;
+ *q++ = h >> 24;
+ *q++ = h >> 16;
+ *q++ = h >> 8;
+ *q++ = h;
+ }
+
+ len = max_packet_size - (q - s->buf);
+ if (len > size)
+ len = size;
+
+ memcpy(q, buf1, len);
+ q += len;
+
+ /* 90 KHz time stamp */
+ /* XXX: overflow */
+ s->timestamp = s->base_timestamp +
+ (s->cur_timestamp * 90000LL * FRAME_RATE_BASE) / st->codec.frame_rate;
+ rtp_send_data(s1, s->buf, q - s->buf);
+
+ buf1 += len;
+ size -= len;
+ }
+ s->cur_timestamp++;
+}
+
+static void rtp_send_raw(AVFormatContext *s1,
+ UINT8 *buf1, int size)
+{
+ RTPContext *s = s1->priv_data;
+ AVStream *st = s1->streams[0];
+ int len, max_packet_size;
+
+ max_packet_size = s->max_payload_size;
+
+ while (size > 0) {
+ len = max_packet_size;
+ if (len > size)
+ len = size;
+
+ /* 90 KHz time stamp */
+ /* XXX: overflow */
+ s->timestamp = s->base_timestamp +
+ (s->cur_timestamp * 90000LL * FRAME_RATE_BASE) / st->codec.frame_rate;
+ rtp_send_data(s1, buf1, len);
+
+ buf1 += len;
+ size -= len;
+ }
+ s->cur_timestamp++;
+}
+
+/* write an RTP packet. 'buf1' must contain a single specific frame. */
+static int rtp_write_packet(AVFormatContext *s1, int stream_index,
+ UINT8 *buf1, int size, int force_pts)
+{
+ RTPContext *s = s1->priv_data;
+ AVStream *st = s1->streams[0];
+ int rtcp_bytes;
+ INT64 ntp_time;
+
+#ifdef DEBUG
+ printf("%d: write len=%d\n", stream_index, size);
+#endif
+
+ /* XXX: mpeg pts hardcoded. RTCP send every 0.5 seconds */
+ rtcp_bytes = ((s->octet_count - s->last_octet_count) * RTCP_TX_RATIO_NUM) /
+ RTCP_TX_RATIO_DEN;
+ if (s->first_packet || rtcp_bytes >= 28) {
+ /* compute NTP time */
+ ntp_time = force_pts; // ((INT64)force_pts << 28) / 5625
+ rtcp_send_sr(s1, ntp_time);
+ s->last_octet_count = s->octet_count;
+ s->first_packet = 0;
+ }
+
+ switch(st->codec.codec_id) {
+ case CODEC_ID_PCM_MULAW:
+ case CODEC_ID_PCM_ALAW:
+ case CODEC_ID_PCM_U8:
+ case CODEC_ID_PCM_S8:
+ rtp_send_samples(s1, buf1, size, 1 * st->codec.channels);
+ break;
+ case CODEC_ID_PCM_U16BE:
+ case CODEC_ID_PCM_U16LE:
+ case CODEC_ID_PCM_S16BE:
+ case CODEC_ID_PCM_S16LE:
+ rtp_send_samples(s1, buf1, size, 2 * st->codec.channels);
+ break;
+ case CODEC_ID_MP2:
+ case CODEC_ID_MP3LAME:
+ rtp_send_mpegaudio(s1, buf1, size);
+ break;
+ case CODEC_ID_MPEG1VIDEO:
+ rtp_send_mpegvideo(s1, buf1, size);
+ break;
+ default:
+ /* better than nothing : send the codec raw data */
+ rtp_send_raw(s1, buf1, size);
+ break;
+ }
+ return 0;
+}
+
+static int rtp_write_trailer(AVFormatContext *s1)
+{
+ // RTPContext *s = s1->priv_data;
+ return 0;
+}
+
+AVInputFormat rtp_demux = {
+ "rtp",
+ "RTP input format",
+ sizeof(RTPContext),
+ rtp_probe,
+ rtp_read_header,
+ rtp_read_packet,
+ rtp_read_close,
+ .flags = AVFMT_NOHEADER,
+};
+
+AVOutputFormat rtp_mux = {
+ "rtp",
+ "RTP output format",
+ NULL,
+ NULL,
+ sizeof(RTPContext),
+ CODEC_ID_PCM_MULAW,
+ CODEC_ID_NONE,
+ rtp_write_header,
+ rtp_write_packet,
+ rtp_write_trailer,
+};
+
+int rtp_init(void)
+{
+ av_register_output_format(&rtp_mux);
+ av_register_input_format(&rtp_demux);
+ return 0;
+}
diff --git a/libavformat/rtp.h b/libavformat/rtp.h
new file mode 100644
index 0000000000..0c0ae35ac0
--- /dev/null
+++ b/libavformat/rtp.h
@@ -0,0 +1,40 @@
+/*
+ * RTP definitions
+ * Copyright (c) 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
+ */
+#ifndef RTP_H
+#define RTP_H
+
+#define RTP_MIN_PACKET_LENGTH 12
+#define RTP_MAX_PACKET_LENGTH 1500 /* XXX: suppress this define */
+
+int rtp_init(void);
+int rtp_get_codec_info(AVCodecContext *codec, int payload_type);
+int rtp_get_payload_type(AVCodecContext *codec);
+int rtp_parse_packet(AVFormatContext *s1, AVPacket *pkt,
+ const unsigned char *buf, int len);
+
+extern AVOutputFormat rtp_mux;
+extern AVInputFormat rtp_demux;
+
+int rtp_get_local_port(URLContext *h);
+int rtp_set_remote_url(URLContext *h, const char *uri);
+void rtp_get_file_handles(URLContext *h, int *prtp_fd, int *prtcp_fd);
+
+extern URLProtocol rtp_protocol;
+
+#endif /* RTP_H */
diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c
new file mode 100644
index 0000000000..41823fc829
--- /dev/null
+++ b/libavformat/rtpproto.c
@@ -0,0 +1,300 @@
+/*
+ * RTP network protocol
+ * Copyright (c) 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"
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#ifndef __BEOS__
+# include <arpa/inet.h>
+#else
+# include "barpainet.h"
+#endif
+#include <netdb.h>
+#include <fcntl.h>
+
+#define RTP_TX_BUF_SIZE (64 * 1024)
+#define RTP_RX_BUF_SIZE (128 * 1024)
+
+typedef struct RTPContext {
+ URLContext *rtp_hd, *rtcp_hd;
+ int rtp_fd, rtcp_fd;
+} RTPContext;
+
+/**
+ * If no filename is given to av_open_input_file because you want to
+ * get the local port first, then you must call this function to set
+ * the remote server address.
+ *
+ * @param s1 media file context
+ * @param uri of the remote server
+ * @return zero if no error.
+ */
+int rtp_set_remote_url(URLContext *h, const char *uri)
+{
+ RTPContext *s = h->priv_data;
+ char hostname[256];
+ int port;
+
+ char buf[1024];
+ char path[1024];
+
+ url_split(NULL, 0, hostname, sizeof(hostname), &port,
+ path, sizeof(path), uri);
+
+ snprintf(buf, sizeof(buf), "udp://%s:%d%s", hostname, port, path);
+ udp_set_remote_url(s->rtp_hd, buf);
+
+ snprintf(buf, sizeof(buf), "udp://%s:%d%s", hostname, port + 1, path);
+ udp_set_remote_url(s->rtcp_hd, buf);
+ return 0;
+}
+
+
+/* add option to url of the form:
+ "http://host:port/path?option1=val1&option2=val2... */
+void url_add_option(char *buf, int buf_size, const char *fmt, ...)
+{
+ char buf1[1024];
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (strchr(buf, '?'))
+ pstrcat(buf, buf_size, "&");
+ else
+ pstrcat(buf, buf_size, "?");
+ vsnprintf(buf1, sizeof(buf1), fmt, ap);
+ pstrcat(buf, buf_size, buf1);
+ va_end(ap);
+}
+
+void build_udp_url(char *buf, int buf_size,
+ const char *hostname, int port,
+ int local_port, int multicast, int ttl)
+{
+ snprintf(buf, buf_size, "udp://%s:%d", hostname, port);
+ if (local_port >= 0)
+ url_add_option(buf, buf_size, "localport=%d", local_port);
+ if (multicast)
+ url_add_option(buf, buf_size, "multicast=1", multicast);
+ if (ttl >= 0)
+ url_add_option(buf, buf_size, "ttl=%d", ttl);
+}
+
+/*
+ * url syntax: rtp://host:port[?option=val...]
+ * option: 'multicast=1' : enable multicast
+ * 'ttl=n' : set the ttl value (for multicast only)
+ * 'localport=n' : set the local port to n
+ *
+ */
+static int rtp_open(URLContext *h, const char *uri, int flags)
+{
+ RTPContext *s;
+ int port, is_output, is_multicast, ttl, local_port;
+ char hostname[256];
+ char buf[1024];
+ char path[1024];
+ const char *p;
+
+ is_output = (flags & URL_WRONLY);
+
+ s = av_mallocz(sizeof(RTPContext));
+ if (!s)
+ return -ENOMEM;
+ h->priv_data = s;
+
+ url_split(NULL, 0, hostname, sizeof(hostname), &port,
+ path, sizeof(path), uri);
+ /* extract parameters */
+ is_multicast = 0;
+ ttl = -1;
+ local_port = -1;
+ p = strchr(uri, '?');
+ if (p) {
+ is_multicast = find_info_tag(buf, sizeof(buf), "multicast", p);
+ if (find_info_tag(buf, sizeof(buf), "ttl", p)) {
+ ttl = strtol(buf, NULL, 10);
+ }
+ if (find_info_tag(buf, sizeof(buf), "localport", p)) {
+ local_port = strtol(buf, NULL, 10);
+ }
+ }
+
+ build_udp_url(buf, sizeof(buf),
+ hostname, port, local_port, is_multicast, ttl);
+ if (url_open(&s->rtp_hd, buf, flags) < 0)
+ goto fail;
+ local_port = udp_get_local_port(s->rtp_hd);
+ /* XXX: need to open another connexion if the port is not even */
+
+ /* well, should suppress localport in path */
+
+ build_udp_url(buf, sizeof(buf),
+ hostname, port + 1, local_port + 1, is_multicast, ttl);
+ if (url_open(&s->rtcp_hd, buf, flags) < 0)
+ goto fail;
+
+ /* just to ease handle access. XXX: need to suppress direct handle
+ access */
+ s->rtp_fd = udp_get_file_handle(s->rtp_hd);
+ s->rtcp_fd = udp_get_file_handle(s->rtcp_hd);
+
+ h->max_packet_size = url_get_max_packet_size(s->rtp_hd);
+ h->is_streamed = 1;
+ return 0;
+
+ fail:
+ if (s->rtp_hd)
+ url_close(s->rtp_hd);
+ if (s->rtcp_hd)
+ url_close(s->rtcp_hd);
+ av_free(s);
+ return -EIO;
+}
+
+static int rtp_read(URLContext *h, UINT8 *buf, int size)
+{
+ RTPContext *s = h->priv_data;
+ struct sockaddr_in from;
+ int from_len, len, fd_max, n;
+ fd_set rfds;
+#if 0
+ for(;;) {
+ from_len = sizeof(from);
+ len = recvfrom (s->rtp_fd, buf, size, 0,
+ (struct sockaddr *)&from, &from_len);
+ if (len < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ return -EIO;
+ }
+ break;
+ }
+#else
+ for(;;) {
+ /* build fdset to listen to RTP and RTCP packets */
+ FD_ZERO(&rfds);
+ fd_max = s->rtp_fd;
+ FD_SET(s->rtp_fd, &rfds);
+ if (s->rtcp_fd > fd_max)
+ fd_max = s->rtcp_fd;
+ FD_SET(s->rtcp_fd, &rfds);
+ n = select(fd_max + 1, &rfds, NULL, NULL, NULL);
+ if (n > 0) {
+ /* first try RTCP */
+ if (FD_ISSET(s->rtcp_fd, &rfds)) {
+ from_len = sizeof(from);
+ len = recvfrom (s->rtcp_fd, buf, size, 0,
+ (struct sockaddr *)&from, &from_len);
+ if (len < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ return -EIO;
+ }
+ break;
+ }
+ /* then RTP */
+ if (FD_ISSET(s->rtp_fd, &rfds)) {
+ from_len = sizeof(from);
+ len = recvfrom (s->rtp_fd, buf, size, 0,
+ (struct sockaddr *)&from, &from_len);
+ if (len < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ return -EIO;
+ }
+ break;
+ }
+ }
+ }
+#endif
+ return len;
+}
+
+static int rtp_write(URLContext *h, UINT8 *buf, int size)
+{
+ RTPContext *s = h->priv_data;
+ int ret;
+ URLContext *hd;
+
+ if (buf[1] >= 200 && buf[1] <= 204) {
+ /* RTCP payload type */
+ hd = s->rtcp_hd;
+ } else {
+ /* RTP payload type */
+ hd = s->rtp_hd;
+ }
+
+ ret = url_write(hd, buf, size);
+#if 0
+ {
+ struct timespec ts;
+ ts.tv_sec = 0;
+ ts.tv_nsec = 10 * 1000000;
+ nanosleep(&ts, NULL);
+ }
+#endif
+ return ret;
+}
+
+static int rtp_close(URLContext *h)
+{
+ RTPContext *s = h->priv_data;
+
+ url_close(s->rtp_hd);
+ url_close(s->rtcp_hd);
+ av_free(s);
+ return 0;
+}
+
+/**
+ * Return the local port used by the RTP connexion
+ * @param s1 media file context
+ * @return the local port number
+ */
+int rtp_get_local_port(URLContext *h)
+{
+ RTPContext *s = h->priv_data;
+ return udp_get_local_port(s->rtp_hd);
+}
+
+/**
+ * Return the rtp and rtcp file handles for select() usage to wait for several RTP
+ * streams at the same time.
+ * @param h media file context
+ */
+void rtp_get_file_handles(URLContext *h, int *prtp_fd, int *prtcp_fd)
+{
+ RTPContext *s = h->priv_data;
+
+ *prtp_fd = s->rtp_fd;
+ *prtcp_fd = s->rtcp_fd;
+}
+
+URLProtocol rtp_protocol = {
+ "rtp",
+ rtp_open,
+ rtp_read,
+ rtp_write,
+ NULL, /* seek */
+ rtp_close,
+};
diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c
new file mode 100644
index 0000000000..c173cb89b7
--- /dev/null
+++ b/libavformat/rtsp.c
@@ -0,0 +1,1163 @@
+/*
+ * RTSP/SDP client
+ * Copyright (c) 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"
+
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <ctype.h>
+#ifndef __BEOS__
+# include <arpa/inet.h>
+#else
+# include "barpainet.h"
+#endif
+
+//#define DEBUG
+
+typedef struct RTSPState {
+ URLContext *rtsp_hd; /* RTSP TCP connexion handle */
+ ByteIOContext rtsp_gb;
+ int seq; /* RTSP command sequence number */
+ char session_id[512];
+ enum RTSPProtocol protocol;
+ char last_reply[2048]; /* XXX: allocate ? */
+} RTSPState;
+
+typedef struct RTSPStream {
+ AVFormatContext *ic;
+ int interleaved_min, interleaved_max; /* interleave ids, if TCP transport */
+ char control_url[1024]; /* url for this stream (from SDP) */
+
+ int sdp_port; /* port (from SDP content - not used in RTSP) */
+ struct in_addr sdp_ip; /* IP address (from SDP content - not used in RTSP) */
+ int sdp_ttl; /* IP TTL (from SDP content - not used in RTSP) */
+ int sdp_payload_type; /* payload type - only used in SDP */
+} RTSPStream;
+
+/* suppress this hack */
+int rtsp_abort_req = 0;
+
+/* XXX: currently, the only way to change the protocols consists in
+ changing this variable */
+int rtsp_default_protocols = (1 << RTSP_PROTOCOL_RTP_TCP) | (1 << RTSP_PROTOCOL_RTP_UDP) | (1 << RTSP_PROTOCOL_RTP_UDP_MULTICAST);
+
+/* if non zero, then set a range for RTP ports */
+int rtsp_rtp_port_min = 0;
+int rtsp_rtp_port_max = 0;
+
+FFRTSPCallback *ff_rtsp_callback = NULL;
+
+static int rtsp_probe(AVProbeData *p)
+{
+ if (strstart(p->filename, "rtsp:", NULL))
+ return AVPROBE_SCORE_MAX;
+ return 0;
+}
+
+static int redir_isspace(int c)
+{
+ return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
+}
+
+static void skip_spaces(const char **pp)
+{
+ const char *p;
+ p = *pp;
+ while (redir_isspace(*p))
+ p++;
+ *pp = p;
+}
+
+static void get_word_sep(char *buf, int buf_size, const char *sep,
+ const char **pp)
+{
+ const char *p;
+ char *q;
+
+ p = *pp;
+ skip_spaces(&p);
+ q = buf;
+ while (!strchr(sep, *p) && *p != '\0') {
+ if ((q - buf) < buf_size - 1)
+ *q++ = *p;
+ p++;
+ }
+ if (buf_size > 0)
+ *q = '\0';
+ *pp = p;
+}
+
+static void get_word(char *buf, int buf_size, const char **pp)
+{
+ const char *p;
+ char *q;
+
+ p = *pp;
+ skip_spaces(&p);
+ q = buf;
+ while (!redir_isspace(*p) && *p != '\0') {
+ if ((q - buf) < buf_size - 1)
+ *q++ = *p;
+ p++;
+ }
+ if (buf_size > 0)
+ *q = '\0';
+ *pp = p;
+}
+
+/* parse the rtpmap description: <codec_name>/<clock_rate>[/<other
+ params>] */
+static int sdp_parse_rtpmap(AVCodecContext *codec, const char *p)
+{
+ char buf[256];
+
+ /* codec name */
+ get_word_sep(buf, sizeof(buf), "/", &p);
+ if (!strcmp(buf, "MP4V-ES")) {
+ codec->codec_id = CODEC_ID_MPEG4;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/* return the length and optionnaly the data */
+static int hex_to_data(uint8_t *data, const char *p)
+{
+ int c, len, v;
+
+ len = 0;
+ v = 1;
+ for(;;) {
+ skip_spaces(&p);
+ if (p == '\0')
+ break;
+ c = toupper((unsigned char)*p++);
+ if (c >= '0' && c <= '9')
+ c = c - '0';
+ else if (c >= 'A' && c <= 'F')
+ c = c - 'A' + 10;
+ else
+ break;
+ v = (v << 4) | c;
+ if (v & 0x100) {
+ if (data)
+ data[len] = v;
+ len++;
+ v = 1;
+ }
+ }
+ return len;
+}
+
+static void sdp_parse_fmtp(AVCodecContext *codec, const char *p)
+{
+ char attr[256];
+ char value[4096];
+ int len;
+
+ /* loop on each attribute */
+ for(;;) {
+ skip_spaces(&p);
+ if (*p == '\0')
+ break;
+ get_word_sep(attr, sizeof(attr), "=", &p);
+ if (*p == '=')
+ p++;
+ get_word_sep(value, sizeof(value), ";", &p);
+ if (*p == ';')
+ p++;
+ /* handle MPEG4 video */
+ switch(codec->codec_id) {
+ case CODEC_ID_MPEG4:
+ if (!strcmp(attr, "config")) {
+ /* decode the hexa encoded parameter */
+ len = hex_to_data(NULL, value);
+ codec->extradata = av_mallocz(len);
+ if (!codec->extradata)
+ goto fail;
+ codec->extradata_size = len;
+ hex_to_data(codec->extradata, value);
+ }
+ break;
+ default:
+ /* ignore data for other codecs */
+ break;
+ }
+ fail: ;
+ // printf("'%s' = '%s'\n", attr, value);
+ }
+}
+
+typedef struct SDPParseState {
+ /* SDP only */
+ struct in_addr default_ip;
+ int default_ttl;
+} SDPParseState;
+
+static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
+ int letter, const char *buf)
+{
+ char buf1[64], st_type[64];
+ const char *p;
+ int codec_type, payload_type, i;
+ AVStream *st;
+ RTSPStream *rtsp_st;
+ struct in_addr sdp_ip;
+ int ttl;
+
+#ifdef DEBUG
+ printf("sdp: %c='%s'\n", letter, buf);
+#endif
+
+ p = buf;
+ switch(letter) {
+ case 'c':
+ get_word(buf1, sizeof(buf1), &p);
+ if (strcmp(buf1, "IN") != 0)
+ return;
+ get_word(buf1, sizeof(buf1), &p);
+ if (strcmp(buf1, "IP4") != 0)
+ return;
+ get_word_sep(buf1, sizeof(buf1), "/", &p);
+ if (inet_aton(buf1, &sdp_ip) == 0)
+ return;
+ ttl = 16;
+ if (*p == '/') {
+ p++;
+ get_word_sep(buf1, sizeof(buf1), "/", &p);
+ ttl = atoi(buf1);
+ }
+ if (s->nb_streams == 0) {
+ s1->default_ip = sdp_ip;
+ s1->default_ttl = ttl;
+ } else {
+ st = s->streams[s->nb_streams - 1];
+ rtsp_st = st->priv_data;
+ rtsp_st->sdp_ip = sdp_ip;
+ rtsp_st->sdp_ttl = ttl;
+ }
+ break;
+ case 's':
+ pstrcpy(s->title, sizeof(s->title), p);
+ break;
+ case 'i':
+ if (s->nb_streams == 0) {
+ pstrcpy(s->comment, sizeof(s->comment), p);
+ break;
+ }
+ break;
+ case 'm':
+ /* new stream */
+ get_word(st_type, sizeof(st_type), &p);
+ if (!strcmp(st_type, "audio")) {
+ codec_type = CODEC_TYPE_AUDIO;
+ } else if (!strcmp(st_type, "video")) {
+ codec_type = CODEC_TYPE_VIDEO;
+ } else {
+ return;
+ }
+ rtsp_st = av_mallocz(sizeof(RTSPStream));
+ if (!rtsp_st)
+ return;
+ st = av_new_stream(s, s->nb_streams);
+ if (!st)
+ return;
+ st->priv_data = rtsp_st;
+
+ rtsp_st->sdp_ip = s1->default_ip;
+ rtsp_st->sdp_ttl = s1->default_ttl;
+
+ st->codec.codec_type = codec_type;
+
+ get_word(buf1, sizeof(buf1), &p); /* port */
+ rtsp_st->sdp_port = atoi(buf1);
+
+ get_word(buf1, sizeof(buf1), &p); /* protocol (ignored) */
+
+ /* XXX: handle list of formats */
+ get_word(buf1, sizeof(buf1), &p); /* format list */
+ rtsp_st->sdp_payload_type = atoi(buf1);
+ if (rtsp_st->sdp_payload_type < 96) {
+ /* if standard payload type, we can find the codec right now */
+ rtp_get_codec_info(&st->codec, rtsp_st->sdp_payload_type);
+ }
+
+ /* put a default control url */
+ pstrcpy(rtsp_st->control_url, sizeof(rtsp_st->control_url), s->filename);
+ break;
+ case 'a':
+ if (strstart(p, "control:", &p) && s->nb_streams > 0) {
+ char proto[32];
+ /* get the control url */
+ st = s->streams[s->nb_streams - 1];
+ rtsp_st = st->priv_data;
+
+ /* XXX: may need to add full url resolution */
+ url_split(proto, sizeof(proto), NULL, 0, NULL, NULL, 0, p);
+ if (proto[0] == '\0') {
+ /* relative control URL */
+ pstrcat(rtsp_st->control_url, sizeof(rtsp_st->control_url), "/");
+ pstrcat(rtsp_st->control_url, sizeof(rtsp_st->control_url), p);
+ } else {
+ pstrcpy(rtsp_st->control_url, sizeof(rtsp_st->control_url), p);
+ }
+ } else if (strstart(p, "rtpmap:", &p)) {
+ /* NOTE: rtpmap is only supported AFTER the 'm=' tag */
+ get_word(buf1, sizeof(buf1), &p);
+ payload_type = atoi(buf1);
+ for(i = 0; i < s->nb_streams;i++) {
+ st = s->streams[i];
+ rtsp_st = st->priv_data;
+ if (rtsp_st->sdp_payload_type == payload_type) {
+ sdp_parse_rtpmap(&st->codec, p);
+ }
+ }
+ } else if (strstart(p, "fmtp:", &p)) {
+ /* NOTE: fmtp is only supported AFTER the 'a=rtpmap:xxx' tag */
+ get_word(buf1, sizeof(buf1), &p);
+ payload_type = atoi(buf1);
+ for(i = 0; i < s->nb_streams;i++) {
+ st = s->streams[i];
+ rtsp_st = st->priv_data;
+ if (rtsp_st->sdp_payload_type == payload_type) {
+ sdp_parse_fmtp(&st->codec, p);
+ }
+ }
+ }
+ break;
+ }
+}
+
+int sdp_parse(AVFormatContext *s, const char *content)
+{
+ const char *p;
+ int letter;
+ char buf[1024], *q;
+ SDPParseState sdp_parse_state, *s1 = &sdp_parse_state;
+
+ memset(s1, 0, sizeof(SDPParseState));
+ p = content;
+ for(;;) {
+ skip_spaces(&p);
+ letter = *p;
+ if (letter == '\0')
+ break;
+ p++;
+ if (*p != '=')
+ goto next_line;
+ p++;
+ /* get the content */
+ q = buf;
+ while (*p != '\n' && *p != '\0') {
+ if ((q - buf) < sizeof(buf) - 1)
+ *q++ = *p;
+ p++;
+ }
+ *q = '\0';
+ sdp_parse_line(s, s1, letter, buf);
+ next_line:
+ while (*p != '\n' && *p != '\0')
+ p++;
+ if (*p == '\n')
+ p++;
+ }
+ return 0;
+}
+
+static void rtsp_parse_range(int *min_ptr, int *max_ptr, const char **pp)
+{
+ const char *p;
+ int v;
+
+ p = *pp;
+ skip_spaces(&p);
+ v = strtol(p, (char **)&p, 10);
+ if (*p == '-') {
+ p++;
+ *min_ptr = v;
+ v = strtol(p, (char **)&p, 10);
+ *max_ptr = v;
+ } else {
+ *min_ptr = v;
+ *max_ptr = v;
+ }
+ *pp = p;
+}
+
+/* XXX: only one transport specification is parsed */
+static void rtsp_parse_transport(RTSPHeader *reply, const char *p)
+{
+ char transport_protocol[16];
+ char profile[16];
+ char lower_transport[16];
+ char parameter[16];
+ RTSPTransportField *th;
+ char buf[256];
+
+ reply->nb_transports = 0;
+
+ for(;;) {
+ skip_spaces(&p);
+ if (*p == '\0')
+ break;
+
+ th = &reply->transports[reply->nb_transports];
+
+ get_word_sep(transport_protocol, sizeof(transport_protocol),
+ "/", &p);
+ if (*p == '/')
+ p++;
+ get_word_sep(profile, sizeof(profile), "/;,", &p);
+ lower_transport[0] = '\0';
+ if (*p == '/') {
+ get_word_sep(lower_transport, sizeof(lower_transport),
+ ";,", &p);
+ }
+ if (!strcmp(lower_transport, "TCP"))
+ th->protocol = RTSP_PROTOCOL_RTP_TCP;
+ else
+ th->protocol = RTSP_PROTOCOL_RTP_UDP;
+
+ if (*p == ';')
+ p++;
+ /* get each parameter */
+ while (*p != '\0' && *p != ',') {
+ get_word_sep(parameter, sizeof(parameter), "=;,", &p);
+ if (!strcmp(parameter, "port")) {
+ if (*p == '=') {
+ p++;
+ rtsp_parse_range(&th->port_min, &th->port_max, &p);
+ }
+ } else if (!strcmp(parameter, "client_port")) {
+ if (*p == '=') {
+ p++;
+ rtsp_parse_range(&th->client_port_min,
+ &th->client_port_max, &p);
+ }
+ } else if (!strcmp(parameter, "server_port")) {
+ if (*p == '=') {
+ p++;
+ rtsp_parse_range(&th->server_port_min,
+ &th->server_port_max, &p);
+ }
+ } else if (!strcmp(parameter, "interleaved")) {
+ if (*p == '=') {
+ p++;
+ rtsp_parse_range(&th->interleaved_min,
+ &th->interleaved_max, &p);
+ }
+ } else if (!strcmp(parameter, "multicast")) {
+ if (th->protocol == RTSP_PROTOCOL_RTP_UDP)
+ th->protocol = RTSP_PROTOCOL_RTP_UDP_MULTICAST;
+ } else if (!strcmp(parameter, "ttl")) {
+ if (*p == '=') {
+ p++;
+ th->ttl = strtol(p, (char **)&p, 10);
+ }
+ } else if (!strcmp(parameter, "destination")) {
+ struct in_addr ipaddr;
+
+ if (*p == '=') {
+ p++;
+ get_word_sep(buf, sizeof(buf), ";,", &p);
+ if (inet_aton(buf, &ipaddr))
+ th->destination = ntohl(ipaddr.s_addr);
+ }
+ }
+ while (*p != ';' && *p != '\0' && *p != ',')
+ p++;
+ if (*p == ';')
+ p++;
+ }
+ if (*p == ',')
+ p++;
+
+ reply->nb_transports++;
+ }
+}
+
+void rtsp_parse_line(RTSPHeader *reply, const char *buf)
+{
+ const char *p;
+
+ /* NOTE: we do case independent match for broken servers */
+ p = buf;
+ if (stristart(p, "Session:", &p)) {
+ get_word_sep(reply->session_id, sizeof(reply->session_id), ";", &p);
+ } else if (stristart(p, "Content-Length:", &p)) {
+ reply->content_length = strtol(p, NULL, 10);
+ } else if (stristart(p, "Transport:", &p)) {
+ rtsp_parse_transport(reply, p);
+ } else if (stristart(p, "CSeq:", &p)) {
+ reply->seq = strtol(p, NULL, 10);
+ }
+}
+
+
+static void rtsp_send_cmd(AVFormatContext *s,
+ const char *cmd, RTSPHeader *reply,
+ unsigned char **content_ptr)
+{
+ RTSPState *rt = s->priv_data;
+ char buf[4096], buf1[1024], *q;
+ unsigned char ch;
+ const char *p;
+ int content_length, line_count;
+ unsigned char *content = NULL;
+
+ memset(reply, 0, sizeof(RTSPHeader));
+
+ rt->seq++;
+ pstrcpy(buf, sizeof(buf), cmd);
+ snprintf(buf1, sizeof(buf1), "CSeq: %d\n", rt->seq);
+ pstrcat(buf, sizeof(buf), buf1);
+ if (rt->session_id[0] != '\0' && !strstr(cmd, "\nIf-Match:")) {
+ snprintf(buf1, sizeof(buf1), "Session: %s\n", rt->session_id);
+ pstrcat(buf, sizeof(buf), buf1);
+ }
+ pstrcat(buf, sizeof(buf), "\n");
+#ifdef DEBUG
+ printf("Sending:\n%s--\n", buf);
+#endif
+ url_write(rt->rtsp_hd, buf, strlen(buf));
+
+ /* parse reply (XXX: use buffers) */
+ line_count = 0;
+ rt->last_reply[0] = '\0';
+ for(;;) {
+ q = buf;
+ for(;;) {
+ if (url_read(rt->rtsp_hd, &ch, 1) == 0)
+ break;
+ if (ch == '\n')
+ break;
+ if (ch != '\r') {
+ if ((q - buf) < sizeof(buf) - 1)
+ *q++ = ch;
+ }
+ }
+ *q = '\0';
+#ifdef DEBUG
+ printf("line='%s'\n", buf);
+#endif
+ /* test if last line */
+ if (buf[0] == '\0')
+ break;
+ p = buf;
+ if (line_count == 0) {
+ /* get reply code */
+ get_word(buf1, sizeof(buf1), &p);
+ get_word(buf1, sizeof(buf1), &p);
+ reply->status_code = atoi(buf1);
+ } else {
+ rtsp_parse_line(reply, p);
+ pstrcat(rt->last_reply, sizeof(rt->last_reply), p);
+ pstrcat(rt->last_reply, sizeof(rt->last_reply), "\n");
+ }
+ line_count++;
+ }
+
+ if (rt->session_id[0] == '\0' && reply->session_id[0] != '\0')
+ pstrcpy(rt->session_id, sizeof(rt->session_id), reply->session_id);
+
+ content_length = reply->content_length;
+ if (content_length > 0) {
+ /* leave some room for a trailing '\0' (useful for simple parsing) */
+ content = av_malloc(content_length + 1);
+ url_read(rt->rtsp_hd, content, content_length);
+ content[content_length] = '\0';
+ }
+ if (content_ptr)
+ *content_ptr = content;
+}
+
+/* useful for modules: set RTSP callback function */
+
+void rtsp_set_callback(FFRTSPCallback *rtsp_cb)
+{
+ ff_rtsp_callback = rtsp_cb;
+}
+
+
+static int rtsp_read_header(AVFormatContext *s,
+ AVFormatParameters *ap)
+{
+ RTSPState *rt = s->priv_data;
+ char host[1024], path[1024], tcpname[1024], cmd[2048];
+ URLContext *rtsp_hd;
+ int port, i, ret, err;
+ RTSPHeader reply1, *reply = &reply1;
+ unsigned char *content = NULL;
+ AVStream *st;
+ RTSPStream *rtsp_st;
+ int protocol_mask;
+
+ rtsp_abort_req = 0;
+
+ /* extract hostname and port */
+ url_split(NULL, 0,
+ host, sizeof(host), &port, path, sizeof(path), s->filename);
+ if (port < 0)
+ port = RTSP_DEFAULT_PORT;
+
+ /* open the tcp connexion */
+ snprintf(tcpname, sizeof(tcpname), "tcp://%s:%d", host, port);
+ if (url_open(&rtsp_hd, tcpname, URL_RDWR) < 0)
+ return AVERROR_IO;
+ rt->rtsp_hd = rtsp_hd;
+ rt->seq = 0;
+
+ /* describe the stream */
+ snprintf(cmd, sizeof(cmd),
+ "DESCRIBE %s RTSP/1.0\n"
+ "Accept: application/sdp\n",
+ s->filename);
+ rtsp_send_cmd(s, cmd, reply, &content);
+ if (!content) {
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ if (reply->status_code != RTSP_STATUS_OK) {
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ /* now we got the SDP description, we parse it */
+ ret = sdp_parse(s, (const char *)content);
+ av_freep(&content);
+ if (ret < 0) {
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ protocol_mask = rtsp_default_protocols;
+
+ /* for each stream, make the setup request */
+ /* XXX: we assume the same server is used for the control of each
+ RTSP stream */
+ for(i=0;i<s->nb_streams;i++) {
+ char transport[2048];
+ AVInputFormat *fmt;
+
+ st = s->streams[i];
+ rtsp_st = st->priv_data;
+
+ /* compute available transports */
+ transport[0] = '\0';
+
+ /* RTP/UDP */
+ if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP)) {
+ char buf[256];
+ int j;
+
+ /* first try in specified port range */
+ if (rtsp_rtp_port_min != 0) {
+ for(j=rtsp_rtp_port_min;j<=rtsp_rtp_port_max;j++) {
+ snprintf(buf, sizeof(buf), "rtp://?localport=%d", j);
+ if (!av_open_input_file(&rtsp_st->ic, buf,
+ &rtp_demux, 0, NULL))
+ goto rtp_opened;
+ }
+ }
+
+ /* then try on any port */
+ if (av_open_input_file(&rtsp_st->ic, "rtp://",
+ &rtp_demux, 0, NULL) < 0) {
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ rtp_opened:
+ port = rtp_get_local_port(url_fileno(&rtsp_st->ic->pb));
+ if (transport[0] != '\0')
+ pstrcat(transport, sizeof(transport), ",");
+ snprintf(transport + strlen(transport), sizeof(transport) - strlen(transport) - 1,
+ "RTP/AVP/UDP;unicast;client_port=%d-%d",
+ port, port + 1);
+ }
+
+ /* RTP/TCP */
+ if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_TCP)) {
+ if (transport[0] != '\0')
+ pstrcat(transport, sizeof(transport), ",");
+ snprintf(transport + strlen(transport), sizeof(transport) - strlen(transport) - 1,
+ "RTP/AVP/TCP");
+ }
+
+ if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP_MULTICAST)) {
+ if (transport[0] != '\0')
+ pstrcat(transport, sizeof(transport), ",");
+ snprintf(transport + strlen(transport),
+ sizeof(transport) - strlen(transport) - 1,
+ "RTP/AVP/UDP;multicast");
+ }
+
+ snprintf(cmd, sizeof(cmd),
+ "SETUP %s RTSP/1.0\n"
+ "Transport: %s\n",
+ rtsp_st->control_url, transport);
+ rtsp_send_cmd(s, cmd, reply, NULL);
+ if (reply->status_code != RTSP_STATUS_OK ||
+ reply->nb_transports != 1) {
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ /* XXX: same protocol for all streams is required */
+ if (i > 0) {
+ if (reply->transports[0].protocol != rt->protocol) {
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ } else {
+ rt->protocol = reply->transports[0].protocol;
+ }
+
+ /* close RTP connection if not choosen */
+ if (reply->transports[0].protocol != RTSP_PROTOCOL_RTP_UDP &&
+ (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP))) {
+ av_close_input_file(rtsp_st->ic);
+ rtsp_st->ic = NULL;
+ }
+
+ switch(reply->transports[0].protocol) {
+ case RTSP_PROTOCOL_RTP_TCP:
+ fmt = &rtp_demux;
+ if (av_open_input_file(&rtsp_st->ic, "null", fmt, 0, NULL) < 0) {
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ rtsp_st->interleaved_min = reply->transports[0].interleaved_min;
+ rtsp_st->interleaved_max = reply->transports[0].interleaved_max;
+ break;
+
+ case RTSP_PROTOCOL_RTP_UDP:
+ {
+ char url[1024];
+
+ /* XXX: also use address if specified */
+ snprintf(url, sizeof(url), "rtp://%s:%d",
+ host, reply->transports[0].server_port_min);
+ if (rtp_set_remote_url(url_fileno(&rtsp_st->ic->pb), url) < 0) {
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ }
+ break;
+ case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
+ {
+ char url[1024];
+ int ttl;
+
+ fmt = &rtp_demux;
+ ttl = reply->transports[0].ttl;
+ if (!ttl)
+ ttl = 16;
+ snprintf(url, sizeof(url), "rtp://%s:%d?multicast=1&ttl=%d",
+ host,
+ reply->transports[0].server_port_min,
+ ttl);
+ if (av_open_input_file(&rtsp_st->ic, url, fmt, 0, NULL) < 0) {
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ }
+ break;
+ }
+ }
+
+ /* use callback if available to extend setup */
+ if (ff_rtsp_callback) {
+ if (ff_rtsp_callback(RTSP_ACTION_CLIENT_SETUP, rt->session_id,
+ NULL, 0, rt->last_reply) < 0) {
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ }
+
+ /* start playing */
+ snprintf(cmd, sizeof(cmd),
+ "PLAY %s RTSP/1.0\n",
+ s->filename);
+ rtsp_send_cmd(s, cmd, reply, NULL);
+ if (reply->status_code != RTSP_STATUS_OK) {
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ /* open TCP with bufferized input */
+ if (rt->protocol == RTSP_PROTOCOL_RTP_TCP) {
+ if (url_fdopen(&rt->rtsp_gb, rt->rtsp_hd) < 0) {
+ err = AVERROR_NOMEM;
+ goto fail;
+ }
+ }
+
+ return 0;
+ fail:
+ for(i=0;i<s->nb_streams;i++) {
+ st = s->streams[i];
+ rtsp_st = st->priv_data;
+ if (rtsp_st) {
+ if (rtsp_st->ic)
+ av_close_input_file(rtsp_st->ic);
+ }
+ av_free(rtsp_st);
+ }
+ av_freep(&content);
+ url_close(rt->rtsp_hd);
+ return err;
+}
+
+static int tcp_read_packet(AVFormatContext *s,
+ AVPacket *pkt)
+{
+ RTSPState *rt = s->priv_data;
+ ByteIOContext *rtsp_gb = &rt->rtsp_gb;
+ int c, id, len, i, ret;
+ AVStream *st;
+ RTSPStream *rtsp_st;
+ char buf[RTP_MAX_PACKET_LENGTH];
+
+ redo:
+ for(;;) {
+ c = url_fgetc(rtsp_gb);
+ if (c == URL_EOF)
+ return AVERROR_IO;
+ if (c == '$')
+ break;
+ }
+ id = get_byte(rtsp_gb);
+ len = get_be16(rtsp_gb);
+ if (len > RTP_MAX_PACKET_LENGTH || len < 12)
+ goto redo;
+ /* get the data */
+ get_buffer(rtsp_gb, buf, len);
+
+ /* find the matching stream */
+ for(i = 0; i < s->nb_streams; i++) {
+ st = s->streams[i];
+ rtsp_st = st->priv_data;
+ if (i >= rtsp_st->interleaved_min &&
+ i <= rtsp_st->interleaved_max)
+ goto found;
+ }
+ goto redo;
+ found:
+ ret = rtp_parse_packet(rtsp_st->ic, pkt, buf, len);
+ if (ret < 0)
+ goto redo;
+ pkt->stream_index = i;
+ return ret;
+}
+
+/* NOTE: output one packet at a time. May need to add a small fifo */
+static int udp_read_packet(AVFormatContext *s,
+ AVPacket *pkt)
+{
+ AVFormatContext *ic;
+ AVStream *st;
+ RTSPStream *rtsp_st;
+ fd_set rfds;
+ int fd1, fd2, fd_max, n, i, ret;
+ char buf[RTP_MAX_PACKET_LENGTH];
+ struct timeval tv;
+
+ for(;;) {
+ if (rtsp_abort_req)
+ return -EIO;
+ FD_ZERO(&rfds);
+ fd_max = -1;
+ for(i = 0; i < s->nb_streams; i++) {
+ st = s->streams[i];
+ rtsp_st = st->priv_data;
+ ic = rtsp_st->ic;
+ /* currently, we cannot probe RTCP handle because of blocking restrictions */
+ rtp_get_file_handles(url_fileno(&ic->pb), &fd1, &fd2);
+ if (fd1 > fd_max)
+ fd_max = fd1;
+ FD_SET(fd1, &rfds);
+ }
+ /* XXX: also add proper API to abort */
+ tv.tv_sec = 0;
+ tv.tv_usec = 500000;
+ n = select(fd_max + 1, &rfds, NULL, NULL, &tv);
+ if (n > 0) {
+ for(i = 0; i < s->nb_streams; i++) {
+ st = s->streams[i];
+ rtsp_st = st->priv_data;
+ ic = rtsp_st->ic;
+ rtp_get_file_handles(url_fileno(&ic->pb), &fd1, &fd2);
+ if (FD_ISSET(fd1, &rfds)) {
+ ret = url_read(url_fileno(&ic->pb), buf, sizeof(buf));
+ if (ret >= 0 &&
+ rtp_parse_packet(ic, pkt, buf, ret) == 0) {
+ pkt->stream_index = i;
+ return ret;
+ }
+ }
+ }
+ }
+ }
+}
+
+static int rtsp_read_packet(AVFormatContext *s,
+ AVPacket *pkt)
+{
+ RTSPState *rt = s->priv_data;
+ int ret;
+
+ switch(rt->protocol) {
+ default:
+ case RTSP_PROTOCOL_RTP_TCP:
+ ret = tcp_read_packet(s, pkt);
+ break;
+ case RTSP_PROTOCOL_RTP_UDP:
+ ret = udp_read_packet(s, pkt);
+ break;
+ }
+ return ret;
+}
+
+static int rtsp_read_close(AVFormatContext *s)
+{
+ RTSPState *rt = s->priv_data;
+ AVStream *st;
+ RTSPStream *rtsp_st;
+ RTSPHeader reply1, *reply = &reply1;
+ int i;
+ char cmd[1024];
+
+ /* NOTE: it is valid to flush the buffer here */
+ if (rt->protocol == RTSP_PROTOCOL_RTP_TCP) {
+ url_fclose(&rt->rtsp_gb);
+ }
+
+ snprintf(cmd, sizeof(cmd),
+ "TEARDOWN %s RTSP/1.0\n",
+ s->filename);
+ rtsp_send_cmd(s, cmd, reply, NULL);
+
+ if (ff_rtsp_callback) {
+ ff_rtsp_callback(RTSP_ACTION_CLIENT_TEARDOWN, rt->session_id,
+ NULL, 0, NULL);
+ }
+
+ for(i=0;i<s->nb_streams;i++) {
+ st = s->streams[i];
+ rtsp_st = st->priv_data;
+ if (rtsp_st) {
+ if (rtsp_st->ic)
+ av_close_input_file(rtsp_st->ic);
+ }
+ av_free(rtsp_st);
+ }
+ url_close(rt->rtsp_hd);
+ return 0;
+}
+
+static AVInputFormat rtsp_demux = {
+ "rtsp",
+ "RTSP input format",
+ sizeof(RTSPState),
+ rtsp_probe,
+ rtsp_read_header,
+ rtsp_read_packet,
+ rtsp_read_close,
+ .flags = AVFMT_NOFILE,
+};
+
+static int sdp_probe(AVProbeData *p1)
+{
+ const char *p;
+
+ /* we look for a line beginning "c=IN IP4" */
+ p = p1->buf;
+ while (*p != '\0') {
+ if (strstart(p, "c=IN IP4", NULL))
+ return AVPROBE_SCORE_MAX / 2;
+ p = strchr(p, '\n');
+ if (!p)
+ break;
+ p++;
+ if (*p == '\r')
+ p++;
+ }
+ return 0;
+}
+
+#define SDP_MAX_SIZE 8192
+
+static int sdp_read_header(AVFormatContext *s,
+ AVFormatParameters *ap)
+{
+ AVStream *st;
+ RTSPStream *rtsp_st;
+ int size, i, err;
+ char *content;
+ char url[1024];
+
+ /* read the whole sdp file */
+ /* XXX: better loading */
+ content = av_malloc(SDP_MAX_SIZE);
+ size = get_buffer(&s->pb, content, SDP_MAX_SIZE - 1);
+ if (size <= 0) {
+ av_free(content);
+ return AVERROR_INVALIDDATA;
+ }
+ content[size] ='\0';
+
+ sdp_parse(s, content);
+ av_free(content);
+
+ /* open each RTP stream */
+ for(i=0;i<s->nb_streams;i++) {
+ st = s->streams[i];
+ rtsp_st = st->priv_data;
+
+ snprintf(url, sizeof(url), "rtp://%s:%d?multicast=1&ttl=%d",
+ inet_ntoa(rtsp_st->sdp_ip),
+ rtsp_st->sdp_port,
+ rtsp_st->sdp_ttl);
+ if (av_open_input_file(&rtsp_st->ic, url, &rtp_demux, 0, NULL) < 0) {
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ }
+ return 0;
+ fail:
+ for(i=0;i<s->nb_streams;i++) {
+ st = s->streams[i];
+ rtsp_st = st->priv_data;
+ if (rtsp_st) {
+ if (rtsp_st->ic)
+ av_close_input_file(rtsp_st->ic);
+ }
+ av_free(rtsp_st);
+ }
+ return err;
+}
+
+static int sdp_read_packet(AVFormatContext *s,
+ AVPacket *pkt)
+{
+ return udp_read_packet(s, pkt);
+}
+
+static int sdp_read_close(AVFormatContext *s)
+{
+ AVStream *st;
+ RTSPStream *rtsp_st;
+ int i;
+
+ for(i=0;i<s->nb_streams;i++) {
+ st = s->streams[i];
+ rtsp_st = st->priv_data;
+ if (rtsp_st) {
+ if (rtsp_st->ic)
+ av_close_input_file(rtsp_st->ic);
+ }
+ av_free(rtsp_st);
+ }
+ return 0;
+}
+
+
+static AVInputFormat sdp_demux = {
+ "sdp",
+ "SDP",
+ sizeof(RTSPState),
+ sdp_probe,
+ sdp_read_header,
+ sdp_read_packet,
+ sdp_read_close,
+};
+
+
+/* dummy redirector format (used directly in av_open_input_file now) */
+static int redir_probe(AVProbeData *pd)
+{
+ const char *p;
+ p = pd->buf;
+ while (redir_isspace(*p))
+ p++;
+ if (strstart(p, "http://", NULL) ||
+ strstart(p, "rtsp://", NULL))
+ return AVPROBE_SCORE_MAX;
+ return 0;
+}
+
+/* called from utils.c */
+int redir_open(AVFormatContext **ic_ptr, ByteIOContext *f)
+{
+ char buf[4096], *q;
+ int c;
+ AVFormatContext *ic = NULL;
+
+ /* parse each URL and try to open it */
+ c = url_fgetc(f);
+ while (c != URL_EOF) {
+ /* skip spaces */
+ for(;;) {
+ if (!redir_isspace(c))
+ break;
+ c = url_fgetc(f);
+ }
+ if (c == URL_EOF)
+ break;
+ /* record url */
+ q = buf;
+ for(;;) {
+ if (c == URL_EOF || redir_isspace(c))
+ break;
+ if ((q - buf) < sizeof(buf) - 1)
+ *q++ = c;
+ c = url_fgetc(f);
+ }
+ *q = '\0';
+ //printf("URL='%s'\n", buf);
+ /* try to open the media file */
+ if (av_open_input_file(&ic, buf, NULL, 0, NULL) == 0)
+ break;
+ }
+ *ic_ptr = ic;
+ if (!ic)
+ return AVERROR_IO;
+ else
+ return 0;
+}
+
+AVInputFormat redir_demux = {
+ "redir",
+ "Redirector format",
+ 0,
+ redir_probe,
+ NULL,
+ NULL,
+ NULL,
+};
+
+int rtsp_init(void)
+{
+ av_register_input_format(&rtsp_demux);
+ av_register_input_format(&redir_demux);
+ av_register_input_format(&sdp_demux);
+ return 0;
+}
diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h
new file mode 100644
index 0000000000..3dd231571e
--- /dev/null
+++ b/libavformat/rtsp.h
@@ -0,0 +1,86 @@
+/*
+ * RTSP definitions
+ * Copyright (c) 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
+ */
+#ifndef RTSP_H
+#define RTSP_H
+
+/* RTSP handling */
+enum RTSPStatusCode {
+#define DEF(n, c, s) c = n,
+#include "rtspcodes.h"
+#undef DEF
+};
+
+enum RTSPProtocol {
+ RTSP_PROTOCOL_RTP_UDP = 0,
+ RTSP_PROTOCOL_RTP_TCP = 1,
+ RTSP_PROTOCOL_RTP_UDP_MULTICAST = 2,
+};
+
+#define RTSP_DEFAULT_PORT 554
+#define RTSP_MAX_TRANSPORTS 8
+
+typedef struct RTSPTransportField {
+ int interleaved_min, interleaved_max; /* interleave ids, if TCP transport */
+ int port_min, port_max; /* RTP ports */
+ int client_port_min, client_port_max; /* RTP ports */
+ int server_port_min, server_port_max; /* RTP ports */
+ int ttl; /* ttl value */
+ UINT32 destination; /* destination IP address */
+ enum RTSPProtocol protocol;
+} RTSPTransportField;
+
+typedef struct RTSPHeader {
+ int content_length;
+ enum RTSPStatusCode status_code; /* response code from server */
+ int nb_transports;
+ RTSPTransportField transports[RTSP_MAX_TRANSPORTS];
+ int seq; /* sequence number */
+ char session_id[512];
+} RTSPHeader;
+
+/* the callback can be used to extend the connection setup/teardown step */
+enum RTSPCallbackAction {
+ RTSP_ACTION_SERVER_SETUP,
+ RTSP_ACTION_SERVER_TEARDOWN,
+ RTSP_ACTION_CLIENT_SETUP,
+ RTSP_ACTION_CLIENT_TEARDOWN,
+};
+
+typedef struct RTSPActionServerSetup {
+ UINT32 ipaddr;
+ char transport_option[512];
+} RTSPActionServerSetup;
+
+typedef int FFRTSPCallback(enum RTSPCallbackAction action,
+ const char *session_id,
+ char *buf, int buf_size,
+ void *arg);
+
+void rtsp_set_callback(FFRTSPCallback *rtsp_cb);
+
+int rtsp_init(void);
+void rtsp_parse_line(RTSPHeader *reply, const char *buf);
+
+extern int rtsp_abort_req;
+extern int rtsp_default_protocols;
+extern int rtsp_rtp_port_min;
+extern int rtsp_rtp_port_max;
+extern FFRTSPCallback *ff_rtsp_callback;
+
+#endif /* RTSP_H */
diff --git a/libavformat/rtspcodes.h b/libavformat/rtspcodes.h
new file mode 100644
index 0000000000..b967cb932a
--- /dev/null
+++ b/libavformat/rtspcodes.h
@@ -0,0 +1,11 @@
+DEF(200, RTSP_STATUS_OK, "OK")
+DEF(405, RTSP_STATUS_METHOD, "Method Not Allowed")
+DEF(453, RTSP_STATUS_BANDWIDTH, "Not Enough Bandwidth")
+DEF(454, RTSP_STATUS_SESSION, "Session Not Found")
+DEF(455, RTSP_STATUS_STATE, "Method Not Valid in This State")
+DEF(459, RTSP_STATUS_AGGREGATE, "Aggregate operation not allowed")
+DEF(460, RTSP_STATUS_ONLY_AGGREGATE, "Only aggregate operation allowed")
+DEF(461, RTSP_STATUS_TRANSPORT, "Unsupported transport")
+DEF(500, RTSP_STATUS_INTERNAL, "Internal Server Error")
+DEF(503, RTSP_STATUS_SERVICE, "Service Unavailable")
+DEF(505, RTSP_STATUS_VERSION, "RTSP Version not supported")
diff --git a/libavformat/strptime.c b/libavformat/strptime.c
new file mode 100644
index 0000000000..7aece6a08e
--- /dev/null
+++ b/libavformat/strptime.c
@@ -0,0 +1,1004 @@
+/* Convert a string representation of time to a time value.
+ Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* XXX This version of the implementation is not really complete.
+ Some of the fields cannot add information alone. But if seeing
+ some of them in the same format (such as year, week and weekday)
+ this is enough information for determining the date. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <ctype.h>
+#include <limits.h>
+#include <string.h>
+#include <time.h>
+
+#ifdef _LIBC
+# include "../locale/localeinfo.h"
+#endif
+
+#include "strptime.h"
+
+#ifndef __P
+# if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
+# define __P(args) args
+# else
+# define __P(args) ()
+# endif /* GCC. */
+#endif /* Not __P. */
+
+#if ! HAVE_LOCALTIME_R && ! defined localtime_r
+# ifdef _LIBC
+# define localtime_r __localtime_r
+# else
+/* Approximate localtime_r as best we can in its absence. */
+# define localtime_r my_localtime_r
+static struct tm *localtime_r __P ((const time_t *, struct tm *));
+static struct tm *
+localtime_r (t, tp)
+ const time_t *t;
+ struct tm *tp;
+{
+ struct tm *l = localtime (t);
+ if (! l)
+ return 0;
+ *tp = *l;
+ return tp;
+}
+# endif /* ! _LIBC */
+#endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
+
+
+#define match_char(ch1, ch2) if (ch1 != ch2) return NULL
+#if defined __GNUC__ && __GNUC__ >= 2
+# define match_string(cs1, s2) \
+ ({ size_t len = strlen (cs1); \
+ int result = strncasecmp ((cs1), (s2), len) == 0; \
+ if (result) (s2) += len; \
+ result; })
+#else
+/* Oh come on. Get a reasonable compiler. */
+# define match_string(cs1, s2) \
+ (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
+#endif
+/* We intentionally do not use isdigit() for testing because this will
+ lead to problems with the wide character version. */
+#define get_number(from, to, n) \
+ do { \
+ int __n = n; \
+ val = 0; \
+ while (*rp == ' ') \
+ ++rp; \
+ if (*rp < '0' || *rp > '9') \
+ return NULL; \
+ do { \
+ val *= 10; \
+ val += *rp++ - '0'; \
+ } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
+ if (val < from || val > to) \
+ return NULL; \
+ } while (0)
+#ifdef _NL_CURRENT
+# define get_alt_number(from, to, n) \
+ ({ \
+ __label__ do_normal; \
+ if (*decided != raw) \
+ { \
+ const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS); \
+ int __n = n; \
+ int any = 0; \
+ while (*rp == ' ') \
+ ++rp; \
+ val = 0; \
+ do { \
+ val *= 10; \
+ while (*alts != '\0') \
+ { \
+ size_t len = strlen (alts); \
+ if (strncasecmp (alts, rp, len) == 0) \
+ break; \
+ alts += len + 1; \
+ ++val; \
+ } \
+ if (*alts == '\0') \
+ { \
+ if (*decided == not && ! any) \
+ goto do_normal; \
+ /* If we haven't read anything it's an error. */ \
+ if (! any) \
+ return NULL; \
+ /* Correct the premature multiplication. */ \
+ val /= 10; \
+ break; \
+ } \
+ else \
+ *decided = loc; \
+ } while (--__n > 0 && val * 10 <= to); \
+ if (val < from || val > to) \
+ return NULL; \
+ } \
+ else \
+ { \
+ do_normal: \
+ get_number (from, to, n); \
+ } \
+ 0; \
+ })
+#else
+# define get_alt_number(from, to, n) \
+ /* We don't have the alternate representation. */ \
+ get_number(from, to, n)
+#endif
+#define recursive(new_fmt) \
+ (*(new_fmt) != '\0' \
+ && (rp = strptime_internal (rp, (new_fmt), tm, decided, era_cnt)) != NULL)
+
+
+#ifdef _LIBC
+/* This is defined in locale/C-time.c in the GNU libc. */
+extern const struct locale_data _nl_C_LC_TIME;
+extern const unsigned short int __mon_yday[2][13];
+
+# define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
+# define ab_weekday_name \
+ (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
+# define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
+# define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
+# define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
+# define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
+# define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
+# define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
+# define HERE_T_FMT_AMPM \
+ (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
+# define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
+
+# define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
+#else
+static char const weekday_name[][10] =
+ {
+ "Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday"
+ };
+static char const ab_weekday_name[][4] =
+ {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+ };
+static char const month_name[][10] =
+ {
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+ };
+static char const ab_month_name[][4] =
+ {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+# define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
+# define HERE_D_FMT "%m/%d/%y"
+# define HERE_AM_STR "AM"
+# define HERE_PM_STR "PM"
+# define HERE_T_FMT_AMPM "%I:%M:%S %p"
+# define HERE_T_FMT "%H:%M:%S"
+
+const unsigned short int __mon_yday[2][13] =
+ {
+ /* Normal years. */
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+ /* Leap years. */
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+ };
+#endif
+
+/* Status of lookup: do we use the locale data or the raw data? */
+enum locale_status { not, loc, raw };
+
+
+#ifndef __isleap
+/* Nonzero if YEAR is a leap year (every 4 years,
+ except every 100th isn't, and every 400th is). */
+# define __isleap(year) \
+ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
+#endif
+
+/* Compute the day of the week. */
+static void
+day_of_the_week (struct tm *tm)
+{
+ /* We know that January 1st 1970 was a Thursday (= 4). Compute the
+ the difference between this data in the one on TM and so determine
+ the weekday. */
+ int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
+ int wday = (-473
+ + (365 * (tm->tm_year - 70))
+ + (corr_year / 4)
+ - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
+ + (((corr_year / 4) / 25) / 4)
+ + __mon_yday[0][tm->tm_mon]
+ + tm->tm_mday - 1);
+ tm->tm_wday = ((wday % 7) + 7) % 7;
+}
+
+/* Compute the day of the year. */
+static void
+day_of_the_year (struct tm *tm)
+{
+ tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
+ + (tm->tm_mday - 1));
+}
+
+static char *
+#ifdef _LIBC
+internal_function
+#endif
+strptime_internal __P ((const char *rp, const char *fmt, struct tm *tm,
+ enum locale_status *decided, int era_cnt));
+
+static char *
+#ifdef _LIBC
+internal_function
+#endif
+strptime_internal (rp, fmt, tm, decided, era_cnt)
+ const char *rp;
+ const char *fmt;
+ struct tm *tm;
+ enum locale_status *decided;
+ int era_cnt;
+{
+ const char *rp_backup;
+ int cnt;
+ size_t val;
+ int have_I, is_pm;
+ int century, want_century;
+ int want_era;
+ int have_wday, want_xday;
+ int have_yday;
+ int have_mon, have_mday;
+#ifdef _NL_CURRENT
+ size_t num_eras;
+#endif
+ struct era_entry *era;
+
+ have_I = is_pm = 0;
+ century = -1;
+ want_century = 0;
+ want_era = 0;
+ era = NULL;
+
+ have_wday = want_xday = have_yday = have_mon = have_mday = 0;
+
+ while (*fmt != '\0')
+ {
+ /* A white space in the format string matches 0 more or white
+ space in the input string. */
+ if (isspace (*fmt))
+ {
+ while (isspace (*rp))
+ ++rp;
+ ++fmt;
+ continue;
+ }
+
+ /* Any character but `%' must be matched by the same character
+ in the iput string. */
+ if (*fmt != '%')
+ {
+ match_char (*fmt++, *rp++);
+ continue;
+ }
+
+ ++fmt;
+#ifndef _NL_CURRENT
+ /* We need this for handling the `E' modifier. */
+ start_over:
+#endif
+
+ /* Make back up of current processing pointer. */
+ rp_backup = rp;
+
+ switch (*fmt++)
+ {
+ case '%':
+ /* Match the `%' character itself. */
+ match_char ('%', *rp++);
+ break;
+ case 'a':
+ case 'A':
+ /* Match day of week. */
+ for (cnt = 0; cnt < 7; ++cnt)
+ {
+#ifdef _NL_CURRENT
+ if (*decided !=raw)
+ {
+ if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
+ {
+ if (*decided == not
+ && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
+ weekday_name[cnt]))
+ *decided = loc;
+ break;
+ }
+ if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
+ {
+ if (*decided == not
+ && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
+ ab_weekday_name[cnt]))
+ *decided = loc;
+ break;
+ }
+ }
+#endif
+ if (*decided != loc
+ && (match_string (weekday_name[cnt], rp)
+ || match_string (ab_weekday_name[cnt], rp)))
+ {
+ *decided = raw;
+ break;
+ }
+ }
+ if (cnt == 7)
+ /* Does not match a weekday name. */
+ return NULL;
+ tm->tm_wday = cnt;
+ have_wday = 1;
+ break;
+ case 'b':
+ case 'B':
+ case 'h':
+ /* Match month name. */
+ for (cnt = 0; cnt < 12; ++cnt)
+ {
+#ifdef _NL_CURRENT
+ if (*decided !=raw)
+ {
+ if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
+ {
+ if (*decided == not
+ && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
+ month_name[cnt]))
+ *decided = loc;
+ break;
+ }
+ if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
+ {
+ if (*decided == not
+ && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
+ ab_month_name[cnt]))
+ *decided = loc;
+ break;
+ }
+ }
+#endif
+ if (match_string (month_name[cnt], rp)
+ || match_string (ab_month_name[cnt], rp))
+ {
+ *decided = raw;
+ break;
+ }
+ }
+ if (cnt == 12)
+ /* Does not match a month name. */
+ return NULL;
+ tm->tm_mon = cnt;
+ want_xday = 1;
+ break;
+ case 'c':
+ /* Match locale's date and time format. */
+#ifdef _NL_CURRENT
+ if (*decided != raw)
+ {
+ if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
+ {
+ if (*decided == loc)
+ return NULL;
+ else
+ rp = rp_backup;
+ }
+ else
+ {
+ if (*decided == not &&
+ strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
+ *decided = loc;
+ want_xday = 1;
+ break;
+ }
+ *decided = raw;
+ }
+#endif
+ if (!recursive (HERE_D_T_FMT))
+ return NULL;
+ want_xday = 1;
+ break;
+ case 'C':
+ /* Match century number. */
+#ifdef _NL_CURRENT
+ match_century:
+#endif
+ get_number (0, 99, 2);
+ century = val;
+ want_xday = 1;
+ break;
+ case 'd':
+ case 'e':
+ /* Match day of month. */
+ get_number (1, 31, 2);
+ tm->tm_mday = val;
+ have_mday = 1;
+ want_xday = 1;
+ break;
+ case 'F':
+ if (!recursive ("%Y-%m-%d"))
+ return NULL;
+ want_xday = 1;
+ break;
+ case 'x':
+#ifdef _NL_CURRENT
+ if (*decided != raw)
+ {
+ if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
+ {
+ if (*decided == loc)
+ return NULL;
+ else
+ rp = rp_backup;
+ }
+ else
+ {
+ if (*decided == not
+ && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
+ *decided = loc;
+ want_xday = 1;
+ break;
+ }
+ *decided = raw;
+ }
+#endif
+ /* Fall through. */
+ case 'D':
+ /* Match standard day format. */
+ if (!recursive (HERE_D_FMT))
+ return NULL;
+ want_xday = 1;
+ break;
+ case 'k':
+ case 'H':
+ /* Match hour in 24-hour clock. */
+ get_number (0, 23, 2);
+ tm->tm_hour = val;
+ have_I = 0;
+ break;
+ case 'I':
+ /* Match hour in 12-hour clock. */
+ get_number (1, 12, 2);
+ tm->tm_hour = val % 12;
+ have_I = 1;
+ break;
+ case 'j':
+ /* Match day number of year. */
+ get_number (1, 366, 3);
+ tm->tm_yday = val - 1;
+ have_yday = 1;
+ break;
+ case 'm':
+ /* Match number of month. */
+ get_number (1, 12, 2);
+ tm->tm_mon = val - 1;
+ have_mon = 1;
+ want_xday = 1;
+ break;
+ case 'M':
+ /* Match minute. */
+ get_number (0, 59, 2);
+ tm->tm_min = val;
+ break;
+ case 'n':
+ case 't':
+ /* Match any white space. */
+ while (isspace (*rp))
+ ++rp;
+ break;
+ case 'p':
+ /* Match locale's equivalent of AM/PM. */
+#ifdef _NL_CURRENT
+ if (*decided != raw)
+ {
+ if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
+ {
+ if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
+ *decided = loc;
+ break;
+ }
+ if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
+ {
+ if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
+ *decided = loc;
+ is_pm = 1;
+ break;
+ }
+ *decided = raw;
+ }
+#endif
+ if (!match_string (HERE_AM_STR, rp))
+ if (match_string (HERE_PM_STR, rp))
+ is_pm = 1;
+ else
+ return NULL;
+ break;
+ case 'r':
+#ifdef _NL_CURRENT
+ if (*decided != raw)
+ {
+ if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
+ {
+ if (*decided == loc)
+ return NULL;
+ else
+ rp = rp_backup;
+ }
+ else
+ {
+ if (*decided == not &&
+ strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
+ HERE_T_FMT_AMPM))
+ *decided = loc;
+ break;
+ }
+ *decided = raw;
+ }
+#endif
+ if (!recursive (HERE_T_FMT_AMPM))
+ return NULL;
+ break;
+ case 'R':
+ if (!recursive ("%H:%M"))
+ return NULL;
+ break;
+ case 's':
+ {
+ /* The number of seconds may be very high so we cannot use
+ the `get_number' macro. Instead read the number
+ character for character and construct the result while
+ doing this. */
+ time_t secs = 0;
+ if (*rp < '0' || *rp > '9')
+ /* We need at least one digit. */
+ return NULL;
+
+ do
+ {
+ secs *= 10;
+ secs += *rp++ - '0';
+ }
+ while (*rp >= '0' && *rp <= '9');
+
+ if (localtime_r (&secs, tm) == NULL)
+ /* Error in function. */
+ return NULL;
+ }
+ break;
+ case 'S':
+ get_number (0, 61, 2);
+ tm->tm_sec = val;
+ break;
+ case 'X':
+#ifdef _NL_CURRENT
+ if (*decided != raw)
+ {
+ if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
+ {
+ if (*decided == loc)
+ return NULL;
+ else
+ rp = rp_backup;
+ }
+ else
+ {
+ if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
+ *decided = loc;
+ break;
+ }
+ *decided = raw;
+ }
+#endif
+ /* Fall through. */
+ case 'T':
+ if (!recursive (HERE_T_FMT))
+ return NULL;
+ break;
+ case 'u':
+ get_number (1, 7, 1);
+ tm->tm_wday = val % 7;
+ have_wday = 1;
+ break;
+ case 'g':
+ get_number (0, 99, 2);
+ /* XXX This cannot determine any field in TM. */
+ break;
+ case 'G':
+ if (*rp < '0' || *rp > '9')
+ return NULL;
+ /* XXX Ignore the number since we would need some more
+ information to compute a real date. */
+ do
+ ++rp;
+ while (*rp >= '0' && *rp <= '9');
+ break;
+ case 'U':
+ case 'V':
+ case 'W':
+ get_number (0, 53, 2);
+ /* XXX This cannot determine any field in TM without some
+ information. */
+ break;
+ case 'w':
+ /* Match number of weekday. */
+ get_number (0, 6, 1);
+ tm->tm_wday = val;
+ have_wday = 1;
+ break;
+ case 'y':
+#ifdef _NL_CURRENT
+ match_year_in_century:
+#endif
+ /* Match year within century. */
+ get_number (0, 99, 2);
+ /* The "Year 2000: The Millennium Rollover" paper suggests that
+ values in the range 69-99 refer to the twentieth century. */
+ tm->tm_year = val >= 69 ? val : val + 100;
+ /* Indicate that we want to use the century, if specified. */
+ want_century = 1;
+ want_xday = 1;
+ break;
+ case 'Y':
+ /* Match year including century number. */
+ get_number (0, 9999, 4);
+ tm->tm_year = val - 1900;
+ want_century = 0;
+ want_xday = 1;
+ break;
+ case 'Z':
+ /* XXX How to handle this? */
+ break;
+ case 'E':
+#ifdef _NL_CURRENT
+ switch (*fmt++)
+ {
+ case 'c':
+ /* Match locale's alternate date and time format. */
+ if (*decided != raw)
+ {
+ const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
+
+ if (*fmt == '\0')
+ fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
+
+ if (!recursive (fmt))
+ {
+ if (*decided == loc)
+ return NULL;
+ else
+ rp = rp_backup;
+ }
+ else
+ {
+ if (strcmp (fmt, HERE_D_T_FMT))
+ *decided = loc;
+ want_xday = 1;
+ break;
+ }
+ *decided = raw;
+ }
+ /* The C locale has no era information, so use the
+ normal representation. */
+ if (!recursive (HERE_D_T_FMT))
+ return NULL;
+ want_xday = 1;
+ break;
+ case 'C':
+ if (*decided != raw)
+ {
+ if (era_cnt >= 0)
+ {
+ era = _nl_select_era_entry (era_cnt);
+ if (match_string (era->era_name, rp))
+ {
+ *decided = loc;
+ break;
+ }
+ else
+ return NULL;
+ }
+ else
+ {
+ num_eras = _NL_CURRENT_WORD (LC_TIME,
+ _NL_TIME_ERA_NUM_ENTRIES);
+ for (era_cnt = 0; era_cnt < (int) num_eras;
+ ++era_cnt, rp = rp_backup)
+ {
+ era = _nl_select_era_entry (era_cnt);
+ if (match_string (era->era_name, rp))
+ {
+ *decided = loc;
+ break;
+ }
+ }
+ if (era_cnt == (int) num_eras)
+ {
+ era_cnt = -1;
+ if (*decided == loc)
+ return NULL;
+ }
+ else
+ break;
+ }
+
+ *decided = raw;
+ }
+ /* The C locale has no era information, so use the
+ normal representation. */
+ goto match_century;
+ case 'y':
+ if (*decided == raw)
+ goto match_year_in_century;
+
+ get_number(0, 9999, 4);
+ tm->tm_year = val;
+ want_era = 1;
+ want_xday = 1;
+ break;
+ case 'Y':
+ if (*decided != raw)
+ {
+ num_eras = _NL_CURRENT_WORD (LC_TIME,
+ _NL_TIME_ERA_NUM_ENTRIES);
+ for (era_cnt = 0; era_cnt < (int) num_eras;
+ ++era_cnt, rp = rp_backup)
+ {
+ era = _nl_select_era_entry (era_cnt);
+ if (recursive (era->era_format))
+ break;
+ }
+ if (era_cnt == (int) num_eras)
+ {
+ era_cnt = -1;
+ if (*decided == loc)
+ return NULL;
+ else
+ rp = rp_backup;
+ }
+ else
+ {
+ *decided = loc;
+ era_cnt = -1;
+ break;
+ }
+
+ *decided = raw;
+ }
+ get_number (0, 9999, 4);
+ tm->tm_year = val - 1900;
+ want_century = 0;
+ want_xday = 1;
+ break;
+ case 'x':
+ if (*decided != raw)
+ {
+ const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
+
+ if (*fmt == '\0')
+ fmt = _NL_CURRENT (LC_TIME, D_FMT);
+
+ if (!recursive (fmt))
+ {
+ if (*decided == loc)
+ return NULL;
+ else
+ rp = rp_backup;
+ }
+ else
+ {
+ if (strcmp (fmt, HERE_D_FMT))
+ *decided = loc;
+ break;
+ }
+ *decided = raw;
+ }
+ if (!recursive (HERE_D_FMT))
+ return NULL;
+ break;
+ case 'X':
+ if (*decided != raw)
+ {
+ const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
+
+ if (*fmt == '\0')
+ fmt = _NL_CURRENT (LC_TIME, T_FMT);
+
+ if (!recursive (fmt))
+ {
+ if (*decided == loc)
+ return NULL;
+ else
+ rp = rp_backup;
+ }
+ else
+ {
+ if (strcmp (fmt, HERE_T_FMT))
+ *decided = loc;
+ break;
+ }
+ *decided = raw;
+ }
+ if (!recursive (HERE_T_FMT))
+ return NULL;
+ break;
+ default:
+ return NULL;
+ }
+ break;
+#else
+ /* We have no information about the era format. Just use
+ the normal format. */
+ if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
+ && *fmt != 'x' && *fmt != 'X')
+ /* This is an illegal format. */
+ return NULL;
+
+ goto start_over;
+#endif
+ case 'O':
+ switch (*fmt++)
+ {
+ case 'd':
+ case 'e':
+ /* Match day of month using alternate numeric symbols. */
+ get_alt_number (1, 31, 2);
+ tm->tm_mday = val;
+ have_mday = 1;
+ want_xday = 1;
+ break;
+ case 'H':
+ /* Match hour in 24-hour clock using alternate numeric
+ symbols. */
+ get_alt_number (0, 23, 2);
+ tm->tm_hour = val;
+ have_I = 0;
+ break;
+ case 'I':
+ /* Match hour in 12-hour clock using alternate numeric
+ symbols. */
+ get_alt_number (1, 12, 2);
+ tm->tm_hour = val - 1;
+ have_I = 1;
+ break;
+ case 'm':
+ /* Match month using alternate numeric symbols. */
+ get_alt_number (1, 12, 2);
+ tm->tm_mon = val - 1;
+ have_mon = 1;
+ want_xday = 1;
+ break;
+ case 'M':
+ /* Match minutes using alternate numeric symbols. */
+ get_alt_number (0, 59, 2);
+ tm->tm_min = val;
+ break;
+ case 'S':
+ /* Match seconds using alternate numeric symbols. */
+ get_alt_number (0, 61, 2);
+ tm->tm_sec = val;
+ break;
+ case 'U':
+ case 'V':
+ case 'W':
+ get_alt_number (0, 53, 2);
+ /* XXX This cannot determine any field in TM without
+ further information. */
+ break;
+ case 'w':
+ /* Match number of weekday using alternate numeric symbols. */
+ get_alt_number (0, 6, 1);
+ tm->tm_wday = val;
+ have_wday = 1;
+ break;
+ case 'y':
+ /* Match year within century using alternate numeric symbols. */
+ get_alt_number (0, 99, 2);
+ tm->tm_year = val >= 69 ? val : val + 100;
+ want_xday = 1;
+ break;
+ default:
+ return NULL;
+ }
+ break;
+ default:
+ return NULL;
+ }
+ }
+
+ if (have_I && is_pm)
+ tm->tm_hour += 12;
+
+ if (century != -1)
+ {
+ if (want_century)
+ tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
+ else
+ /* Only the century, but not the year. Strange, but so be it. */
+ tm->tm_year = (century - 19) * 100;
+ }
+
+#ifdef _NL_CURRENT
+ if (era_cnt != -1)
+ {
+ era = _nl_select_era_entry(era_cnt);
+ if (want_era)
+ tm->tm_year = (era->start_date[0]
+ + ((tm->tm_year - era->offset)
+ * era->absolute_direction));
+ else
+ /* Era start year assumed. */
+ tm->tm_year = era->start_date[0];
+ }
+ else
+#endif
+ if (want_era)
+ return NULL;
+
+ if (want_xday && !have_wday)
+ {
+ if ( !(have_mon && have_mday) && have_yday)
+ {
+ /* We don't have tm_mon and/or tm_mday, compute them. */
+ int t_mon = 0;
+ while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
+ t_mon++;
+ if (!have_mon)
+ tm->tm_mon = t_mon - 1;
+ if (!have_mday)
+ tm->tm_mday =
+ (tm->tm_yday
+ - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
+ }
+ day_of_the_week (tm);
+ }
+ if (want_xday && !have_yday)
+ day_of_the_year (tm);
+
+ return (char *) rp;
+}
+
+
+char *
+strptime (buf, format, tm)
+ const char *buf;
+ const char *format;
+ struct tm *tm;
+{
+ enum locale_status decided;
+
+#ifdef _NL_CURRENT
+ decided = not;
+#else
+ decided = raw;
+#endif
+ return strptime_internal (buf, format, tm, &decided, -1);
+}
diff --git a/libavformat/strptime.h b/libavformat/strptime.h
new file mode 100644
index 0000000000..a1106fda40
--- /dev/null
+++ b/libavformat/strptime.h
@@ -0,0 +1,32 @@
+/* strptime.h
+ *
+ * $Id$
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __STRPTIME_H__
+#define __STRPTIME_H__
+
+/*
+ * Version of "strptime()", for the benefit of OSes that don't have it.
+ */
+extern char *strptime(const char *, const char *, struct tm *);
+
+#endif
diff --git a/libavformat/swf.c b/libavformat/swf.c
new file mode 100644
index 0000000000..14f8707f96
--- /dev/null
+++ b/libavformat/swf.c
@@ -0,0 +1,571 @@
+/*
+ * Flash Compatible Streaming Format
+ * Copyright (c) 2000 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"
+
+/* should have a generic way to indicate probable size */
+#define DUMMY_FILE_SIZE (100 * 1024 * 1024)
+#define DUMMY_DURATION 600 /* in seconds */
+
+#define TAG_END 0
+#define TAG_SHOWFRAME 1
+#define TAG_DEFINESHAPE 2
+#define TAG_FREECHARACTER 3
+#define TAG_PLACEOBJECT 4
+#define TAG_REMOVEOBJECT 5
+#define TAG_STREAMHEAD 18
+#define TAG_STREAMBLOCK 19
+#define TAG_JPEG2 21
+
+#define TAG_LONG 0x100
+
+/* flags for shape definition */
+#define FLAG_MOVETO 0x01
+#define FLAG_SETFILL0 0x02
+#define FLAG_SETFILL1 0x04
+
+/* character id used */
+#define BITMAP_ID 0
+#define SHAPE_ID 1
+
+typedef struct {
+ offset_t duration_pos;
+ offset_t tag_pos;
+ int tag;
+} SWFContext;
+
+static void put_swf_tag(AVFormatContext *s, int tag)
+{
+ SWFContext *swf = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+
+ swf->tag_pos = url_ftell(pb);
+ swf->tag = tag;
+ /* reserve some room for the tag */
+ if (tag & TAG_LONG) {
+ put_le16(pb, 0);
+ put_le32(pb, 0);
+ } else {
+ put_le16(pb, 0);
+ }
+}
+
+static void put_swf_end_tag(AVFormatContext *s)
+{
+ SWFContext *swf = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ offset_t pos;
+ int tag_len, tag;
+
+ pos = url_ftell(pb);
+ tag_len = pos - swf->tag_pos - 2;
+ tag = swf->tag;
+ url_fseek(pb, swf->tag_pos, SEEK_SET);
+ if (tag & TAG_LONG) {
+ tag &= ~TAG_LONG;
+ put_le16(pb, (tag << 6) | 0x3f);
+ put_le32(pb, tag_len - 4);
+ } else {
+ assert(tag_len < 0x3f);
+ put_le16(pb, (tag << 6) | tag_len);
+ }
+ url_fseek(pb, pos, SEEK_SET);
+}
+
+static inline void max_nbits(int *nbits_ptr, int val)
+{
+ int n;
+
+ if (val == 0)
+ return;
+ val = abs(val);
+ n = 1;
+ while (val != 0) {
+ n++;
+ val >>= 1;
+ }
+ if (n > *nbits_ptr)
+ *nbits_ptr = n;
+}
+
+static void put_swf_rect(ByteIOContext *pb,
+ int xmin, int xmax, int ymin, int ymax)
+{
+ PutBitContext p;
+ UINT8 buf[256];
+ int nbits, mask;
+
+ init_put_bits(&p, buf, sizeof(buf), NULL, NULL);
+
+ nbits = 0;
+ max_nbits(&nbits, xmin);
+ max_nbits(&nbits, xmax);
+ max_nbits(&nbits, ymin);
+ max_nbits(&nbits, ymax);
+ mask = (1 << nbits) - 1;
+
+ /* rectangle info */
+ put_bits(&p, 5, nbits);
+ put_bits(&p, nbits, xmin & mask);
+ put_bits(&p, nbits, xmax & mask);
+ put_bits(&p, nbits, ymin & mask);
+ put_bits(&p, nbits, ymax & mask);
+
+ flush_put_bits(&p);
+ put_buffer(pb, buf, pbBufPtr(&p) - p.buf);
+}
+
+static void put_swf_line_edge(PutBitContext *pb, int dx, int dy)
+{
+ int nbits, mask;
+
+ put_bits(pb, 1, 1); /* edge */
+ put_bits(pb, 1, 1); /* line select */
+ nbits = 2;
+ max_nbits(&nbits, dx);
+ max_nbits(&nbits, dy);
+
+ mask = (1 << nbits) - 1;
+ put_bits(pb, 4, nbits - 2); /* 16 bits precision */
+ if (dx == 0) {
+ put_bits(pb, 1, 0);
+ put_bits(pb, 1, 1);
+ put_bits(pb, nbits, dy & mask);
+ } else if (dy == 0) {
+ put_bits(pb, 1, 0);
+ put_bits(pb, 1, 0);
+ put_bits(pb, nbits, dx & mask);
+ } else {
+ put_bits(pb, 1, 1);
+ put_bits(pb, nbits, dx & mask);
+ put_bits(pb, nbits, dy & mask);
+ }
+}
+
+#define FRAC_BITS 16
+
+/* put matrix (not size optimized */
+static void put_swf_matrix(ByteIOContext *pb,
+ int a, int b, int c, int d, int tx, int ty)
+{
+ PutBitContext p;
+ UINT8 buf[256];
+
+ init_put_bits(&p, buf, sizeof(buf), NULL, NULL);
+
+ put_bits(&p, 1, 1); /* a, d present */
+ put_bits(&p, 5, 20); /* nb bits */
+ put_bits(&p, 20, a);
+ put_bits(&p, 20, d);
+
+ put_bits(&p, 1, 1); /* b, c present */
+ put_bits(&p, 5, 20); /* nb bits */
+ put_bits(&p, 20, c);
+ put_bits(&p, 20, b);
+
+ put_bits(&p, 5, 20); /* nb bits */
+ put_bits(&p, 20, tx);
+ put_bits(&p, 20, ty);
+
+ flush_put_bits(&p);
+ put_buffer(pb, buf, pbBufPtr(&p) - p.buf);
+}
+
+/* XXX: handle audio only */
+static int swf_write_header(AVFormatContext *s)
+{
+ SWFContext *swf;
+ ByteIOContext *pb = &s->pb;
+ AVCodecContext *enc, *audio_enc, *video_enc;
+ PutBitContext p;
+ UINT8 buf1[256];
+ int i, width, height, rate;
+
+ swf = av_malloc(sizeof(SWFContext));
+ if (!swf)
+ return -1;
+ s->priv_data = swf;
+
+ video_enc = NULL;
+ audio_enc = NULL;
+ for(i=0;i<s->nb_streams;i++) {
+ enc = &s->streams[i]->codec;
+ if (enc->codec_type == CODEC_TYPE_AUDIO)
+ audio_enc = enc;
+ else
+ video_enc = enc;
+ }
+
+ if (!video_enc) {
+ /* currenty, cannot work correctly if audio only */
+ width = 320;
+ height = 200;
+ rate = 10 * FRAME_RATE_BASE;
+ } else {
+ width = video_enc->width;
+ height = video_enc->height;
+ rate = video_enc->frame_rate;
+ }
+
+ put_tag(pb, "FWS");
+ put_byte(pb, 4); /* version (should use 4 for mpeg audio support) */
+ put_le32(pb, DUMMY_FILE_SIZE); /* dummy size
+ (will be patched if not streamed) */
+
+ put_swf_rect(pb, 0, width, 0, height);
+ put_le16(pb, (rate * 256) / FRAME_RATE_BASE); /* frame rate */
+ swf->duration_pos = url_ftell(pb);
+ put_le16(pb, (UINT16)(DUMMY_DURATION * (INT64)rate / FRAME_RATE_BASE)); /* frame count */
+
+ /* define a shape with the jpeg inside */
+
+ put_swf_tag(s, TAG_DEFINESHAPE);
+
+ put_le16(pb, SHAPE_ID); /* ID of shape */
+ /* bounding rectangle */
+ put_swf_rect(pb, 0, width, 0, height);
+ /* style info */
+ put_byte(pb, 1); /* one fill style */
+ put_byte(pb, 0x41); /* clipped bitmap fill */
+ put_le16(pb, BITMAP_ID); /* bitmap ID */
+ /* position of the bitmap */
+ put_swf_matrix(pb, (int)(1.0 * (1 << FRAC_BITS)), 0,
+ 0, (int)(1.0 * (1 << FRAC_BITS)), 0, 0);
+ put_byte(pb, 0); /* no line style */
+
+ /* shape drawing */
+ init_put_bits(&p, buf1, sizeof(buf1), NULL, NULL);
+ put_bits(&p, 4, 1); /* one fill bit */
+ put_bits(&p, 4, 0); /* zero line bit */
+
+ put_bits(&p, 1, 0); /* not an edge */
+ put_bits(&p, 5, FLAG_MOVETO | FLAG_SETFILL0);
+ put_bits(&p, 5, 1); /* nbits */
+ put_bits(&p, 1, 0); /* X */
+ put_bits(&p, 1, 0); /* Y */
+ put_bits(&p, 1, 1); /* set fill style 1 */
+
+ /* draw the rectangle ! */
+ put_swf_line_edge(&p, width, 0);
+ put_swf_line_edge(&p, 0, height);
+ put_swf_line_edge(&p, -width, 0);
+ put_swf_line_edge(&p, 0, -height);
+
+ /* end of shape */
+ put_bits(&p, 1, 0); /* not an edge */
+ put_bits(&p, 5, 0);
+
+ flush_put_bits(&p);
+ put_buffer(pb, buf1, pbBufPtr(&p) - p.buf);
+
+ put_swf_end_tag(s);
+
+
+ if (audio_enc) {
+ int v;
+
+ /* start sound */
+
+ v = 0;
+ switch(audio_enc->sample_rate) {
+ case 11025:
+ v |= 1 << 2;
+ break;
+ case 22050:
+ v |= 2 << 2;
+ break;
+ case 44100:
+ v |= 3 << 2;
+ break;
+ default:
+ /* not supported */
+ av_free(swf);
+ return -1;
+ }
+ if (audio_enc->channels == 2)
+ v |= 1;
+ v |= 0x20; /* mp3 compressed */
+ v |= 0x02; /* 16 bits */
+
+ put_swf_tag(s, TAG_STREAMHEAD);
+ put_byte(&s->pb, 0);
+ put_byte(&s->pb, v);
+ put_le16(&s->pb, (audio_enc->sample_rate * FRAME_RATE_BASE) / rate); /* avg samples per frame */
+
+
+ put_swf_end_tag(s);
+ }
+
+ put_flush_packet(&s->pb);
+ return 0;
+}
+
+static int swf_write_video(AVFormatContext *s,
+ AVCodecContext *enc, UINT8 *buf, int size)
+{
+ ByteIOContext *pb = &s->pb;
+ static int tag_id = 0;
+
+ if (enc->frame_number > 1) {
+ /* remove the shape */
+ put_swf_tag(s, TAG_REMOVEOBJECT);
+ put_le16(pb, SHAPE_ID); /* shape ID */
+ put_le16(pb, 1); /* depth */
+ put_swf_end_tag(s);
+
+ /* free the bitmap */
+ put_swf_tag(s, TAG_FREECHARACTER);
+ put_le16(pb, BITMAP_ID);
+ put_swf_end_tag(s);
+ }
+
+ put_swf_tag(s, TAG_JPEG2 | TAG_LONG);
+
+ put_le16(pb, tag_id); /* ID of the image */
+
+ /* a dummy jpeg header seems to be required */
+ put_byte(pb, 0xff);
+ put_byte(pb, 0xd8);
+ put_byte(pb, 0xff);
+ put_byte(pb, 0xd9);
+ /* write the jpeg image */
+ put_buffer(pb, buf, size);
+
+ put_swf_end_tag(s);
+
+ /* draw the shape */
+
+ put_swf_tag(s, TAG_PLACEOBJECT);
+ put_le16(pb, SHAPE_ID); /* shape ID */
+ put_le16(pb, 1); /* depth */
+ put_swf_matrix(pb, 1 << FRAC_BITS, 0, 0, 1 << FRAC_BITS, 0, 0);
+ put_swf_end_tag(s);
+
+ /* output the frame */
+ put_swf_tag(s, TAG_SHOWFRAME);
+ put_swf_end_tag(s);
+
+ put_flush_packet(&s->pb);
+ return 0;
+}
+
+static int swf_write_audio(AVFormatContext *s, UINT8 *buf, int size)
+{
+ ByteIOContext *pb = &s->pb;
+
+ put_swf_tag(s, TAG_STREAMBLOCK | TAG_LONG);
+
+ put_buffer(pb, buf, size);
+
+ put_swf_end_tag(s);
+ put_flush_packet(&s->pb);
+ return 0;
+}
+
+static int swf_write_packet(AVFormatContext *s, int stream_index,
+ UINT8 *buf, int size, int force_pts)
+{
+ AVCodecContext *codec = &s->streams[stream_index]->codec;
+ if (codec->codec_type == CODEC_TYPE_AUDIO)
+ return swf_write_audio(s, buf, size);
+ else
+ return swf_write_video(s, codec, buf, size);
+}
+
+static int swf_write_trailer(AVFormatContext *s)
+{
+ SWFContext *swf = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ AVCodecContext *enc, *video_enc;
+ int file_size, i;
+
+ video_enc = NULL;
+ for(i=0;i<s->nb_streams;i++) {
+ enc = &s->streams[i]->codec;
+ if (enc->codec_type == CODEC_TYPE_VIDEO)
+ video_enc = enc;
+ }
+
+ put_swf_tag(s, TAG_END);
+ put_swf_end_tag(s);
+
+ put_flush_packet(&s->pb);
+
+ /* patch file size and number of frames if not streamed */
+ if (!url_is_streamed(&s->pb) && video_enc) {
+ file_size = url_ftell(pb);
+ url_fseek(pb, 4, SEEK_SET);
+ put_le32(pb, file_size);
+ url_fseek(pb, swf->duration_pos, SEEK_SET);
+ put_le16(pb, video_enc->frame_number);
+ }
+ return 0;
+}
+
+/***********************************/
+/* just to extract MP3 from swf */
+
+static int get_swf_tag(ByteIOContext *pb, int *len_ptr)
+{
+ int tag, len;
+
+ if (url_feof(pb))
+ return -1;
+
+ tag = get_le16(pb);
+ len = tag & 0x3f;
+ tag = tag >> 6;
+ if (len == 0x3f) {
+ len = get_le32(pb);
+ }
+ *len_ptr = len;
+ return tag;
+}
+
+
+static int swf_probe(AVProbeData *p)
+{
+ /* check file header */
+ if (p->buf_size <= 16)
+ return 0;
+ if (p->buf[0] == 'F' && p->buf[1] == 'W' &&
+ p->buf[2] == 'S')
+ return AVPROBE_SCORE_MAX;
+ else
+ return 0;
+}
+
+static int swf_read_header(AVFormatContext *s, AVFormatParameters *ap)
+{
+ ByteIOContext *pb = &s->pb;
+ int nbits, len, frame_rate, tag, v;
+ AVStream *st;
+
+ if ((get_be32(pb) & 0xffffff00) != MKBETAG('F', 'W', 'S', 0))
+ return -EIO;
+ get_le32(pb);
+ /* skip rectangle size */
+ nbits = get_byte(pb) >> 3;
+ len = (4 * nbits - 3 + 7) / 8;
+ url_fskip(pb, len);
+ frame_rate = get_le16(pb);
+ get_le16(pb); /* frame count */
+
+ for(;;) {
+ tag = get_swf_tag(pb, &len);
+ if (tag < 0) {
+ fprintf(stderr, "No streaming found in SWF\n");
+ return -EIO;
+ }
+ if (tag == TAG_STREAMHEAD) {
+ /* streaming found */
+ get_byte(pb);
+ v = get_byte(pb);
+ get_le16(pb);
+ /* if mp3 streaming found, OK */
+ if ((v & 0x20) != 0) {
+ st = av_mallocz(sizeof(AVStream));
+ if (!st)
+ return -ENOMEM;
+ if (v & 0x01)
+ st->codec.channels = 2;
+ else
+ st->codec.channels = 1;
+ s->nb_streams = 1;
+ s->streams[0] = st;
+
+ switch((v>> 2) & 0x03) {
+ case 1:
+ st->codec.sample_rate = 11025;
+ break;
+ case 2:
+ st->codec.sample_rate = 22050;
+ break;
+ case 3:
+ st->codec.sample_rate = 44100;
+ break;
+ default:
+ av_free(st);
+ return -EIO;
+ }
+ st->codec.codec_type = CODEC_TYPE_AUDIO;
+ st->codec.codec_id = CODEC_ID_MP2;
+ break;
+ }
+ } else {
+ url_fskip(pb, len);
+ }
+ }
+
+ return 0;
+}
+
+static int swf_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ ByteIOContext *pb = &s->pb;
+ int tag, len;
+
+ for(;;) {
+ tag = get_swf_tag(pb, &len);
+ if (tag < 0)
+ return -EIO;
+ if (tag == TAG_STREAMBLOCK) {
+ av_new_packet(pkt, len);
+ get_buffer(pb, pkt->data, pkt->size);
+ break;
+ } else {
+ url_fskip(pb, len);
+ }
+ }
+ return 0;
+}
+
+static int swf_read_close(AVFormatContext *s)
+{
+ return 0;
+}
+
+static AVInputFormat swf_iformat = {
+ "swf",
+ "Flash format",
+ 0,
+ swf_probe,
+ swf_read_header,
+ swf_read_packet,
+ swf_read_close,
+};
+
+static AVOutputFormat swf_oformat = {
+ "swf",
+ "Flash format",
+ "application/x-shockwave-flash",
+ "swf",
+ sizeof(SWFContext),
+ CODEC_ID_MP2,
+ CODEC_ID_MJPEG,
+ swf_write_header,
+ swf_write_packet,
+ swf_write_trailer,
+};
+
+int swf_init(void)
+{
+ av_register_input_format(&swf_iformat);
+ av_register_output_format(&swf_oformat);
+ return 0;
+}
diff --git a/libavformat/tcp.c b/libavformat/tcp.c
new file mode 100644
index 0000000000..61d8665525
--- /dev/null
+++ b/libavformat/tcp.c
@@ -0,0 +1,176 @@
+/*
+ * TCP protocol
+ * Copyright (c) 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"
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#ifndef __BEOS__
+# include <arpa/inet.h>
+#else
+# include "barpainet.h"
+#endif
+#include <netdb.h>
+
+typedef struct TCPContext {
+ int fd;
+} TCPContext;
+
+/* resolve host with also IP address parsing */
+int resolve_host(struct in_addr *sin_addr, const char *hostname)
+{
+ struct hostent *hp;
+
+ if ((inet_aton(hostname, sin_addr)) == 0) {
+ hp = gethostbyname(hostname);
+ if (!hp)
+ return -1;
+ memcpy (sin_addr, hp->h_addr, sizeof(struct in_addr));
+ }
+ return 0;
+}
+
+/* return non zero if error */
+static int tcp_open(URLContext *h, const char *uri, int flags)
+{
+ struct sockaddr_in dest_addr;
+ char hostname[1024], *q;
+ int port, fd = -1;
+ TCPContext *s;
+ const char *p;
+
+ s = av_malloc(sizeof(TCPContext));
+ if (!s)
+ return -ENOMEM;
+ h->priv_data = s;
+ p = uri;
+ if (!strstart(p, "tcp://", &p))
+ goto fail;
+ q = hostname;
+ while (*p != ':' && *p != '/' && *p != '\0') {
+ if ((q - hostname) < sizeof(hostname) - 1)
+ *q++ = *p;
+ p++;
+ }
+ *q = '\0';
+ if (*p != ':')
+ goto fail;
+ p++;
+ port = strtoul(p, (char **)&p, 10);
+ if (port <= 0 || port >= 65536)
+ goto fail;
+
+ dest_addr.sin_family = AF_INET;
+ dest_addr.sin_port = htons(port);
+ if (resolve_host(&dest_addr.sin_addr, hostname) < 0)
+ goto fail;
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ goto fail;
+
+ if (connect(fd, (struct sockaddr *)&dest_addr,
+ sizeof(dest_addr)) < 0)
+ goto fail;
+
+ s->fd = fd;
+ return 0;
+
+ fail:
+ if (fd >= 0)
+ close(fd);
+ av_free(s);
+ return -EIO;
+}
+
+static int tcp_read(URLContext *h, UINT8 *buf, int size)
+{
+ TCPContext *s = h->priv_data;
+ int size1, len;
+
+ size1 = size;
+ while (size > 0) {
+#ifdef CONFIG_BEOS_NETSERVER
+ len = recv (s->fd, buf, size, 0);
+#else
+ len = read (s->fd, buf, size);
+#endif
+ if (len < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+#ifdef __BEOS__
+ return errno;
+#else
+ return -errno;
+#endif
+ else
+ continue;
+ } else if (len == 0) {
+ break;
+ }
+ size -= len;
+ buf += len;
+ }
+ return size1 - size;
+}
+
+static int tcp_write(URLContext *h, UINT8 *buf, int size)
+{
+ TCPContext *s = h->priv_data;
+ int ret, size1;
+
+ size1 = size;
+ while (size > 0) {
+#ifdef CONFIG_BEOS_NETSERVER
+ ret = send (s->fd, buf, size, 0);
+#else
+ ret = write (s->fd, buf, size);
+#endif
+ if (ret < 0 && errno != EINTR && errno != EAGAIN)
+#ifdef __BEOS__
+ return errno;
+#else
+ return -errno;
+#endif
+ size -= ret;
+ buf += ret;
+ }
+ return size1 - size;
+}
+
+static int tcp_close(URLContext *h)
+{
+ TCPContext *s = h->priv_data;
+#ifdef CONFIG_BEOS_NETSERVER
+ closesocket(s->fd);
+#else
+ close(s->fd);
+#endif
+ av_free(s);
+ return 0;
+}
+
+URLProtocol tcp_protocol = {
+ "tcp",
+ tcp_open,
+ tcp_read,
+ tcp_write,
+ NULL, /* seek */
+ tcp_close,
+};
diff --git a/libavformat/udp.c b/libavformat/udp.c
new file mode 100644
index 0000000000..3d159ef4f8
--- /dev/null
+++ b/libavformat/udp.c
@@ -0,0 +1,272 @@
+/*
+ * UDP prototype streaming system
+ * 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"
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#ifndef __BEOS__
+# include <arpa/inet.h>
+#else
+# include "barpainet.h"
+#endif
+#include <netdb.h>
+
+typedef struct {
+ int udp_fd;
+ int ttl;
+ int is_multicast;
+ int local_port;
+ struct ip_mreq mreq;
+ struct sockaddr_in dest_addr;
+} UDPContext;
+
+#define UDP_TX_BUF_SIZE 32768
+
+/**
+ * If no filename is given to av_open_input_file because you want to
+ * get the local port first, then you must call this function to set
+ * the remote server address.
+ *
+ * url syntax: udp://host:port[?option=val...]
+ * option: 'multicast=1' : enable multicast
+ * 'ttl=n' : set the ttl value (for multicast only)
+ * 'localport=n' : set the local port
+ *
+ * @param s1 media file context
+ * @param uri of the remote server
+ * @return zero if no error.
+ */
+int udp_set_remote_url(URLContext *h, const char *uri)
+{
+ UDPContext *s = h->priv_data;
+ char hostname[256];
+ int port;
+
+ url_split(NULL, 0, hostname, sizeof(hostname), &port, NULL, 0, uri);
+
+ /* set the destination address */
+ if (resolve_host(&s->dest_addr.sin_addr, hostname) < 0)
+ return -EIO;
+ s->dest_addr.sin_family = AF_INET;
+ s->dest_addr.sin_port = htons(port);
+ return 0;
+}
+
+/**
+ * Return the local port used by the UDP connexion
+ * @param s1 media file context
+ * @return the local port number
+ */
+int udp_get_local_port(URLContext *h)
+{
+ UDPContext *s = h->priv_data;
+ return s->local_port;
+}
+
+/**
+ * Return the udp file handle for select() usage to wait for several RTP
+ * streams at the same time.
+ * @param h media file context
+ */
+int udp_get_file_handle(URLContext *h)
+{
+ UDPContext *s = h->priv_data;
+ return s->udp_fd;
+}
+
+/* put it in UDP context */
+/* return non zero if error */
+static int udp_open(URLContext *h, const char *uri, int flags)
+{
+ struct sockaddr_in my_addr, my_addr1;
+ char hostname[1024];
+ int port, udp_fd = -1, tmp;
+ UDPContext *s = NULL;
+ int is_output, len;
+ const char *p;
+ char buf[256];
+
+ h->is_streamed = 1;
+
+ is_output = (flags & URL_WRONLY);
+
+ s = av_malloc(sizeof(UDPContext));
+ if (!s)
+ return -ENOMEM;
+
+ h->priv_data = s;
+ s->ttl = 16;
+ s->is_multicast = 0;
+ p = strchr(uri, '?');
+ if (p) {
+ s->is_multicast = find_info_tag(buf, sizeof(buf), "multicast", p);
+ if (find_info_tag(buf, sizeof(buf), "ttl", p)) {
+ s->ttl = strtol(buf, NULL, 10);
+ }
+ if (find_info_tag(buf, sizeof(buf), "localport", p)) {
+ s->local_port = strtol(buf, NULL, 10);
+ }
+ }
+
+ /* fill the dest addr */
+ url_split(NULL, 0, hostname, sizeof(hostname), &port, NULL, 0, uri);
+
+ /* XXX: fix url_split */
+ if (hostname[0] == '\0' || hostname[0] == '?') {
+ /* only accepts null hostname if input */
+ if (s->is_multicast || (flags & URL_WRONLY))
+ goto fail;
+ } else {
+ udp_set_remote_url(h, uri);
+ }
+
+ udp_fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (udp_fd < 0)
+ goto fail;
+
+ my_addr.sin_family = AF_INET;
+ my_addr.sin_addr.s_addr = htonl (INADDR_ANY);
+ if (s->is_multicast && !(h->flags & URL_WRONLY)) {
+ /* special case: the bind must be done on the multicast address port */
+ my_addr.sin_port = s->dest_addr.sin_port;
+ } else {
+ my_addr.sin_port = htons(s->local_port);
+ }
+
+ /* the bind is needed to give a port to the socket now */
+ if (bind(udp_fd,(struct sockaddr *)&my_addr, sizeof(my_addr)) < 0)
+ goto fail;
+
+ len = sizeof(my_addr1);
+ getsockname(udp_fd, (struct sockaddr *)&my_addr1, &len);
+ s->local_port = ntohs(my_addr1.sin_port);
+
+#ifndef CONFIG_BEOS_NETSERVER
+ if (s->is_multicast) {
+ if (h->flags & URL_WRONLY) {
+ /* output */
+ if (setsockopt(udp_fd, IPPROTO_IP, IP_MULTICAST_TTL,
+ &s->ttl, sizeof(s->ttl)) < 0) {
+ perror("IP_MULTICAST_TTL");
+ goto fail;
+ }
+ } else {
+ /* input */
+ memset(&s->mreq, 0, sizeof(s->mreq));
+ s->mreq.imr_multiaddr = s->dest_addr.sin_addr;
+ s->mreq.imr_interface.s_addr = htonl (INADDR_ANY);
+ if (setsockopt(udp_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &s->mreq, sizeof(s->mreq)) < 0) {
+ perror("rtp: IP_ADD_MEMBERSHIP");
+ goto fail;
+ }
+ }
+ }
+#endif
+
+ if (is_output) {
+ /* limit the tx buf size to limit latency */
+ tmp = UDP_TX_BUF_SIZE;
+ if (setsockopt(udp_fd, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(tmp)) < 0) {
+ perror("setsockopt sndbuf");
+ goto fail;
+ }
+ }
+
+ s->udp_fd = udp_fd;
+ h->max_packet_size = 1472; /* XXX: probe it ? */
+ return 0;
+ fail:
+ if (udp_fd >= 0)
+#ifdef CONFIG_BEOS_NETSERVER
+ closesocket(udp_fd);
+#else
+ close(udp_fd);
+#endif
+ av_free(s);
+ return -EIO;
+}
+
+static int udp_read(URLContext *h, UINT8 *buf, int size)
+{
+ UDPContext *s = h->priv_data;
+ struct sockaddr_in from;
+ int from_len, len;
+
+ for(;;) {
+ from_len = sizeof(from);
+ len = recvfrom (s->udp_fd, buf, size, 0,
+ (struct sockaddr *)&from, &from_len);
+ if (len < 0) {
+ if (errno != EAGAIN && errno != EINTR)
+ return -EIO;
+ } else {
+ break;
+ }
+ }
+ return len;
+}
+
+static int udp_write(URLContext *h, UINT8 *buf, int size)
+{
+ UDPContext *s = h->priv_data;
+ int ret;
+
+ for(;;) {
+ ret = sendto (s->udp_fd, buf, size, 0,
+ (struct sockaddr *) &s->dest_addr,
+ sizeof (s->dest_addr));
+ if (ret < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ return -EIO;
+ } else {
+ break;
+ }
+ }
+ return size;
+}
+
+static int udp_close(URLContext *h)
+{
+ UDPContext *s = h->priv_data;
+
+#ifndef CONFIG_BEOS_NETSERVER
+ if (s->is_multicast && !(h->flags & URL_WRONLY)) {
+ if (setsockopt(s->udp_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ &s->mreq, sizeof(s->mreq)) < 0) {
+ perror("IP_DROP_MEMBERSHIP");
+ }
+ }
+ close(s->udp_fd);
+#else
+ closesocket(s->udp_fd);
+#endif
+ av_free(s);
+ return 0;
+}
+
+URLProtocol udp_protocol = {
+ "udp",
+ udp_open,
+ udp_read,
+ udp_write,
+ NULL, /* seek */
+ udp_close,
+};
diff --git a/libavformat/utils.c b/libavformat/utils.c
new file mode 100644
index 0000000000..5a9aa082f4
--- /dev/null
+++ b/libavformat/utils.c
@@ -0,0 +1,1280 @@
+/*
+ * Various utilities for ffmpeg system
+ * 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"
+#include <ctype.h>
+#ifndef CONFIG_WIN32
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#else
+#define strcasecmp _stricmp
+#include <sys/types.h>
+#include <sys/timeb.h>
+#endif
+#include <time.h>
+
+#ifndef HAVE_STRPTIME
+#include "strptime.h"
+#endif
+
+AVInputFormat *first_iformat;
+AVOutputFormat *first_oformat;
+
+void av_register_input_format(AVInputFormat *format)
+{
+ AVInputFormat **p;
+ p = &first_iformat;
+ while (*p != NULL) p = &(*p)->next;
+ *p = format;
+ format->next = NULL;
+}
+
+void av_register_output_format(AVOutputFormat *format)
+{
+ AVOutputFormat **p;
+ p = &first_oformat;
+ while (*p != NULL) p = &(*p)->next;
+ *p = format;
+ format->next = NULL;
+}
+
+int match_ext(const char *filename, const char *extensions)
+{
+ const char *ext, *p;
+ char ext1[32], *q;
+
+ ext = strrchr(filename, '.');
+ if (ext) {
+ ext++;
+ p = extensions;
+ for(;;) {
+ q = ext1;
+ while (*p != '\0' && *p != ',')
+ *q++ = *p++;
+ *q = '\0';
+ if (!strcasecmp(ext1, ext))
+ return 1;
+ if (*p == '\0')
+ break;
+ p++;
+ }
+ }
+ return 0;
+}
+
+AVOutputFormat *guess_format(const char *short_name, const char *filename,
+ const char *mime_type)
+{
+ AVOutputFormat *fmt, *fmt_found;
+ int score_max, score;
+
+ /* find the proper file type */
+ fmt_found = NULL;
+ score_max = 0;
+ fmt = first_oformat;
+ while (fmt != NULL) {
+ score = 0;
+ if (fmt->name && short_name && !strcmp(fmt->name, short_name))
+ score += 100;
+ if (fmt->mime_type && mime_type && !strcmp(fmt->mime_type, mime_type))
+ score += 10;
+ if (filename && fmt->extensions &&
+ match_ext(filename, fmt->extensions)) {
+ score += 5;
+ }
+ if (score > score_max) {
+ score_max = score;
+ fmt_found = fmt;
+ }
+ fmt = fmt->next;
+ }
+ return fmt_found;
+}
+
+AVOutputFormat *guess_stream_format(const char *short_name, const char *filename,
+ const char *mime_type)
+{
+ AVOutputFormat *fmt = guess_format(short_name, filename, mime_type);
+
+ if (fmt) {
+ AVOutputFormat *stream_fmt;
+ char stream_format_name[64];
+
+ snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
+ stream_fmt = guess_format(stream_format_name, NULL, NULL);
+
+ if (stream_fmt)
+ fmt = stream_fmt;
+ }
+
+ return fmt;
+}
+
+AVInputFormat *av_find_input_format(const char *short_name)
+{
+ AVInputFormat *fmt;
+ for(fmt = first_iformat; fmt != NULL; fmt = fmt->next) {
+ if (!strcmp(fmt->name, short_name))
+ return fmt;
+ }
+ return NULL;
+}
+
+/* memory handling */
+
+/**
+ * Allocate the payload of a packet and intialized its fields to default values.
+ *
+ * @param pkt packet
+ * @param size wanted payload size
+ * @return 0 if OK. AVERROR_xxx otherwise.
+ */
+int av_new_packet(AVPacket *pkt, int size)
+{
+ int i;
+ pkt->data = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
+ if (!pkt->data)
+ return AVERROR_NOMEM;
+ pkt->size = size;
+ /* sane state */
+ pkt->pts = AV_NOPTS_VALUE;
+ pkt->stream_index = 0;
+ pkt->flags = 0;
+
+ for(i=0; i<FF_INPUT_BUFFER_PADDING_SIZE; i++)
+ pkt->data[size+i]= 0;
+
+ return 0;
+}
+
+/**
+ * Free a packet
+ *
+ * @param pkt packet to free
+ */
+void av_free_packet(AVPacket *pkt)
+{
+ av_freep(&pkt->data);
+ /* fail safe */
+ pkt->size = 0;
+}
+
+/* fifo handling */
+
+int fifo_init(FifoBuffer *f, int size)
+{
+ f->buffer = av_malloc(size);
+ if (!f->buffer)
+ return -1;
+ f->end = f->buffer + size;
+ f->wptr = f->rptr = f->buffer;
+ return 0;
+}
+
+void fifo_free(FifoBuffer *f)
+{
+ av_free(f->buffer);
+}
+
+int fifo_size(FifoBuffer *f, UINT8 *rptr)
+{
+ int size;
+
+ if (f->wptr >= rptr) {
+ size = f->wptr - rptr;
+ } else {
+ size = (f->end - rptr) + (f->wptr - f->buffer);
+ }
+ return size;
+}
+
+/* get data from the fifo (return -1 if not enough data) */
+int fifo_read(FifoBuffer *f, UINT8 *buf, int buf_size, UINT8 **rptr_ptr)
+{
+ UINT8 *rptr = *rptr_ptr;
+ int size, len;
+
+ if (f->wptr >= rptr) {
+ size = f->wptr - rptr;
+ } else {
+ size = (f->end - rptr) + (f->wptr - f->buffer);
+ }
+
+ if (size < buf_size)
+ return -1;
+ while (buf_size > 0) {
+ len = f->end - rptr;
+ if (len > buf_size)
+ len = buf_size;
+ memcpy(buf, rptr, len);
+ buf += len;
+ rptr += len;
+ if (rptr >= f->end)
+ rptr = f->buffer;
+ buf_size -= len;
+ }
+ *rptr_ptr = rptr;
+ return 0;
+}
+
+void fifo_write(FifoBuffer *f, UINT8 *buf, int size, UINT8 **wptr_ptr)
+{
+ int len;
+ UINT8 *wptr;
+ wptr = *wptr_ptr;
+ while (size > 0) {
+ len = f->end - wptr;
+ if (len > size)
+ len = size;
+ memcpy(wptr, buf, len);
+ wptr += len;
+ if (wptr >= f->end)
+ wptr = f->buffer;
+ buf += len;
+ size -= len;
+ }
+ *wptr_ptr = wptr;
+}
+
+int filename_number_test(const char *filename)
+{
+ char buf[1024];
+ return get_frame_filename(buf, sizeof(buf), filename, 1);
+}
+
+/* guess file format */
+AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened)
+{
+ AVInputFormat *fmt1, *fmt;
+ int score, score_max;
+
+ fmt = NULL;
+ score_max = 0;
+ for(fmt1 = first_iformat; fmt1 != NULL; fmt1 = fmt1->next) {
+ if (!is_opened && !(fmt1->flags & AVFMT_NOFILE))
+ continue;
+ score = 0;
+ if (fmt1->read_probe) {
+ score = fmt1->read_probe(pd);
+ } else if (fmt1->extensions) {
+ if (match_ext(pd->filename, fmt1->extensions)) {
+ score = 50;
+ }
+ }
+ if (score > score_max) {
+ score_max = score;
+ fmt = fmt1;
+ }
+ }
+ return fmt;
+}
+
+/************************************************************/
+/* input media file */
+
+#define PROBE_BUF_SIZE 2048
+
+/**
+ * Open a media file as input. The codec are not opened. Only the file
+ * header (if present) is read.
+ *
+ * @param ic_ptr the opened media file handle is put here
+ * @param filename filename to open.
+ * @param fmt if non NULL, force the file format to use
+ * @param buf_size optional buffer size (zero if default is OK)
+ * @param ap additionnal parameters needed when opening the file (NULL if default)
+ * @return 0 if OK. AVERROR_xxx otherwise.
+ */
+int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
+ AVInputFormat *fmt,
+ int buf_size,
+ AVFormatParameters *ap)
+{
+ AVFormatContext *ic = NULL;
+ int err;
+ char buf[PROBE_BUF_SIZE];
+ AVProbeData probe_data, *pd = &probe_data;
+
+ ic = av_mallocz(sizeof(AVFormatContext));
+ if (!ic) {
+ err = AVERROR_NOMEM;
+ goto fail;
+ }
+ pstrcpy(ic->filename, sizeof(ic->filename), filename);
+ pd->filename = ic->filename;
+ pd->buf = buf;
+ pd->buf_size = 0;
+
+ if (!fmt) {
+ /* guess format if no file can be opened */
+ fmt = av_probe_input_format(pd, 0);
+ }
+
+ /* if no file needed do not try to open one */
+ if (!fmt || !(fmt->flags & AVFMT_NOFILE)) {
+ if (url_fopen(&ic->pb, filename, URL_RDONLY) < 0) {
+ err = AVERROR_IO;
+ goto fail;
+ }
+ if (buf_size > 0) {
+ url_setbufsize(&ic->pb, buf_size);
+ }
+ if (!fmt) {
+ /* read probe data */
+ pd->buf_size = get_buffer(&ic->pb, buf, PROBE_BUF_SIZE);
+ url_fseek(&ic->pb, 0, SEEK_SET);
+ }
+ }
+
+ /* guess file format */
+ if (!fmt) {
+ fmt = av_probe_input_format(pd, 1);
+ }
+
+ /* if still no format found, error */
+ if (!fmt) {
+ err = AVERROR_NOFMT;
+ goto fail;
+ }
+
+ /* XXX: suppress this hack for redirectors */
+ if (fmt == &redir_demux) {
+ err = redir_open(ic_ptr, &ic->pb);
+ url_fclose(&ic->pb);
+ av_free(ic);
+ return err;
+ }
+
+ ic->iformat = fmt;
+
+ /* allocate private data */
+ ic->priv_data = av_mallocz(fmt->priv_data_size);
+ if (!ic->priv_data) {
+ err = AVERROR_NOMEM;
+ goto fail;
+ }
+
+ /* default pts settings is MPEG like */
+ av_set_pts_info(ic, 33, 1, 90000);
+
+ /* check filename in case of an image number is expected */
+ if (ic->iformat->flags & AVFMT_NEEDNUMBER) {
+ if (filename_number_test(ic->filename) < 0) {
+ err = AVERROR_NUMEXPECTED;
+ goto fail1;
+ }
+ }
+
+ err = ic->iformat->read_header(ic, ap);
+ if (err < 0)
+ goto fail1;
+ *ic_ptr = ic;
+ return 0;
+ fail1:
+ if (!(fmt->flags & AVFMT_NOFILE)) {
+ url_fclose(&ic->pb);
+ }
+ fail:
+ if (ic) {
+ av_freep(&ic->priv_data);
+ }
+ av_free(ic);
+ *ic_ptr = NULL;
+ return err;
+}
+
+/**
+ * Read a packet from a media file
+ * @param s media file handle
+ * @param pkt is filled
+ * @return 0 if OK. AVERROR_xxx if error.
+ */
+int av_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ AVPacketList *pktl;
+
+ pktl = s->packet_buffer;
+ if (pktl) {
+ /* read packet from packet buffer, if there is data */
+ *pkt = pktl->pkt;
+ s->packet_buffer = pktl->next;
+ av_free(pktl);
+ return 0;
+ } else {
+ return s->iformat->read_packet(s, pkt);
+ }
+}
+
+/* state for codec information */
+#define CSTATE_NOTFOUND 0
+#define CSTATE_DECODING 1
+#define CSTATE_FOUND 2
+
+static int has_codec_parameters(AVCodecContext *enc)
+{
+ int val;
+ switch(enc->codec_type) {
+ case CODEC_TYPE_AUDIO:
+ val = enc->sample_rate;
+ break;
+ case CODEC_TYPE_VIDEO:
+ val = enc->width;
+ break;
+ default:
+ val = 1;
+ break;
+ }
+ return (val != 0);
+}
+
+/**
+ * Read the beginning of a media file to get stream information. This
+ * is useful for file formats with no headers such as MPEG. This
+ * function also compute the real frame rate in case of mpeg2 repeat
+ * frame mode.
+ *
+ * @param ic media file handle
+ * @return >=0 if OK. AVERROR_xxx if error.
+ */
+int av_find_stream_info(AVFormatContext *ic)
+{
+ int i, count, ret, got_picture, size, read_size;
+ AVCodec *codec;
+ AVStream *st;
+ AVPacket *pkt;
+ AVPicture picture;
+ AVPacketList *pktl=NULL, **ppktl;
+ short samples[AVCODEC_MAX_AUDIO_FRAME_SIZE / 2];
+ UINT8 *ptr;
+ int min_read_size, max_read_size;
+
+ /* typical mpeg ts rate is 40 Mbits. DVD rate is about 10
+ Mbits. We read at most 0.1 second of file to find all streams */
+
+ /* XXX: base it on stream bitrate when possible */
+ if (ic->iformat == &mpegts_demux) {
+ /* maximum number of bytes we accept to read to find all the streams
+ in a file */
+ min_read_size = 3000000;
+ } else {
+ min_read_size = 125000;
+ }
+ /* max read size is 2 seconds of video max */
+ max_read_size = min_read_size * 20;
+
+ /* set initial codec state */
+ for(i=0;i<ic->nb_streams;i++) {
+ st = ic->streams[i];
+ if (has_codec_parameters(&st->codec))
+ st->codec_info_state = CSTATE_FOUND;
+ else
+ st->codec_info_state = CSTATE_NOTFOUND;
+ st->codec_info_nb_repeat_frames = 0;
+ st->codec_info_nb_real_frames = 0;
+ }
+
+ count = 0;
+ read_size = 0;
+ ppktl = &ic->packet_buffer;
+ for(;;) {
+ /* check if one codec still needs to be handled */
+ for(i=0;i<ic->nb_streams;i++) {
+ st = ic->streams[i];
+ if (st->codec_info_state != CSTATE_FOUND)
+ break;
+ }
+ if (i == ic->nb_streams) {
+ /* NOTE: if the format has no header, then we need to read
+ some packets to get most of the streams, so we cannot
+ stop here */
+ if (!(ic->iformat->flags & AVFMT_NOHEADER) ||
+ read_size >= min_read_size) {
+ /* if we found the info for all the codecs, we can stop */
+ ret = count;
+ break;
+ }
+ } else {
+ /* we did not get all the codec info, but we read too much data */
+ if (read_size >= max_read_size) {
+ ret = count;
+ break;
+ }
+ }
+
+ pktl = av_mallocz(sizeof(AVPacketList));
+ if (!pktl) {
+ ret = AVERROR_NOMEM;
+ break;
+ }
+
+ /* add the packet in the buffered packet list */
+ *ppktl = pktl;
+ ppktl = &pktl->next;
+
+ /* NOTE: a new stream can be added there if no header in file
+ (AVFMT_NOHEADER) */
+ pkt = &pktl->pkt;
+ if (ic->iformat->read_packet(ic, pkt) < 0) {
+ /* EOF or error */
+ ret = -1; /* we could not have all the codec parameters before EOF */
+ if ((ic->iformat->flags & AVFMT_NOHEADER) &&
+ i == ic->nb_streams)
+ ret = 0;
+ break;
+ }
+ read_size += pkt->size;
+
+ /* open new codecs */
+ for(i=0;i<ic->nb_streams;i++) {
+ st = ic->streams[i];
+ if (st->codec_info_state == CSTATE_NOTFOUND) {
+ /* set to found in case of error */
+ st->codec_info_state = CSTATE_FOUND;
+ codec = avcodec_find_decoder(st->codec.codec_id);
+ if (codec) {
+ if(codec->capabilities & CODEC_CAP_TRUNCATED)
+ st->codec.flags |= CODEC_FLAG_TRUNCATED;
+
+ ret = avcodec_open(&st->codec, codec);
+ if (ret >= 0)
+ st->codec_info_state = CSTATE_DECODING;
+ }
+ }
+ }
+
+ st = ic->streams[pkt->stream_index];
+ if (st->codec_info_state == CSTATE_DECODING) {
+ /* decode the data and update codec parameters */
+ ptr = pkt->data;
+ size = pkt->size;
+ while (size > 0) {
+ switch(st->codec.codec_type) {
+ case CODEC_TYPE_VIDEO:
+ ret = avcodec_decode_video(&st->codec, &picture,
+ &got_picture, ptr, size);
+ break;
+ case CODEC_TYPE_AUDIO:
+ ret = avcodec_decode_audio(&st->codec, samples,
+ &got_picture, ptr, size);
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ if (ret < 0) {
+ /* if error, simply ignore because another packet
+ may be OK */
+ break;
+ }
+ if (got_picture) {
+ /* we got the parameters - now we can stop
+ examining this stream */
+ /* XXX: add a codec info so that we can decide if
+ the codec can repeat frames */
+ if (st->codec.codec_id == CODEC_ID_MPEG1VIDEO &&
+ ic->iformat != &mpegts_demux &&
+ st->codec.sub_id == 2) {
+ /* for mpeg2 video, we want to know the real
+ frame rate, so we decode 40 frames. In mpeg
+ TS case we do not do it because it would be
+ too long */
+ st->codec_info_nb_real_frames++;
+ st->codec_info_nb_repeat_frames += st->codec.repeat_pict;
+#if 0
+ /* XXX: testing */
+ if ((st->codec_info_nb_real_frames % 24) == 23) {
+ st->codec_info_nb_repeat_frames += 2;
+ }
+#endif
+ /* stop after 40 frames */
+ if (st->codec_info_nb_real_frames >= 40) {
+ st->r_frame_rate = (st->codec.frame_rate *
+ st->codec_info_nb_real_frames) /
+ (st->codec_info_nb_real_frames +
+ (st->codec_info_nb_repeat_frames >> 1));
+ goto close_codec;
+ }
+ } else {
+ close_codec:
+ st->codec_info_state = CSTATE_FOUND;
+ avcodec_close(&st->codec);
+ break;
+ }
+ }
+ ptr += ret;
+ size -= ret;
+ }
+ }
+ count++;
+ }
+
+ /* close each codec if there are opened */
+ for(i=0;i<ic->nb_streams;i++) {
+ st = ic->streams[i];
+ if (st->codec_info_state == CSTATE_DECODING)
+ avcodec_close(&st->codec);
+ }
+
+ /* set real frame rate info */
+ for(i=0;i<ic->nb_streams;i++) {
+ st = ic->streams[i];
+ if (st->codec.codec_type == CODEC_TYPE_VIDEO) {
+ if (!st->r_frame_rate)
+ st->r_frame_rate = st->codec.frame_rate;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * Close a media file (but not its codecs)
+ *
+ * @param s media file handle
+ */
+void av_close_input_file(AVFormatContext *s)
+{
+ int i;
+
+ if (s->iformat->read_close)
+ s->iformat->read_close(s);
+ for(i=0;i<s->nb_streams;i++) {
+ av_free(s->streams[i]);
+ }
+ if (s->packet_buffer) {
+ AVPacketList *p, *p1;
+ p = s->packet_buffer;
+ while (p != NULL) {
+ p1 = p->next;
+ av_free_packet(&p->pkt);
+ av_free(p);
+ p = p1;
+ }
+ s->packet_buffer = NULL;
+ }
+ if (!(s->iformat->flags & AVFMT_NOFILE)) {
+ url_fclose(&s->pb);
+ }
+ av_freep(&s->priv_data);
+ av_free(s);
+}
+
+/**
+ * Add a new stream to a media file. Can only be called in the
+ * read_header function. If the flag AVFMT_NOHEADER is in the format
+ * description, then new streams can be added in read_packet too.
+ *
+ *
+ * @param s media file handle
+ * @param id file format dependent stream id
+ */
+AVStream *av_new_stream(AVFormatContext *s, int id)
+{
+ AVStream *st;
+
+ if (s->nb_streams >= MAX_STREAMS)
+ return NULL;
+
+ st = av_mallocz(sizeof(AVStream));
+ if (!st)
+ return NULL;
+ st->index = s->nb_streams;
+ st->id = id;
+ s->streams[s->nb_streams++] = st;
+ return st;
+}
+
+/************************************************************/
+/* output media file */
+
+/**
+ * allocate the stream private data and write the stream header to an
+ * output media file
+ *
+ * @param s media file handle
+ * @return 0 if OK. AVERROR_xxx if error.
+ */
+int av_write_header(AVFormatContext *s)
+{
+ int ret, i;
+ AVStream *st;
+
+ s->priv_data = av_mallocz(s->oformat->priv_data_size);
+ if (!s->priv_data)
+ return AVERROR_NOMEM;
+ /* default pts settings is MPEG like */
+ av_set_pts_info(s, 33, 1, 90000);
+ ret = s->oformat->write_header(s);
+ if (ret < 0)
+ return ret;
+
+ /* init PTS generation */
+ for(i=0;i<s->nb_streams;i++) {
+ st = s->streams[i];
+
+ switch (st->codec.codec_type) {
+ case CODEC_TYPE_AUDIO:
+ av_frac_init(&st->pts, 0, 0,
+ (INT64)s->pts_num * st->codec.sample_rate);
+ break;
+ case CODEC_TYPE_VIDEO:
+ av_frac_init(&st->pts, 0, 0,
+ (INT64)s->pts_num * st->codec.frame_rate);
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Write a packet to an output media file. The packet shall contain
+ * one audio or video frame.
+ *
+ * @param s media file handle
+ * @param stream_index stream index
+ * @param buf buffer containing the frame data
+ * @param size size of buffer
+ * @return < 0 if error, = 0 if OK, 1 if end of stream wanted.
+ */
+int av_write_frame(AVFormatContext *s, int stream_index, const uint8_t *buf,
+ int size)
+{
+ AVStream *st;
+ INT64 pts_mask;
+ int ret, frame_size;
+
+ st = s->streams[stream_index];
+ pts_mask = (1LL << s->pts_wrap_bits) - 1;
+ ret = s->oformat->write_packet(s, stream_index, (uint8_t *)buf, size,
+ st->pts.val & pts_mask);
+ if (ret < 0)
+ return ret;
+
+ /* update pts */
+ switch (st->codec.codec_type) {
+ case CODEC_TYPE_AUDIO:
+ if (st->codec.frame_size <= 1) {
+ frame_size = size / st->codec.channels;
+ /* specific hack for pcm codecs because no frame size is provided */
+ switch(st->codec.codec_id) {
+ case CODEC_ID_PCM_S16LE:
+ case CODEC_ID_PCM_S16BE:
+ case CODEC_ID_PCM_U16LE:
+ case CODEC_ID_PCM_U16BE:
+ frame_size >>= 1;
+ break;
+ default:
+ break;
+ }
+ } else {
+ frame_size = st->codec.frame_size;
+ }
+ av_frac_add(&st->pts,
+ (INT64)s->pts_den * frame_size);
+ break;
+ case CODEC_TYPE_VIDEO:
+ av_frac_add(&st->pts,
+ (INT64)s->pts_den * FRAME_RATE_BASE);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+/**
+ * write the stream trailer to an output media file and and free the
+ * file private data.
+ *
+ * @param s media file handle
+ * @return 0 if OK. AVERROR_xxx if error. */
+int av_write_trailer(AVFormatContext *s)
+{
+ int ret;
+ ret = s->oformat->write_trailer(s);
+ av_freep(&s->priv_data);
+ return ret;
+}
+
+/* "user interface" functions */
+
+void dump_format(AVFormatContext *ic,
+ int index,
+ const char *url,
+ int is_output)
+{
+ int i, flags;
+ char buf[256];
+
+ fprintf(stderr, "%s #%d, %s, %s '%s':\n",
+ is_output ? "Output" : "Input",
+ index,
+ is_output ? ic->oformat->name : ic->iformat->name,
+ is_output ? "to" : "from", url);
+ for(i=0;i<ic->nb_streams;i++) {
+ AVStream *st = ic->streams[i];
+ avcodec_string(buf, sizeof(buf), &st->codec, is_output);
+ fprintf(stderr, " Stream #%d.%d", index, i);
+ /* the pid is an important information, so we display it */
+ /* XXX: add a generic system */
+ if (is_output)
+ flags = ic->oformat->flags;
+ else
+ flags = ic->iformat->flags;
+ if (flags & AVFMT_SHOW_IDS) {
+ fprintf(stderr, "[0x%x]", st->id);
+ }
+ fprintf(stderr, ": %s\n", buf);
+ }
+}
+
+typedef struct {
+ const char *str;
+ int width, height;
+} SizeEntry;
+
+static SizeEntry sizes[] = {
+ { "sqcif", 128, 96 },
+ { "qcif", 176, 144 },
+ { "cif", 352, 288 },
+ { "4cif", 704, 576 },
+};
+
+int parse_image_size(int *width_ptr, int *height_ptr, const char *str)
+{
+ int i;
+ int n = sizeof(sizes) / sizeof(SizeEntry);
+ const char *p;
+ int frame_width = 0, frame_height = 0;
+
+ for(i=0;i<n;i++) {
+ if (!strcmp(sizes[i].str, str)) {
+ frame_width = sizes[i].width;
+ frame_height = sizes[i].height;
+ break;
+ }
+ }
+ if (i == n) {
+ p = str;
+ frame_width = strtol(p, (char **)&p, 10);
+ if (*p)
+ p++;
+ frame_height = strtol(p, (char **)&p, 10);
+ }
+ if (frame_width <= 0 || frame_height <= 0)
+ return -1;
+ *width_ptr = frame_width;
+ *height_ptr = frame_height;
+ return 0;
+}
+
+INT64 av_gettime(void)
+{
+#ifdef CONFIG_WIN32
+ struct _timeb tb;
+ _ftime(&tb);
+ return ((INT64)tb.time * INT64_C(1000) + (INT64)tb.millitm) * INT64_C(1000);
+#else
+ struct timeval tv;
+ gettimeofday(&tv,NULL);
+ return (INT64)tv.tv_sec * 1000000 + tv.tv_usec;
+#endif
+}
+
+static time_t mktimegm(struct tm *tm)
+{
+ time_t t;
+
+ int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday;
+
+ if (m < 3) {
+ m += 12;
+ y--;
+ }
+
+ t = 86400 *
+ (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 719469);
+
+ t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
+
+ return t;
+}
+
+/* Syntax:
+ * - If not a duration:
+ * [{YYYY-MM-DD|YYYYMMDD}]{T| }{HH[:MM[:SS[.m...]]][Z]|HH[MM[SS[.m...]]][Z]}
+ * Time is localtime unless Z is suffixed to the end. In this case GMT
+ * Return the date in micro seconds since 1970
+ * - If duration:
+ * HH[:MM[:SS[.m...]]]
+ * S+[.m...]
+ */
+INT64 parse_date(const char *datestr, int duration)
+{
+ const char *p;
+ INT64 t;
+ struct tm dt;
+ int i;
+ static const char *date_fmt[] = {
+ "%Y-%m-%d",
+ "%Y%m%d",
+ };
+ static const char *time_fmt[] = {
+ "%H:%M:%S",
+ "%H%M%S",
+ };
+ const char *q;
+ int is_utc, len;
+ char lastch;
+ time_t now = time(0);
+
+ len = strlen(datestr);
+ if (len > 0)
+ lastch = datestr[len - 1];
+ else
+ lastch = '\0';
+ is_utc = (lastch == 'z' || lastch == 'Z');
+
+ memset(&dt, 0, sizeof(dt));
+
+ p = datestr;
+ q = NULL;
+ if (!duration) {
+ for (i = 0; i < sizeof(date_fmt) / sizeof(date_fmt[0]); i++) {
+ q = strptime(p, date_fmt[i], &dt);
+ if (q) {
+ break;
+ }
+ }
+
+ if (!q) {
+ if (is_utc) {
+ dt = *gmtime(&now);
+ } else {
+ dt = *localtime(&now);
+ }
+ dt.tm_hour = dt.tm_min = dt.tm_sec = 0;
+ } else {
+ p = q;
+ }
+
+ if (*p == 'T' || *p == 't' || *p == ' ')
+ p++;
+
+ for (i = 0; i < sizeof(time_fmt) / sizeof(time_fmt[0]); i++) {
+ q = strptime(p, time_fmt[i], &dt);
+ if (q) {
+ break;
+ }
+ }
+ } else {
+ q = strptime(p, time_fmt[0], &dt);
+ if (!q) {
+ dt.tm_sec = strtol(p, (char **)&q, 10);
+ dt.tm_min = 0;
+ dt.tm_hour = 0;
+ }
+ }
+
+ /* Now we have all the fields that we can get */
+ if (!q) {
+ if (duration)
+ return 0;
+ else
+ return now * INT64_C(1000000);
+ }
+
+ if (duration) {
+ t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
+ } else {
+ dt.tm_isdst = -1; /* unknown */
+ if (is_utc) {
+ t = mktimegm(&dt);
+ } else {
+ t = mktime(&dt);
+ }
+ }
+
+ t *= 1000000;
+
+ if (*q == '.') {
+ int val, n;
+ q++;
+ for (val = 0, n = 100000; n >= 1; n /= 10, q++) {
+ if (!isdigit(*q))
+ break;
+ val += n * (*q - '0');
+ }
+ t += val;
+ }
+ return t;
+}
+
+/* syntax: '?tag1=val1&tag2=val2...'. Little URL decoding is done. Return
+ 1 if found */
+int find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
+{
+ const char *p;
+ char tag[128], *q;
+
+ p = info;
+ if (*p == '?')
+ p++;
+ for(;;) {
+ q = tag;
+ while (*p != '\0' && *p != '=' && *p != '&') {
+ if ((q - tag) < sizeof(tag) - 1)
+ *q++ = *p;
+ p++;
+ }
+ *q = '\0';
+ q = arg;
+ if (*p == '=') {
+ p++;
+ while (*p != '&' && *p != '\0') {
+ if ((q - arg) < arg_size - 1) {
+ if (*p == '+')
+ *q++ = ' ';
+ else
+ *q++ = *p;
+ }
+ p++;
+ }
+ *q = '\0';
+ }
+ if (!strcmp(tag, tag1))
+ return 1;
+ if (*p != '&')
+ break;
+ p++;
+ }
+ return 0;
+}
+
+/* Return in 'buf' the path with '%d' replaced by number. Also handles
+ the '%0nd' format where 'n' is the total number of digits and
+ '%%'. Return 0 if OK, and -1 if format error */
+int get_frame_filename(char *buf, int buf_size,
+ const char *path, int number)
+{
+ const char *p;
+ char *q, buf1[20];
+ int nd, len, c, percentd_found;
+
+ q = buf;
+ p = path;
+ percentd_found = 0;
+ for(;;) {
+ c = *p++;
+ if (c == '\0')
+ break;
+ if (c == '%') {
+ nd = 0;
+ while (*p >= '0' && *p <= '9') {
+ nd = nd * 10 + *p++ - '0';
+ }
+ c = *p++;
+ switch(c) {
+ case '%':
+ goto addchar;
+ case 'd':
+ if (percentd_found)
+ goto fail;
+ percentd_found = 1;
+ snprintf(buf1, sizeof(buf1), "%0*d", nd, number);
+ len = strlen(buf1);
+ if ((q - buf + len) > buf_size - 1)
+ goto fail;
+ memcpy(q, buf1, len);
+ q += len;
+ break;
+ default:
+ goto fail;
+ }
+ } else {
+ addchar:
+ if ((q - buf) < buf_size - 1)
+ *q++ = c;
+ }
+ }
+ if (!percentd_found)
+ goto fail;
+ *q = '\0';
+ return 0;
+ fail:
+ *q = '\0';
+ return -1;
+}
+
+/**
+ *
+ * Print on stdout a nice hexa dump of a buffer
+ * @param buf buffer
+ * @param size buffer size
+ */
+void av_hex_dump(UINT8 *buf, int size)
+{
+ int len, i, j, c;
+
+ for(i=0;i<size;i+=16) {
+ len = size - i;
+ if (len > 16)
+ len = 16;
+ printf("%08x ", i);
+ for(j=0;j<16;j++) {
+ if (j < len)
+ printf(" %02x", buf[i+j]);
+ else
+ printf(" ");
+ }
+ printf(" ");
+ for(j=0;j<len;j++) {
+ c = buf[i+j];
+ if (c < ' ' || c > '~')
+ c = '.';
+ printf("%c", c);
+ }
+ printf("\n");
+ }
+}
+
+void url_split(char *proto, int proto_size,
+ char *hostname, int hostname_size,
+ int *port_ptr,
+ char *path, int path_size,
+ const char *url)
+{
+ const char *p;
+ char *q;
+ int port;
+
+ port = -1;
+
+ p = url;
+ q = proto;
+ while (*p != ':' && *p != '\0') {
+ if ((q - proto) < proto_size - 1)
+ *q++ = *p;
+ p++;
+ }
+ if (proto_size > 0)
+ *q = '\0';
+ if (*p == '\0') {
+ if (proto_size > 0)
+ proto[0] = '\0';
+ if (hostname_size > 0)
+ hostname[0] = '\0';
+ p = url;
+ } else {
+ p++;
+ if (*p == '/')
+ p++;
+ if (*p == '/')
+ p++;
+ q = hostname;
+ while (*p != ':' && *p != '/' && *p != '?' && *p != '\0') {
+ if ((q - hostname) < hostname_size - 1)
+ *q++ = *p;
+ p++;
+ }
+ if (hostname_size > 0)
+ *q = '\0';
+ if (*p == ':') {
+ p++;
+ port = strtoul(p, (char **)&p, 10);
+ }
+ }
+ if (port_ptr)
+ *port_ptr = port;
+ pstrcpy(path, path_size, p);
+}
+
+/**
+ * Set the pts for a given stream
+ * @param s stream
+ * @param pts_wrap_bits number of bits effectively used by the pts
+ * (used for wrap control, 33 is the value for MPEG)
+ * @param pts_num numerator to convert to seconds (MPEG: 1)
+ * @param pts_den denominator to convert to seconds (MPEG: 90000)
+ */
+void av_set_pts_info(AVFormatContext *s, int pts_wrap_bits,
+ int pts_num, int pts_den)
+{
+ s->pts_wrap_bits = pts_wrap_bits;
+ s->pts_num = pts_num;
+ s->pts_den = pts_den;
+}
+
+/* fraction handling */
+
+/**
+ * f = val + (num / den) + 0.5. 'num' is normalized so that it is such
+ * as 0 <= num < den.
+ *
+ * @param f fractional number
+ * @param val integer value
+ * @param num must be >= 0
+ * @param den must be >= 1
+ */
+void av_frac_init(AVFrac *f, INT64 val, INT64 num, INT64 den)
+{
+ num += (den >> 1);
+ if (num >= den) {
+ val += num / den;
+ num = num % den;
+ }
+ f->val = val;
+ f->num = num;
+ f->den = den;
+}
+
+/* set f to (val + 0.5) */
+void av_frac_set(AVFrac *f, INT64 val)
+{
+ f->val = val;
+ f->num = f->den >> 1;
+}
+
+/**
+ * Fractionnal addition to f: f = f + (incr / f->den)
+ *
+ * @param f fractional number
+ * @param incr increment, can be positive or negative
+ */
+void av_frac_add(AVFrac *f, INT64 incr)
+{
+ INT64 num, den;
+
+ num = f->num + incr;
+ den = f->den;
+ if (num < 0) {
+ f->val += num / den;
+ num = num % den;
+ if (num < 0) {
+ num += den;
+ f->val--;
+ }
+ } else if (num >= den) {
+ f->val += num / den;
+ num = num % den;
+ }
+ f->num = num;
+}
diff --git a/libavformat/wav.c b/libavformat/wav.c
new file mode 100644
index 0000000000..f6bcd7b0e5
--- /dev/null
+++ b/libavformat/wav.c
@@ -0,0 +1,326 @@
+/*
+ * WAV encoder and decoder
+ * Copyright (c) 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"
+#include "avi.h"
+
+const CodecTag codec_wav_tags[] = {
+ { CODEC_ID_MP2, 0x50 },
+ { CODEC_ID_MP3LAME, 0x55 },
+ { CODEC_ID_AC3, 0x2000 },
+ { CODEC_ID_PCM_S16LE, 0x01 },
+ { CODEC_ID_PCM_U8, 0x01 }, /* must come after s16le in this list */
+ { CODEC_ID_PCM_ALAW, 0x06 },
+ { CODEC_ID_PCM_MULAW, 0x07 },
+ { CODEC_ID_ADPCM_MS, 0x02 },
+ { CODEC_ID_ADPCM_IMA_WAV, 0x11 },
+ { CODEC_ID_WMAV1, 0x160 },
+ { CODEC_ID_WMAV2, 0x161 },
+ { 0, 0 },
+};
+
+/* WAVEFORMATEX header */
+/* returns the size or -1 on error */
+int put_wav_header(ByteIOContext *pb, AVCodecContext *enc)
+{
+ int tag, bps, blkalign, bytespersec;
+ int hdrsize = 18;
+
+ tag = codec_get_tag(codec_wav_tags, enc->codec_id);
+ if (tag == 0)
+ return -1;
+ put_le16(pb, tag);
+ put_le16(pb, enc->channels);
+ put_le32(pb, enc->sample_rate);
+ if (enc->codec_id == CODEC_ID_PCM_U8 ||
+ enc->codec_id == CODEC_ID_PCM_ALAW ||
+ enc->codec_id == CODEC_ID_PCM_MULAW) {
+ bps = 8;
+ } else if (enc->codec_id == CODEC_ID_MP2 || enc->codec_id == CODEC_ID_MP3LAME) {
+ bps = 0;
+ } else if (enc->codec_id == CODEC_ID_ADPCM_IMA_WAV || enc->codec_id == CODEC_ID_ADPCM_MS) {
+ bps = 4;
+ } else {
+ bps = 16;
+ }
+
+ if (enc->codec_id == CODEC_ID_MP2 || enc->codec_id == CODEC_ID_MP3LAME) {
+ blkalign = 1;
+ //blkalign = 144 * enc->bit_rate/enc->sample_rate;
+ } else if (enc->block_align != 0) { /* specified by the codec */
+ blkalign = enc->block_align;
+ } else
+ blkalign = enc->channels*bps >> 3;
+ if (enc->codec_id == CODEC_ID_PCM_U8 ||
+ enc->codec_id == CODEC_ID_PCM_S16LE) {
+ bytespersec = enc->sample_rate * blkalign;
+ } else {
+ bytespersec = enc->bit_rate / 8;
+ }
+ put_le32(pb, bytespersec); /* bytes per second */
+ put_le16(pb, blkalign); /* block align */
+ put_le16(pb, bps); /* bits per sample */
+ if (enc->codec_id == CODEC_ID_MP3LAME) {
+ put_le16(pb, 12); /* wav_extra_size */
+ hdrsize += 12;
+ put_le16(pb, 1); /* wID */
+ put_le32(pb, 2); /* fdwFlags */
+ put_le16(pb, 1152); /* nBlockSize */
+ put_le16(pb, 1); /* nFramesPerBlock */
+ put_le16(pb, 1393); /* nCodecDelay */
+ } else if (enc->codec_id == CODEC_ID_MP2) {
+ put_le16(pb, 22); /* wav_extra_size */
+ hdrsize += 22;
+ put_le16(pb, 2); /* fwHeadLayer */
+ put_le32(pb, enc->bit_rate); /* dwHeadBitrate */
+ put_le16(pb, enc->channels == 2 ? 1 : 8); /* fwHeadMode */
+ put_le16(pb, 0); /* fwHeadModeExt */
+ put_le16(pb, 1); /* wHeadEmphasis */
+ put_le16(pb, 16); /* fwHeadFlags */
+ put_le32(pb, 0); /* dwPTSLow */
+ put_le32(pb, 0); /* dwPTSHigh */
+ } else if (enc->codec_id == CODEC_ID_ADPCM_IMA_WAV) {
+ put_le16(pb, 2); /* wav_extra_size */
+ put_le16(pb, ((enc->block_align - 4 * enc->channels) / (4 * enc->channels)) * 8 + 1); /* wSamplesPerBlock */
+ } else
+ put_le16(pb, 0); /* wav_extra_size */
+
+ return hdrsize;
+}
+
+void get_wav_header(ByteIOContext *pb, AVCodecContext *codec,
+ int has_extra_data)
+{
+ int id;
+
+ id = get_le16(pb);
+ codec->codec_type = CODEC_TYPE_AUDIO;
+ codec->codec_tag = id;
+ codec->fourcc = id;
+ codec->channels = get_le16(pb);
+ codec->sample_rate = get_le32(pb);
+ codec->bit_rate = get_le32(pb) * 8;
+ codec->block_align = get_le16(pb);
+ codec->frame_bits = get_le16(pb); /* bits per sample */
+ codec->codec_id = wav_codec_get_id(id, codec->frame_bits);
+ if (has_extra_data) {
+ codec->extradata_size = get_le16(pb);
+ if (codec->extradata_size > 0) {
+ codec->extradata = av_mallocz(codec->extradata_size);
+ get_buffer(pb, codec->extradata, codec->extradata_size);
+ }
+ }
+}
+
+
+int wav_codec_get_id(unsigned int tag, int bps)
+{
+ int id;
+ id = codec_get_id(codec_wav_tags, tag);
+ if (id <= 0)
+ return id;
+ /* handle specific u8 codec */
+ if (id == CODEC_ID_PCM_S16LE && bps == 8)
+ id = CODEC_ID_PCM_U8;
+ return id;
+}
+
+typedef struct {
+ offset_t data;
+} WAVContext;
+
+static int wav_write_header(AVFormatContext *s)
+{
+ WAVContext *wav = s->priv_data;
+ ByteIOContext *pb = &s->pb;
+ offset_t fmt;
+
+ put_tag(pb, "RIFF");
+ put_le32(pb, 0); /* file length */
+ put_tag(pb, "WAVE");
+
+ /* format header */
+ fmt = start_tag(pb, "fmt ");
+ if (put_wav_header(pb, &s->streams[0]->codec) < 0) {
+ av_free(wav);
+ return -1;
+ }
+ end_tag(pb, fmt);
+
+ /* data header */
+ wav->data = start_tag(pb, "data");
+
+ put_flush_packet(pb);
+
+ return 0;
+}
+
+static int wav_write_packet(AVFormatContext *s, int stream_index_ptr,
+ UINT8 *buf, int size, int force_pts)
+{
+ ByteIOContext *pb = &s->pb;
+ put_buffer(pb, buf, size);
+ return 0;
+}
+
+static int wav_write_trailer(AVFormatContext *s)
+{
+ ByteIOContext *pb = &s->pb;
+ WAVContext *wav = s->priv_data;
+ offset_t file_size;
+
+ if (!url_is_streamed(&s->pb)) {
+ end_tag(pb, wav->data);
+
+ /* update file size */
+ file_size = url_ftell(pb);
+ url_fseek(pb, 4, SEEK_SET);
+ put_le32(pb, (UINT32)(file_size - 8));
+ url_fseek(pb, file_size, SEEK_SET);
+
+ put_flush_packet(pb);
+ }
+ return 0;
+}
+
+/* return the size of the found tag */
+/* XXX: > 2GB ? */
+static int find_tag(ByteIOContext *pb, UINT32 tag1)
+{
+ unsigned int tag;
+ int size;
+
+ for(;;) {
+ if (url_feof(pb))
+ return -1;
+ tag = get_le32(pb);
+ size = get_le32(pb);
+ if (tag == tag1)
+ break;
+ url_fseek(pb, size, SEEK_CUR);
+ }
+ if (size < 0)
+ size = 0x7fffffff;
+ return size;
+}
+
+static int wav_probe(AVProbeData *p)
+{
+ /* check file header */
+ if (p->buf_size <= 32)
+ return 0;
+ if (p->buf[0] == 'R' && p->buf[1] == 'I' &&
+ p->buf[2] == 'F' && p->buf[3] == 'F' &&
+ p->buf[8] == 'W' && p->buf[9] == 'A' &&
+ p->buf[10] == 'V' && p->buf[11] == 'E')
+ return AVPROBE_SCORE_MAX;
+ else
+ return 0;
+}
+
+/* wav input */
+static int wav_read_header(AVFormatContext *s,
+ AVFormatParameters *ap)
+{
+ int size;
+ unsigned int tag;
+ ByteIOContext *pb = &s->pb;
+ AVStream *st;
+
+ /* check RIFF header */
+ tag = get_le32(pb);
+
+ if (tag != MKTAG('R', 'I', 'F', 'F'))
+ return -1;
+ get_le32(pb); /* file size */
+ tag = get_le32(pb);
+ if (tag != MKTAG('W', 'A', 'V', 'E'))
+ return -1;
+
+ /* parse fmt header */
+ size = find_tag(pb, MKTAG('f', 'm', 't', ' '));
+ if (size < 0)
+ return -1;
+ st = av_new_stream(s, 0);
+ if (!st)
+ return AVERROR_NOMEM;
+
+ get_wav_header(pb, &st->codec, (size >= 18));
+
+ size = find_tag(pb, MKTAG('d', 'a', 't', 'a'));
+ if (size < 0)
+ return -1;
+ return 0;
+}
+
+#define MAX_SIZE 4096
+
+static int wav_read_packet(AVFormatContext *s,
+ AVPacket *pkt)
+{
+ int ret;
+
+ if (url_feof(&s->pb))
+ return -EIO;
+ if (av_new_packet(pkt, MAX_SIZE))
+ return -EIO;
+ pkt->stream_index = 0;
+
+ ret = get_buffer(&s->pb, pkt->data, pkt->size);
+ if (ret < 0)
+ av_free_packet(pkt);
+ /* note: we need to modify the packet size here to handle the last
+ packet */
+ pkt->size = ret;
+ return ret;
+}
+
+static int wav_read_close(AVFormatContext *s)
+{
+ return 0;
+}
+
+static AVInputFormat wav_iformat = {
+ "wav",
+ "wav format",
+ 0,
+ wav_probe,
+ wav_read_header,
+ wav_read_packet,
+ wav_read_close,
+};
+
+static AVOutputFormat wav_oformat = {
+ "wav",
+ "wav format",
+ "audio/x-wav",
+ "wav",
+ sizeof(WAVContext),
+ CODEC_ID_PCM_S16LE,
+ CODEC_ID_NONE,
+ wav_write_header,
+ wav_write_packet,
+ wav_write_trailer,
+};
+
+int wav_init(void)
+{
+ av_register_input_format(&wav_iformat);
+ av_register_output_format(&wav_oformat);
+ return 0;
+}