Mercurial > libavformat.hg
annotate vqf.c @ 5711:1481fbffd30b libavformat
low-complexity Bink file seeking
author | pross |
---|---|
date | Wed, 24 Feb 2010 11:43:33 +0000 |
parents | 4211f91f69b1 |
children | 536e5527c1e0 |
rev | line source |
---|---|
4668 | 1 /* |
2 * VQF demuxer | |
3 * Copyright (c) 2009 Vitor Sessak | |
4 * | |
5 * This file is part of FFmpeg. | |
6 * | |
7 * FFmpeg is free software; you can redistribute it and/or | |
8 * modify it under the terms of the GNU Lesser General Public | |
9 * License as published by the Free Software Foundation; either | |
10 * version 2.1 of the License, or (at your option) any later version. | |
11 * | |
12 * FFmpeg is distributed in the hope that it will be useful, | |
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Lesser General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Lesser General Public | |
18 * License along with FFmpeg; if not, write to the Free Software | |
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
20 */ | |
21 | |
22 #include "avformat.h" | |
23 #include "libavutil/intreadwrite.h" | |
24 | |
25 typedef struct VqfContext { | |
26 int frame_bit_len; | |
27 uint8_t last_frame_bits; | |
28 int remaining_bits; | |
29 } VqfContext; | |
30 | |
31 static int vqf_probe(AVProbeData *probe_packet) | |
32 { | |
33 if (AV_RL32(probe_packet->buf) != MKTAG('T','W','I','N')) | |
34 return 0; | |
35 | |
36 if (!memcmp(probe_packet->buf + 4, "97012000", 8)) | |
37 return AVPROBE_SCORE_MAX; | |
38 | |
39 if (!memcmp(probe_packet->buf + 4, "00052200", 8)) | |
40 return AVPROBE_SCORE_MAX; | |
41 | |
42 return AVPROBE_SCORE_MAX/2; | |
43 } | |
44 | |
45 static void add_metadata(AVFormatContext *s, const char *tag, | |
46 unsigned int tag_len, unsigned int remaining) | |
47 { | |
5446
4211f91f69b1
Use AV_METADATA_DONT_STRDUP* / use av_malloced metadata instead of strduped
michael
parents:
4668
diff
changeset
|
48 int len = FFMIN(tag_len, remaining); |
4211f91f69b1
Use AV_METADATA_DONT_STRDUP* / use av_malloced metadata instead of strduped
michael
parents:
4668
diff
changeset
|
49 char *buf; |
4668 | 50 |
5446
4211f91f69b1
Use AV_METADATA_DONT_STRDUP* / use av_malloced metadata instead of strduped
michael
parents:
4668
diff
changeset
|
51 if (len == UINT_MAX) |
4211f91f69b1
Use AV_METADATA_DONT_STRDUP* / use av_malloced metadata instead of strduped
michael
parents:
4668
diff
changeset
|
52 return; |
4668 | 53 |
5446
4211f91f69b1
Use AV_METADATA_DONT_STRDUP* / use av_malloced metadata instead of strduped
michael
parents:
4668
diff
changeset
|
54 buf = av_malloc(len+1); |
4211f91f69b1
Use AV_METADATA_DONT_STRDUP* / use av_malloced metadata instead of strduped
michael
parents:
4668
diff
changeset
|
55 if (!buf) |
4211f91f69b1
Use AV_METADATA_DONT_STRDUP* / use av_malloced metadata instead of strduped
michael
parents:
4668
diff
changeset
|
56 return; |
4668 | 57 get_buffer(s->pb, buf, len); |
58 buf[len] = 0; | |
5446
4211f91f69b1
Use AV_METADATA_DONT_STRDUP* / use av_malloced metadata instead of strduped
michael
parents:
4668
diff
changeset
|
59 av_metadata_set2(&s->metadata, tag, buf, AV_METADATA_DONT_STRDUP_VAL); |
4668 | 60 } |
61 | |
62 static int vqf_read_header(AVFormatContext *s, AVFormatParameters *ap) | |
63 { | |
64 VqfContext *c = s->priv_data; | |
65 AVStream *st = av_new_stream(s, 0); | |
66 int chunk_tag; | |
67 int rate_flag = -1; | |
68 int header_size; | |
69 int read_bitrate = 0; | |
70 int size; | |
71 | |
72 if (!st) | |
73 return AVERROR(ENOMEM); | |
74 | |
75 url_fskip(s->pb, 12); | |
76 | |
77 header_size = get_be32(s->pb); | |
78 | |
79 st->codec->codec_type = CODEC_TYPE_AUDIO; | |
80 st->codec->codec_id = CODEC_ID_TWINVQ; | |
81 st->start_time = 0; | |
82 | |
83 do { | |
84 int len; | |
85 chunk_tag = get_le32(s->pb); | |
86 | |
87 if (chunk_tag == MKTAG('D','A','T','A')) | |
88 break; | |
89 | |
90 len = get_be32(s->pb); | |
91 | |
92 if ((unsigned) len > INT_MAX/2) { | |
93 av_log(s, AV_LOG_ERROR, "Malformed header\n"); | |
94 return -1; | |
95 } | |
96 | |
97 header_size -= 8; | |
98 | |
99 switch(chunk_tag){ | |
100 case MKTAG('C','O','M','M'): | |
101 st->codec->channels = get_be32(s->pb) + 1; | |
102 read_bitrate = get_be32(s->pb); | |
103 rate_flag = get_be32(s->pb); | |
104 url_fskip(s->pb, len-12); | |
105 | |
106 st->codec->bit_rate = read_bitrate*1000; | |
107 st->codec->bits_per_coded_sample = 16; | |
108 break; | |
109 case MKTAG('N','A','M','E'): | |
110 add_metadata(s, "title" , len, header_size); | |
111 break; | |
112 case MKTAG('(','c',')',' '): | |
113 add_metadata(s, "copyright", len, header_size); | |
114 break; | |
115 case MKTAG('A','U','T','H'): | |
116 add_metadata(s, "author" , len, header_size); | |
117 break; | |
118 case MKTAG('A','L','B','M'): | |
119 add_metadata(s, "album" , len, header_size); | |
120 break; | |
121 case MKTAG('T','R','C','K'): | |
122 add_metadata(s, "track" , len, header_size); | |
123 break; | |
124 case MKTAG('C','O','M','T'): | |
125 add_metadata(s, "comment" , len, header_size); | |
126 break; | |
127 case MKTAG('F','I','L','E'): | |
128 add_metadata(s, "filename" , len, header_size); | |
129 break; | |
130 case MKTAG('D','S','I','Z'): | |
131 add_metadata(s, "size" , len, header_size); | |
132 break; | |
133 case MKTAG('D','A','T','E'): | |
134 add_metadata(s, "date" , len, header_size); | |
135 break; | |
136 case MKTAG('G','E','N','R'): | |
137 add_metadata(s, "genre" , len, header_size); | |
138 break; | |
139 default: | |
140 av_log(s, AV_LOG_ERROR, "Unknown chunk: %c%c%c%c\n", | |
141 ((char*)&chunk_tag)[0], ((char*)&chunk_tag)[1], | |
142 ((char*)&chunk_tag)[2], ((char*)&chunk_tag)[3]); | |
143 url_fskip(s->pb, FFMIN(len, header_size)); | |
144 break; | |
145 } | |
146 | |
147 header_size -= len; | |
148 | |
149 } while (header_size >= 0); | |
150 | |
151 switch (rate_flag) { | |
152 case -1: | |
153 av_log(s, AV_LOG_ERROR, "COMM tag not found!\n"); | |
154 return -1; | |
155 case 44: | |
156 st->codec->sample_rate = 44100; | |
157 break; | |
158 case 22: | |
159 st->codec->sample_rate = 22050; | |
160 break; | |
161 case 11: | |
162 st->codec->sample_rate = 11025; | |
163 break; | |
164 default: | |
165 st->codec->sample_rate = rate_flag*1000; | |
166 break; | |
167 } | |
168 | |
169 switch (((st->codec->sample_rate/1000) << 8) + | |
170 read_bitrate/st->codec->channels) { | |
171 case (11<<8) + 8 : | |
172 case (8 <<8) + 8 : | |
173 case (11<<8) + 10: | |
174 case (22<<8) + 32: | |
175 size = 512; | |
176 break; | |
177 case (16<<8) + 16: | |
178 case (22<<8) + 20: | |
179 case (22<<8) + 24: | |
180 size = 1024; | |
181 break; | |
182 case (44<<8) + 40: | |
183 case (44<<8) + 48: | |
184 size = 2048; | |
185 break; | |
186 default: | |
187 av_log(s, AV_LOG_ERROR, "Mode not suported: %d Hz, %d kb/s.\n", | |
188 st->codec->sample_rate, st->codec->bit_rate); | |
189 return -1; | |
190 } | |
191 c->frame_bit_len = st->codec->bit_rate*size/st->codec->sample_rate; | |
192 | |
193 return 0; | |
194 } | |
195 | |
196 static int vqf_read_packet(AVFormatContext *s, AVPacket *pkt) | |
197 { | |
198 VqfContext *c = s->priv_data; | |
199 int ret; | |
200 int size = (c->frame_bit_len - c->remaining_bits + 7)>>3; | |
201 | |
202 pkt->pos = url_ftell(s->pb); | |
203 pkt->stream_index = 0; | |
204 | |
205 if (av_new_packet(pkt, size+2) < 0) | |
206 return AVERROR(EIO); | |
207 | |
208 pkt->data[0] = 8 - c->remaining_bits; // Number of bits to skip | |
209 pkt->data[1] = c->last_frame_bits; | |
210 ret = get_buffer(s->pb, pkt->data+2, size); | |
211 | |
212 if (ret<=0) { | |
213 av_free_packet(pkt); | |
214 return AVERROR(EIO); | |
215 } | |
216 | |
217 c->last_frame_bits = pkt->data[size+1]; | |
218 c->remaining_bits = (size << 3) - c->frame_bit_len + c->remaining_bits; | |
219 | |
220 return size+2; | |
221 } | |
222 | |
223 static int vqf_read_seek(AVFormatContext *s, | |
224 int stream_index, int64_t timestamp, int flags) | |
225 { | |
226 VqfContext *c = s->priv_data; | |
227 AVStream *st; | |
228 int ret; | |
229 int64_t pos; | |
230 | |
231 st = s->streams[stream_index]; | |
232 pos = av_rescale_rnd(timestamp * st->codec->bit_rate, | |
233 st->time_base.num, | |
234 st->time_base.den * (int64_t)c->frame_bit_len, | |
235 (flags & AVSEEK_FLAG_BACKWARD) ? | |
236 AV_ROUND_DOWN : AV_ROUND_UP); | |
237 pos *= c->frame_bit_len; | |
238 | |
239 st->cur_dts = av_rescale(pos, st->time_base.den, | |
240 st->codec->bit_rate * (int64_t)st->time_base.num); | |
241 | |
242 if ((ret = url_fseek(s->pb, ((pos-7) >> 3) + s->data_offset, SEEK_SET)) < 0) | |
243 return ret; | |
244 | |
245 c->remaining_bits = -7 - ((pos-7)&7); | |
246 return 0; | |
247 } | |
248 | |
249 AVInputFormat vqf_demuxer = { | |
250 "vqf", | |
251 NULL_IF_CONFIG_SMALL("Nippon Telegraph and Telephone Corporation (NTT) TwinVQ"), | |
252 sizeof(VqfContext), | |
253 vqf_probe, | |
254 vqf_read_header, | |
255 vqf_read_packet, | |
256 NULL, | |
257 vqf_read_seek, | |
258 .extensions = "vqf", | |
259 }; |