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
|
|
|
|
|
|
|
} |