Mercurial > libavformat.hg
comparison sierravmd.c @ 338:6f50cb0ead51 libavformat
initial commit for Sierra VMD file demuxer
author | melanson |
---|---|
date | Fri, 02 Jan 2004 04:47:02 +0000 |
parents | |
children | f2760852ed18 |
comparison
equal
deleted
inserted
replaced
337:f6b2b0718235 | 338:6f50cb0ead51 |
---|---|
1 /* | |
2 * Sierra VMD Format Demuxer | |
3 * Copyright (c) 2004 The ffmpeg Project | |
4 * | |
5 * This library is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Lesser General Public | |
7 * License as published by the Free Software Foundation; either | |
8 * version 2 of the License, or (at your option) any later version. | |
9 * | |
10 * This library 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 GNU | |
13 * Lesser General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Lesser General Public | |
16 * License along with this library; if not, write to the Free Software | |
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 */ | |
19 | |
20 /** | |
21 * @file sierravmd.c | |
22 * Sierra VMD file demuxer | |
23 * by Vladimir "VAG" Gneushev (vagsoft at mail.ru) | |
24 */ | |
25 | |
26 #include "avformat.h" | |
27 | |
28 #define LE_16(x) ((((uint8_t*)(x))[1] << 8) | ((uint8_t*)(x))[0]) | |
29 #define LE_32(x) ((((uint8_t*)(x))[3] << 24) | \ | |
30 (((uint8_t*)(x))[2] << 16) | \ | |
31 (((uint8_t*)(x))[1] << 8) | \ | |
32 ((uint8_t*)(x))[0]) | |
33 | |
34 #define VMD_HEADER_SIZE 0x0330 | |
35 #define BYTES_PER_FRAME_RECORD 16 | |
36 | |
37 typedef struct { | |
38 int stream_index; | |
39 offset_t frame_offset; | |
40 unsigned int frame_size; | |
41 int64_t pts; | |
42 int keyframe; | |
43 unsigned char frame_record[BYTES_PER_FRAME_RECORD]; | |
44 } vmd_frame_t; | |
45 | |
46 typedef struct VmdDemuxContext { | |
47 int video_stream_index; | |
48 int audio_stream_index; | |
49 | |
50 unsigned int audio_type; | |
51 unsigned int audio_samplerate; | |
52 unsigned int audio_bits; | |
53 unsigned int audio_channels; | |
54 | |
55 unsigned int frame_count; | |
56 vmd_frame_t *frame_table; | |
57 unsigned int current_frame; | |
58 | |
59 unsigned char vmd_header[VMD_HEADER_SIZE]; | |
60 } VmdDemuxContext; | |
61 | |
62 static int vmd_probe(AVProbeData *p) | |
63 { | |
64 if (p->buf_size < 2) | |
65 return 0; | |
66 | |
67 /* check if the first 2 bytes of the file contain the appropriate size | |
68 * of a VMD header chunk */ | |
69 if (LE_16(&p->buf[0]) != VMD_HEADER_SIZE - 2) | |
70 return 0; | |
71 | |
72 /* only return half certainty since this check is a bit sketchy */ | |
73 return AVPROBE_SCORE_MAX / 2; | |
74 } | |
75 | |
76 static int vmd_read_header(AVFormatContext *s, | |
77 AVFormatParameters *ap) | |
78 { | |
79 VmdDemuxContext *vmd = (VmdDemuxContext *)s->priv_data; | |
80 ByteIOContext *pb = &s->pb; | |
81 AVStream *st; | |
82 unsigned int toc_offset; | |
83 unsigned char *raw_frame_table; | |
84 int raw_frame_table_size; | |
85 unsigned char *current_frame_record; | |
86 offset_t current_offset; | |
87 int i; | |
88 int sample_rate; | |
89 unsigned int total_frames; | |
90 int video_frame_count = 0; | |
91 | |
92 /* fetch the main header, including the 2 header length bytes */ | |
93 url_fseek(pb, 0, SEEK_SET); | |
94 if (get_buffer(pb, vmd->vmd_header, VMD_HEADER_SIZE) != VMD_HEADER_SIZE) | |
95 return -EIO; | |
96 | |
97 /* start up the decoders */ | |
98 st = av_new_stream(s, 0); | |
99 if (!st) | |
100 return AVERROR_NOMEM; | |
101 vmd->video_stream_index = st->index; | |
102 st->codec.codec_type = CODEC_TYPE_VIDEO; | |
103 st->codec.codec_id = CODEC_ID_VMDVIDEO; | |
104 st->codec.codec_tag = 0; /* no fourcc */ | |
105 st->codec.width = LE_16(&vmd->vmd_header[12]); | |
106 st->codec.height = LE_16(&vmd->vmd_header[14]); | |
107 st->codec.extradata_size = VMD_HEADER_SIZE; | |
108 st->codec.extradata = av_malloc(VMD_HEADER_SIZE); | |
109 memcpy(st->codec.extradata, vmd->vmd_header, VMD_HEADER_SIZE); | |
110 | |
111 /* if sample rate is 0, assume no audio */ | |
112 sample_rate = LE_16(&vmd->vmd_header[804]); | |
113 if (sample_rate) { | |
114 st = av_new_stream(s, 0); | |
115 if (!st) | |
116 return AVERROR_NOMEM; | |
117 vmd->audio_stream_index = st->index; | |
118 st->codec.codec_type = CODEC_TYPE_AUDIO; | |
119 st->codec.codec_id = CODEC_ID_VMDAUDIO; | |
120 st->codec.codec_tag = 0; /* no codec tag */ | |
121 st->codec.channels = (vmd->vmd_header[811] & 0x80) ? 2 : 1; | |
122 st->codec.sample_rate = sample_rate; | |
123 st->codec.bit_rate = st->codec.sample_rate * | |
124 st->codec.bits_per_sample * st->codec.channels; | |
125 st->codec.block_align = LE_16(&vmd->vmd_header[806]); | |
126 if (st->codec.block_align & 0x8000) { | |
127 st->codec.bits_per_sample = 16; | |
128 st->codec.block_align = -(st->codec.block_align - 0x10000); | |
129 } else | |
130 st->codec.bits_per_sample = 8; | |
131 } | |
132 | |
133 /* skip over the offset table and load the table of contents; don't | |
134 * care about the offset table since demuxer will calculate those | |
135 * independently */ | |
136 toc_offset = LE_32(&vmd->vmd_header[812]); | |
137 vmd->frame_count = LE_16(&vmd->vmd_header[6]); | |
138 url_fseek(pb, toc_offset + vmd->frame_count * 6, SEEK_SET); | |
139 | |
140 /* each on-disk VMD frame has an audio part and a video part; demuxer | |
141 * accounts them separately */ | |
142 vmd->frame_count *= 2; | |
143 raw_frame_table = NULL; | |
144 vmd->frame_table = NULL; | |
145 raw_frame_table_size = vmd->frame_count * BYTES_PER_FRAME_RECORD; | |
146 raw_frame_table = av_malloc(raw_frame_table_size); | |
147 vmd->frame_table = av_malloc(vmd->frame_count * sizeof(vmd_frame_t)); | |
148 if (!raw_frame_table || !vmd->frame_table) { | |
149 av_free(raw_frame_table); | |
150 av_free(vmd->frame_table); | |
151 return AVERROR_NOMEM; | |
152 } | |
153 if (get_buffer(pb, raw_frame_table, raw_frame_table_size) != | |
154 raw_frame_table_size) { | |
155 av_free(raw_frame_table); | |
156 av_free(vmd->frame_table); | |
157 return -EIO; | |
158 } | |
159 | |
160 current_offset = LE_32(&vmd->vmd_header[20]); | |
161 current_frame_record = raw_frame_table; | |
162 total_frames = vmd->frame_count; | |
163 i = 0; | |
164 while (total_frames--) { | |
165 | |
166 /* if the frame size is 0, do not count the frame and bring the | |
167 * total frame count down */ | |
168 vmd->frame_table[i].frame_size = LE_32(¤t_frame_record[2]); | |
169 | |
170 /* this logic is present so that 0-length audio chunks are not | |
171 * accounted */ | |
172 if (!vmd->frame_table[i].frame_size) { | |
173 vmd->frame_count--; /* one less frame to count */ | |
174 current_frame_record += BYTES_PER_FRAME_RECORD; | |
175 continue; | |
176 } | |
177 | |
178 if (current_frame_record[0] == 0x02) | |
179 vmd->frame_table[i].stream_index = vmd->video_stream_index; | |
180 else | |
181 vmd->frame_table[i].stream_index = vmd->audio_stream_index; | |
182 vmd->frame_table[i].frame_offset = current_offset; | |
183 current_offset += vmd->frame_table[i].frame_size; | |
184 memcpy(vmd->frame_table[i].frame_record, current_frame_record, | |
185 BYTES_PER_FRAME_RECORD); | |
186 | |
187 if (current_frame_record[0] == 0x02) { | |
188 /* assume 15 fps for now */ | |
189 vmd->frame_table[i].pts = video_frame_count++; | |
190 vmd->frame_table[i].pts *= 90000; | |
191 vmd->frame_table[i].pts /= 15; | |
192 } | |
193 | |
194 current_frame_record += BYTES_PER_FRAME_RECORD; | |
195 i++; | |
196 } | |
197 | |
198 av_free(raw_frame_table); | |
199 | |
200 /* set the pts reference at 1 pts = 1/90000 sec */ | |
201 s->pts_num = 1; | |
202 s->pts_den = 90000; | |
203 | |
204 vmd->current_frame = 0; | |
205 | |
206 return 0; | |
207 } | |
208 | |
209 static int vmd_read_packet(AVFormatContext *s, | |
210 AVPacket *pkt) | |
211 { | |
212 VmdDemuxContext *vmd = (VmdDemuxContext *)s->priv_data; | |
213 ByteIOContext *pb = &s->pb; | |
214 int ret = 0; | |
215 vmd_frame_t *frame; | |
216 | |
217 if (vmd->current_frame >= vmd->frame_count) | |
218 return -EIO; | |
219 | |
220 frame = &vmd->frame_table[vmd->current_frame]; | |
221 /* position the stream (will probably be there already) */ | |
222 url_fseek(pb, frame->frame_offset, SEEK_SET); | |
223 | |
224 if (av_new_packet(pkt, frame->frame_size + BYTES_PER_FRAME_RECORD)) | |
225 return AVERROR_NOMEM; | |
226 memcpy(pkt->data, frame->frame_record, BYTES_PER_FRAME_RECORD); | |
227 ret = get_buffer(pb, pkt->data + BYTES_PER_FRAME_RECORD, | |
228 frame->frame_size); | |
229 | |
230 if (ret != frame->frame_size) | |
231 ret = -EIO; | |
232 pkt->stream_index = frame->stream_index; | |
233 pkt->pts = frame->pts; | |
234 | |
235 vmd->current_frame++; | |
236 | |
237 return ret; | |
238 } | |
239 | |
240 static int vmd_read_close(AVFormatContext *s) | |
241 { | |
242 VmdDemuxContext *vmd = (VmdDemuxContext *)s->priv_data; | |
243 | |
244 av_free(vmd->frame_table); | |
245 | |
246 return 0; | |
247 } | |
248 | |
249 static AVInputFormat vmd_iformat = { | |
250 "vmd", | |
251 "Sierra VMD format", | |
252 sizeof(VmdDemuxContext), | |
253 vmd_probe, | |
254 vmd_read_header, | |
255 vmd_read_packet, | |
256 vmd_read_close, | |
257 }; | |
258 | |
259 int vmd_init(void) | |
260 { | |
261 av_register_input_format(&vmd_iformat); | |
262 return 0; | |
263 } |