File Coverage

src/asf.c
Criterion Covered Total %
statement 683 800 85.3
branch 264 362 72.9
condition n/a
subroutine n/a
pod n/a
total 947 1162 81.5


line stmt bran cond sub pod time code
1             /*
2             * This program is free software; you can redistribute it and/or modify
3             * it under the terms of the GNU General Public License as published by
4             * the Free Software Foundation; either version 2 of the License, or
5             * (at your option) any later version.
6             *
7             * This program is distributed in the hope that it will be useful,
8             * but WITHOUT ANY WARRANTY; without even the implied warranty of
9             * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10             * GNU General Public License for more details.
11             *
12             * You should have received a copy of the GNU General Public License
13             * along with this program; if not, write to the Free Software
14             * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15             */
16              
17             /*
18             TODO:
19             These will be added when I see a real file that uses them.
20              
21             Header objects:
22              
23             Marker (3.7)
24             Bitrate Mutual Exclusion (3.8)
25             Content Branding (3.13)
26              
27             Header Extension objects:
28              
29             Group Mutual Exclusion (4.3)
30             Stream Prioritization (4.4)
31             Bandwidth Sharing (4.5)
32             Media Object Index Parameters (4.10)
33             Timecode Index Parameters (4.11)
34             Advanced Content Encryption (4.13)
35              
36             Index objects:
37              
38             Media Object Index (6.3)
39             Timecode Index (6.4)
40             */
41              
42             #include "asf.h"
43              
44             static void
45 0           print_guid(GUID guid)
46             {
47 0           PerlIO_printf(PerlIO_stderr(),
48             "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x ",
49 0           guid.Data1, guid.Data2, guid.Data3,
50 0           guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
51 0           guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]
52             );
53 0           }
54              
55             int
56 15           get_asf_metadata(PerlIO *infile, char *file, HV *info, HV *tags)
57             {
58 15           asfinfo *asf = _asf_parse(infile, file, info, tags, 0);
59              
60 15           Safefree(asf);
61              
62 15           return 0;
63             }
64              
65             asfinfo *
66 23           _asf_parse(PerlIO *infile, char *file, HV *info, HV *tags, uint8_t seeking)
67             {
68             ASF_Object hdr;
69             ASF_Object data;
70             ASF_Object tmp;
71             asfinfo *asf;
72              
73 23           Newz(0, asf, sizeof(asfinfo), asfinfo);
74 23           Newz(0, asf->buf, sizeof(Buffer), Buffer);
75 23           Newz(0, asf->scratch, sizeof(Buffer), Buffer);
76              
77 23           asf->file_size = _file_size(infile);
78 23           asf->audio_offset = 0;
79 23           asf->object_offset = 0;
80 23           asf->infile = infile;
81 23           asf->file = file;
82 23           asf->info = info;
83 23           asf->tags = tags;
84 23           asf->seeking = seeking;
85              
86 23           buffer_init(asf->buf, ASF_BLOCK_SIZE);
87              
88 23 50         if ( !_check_buf(infile, asf->buf, 30, ASF_BLOCK_SIZE) ) {
89 0           goto out;
90             }
91              
92 23           buffer_get_guid(asf->buf, &hdr.ID);
93              
94 23 50         if ( !IsEqualGUID(&hdr.ID, &ASF_Header_Object) ) {
95 0           PerlIO_printf(PerlIO_stderr(), "Invalid ASF header: %s\n", file);
96 0           PerlIO_printf(PerlIO_stderr(), " Expecting: ");
97 0           print_guid(ASF_Header_Object);
98 0           PerlIO_printf(PerlIO_stderr(), "\n Got: ");
99 0           print_guid(hdr.ID);
100 0           PerlIO_printf(PerlIO_stderr(), "\n");
101 0           goto out;
102             }
103              
104 23           hdr.size = buffer_get_int64_le(asf->buf);
105 23           hdr.num_objects = buffer_get_int_le(asf->buf);
106 23           hdr.reserved1 = buffer_get_char(asf->buf);
107 23           hdr.reserved2 = buffer_get_char(asf->buf);
108              
109 23 50         if ( hdr.reserved2 != 0x02 ) {
110 0           PerlIO_printf(PerlIO_stderr(), "Invalid ASF header: %s\n", file);
111 0           goto out;
112             }
113              
114 23           asf->object_offset += 30;
115              
116 189 100         while ( hdr.num_objects-- ) {
117 166 50         if ( !_check_buf(infile, asf->buf, 24, ASF_BLOCK_SIZE) ) {
118 0           goto out;
119             }
120              
121 166           buffer_get_guid(asf->buf, &tmp.ID);
122 166           tmp.size = buffer_get_int64_le(asf->buf);
123              
124 166 50         if ( !_check_buf(infile, asf->buf, tmp.size - 24, ASF_BLOCK_SIZE) ) {
125 0           goto out;
126             }
127              
128 166           asf->object_offset += 24;
129              
130             DEBUG_TRACE("object_offset %d\n", asf->object_offset);
131              
132 166 100         if ( IsEqualGUID(&tmp.ID, &ASF_Content_Description) ) {
133             DEBUG_TRACE("Content_Description\n");
134 22           _parse_content_description(asf);
135             }
136 144 100         else if ( IsEqualGUID(&tmp.ID, &ASF_File_Properties) ) {
137             DEBUG_TRACE("File_Properties\n");
138 23           _parse_file_properties(asf);
139             }
140 121 100         else if ( IsEqualGUID(&tmp.ID, &ASF_Stream_Properties) ) {
141             DEBUG_TRACE("Stream_Properties\n");
142 26           _parse_stream_properties(asf);
143             }
144 95 100         else if ( IsEqualGUID(&tmp.ID, &ASF_Extended_Content_Description) ) {
145             DEBUG_TRACE("Extended_Content_Description\n");
146 22           _parse_extended_content_description(asf);
147             }
148 73 100         else if ( IsEqualGUID(&tmp.ID, &ASF_Codec_List) ) {
149             DEBUG_TRACE("Codec_List\n");
150 23           _parse_codec_list(asf);
151             }
152 50 100         else if ( IsEqualGUID(&tmp.ID, &ASF_Stream_Bitrate_Properties) ) {
153             DEBUG_TRACE("Stream_Bitrate_Properties\n");
154 22           _parse_stream_bitrate_properties(asf);
155             }
156 28 100         else if ( IsEqualGUID(&tmp.ID, &ASF_Content_Encryption) ) {
157             DEBUG_TRACE("Content_Encryption\n");
158 1           _parse_content_encryption(asf);
159             }
160 27 100         else if ( IsEqualGUID(&tmp.ID, &ASF_Extended_Content_Encryption) ) {
161             DEBUG_TRACE("Extended_Content_Encryption\n");
162 1           _parse_extended_content_encryption(asf);
163             }
164 26 100         else if ( IsEqualGUID(&tmp.ID, &ASF_Script_Command) ) {
165             DEBUG_TRACE("Script_Command\n");
166 2           _parse_script_command(asf);
167             }
168 24 100         else if ( IsEqualGUID(&tmp.ID, &ASF_Digital_Signature) ) {
169             DEBUG_TRACE("Skipping Digital_Signature\n");
170 1           buffer_consume(asf->buf, tmp.size - 24);
171             }
172 23 50         else if ( IsEqualGUID(&tmp.ID, &ASF_Header_Extension) ) {
173             DEBUG_TRACE("Header_Extension\n");
174 23 50         if ( !_parse_header_extension(asf, tmp.size) ) {
175 0           PerlIO_printf(PerlIO_stderr(), "Invalid ASF file: %s (invalid header extension object)\n", file);
176 0           goto out;
177             }
178             }
179 0 0         else if ( IsEqualGUID(&tmp.ID, &ASF_Error_Correction) ) {
180             DEBUG_TRACE("Skipping Error_Correction\n");
181 0           buffer_consume(asf->buf, tmp.size - 24);
182             }
183             else {
184             // Unhandled GUID
185 0           PerlIO_printf(PerlIO_stderr(), "** Unhandled GUID: ");
186 0           print_guid(tmp.ID);
187 0           PerlIO_printf(PerlIO_stderr(), "size: %llu\n", tmp.size);
188              
189 0           buffer_consume(asf->buf, tmp.size - 24);
190             }
191              
192 166           asf->object_offset += tmp.size - 24;
193             }
194              
195             // We should be at the start of the Data object.
196             // Seek past it to find more objects
197 23 50         if ( !_check_buf(infile, asf->buf, 24, ASF_BLOCK_SIZE) ) {
198 0           goto out;
199             }
200              
201 23           buffer_get_guid(asf->buf, &data.ID);
202              
203 23 50         if ( !IsEqualGUID(&data.ID, &ASF_Data) ) {
204 0           PerlIO_printf(PerlIO_stderr(), "Invalid ASF file: %s (no Data object after Header)\n", file);
205 0           goto out;
206             }
207              
208             // Store offset to beginning of data (50 goes past the top-level data packet)
209 23           asf->audio_offset = hdr.size + 50;
210 23           my_hv_store( info, "audio_offset", newSVuv(asf->audio_offset) );
211              
212 23           my_hv_store( info, "file_size", newSVuv(asf->file_size) );
213              
214 23           data.size = buffer_get_int64_le(asf->buf);
215 23           asf->audio_size = data.size;
216              
217             // Check audio_size is not larger than file
218 23 100         if (asf->audio_size > asf->file_size - asf->audio_offset) {
219 9           asf->audio_size = asf->file_size - asf->audio_offset;
220             DEBUG_TRACE("audio_size too large, fixed to %lld\n", asf->audio_size);
221             }
222 23           my_hv_store( info, "audio_size", newSVuv(asf->audio_size) );
223              
224 23 100         if (seeking) {
225 8 100         if ( hdr.size + data.size < asf->file_size ) {
226             DEBUG_TRACE("Seeking past data: %llu\n", hdr.size + data.size);
227              
228 7 50         if ( PerlIO_seek(infile, hdr.size + data.size, SEEK_SET) != 0 ) {
229 0           PerlIO_printf(PerlIO_stderr(), "Invalid ASF file: %s (Invalid Data object size)\n", file);
230 0           goto out;
231             }
232              
233 7           buffer_clear(asf->buf);
234              
235 7 50         if ( !_parse_index_objects(asf, asf->file_size - hdr.size - data.size) ) {
236 0           PerlIO_printf(PerlIO_stderr(), "Invalid ASF file: %s (Invalid Index object)\n", file);
237 0           goto out;
238             }
239             }
240             }
241              
242             out:
243 23           buffer_free(asf->buf);
244 23           Safefree(asf->buf);
245              
246 23 50         if (asf->scratch->alloc)
247 23           buffer_free(asf->scratch);
248 23           Safefree(asf->scratch);
249              
250 23           return asf;
251             }
252              
253             void
254 22           _parse_content_description(asfinfo *asf)
255             {
256             int i;
257             uint16_t len[5];
258 22           char fields[5][12] = {
259             { "Title" },
260             { "Author" },
261             { "Copyright" },
262             { "Description" },
263             { "Rating" }
264             };
265              
266 132 100         for (i = 0; i < 5; i++) {
267 110           len[i] = buffer_get_short_le(asf->buf);
268             }
269              
270 22           buffer_init_or_clear(asf->scratch, len[0]);
271              
272 132 100         for (i = 0; i < 5; i++) {
273             SV *value;
274              
275 110 100         if ( len[i] ) {
276 101           buffer_clear(asf->scratch);
277 101           buffer_get_utf16_as_utf8(asf->buf, asf->scratch, len[i], UTF16_BYTEORDER_LE);
278 101           value = newSVpv( buffer_ptr(asf->scratch), 0 );
279 101           sv_utf8_decode(value);
280              
281             DEBUG_TRACE(" %s / %s\n", fields[i], SvPVX(value));
282              
283 101           _store_tag( asf->tags, newSVpv(fields[i], 0), value );
284             }
285             }
286 22           }
287              
288             void
289 22           _parse_extended_content_description(asfinfo *asf)
290             {
291 22           uint16_t count = buffer_get_short_le(asf->buf);
292 22           uint32_t picture_offset = 0;
293              
294 22           buffer_init_or_clear(asf->scratch, 32);
295              
296 174 100         while ( count-- ) {
297             uint16_t name_len;
298             uint16_t data_type;
299             uint16_t value_len;
300 152           SV *key = NULL;
301 152           SV *value = NULL;
302              
303 152           name_len = buffer_get_short_le(asf->buf);
304              
305 152           buffer_clear(asf->scratch);
306 152           buffer_get_utf16_as_utf8(asf->buf, asf->scratch, name_len, UTF16_BYTEORDER_LE);
307 152           key = newSVpv( buffer_ptr(asf->scratch), 0 );
308 152           sv_utf8_decode(key);
309              
310 152           data_type = buffer_get_short_le(asf->buf);
311 152           value_len = buffer_get_short_le(asf->buf);
312              
313 152           picture_offset += 2 + name_len + 4;
314              
315 152 100         if (data_type == TYPE_UNICODE) {
316 107           buffer_clear(asf->scratch);
317 107           buffer_get_utf16_as_utf8(asf->buf, asf->scratch, value_len, UTF16_BYTEORDER_LE);
318 107           value = newSVpv( buffer_ptr(asf->scratch), 0 );
319 107           sv_utf8_decode(value);
320             }
321 45 100         else if (data_type == TYPE_BYTE) {
322             // handle picture data, interestingly it is compatible with the ID3v2 APIC frame
323 19 100         if ( !strcmp( SvPVX(key), "WM/Picture" ) ) {
324 7           value = _parse_picture(asf, picture_offset);
325             }
326             else {
327 12           value = newSVpvn( buffer_ptr(asf->buf), value_len );
328 19           buffer_consume(asf->buf, value_len);
329             }
330             }
331 26 100         else if (data_type == TYPE_BOOL) {
332 22           value = newSViv( buffer_get_int_le(asf->buf) );
333             }
334 4 100         else if (data_type == TYPE_DWORD) {
335 3           value = newSViv( buffer_get_int_le(asf->buf) );
336             }
337 1 50         else if (data_type == TYPE_QWORD) {
338 1           value = newSViv( buffer_get_int64_le(asf->buf) );
339             }
340 0 0         else if (data_type == TYPE_WORD) {
341 0           value = newSViv( buffer_get_short_le(asf->buf) );
342             }
343             else {
344 0           PerlIO_printf(PerlIO_stderr(), "Unknown extended content description data type %d\n", data_type);
345 0           buffer_consume(asf->buf, value_len);
346             }
347              
348 152           picture_offset += value_len;
349              
350 152 50         if (value != NULL) {
351             #ifdef AUDIO_SCAN_DEBUG
352             if ( data_type == 0 ) {
353             DEBUG_TRACE(" %s / type %d / %s\n", SvPVX(key), data_type, SvPVX(value));
354             }
355             else if ( data_type > 1 ) {
356             DEBUG_TRACE(" %s / type %d / %d\n", SvPVX(key), data_type, (int)SvIV(value));
357             }
358             else {
359             DEBUG_TRACE(" %s / type %d / \n", SvPVX(key), data_type);
360             }
361             #endif
362              
363 152           _store_tag( asf->tags, key, value );
364             }
365             }
366 22           }
367              
368             void
369 23           _parse_file_properties(asfinfo *asf)
370             {
371             GUID file_id;
372             uint64_t file_size;
373             uint64_t creation_date;
374             uint64_t data_packets;
375             uint64_t play_duration;
376             uint64_t send_duration;
377             uint64_t preroll;
378             uint32_t flags;
379             uint32_t min_packet_size;
380             uint32_t max_packet_size;
381             uint32_t max_bitrate;
382             uint8_t broadcast;
383             uint8_t seekable;
384              
385 23           buffer_get_guid(asf->buf, &file_id);
386 23           my_hv_store(
387             asf->info, "file_id", newSVpvf( "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
388             file_id.Data1, file_id.Data2, file_id.Data3,
389             file_id.Data4[0], file_id.Data4[1], file_id.Data4[2], file_id.Data4[3],
390             file_id.Data4[4], file_id.Data4[5], file_id.Data4[6], file_id.Data4[7]
391             )
392             );
393              
394 23           file_size = buffer_get_int64_le(asf->buf);
395 23           creation_date = buffer_get_int64_le(asf->buf);
396 23           data_packets = buffer_get_int64_le(asf->buf);
397 23           play_duration = buffer_get_int64_le(asf->buf);
398 23           send_duration = buffer_get_int64_le(asf->buf);
399 23           preroll = buffer_get_int64_le(asf->buf);
400 23           flags = buffer_get_int_le(asf->buf);
401 23           min_packet_size = buffer_get_int_le(asf->buf);
402 23           max_packet_size = buffer_get_int_le(asf->buf);
403 23           max_bitrate = buffer_get_int_le(asf->buf);
404              
405 23           broadcast = flags & 0x01 ? 1 : 0;
406 23           seekable = flags & 0x02 ? 1 : 0;
407              
408 23 100         if ( !broadcast ) {
409 22           creation_date = (creation_date - 116444736000000000ULL) / 10000000;
410 22           play_duration /= 10000;
411 22           send_duration /= 10000;
412              
413             // Don't overwrite the actual file size we found from stat
414             //my_hv_store( info, "file_size", newSViv(file_size) );
415              
416 22           my_hv_store( asf->info, "creation_date", newSViv(creation_date) );
417 22           my_hv_store( asf->info, "data_packets", newSViv(data_packets) );
418 22           my_hv_store( asf->info, "play_duration_ms", newSViv(play_duration) );
419 22           my_hv_store( asf->info, "send_duration_ms", newSViv(send_duration) );
420              
421             // Calculate actual song duration
422 22           my_hv_store( asf->info, "song_length_ms", newSViv( play_duration - preroll ) );
423             }
424              
425 23           my_hv_store( asf->info, "preroll", newSViv(preroll) );
426 23           my_hv_store( asf->info, "broadcast", newSViv(broadcast) );
427 23           my_hv_store( asf->info, "seekable", newSViv(seekable) );
428 23           my_hv_store( asf->info, "min_packet_size", newSViv(min_packet_size) );
429 23           my_hv_store( asf->info, "max_packet_size", newSViv(max_packet_size) );
430 23           my_hv_store( asf->info, "max_bitrate", newSViv(max_bitrate) );
431              
432             // DLNA, need to store max_bitrate for later
433 23           asf->max_bitrate = max_bitrate;
434 23           }
435              
436             void
437 30           _parse_stream_properties(asfinfo *asf)
438             {
439             GUID stream_type;
440             GUID ec_type;
441             uint64_t time_offset;
442             uint32_t type_data_len;
443             uint32_t ec_data_len;
444             uint16_t flags;
445             uint16_t stream_number;
446             Buffer type_data_buf;
447              
448 30           buffer_get_guid(asf->buf, &stream_type);
449 30           buffer_get_guid(asf->buf, &ec_type);
450 30           time_offset = buffer_get_int64_le(asf->buf);
451 30           type_data_len = buffer_get_int_le(asf->buf);
452 30           ec_data_len = buffer_get_int_le(asf->buf);
453 30           flags = buffer_get_short_le(asf->buf);
454 30           stream_number = flags & 0x007f;
455              
456             // skip reserved bytes
457 30           buffer_consume(asf->buf, 4);
458              
459             // type-specific data
460 30           buffer_init(&type_data_buf, type_data_len);
461 30           buffer_append(&type_data_buf, buffer_ptr(asf->buf), type_data_len);
462 30           buffer_consume(asf->buf, type_data_len);
463              
464             // skip error-correction data
465 30           buffer_consume(asf->buf, ec_data_len);
466              
467 30 100         if ( IsEqualGUID(&stream_type, &ASF_Audio_Media) ) {
468 26           uint8_t is_wma = 0;
469             uint16_t codec_id, channels;
470             uint32_t samplerate;
471              
472 26           _store_stream_info( stream_number, asf->info, newSVpv("stream_type", 0), newSVpv("ASF_Audio_Media", 0) );
473              
474             // Parse WAVEFORMATEX data
475 26           codec_id = buffer_get_short_le(&type_data_buf);
476 26           switch (codec_id) {
477             case 0x000a:
478 1           is_wma = 1;
479 1           break;
480              
481             case 0x0161:
482 21           is_wma = 1;
483 21           asf->valid_profiles |= IS_VALID_WMA_BASE | IS_VALID_WMA_FULL;
484 21           break;
485              
486             case 0x0162:
487 1           is_wma = 1;
488 1           asf->valid_profiles |= IS_VALID_WMA_PRO;
489 1           break;
490              
491             case 0x0163:
492 2           is_wma = 1;
493 2           asf->valid_profiles |= IS_VALID_WMA_LSL;
494 2           break;
495             }
496              
497 26           _store_stream_info( stream_number, asf->info, newSVpv("codec_id", 0), newSViv(codec_id) );
498              
499 26           channels = buffer_get_short_le(&type_data_buf);
500 26           _store_stream_info( stream_number, asf->info, newSVpv("channels", 0), newSViv(channels) );
501              
502 26           samplerate = buffer_get_int_le(&type_data_buf);
503 26           _store_stream_info( stream_number, asf->info, newSVpv("samplerate", 0), newSViv(samplerate) );
504              
505             // Determine DLNA profile
506 26 50         if (channels > 2) {
507 0           asf->valid_profiles &= ~IS_VALID_WMA_BASE;
508 0           asf->valid_profiles &= ~IS_VALID_WMA_FULL;
509              
510 0 0         if (codec_id == 0x0163) {
511 0           asf->valid_profiles &= ~IS_VALID_WMA_LSL;
512 0           asf->valid_profiles |= IS_VALID_WMA_LSL_MULT5;
513             }
514             }
515              
516 26 50         if (samplerate > 48000) {
517 0           asf->valid_profiles &= ~IS_VALID_WMA_BASE;
518 0           asf->valid_profiles &= ~IS_VALID_WMA_FULL;
519              
520 0 0         if (samplerate > 96000) {
521 0           asf->valid_profiles &= ~IS_VALID_WMA_PRO;
522 0           asf->valid_profiles &= ~IS_VALID_WMA_LSL; // XXX check N1/N2 defs
523 0           asf->valid_profiles &= ~IS_VALID_WMA_LSL_MULT5;
524             }
525             }
526              
527 26 100         if (asf->max_bitrate > 192999) {
528 3           asf->valid_profiles &= ~IS_VALID_WMA_BASE;
529              
530 3 100         if (asf->max_bitrate > 384999) {
531 2           asf->valid_profiles &= ~IS_VALID_WMA_FULL;
532              
533 2 50         if (asf->max_bitrate > 1499999) {
534 0           asf->valid_profiles &= ~IS_VALID_WMA_PRO;
535 0           asf->valid_profiles &= ~IS_VALID_WMA_LSL; // XXX check N1/N2 defs
536 0           asf->valid_profiles &= ~IS_VALID_WMA_LSL_MULT5;
537             }
538             }
539             }
540              
541 26 100         if (asf->valid_profiles & IS_VALID_WMA_BASE)
542 20           my_hv_store( asf->info, "dlna_profile", newSVpvn("WMABASE", 7) );
543 6 100         else if (asf->valid_profiles & IS_VALID_WMA_FULL)
544 1           my_hv_store( asf->info, "dlna_profile", newSVpvn("WMAFULL", 7) );
545 5 100         else if (asf->valid_profiles & IS_VALID_WMA_PRO)
546 1           my_hv_store( asf->info, "dlna_profile", newSVpvn("WMAPRO", 6) );
547 4 100         else if (asf->valid_profiles & IS_VALID_WMA_LSL)
548 2           my_hv_store( asf->info, "dlna_profile", newSVpvn("WMALSL", 6) );
549 2 50         else if (asf->valid_profiles & IS_VALID_WMA_LSL_MULT5)
550 0           my_hv_store( asf->info, "dlna_profile", newSVpvn("WMALSL_MULT5", 12) );
551              
552 26           _store_stream_info( stream_number, asf->info, newSVpv("avg_bytes_per_sec", 0), newSViv( buffer_get_int_le(&type_data_buf) ) );
553 26           _store_stream_info( stream_number, asf->info, newSVpv("block_alignment", 0), newSViv( buffer_get_short_le(&type_data_buf) ) );
554 26           _store_stream_info( stream_number, asf->info, newSVpv("bits_per_sample", 0), newSViv( buffer_get_short_le(&type_data_buf) ) );
555              
556             // Read WMA-specific data
557 26 100         if (is_wma) {
558 25           buffer_consume(&type_data_buf, 2);
559 25           _store_stream_info( stream_number, asf->info, newSVpv("samples_per_block", 0), newSViv( buffer_get_int_le(&type_data_buf) ) );
560 25           _store_stream_info( stream_number, asf->info, newSVpv("encode_options", 0), newSViv( buffer_get_short_le(&type_data_buf) ) );
561 26           _store_stream_info( stream_number, asf->info, newSVpv("super_block_align", 0), newSViv( buffer_get_int_le(&type_data_buf) ) );
562             }
563             }
564 4 100         else if ( IsEqualGUID(&stream_type, &ASF_Video_Media) ) {
565 2           _store_stream_info( stream_number, asf->info, newSVpv("stream_type", 0), newSVpv("ASF_Video_Media", 0) );
566              
567             DEBUG_TRACE("type_data_len: %d\n", type_data_len);
568              
569             // Read video-specific data
570 2           _store_stream_info( stream_number, asf->info, newSVpv("width", 0), newSVuv( buffer_get_int_le(&type_data_buf) ) );
571 2           _store_stream_info( stream_number, asf->info, newSVpv("height", 0), newSVuv( buffer_get_int_le(&type_data_buf) ) );
572              
573             // Skip format size, width, height, reserved
574 2           buffer_consume(&type_data_buf, 17);
575              
576 2           _store_stream_info( stream_number, asf->info, newSVpv("bpp", 0), newSVuv( buffer_get_short_le(&type_data_buf) ) );
577              
578 2           _store_stream_info( stream_number, asf->info, newSVpv("compression_id", 0), newSVpv( buffer_ptr(&type_data_buf), 4 ) );
579              
580             // Rest of the data does not seem to apply to video
581             }
582 2 100         else if ( IsEqualGUID(&stream_type, &ASF_Command_Media) ) {
583 1           _store_stream_info( stream_number, asf->info, newSVpv("stream_type", 0), newSVpv("ASF_Command_Media", 0) );
584             }
585 1 50         else if ( IsEqualGUID(&stream_type, &ASF_JFIF_Media) ) {
586 1           _store_stream_info( stream_number, asf->info, newSVpv("stream_type", 0), newSVpv("ASF_JFIF_Media", 0) );
587              
588             // type-specific data
589 1           _store_stream_info( stream_number, asf->info, newSVpv("width", 0), newSVuv( buffer_get_int_le(&type_data_buf) ) );
590 1           _store_stream_info( stream_number, asf->info, newSVpv("height", 0), newSVuv( buffer_get_int_le(&type_data_buf) ) );
591             }
592 0 0         else if ( IsEqualGUID(&stream_type, &ASF_Degradable_JPEG_Media) ) {
593 0           _store_stream_info( stream_number, asf->info, newSVpv("stream_type", 0), newSVpv("ASF_Degradable_JPEG_Media", 0) );
594              
595             // XXX: type-specific data (section 9.4.2)
596             }
597 0 0         else if ( IsEqualGUID(&stream_type, &ASF_File_Transfer_Media) ) {
598 0           _store_stream_info( stream_number, asf->info, newSVpv("stream_type", 0), newSVpv("ASF_File_Transfer_Media", 0) );
599              
600             // XXX: type-specific data (section 9.5)
601             }
602 0 0         else if ( IsEqualGUID(&stream_type, &ASF_Binary_Media) ) {
603 0           _store_stream_info( stream_number, asf->info, newSVpv("stream_type", 0), newSVpv("ASF_Binary_Media", 0) );
604              
605             // XXX: type-specific data (section 9.5)
606             }
607              
608 30 100         if ( IsEqualGUID(&ec_type, &ASF_No_Error_Correction) ) {
609 4           _store_stream_info( stream_number, asf->info, newSVpv("error_correction_type", 0), newSVpv("ASF_No_Error_Correction", 0) );
610             }
611 26 100         else if ( IsEqualGUID(&ec_type, &ASF_Audio_Spread) ) {
612 25           _store_stream_info( stream_number, asf->info, newSVpv("error_correction_type", 0), newSVpv("ASF_Audio_Spread", 0) );
613             }
614              
615 30           _store_stream_info( stream_number, asf->info, newSVpv("time_offset", 0), newSViv(time_offset) );
616 30           _store_stream_info( stream_number, asf->info, newSVpv("encrypted", 0), newSVuv( flags & 0x8000 ? 1 : 0 ) );
617              
618 30           buffer_free(&type_data_buf);
619 30           }
620              
621             int
622 23           _parse_header_extension(asfinfo *asf, uint64_t len)
623             {
624             int ext_size;
625             GUID hdr;
626             uint64_t hdr_size;
627 23           uint32_t tmp_offset = asf->object_offset;
628              
629             // Skip reserved fields
630 23           buffer_consume(asf->buf, 18);
631              
632 23           ext_size = buffer_get_int_le(asf->buf);
633              
634             // Sanity check ext size
635             // Must be 0 or 24+, and 46 less than header extension object size
636 23 100         if (ext_size > 0) {
637 22 50         if (ext_size < 24) {
638 0           return 0;
639             }
640 22 50         if (ext_size != len - 46) {
641 0           return 0;
642             }
643             }
644              
645             DEBUG_TRACE(" size: %d\n", ext_size);
646              
647             // Header Extension is always 46 bytes, and we've already counted 24 of it
648 23           asf->object_offset += 46 - 24;
649              
650 159 100         while (ext_size > 0) {
651 136           buffer_get_guid(asf->buf, &hdr);
652 136           hdr_size = buffer_get_int64_le(asf->buf);
653 136           ext_size -= hdr_size;
654              
655 136           asf->object_offset += 24;
656              
657             DEBUG_TRACE(" object_offset %d\n", asf->object_offset);
658              
659 136 100         if ( IsEqualGUID(&hdr, &ASF_Metadata) ) {
660             DEBUG_TRACE(" Metadata\n");
661 22           _parse_metadata(asf);
662             }
663 114 100         else if ( IsEqualGUID(&hdr, &ASF_Extended_Stream_Properties) ) {
664             DEBUG_TRACE(" Extended_Stream_Properties\n");
665 28           _parse_extended_stream_properties(asf, hdr_size);
666             }
667 86 100         else if ( IsEqualGUID(&hdr, &ASF_Language_List) ) {
668             DEBUG_TRACE(" Language_List\n");
669 13           _parse_language_list(asf);
670             }
671 73 100         else if ( IsEqualGUID(&hdr, &ASF_Advanced_Mutual_Exclusion) ) {
672             DEBUG_TRACE(" Advanced_Mutual_Exclusion\n");
673 4           _parse_advanced_mutual_exclusion(asf);
674             }
675 69 100         else if ( IsEqualGUID(&hdr, &ASF_Metadata_Library) ) {
676             DEBUG_TRACE(" Metadata_Library\n");
677 4           _parse_metadata_library(asf);
678             }
679 65 100         else if ( IsEqualGUID(&hdr, &ASF_Index_Parameters) ) {
680             DEBUG_TRACE(" Index_Parameters\n");
681 13           _parse_index_parameters(asf);
682             }
683 52 100         else if ( IsEqualGUID(&hdr, &ASF_Compatibility) ) {
684             // reserved for future use, just ignore
685             DEBUG_TRACE(" Skipping Compatibility\n");
686 22           buffer_consume(asf->buf, 2);
687             }
688 30 100         else if ( IsEqualGUID(&hdr, &ASF_Padding) ) {
689             // skip padding
690             DEBUG_TRACE(" Skipping Padding\n");
691 22           buffer_consume(asf->buf, hdr_size - 24);
692             }
693 8 50         else if ( IsEqualGUID(&hdr, &ASF_Index_Placeholder) ) {
694             // skip undocumented placeholder
695             DEBUG_TRACE(" Skipping Index_Placeholder\n");
696 8           buffer_consume(asf->buf, hdr_size - 24);
697             }
698             else {
699             // Unhandled
700 0           PerlIO_printf(PerlIO_stderr(), " ** Unhandled extended header: ");
701 0           print_guid(hdr);
702 0           PerlIO_printf(PerlIO_stderr(), "size: %llu\n", hdr_size);
703              
704 0           buffer_consume(asf->buf, hdr_size - 24);
705             }
706              
707 136           asf->object_offset += hdr_size - 24;
708             }
709              
710             // Put back the original offset, or calcs will be wrong in _asf_parse
711 23           asf->object_offset = tmp_offset;
712              
713 23           return 1;
714             }
715              
716             void
717 22           _parse_metadata(asfinfo *asf)
718             {
719 22           uint16_t count = buffer_get_short_le(asf->buf);
720              
721 22           buffer_init_or_clear(asf->scratch, 32);
722              
723 88 100         while ( count-- ) {
724             uint16_t stream_number;
725             uint16_t name_len;
726             uint16_t data_type;
727             uint32_t data_len;
728 66           SV *key = NULL;
729 66           SV *value = NULL;
730              
731             // Skip reserved
732 66           buffer_consume(asf->buf, 2);
733              
734 66           stream_number = buffer_get_short_le(asf->buf);
735 66           name_len = buffer_get_short_le(asf->buf);
736 66           data_type = buffer_get_short_le(asf->buf);
737 66           data_len = buffer_get_int_le(asf->buf);
738              
739 66           buffer_clear(asf->scratch);
740 66           buffer_get_utf16_as_utf8(asf->buf, asf->scratch, name_len, UTF16_BYTEORDER_LE);
741 66           key = newSVpv( buffer_ptr(asf->scratch), 0 );
742 66           sv_utf8_decode(key);
743              
744 66 100         if (data_type == TYPE_UNICODE) {
745 27           buffer_clear(asf->scratch);
746 27           buffer_get_utf16_as_utf8(asf->buf, asf->scratch, data_len, UTF16_BYTEORDER_LE);
747 27           value = newSVpv( buffer_ptr(asf->scratch), 0 );
748 27           sv_utf8_decode(value);
749             }
750 39 50         else if (data_type == TYPE_BYTE) {
751 0           value = newSVpvn( buffer_ptr(asf->buf), data_len );
752 0           buffer_consume(asf->buf, data_len);
753             }
754 39 100         else if (data_type == TYPE_BOOL || data_type == TYPE_WORD) {
    50          
755 27           value = newSViv( buffer_get_short_le(asf->buf) );
756             }
757 12 50         else if (data_type == TYPE_DWORD) {
758 12           value = newSViv( buffer_get_int_le(asf->buf) );
759             }
760 0 0         else if (data_type == TYPE_QWORD) {
761 0           value = newSViv( buffer_get_int64_le(asf->buf) );
762             }
763             else {
764             DEBUG_TRACE("Unknown metadata data type %d\n", data_type);
765 0           buffer_consume(asf->buf, data_len);
766             }
767              
768 66 50         if (value != NULL) {
769             #ifdef AUDIO_SCAN_DEBUG
770             if ( data_type == 0 ) {
771             DEBUG_TRACE(" %s / type %d / stream_number %d / %s\n", SvPVX(key), data_type, stream_number, SvPVX(value));
772             }
773             else if ( data_type > 1 ) {
774             DEBUG_TRACE(" %s / type %d / stream_number %d / %d\n", SvPVX(key), data_type, stream_number, (int)SvIV(value));
775             }
776             else {
777             DEBUG_TRACE(" %s / type %d / stream_number %d / \n", SvPVX(key), stream_number, data_type);
778             }
779             #endif
780              
781             // If stream_number is available, store the data with the stream info
782 66 50         if (stream_number > 0) {
783 66           _store_stream_info( stream_number, asf->info, key, value );
784             }
785             else {
786 0           my_hv_store_ent( asf->info, key, value );
787 0           SvREFCNT_dec(key);
788             }
789             }
790             }
791 22           }
792              
793             void
794 28           _parse_extended_stream_properties(asfinfo *asf, uint64_t len)
795             {
796 28           uint64_t start_time = buffer_get_int64_le(asf->buf);
797 28           uint64_t end_time = buffer_get_int64_le(asf->buf);
798 28           uint32_t bitrate = buffer_get_int_le(asf->buf);
799 28           uint32_t buffer_size = buffer_get_int_le(asf->buf);
800 28           uint32_t buffer_fullness = buffer_get_int_le(asf->buf);
801 28           uint32_t alt_bitrate = buffer_get_int_le(asf->buf);
802 28           uint32_t alt_buffer_size = buffer_get_int_le(asf->buf);
803 28           uint32_t alt_buffer_fullness = buffer_get_int_le(asf->buf);
804 28           uint32_t max_object_size = buffer_get_int_le(asf->buf);
805 28           uint32_t flags = buffer_get_int_le(asf->buf);
806 28           uint16_t stream_number = buffer_get_short_le(asf->buf);
807 28           uint16_t lang_id = buffer_get_short_le(asf->buf);
808 28           uint64_t avg_time_per_frame = buffer_get_int64_le(asf->buf);
809 28           uint16_t stream_name_count = buffer_get_short_le(asf->buf);
810 28           uint16_t payload_ext_count = buffer_get_short_le(asf->buf);
811              
812 28           len -= 88;
813              
814 28 50         if (start_time > 0) {
815 0           _store_stream_info( stream_number, asf->info, newSVpv("start_time", 0), newSViv(start_time) );
816             }
817              
818 28 50         if (end_time > 0) {
819 0           _store_stream_info( stream_number, asf->info, newSVpv("end_time", 0), newSViv(end_time) );
820             }
821              
822 28           _store_stream_info( stream_number, asf->info, newSVpv("bitrate", 0), newSViv(bitrate) );
823 28           _store_stream_info( stream_number, asf->info, newSVpv("buffer_size", 0), newSViv(buffer_size) );
824 28           _store_stream_info( stream_number, asf->info, newSVpv("buffer_fullness", 0), newSViv(buffer_fullness) );
825 28           _store_stream_info( stream_number, asf->info, newSVpv("alt_bitrate", 0), newSViv(alt_bitrate) );
826 28           _store_stream_info( stream_number, asf->info, newSVpv("alt_buffer_size", 0), newSViv(alt_buffer_size) );
827 28           _store_stream_info( stream_number, asf->info, newSVpv("alt_buffer_fullness", 0), newSViv(alt_buffer_fullness) );
828 28           _store_stream_info( stream_number, asf->info, newSVpv("alt_buffer_size", 0), newSViv(alt_buffer_size) );
829 28           _store_stream_info( stream_number, asf->info, newSVpv("max_object_size", 0), newSViv(max_object_size) );
830              
831 28 50         if ( flags & 0x01 )
832 0           _store_stream_info( stream_number, asf->info, newSVpv("flag_reliable", 0), newSViv(1) );
833              
834 28 100         if ( flags & 0x02 )
835 26           _store_stream_info( stream_number, asf->info, newSVpv("flag_seekable", 0), newSViv(1) );
836              
837 28 50         if ( flags & 0x04 )
838 0           _store_stream_info( stream_number, asf->info, newSVpv("flag_no_cleanpoint", 0), newSViv(1) );
839              
840 28 50         if ( flags & 0x08 )
841 0           _store_stream_info( stream_number, asf->info, newSVpv("flag_resend_cleanpoints", 0), newSViv(1) );
842              
843 28           _store_stream_info( stream_number, asf->info, newSVpv("language_index", 0), newSViv(lang_id) );
844              
845             if (avg_time_per_frame > 0) {
846             // XXX: can't get this to divide properly (?!)
847             //_store_stream_info( stream_number, asf->info, newSVpv("avg_time_per_frame", 0), newSVuv(avg_time_per_frame / 10000) );
848             }
849              
850 28 50         while ( stream_name_count-- ) {
851             uint16_t stream_name_len;
852              
853             // stream_name_lang_id
854 0           buffer_consume(asf->buf, 2);
855 0           stream_name_len = buffer_get_short_le(asf->buf);
856              
857             DEBUG_TRACE("stream_name_len: %d\n", stream_name_len);
858              
859             // XXX, store this?
860 0           buffer_consume(asf->buf, stream_name_len);
861              
862 0           len -= 4 + stream_name_len;
863             }
864              
865 29 100         while ( payload_ext_count-- ) {
866             // Skip
867             uint32_t payload_len;
868              
869 1           buffer_consume(asf->buf, 18);
870 1           payload_len = buffer_get_int_le(asf->buf);
871 1           buffer_consume(asf->buf, payload_len);
872              
873 1           len -= 22 + payload_len;
874             }
875              
876 28 100         if (len) {
877             // Anything left over means we have an embedded Stream Properties Object
878             DEBUG_TRACE(" embedded Stream_Properties, size %llu\n", len);
879 4           buffer_consume(asf->buf, 24);
880 4           _parse_stream_properties(asf);
881             }
882 28           }
883              
884             void
885 13           _parse_language_list(asfinfo *asf)
886             {
887 13           AV *list = newAV();
888 13           uint16_t count = buffer_get_short_le(asf->buf);
889              
890 13           buffer_init_or_clear(asf->scratch, 32);
891              
892 28 100         while ( count-- ) {
893             SV *value;
894              
895 15           uint8_t len = buffer_get_char(asf->buf);
896 15           buffer_clear(asf->scratch);
897 15           buffer_get_utf16_as_utf8(asf->buf, asf->scratch, len, UTF16_BYTEORDER_LE);
898 15           value = newSVpv( buffer_ptr(asf->scratch), 0 );
899 15           sv_utf8_decode(value);
900              
901 15           av_push( list, value );
902             }
903              
904 13           my_hv_store( asf->info, "language_list", newRV_noinc( (SV*)list ) );
905 13           }
906              
907             void
908 4           _parse_advanced_mutual_exclusion(asfinfo *asf)
909             {
910             GUID mutex_type;
911             uint16_t count;
912             AV *mutex_list;
913 4           HV *mutex_hv = newHV();
914             SV *mutex_type_sv;
915 4           AV *mutex_streams = newAV();
916              
917 4           buffer_get_guid(asf->buf, &mutex_type);
918 4           count = buffer_get_short_le(asf->buf);
919              
920 4 50         if ( IsEqualGUID(&mutex_type, &ASF_Mutex_Language) ) {
921 0           mutex_type_sv = newSVpv( "ASF_Mutex_Language", 0 );
922             }
923 4 50         else if ( IsEqualGUID(&mutex_type, &ASF_Mutex_Bitrate) ) {
924 4           mutex_type_sv = newSVpv( "ASF_Mutex_Bitrate", 0 );
925             }
926             else {
927 0           mutex_type_sv = newSVpv( "ASF_Mutex_Unknown", 0 );
928             }
929              
930 12 100         while ( count-- ) {
931 8           av_push( mutex_streams, newSViv( buffer_get_short_le(asf->buf) ) );
932             }
933              
934 4           my_hv_store_ent( mutex_hv, mutex_type_sv, newRV_noinc( (SV *)mutex_streams ) );
935 4           SvREFCNT_dec(mutex_type_sv);
936              
937 4 50         if ( !my_hv_exists( asf->info, "mutex_list" ) ) {
938 4           mutex_list = newAV();
939 4           av_push( mutex_list, newRV_noinc( (SV *)mutex_hv ) );
940 4           my_hv_store( asf->info, "mutex_list", newRV_noinc( (SV *)mutex_list ) );
941             }
942             else {
943 0           SV **entry = my_hv_fetch( asf->info, "mutex_list" );
944 0 0         if (entry != NULL) {
945 0           mutex_list = (AV *)SvRV(*entry);
946             }
947             else {
948 0           return;
949             }
950              
951 4           av_push( mutex_list, newRV_noinc( (SV *)mutex_hv ) );
952             }
953             }
954              
955             void
956 23           _parse_codec_list(asfinfo *asf)
957             {
958             uint32_t count;
959 23           AV *list = newAV();
960              
961 23           buffer_init_or_clear(asf->scratch, 32);
962              
963             // Skip reserved
964 23           buffer_consume(asf->buf, 16);
965              
966 23           count = buffer_get_int_le(asf->buf);
967              
968 47 100         while ( count-- ) {
969 24           HV *codec_info = newHV();
970             uint16_t name_len;
971             uint16_t desc_len;
972 24           SV *name = NULL;
973 24           SV *desc = NULL;
974              
975 24           uint16_t codec_type = buffer_get_short_le(asf->buf);
976              
977 24           switch (codec_type) {
978             case 0x0001:
979 2           my_hv_store( codec_info, "type", newSVpv("Video", 0) );
980 2           break;
981             case 0x0002:
982 22           my_hv_store( codec_info, "type", newSVpv("Audio", 0) );
983 22           break;
984             default:
985 0           my_hv_store( codec_info, "type", newSVpv("Unknown", 0) );
986             }
987              
988             // Unlike other objects, these lengths are the
989             // "number of Unicode chars", not bytes, so we need to double it
990 24           name_len = buffer_get_short_le(asf->buf) * 2;
991 24           buffer_clear(asf->scratch);
992 24           buffer_get_utf16_as_utf8(asf->buf, asf->scratch, name_len, UTF16_BYTEORDER_LE);
993 24           name = newSVpv( buffer_ptr(asf->scratch), 0 );
994 24           sv_utf8_decode(name);
995 24           my_hv_store( codec_info, "name", name );
996              
997             // Set a 'lossless' flag in info if Lossless codec is used
998 24 100         if ( strstr( buffer_ptr(asf->scratch), "Lossless" ) ) {
999 2           my_hv_store( asf->info, "lossless", newSVuv(1) );
1000             }
1001              
1002 24           desc_len = buffer_get_short_le(asf->buf) * 2;
1003 24           buffer_clear(asf->scratch);
1004 24           buffer_get_utf16_as_utf8(asf->buf, asf->scratch, desc_len, UTF16_BYTEORDER_LE);
1005 24           desc = newSVpv( buffer_ptr(asf->scratch), 0 );
1006 24           sv_utf8_decode(desc);
1007 24           my_hv_store( codec_info, "description", desc );
1008              
1009             // Skip info
1010 24           buffer_consume(asf->buf, buffer_get_short_le(asf->buf));
1011              
1012 24           av_push( list, newRV_noinc( (SV *)codec_info ) );
1013             }
1014              
1015 23           my_hv_store( asf->info, "codec_list", newRV_noinc( (SV *)list ) );
1016 23           }
1017              
1018             void
1019 22           _parse_stream_bitrate_properties(asfinfo *asf)
1020             {
1021 22           uint16_t count = buffer_get_short_le(asf->buf);
1022              
1023 50 100         while ( count-- ) {
1024 28           uint16_t stream_number = buffer_get_short_le(asf->buf) & 0x007f;
1025              
1026 28           _store_stream_info( stream_number, asf->info, newSVpv("avg_bitrate", 0), newSViv( buffer_get_int_le(asf->buf) ) );
1027             }
1028 22           }
1029              
1030             void
1031 4           _parse_metadata_library(asfinfo *asf)
1032             {
1033 4           uint16_t count = buffer_get_short_le(asf->buf);
1034 4           uint32_t picture_offset = 0;
1035              
1036 4           buffer_init_or_clear(asf->scratch, 32);
1037              
1038 23 100         while ( count-- ) {
1039 19           SV *key = NULL;
1040 19           SV *value = NULL;
1041             uint16_t stream_number, name_len, data_type;
1042             uint32_t data_len;
1043              
1044             #ifdef AUDIO_SCAN_DEBUG
1045             uint16_t lang_index = buffer_get_short_le(asf->buf);
1046             #else
1047 19           buffer_consume(asf->buf, 2);
1048             #endif
1049              
1050 19           stream_number = buffer_get_short_le(asf->buf);
1051 19           name_len = buffer_get_short_le(asf->buf);
1052 19           data_type = buffer_get_short_le(asf->buf);
1053 19           data_len = buffer_get_int_le(asf->buf);
1054              
1055 19           buffer_clear(asf->scratch);
1056 19           buffer_get_utf16_as_utf8(asf->buf, asf->scratch, name_len, UTF16_BYTEORDER_LE);
1057 19           key = newSVpv( buffer_ptr(asf->scratch), 0 );
1058 19           sv_utf8_decode(key);
1059              
1060 19           picture_offset += 12 + name_len;
1061              
1062 19 100         if (data_type == TYPE_UNICODE) {
1063 12           buffer_clear(asf->scratch);
1064 12           buffer_get_utf16_as_utf8(asf->buf, asf->scratch, data_len, UTF16_BYTEORDER_LE);
1065 12           value = newSVpv( buffer_ptr(asf->scratch), 0 );
1066 12           sv_utf8_decode(value);
1067             }
1068 7 100         else if (data_type == TYPE_BYTE) {
1069             // handle picture data
1070 2 50         if ( !strcmp( SvPVX(key), "WM/Picture" ) ) {
1071 2           value = _parse_picture(asf, picture_offset);
1072             }
1073             else {
1074 0           value = newSVpvn( buffer_ptr(asf->buf), data_len );
1075 2           buffer_consume(asf->buf, data_len);
1076             }
1077             }
1078 5 50         else if (data_type == TYPE_BOOL || data_type == TYPE_WORD) {
    50          
1079 0           value = newSViv( buffer_get_short_le(asf->buf) );
1080             }
1081 5 50         else if (data_type == TYPE_DWORD) {
1082 0           value = newSViv( buffer_get_int_le(asf->buf) );
1083             }
1084 5 50         else if (data_type == TYPE_QWORD) {
1085 0           value = newSViv( buffer_get_int64_le(asf->buf) );
1086             }
1087 5 50         else if (data_type == TYPE_GUID) {
1088             GUID g;
1089 5           buffer_get_guid(asf->buf, &g);
1090 5           value = newSVpvf(
1091             "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
1092 10           g.Data1, g.Data2, g.Data3,
1093 20           g.Data4[0], g.Data4[1], g.Data4[2], g.Data4[3],
1094 20           g.Data4[4], g.Data4[5], g.Data4[6], g.Data4[7]
1095             );
1096             }
1097             else {
1098 0           PerlIO_printf(PerlIO_stderr(), "Unknown metadata library data type %d\n", data_type);
1099 0           buffer_consume(asf->buf, data_len);
1100             }
1101              
1102 19           picture_offset += data_len;
1103              
1104 19 50         if (value != NULL) {
1105             #ifdef AUDIO_SCAN_DEBUG
1106             if ( data_type == 0 || data_type == 6 ) {
1107             DEBUG_TRACE(" %s / type %d / lang_index %d / stream_number %d / %s\n", SvPVX(key), data_type, lang_index, stream_number, SvPVX(value));
1108             }
1109             else if ( data_type > 1 ) {
1110             DEBUG_TRACE(" %s / type %d / lang_index %d / stream_number %d / %d\n", SvPVX(key), data_type, lang_index, stream_number, (int)SvIV(value));
1111             }
1112             else {
1113             DEBUG_TRACE(" %s / type %d / lang_index %d / stream_number %d / \n", SvPVX(key), lang_index, stream_number, data_type);
1114             }
1115             #endif
1116              
1117             // If stream_number is available, store the data with the stream info
1118             // XXX: should store lang_index?
1119 19 50         if (stream_number > 0) {
1120 0           _store_stream_info( stream_number, asf->info, key, value );
1121             }
1122             else {
1123 19           _store_tag( asf->tags, key, value );
1124             }
1125             }
1126             }
1127 4           }
1128              
1129             void
1130 13           _parse_index_parameters(asfinfo *asf)
1131             {
1132             uint16_t count;
1133              
1134 13           my_hv_store( asf->info, "index_entry_interval", newSViv( buffer_get_int_le(asf->buf) ) );
1135              
1136 13           count = buffer_get_short_le(asf->buf);
1137              
1138 30 100         while ( count-- ) {
1139 17           uint16_t stream_number = buffer_get_short_le(asf->buf);
1140 17           uint16_t index_type = buffer_get_short_le(asf->buf);
1141              
1142 17           switch (index_type) {
1143             case 0x0001:
1144 0           _store_stream_info( stream_number, asf->info, newSVpv("index_type", 0), newSVpv("Nearest Past Data Packet", 0) );
1145 0           break;
1146             case 0x0002:
1147 0           _store_stream_info( stream_number, asf->info, newSVpv("index_type", 0), newSVpv("Nearest Past Media Object", 0) );
1148 0           break;
1149             case 0x0003:
1150 17           _store_stream_info( stream_number, asf->info, newSVpv("index_type", 0), newSVpv("Nearest Past Cleanpoint", 0) );
1151 17           break;
1152             default:
1153 0           _store_stream_info( stream_number, asf->info, newSVpv("index_type", 0), newSViv(index_type) );
1154             }
1155             }
1156 13           }
1157              
1158             void
1159 749           _store_stream_info(int stream_number, HV *info, SV *key, SV *value )
1160             {
1161             AV *streams;
1162             HV *streaminfo;
1163 749           uint8_t found = 0;
1164 749           int i = 0;
1165              
1166 749 100         if ( !my_hv_exists( info, "streams" ) ) {
1167             // Create
1168 23           streams = newAV();
1169 23           my_hv_store( info, "streams", newRV_noinc( (SV*)streams ) );
1170             }
1171             else {
1172 726           SV **entry = my_hv_fetch( info, "streams" );
1173 726 50         if (entry != NULL) {
1174 726           streams = (AV *)SvRV(*entry);
1175             }
1176             else {
1177 0           return;
1178             }
1179             }
1180              
1181 749 50         if (streams != NULL) {
1182             // Find entry for this stream number
1183 901 100         for (i = 0; av_len(streams) >= 0 && i <= av_len(streams); i++) {
    100          
1184 871           SV **stream = av_fetch(streams, i, 0);
1185 871 50         if (stream != NULL) {
1186             SV **sn;
1187              
1188 871           streaminfo = (HV *)SvRV(*stream);
1189 871           sn = my_hv_fetch( streaminfo, "stream_number" );
1190 871 50         if (sn != NULL) {
1191 871 50         if ( SvIV(*sn) == stream_number ) {
    100          
1192             // XXX: if item exists, create array
1193 719           my_hv_store_ent( streaminfo, key, value );
1194 719           SvREFCNT_dec(key);
1195              
1196 719           found = 1;
1197 719           break;
1198             }
1199             }
1200             }
1201             }
1202              
1203 749 100         if ( !found ) {
1204             // New stream number
1205 30           streaminfo = newHV();
1206              
1207 30           my_hv_store( streaminfo, "stream_number", newSViv(stream_number) );
1208 30           my_hv_store_ent( streaminfo, key, value );
1209 30           SvREFCNT_dec(key);
1210              
1211 30           av_push( streams, newRV_noinc( (SV *)streaminfo ) );
1212             }
1213             }
1214             }
1215              
1216             void
1217 272           _store_tag(HV *tags, SV *key, SV *value)
1218             {
1219             // if key exists, create array
1220 272 100         if ( my_hv_exists_ent( tags, key ) ) {
1221 10           SV **entry = my_hv_fetch( tags, SvPVX(key) );
1222 10 50         if (entry != NULL) {
1223 10 100         if ( SvROK(*entry) && SvTYPE(SvRV(*entry)) == SVt_PVAV ) {
    100          
1224 1           av_push( (AV *)SvRV(*entry), value );
1225             }
1226             else {
1227             // A non-array entry, convert to array.
1228 9           AV *ref = newAV();
1229 9           av_push( ref, newSVsv(*entry) );
1230 9           av_push( ref, value );
1231 10           my_hv_store_ent( tags, key, newRV_noinc( (SV*)ref ) );
1232             }
1233             }
1234             }
1235             else {
1236 262           my_hv_store_ent( tags, key, value );
1237             }
1238              
1239 272           SvREFCNT_dec(key);
1240 272           }
1241              
1242             int
1243 7           _parse_index_objects(asfinfo *asf, int index_size)
1244             {
1245             GUID tmp;
1246             uint64_t size;
1247              
1248 21 100         while (index_size > 0) {
1249             // Make sure we have enough data
1250 14 50         if ( !_check_buf(asf->infile, asf->buf, 24, ASF_BLOCK_SIZE) ) {
1251 0           return 0;
1252             }
1253              
1254 14           buffer_get_guid(asf->buf, &tmp);
1255 14           size = buffer_get_int64_le(asf->buf);
1256              
1257 14 50         if ( !_check_buf(asf->infile, asf->buf, size - 24, ASF_BLOCK_SIZE) ) {
1258 0           return 0;
1259             }
1260              
1261 14 100         if ( IsEqualGUID(&tmp, &ASF_Index) ) {
1262             DEBUG_TRACE("Index size %llu\n", size);
1263 7           _parse_index(asf, size - 24);
1264             }
1265 7 50         else if ( IsEqualGUID(&tmp, &ASF_Simple_Index) ) {
1266             DEBUG_TRACE("Skipping Simple_Index size %llu\n", size);
1267             // Simple Index is used for video files only
1268 7           buffer_consume(asf->buf, size - 24);
1269             }
1270             else {
1271             // Unhandled GUID
1272 0           PerlIO_printf(PerlIO_stderr(), "** Unhandled Index GUID: ");
1273 0           print_guid(tmp);
1274 0           PerlIO_printf(PerlIO_stderr(), "size: %llu\n", size);
1275              
1276 0           buffer_consume(asf->buf, size - 24);
1277             }
1278              
1279 14           index_size -= size;
1280             }
1281              
1282 7           return 1;
1283             }
1284              
1285             void
1286 7           _parse_index(asfinfo *asf, uint64_t size)
1287             {
1288             uint32_t time_interval;
1289             uint16_t spec_count;
1290             uint32_t block_count;
1291             uint32_t entry_count;
1292             int i, ec;
1293              
1294 7           time_interval = buffer_get_int_le(asf->buf);
1295 7           spec_count = buffer_get_short_le(asf->buf);
1296 7           block_count = buffer_get_int_le(asf->buf);
1297              
1298             // XXX ignore block_count > 1 for now, for files larger than 2^32
1299 7 50         if (block_count > 1) {
1300 0           buffer_consume(asf->buf, size);
1301 0           return;
1302             }
1303              
1304             DEBUG_TRACE(" time_interval %d, spec_count %d\n", time_interval, spec_count);
1305              
1306 7           asf->spec_count = spec_count;
1307              
1308 7 50         New(0, asf->specs, spec_count * sizeof(*asf->specs), struct asf_index_specs);
1309              
1310             DEBUG_TRACE(" Index Specifiers:\n");
1311 17 100         for (i = 0; i < spec_count; i++) {
1312 10           asf->specs[i].stream_number = buffer_get_short_le(asf->buf);
1313 10           asf->specs[i].index_type = buffer_get_short_le(asf->buf);
1314 10           asf->specs[i].time_interval = time_interval;
1315             DEBUG_TRACE(" stream_number %d, index_type %d\n", asf->specs[i].stream_number, asf->specs[i].index_type);
1316             }
1317              
1318 7           entry_count = buffer_get_int_le(asf->buf);
1319              
1320             DEBUG_TRACE(" entry_count %d\n", entry_count);
1321              
1322 17 100         for (i = 0; i < spec_count; i++) {
1323 10           asf->specs[i].block_pos = buffer_get_int64_le(asf->buf);
1324 10           asf->specs[i].entry_count = entry_count;
1325             DEBUG_TRACE(" specs[%d].block_pos %llu\n", i, asf->specs[i].block_pos);
1326              
1327             // allocate space for this spec's offsets
1328 10 50         New(0, asf->specs[i].offsets, entry_count * sizeof(uint32_t), uint32_t);
1329             }
1330              
1331 46 100         for (ec = 0; ec < entry_count; ec++) {
1332 93 100         for (i = 0; i < spec_count; i++) {
1333             // These are byte offsets relative to start of the first data packet,
1334             // so we add audio_offset here. An additional 50 bytes are already added
1335             // to skip past the top-level Data Object
1336 54           asf->specs[i].offsets[ec] = asf->audio_offset + buffer_get_int_le(asf->buf);
1337             DEBUG_TRACE(" entry %d spec %d offset: %d\n", ec, i, asf->specs[i].offsets[ec]);
1338             }
1339             }
1340             }
1341              
1342             void
1343 1           _parse_content_encryption(asfinfo *asf)
1344             {
1345             uint32_t protection_type_len;
1346             uint32_t key_len;
1347             uint32_t license_url_len;
1348              
1349             // Skip secret data
1350 1           buffer_consume(asf->buf, buffer_get_int_le(asf->buf));
1351              
1352 1           protection_type_len = buffer_get_int_le(asf->buf);
1353 1           my_hv_store( asf->info, "drm_protection_type", newSVpvn( buffer_ptr(asf->buf), protection_type_len - 1 ) );
1354 1           buffer_consume(asf->buf, protection_type_len);
1355              
1356 1           key_len = buffer_get_int_le(asf->buf);
1357 1           my_hv_store( asf->info, "drm_key", newSVpvn( buffer_ptr(asf->buf), key_len - 1 ) );
1358 1           buffer_consume(asf->buf, key_len);
1359              
1360 1           license_url_len = buffer_get_int_le(asf->buf);
1361 1           my_hv_store( asf->info, "drm_license_url", newSVpvn( buffer_ptr(asf->buf), license_url_len - 1 ) );
1362 1           buffer_consume(asf->buf, license_url_len);
1363 1           }
1364              
1365             void
1366 1           _parse_extended_content_encryption(asfinfo *asf)
1367             {
1368 1           uint32_t len = buffer_get_int_le(asf->buf);
1369             SV *value;
1370 1           unsigned char *tmp_ptr = buffer_ptr(asf->buf);
1371              
1372 1 50         if ( tmp_ptr[0] == 0xFF && tmp_ptr[1] == 0xFE ) {
    50          
1373 1           buffer_consume(asf->buf, 2);
1374 1           buffer_init_or_clear(asf->scratch, len - 2);
1375 1           buffer_get_utf16_as_utf8(asf->buf, asf->scratch, len - 2, UTF16_BYTEORDER_LE);
1376 1           value = newSVpv( buffer_ptr(asf->scratch), 0 );
1377 1           sv_utf8_decode(value);
1378              
1379 1           my_hv_store( asf->info, "drm_data", value );
1380             }
1381             else {
1382 0           buffer_consume(asf->buf, len);
1383             }
1384 1           }
1385              
1386             void
1387 2           _parse_script_command(asfinfo *asf)
1388             {
1389             uint16_t command_count;
1390             uint16_t type_count;
1391 2           AV *types = newAV();
1392 2           AV *commands = newAV();
1393              
1394 2           buffer_init_or_clear(asf->scratch, 32);
1395              
1396             // Skip reserved
1397 2           buffer_consume(asf->buf, 16);
1398              
1399 2           command_count = buffer_get_short_le(asf->buf);
1400 2           type_count = buffer_get_short_le(asf->buf);
1401              
1402 5 100         while ( type_count-- ) {
1403             SV *value;
1404 3           uint16_t len = buffer_get_short_le(asf->buf);
1405              
1406 3           buffer_clear(asf->scratch);
1407 3           buffer_get_utf16_as_utf8(asf->buf, asf->scratch, len * 2, UTF16_BYTEORDER_LE);
1408 3           value = newSVpv( buffer_ptr(asf->scratch), 0 );
1409 3           sv_utf8_decode(value);
1410              
1411 3           av_push( types, value );
1412             }
1413              
1414 5 100         while ( command_count-- ) {
1415 3           HV *command = newHV();
1416             SV *value;
1417              
1418 3           uint32_t pres_time = buffer_get_int_le(asf->buf);
1419 3           uint16_t type_index = buffer_get_short_le(asf->buf);
1420 3           uint16_t name_len = buffer_get_short_le(asf->buf);
1421              
1422 3 100         if (name_len) {
1423 2           buffer_clear(asf->scratch);
1424 2           buffer_get_utf16_as_utf8(asf->buf, asf->scratch, name_len * 2, UTF16_BYTEORDER_LE);
1425 2           value = newSVpv( buffer_ptr(asf->scratch), 0 );
1426 2           sv_utf8_decode(value);
1427 2           my_hv_store( command, "command", value );
1428             }
1429              
1430 3           my_hv_store( command, "time", newSVuv(pres_time) );
1431 3           my_hv_store( command, "type", newSVuv(type_index) );
1432              
1433 3           av_push( commands, newRV_noinc( (SV *)command ) );
1434             }
1435              
1436 2           my_hv_store( asf->info, "script_types", newRV_noinc( (SV *)types ) );
1437 2           my_hv_store( asf->info, "script_commands", newRV_noinc( (SV *)commands ) );
1438 2           }
1439              
1440             SV *
1441 9           _parse_picture(asfinfo *asf, uint32_t picture_offset)
1442             {
1443             char *tmp_ptr;
1444 9           uint16_t mime_len = 2; // to handle double-null
1445 9           uint16_t desc_len = 2;
1446             uint32_t image_len;
1447             SV *mime;
1448             SV *desc;
1449 9           HV *picture = newHV();
1450              
1451 9           buffer_init_or_clear(asf->scratch, 32);
1452              
1453 9           my_hv_store( picture, "image_type", newSVuv( buffer_get_char(asf->buf) ) );
1454              
1455 9           image_len = buffer_get_int_le(asf->buf);
1456              
1457             // MIME type is a double-null-terminated UTF-16 string
1458 9           tmp_ptr = buffer_ptr(asf->buf);
1459 99 100         while ( tmp_ptr[0] != '\0' || tmp_ptr[1] != '\0' ) {
    50          
1460 90           mime_len += 2;
1461 90           tmp_ptr += 2;
1462             }
1463              
1464 9           buffer_get_utf16_as_utf8(asf->buf, asf->scratch, mime_len, UTF16_BYTEORDER_LE);
1465 9           mime = newSVpv( buffer_ptr(asf->scratch), 0 );
1466 9           sv_utf8_decode(mime);
1467 9           my_hv_store( picture, "mime_type", mime );
1468              
1469             // Description is a double-null-terminated UTF-16 string
1470 9           tmp_ptr = buffer_ptr(asf->buf);
1471 33 100         while ( tmp_ptr[0] != '\0' || tmp_ptr[1] != '\0' ) {
    50          
1472 24           desc_len += 2;
1473 24           tmp_ptr += 2;
1474             }
1475              
1476 9           buffer_clear(asf->scratch);
1477 9           buffer_get_utf16_as_utf8(asf->buf, asf->scratch, desc_len, UTF16_BYTEORDER_LE);
1478 9           desc = newSVpv( buffer_ptr(asf->scratch), 0 );
1479 9           sv_utf8_decode(desc);
1480 9           my_hv_store( picture, "description", desc );
1481              
1482 9 100         if ( _env_true("AUDIO_SCAN_NO_ARTWORK") ) {
1483 2           my_hv_store( picture, "image", newSVuv(image_len) );
1484 2           picture_offset += 5 + mime_len + desc_len + 2;
1485 2           my_hv_store( picture, "offset", newSVuv(asf->object_offset + picture_offset) );
1486             }
1487             else {
1488 7           my_hv_store( picture, "image", newSVpvn( buffer_ptr(asf->buf), image_len ) );
1489             }
1490              
1491 9           buffer_consume(asf->buf, image_len);
1492              
1493 9           return newRV_noinc( (SV *)picture );
1494             }
1495              
1496             // offset is in ms
1497             // Based on some code from Rockbox
1498             int
1499 8           asf_find_frame(PerlIO *infile, char *file, int time_offset)
1500             {
1501 8           int frame_offset = -1;
1502             uint32_t song_length_ms;
1503 8           int32_t offset_index = 0;
1504             uint32_t min_packet_size, max_packet_size;
1505 8           uint8_t found = 0;
1506              
1507             // We need to read all info first to get some data we need to calculate
1508 8           HV *info = newHV();
1509 8           HV *tags = newHV();
1510 8           asfinfo *asf = _asf_parse(infile, file, info, tags, 1);
1511              
1512             // We'll need to reuse the scratch buffer
1513 8           Newz(0, asf->scratch, sizeof(Buffer), Buffer);
1514              
1515             // No seeking without at least 1 stream
1516 8 50         if ( !my_hv_exists(info, "streams") ) {
1517             DEBUG_TRACE("No streams found in file, not seeking\n");
1518 0           goto out;
1519             }
1520              
1521 8 50         min_packet_size = SvIV( *(my_hv_fetch(info, "min_packet_size")) );
1522 8 50         max_packet_size = SvIV( *(my_hv_fetch(info, "max_packet_size")) );
1523              
1524             // No seeking if min != max, according to the ASF spec these must be the same
1525             // and without this value we can't find the data packets properly
1526 8 50         if (min_packet_size != max_packet_size) {
1527             DEBUG_TRACE("min_packet_size != max_packet_size, cannot seek\n");
1528 0           goto out;
1529             }
1530              
1531 8 50         song_length_ms = SvIV( *(my_hv_fetch( info, "song_length_ms" )) );
1532              
1533 8 100         if (time_offset > song_length_ms)
1534 2           time_offset = song_length_ms;
1535              
1536             // Use ASF_Index if available
1537 8 100         if ( asf->spec_count ) {
1538             // Use the index to find the nearest offset
1539 7           offset_index = time_offset / asf->specs[0].time_interval;
1540              
1541 7 50         if (offset_index >= asf->specs[0].entry_count)
1542 0           offset_index = asf->specs[0].entry_count - 1;
1543              
1544             // An offset may be -1 so look backwards if we find one of those
1545 14 100         while (frame_offset == -1 && offset_index >= 0) {
    50          
1546 7           frame_offset = asf->specs[0].offsets[offset_index];
1547              
1548             // XXX should add asf->specs[0].block_pos here, but since we
1549             // aren't supporting 64-bit it should always be 0
1550              
1551             DEBUG_TRACE(
1552             "offset_index for %d / %d: %d = %d\n",
1553             time_offset, asf->specs[0].time_interval, offset_index, frame_offset
1554             );
1555              
1556 7           offset_index--;
1557             }
1558             }
1559              
1560             // Calculate seek position using bitrate
1561 1 50         else if (asf->max_bitrate) {
1562 1           float bytes_per_ms = asf->max_bitrate / 8000.0;
1563 1           int packet = (int)((bytes_per_ms * time_offset) / max_packet_size);
1564              
1565 1           frame_offset = asf->audio_offset + (packet * max_packet_size);
1566              
1567             DEBUG_TRACE("seeking to data packet %d @ %d, via max_bitrate (bytes_per_ms %.2f, time_offset %d, packet size %d)\n",
1568             packet, frame_offset, bytes_per_ms, time_offset, max_packet_size);
1569             }
1570             else {
1571             // No ASF_Index, no max_bitrate, probably an invalid file
1572 0           goto out;
1573             }
1574              
1575             // Double-check above frame, make sure we have the right one
1576             // with a timestamp within our desired range
1577 22 100         while ( !found && frame_offset >= 0 ) {
    50          
1578             int time, duration;
1579              
1580             DEBUG_TRACE("Checking for frame with timestamp %d at %d\n", time_offset, frame_offset);
1581              
1582 14 50         if ( frame_offset > asf->file_size - 64 ) {
1583             DEBUG_TRACE(" Offset too large: %d\n", frame_offset);
1584 0           break;
1585             }
1586              
1587 14           time = _timestamp(asf, frame_offset, &duration);
1588              
1589             DEBUG_TRACE(" Timestamp for frame at %d: %d, duration: %d\n", frame_offset, time, duration);
1590              
1591 14 50         if (time < 0) {
1592             DEBUG_TRACE(" Invalid timestamp, giving up\n");
1593 0           break;
1594             }
1595              
1596 14 100         if ( time + duration >= time_offset && time <= time_offset ) {
    100          
1597             DEBUG_TRACE(" Found frame at offset %d\n", frame_offset);
1598 8           found = 1;
1599             }
1600             else {
1601 6           int delta = time_offset - time;
1602              
1603             DEBUG_TRACE(" Wrong frame, delta: %d\n", delta);
1604              
1605 6 100         if (
1606 1 50         (delta < 0 && (frame_offset - max_packet_size) < asf->audio_offset)
1607 6 100         ||
1608 5 50         (delta > 0 && (frame_offset + max_packet_size) > (asf->audio_offset + asf->audio_size - 64))
1609             ) {
1610             // Reached the first/last audio packet, break out
1611             DEBUG_TRACE(" Giving up, reached the beginning or end of audio\n");
1612             break;
1613             }
1614              
1615             // XXX probably could be more efficient using a binary search,
1616             // but with the use of an index we should already be very close to the right place
1617 6 100         if (delta > 0) {
1618 5           frame_offset += max_packet_size;
1619             }
1620             else {
1621 14           frame_offset -= max_packet_size;
1622             }
1623             }
1624             }
1625              
1626             out:
1627             // Don't leak
1628 8           SvREFCNT_dec(info);
1629 8           SvREFCNT_dec(tags);
1630              
1631 8 100         if (asf->spec_count) {
1632             int i;
1633 17 100         for (i = 0; i < asf->spec_count; i++) {
1634             DEBUG_TRACE("Freeing specs[%d] offsets\n", i);
1635 10           Safefree(asf->specs[i].offsets);
1636             }
1637              
1638             DEBUG_TRACE("Freeing specs\n");
1639 7           Safefree(asf->specs);
1640             }
1641              
1642 8 50         if (asf->scratch->alloc)
1643 8           buffer_free(asf->scratch);
1644 8           Safefree(asf->scratch);
1645              
1646 8           Safefree(asf);
1647              
1648 8           return frame_offset;
1649             }
1650              
1651             // Return the timestamp of the data packet at offset
1652             int
1653 14           _timestamp(asfinfo *asf, int offset, int *duration)
1654             {
1655 14           int timestamp = -1;
1656             uint8_t tmp;
1657              
1658 14 50         if ((PerlIO_seek(asf->infile, offset, SEEK_SET)) != 0) {
1659 0           return -1;
1660             }
1661              
1662 14           buffer_init_or_clear(asf->scratch, 64);
1663              
1664 14 50         if ( !_check_buf(asf->infile, asf->scratch, 64, 64) ) {
1665 0           goto out;
1666             }
1667              
1668             // Read Error Correction Flags
1669 14           tmp = buffer_get_char(asf->scratch);
1670              
1671 14 50         if (tmp & 0x80) {
1672             // Skip error correction data
1673 14           buffer_consume(asf->scratch, tmp & 0x0f);
1674              
1675             // Read Length Type Flags
1676 14           tmp = buffer_get_char(asf->scratch);
1677             }
1678             else {
1679             // The byte we already read is Length Type Flags
1680             }
1681              
1682             // Skip Property Flags, Packet Length, Sequence, Padding Length
1683 14           buffer_consume( asf->scratch,
1684 14 50         1 + GETLEN2b((tmp >> 1) & 0x03) +
1685 14 50         GETLEN2b((tmp >> 3) & 0x03) +
1686 14 50         GETLEN2b((tmp >> 5) & 0x03)
1687             );
1688              
1689 14           timestamp = buffer_get_int_le(asf->scratch);
1690 14           *duration = buffer_get_short_le(asf->scratch);
1691              
1692             out:
1693 14           return timestamp;
1694             }