Commit | Line | Data |
---|---|---|
de6d9b64 FB |
1 | /* |
2 | * AVI decoder. | |
3 | * Copyright (c) 2001 Gerard Lantau. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
18 | */ | |
19 | #include <stdlib.h> | |
20 | #include <stdio.h> | |
21 | #include <netinet/in.h> | |
22 | #include <string.h> | |
23 | #include <errno.h> | |
24 | ||
25 | #include "avformat.h" | |
26 | #include "avi.h" | |
27 | ||
28 | //#define DEBUG | |
29 | ||
30 | typedef struct AVIIndex { | |
31 | unsigned char tag[4]; | |
32 | unsigned int flags, pos, len; | |
33 | struct AVIIndex *next; | |
34 | } AVIIndex; | |
35 | ||
36 | typedef struct { | |
37 | INT64 movi_end; | |
38 | offset_t movi_list; | |
39 | AVIIndex *first, *last; | |
40 | } AVIContext; | |
41 | ||
42 | #ifdef DEBUG | |
43 | void print_tag(const char *str, unsigned int tag, int size) | |
44 | { | |
45 | printf("%s: tag=%c%c%c%c size=0x%x\n", | |
46 | str, tag & 0xff, | |
47 | (tag >> 8) & 0xff, | |
48 | (tag >> 16) & 0xff, | |
49 | (tag >> 24) & 0xff, | |
50 | size); | |
51 | } | |
52 | #endif | |
53 | ||
54 | int avi_read_header(AVFormatContext *s, AVFormatParameters *ap) | |
55 | { | |
56 | AVIContext *avi; | |
57 | ByteIOContext *pb = &s->pb; | |
58 | UINT32 tag, tag1; | |
59 | int codec_type, stream_index, size, frame_period, bit_rate; | |
60 | int i; | |
61 | AVStream *st; | |
62 | ||
63 | avi = malloc(sizeof(AVIContext)); | |
64 | if (!avi) | |
65 | return -1; | |
66 | memset(avi, 0, sizeof(AVIContext)); | |
67 | s->priv_data = avi; | |
68 | ||
69 | /* check RIFF header */ | |
70 | tag = get_le32(pb); | |
71 | ||
72 | if (tag != MKTAG('R', 'I', 'F', 'F')) | |
73 | return -1; | |
74 | get_le32(pb); /* file size */ | |
75 | tag = get_le32(pb); | |
76 | if (tag != MKTAG('A', 'V', 'I', ' ')) | |
77 | return -1; | |
78 | ||
79 | /* first list tag */ | |
80 | stream_index = -1; | |
81 | codec_type = -1; | |
82 | frame_period = 0; | |
83 | for(;;) { | |
84 | if (url_feof(pb)) | |
85 | goto fail; | |
86 | tag = get_le32(pb); | |
87 | size = get_le32(pb); | |
88 | #ifdef DEBUG | |
89 | print_tag("tag", tag, size); | |
90 | #endif | |
91 | ||
92 | switch(tag) { | |
93 | case MKTAG('L', 'I', 'S', 'T'): | |
94 | /* ignored, except when start of video packets */ | |
95 | tag1 = get_le32(pb); | |
96 | #ifdef DEBUG | |
97 | print_tag("list", tag1, 0); | |
98 | #endif | |
99 | if (tag1 == MKTAG('m', 'o', 'v', 'i')) { | |
100 | avi->movi_end = url_ftell(pb) + size - 4; | |
101 | #ifdef DEBUG | |
102 | printf("movi end=%Lx\n", avi->movi_end); | |
103 | #endif | |
104 | goto end_of_header; | |
105 | } | |
106 | break; | |
107 | case MKTAG('a', 'v', 'i', 'h'): | |
108 | /* avi header */ | |
109 | frame_period = get_le32(pb); | |
110 | bit_rate = get_le32(pb) * 8; | |
111 | url_fskip(pb, 4 * 4); | |
112 | s->nb_streams = get_le32(pb); | |
113 | for(i=0;i<s->nb_streams;i++) { | |
114 | AVStream *st; | |
115 | st = malloc(sizeof(AVStream)); | |
116 | if (!st) | |
117 | goto fail; | |
118 | memset(st, 0, sizeof(AVStream)); | |
119 | s->streams[i] = st; | |
120 | } | |
121 | url_fskip(pb, size - 7 * 4); | |
122 | break; | |
123 | case MKTAG('s', 't', 'r', 'h'): | |
124 | /* stream header */ | |
125 | stream_index++; | |
126 | tag1 = get_le32(pb); | |
127 | switch(tag1) { | |
128 | case MKTAG('v', 'i', 'd', 's'): | |
129 | codec_type = CODEC_TYPE_VIDEO; | |
130 | get_le32(pb); /* codec tag */ | |
131 | get_le32(pb); /* flags */ | |
132 | get_le16(pb); /* priority */ | |
133 | get_le16(pb); /* language */ | |
134 | get_le32(pb); /* XXX: initial frame ? */ | |
135 | get_le32(pb); /* scale */ | |
136 | get_le32(pb); /* rate */ | |
137 | url_fskip(pb, size - 7 * 4); | |
138 | break; | |
139 | case MKTAG('a', 'u', 'd', 's'): | |
140 | codec_type = CODEC_TYPE_AUDIO; | |
141 | /* nothing really useful */ | |
142 | url_fskip(pb, size - 4); | |
143 | break; | |
144 | default: | |
145 | goto fail; | |
146 | } | |
147 | break; | |
148 | case MKTAG('s', 't', 'r', 'f'): | |
149 | /* stream header */ | |
150 | if (stream_index >= s->nb_streams) { | |
151 | url_fskip(pb, size); | |
152 | } else { | |
153 | st = s->streams[stream_index]; | |
154 | switch(codec_type) { | |
155 | case CODEC_TYPE_VIDEO: | |
156 | get_le32(pb); /* size */ | |
157 | st->codec.width = get_le32(pb); | |
158 | st->codec.height = get_le32(pb); | |
159 | if (frame_period) | |
160 | st->codec.frame_rate = (1000000LL * FRAME_RATE_BASE) / frame_period; | |
161 | else | |
162 | st->codec.frame_rate = 25 * FRAME_RATE_BASE; | |
163 | get_le16(pb); /* panes */ | |
164 | get_le16(pb); /* depth */ | |
165 | tag1 = get_le32(pb); | |
166 | #ifdef DEBUG | |
167 | print_tag("video", tag1, 0); | |
168 | #endif | |
169 | st->codec.codec_type = CODEC_TYPE_VIDEO; | |
170 | st->codec.codec_tag = tag1; | |
171 | st->codec.codec_id = codec_get_id(codec_bmp_tags, tag1); | |
172 | url_fskip(pb, size - 5 * 4); | |
173 | break; | |
174 | case CODEC_TYPE_AUDIO: | |
175 | tag1 = get_le16(pb); | |
176 | st->codec.codec_type = CODEC_TYPE_AUDIO; | |
177 | st->codec.codec_tag = tag1; | |
178 | st->codec.codec_id = codec_get_id(codec_wav_tags, tag1); | |
179 | #ifdef DEBUG | |
180 | printf("audio: 0x%x\n", tag1); | |
181 | #endif | |
182 | st->codec.channels = get_le16(pb); | |
183 | st->codec.sample_rate = get_le32(pb); | |
184 | st->codec.bit_rate = get_le32(pb) * 8; | |
185 | url_fskip(pb, size - 3 * 4); | |
186 | break; | |
187 | default: | |
188 | url_fskip(pb, size); | |
189 | break; | |
190 | } | |
191 | } | |
192 | break; | |
193 | default: | |
194 | /* skip tag */ | |
195 | size += (size & 1); | |
196 | url_fskip(pb, size); | |
197 | break; | |
198 | } | |
199 | } | |
200 | end_of_header: | |
201 | /* check stream number */ | |
202 | if (stream_index != s->nb_streams - 1) { | |
203 | fail: | |
204 | for(i=0;i<s->nb_streams;i++) { | |
205 | if (s->streams[i]) | |
206 | free(s->streams[i]); | |
207 | } | |
208 | return -1; | |
209 | } | |
210 | ||
211 | return 0; | |
212 | } | |
213 | ||
214 | int avi_read_packet(AVFormatContext *s, AVPacket *pkt) | |
215 | { | |
216 | AVIContext *avi = s->priv_data; | |
217 | ByteIOContext *pb = &s->pb; | |
218 | int n, d1, d2, size; | |
219 | ||
220 | find_next: | |
221 | if (url_feof(pb) || url_ftell(pb) >= avi->movi_end) | |
222 | return -1; | |
223 | d1 = get_byte(pb); | |
224 | if (d1 < '0' || d1 > '9') | |
225 | goto find_next; | |
226 | d2 = get_byte(pb); | |
227 | if (d2 < '0' || d2 > '9') | |
228 | goto find_next; | |
229 | n = (d1 - '0') * 10 + (d2 - '0'); | |
230 | ||
231 | if (n < 0 || n >= s->nb_streams) | |
232 | goto find_next; | |
233 | ||
234 | d1 = get_byte(pb); | |
235 | d2 = get_byte(pb); | |
236 | if ((d1 != 'd' && d2 != 'c') && | |
237 | (d1 != 'w' && d2 != 'b')) | |
238 | goto find_next; | |
239 | ||
240 | size = get_le32(pb); | |
241 | av_new_packet(pkt, size); | |
242 | pkt->stream_index = n; | |
243 | ||
244 | get_buffer(pb, pkt->data, pkt->size); | |
245 | ||
246 | if (size & 1) | |
247 | get_byte(pb); | |
248 | ||
249 | return 0; | |
250 | } | |
251 | ||
252 | int avi_read_close(AVFormatContext *s) | |
253 | { | |
254 | AVIContext *avi = s->priv_data; | |
255 | free(avi); | |
256 | return 0; | |
257 | } |