diff options
author | Fabrice Bellard <fabrice@bellard.org> | 2001-07-22 14:18:56 +0000 |
---|---|---|
committer | Fabrice Bellard <fabrice@bellard.org> | 2001-07-22 14:18:56 +0000 |
commit | de6d9b6404bfd1c589799142da5a95428f146edd (patch) | |
tree | 75ae0cbb74bdfafb6f1a40922db111a103db3bcf /libav | |
parent | 1b58d58ddaf8a8c766a0353885ff504babed0453 (diff) | |
download | ffmpeg-de6d9b6404bfd1c589799142da5a95428f146edd.tar.gz |
Initial revision
Originally committed as revision 5 to svn://svn.ffmpeg.org/ffmpeg/trunk
Diffstat (limited to 'libav')
-rw-r--r-- | libav/asf.c | 956 | ||||
-rw-r--r-- | libav/audio.c | 179 | ||||
-rw-r--r-- | libav/avformat.h | 192 | ||||
-rw-r--r-- | libav/avi.h | 29 | ||||
-rw-r--r-- | libav/avidec.c | 257 | ||||
-rw-r--r-- | libav/avienc.c | 366 | ||||
-rw-r--r-- | libav/avio.c | 146 | ||||
-rw-r--r-- | libav/avio.h | 151 | ||||
-rw-r--r-- | libav/aviobuf.c | 426 | ||||
-rw-r--r-- | libav/ffm.c | 599 | ||||
-rw-r--r-- | libav/file.c | 121 | ||||
-rw-r--r-- | libav/grab.c | 321 | ||||
-rw-r--r-- | libav/http.c | 318 | ||||
-rw-r--r-- | libav/img.c | 578 | ||||
-rw-r--r-- | libav/jpegenc.c | 102 | ||||
-rw-r--r-- | libav/mpeg.c | 663 | ||||
-rw-r--r-- | libav/raw.c | 288 | ||||
-rw-r--r-- | libav/rm.c | 710 | ||||
-rw-r--r-- | libav/swf.c | 552 | ||||
-rw-r--r-- | libav/udp.c | 148 | ||||
-rw-r--r-- | libav/utils.c | 533 | ||||
-rw-r--r-- | libav/wav.c | 211 |
22 files changed, 7846 insertions, 0 deletions
diff --git a/libav/asf.c b/libav/asf.c new file mode 100644 index 0000000000..0306ed37c9 --- /dev/null +++ b/libav/asf.c @@ -0,0 +1,956 @@ +/* + * ASF compatible encoder and decoder. + * Copyright (c) 2000, 2001 Gerard Lantau. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include "avformat.h" +#include "avi.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; +} ASFStream; + +typedef struct { + int seqno; + int packet_size; + + ASFStream streams[2]; + /* non streamed additonnal info */ + int data_offset; + 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 */ + int packet_padsize; +} ASFContext; + +typedef struct { + UINT32 v1; + UINT16 v2; + UINT16 v3; + UINT8 v4[8]; +} GUID; + +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 }, +}; + +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) +{ + 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, 0); /* 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 * 10000000LL; + t += 116444736000000000LL; + 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; + + has_title = (s->title[0] != '\0'); + + if (!url_is_streamed(&s->pb)) { + put_guid(pb, &asf_header); + put_le64(pb, 0); /* 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); /* ??? */ + } else { + put_chunk(s, 0x4824, 0); /* start of stream (length will be patched later) */ + } + + /* 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, 0); /* ??? */ + put_le32(pb, asf->packet_size); /* packet size */ + put_le32(pb, asf->packet_size); /* packet size */ + put_le32(pb, 80 * asf->packet_size); /* frame_size ??? */ + 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++) { + 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); /* ??? */ + 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 */ + put_wav_header(pb, enc); + } 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); + } + 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++) { + enc = &s->streams[n]->codec; + + put_le16(pb, asf->streams[n].num); + put_str16(pb, 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 (!url_is_streamed(&s->pb)) { + header_size += 24 + 6; + url_fseek(pb, header_offset - 14, SEEK_SET); + put_le64(pb, header_size); + } else { + header_size += 8 + 50; + url_fseek(pb, header_offset - 10, SEEK_SET); + put_le16(pb, header_size); + url_fseek(pb, header_offset - 2, SEEK_SET); + put_le16(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; + + asf = av_mallocz(sizeof(ASFContext)); + if (!asf) + return -1; + s->priv_data = asf; + + asf->packet_size = PACKET_SIZE; + asf->nb_packets = 0; + + asf_write_header1(s, 0, 24); + + 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; +} + +/* write a fixed size packet */ +static void 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 (url_is_streamed(&s->pb)) { + put_chunk(s, 0x4424, asf->packet_size); + } + + 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); + if (flags & 0x08) + put_byte(pb, padsize); + put_le32(pb, timestamp); + put_le16(pb, duration); + put_byte(pb, nb_frames | 0x80); +} + +static void flush_packet(AVFormatContext *s) +{ + ASFContext *asf = s->priv_data; + int hdr_size, ptr; + + put_packet(s, asf->packet_timestamp_start, + asf->packet_timestamp_end - asf->packet_timestamp_start, + asf->packet_nb_frames, asf->packet_size_left); + + /* compute padding */ + hdr_size = PACKET_HEADER_SIZE; + if (asf->packet_size_left > 0) { + /* if padding needed, don't forget to count the + padding byte in the header size */ + hdr_size++; + asf->packet_size_left--; + /* XXX: I do not test again exact limit to avoid boundary problems */ + if (asf->packet_size_left > 200) { + hdr_size++; + asf->packet_size_left--; + } + } + ptr = asf->packet_size - PACKET_HEADER_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) + 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, 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) +{ + ASFContext *asf = s->priv_data; + int timestamp; + INT64 duration; + AVCodecContext *codec; + + codec = &s->streams[stream_index]->codec; + if (codec->codec_type == CODEC_TYPE_AUDIO) { + timestamp = (int)((float)codec->frame_number * codec->frame_size * 1000.0 / + codec->sample_rate); + duration = (codec->frame_number * codec->frame_size * 10000000LL) / + codec->sample_rate; + } else { + timestamp = (int)((float)codec->frame_number * 1000.0 * FRAME_RATE_BASE / + codec->frame_rate); + duration = codec->frame_number * + ((10000000LL * FRAME_RATE_BASE) / codec->frame_rate); + } + if (duration > asf->duration) + asf->duration = duration; + + put_frame(s, &asf->streams[stream_index], (int)timestamp, buf, size); + return 0; +} + +static int asf_write_trailer(AVFormatContext *s) +{ + ASFContext *asf = s->priv_data; + long long file_size; + + /* flush the current packet */ + if (asf->pb.buf_ptr > asf->pb.buffer) + flush_packet(s); + + if (url_is_streamed(&s->pb)) { + put_chunk(s, 0x4524, 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); + + free(asf); + 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_read_header(AVFormatContext *s, AVFormatParameters *ap) +{ + ASFContext *asf; + GUID g; + ByteIOContext *pb = &s->pb; + AVStream *st; + ASFStream *asf_st; + int size, i; + INT64 gsize; + + asf = av_mallocz(sizeof(ASFContext)); + if (!asf) + return -1; + s->priv_data = asf; + + 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); + + 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, &g); + get_le64(pb); /* file size */ + get_le64(pb); /* file time */ + get_le64(pb); /* nb_packets */ + get_le64(pb); /* length 0 in us */ + get_le64(pb); /* length 1 in us */ + get_le32(pb); + get_le32(pb); + get_le32(pb); + asf->packet_size = get_le32(pb); + get_le32(pb); + get_le32(pb); + } else if (!memcmp(&g, &stream_header, sizeof(GUID))) { + int type, id, 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; + + 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); /* stream id */ + get_le32(pb); + st->codec.codec_type = type; + if (type == CODEC_TYPE_AUDIO) { + id = get_le16(pb); + st->codec.codec_tag = id; + st->codec.codec_id = codec_get_id(codec_wav_tags, id); + st->codec.channels = get_le16(pb); + st->codec.sample_rate = get_le32(pb); + st->codec.bit_rate = get_le32(pb) * 8; + get_le16(pb); /* block align */ + get_le16(pb); /* bits per sample */ + size = get_le16(pb); + url_fskip(pb, size); + } 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); + st->codec.frame_rate = 25 * FRAME_RATE_BASE; /* XXX: find it */ + get_le16(pb); /* panes */ + get_le16(pb); /* depth */ + tag1 = get_le32(pb); + st->codec.codec_tag = tag1; + st->codec.codec_id = codec_get_id(codec_bmp_tags, tag1); + url_fskip(pb, size - 5 * 4); + } + 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); + + asf->packet_size_left = 0; + + return 0; + + fail: + for(i=0;i<s->nb_streams;i++) { + AVStream *st = s->streams[i]; + if (st) + free(st->priv_data); + free(st); + } + free(asf); + return -1; +} + +static int asf_get_packet(AVFormatContext *s) +{ + ASFContext *asf = s->priv_data; + ByteIOContext *pb = &s->pb; + int c, flags, timestamp, hdr_size; + + hdr_size = 12; + c = get_byte(pb); + if (c != 0x82) + return -EIO; + get_le16(pb); + flags = get_byte(pb); + get_byte(pb); + asf->packet_padsize = 0; + if (flags & 0x10) { + asf->packet_padsize = get_le16(pb); + hdr_size += 2; + } else if (flags & 0x08) { + asf->packet_padsize = get_byte(pb); + hdr_size++; + } + timestamp = get_le32(pb); + get_le16(pb); /* duration */ + get_byte(pb); /* nb_frames */ +#ifdef DEBUG + printf("packet: size=%d padsize=%d\n", asf->packet_size, asf->packet_padsize); +#endif + asf->packet_size_left = asf->packet_size - hdr_size; + return 0; +} + +static int asf_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + ASFContext *asf = s->priv_data; + AVStream *st; + ASFStream *asf_st; + ByteIOContext *pb = &s->pb; + int ret, num, seq, frag_offset, payload_size, frag_len; + int timestamp, i; + + for(;;) { + if (asf->packet_size_left < FRAME_HEADER_SIZE || + asf->packet_size_left <= asf->packet_padsize) { + /* fail safe */ + if (asf->packet_size_left) + url_fskip(pb, asf->packet_size_left); + ret = asf_get_packet(s); + if (ret < 0) + return -EIO; + } + /* read frame header */ + num = get_byte(pb) & 0x7f; + seq = get_byte(pb); + frag_offset = get_le32(pb); + get_byte(pb); /* flags */ + payload_size = get_le32(pb); + timestamp = get_le32(pb); + frag_len = get_le16(pb); +#ifdef DEBUG + printf("num=%d seq=%d totsize=%d frag_off=%d frag_size=%d\n", + num, seq, payload_size, frag_offset, frag_len); +#endif + st = NULL; + for(i=0;i<s->nb_streams;i++) { + st = s->streams[i]; + if (st->id == num) + break; + } + asf->packet_size_left -= FRAME_HEADER_SIZE + frag_len; + if (i == s->nb_streams) { + /* unhandled packet (should not happen) */ + url_fskip(pb, frag_len); + } else { + asf_st = st->priv_data; + if (asf_st->frag_offset == 0) { + /* new packet */ + av_new_packet(&asf_st->pkt, payload_size); + asf_st->seq = seq; + } else { + if (seq == asf_st->seq && + frag_offset == asf_st->frag_offset) { + /* continuing packet */ + } else { + /* cannot continue current packet: free it */ + av_free_packet(&asf_st->pkt); + asf_st->frag_offset = 0; + if (frag_offset != 0) { + /* cannot create new packet */ + url_fskip(pb, frag_len); + goto next_frame; + } else { + /* create new packet */ + av_new_packet(&asf_st->pkt, payload_size); + asf_st->seq = seq; + } + } + } + /* read data */ + get_buffer(pb, asf_st->pkt.data + frag_offset, frag_len); + asf_st->frag_offset += frag_len; + /* test if whole packet read */ + if (asf_st->frag_offset == asf_st->pkt.size) { + /* return packet */ + asf_st->pkt.stream_index = i; + asf_st->frag_offset = 0; + memcpy(pkt, &asf_st->pkt, sizeof(AVPacket)); + break; + } + } + next_frame: + } + + return 0; +} + +static int asf_read_close(AVFormatContext *s) +{ + ASFContext *asf = s->priv_data; + int i; + + for(i=0;i<s->nb_streams;i++) { + AVStream *st = s->streams[i]; + free(st->priv_data); + } + free(asf); + return 0; +} + +AVFormat asf_format = { + "asf", + "asf format", + "application/octet-stream", + "asf", + CODEC_ID_MP2, + CODEC_ID_MSMPEG4, + asf_write_header, + asf_write_packet, + asf_write_trailer, + + asf_read_header, + asf_read_packet, + asf_read_close, +}; diff --git a/libav/audio.c b/libav/audio.c new file mode 100644 index 0000000000..df5d88eaf6 --- /dev/null +++ b/libav/audio.c @@ -0,0 +1,179 @@ +/* + * Linux audio play and grab interface + * Copyright (c) 2000, 2001 Gerard Lantau. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <linux/soundcard.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <errno.h> +#include <sys/time.h> + +#include "avformat.h" + +const char *audio_device = "/dev/dsp"; + +typedef struct { + int fd; + int rate; + int channels; +} AudioData; + +#define AUDIO_BLOCK_SIZE 4096 + +/* audio read support */ + +static int audio_read(URLContext *h, UINT8 *buf, int size) +{ + AudioData *s = h->priv_data; + int ret; + + ret = read(s->fd, buf, size); + if (ret < 0) + return -errno; + else + return ret; +} + +static int audio_write(URLContext *h, UINT8 *buf, int size) +{ + AudioData *s = h->priv_data; + int ret; + + ret = write(s->fd, buf, size); + if (ret < 0) + return -errno; + else + return ret; +} + +static int audio_get_format(URLContext *h, URLFormat *f) +{ + AudioData *s = h->priv_data; + + strcpy(f->format_name, "pcm"); + f->sample_rate = s->rate; + f->channels = s->channels; + return 0; +} + +/* URI syntax: 'audio:[rate[,channels]]' + default: rate=44100, channels=2 + */ +static int audio_open(URLContext *h, const char *uri, int flags) +{ + AudioData *s; + const char *p; + int freq, channels, audio_fd; + int tmp, err; + + h->is_streamed = 1; + h->packet_size = AUDIO_BLOCK_SIZE; + + s = malloc(sizeof(AudioData)); + if (!s) + return -ENOMEM; + h->priv_data = s; + + /* extract parameters */ + p = uri; + strstart(p, "audio:", &p); + freq = strtol(p, (char **)&p, 0); + if (freq <= 0) + freq = 44100; + if (*p == ',') + p++; + channels = strtol(p, (char **)&p, 0); + if (channels <= 0) + channels = 2; + s->rate = freq; + s->channels = channels; + + /* open linux audio device */ + if (flags & URL_WRONLY) + audio_fd = open(audio_device,O_WRONLY); + else + audio_fd = open(audio_device,O_RDONLY); + if (audio_fd < 0) { + perror(audio_device); + return -EIO; + } + + /* non blocking mode */ + fcntl(audio_fd, F_SETFL, O_NONBLOCK); + +#if 0 + tmp=(NB_FRAGMENTS << 16) | FRAGMENT_BITS; + err=ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &tmp); + if (err < 0) { + perror("SNDCTL_DSP_SETFRAGMENT"); + } +#endif + + tmp=AFMT_S16_LE; + err=ioctl(audio_fd,SNDCTL_DSP_SETFMT,&tmp); + if (err < 0) { + perror("SNDCTL_DSP_SETFMT"); + goto fail; + } + + tmp= (channels == 2); + err=ioctl(audio_fd,SNDCTL_DSP_STEREO,&tmp); + if (err < 0) { + perror("SNDCTL_DSP_STEREO"); + goto fail; + } + + tmp = freq; + err=ioctl(audio_fd, SNDCTL_DSP_SPEED, &tmp); + if (err < 0) { + perror("SNDCTL_DSP_SPEED"); + goto fail; + } + + s->rate = tmp; + s->fd = audio_fd; + + return 0; + fail: + close(audio_fd); + free(s); + return -EIO; +} + +static int audio_close(URLContext *h) +{ + AudioData *s = h->priv_data; + + close(s->fd); + free(s); + return 0; +} + +URLProtocol audio_protocol = { + "audio", + audio_open, + audio_read, + audio_write, + NULL, /* seek */ + audio_close, + audio_get_format, +}; diff --git a/libav/avformat.h b/libav/avformat.h new file mode 100644 index 0000000000..44e9da2a55 --- /dev/null +++ b/libav/avformat.h @@ -0,0 +1,192 @@ + +#include "avcodec.h" + +#define FFMPEG_VERSION "0.4.5" + +#include "avio.h" + +/* packet functions */ + +typedef struct AVPacket { + INT64 pts; + UINT8 *data; + int size; + int stream_index; + int flags; +#define PKT_FLAG_KEY 0x0001 +} AVPacket; + +int av_new_packet(AVPacket *pkt, int size); +void av_free_packet(AVPacket *pkt); + +/*************************************************/ +/* output formats */ + +struct AVFormatContext; +struct AVFormatInputContext; + +typedef struct AVFormatParameters { + int frame_rate; + int sample_rate; + int channels; + int width; + int height; + int pix_fmt; +} AVFormatParameters; + +typedef struct AVFormat { + const char *name; + const char *long_name; + const char *mime_type; + const char *extensions; /* comma separated extensions */ + + /* output support */ + enum CodecID audio_codec; /* default audio codec */ + enum CodecID video_codec; /* default video codec */ + int (*write_header)(struct AVFormatContext *); + int (*write_packet)(struct AVFormatContext *, + int stream_index, + unsigned char *buf, int size); + int (*write_trailer)(struct AVFormatContext *); + + /* optional input support */ + /* read the format header and initialize the AVFormatInputContext + structure. Return 0 if OK. 'ap' if non NULL contains + additionnal paramters. Only used in raw format right now */ + int (*read_header)(struct AVFormatContext *, + AVFormatParameters *ap); + /* read one packet and put it in 'pkt'. pts and flags are also set */ + 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); + int flags; +#define AVFMT_NOFILE 0x0001 /* no file should be opened */ + struct AVFormat *next; +} AVFormat; + +typedef struct AVStream { + int id; /* internal stream id */ + AVCodecContext codec; /* codec context */ + void *priv_data; +} AVStream; + +#define MAX_STREAMS 20 + +/* format I/O context */ +typedef struct AVFormatContext { + struct AVFormat *format; + 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]; + /* 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 AVFormat *first_format; + +/* rv10enc.c */ +extern AVFormat rm_format; + +/* mpegmux.c */ +extern AVFormat mpeg_mux_format; + +/* asfenc.c */ +extern AVFormat asf_format; + +/* avienc.c */ +extern AVFormat avi_format; + +/* jpegenc.c */ +extern AVFormat mpjpeg_format; +extern AVFormat jpeg_format; + +/* swfenc.c */ +extern AVFormat swf_format; + +/* wav.c */ +extern AVFormat wav_format; + +/* img.c */ +extern AVFormat pgm_format; +extern AVFormat pgmyuv_format; +extern AVFormat imgyuv_format; +extern AVFormat pgmpipe_format; + +/* raw.c */ +extern AVFormat mp2_format; +extern AVFormat ac3_format; +extern AVFormat h263_format; +extern AVFormat mpeg1video_format; +extern AVFormat pcm_format; +extern AVFormat rawvideo_format; + +/* ffm.c */ +extern AVFormat ffm_format; + +/* formats.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 register_avformat(AVFormat *format); +AVFormat *guess_format(const char *short_name, const char *filename, const char *mime_type); + +int strstart(const char *str, const char *val, const char **ptr); +void nstrcpy(char *buf, int buf_size, const char *str); +int match_ext(const char *filename, const char *extensions); + +void register_all(void); + +INT64 gettime(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); + +AVFormatContext *av_open_input_file(const char *filename, int buf_size); +int av_read_packet(AVFormatContext *s, AVPacket *pkt); +void av_close_input_file(AVFormatContext *s); + +int av_write_packet(AVFormatContext *s, AVPacket *pkt); + +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 gettime(void); +INT64 parse_date(const char *datestr, int duration); + +/* 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); + diff --git a/libav/avi.h b/libav/avi.h new file mode 100644 index 0000000000..06834cfcbf --- /dev/null +++ b/libav/avi.h @@ -0,0 +1,29 @@ + +#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, char *tag); +void end_tag(ByteIOContext *pb, offset_t start); + +void put_bmp_header(ByteIOContext *pb, AVCodecContext *enc); +void put_wav_header(ByteIOContext *pb, AVCodecContext *enc); + +typedef struct CodecTag { + int id; + unsigned int tag; +} CodecTag; + +extern CodecTag codec_bmp_tags[]; +extern CodecTag codec_wav_tags[]; + +unsigned int codec_get_tag(CodecTag *tags, int id); +int codec_get_id(CodecTag *tags, unsigned int tag); + +/* avidec.c */ +int avi_read_header(AVFormatContext *s, AVFormatParameters *ap); +int avi_read_packet(AVFormatContext *s, AVPacket *pkt); +int avi_read_close(AVFormatContext *s); diff --git a/libav/avidec.c b/libav/avidec.c new file mode 100644 index 0000000000..ec90565feb --- /dev/null +++ b/libav/avidec.c @@ -0,0 +1,257 @@ +/* + * AVI decoder. + * Copyright (c) 2001 Gerard Lantau. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include <string.h> +#include <errno.h> + +#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 +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 + +int avi_read_header(AVFormatContext *s, AVFormatParameters *ap) +{ + AVIContext *avi; + ByteIOContext *pb = &s->pb; + UINT32 tag, tag1; + int codec_type, stream_index, size, frame_period, bit_rate; + int i; + AVStream *st; + + avi = malloc(sizeof(AVIContext)); + if (!avi) + return -1; + memset(avi, 0, sizeof(AVIContext)); + s->priv_data = avi; + + /* 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 */ + 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; + st = malloc(sizeof(AVStream)); + if (!st) + goto fail; + memset(st, 0, sizeof(AVStream)); + 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 */ + url_fskip(pb, size - 7 * 4); + break; + case MKTAG('a', 'u', 'd', 's'): + codec_type = CODEC_TYPE_AUDIO; + /* nothing really useful */ + url_fskip(pb, size - 4); + break; + default: + goto fail; + } + 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 = (1000000LL * 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: + tag1 = get_le16(pb); + st->codec.codec_type = CODEC_TYPE_AUDIO; + st->codec.codec_tag = tag1; + st->codec.codec_id = codec_get_id(codec_wav_tags, tag1); +#ifdef DEBUG + printf("audio: 0x%x\n", tag1); +#endif + st->codec.channels = get_le16(pb); + st->codec.sample_rate = get_le32(pb); + st->codec.bit_rate = get_le32(pb) * 8; + url_fskip(pb, size - 3 * 4); + 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++) { + if (s->streams[i]) + free(s->streams[i]); + } + return -1; + } + + return 0; +} + +int avi_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIContext *avi = s->priv_data; + ByteIOContext *pb = &s->pb; + int n, d1, d2, size; + + find_next: + if (url_feof(pb) || url_ftell(pb) >= avi->movi_end) + return -1; + d1 = get_byte(pb); + if (d1 < '0' || d1 > '9') + goto find_next; + d2 = get_byte(pb); + if (d2 < '0' || d2 > '9') + goto find_next; + n = (d1 - '0') * 10 + (d2 - '0'); + + if (n < 0 || n >= s->nb_streams) + goto find_next; + + d1 = get_byte(pb); + d2 = get_byte(pb); + if ((d1 != 'd' && d2 != 'c') && + (d1 != 'w' && d2 != 'b')) + goto find_next; + + 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; +} + +int avi_read_close(AVFormatContext *s) +{ + AVIContext *avi = s->priv_data; + free(avi); + return 0; +} diff --git a/libav/avienc.c b/libav/avienc.c new file mode 100644 index 0000000000..cef541d7ba --- /dev/null +++ b/libav/avienc.c @@ -0,0 +1,366 @@ +/* + * AVI encoder. + * Copyright (c) 2000 Gerard Lantau. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include <string.h> + +#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; + AVIIndex *first, *last; +} AVIContext; + +offset_t start_tag(ByteIOContext *pb, 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, 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. */ +CodecTag codec_bmp_tags[] = { + { CODEC_ID_H263, MKTAG('U', '2', '6', '3') }, + { CODEC_ID_H263I, MKTAG('I', '2', '6', '3') }, /* intel h263 */ + { CODEC_ID_MJPEG, MKTAG('M', 'J', 'P', 'G') }, + { CODEC_ID_OPENDIVX, MKTAG('D', 'I', 'V', 'X') }, + { CODEC_ID_OPENDIVX, MKTAG('d', 'i', 'v', 'x') }, + { CODEC_ID_OPENDIVX, MKTAG(0x04, 0, 0, 0) }, /* some broken avi use this */ + { CODEC_ID_MSMPEG4, MKTAG('D', 'I', 'V', '3') }, /* default signature when using MSMPEG4 */ + { CODEC_ID_MSMPEG4, MKTAG('M', 'P', '4', '3') }, + { 0, 0 }, +}; + +CodecTag codec_wav_tags[] = { + { CODEC_ID_MP2, 0x55 }, + { CODEC_ID_MP2, 0x50 }, + { CODEC_ID_AC3, 0x2000 }, + { CODEC_ID_PCM, 0x01 }, + { 0, 0 }, +}; + + +unsigned int codec_get_tag(CodecTag *tags, int id) +{ + while (tags->id != 0) { + if (tags->id == id) + return tags->tag; + tags++; + } + return 0; +} + +int codec_get_id(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) +{ + 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, codec_get_bmp_tag(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); +} + +/* WAVEFORMATEX header */ +void put_wav_header(ByteIOContext *pb, AVCodecContext *enc) +{ + int tag; + + tag = codec_get_tag(codec_wav_tags, enc->codec_id); + + put_le16(pb, tag); + put_le16(pb, enc->channels); + put_le32(pb, enc->sample_rate); + put_le32(pb, enc->bit_rate / 8); + put_le16(pb, 1); /* block align */ + put_le16(pb, 16); /* bits per sample */ + put_le16(pb, 0); /* wav_extra_size */ +} + +static int avi_write_header(AVFormatContext *s) +{ + AVIContext *avi; + ByteIOContext *pb = &s->pb; + int bitrate, n, i, nb_frames; + AVCodecContext *stream, *video_enc; + offset_t list1, list2, strh, strf; + + avi = malloc(sizeof(AVIContext)); + if (!avi) + return -1; + memset(avi, 0, sizeof(AVIContext)); + s->priv_data = avi; + + 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) { + free(avi); + return -1; + } + nb_frames = 0; + + put_le32(pb, 1000000LL * 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 */ + 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 */ + put_le32(pb, nb_frames); /* length, XXX: fill later */ + put_le32(pb, 1024 * 1024); /* suggested buffer size */ + put_le32(pb, 10000); /* 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, 0); + put_le32(pb, 0); /* flags */ + put_le16(pb, 0); /* priority */ + put_le16(pb, 0); /* language */ + put_le32(pb, 0); /* initial frame */ + put_le32(pb, 1); /* scale */ + put_le32(pb, stream->bit_rate / 8); /* rate */ + put_le32(pb, 0); /* start */ + put_le32(pb, 0); /* length, XXX: filled later */ + put_le32(pb, 12 * 1024); /* suggested buffer size */ + put_le32(pb, -1); /* quality */ + put_le32(pb, 1); /* sample size */ + put_le32(pb, 0); + put_le32(pb, 0); + break; + } + end_tag(pb, strh); + + strf = start_tag(pb, "strf"); + switch(stream->codec_type) { + case CODEC_TYPE_VIDEO: + put_bmp_header(pb, stream); + break; + case CODEC_TYPE_AUDIO: + put_wav_header(pb, stream); + break; + } + 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) +{ + 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 (!url_is_streamed(&s->pb)) { + idx = 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; + 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, file_size); + url_fseek(pb, file_size, SEEK_SET); + } + put_flush_packet(pb); + + free(avi); + return 0; +} + +AVFormat avi_format = { + "avi", + "avi format", + "video/x-msvideo", + "avi", + CODEC_ID_MP2, + CODEC_ID_MSMPEG4, + avi_write_header, + avi_write_packet, + avi_write_trailer, + + avi_read_header, + avi_read_packet, + avi_read_close, +}; diff --git a/libav/avio.c b/libav/avio.c new file mode 100644 index 0000000000..243f76f379 --- /dev/null +++ b/libav/avio.c @@ -0,0 +1,146 @@ +/* + * Unbuffered io for ffmpeg system + * Copyright (c) 2001 Gerard Lantau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#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 (*p == '\0') { + strcpy(proto_str, "file"); + } else { + *q = '\0'; + } + + up = first_protocol; + while (up != NULL) { + if (!strcmp(proto_str, up->name)) + goto found; + up = up->next; + } + return -ENOENT; + found: + uc = malloc(sizeof(URLContext)); + if (!uc) + return -ENOMEM; + uc->prot = up; + uc->flags = flags; + uc->is_streamed = 0; /* default = not streamed */ + uc->packet_size = 1; /* default packet size */ + err = up->url_open(uc, filename, flags); + if (err < 0) { + free(uc); + *puc = NULL; + return err; + } + *puc = uc; + return 0; +} + +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)) + 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_getformat(URLContext *h, URLFormat *f) +{ + memset(f, 0, sizeof(*f)); + if (!h->prot->url_getformat) + return -ENODATA; + return h->prot->url_getformat(h, f); +} + +int url_close(URLContext *h) +{ + int ret; + + ret = h->prot->url_close(h); + 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; +} diff --git a/libav/avio.h b/libav/avio.h new file mode 100644 index 0000000000..a3e11b61aa --- /dev/null +++ b/libav/avio.h @@ -0,0 +1,151 @@ +/* output byte stream handling */ + +typedef long long offset_t; + +/* unbuffered I/O */ + +struct URLContext { + struct URLProtocol *prot; + int flags; + int is_streamed; /* true if streamed (no seek possible), default = false */ + int packet_size; + void *priv_data; +}; + +typedef struct URLFormat { + char format_name[32]; + int sample_rate; + int frame_rate; + int channels; + int height; + int width; + int pix_fmt; +} URLFormat; + +typedef struct URLContext URLContext; + +typedef struct URLPollEntry { + URLContext *handle; + int events; + int revents; +} URLPollEntry; + +#define URL_RDONLY 0 +#define URL_WRONLY 1 +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_getformat(URLContext *h, URLFormat *f); +int url_close(URLContext *h); +int url_exist(const char *filename); +offset_t url_filesize(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); + /* get precise information about the format, if available. return + -ENODATA if not available */ + int (*url_getformat)(URLContext *h, URLFormat *f); + 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 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, unsigned char *buf, int size); +void put_le64(ByteIOContext *s, unsigned long long val); +void put_be64(ByteIOContext *s, unsigned long long 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, char *tag); + +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); + +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); +unsigned long long get_le64(ByteIOContext *s); +unsigned int get_le16(ByteIOContext *s); + +unsigned int get_be16(ByteIOContext *s); +unsigned int get_be32(ByteIOContext *s); +unsigned long long get_be64(ByteIOContext *s); + +extern inline int url_is_streamed(ByteIOContext *s) +{ + return s->is_streamed; +} +/* get the prefered packet size of the device. All I/Os should be done + by multiple of this size */ +extern inline int url_get_packet_size(ByteIOContext *s) +{ + return s->packet_size; +} + +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_open_buf(ByteIOContext *s, UINT8 *buf, int buf_size, int flags); +int url_close_buf(ByteIOContext *s); + +/* file.c */ +extern URLProtocol file_protocol; +extern URLProtocol pipe_protocol; + +/* udp.c */ +extern URLProtocol udp_protocol; + +/* http.c */ +extern URLProtocol http_protocol; + +/* audio.c */ +extern const char *audio_device; +extern URLProtocol audio_protocol; + +/* grab.c */ +extern const char *v4l_device; +extern URLProtocol video_protocol; diff --git a/libav/aviobuf.c b/libav/aviobuf.c new file mode 100644 index 0000000000..c50629e067 --- /dev/null +++ b/libav/aviobuf.c @@ -0,0 +1,426 @@ +/* + * Buffered I/O for ffmpeg system + * Copyright (c) 2000,2001 Gerard Lantau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <sys/time.h> +#include <getopt.h> +#include <string.h> + +#include "avformat.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->packet_size = 1; + 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, 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->eof_reached = 0; + s->seek(s->opaque, offset, SEEK_SET); + s->pos = offset; + } + } + 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); +} + +void put_le64(ByteIOContext *s, unsigned long long val) +{ + put_le32(s, val & 0xffffffff); + put_le32(s, val >> 32); +} + +void put_be64(ByteIOContext *s, unsigned long long val) +{ + put_be32(s, val >> 32); + put_be32(s, 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, char *tag) +{ + while (*tag) { + put_byte(s, *tag++); + } +} + +/* Input stream */ + +static void fill_buffer(ByteIOContext *s) +{ + int len; + + len = s->read_packet(s->opaque, s->buffer, s->buffer_size); + s->pos += len; + s->buf_ptr = s->buffer; + s->buf_end = s->buffer + len; + if (len == 0) { + s->eof_reached = 1; + } +} + +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; + } +} + +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; +} + +unsigned long long 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; +} + +unsigned long long 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, long long 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; + + buffer_size = (IO_BUFFER_SIZE / h->packet_size) * h->packet_size; + buffer = 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) { + free(buffer); + return -EIO; + } + s->is_streamed = h->is_streamed; + s->packet_size = h->packet_size; + return 0; +} + +/* XXX: must be called before any I/O */ +int url_setbufsize(ByteIOContext *s, int buf_size) +{ + UINT8 *buffer; + buffer = malloc(buf_size); + if (!buffer) + return -ENOMEM; + + 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; +} + +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; + + free(s->buffer); + memset(s, 0, sizeof(ByteIOContext)); + return url_close(h); +} + +URLContext *url_fileno(ByteIOContext *s) +{ + return s->opaque; +} + +/* 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) +{ + return s->buf_ptr - s->buffer; +} diff --git a/libav/ffm.c b/libav/ffm.c new file mode 100644 index 0000000000..76e7613286 --- /dev/null +++ b/libav/ffm.c @@ -0,0 +1,599 @@ +/* + * FFM (ffserver live feed) encoder and decoder + * Copyright (c) 2001 Gerard Lantau. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "avformat.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 5 +#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[1]; /* must be last */ +} FFMContext; + +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) +{ + AVStream *st; + FFMStream *fst; + FFMContext *ffm; + ByteIOContext *pb = &s->pb; + AVCodecContext *codec; + int bit_rate, i; + + ffm = av_mallocz(sizeof(FFMContext) + FFM_PACKET_SIZE); + if (!ffm) + return -1; + + s->priv_data = ffm; + 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) + ffm->packet_size); + 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); + /* 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); + break; + case CODEC_TYPE_AUDIO: + put_be32(pb, codec->sample_rate); + put_le16(pb, codec->channels); + break; + } + /* hack to have real time */ + fst->pts = 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]; + fst = st->priv_data; + if (fst) + free(fst); + } + free(ffm); + return -1; +} + +static int ffm_write_packet(AVFormatContext *s, int stream_index, + UINT8 *buf, int size) +{ + AVStream *st = s->streams[stream_index]; + FFMStream *fst = st->priv_data; + INT64 pts; + UINT8 header[FRAME_HEADER_SIZE]; + + 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; + ffm_write_data(s, header, FRAME_HEADER_SIZE, pts, 1); + ffm_write_data(s, buf, size, pts, 0); + + if (st->codec.codec_type == CODEC_TYPE_AUDIO) { + fst->pts += (INT64)((float)st->codec.frame_size / st->codec.sample_rate * 1000000.0); + } else { + fst->pts += (INT64)(1000000.0 * FRAME_RATE_BASE / (float)st->codec.frame_rate); + } + 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); + + for(i=0;i<s->nb_streams;i++) + free(s->streams[i]->priv_data); + free(ffm); + 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 (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); + 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)) { + ffm->first_packet = 0; + 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) +{ + AVStream *st; + FFMStream *fst; + FFMContext *ffm; + ByteIOContext *pb = &s->pb; + AVCodecContext *codec; + int i; + UINT32 tag; + + ffm = av_mallocz(sizeof(FFMContext) + FFM_PACKET_SIZE); + if (!ffm) + return -1; + + s->priv_data = ffm; + + /* 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 = (1ULL << 63) - 1; + } + + s->nb_streams = get_be32(pb); + get_be32(pb); /* total bitrate */ + /* read each stream */ + for(i=0;i<s->nb_streams;i++) { + st = av_mallocz(sizeof(AVStream)); + if (!st) + goto fail; + s->streams[i] = st; + fst = av_mallocz(sizeof(FFMStream) + ffm->packet_size); + 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); + /* 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); + break; + case CODEC_TYPE_AUDIO: + codec->sample_rate = get_be32(pb); + codec->channels = get_le16(pb); + break; + } + + } + + /* 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) { + fst = st->priv_data; + if (fst) + free(fst); + free(st); + } + } + if (ffm) + free(ffm); + return -1; +} + +/* return < 0 if eof */ +static int ffm_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int size; + FFMContext *ffm = s->priv_data; + + 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; + } + + 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; + } + 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]; + free(st->priv_data); + } + free(s->priv_data); + return 0; +} + +AVFormat ffm_format = { + "ffm", + "ffm format", + "", + "ffm", + /* not really used */ + CODEC_ID_MP2, + CODEC_ID_MPEG1VIDEO, + ffm_write_header, + ffm_write_packet, + ffm_write_trailer, + ffm_read_header, + ffm_read_packet, + ffm_read_close, + ffm_seek, +}; diff --git a/libav/file.c b/libav/file.c new file mode 100644 index 0000000000..0b255341c8 --- /dev/null +++ b/libav/file.c @@ -0,0 +1,121 @@ +/* + * Buffered file io for ffmpeg system + * Copyright (c) 2001 Gerard Lantau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <sys/time.h> + +#include "avformat.h" + +/* 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; + } + 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; + return lseek(fd, pos, whence); +} + +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/libav/grab.c b/libav/grab.c new file mode 100644 index 0000000000..f92cc397e6 --- /dev/null +++ b/libav/grab.c @@ -0,0 +1,321 @@ +/* + * Linux video grab interface + * Copyright (c) 2000,2001 Gerard Lantau. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <linux/videodev.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <errno.h> +#include <sys/time.h> + +#include "avformat.h" + +typedef struct { + int fd; + int frame_format; /* see VIDEO_PALETTE_xxx */ + int use_mmap; + int width, height; + float rate; + INT64 time_frame; +} VideoData; + +const char *v4l_device = "/dev/video"; + +/* XXX: move all that to the context */ + +static struct video_capability video_cap; +static UINT8 *video_buf; +static struct video_mbuf gb_buffers; +static struct video_mmap gb_buf; +static struct video_audio audio; +static int gb_frame = 0; + +static int v4l_init(URLContext *h) +{ + VideoData *s = h->priv_data; + int width, height; + int ret; + int video_fd, frame_size; + + width = s->width; + height = s->height; + + video_fd = open(v4l_device, O_RDWR); + if (video_fd < 0) { + perror(v4l_device); + return -EIO; + } + + if (ioctl(video_fd,VIDIOCGCAP,&video_cap) < 0) { + perror("VIDIOCGCAP"); + goto fail; + } + + if (!(video_cap.type & VID_TYPE_CAPTURE)) { + fprintf(stderr, "Fatal: grab device does not handle capture\n"); + goto fail; + } + + /* unmute audio */ + ioctl(video_fd, VIDIOCGAUDIO, &audio); + audio.flags &= ~VIDEO_AUDIO_MUTE; + ioctl(video_fd, VIDIOCSAUDIO, &audio); + + ret = ioctl(video_fd,VIDIOCGMBUF,&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=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 = gettime(); + s->use_mmap = 0; + } else { + video_buf = mmap(0,gb_buffers.size,PROT_READ|PROT_WRITE,MAP_SHARED,video_fd,0); + if ((unsigned char*)-1 == video_buf) { + perror("mmap"); + goto fail; + } + gb_frame = 0; + s->time_frame = gettime(); + + /* start to grab the first frame */ + gb_buf.frame = 1 - gb_frame; + gb_buf.height = height; + gb_buf.width = width; + gb_buf.format = VIDEO_PALETTE_YUV420P; + + ret = ioctl(video_fd, VIDIOCMCAPTURE, &gb_buf); + if (ret < 0 && errno != EAGAIN) { + /* try YUV422 */ + gb_buf.format = VIDEO_PALETTE_YUV422; + + ret = ioctl(video_fd, VIDIOCMCAPTURE, &gb_buf); + if (ret < 0 && errno != EAGAIN) { + /* try RGB24 */ + gb_buf.format = VIDEO_PALETTE_RGB24; + ret = ioctl(video_fd, VIDIOCMCAPTURE, &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 = gb_buf.format; + s->use_mmap = 1; + } + + switch(s->frame_format) { + case VIDEO_PALETTE_YUV420P: + frame_size = (width * height * 3) / 2; + break; + case VIDEO_PALETTE_YUV422: + frame_size = width * height * 2; + break; + case VIDEO_PALETTE_RGB24: + frame_size = width * height * 3; + break; + default: + goto fail; + } + s->fd = video_fd; + h->packet_size = frame_size; + return 0; + fail: + close(video_fd); + return -EIO; +} + +static int v4l_mm_read_picture(URLContext *h, UINT8 *buf) +{ + VideoData *s = h->priv_data; + UINT8 *ptr; + + gb_buf.frame = gb_frame; + if (ioctl(s->fd, VIDIOCMCAPTURE, &gb_buf) < 0) { + if (errno == EAGAIN) + fprintf(stderr,"Cannot Sync\n"); + else + perror("VIDIOCMCAPTURE"); + return -EIO; + } + gb_frame = 1 - gb_frame; + + while (ioctl(s->fd, VIDIOCSYNC, &gb_frame) < 0 && + (errno == EAGAIN || errno == EINTR)); + + ptr = video_buf + gb_buffers.offsets[gb_frame]; + memcpy(buf, ptr, h->packet_size); + return h->packet_size; +} + +/* note: we support only one picture read at a time */ +static int video_read(URLContext *h, UINT8 *buf, int size) +{ + VideoData *s = h->priv_data; + INT64 curtime; + + if (size != h->packet_size) + return -EINVAL; + + /* wait based on the frame rate */ + s->time_frame += (int)(1000000 / s->rate); + do { + curtime = gettime(); + } while (curtime < s->time_frame); + + /* read one frame */ + if (s->use_mmap) { + return v4l_mm_read_picture(h, buf); + } else { + if (read(s->fd, buf, size) != size) + return -EIO; + return h->packet_size; + } +} + +static int video_get_format(URLContext *h, URLFormat *f) +{ + VideoData *s = h->priv_data; + + f->width = s->width; + f->height = s->height; + f->frame_rate = (int)(s->rate * FRAME_RATE_BASE); + strcpy(f->format_name, "rawvideo"); + + switch(s->frame_format) { + case VIDEO_PALETTE_YUV420P: + f->pix_fmt = PIX_FMT_YUV420P; + break; + case VIDEO_PALETTE_YUV422: + f->pix_fmt = PIX_FMT_YUV422; + break; + case VIDEO_PALETTE_RGB24: + f->pix_fmt = PIX_FMT_BGR24; /* NOTE: v4l uses BGR24, not RGB24 ! */ + break; + default: + abort(); + } + return 0; +} + +/* URI syntax: 'video:width,height,rate' + */ +static int video_open(URLContext *h, const char *uri, int flags) +{ + VideoData *s; + const char *p; + int width, height; + int ret; + float rate; + + /* extract parameters */ + p = uri; + strstart(p, "video:", &p); + width = strtol(p, (char **)&p, 0); + if (width <= 0) + return -EINVAL; + if (*p == ',') + p++; + height = strtol(p, (char **)&p, 0); + if (height <= 0) + return -EINVAL; + if (*p == ',') + p++; + rate = strtod(p, (char **)&p); + if (rate <= 0) + return -EINVAL; + + s = malloc(sizeof(VideoData)); + if (!s) + return -ENOMEM; + h->priv_data = s; + h->is_streamed = 1; + s->width = width; + s->height = height; + s->rate = rate; + ret = v4l_init(h); + if (ret) + free(s); + return ret; +} + +static int video_close(URLContext *h) +{ + VideoData *s = h->priv_data; + + close(s->fd); + free(s); + return 0; +} + +URLProtocol video_protocol = { + "video", + video_open, + video_read, + NULL, + NULL, /* seek */ + video_close, + video_get_format, +}; diff --git a/libav/http.c b/libav/http.c new file mode 100644 index 0000000000..824f98b9e3 --- /dev/null +++ b/libav/http.c @@ -0,0 +1,318 @@ +/* + * HTTP protocol for ffmpeg client + * Copyright (c) 2000, 2001 Gerard Lantau. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include "avformat.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 { + int fd; + 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); +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) +{ + struct sockaddr_in dest_addr; + const char *p, *path, *proxy_path; + char hostname[1024], *q; + int port, fd = -1, use_proxy; + struct hostent *hp; + HTTPContext *s; + + h->is_streamed = 1; + + s = malloc(sizeof(HTTPContext)); + if (!s) { + return -ENOMEM; + } + h->priv_data = s; + + proxy_path = getenv("http_proxy"); + use_proxy = (proxy_path != NULL) && !getenv("no_proxy"); + + /* fill the dest addr */ + redo: + if (use_proxy) { + p = proxy_path; + } else { + p = uri; + } + if (!strstart(p, "http://", &p)) + goto fail; + q = hostname; + while (*p != ':' && *p != '/' && *p != '\0') { + if ((q - hostname) < sizeof(hostname) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + port = 80; + if (*p == ':') { + p++; + port = strtoul(p, (char **)&p, 10); + } + if (port <= 0) + goto fail; + if (use_proxy) { + path = uri; + } else { + if (*p == '\0') + path = "/"; + else + path = p; + } + + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(port); + if ((inet_aton(hostname, &dest_addr.sin_addr)) == 0) { + hp = gethostbyname(hostname); + if (!hp) + goto fail; + memcpy (&dest_addr.sin_addr, hp->h_addr, sizeof(dest_addr.sin_addr)); + } + + 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; + if (http_connect(h, path) < 0) + goto fail; + if (s->http_code == 303 && s->location[0] != '\0') { + /* url moved, get next */ + uri = s->location; + goto redo; + } + + return 0; + fail: + if (fd >= 0) + close(fd); + free(s); + return -EIO; +} + +static int http_getc(HTTPContext *s) +{ + int len; + if (s->buf_ptr >= s->buf_end) { + redo: + len = read(s->fd, s->buffer, BUFFER_SIZE); + if (len < 0) { + if (errno == EAGAIN || errno == EINTR) + goto redo; + 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) +{ + 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" + "\n", + post ? "POST" : "GET", + path, + FFMPEG_VERSION + ); + + 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) + 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 = read (s->fd, buf, size); + if (len < 0) { + if (errno != EINTR && errno != EAGAIN) + return -errno; + else + continue; + } 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; + int ret, size1; + + size1 = size; + while (size > 0) { + ret = write (s->fd, buf, size); + if (ret < 0 && errno != EINTR && errno != EAGAIN) + return -errno; + size -= ret; + buf += ret; + } + return size1 - size; +} + +static int http_close(URLContext *h) +{ + HTTPContext *s = h->priv_data; + close(s->fd); + return 0; +} + +URLProtocol http_protocol = { + "http", + http_open, + http_read, + http_write, + NULL, /* seek */ + http_close, +}; diff --git a/libav/img.c b/libav/img.c new file mode 100644 index 0000000000..db2d933ac0 --- /dev/null +++ b/libav/img.c @@ -0,0 +1,578 @@ +/* + * Image format + * Copyright (c) 2000, 2001 Gerard Lantau. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <math.h> + +#include "avformat.h" + +#define IMGFMT_YUV 1 +#define IMGFMT_PGMYUV 2 +#define IMGFMT_PGM 3 + +typedef struct { + int width; + int height; + int img_number; + int img_size; + int img_fmt; + int is_pipe; + 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 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; +} + +int img_read_packet(AVFormatContext *s1, AVPacket *pkt) +{ + VideoData *s = s1->priv_data; + char filename[1024]; + int ret; + ByteIOContext f1, *f; + + snprintf(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; + default: + return -EIO; + } + + if (!s->is_pipe) { + url_fclose(f); + } + + if (ret < 0) { + av_free_packet(pkt); + return -EIO; /* signal EOF */ + } else { + 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 }, +}; + +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; + int i, h; + char buf[1024]; + char buf1[32]; + ByteIOContext pb1, *f = &pb1; + AVStream *st; + + s = malloc(sizeof(VideoData)); + if (!s) + return -ENOMEM; + + s1->priv_data = s; + + s1->nb_streams = 1; + st = av_mallocz(sizeof(AVStream)); + if (!st) { + free(s); + return -ENOMEM; + } + s1->streams[0] = st; + + strcpy(s->path, s1->filename); + s->img_number = 0; + + /* find format */ + if (s1->format->flags & AVFMT_NOFILE) + s->is_pipe = 0; + else + s->is_pipe = 1; + + if (s1->format == &pgmpipe_format || + s1->format == &pgmyuv_format) + s->img_fmt = IMGFMT_PGMYUV; + else if (s1->format == &pgm_format) + s->img_fmt = IMGFMT_PGM; + else if (s1->format == &imgyuv_format) + s->img_fmt = IMGFMT_YUV; + else + goto fail; + + if (!s->is_pipe) { + /* try to find the first image */ + for(i=0;i<5;i++) { + snprintf(buf, sizeof(buf), s->path, s->img_number); + 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: + 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 = lseek((int)h->priv_data, 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); + } + + s->img_size = (s->width * s->height * 3) / 2; + + st->codec.codec_type = CODEC_TYPE_VIDEO; + st->codec.codec_id = CODEC_ID_RAWVIDEO; + st->codec.width = s->width; + st->codec.height = s->height; + st->codec.pix_fmt = PIX_FMT_YUV420P; + 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: + free(s); + return -EIO; +} + +static int img_read_close(AVFormatContext *s1) +{ + VideoData *s = s1->priv_data; + free(s); + return 0; +} + +/******************************************************/ +/* image output */ + +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 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 img_write_header(AVFormatContext *s) +{ + VideoData *img; + + img = av_mallocz(sizeof(VideoData)); + if (!img) + return -1; + s->priv_data = img; + img->img_number = 1; + strcpy(img->path, s->filename); + + /* find format */ + if (s->format->flags & AVFMT_NOFILE) + img->is_pipe = 0; + else + img->is_pipe = 1; + + if (s->format == &pgmpipe_format || + s->format == &pgmyuv_format) + img->img_fmt = IMGFMT_PGMYUV; + else if (s->format == &pgm_format) + img->img_fmt = IMGFMT_PGM; + else if (s->format == &imgyuv_format) + img->img_fmt = IMGFMT_YUV; + else + goto fail; + return 0; + fail: + free(img); + return -EIO; +} + +static int img_write_packet(AVFormatContext *s, int stream_index, + UINT8 *buf, int size) +{ + VideoData *img = s->priv_data; + AVStream *st = s->streams[stream_index]; + ByteIOContext pb1, *pb; + AVPicture picture; + int width, height, ret, size1; + char filename[1024]; + + width = st->codec.width; + height = st->codec.height; + 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; + snprintf(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; + } + if (!img->is_pipe) { + url_fclose(pb); + } + + img->img_number++; + return 0; +} + +static int img_write_trailer(AVFormatContext *s) +{ + VideoData *img = s->priv_data; + free(img); + return 0; +} + +AVFormat pgm_format = { + "pgm", + "pgm image format", + "", + "pgm", + CODEC_ID_NONE, + CODEC_ID_RAWVIDEO, + img_write_header, + img_write_packet, + img_write_trailer, + + img_read_header, + img_read_packet, + img_read_close, + NULL, + AVFMT_NOFILE, +}; + +AVFormat pgmyuv_format = { + "pgmyuv", + "pgm with YUV content image format", + "", + "pgm", + CODEC_ID_NONE, + CODEC_ID_RAWVIDEO, + img_write_header, + img_write_packet, + img_write_trailer, + + img_read_header, + img_read_packet, + img_read_close, + NULL, + AVFMT_NOFILE, +}; + +AVFormat imgyuv_format = { + ".Y.U.V", + ".Y.U.V format", + "", + "Y", + CODEC_ID_NONE, + CODEC_ID_RAWVIDEO, + img_write_header, + img_write_packet, + img_write_trailer, + + img_read_header, + img_read_packet, + img_read_close, + NULL, + AVFMT_NOFILE, +}; + +AVFormat pgmpipe_format = { + "pgmpipe", + "PGM pipe format", + "", + "pgm", + CODEC_ID_NONE, + CODEC_ID_RAWVIDEO, + img_write_header, + img_write_packet, + img_write_trailer, + + img_read_header, + img_read_packet, + img_read_close, + NULL, +}; diff --git a/libav/jpegenc.c b/libav/jpegenc.c new file mode 100644 index 0000000000..d02332cd79 --- /dev/null +++ b/libav/jpegenc.c @@ -0,0 +1,102 @@ +/* + * Miscellaneous MJPEG based formats + * Copyright (c) 2000 Gerard Lantau. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include <string.h> +#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) +{ + 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; +} + +AVFormat mpjpeg_format = { + "mpjpeg", + "Mime multipart JPEG format", + "multipart/x-mixed-replace;boundary=" BOUNDARY_TAG, + "mjpg", + CODEC_ID_NONE, + CODEC_ID_MJPEG, + mpjpeg_write_header, + mpjpeg_write_packet, + mpjpeg_write_trailer, +}; + + +/* single frame JPEG */ + +static int jpeg_write_header(AVFormatContext *s) +{ + return 0; +} + +static int jpeg_write_packet(AVFormatContext *s, int stream_index, + UINT8 *buf, int size) +{ + put_buffer(&s->pb, buf, size); + put_flush_packet(&s->pb); + return 1; /* no more data can be sent */ +} + +static int jpeg_write_trailer(AVFormatContext *s) +{ + return 0; +} + +AVFormat jpeg_format = { + "jpeg", + "JPEG image", + "image/jpeg", + "jpg,jpeg", + CODEC_ID_NONE, + CODEC_ID_MJPEG, + jpeg_write_header, + jpeg_write_packet, + jpeg_write_trailer, +}; diff --git a/libav/mpeg.c b/libav/mpeg.c new file mode 100644 index 0000000000..86e3af6184 --- /dev/null +++ b/libav/mpeg.c @@ -0,0 +1,663 @@ +/* + * Output a MPEG1 multiplexed video/audio stream + * Copyright (c) 2000 Gerard Lantau. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/time.h> + +#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; + float pts; + 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; +} MpegMuxContext; + +#define PACK_START_CODE ((unsigned int)0x000001ba) +#define SYSTEM_HEADER_START_CODE ((unsigned int)0x000001bb) +#define PACKET_START_CODE_MASK ((unsigned int)0xffffff00) +#define PACKET_START_CODE_PREFIX ((unsigned int)0x00000100) +#define ISO_11172_END_CODE ((unsigned int)0x000001b9) + +/* 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 + +static int put_pack_header(AVFormatContext *ctx, + UINT8 *buf, long long timestamp) +{ + MpegMuxContext *s = ctx->priv_data; + PutBitContext pb; + + init_put_bits(&pb, buf, 128, NULL, NULL); + + put_bits(&pb, 32, PACK_START_CODE); + put_bits(&pb, 4, 0x2); + put_bits(&pb, 3, (timestamp >> 30) & 0x07); + put_bits(&pb, 1, 1); + put_bits(&pb, 15, (timestamp >> 15) & 0x7fff); + put_bits(&pb, 1, 1); + put_bits(&pb, 15, (timestamp) & 0x7fff); + put_bits(&pb, 1, 1); + put_bits(&pb, 1, 1); + put_bits(&pb, 22, s->mux_rate); + put_bits(&pb, 1, 1); + + flush_put_bits(&pb); + return pb.buf_ptr - 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 = pb.buf_ptr - 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; + int bitrate, i, mpa_id, mpv_id, ac3_id; + AVStream *st; + StreamInfo *stream; + + s = malloc(sizeof(MpegMuxContext)); + if (!s) + return -1; + memset(s, 0, sizeof(MpegMuxContext)); + ctx->priv_data = s; + s->packet_number = 0; + + /* XXX: hardcoded */ + 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; + } + } + + /* 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); + /* every 2 seconds */ + s->pack_header_freq = 2 * bitrate / s->packet_size / 8; + /* every 10 seconds */ + s->system_header_freq = s->pack_header_freq * 5; + + for(i=0;i<ctx->nb_streams;i++) { + stream = ctx->streams[i]->priv_data; + stream->buffer_ptr = 0; + stream->packet_number = 0; + stream->pts = 0; + stream->start_pts = -1; + } + return 0; + fail: + for(i=0;i<ctx->nb_streams;i++) { + free(ctx->streams[i]->priv_data); + } + free(s); + return -ENOMEM; +} + +/* flush the packet on stream stream_index */ +static void flush_packet(AVFormatContext *ctx, int stream_index) +{ + MpegMuxContext *s = ctx->priv_data; + StreamInfo *stream = ctx->streams[stream_index]->priv_data; + UINT8 *buf_ptr; + int size, payload_size, startcode, id, len, stuffing_size, i; + INT64 timestamp; + UINT8 buffer[128]; + + id = stream->id; + timestamp = stream->start_pts; + +#if 0 + printf("packet ID=%2x PTS=%0.3f\n", + id, timestamp / 90000.0); +#endif + + buf_ptr = buffer; + if ((s->packet_number % s->pack_header_freq) == 0) { + /* output pack and systems header if needed */ + size = put_pack_header(ctx, buf_ptr, timestamp); + buf_ptr += size; + if ((s->packet_number % s->system_header_freq) == 0) { + size = put_system_header(ctx, buf_ptr); + buf_ptr += size; + } + } + size = buf_ptr - buffer; + put_buffer(&ctx->pb, buffer, size); + + /* packet header */ + payload_size = s->packet_size - (size + 6 + 5); + 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 + 5); + /* stuffing */ + for(i=0;i<stuffing_size;i++) + put_byte(&ctx->pb, 0xff); + + /* presentation time stamp */ + put_byte(&ctx->pb, + (0x02 << 4) | + (((timestamp >> 30) & 0x07) << 1) | + 1); + put_be16(&ctx->pb, (((timestamp >> 15) & 0x7fff) << 1) | 1); + put_be16(&ctx->pb, (((timestamp) & 0x7fff) << 1) | 1); + + if (startcode == PRIVATE_STREAM_1) { + put_byte(&ctx->pb, id); + if (id >= 0x80 && id <= 0xbf) { + /* XXX: need to check AC3 spec */ + put_byte(&ctx->pb, 1); + put_byte(&ctx->pb, 0); + put_byte(&ctx->pb, 2); + } + } + + /* output data */ + put_buffer(&ctx->pb, stream->buffer, payload_size - stuffing_size); + put_flush_packet(&ctx->pb); + + /* preserve remaining data */ + len = stream->buffer_ptr - payload_size; + if (len < 0) + len = 0; + memmove(stream->buffer, stream->buffer + stream->buffer_ptr - len, len); + stream->buffer_ptr = len; + + s->packet_number++; + stream->packet_number++; + stream->start_pts = -1; +} + +static int mpeg_mux_write_packet(AVFormatContext *ctx, + int stream_index, UINT8 *buf, int size) +{ + 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 = stream->pts * 90000.0; + 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 = stream->pts * 90000.0; + flush_packet(ctx, stream_index); + } + } + + if (st->codec.codec_type == CODEC_TYPE_AUDIO) { + stream->pts += (float)st->codec.frame_size / st->codec.sample_rate; + } else { + stream->pts += FRAME_RATE_BASE / (float)st->codec.frame_rate; + } + 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) + flush_packet(ctx, i); + } + + /* 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 + +typedef struct MpegDemuxContext { + int header_state; + int mux_rate; /* 50 byte/s unit */ +} 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 mpeg_mux_read_header(AVFormatContext *s, + AVFormatParameters *ap) +{ + MpegDemuxContext *m; + int size, startcode, c, rate_bound, audio_bound, video_bound, mux_rate, val; + int codec_id, n, i, type; + AVStream *st; + + m = av_mallocz(sizeof(MpegDemuxContext)); + if (!m) + return -ENOMEM; + s->priv_data = m; + + /* search first pack header */ + m->header_state = 0xff; + size = MAX_SYNC_SIZE; + for(;;) { + while (size > 0) { + startcode = find_start_code(&s->pb, &size, &m->header_state); + if (startcode == PACK_START_CODE) + goto found; + } + return -ENODATA; + found: + /* search system header just after pack header */ + /* parse pack header */ + get_byte(&s->pb); /* ts1 */ + get_be16(&s->pb); /* ts2 */ + get_be16(&s->pb); /* ts3 */ + + mux_rate = get_byte(&s->pb) << 16; + mux_rate |= get_byte(&s->pb) << 8; + mux_rate |= get_byte(&s->pb); + mux_rate &= (1 << 22) - 1; + m->mux_rate = mux_rate; + + startcode = find_start_code(&s->pb, &size, &m->header_state); + if (startcode == SYSTEM_HEADER_START_CODE) + break; + } + size = get_be16(&s->pb); + rate_bound = get_byte(&s->pb) << 16; + rate_bound |= get_byte(&s->pb) << 8; + rate_bound |= get_byte(&s->pb); + rate_bound = (rate_bound >> 1) & ((1 << 22) - 1); + audio_bound = get_byte(&s->pb) >> 2; + video_bound = get_byte(&s->pb) & 0x1f; + get_byte(&s->pb); /* reserved byte */ +#if 0 + printf("mux_rate=%d kbit/s\n", (m->mux_rate * 50 * 8) / 1000); + printf("rate_bound=%d\n", rate_bound); + printf("audio_bound=%d\n", audio_bound); + printf("video_bound=%d\n", video_bound); +#endif + size -= 6; + s->nb_streams = 0; + while (size > 0) { + c = get_byte(&s->pb); + size--; + if ((c & 0x80) == 0) + break; + val = get_be16(&s->pb); + size -= 2; + if (c >= 0xc0 && c <= 0xdf) { + /* mpeg audio stream */ + type = CODEC_TYPE_AUDIO; + codec_id = CODEC_ID_MP2; + n = 1; + c = c | 0x100; + } else if (c >= 0xe0 && c <= 0xef) { + type = CODEC_TYPE_VIDEO; + codec_id = CODEC_ID_MPEG1VIDEO; + n = 1; + c = c | 0x100; + } else if (c == 0xb8) { + /* all audio streams */ + /* XXX: hack for DVD: we force AC3, although we do not + know that this codec will be used */ + type = CODEC_TYPE_AUDIO; + codec_id = CODEC_ID_AC3; + n = audio_bound; + c = 0x80; + /* c = 0x1c0; */ + } else if (c == 0xb9) { + /* all video streams */ + type = CODEC_TYPE_VIDEO; + codec_id = CODEC_ID_MPEG1VIDEO; + n = video_bound; + c = 0x1e0; + } else { + type = 0; + codec_id = 0; + n = 0; + } + + for(i=0;i<n;i++) { + st = av_mallocz(sizeof(AVStream)); + if (!st) + return -ENOMEM; + s->streams[s->nb_streams++] = st; + st->id = c + i; + st->codec.codec_type = type; + st->codec.codec_id = codec_id; + } + } + + 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 mpeg_mux_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + MpegDemuxContext *m = s->priv_data; + AVStream *st; + int len, size, startcode, i, c, flags, header_len; + 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 = 0; + dts = 0; + /* 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; + dts = pts; + } 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) == 0x40) { + pts = get_pts(&s->pb, -1); + dts = pts; + 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; + } + /* skip packet */ + url_fskip(&s->pb, len); + goto redo; + found: + av_new_packet(pkt, len); + get_buffer(&s->pb, pkt->data, pkt->size); + pkt->pts = pts; + pkt->stream_index = i; + return 0; +} + +static int mpeg_mux_read_close(AVFormatContext *s) +{ + MpegDemuxContext *m = s->priv_data; + free(m); + return 0; +} + +AVFormat mpeg_mux_format = { + "mpeg", + "MPEG multiplex format", + "video/x-mpeg", + "mpg,mpeg,vob", + CODEC_ID_MP2, + CODEC_ID_MPEG1VIDEO, + mpeg_mux_init, + mpeg_mux_write_packet, + mpeg_mux_end, + + mpeg_mux_read_header, + mpeg_mux_read_packet, + mpeg_mux_read_close, +}; diff --git a/libav/raw.c b/libav/raw.c new file mode 100644 index 0000000000..47dfa3b0fe --- /dev/null +++ b/libav/raw.c @@ -0,0 +1,288 @@ +/* + * RAW encoder and decoder + * Copyright (c) 2001 Gerard Lantau. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include <string.h> +#include <errno.h> +#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) +{ + 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; + + st = malloc(sizeof(AVStream)); + if (!st) + return -1; + s->nb_streams = 1; + s->streams[0] = st; + + st->id = 0; + + if (ap) { + if (s->format->audio_codec != CODEC_ID_NONE) { + st->codec.codec_type = CODEC_TYPE_AUDIO; + st->codec.codec_id = s->format->audio_codec; + } else if (s->format->video_codec != CODEC_ID_NONE) { + st->codec.codec_type = CODEC_TYPE_VIDEO; + st->codec.codec_id = s->format->video_codec; + } else { + free(st); + return -1; + } + + switch(st->codec.codec_type) { + case CODEC_TYPE_AUDIO: + st->codec.sample_rate = ap->sample_rate; + st->codec.channels = ap->channels; + /* XXX: endianness */ + break; + case CODEC_TYPE_VIDEO: + st->codec.frame_rate = ap->frame_rate; + st->codec.width = ap->width; + st->codec.height = ap->height; + break; + default: + abort(); + break; + } + } else { + abort(); + } + return 0; +} + +#define MIN_SIZE 1024 + +int raw_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + int packet_size, n, ret; + + if (url_feof(&s->pb)) + return -EIO; + + packet_size = url_get_packet_size(&s->pb); + n = MIN_SIZE / packet_size; + if (n <= 0) + n = 1; + if (av_new_packet(pkt, n * packet_size) < 0) + return -EIO; + + pkt->stream_index = 0; + ret = get_buffer(&s->pb, pkt->data, pkt->size); + if (ret < 0) + av_free_packet(pkt); + return ret; +} + +int raw_read_close(AVFormatContext *s) +{ + return 0; +} + +/* mp3 read */ +static int mp3_read_header(AVFormatContext *s, + AVFormatParameters *ap) +{ + AVStream *st; + + st = 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_id = CODEC_ID_MP2; + /* XXX: read the first frame and extract rate and channels */ + st->codec.sample_rate = 44100; + st->codec.channels = 2; + return 0; +} + +/* mpeg1/h263 input */ +static int video_read_header(AVFormatContext *s, + AVFormatParameters *ap) +{ + AVStream *st; + + st = av_mallocz(sizeof(AVStream)); + if (!st) + return -1; + s->nb_streams = 1; + s->streams[0] = st; + + st->codec.codec_type = CODEC_TYPE_VIDEO; + st->codec.codec_id = s->format->video_codec; + return 0; +} + +AVFormat mp2_format = { + "mp2", + "MPEG audio", + "audio/x-mpeg", + "mp2,mp3", + CODEC_ID_MP2, + 0, + raw_write_header, + raw_write_packet, + raw_write_trailer, + + mp3_read_header, + raw_read_packet, + raw_read_close, +}; + +AVFormat ac3_format = { + "ac3", + "raw ac3", + "audio/x-ac3", + "ac3", + CODEC_ID_AC3, + 0, + raw_write_header, + raw_write_packet, + raw_write_trailer, +}; + +AVFormat h263_format = { + "h263", + "raw h263", + "video/x-h263", + "h263", + 0, + CODEC_ID_H263, + raw_write_header, + raw_write_packet, + raw_write_trailer, + video_read_header, + raw_read_packet, + raw_read_close, +}; + +AVFormat mpeg1video_format = { + "mpegvideo", + "MPEG video", + "video/x-mpeg", + "mpg,mpeg", + 0, + CODEC_ID_MPEG1VIDEO, + raw_write_header, + raw_write_packet, + raw_write_trailer, + video_read_header, + raw_read_packet, + raw_read_close, +}; + +AVFormat pcm_format = { + "pcm", + "pcm raw format", + NULL, + "sw", + CODEC_ID_PCM, + 0, + raw_write_header, + raw_write_packet, + raw_write_trailer, + + raw_read_header, + raw_read_packet, + raw_read_close, +}; + +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: + abort(); + break; + } + + if (av_new_packet(pkt, packet_size) < 0) + return -EIO; + + pkt->stream_index = 0; + /* bypass buffered I/O */ + ret = url_read(url_fileno(&s->pb), pkt->data, pkt->size); + if (ret != pkt->size) { + av_free_packet(pkt); + return -EIO; + } else { + return 0; + } +} + +AVFormat rawvideo_format = { + "rawvideo", + "raw video format", + NULL, + "yuv", + CODEC_ID_NONE, + CODEC_ID_RAWVIDEO, + raw_write_header, + raw_write_packet, + raw_write_trailer, + + raw_read_header, + rawvideo_read_packet, + raw_read_close, +}; diff --git a/libav/rm.c b/libav/rm.c new file mode 100644 index 0000000000..8c850400e4 --- /dev/null +++ b/libav/rm.c @@ -0,0 +1,710 @@ +/* + * "Real" compatible mux and demux. + * Copyright (c) 2000, 2001 Gerard Lantau. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#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 */ + 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, 24); /* frames per seconds ? */ + put_be32(s,0); /* unknown meaning */ + put_be16(s, 12); /* 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) +{ + StreamInfo *stream; + RMContext *rm; + int n; + AVCodecContext *codec; + + rm = malloc(sizeof(RMContext)); + if (!rm) + return -1; + memset(rm, 0, sizeof(RMContext)); + s->priv_data = rm; + + 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; + } + } + + 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[size]; + RMContext *rm = s->priv_data; + ByteIOContext *pb = &s->pb; + StreamInfo *stream = rm->audio_stream; + int i; + + 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++; + 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) +{ + 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); + + free(rm); + 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; + 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]; + + if (get_le32(pb) != MKTAG('.', 'R', 'M', 'F')) + return -EIO; + rm = av_mallocz(sizeof(RMContext)); + if (!rm) + return -ENOMEM; + s->priv_data = rm; + + 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 */ + 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; + nstrcpy(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: + st->codec.sub_id = 0; + st->codec.codec_id = CODEC_ID_RV10; + break; + case 0x10003000: + case 0x10003001: + st->codec.sub_id = 3; + 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 */ + get_be32(pb); /* next data header */ + return 0; + + fail: + for(i=0;i<s->nb_streams;i++) { + free(s->streams[i]); + } + return -EIO; +} + +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; + + 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 */ + 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; + } + + 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) +{ + RMContext *rm = s->priv_data; + free(rm); + return 0; +} + +AVFormat rm_format = { + "rm", + "rm format", + "audio/x-pn-realaudio", + "rm,ra", + CODEC_ID_AC3, + CODEC_ID_RV10, + rm_write_header, + rm_write_packet, + rm_write_trailer, + + rm_read_header, + rm_read_packet, + rm_read_close, +}; diff --git a/libav/swf.c b/libav/swf.c new file mode 100644 index 0000000000..a93c4f7097 --- /dev/null +++ b/libav/swf.c @@ -0,0 +1,552 @@ +/* + * Flash Compatible Streaming Format + * Copyright (c) 2000 Gerard Lantau. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "avformat.h" + +#include <assert.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 { + long long duration_pos; + long long 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; + long long 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, p.buf_ptr - 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, p.buf_ptr - 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 = 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, 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, p.buf_ptr - 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 */ + 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) +{ + 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); + } + free(swf); + 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_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: + 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; +} + +AVFormat swf_format = { + "swf", + "Flash format", + "application/x-shockwave-flash", + "swf", + CODEC_ID_MP2, + CODEC_ID_MJPEG, + swf_write_header, + swf_write_packet, + swf_write_trailer, + + swf_read_header, + swf_read_packet, + swf_read_close, +}; diff --git a/libav/udp.c b/libav/udp.c new file mode 100644 index 0000000000..329653bf18 --- /dev/null +++ b/libav/udp.c @@ -0,0 +1,148 @@ +/* + * UDP prototype streaming system + * Copyright (c) 2000 Gerard Lantau. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include "avformat.h" + +typedef struct { + int udp_socket; + int max_payload_size; /* in bytes */ +} UDPContext; + +#define UDP_TX_BUF_SIZE 32768 + +/* put it in UDP context */ +static struct sockaddr_in dest_addr; + +/* return non zero if error */ +static int udp_open(URLContext *h, const char *uri, int flags) +{ + int local_port = 0; + struct sockaddr_in my_addr; + const char *p, *q; + char hostname[1024]; + int port, udp_socket, tmp; + struct hostent *hp; + UDPContext *s; + + h->is_streamed = 1; + + if (!(flags & URL_WRONLY)) + return -EIO; + + /* fill the dest addr */ + p = uri; + if (!strstart(p, "udp:", &p)) + return -1; + q = strchr(p, ':'); + if (!q) + return -1; + memcpy(hostname, p, q - p); + hostname[q - p] = '\0'; + port = strtol(q+1, NULL, 10); + if (port <= 0) + return -1; + + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(port); + if ((inet_aton(hostname, &dest_addr.sin_addr)) == 0) { + hp = gethostbyname(hostname); + if (!hp) + return -1; + memcpy ((char *) &dest_addr.sin_addr, hp->h_addr, hp->h_length); + } + + udp_socket = socket(PF_INET, SOCK_DGRAM, 0); + if (udp_socket < 0) + return -1; + + my_addr.sin_family = AF_INET; + my_addr.sin_port = htons(local_port); + my_addr.sin_addr.s_addr = htonl (INADDR_ANY); + + /* the bind is needed to give a port to the socket now */ + if (bind(udp_socket,(struct sockaddr *)&my_addr, sizeof(my_addr)) < 0) + goto fail; + + /* limit the tx buf size to limit latency */ + + tmp = UDP_TX_BUF_SIZE; + if (setsockopt(udp_socket, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(tmp)) < 0) { + perror("setsockopt sndbuf"); + goto fail; + } + + s = malloc(sizeof(UDPContext)); + if (!s) + return -ENOMEM; + h->priv_data = s; + s->udp_socket = udp_socket; + h->packet_size = 1500; + return 0; + fail: + return -EIO; +} + +int udp_close(URLContext *h) +{ + UDPContext *s = h->priv_data; + close(s->udp_socket); + return 0; +} + +int udp_write(URLContext *h, UINT8 *buf, int size) +{ + UDPContext *s = h->priv_data; + int ret, len, size1; + + /* primitive way to avoid big packets */ + size1 = size; + while (size > 0) { + len = size; + if (len > h->packet_size) + len = h->packet_size; + + ret = sendto (s->udp_socket, buf, len, 0, + (struct sockaddr *) &dest_addr, + sizeof (dest_addr)); + if (ret < 0) + perror("sendto"); + buf += len; + size -= len; + } + return size1 - size; +} + +URLProtocol udp_protocol = { + "udp", + udp_open, + NULL, /* read */ + udp_write, + NULL, /* seek */ + udp_close, +}; diff --git a/libav/utils.c b/libav/utils.c new file mode 100644 index 0000000000..9018d0fb8d --- /dev/null +++ b/libav/utils.c @@ -0,0 +1,533 @@ +/* + * Various utilities for ffmpeg system + * Copyright (c) 2000,2001 Gerard Lantau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/time.h> +#include <time.h> + +#include "avformat.h" + +AVFormat *first_format; + +void register_avformat(AVFormat *format) +{ + AVFormat **p; + p = &first_format; + 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; +} + +AVFormat *guess_format(const char *short_name, const char *filename, const char *mime_type) +{ + AVFormat *fmt, *fmt_found; + int score_max, score; + + /* find the proper file type */ + fmt_found = NULL; + score_max = 0; + fmt = first_format; + 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; +} + +/* 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 */ +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; +} + +void nstrcpy(char *buf, int buf_size, const char *str) +{ + int c; + char *q = buf; + + for(;;) { + c = *str++; + if (c == 0 || q >= buf + buf_size - 1) + break; + *q++ = c; + } + *q = '\0'; +} + +void register_all(void) +{ + avcodec_init(); + avcodec_register_all(); + avcodec_register_more(); + + register_avformat(&mp2_format); + register_avformat(&ac3_format); + register_avformat(&mpeg_mux_format); + register_avformat(&mpeg1video_format); + register_avformat(&h263_format); + register_avformat(&rm_format); + register_avformat(&asf_format); + register_avformat(&avi_format); + register_avformat(&mpjpeg_format); + register_avformat(&jpeg_format); + register_avformat(&swf_format); + register_avformat(&wav_format); + register_avformat(&pcm_format); + register_avformat(&rawvideo_format); + register_avformat(&ffm_format); + register_avformat(&pgm_format); + register_avformat(&pgmyuv_format); + register_avformat(&imgyuv_format); + register_avformat(&pgmpipe_format); + + register_protocol(&file_protocol); + register_protocol(&pipe_protocol); + register_protocol(&audio_protocol); + register_protocol(&video_protocol); + register_protocol(&udp_protocol); + register_protocol(&http_protocol); +} + +/* memory handling */ + +int av_new_packet(AVPacket *pkt, int size) +{ + pkt->data = malloc(size); + if (!pkt->data) + return -ENOMEM; + pkt->size = size; + /* sane state */ + pkt->pts = 0; + pkt->stream_index = 0; + pkt->flags = 0; + return 0; +} + +void av_free_packet(AVPacket *pkt) +{ + free(pkt->data); + /* fail safe */ + pkt->data = NULL; + pkt->size = 0; +} + +/* fifo handling */ + +int fifo_init(FifoBuffer *f, int size) +{ + f->buffer = 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) +{ + 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; +} + +/* media file handling */ + +AVFormatContext *av_open_input_file(const char *filename, int buf_size) +{ + AVFormatParameters params, *ap; + AVFormat *fmt; + AVFormatContext *ic = NULL; + URLFormat url_format; + int err; + + ic = av_mallocz(sizeof(AVFormatContext)); + if (!ic) + goto fail; + if (url_fopen(&ic->pb, filename, URL_RDONLY) < 0) + goto fail; + + if (buf_size > 0) { + url_setbufsize(&ic->pb, buf_size); + } + + /* find format */ + err = url_getformat(url_fileno(&ic->pb), &url_format); + if (err >= 0) { + fmt = guess_format(url_format.format_name, NULL, NULL); + ap = ¶ms; + ap->sample_rate = url_format.sample_rate; + ap->frame_rate = url_format.frame_rate; + ap->channels = url_format.channels; + ap->width = url_format.width; + ap->height = url_format.height; + ap->pix_fmt = url_format.pix_fmt; + } else { + fmt = guess_format(NULL, filename, NULL); + ap = NULL; + } + if (!fmt || !fmt->read_header) { + return NULL; + } + ic->format = fmt; + + err = ic->format->read_header(ic, ap); + if (err < 0) { + url_fclose(&ic->pb); + goto fail; + } + + return ic; + + fail: + if (ic) + free(ic); + return NULL; +} + +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; + free(pktl); + return 0; + } else { + return s->format->read_packet(s, pkt); + } +} + +void av_close_input_file(AVFormatContext *s) +{ + int i; + + if (s->format->read_close) + s->format->read_close(s); + for(i=0;i<s->nb_streams;i++) { + 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); + free(p); + p = p1; + } + s->packet_buffer = NULL; + } + url_fclose(&s->pb); + free(s); +} + + +int av_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + /* XXX: currently, an emulation because internal API must change */ + return s->format->write_packet(s, pkt->stream_index, pkt->data, pkt->size); +} + +/* "user interface" functions */ + +void dump_format(AVFormatContext *ic, + int index, + const char *url, + int is_output) +{ + int i; + char buf[256]; + + fprintf(stderr, "%s #%d, %s, %s '%s':\n", + is_output ? "Output" : "Input", + index, ic->format->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: %s\n", index, i, 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 gettime(void) +{ + struct timeval tv; + gettimeofday(&tv,NULL); + return (INT64)tv.tv_sec * 1000000 + tv.tv_usec; +} + +/* syntax: [YYYY-MM-DD ][[HH:]MM:]SS[.m...] . Return the date in micro seconds since 1970 */ +INT64 parse_date(const char *datestr, int duration) +{ + const char *p; + INT64 t; + int sec; + + p = datestr; + if (!duration) { + static const UINT8 months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + int year, month, day, i; + + if (strlen(p) >= 5 && p[4] == '-') { + + year = strtol(p, (char **)&p, 10); + if (*p) + p++; + month = strtol(p, (char **)&p, 10) - 1; + if (*p) + p++; + day = strtol(p, (char **)&p, 10) - 1; + if (*p) + p++; + day += (year - 1970) * 365; + /* if >= March, take February of current year into account too */ + if (month >= 2) + year++; + for(i=1970;i<year;i++) { + if ((i % 100) == 0) { + if ((i % 400) == 0) day++; + } else if ((i % 4) == 0) { + day++; + } + } + for(i=0;i<month;i++) + day += months[i]; + } else { + day = (time(NULL) / (3600 * 24)); + } + t = day * (3600 * 24); + } else { + t = 0; + } + + sec = 0; + for(;;) { + int val; + val = strtol(p, (char **)&p, 10); + sec = sec * 60 + val; + if (*p != ':') + break; + p++; + } + t = (t + sec) * 1000000; + if (*p == '.') { + int val, n; + p++; + n = strlen(p); + if (n > 6) + n = 6; + val = strtol(p, NULL, 10); + while (n < 6) { + val = val * 10; + n++; + } + t += val; + } + return t; +} + +/* syntax: '?tag1=val1&tag2=val2...'. No 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) + *q++ = *p; + p++; + } + *q = '\0'; + } + if (!strcmp(tag, tag1)) + return 1; + if (*p != '&') + break; + } + return 0; +} + diff --git a/libav/wav.c b/libav/wav.c new file mode 100644 index 0000000000..9f430bb2f6 --- /dev/null +++ b/libav/wav.c @@ -0,0 +1,211 @@ +/* + * WAV encoder and decoder + * Copyright (c) 2001 Gerard Lantau. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include <string.h> +#include <errno.h> +#include "avformat.h" +#include "avi.h" + +typedef struct { + offset_t data; +} WAVContext; + +static int wav_write_header(AVFormatContext *s) +{ + WAVContext *wav; + ByteIOContext *pb = &s->pb; + offset_t fmt; + + wav = malloc(sizeof(WAVContext)); + if (!wav) + return -1; + memset(wav, 0, sizeof(WAVContext)); + s->priv_data = wav; + + put_tag(pb, "RIFF"); + put_le32(pb, 0); /* file length */ + put_tag(pb, "WAVE"); + + /* format header */ + fmt = start_tag(pb, "fmt "); + put_wav_header(pb, &s->streams[0]->codec); + 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) +{ + 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, file_size); + url_fseek(pb, file_size, SEEK_SET); + + put_flush_packet(pb); + } + + free(wav); + return 0; +} + +/* return the size of the found tag */ +/* XXX: > 2GB ? */ +static int find_tag(ByteIOContext *pb, int 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; +} + +/* wav input */ +static int wav_read_header(AVFormatContext *s, + AVFormatParameters *ap) +{ + int size; + unsigned int tag; + ByteIOContext *pb = &s->pb; + unsigned int id, channels, rate, bit_rate, extra_size; + 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; + id = get_le16(pb); + channels = get_le16(pb); + rate = get_le32(pb); + bit_rate = get_le32(pb) * 8; + get_le16(pb); /* block align */ + get_le16(pb); /* bits per sample */ + if (size >= 18) { + /* wav_extra_size */ + extra_size = get_le16(pb); + /* skip unused data */ + url_fseek(pb, size - 18, SEEK_CUR); + } + + size = find_tag(pb, MKTAG('d', 'a', 't', 'a')); + if (size < 0) + return -1; + + /* now we are ready: build format streams */ + st = 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_get_id(codec_wav_tags, id); + st->codec.channels = channels; + st->codec.sample_rate = rate; + return 0; +} + +#define MAX_SIZE 4096 + +static int wav_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + int packet_size, n, ret; + + if (url_feof(&s->pb)) + return -EIO; + packet_size = url_get_packet_size(&s->pb); + n = MAX_SIZE / packet_size; + if (n <= 0) + return n = 1; + if (av_new_packet(pkt, n * packet_size)) + return -EIO; + pkt->stream_index = 0; + + ret = get_buffer(&s->pb, pkt->data, pkt->size); + if (ret < 0) + av_free_packet(pkt); + return ret; +} + +static int wav_read_close(AVFormatContext *s) +{ + return 0; +} + +AVFormat wav_format = { + "wav", + "wav format", + "audio/x-wav", + "wav", + CODEC_ID_PCM, + CODEC_ID_NONE, + wav_write_header, + wav_write_packet, + wav_write_trailer, + + wav_read_header, + wav_read_packet, + wav_read_close, +}; |