diff options
author | Mohamed Tarek <mt@rockbox.org> | 2010-05-02 17:19:42 +0000 |
---|---|---|
committer | Mohamed Tarek <mt@rockbox.org> | 2010-05-02 17:19:42 +0000 |
commit | 792ae6b1bd7ec85f2ab8eec34dad1d22b05a213e (patch) | |
tree | 83a8976bba26b5252061334292bbf6e4e57fa628 /apps/codecs/libasf | |
parent | 13075dea876898afdb195056d22a921b18cd4450 (diff) | |
download | rockbox-792ae6b1bd7ec85f2ab8eec34dad1d22b05a213e.tar.gz rockbox-792ae6b1bd7ec85f2ab8eec34dad1d22b05a213e.zip |
- Factor out container specific code from apps/codecs/wma.c.
- Create an independent asf packet-parsing library in apps/codecs/libasf.
- Modify wma.c to use the newly created libasf.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25780 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/codecs/libasf')
-rw-r--r-- | apps/codecs/libasf/SOURCES | 1 | ||||
-rw-r--r-- | apps/codecs/libasf/asf.c | 354 | ||||
-rw-r--r-- | apps/codecs/libasf/asf.h | 42 | ||||
-rw-r--r-- | apps/codecs/libasf/libasf.make | 25 |
4 files changed, 422 insertions, 0 deletions
diff --git a/apps/codecs/libasf/SOURCES b/apps/codecs/libasf/SOURCES new file mode 100644 index 0000000000..1fee336990 --- /dev/null +++ b/apps/codecs/libasf/SOURCES | |||
@@ -0,0 +1 @@ | |||
asf.c | |||
diff --git a/apps/codecs/libasf/asf.c b/apps/codecs/libasf/asf.c new file mode 100644 index 0000000000..f65627b057 --- /dev/null +++ b/apps/codecs/libasf/asf.c | |||
@@ -0,0 +1,354 @@ | |||
1 | #include <inttypes.h> | ||
2 | #include "codeclib.h" | ||
3 | #include "asf.h" | ||
4 | |||
5 | /* Read an unaligned 32-bit little endian long from buffer. */ | ||
6 | static unsigned long get_long_le(void* buf) | ||
7 | { | ||
8 | unsigned char* p = (unsigned char*) buf; | ||
9 | |||
10 | return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); | ||
11 | } | ||
12 | |||
13 | /* Read an unaligned 16-bit little endian short from buffer. */ | ||
14 | static unsigned short get_short_le(void* buf) | ||
15 | { | ||
16 | unsigned char* p = (unsigned char*) buf; | ||
17 | |||
18 | return p[0] | (p[1] << 8); | ||
19 | } | ||
20 | |||
21 | #define GETLEN2b(bits) (((bits) == 0x03) ? 4 : bits) | ||
22 | |||
23 | #define GETVALUE2b(bits, data) \ | ||
24 | (((bits) != 0x03) ? ((bits) != 0x02) ? ((bits) != 0x01) ? \ | ||
25 | 0 : *(data) : get_short_le(data) : get_long_le(data)) | ||
26 | |||
27 | int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlength, | ||
28 | asf_waveformatex_t* wfx, struct codec_api* ci) | ||
29 | { | ||
30 | uint8_t tmp8, packet_flags, packet_property; | ||
31 | int stream_id; | ||
32 | int ec_length, opaque_data, ec_length_type; | ||
33 | int datalen; | ||
34 | uint8_t data[18]; | ||
35 | uint8_t* datap; | ||
36 | uint32_t length; | ||
37 | uint32_t padding_length; | ||
38 | uint32_t send_time; | ||
39 | uint16_t duration; | ||
40 | uint16_t payload_count; | ||
41 | int payload_length_type; | ||
42 | uint32_t payload_hdrlen; | ||
43 | int payload_datalen; | ||
44 | int multiple; | ||
45 | uint32_t replicated_length; | ||
46 | uint32_t media_object_number; | ||
47 | uint32_t media_object_offset; | ||
48 | uint32_t bytesread = 0; | ||
49 | uint8_t* buf; | ||
50 | size_t bufsize; | ||
51 | int i; | ||
52 | /*DEBUGF("Reading new packet at %d bytes ", (int)ci->curpos);*/ | ||
53 | |||
54 | if (ci->read_filebuf(&tmp8, 1) == 0) { | ||
55 | return ASF_ERROR_EOF; | ||
56 | } | ||
57 | bytesread++; | ||
58 | |||
59 | /* TODO: We need a better way to detect endofstream */ | ||
60 | if (tmp8 != 0x82) { | ||
61 | DEBUGF("Read failed: packet did not sync\n"); | ||
62 | return -1; | ||
63 | } | ||
64 | |||
65 | |||
66 | if (tmp8 & 0x80) { | ||
67 | ec_length = tmp8 & 0x0f; | ||
68 | opaque_data = (tmp8 >> 4) & 0x01; | ||
69 | ec_length_type = (tmp8 >> 5) & 0x03; | ||
70 | |||
71 | if (ec_length_type != 0x00 || opaque_data != 0 || ec_length != 0x02) { | ||
72 | DEBUGF("incorrect error correction flags\n"); | ||
73 | return ASF_ERROR_INVALID_VALUE; | ||
74 | } | ||
75 | |||
76 | /* Skip ec_data */ | ||
77 | ci->advance_buffer(ec_length); | ||
78 | bytesread += ec_length; | ||
79 | } else { | ||
80 | ec_length = 0; | ||
81 | } | ||
82 | |||
83 | if (ci->read_filebuf(&packet_flags, 1) == 0) { return ASF_ERROR_EOF; } | ||
84 | if (ci->read_filebuf(&packet_property, 1) == 0) { return ASF_ERROR_EOF; } | ||
85 | bytesread += 2; | ||
86 | |||
87 | datalen = GETLEN2b((packet_flags >> 1) & 0x03) + | ||
88 | GETLEN2b((packet_flags >> 3) & 0x03) + | ||
89 | GETLEN2b((packet_flags >> 5) & 0x03) + 6; | ||
90 | |||
91 | #if 0 | ||
92 | if (datalen > sizeof(data)) { | ||
93 | DEBUGF("Unexpectedly long datalen in data - %d\n",datalen); | ||
94 | return ASF_ERROR_OUTOFMEM; | ||
95 | } | ||
96 | #endif | ||
97 | |||
98 | if (ci->read_filebuf(data, datalen) == 0) { | ||
99 | return ASF_ERROR_EOF; | ||
100 | } | ||
101 | |||
102 | bytesread += datalen; | ||
103 | |||
104 | datap = data; | ||
105 | length = GETVALUE2b((packet_flags >> 5) & 0x03, datap); | ||
106 | datap += GETLEN2b((packet_flags >> 5) & 0x03); | ||
107 | /* sequence value is not used */ | ||
108 | GETVALUE2b((packet_flags >> 1) & 0x03, datap); | ||
109 | datap += GETLEN2b((packet_flags >> 1) & 0x03); | ||
110 | padding_length = GETVALUE2b((packet_flags >> 3) & 0x03, datap); | ||
111 | datap += GETLEN2b((packet_flags >> 3) & 0x03); | ||
112 | send_time = get_long_le(datap); | ||
113 | datap += 4; | ||
114 | duration = get_short_le(datap); | ||
115 | datap += 2; | ||
116 | /*DEBUGF("and duration %d ms\n", duration);*/ | ||
117 | |||
118 | /* this is really idiotic, packet length can (and often will) be | ||
119 | * undefined and we just have to use the header packet size as the size | ||
120 | * value */ | ||
121 | if (!((packet_flags >> 5) & 0x03)) { | ||
122 | length = wfx->packet_size; | ||
123 | } | ||
124 | |||
125 | /* this is also really idiotic, if packet length is smaller than packet | ||
126 | * size, we need to manually add the additional bytes into padding length | ||
127 | */ | ||
128 | if (length < wfx->packet_size) { | ||
129 | padding_length += wfx->packet_size - length; | ||
130 | length = wfx->packet_size; | ||
131 | } | ||
132 | |||
133 | if (length > wfx->packet_size) { | ||
134 | DEBUGF("packet with too big length value\n"); | ||
135 | return ASF_ERROR_INVALID_LENGTH; | ||
136 | } | ||
137 | |||
138 | /* check if we have multiple payloads */ | ||
139 | if (packet_flags & 0x01) { | ||
140 | if (ci->read_filebuf(&tmp8, 1) == 0) { | ||
141 | return ASF_ERROR_EOF; | ||
142 | } | ||
143 | payload_count = tmp8 & 0x3f; | ||
144 | payload_length_type = (tmp8 >> 6) & 0x03; | ||
145 | bytesread++; | ||
146 | } else { | ||
147 | payload_count = 1; | ||
148 | payload_length_type = 0x02; /* not used */ | ||
149 | } | ||
150 | |||
151 | if (length < bytesread) { | ||
152 | DEBUGF("header exceeded packet size, invalid file - length=%d, bytesread=%d\n",(int)length,(int)bytesread); | ||
153 | /* FIXME: should this be checked earlier? */ | ||
154 | return ASF_ERROR_INVALID_LENGTH; | ||
155 | } | ||
156 | |||
157 | |||
158 | /* We now parse the individual payloads, and move all payloads | ||
159 | belonging to our audio stream to a contiguous block, starting at | ||
160 | the location of the first payload. | ||
161 | */ | ||
162 | |||
163 | *audiobuf = NULL; | ||
164 | *audiobufsize = 0; | ||
165 | *packetlength = length - bytesread; | ||
166 | |||
167 | buf = ci->request_buffer(&bufsize, length); | ||
168 | datap = buf; | ||
169 | |||
170 | if (bufsize != length) { | ||
171 | /* This should only happen with packets larger than 32KB (the | ||
172 | guard buffer size). All the streams I've seen have | ||
173 | relatively small packets less than about 8KB), but I don't | ||
174 | know what is expected. | ||
175 | */ | ||
176 | DEBUGF("Could not read packet (requested %d bytes, received %d), curpos=%d, aborting\n", | ||
177 | (int)length,(int)bufsize,(int)ci->curpos); | ||
178 | return -1; | ||
179 | } | ||
180 | |||
181 | for (i=0; i<payload_count; i++) { | ||
182 | stream_id = datap[0]&0x7f; | ||
183 | datap++; | ||
184 | bytesread++; | ||
185 | |||
186 | payload_hdrlen = GETLEN2b(packet_property & 0x03) + | ||
187 | GETLEN2b((packet_property >> 2) & 0x03) + | ||
188 | GETLEN2b((packet_property >> 4) & 0x03); | ||
189 | |||
190 | //DEBUGF("payload_hdrlen = %d\n",payload_hdrlen); | ||
191 | #if 0 | ||
192 | /* TODO */ | ||
193 | if (payload_hdrlen > size) { | ||
194 | return ASF_ERROR_INVALID_LENGTH; | ||
195 | } | ||
196 | #endif | ||
197 | if (payload_hdrlen > sizeof(data)) { | ||
198 | DEBUGF("Unexpectedly long datalen in data - %d\n",datalen); | ||
199 | return ASF_ERROR_OUTOFMEM; | ||
200 | } | ||
201 | |||
202 | bytesread += payload_hdrlen; | ||
203 | media_object_number = GETVALUE2b((packet_property >> 4) & 0x03, datap); | ||
204 | datap += GETLEN2b((packet_property >> 4) & 0x03); | ||
205 | media_object_offset = GETVALUE2b((packet_property >> 2) & 0x03, datap); | ||
206 | datap += GETLEN2b((packet_property >> 2) & 0x03); | ||
207 | replicated_length = GETVALUE2b(packet_property & 0x03, datap); | ||
208 | datap += GETLEN2b(packet_property & 0x03); | ||
209 | |||
210 | /* TODO: Validate replicated_length */ | ||
211 | /* TODO: Is the content of this important for us? */ | ||
212 | datap += replicated_length; | ||
213 | bytesread += replicated_length; | ||
214 | |||
215 | multiple = packet_flags & 0x01; | ||
216 | |||
217 | |||
218 | if (multiple) { | ||
219 | int x; | ||
220 | |||
221 | x = GETLEN2b(payload_length_type); | ||
222 | |||
223 | if (x != 2) { | ||
224 | /* in multiple payloads datalen should be a word */ | ||
225 | return ASF_ERROR_INVALID_VALUE; | ||
226 | } | ||
227 | |||
228 | #if 0 | ||
229 | if (skip + tmp > datalen) { | ||
230 | /* not enough data */ | ||
231 | return ASF_ERROR_INVALID_LENGTH; | ||
232 | } | ||
233 | #endif | ||
234 | payload_datalen = GETVALUE2b(payload_length_type, datap); | ||
235 | datap += x; | ||
236 | bytesread += x; | ||
237 | } else { | ||
238 | payload_datalen = length - bytesread - padding_length; | ||
239 | } | ||
240 | |||
241 | if (replicated_length==1) | ||
242 | datap++; | ||
243 | |||
244 | if (stream_id == wfx->audiostream) | ||
245 | { | ||
246 | if (*audiobuf == NULL) { | ||
247 | /* The first payload can stay where it is */ | ||
248 | *audiobuf = datap; | ||
249 | *audiobufsize = payload_datalen; | ||
250 | } else { | ||
251 | /* The second and subsequent payloads in this packet | ||
252 | that belong to the audio stream need to be moved to be | ||
253 | contiguous with the first payload. | ||
254 | */ | ||
255 | memmove(*audiobuf + *audiobufsize, datap, payload_datalen); | ||
256 | *audiobufsize += payload_datalen; | ||
257 | } | ||
258 | } | ||
259 | datap += payload_datalen; | ||
260 | bytesread += payload_datalen; | ||
261 | } | ||
262 | |||
263 | if (*audiobuf != NULL) | ||
264 | return 1; | ||
265 | else | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | |||
270 | int asf_get_timestamp(int *duration, struct codec_api* ci) | ||
271 | { | ||
272 | uint8_t tmp8, packet_flags, packet_property; | ||
273 | int ec_length, opaque_data, ec_length_type; | ||
274 | int datalen; | ||
275 | uint8_t data[18]; | ||
276 | uint8_t* datap; | ||
277 | uint32_t length; | ||
278 | uint32_t padding_length; | ||
279 | uint32_t send_time; | ||
280 | static int packet_count = 0; | ||
281 | |||
282 | uint32_t bytesread = 0; | ||
283 | packet_count++; | ||
284 | if (ci->read_filebuf(&tmp8, 1) == 0) { | ||
285 | DEBUGF("ASF ERROR (EOF?)\n"); | ||
286 | return ASF_ERROR_EOF; | ||
287 | } | ||
288 | bytesread++; | ||
289 | |||
290 | /* TODO: We need a better way to detect endofstream */ | ||
291 | if (tmp8 != 0x82) { | ||
292 | DEBUGF("Get timestamp: Detected end of stream\n"); | ||
293 | return ASF_ERROR_EOF; | ||
294 | } | ||
295 | |||
296 | |||
297 | if (tmp8 & 0x80) { | ||
298 | ec_length = tmp8 & 0x0f; | ||
299 | opaque_data = (tmp8 >> 4) & 0x01; | ||
300 | ec_length_type = (tmp8 >> 5) & 0x03; | ||
301 | |||
302 | if (ec_length_type != 0x00 || opaque_data != 0 || ec_length != 0x02) { | ||
303 | DEBUGF("incorrect error correction flags\n"); | ||
304 | return ASF_ERROR_INVALID_VALUE; | ||
305 | } | ||
306 | |||
307 | /* Skip ec_data */ | ||
308 | ci->advance_buffer(ec_length); | ||
309 | bytesread += ec_length; | ||
310 | } else { | ||
311 | ec_length = 0; | ||
312 | } | ||
313 | |||
314 | if (ci->read_filebuf(&packet_flags, 1) == 0) { | ||
315 | DEBUGF("Detected end of stream 2\n"); | ||
316 | return ASF_ERROR_EOF; | ||
317 | } | ||
318 | |||
319 | if (ci->read_filebuf(&packet_property, 1) == 0) { | ||
320 | DEBUGF("Detected end of stream3\n"); | ||
321 | return ASF_ERROR_EOF; | ||
322 | } | ||
323 | bytesread += 2; | ||
324 | |||
325 | datalen = GETLEN2b((packet_flags >> 1) & 0x03) + | ||
326 | GETLEN2b((packet_flags >> 3) & 0x03) + | ||
327 | GETLEN2b((packet_flags >> 5) & 0x03) + 6; | ||
328 | |||
329 | if (ci->read_filebuf(data, datalen) == 0) { | ||
330 | DEBUGF("Detected end of stream4\n"); | ||
331 | return ASF_ERROR_EOF; | ||
332 | } | ||
333 | |||
334 | bytesread += datalen; | ||
335 | |||
336 | datap = data; | ||
337 | length = GETVALUE2b((packet_flags >> 5) & 0x03, datap); | ||
338 | datap += GETLEN2b((packet_flags >> 5) & 0x03); | ||
339 | |||
340 | /* sequence value is not used */ | ||
341 | GETVALUE2b((packet_flags >> 1) & 0x03, datap); | ||
342 | datap += GETLEN2b((packet_flags >> 1) & 0x03); | ||
343 | padding_length = GETVALUE2b((packet_flags >> 3) & 0x03, datap); | ||
344 | datap += GETLEN2b((packet_flags >> 3) & 0x03); | ||
345 | send_time = get_long_le(datap); | ||
346 | datap += 4; | ||
347 | *duration = get_short_le(datap); | ||
348 | |||
349 | /*the asf_get_timestamp function advances us 12-13 bytes past the packet start, | ||
350 | need to undo this here so that we stay synced with the packet*/ | ||
351 | ci->seek_buffer(ci->curpos-bytesread); | ||
352 | |||
353 | return send_time; | ||
354 | } | ||
diff --git a/apps/codecs/libasf/asf.h b/apps/codecs/libasf/asf.h new file mode 100644 index 0000000000..03af2b0de1 --- /dev/null +++ b/apps/codecs/libasf/asf.h | |||
@@ -0,0 +1,42 @@ | |||
1 | #ifndef _ASF_H | ||
2 | #define _ASF_H | ||
3 | |||
4 | #include <inttypes.h> | ||
5 | |||
6 | /* ASF codec IDs */ | ||
7 | #define ASF_CODEC_ID_WMAV1 0x160 | ||
8 | #define ASF_CODEC_ID_WMAV2 0x161 | ||
9 | |||
10 | enum asf_error_e { | ||
11 | ASF_ERROR_INTERNAL = -1, /* incorrect input to API calls */ | ||
12 | ASF_ERROR_OUTOFMEM = -2, /* some malloc inside program failed */ | ||
13 | ASF_ERROR_EOF = -3, /* unexpected end of file */ | ||
14 | ASF_ERROR_IO = -4, /* error reading or writing to file */ | ||
15 | ASF_ERROR_INVALID_LENGTH = -5, /* length value conflict in input data */ | ||
16 | ASF_ERROR_INVALID_VALUE = -6, /* other value conflict in input data */ | ||
17 | ASF_ERROR_INVALID_OBJECT = -7, /* ASF object missing or in wrong place */ | ||
18 | ASF_ERROR_OBJECT_SIZE = -8, /* invalid ASF object size (too small) */ | ||
19 | ASF_ERROR_SEEKABLE = -9, /* file not seekable */ | ||
20 | ASF_ERROR_SEEK = -10 /* file is seekable but seeking failed */ | ||
21 | }; | ||
22 | |||
23 | struct asf_waveformatex_s { | ||
24 | uint32_t packet_size; | ||
25 | int audiostream; | ||
26 | uint16_t codec_id; | ||
27 | uint16_t channels; | ||
28 | uint32_t rate; | ||
29 | uint32_t bitrate; | ||
30 | uint16_t blockalign; | ||
31 | uint16_t bitspersample; | ||
32 | uint16_t datalen; | ||
33 | uint8_t data[6]; | ||
34 | }; | ||
35 | typedef struct asf_waveformatex_s asf_waveformatex_t; | ||
36 | |||
37 | int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlength, | ||
38 | asf_waveformatex_t* wfx, struct codec_api* ci); | ||
39 | |||
40 | int asf_get_timestamp(int *duration, struct codec_api* ci); | ||
41 | |||
42 | #endif | ||
diff --git a/apps/codecs/libasf/libasf.make b/apps/codecs/libasf/libasf.make new file mode 100644 index 0000000000..31d80a33f8 --- /dev/null +++ b/apps/codecs/libasf/libasf.make | |||
@@ -0,0 +1,25 @@ | |||
1 | # __________ __ ___. | ||
2 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
3 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
4 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
5 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
6 | # \/ \/ \/ \/ \/ | ||
7 | # $Id$ | ||
8 | # | ||
9 | |||
10 | # libasf | ||
11 | ASFLIB := $(CODECDIR)/libasf.a | ||
12 | ASFLIB_SRC := $(call preprocess, $(APPSDIR)/codecs/libasf/SOURCES) | ||
13 | ASFLIB_OBJ := $(call c2obj, $(ASFLIB_SRC)) | ||
14 | OTHER_SRC += $(ASFLIB_SRC) | ||
15 | |||
16 | $(ASFLIB): $(ASFLIB_OBJ) | ||
17 | $(SILENT)$(shell rm -f $@) | ||
18 | $(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null | ||
19 | |||
20 | ASFFLAGS = $(filter-out -O%,$(CODECFLAGS)) | ||
21 | ASFFLAGS += -O3 | ||
22 | |||
23 | $(CODECDIR)/libasf/%.o: $(ROOTDIR)/apps/codecs/libasf/%.c | ||
24 | $(SILENT)mkdir -p $(dir $@) | ||
25 | $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) $(ASFFLAGS) -c $< -o $@ | ||