1 /* libasf - An Advanced Systems Format media file parser
2 * Copyright (C) 2006-2010 Juho Vähä-Herttua
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
31 * Finds an object with the corresponding GUID type from header object. If
32 * not found, just returns NULL.
34 static asfint_object_t
*
35 asf_header_get_object(asf_object_header_t
*header
, const guid_type_t type
)
37 asfint_object_t
*current
;
39 current
= header
->first
;
41 if (current
->type
== type
) {
44 current
= current
->next
;
51 * Reads the stream properties object's data into the equivalent
52 * data structure, and stores it in asf_stream_t structure
53 * with the equivalent stream type. Needs the stream properties
54 * object data as its input.
57 asf_parse_header_stream_properties(asf_stream_t
*stream
,
67 return ASF_ERROR_INVALID_LENGTH
;
70 GetGUID(objdata
, &guid
);
71 type
= asf_guid_get_stream_type(&guid
);
73 datalen
= GetDWLE(objdata
+ 40);
74 if (datalen
> objsize
- 78) {
75 return ASF_ERROR_INVALID_LENGTH
;
79 if (type
== GUID_STREAM_TYPE_EXTENDED
) {
80 /* FIXME: Need to find out what actually is here...
81 but for now we can just skip the extended part */
83 return ASF_ERROR_INVALID_LENGTH
;
88 /* update the stream type with correct one */
89 GetGUID(objdata
, &guid
);
90 type
= asf_guid_get_stream_type(&guid
);
94 case GUID_STREAM_TYPE_AUDIO
:
95 case GUID_STREAM_TYPE_EXTENDED_AUDIO
:
97 asf_waveformatex_t
*wfx
;
99 stream
->type
= ASF_STREAM_TYPE_AUDIO
;
102 return ASF_ERROR_INVALID_LENGTH
;
104 if (GetWLE(data
+ 16) > datalen
- 16) {
105 return ASF_ERROR_INVALID_LENGTH
;
108 /* this should be freed in asf_close function */
109 stream
->properties
= malloc(sizeof(asf_waveformatex_t
));
110 if (!stream
->properties
)
111 return ASF_ERROR_OUTOFMEM
;
112 stream
->flags
|= ASF_STREAM_FLAG_AVAILABLE
;
114 wfx
= stream
->properties
;
115 wfx
->wFormatTag
= GetWLE(data
);
116 wfx
->nChannels
= GetWLE(data
+ 2);
117 wfx
->nSamplesPerSec
= GetDWLE(data
+ 4);
118 wfx
->nAvgBytesPerSec
= GetDWLE(data
+ 8);
119 wfx
->nBlockAlign
= GetWLE(data
+ 12);
120 wfx
->wBitsPerSample
= GetWLE(data
+ 14);
121 wfx
->cbSize
= GetWLE(data
+ 16);
122 wfx
->data
= data
+ 18;
124 if (wfx
->cbSize
> datalen
- 18) {
125 debug_printf("Invalid waveformatex data length, truncating!");
126 wfx
->cbSize
= datalen
- 18;
131 case GUID_STREAM_TYPE_VIDEO
:
133 asf_bitmapinfoheader_t
*bmih
;
134 uint32_t width
, height
, flags
, data_size
;
136 stream
->type
= ASF_STREAM_TYPE_VIDEO
;
139 return ASF_ERROR_INVALID_LENGTH
;
142 width
= GetDWLE(data
);
143 height
= GetDWLE(data
+ 4);
145 data_size
= GetWLE(data
+ 9);
150 if (GetDWLE(data
) != datalen
) {
151 return ASF_ERROR_INVALID_LENGTH
;
153 if (width
!= GetDWLE(data
+ 4) ||
154 height
!= GetDWLE(data
+ 8) ||
156 return ASF_ERROR_INVALID_VALUE
;
159 /* this should be freed in asf_close function */
160 stream
->properties
= malloc(sizeof(asf_bitmapinfoheader_t
));
161 if (!stream
->properties
)
162 return ASF_ERROR_OUTOFMEM
;
163 stream
->flags
|= ASF_STREAM_FLAG_AVAILABLE
;
165 bmih
= stream
->properties
;
166 bmih
->biSize
= GetDWLE(data
);
167 bmih
->biWidth
= GetDWLE(data
+ 4);
168 bmih
->biHeight
= GetDWLE(data
+ 8);
169 bmih
->biPlanes
= GetDWLE(data
+ 12);
170 bmih
->biBitCount
= GetDWLE(data
+ 14);
171 bmih
->biCompression
= GetDWLE(data
+ 16);
172 bmih
->biSizeImage
= GetDWLE(data
+ 20);
173 bmih
->biXPelsPerMeter
= GetDWLE(data
+ 24);
174 bmih
->biYPelsPerMeter
= GetDWLE(data
+ 28);
175 bmih
->biClrUsed
= GetDWLE(data
+ 32);
176 bmih
->biClrImportant
= GetDWLE(data
+ 36);
177 bmih
->data
= data
+ 40;
179 if (bmih
->biSize
> datalen
) {
180 debug_printf("Invalid bitmapinfoheader data length, truncating!");
181 bmih
->biSize
= datalen
;
186 case GUID_STREAM_TYPE_COMMAND
:
187 stream
->type
= ASF_STREAM_TYPE_COMMAND
;
190 stream
->type
= ASF_STREAM_TYPE_UNKNOWN
;
198 asf_parse_header_extended_stream_properties(asf_stream_t
*stream
,
202 asf_stream_extended_properties_t ext
;
208 ext
.start_time
= GetQWLE(objdata
);
209 ext
.end_time
= GetQWLE(objdata
+ 8);
210 ext
.data_bitrate
= GetDWLE(objdata
+ 16);
211 ext
.buffer_size
= GetDWLE(objdata
+ 20);
212 ext
.initial_buf_fullness
= GetDWLE(objdata
+ 24);
213 ext
.data_bitrate2
= GetDWLE(objdata
+ 28);
214 ext
.buffer_size2
= GetDWLE(objdata
+ 32);
215 ext
.initial_buf_fullness2
= GetDWLE(objdata
+ 36);
216 ext
.max_obj_size
= GetDWLE(objdata
+ 40);
217 ext
.flags
= GetDWLE(objdata
+ 44);
218 ext
.stream_num
= GetWLE(objdata
+ 48);
219 ext
.lang_idx
= GetWLE(objdata
+ 50);
220 ext
.avg_time_per_frame
= GetQWLE(objdata
+ 52);
221 ext
.stream_name_count
= GetWLE(objdata
+ 60);
222 ext
.num_payload_ext
= GetWLE(objdata
+ 62);
224 datalen
= objsize
- 88;
227 /* iterate through all name strings */
228 for (i
=0; i
<ext
.stream_name_count
; i
++) {
232 return ASF_ERROR_INVALID_VALUE
;
235 strlen
= GetWLE(data
+ 2);
236 if (strlen
> datalen
) {
237 return ASF_ERROR_INVALID_LENGTH
;
240 /* skip the current name string */
242 datalen
-= 4 + strlen
;
245 /* iterate through all extension systems */
246 for (i
=0; i
<ext
.num_payload_ext
; i
++) {
250 return ASF_ERROR_INVALID_VALUE
;
253 extsyslen
= GetDWLE(data
+ 18);
254 if (extsyslen
> datalen
) {
255 return ASF_ERROR_INVALID_LENGTH
;
258 /* skip the current extension system */
259 data
+= 22 + extsyslen
;
260 datalen
-= 22 + extsyslen
;
266 debug_printf("hidden stream properties object found!");
268 /* this is almost same as in stream properties handler */
270 return ASF_ERROR_INVALID_OBJECT_SIZE
;
273 /* check that we really have a stream properties object */
274 GetGUID(data
, &guid
);
275 if (asf_guid_get_type(&guid
) != GUID_STREAM_PROPERTIES
) {
276 return ASF_ERROR_INVALID_OBJECT
;
278 if (GetQWLE(data
+ 16) != datalen
) {
279 return ASF_ERROR_INVALID_OBJECT_SIZE
;
282 flags
= GetWLE(data
+ 72);
284 if ((flags
& 0x7f) != ext
.stream_num
|| stream
->type
) {
285 /* only one stream object per stream allowed and
286 * stream ids have to match with both objects*/
287 return ASF_ERROR_INVALID_OBJECT
;
291 stream
->flags
|= ASF_STREAM_FLAG_HIDDEN
;
292 ret
= asf_parse_header_stream_properties(stream
,
302 stream
->extended_properties
= malloc(sizeof(asf_stream_extended_properties_t
));
303 if (!stream
->extended_properties
) {
304 return ASF_ERROR_OUTOFMEM
;
306 stream
->flags
|= ASF_STREAM_FLAG_EXTENDED
;
307 memcpy(stream
->extended_properties
, &ext
, sizeof(ext
));
313 * Reads the file properties object contents to the asf_file_t structure,
314 * parses the useful values from stream properties object to the equivalent
315 * stream properties info structure and validates that all known header
316 * subobjects have only legal values.
319 asf_parse_header_validate(asf_file_t
*file
, asf_object_header_t
*header
)
321 /* some flags for mandatory subobjects */
322 int fileprop
= 0, streamprop
= 0;
323 asfint_object_t
*current
;
326 current
= header
->first
;
328 uint64_t size
= current
->size
;
330 switch (current
->type
) {
331 case GUID_FILE_PROPERTIES
:
333 uint32_t max_packet_size
;
335 return ASF_ERROR_INVALID_OBJECT_SIZE
;
338 /* multiple file properties objects not allowed */
339 return ASF_ERROR_INVALID_OBJECT
;
343 GetGUID(current
->data
, &file
->file_id
);
344 file
->file_size
= GetQWLE(current
->data
+ 16);
345 file
->creation_date
= GetQWLE(current
->data
+ 24);
346 file
->data_packets_count
= GetQWLE(current
->data
+ 32);
347 file
->play_duration
= GetQWLE(current
->data
+ 40);
348 file
->send_duration
= GetQWLE(current
->data
+ 48);
349 file
->preroll
= GetQWLE(current
->data
+ 56);
350 file
->flags
= GetDWLE(current
->data
+ 64);
351 file
->packet_size
= GetDWLE(current
->data
+ 68);
352 file
->max_bitrate
= GetQWLE(current
->data
+ 76);
354 max_packet_size
= GetDWLE(current
->data
+ 72);
355 if (file
->packet_size
!= max_packet_size
) {
356 /* in ASF file minimum packet size and maximum
357 * packet size have to be same apparently...
359 return ASF_ERROR_INVALID_VALUE
;
363 case GUID_STREAM_PROPERTIES
:
366 asf_stream_t
*stream
;
370 return ASF_ERROR_INVALID_OBJECT_SIZE
;
373 flags
= GetWLE(current
->data
+ 48);
374 stream
= &file
->streams
[flags
& 0x7f];
377 /* only one stream object per stream allowed */
378 return ASF_ERROR_INVALID_OBJECT
;
381 ret
= asf_parse_header_stream_properties(stream
,
390 case GUID_CONTENT_DESCRIPTION
:
392 uint32_t stringlen
= 0;
395 return ASF_ERROR_INVALID_OBJECT_SIZE
;
397 stringlen
+= GetWLE(current
->data
);
398 stringlen
+= GetWLE(current
->data
+ 2);
399 stringlen
+= GetWLE(current
->data
+ 4);
400 stringlen
+= GetWLE(current
->data
+ 6);
401 stringlen
+= GetWLE(current
->data
+ 8);
403 if (size
< stringlen
+ 34) {
404 /* invalid string length values */
405 return ASF_ERROR_INVALID_LENGTH
;
411 case GUID_CODEC_LIST
:
413 return ASF_ERROR_INVALID_OBJECT_SIZE
;
415 case GUID_STREAM_BITRATE_PROPERTIES
:
417 return ASF_ERROR_INVALID_OBJECT_SIZE
;
421 case GUID_EXTENDED_CONTENT_DESCRIPTION
:
423 return ASF_ERROR_INVALID_OBJECT_SIZE
;
426 /* unknown guid type */
429 /* identified type in wrong place */
430 return ASF_ERROR_INVALID_OBJECT
;
433 current
= current
->next
;
438 current
= header
->ext
->first
;
440 uint64_t size
= current
->size
;
442 switch (current
->type
) {
445 return ASF_ERROR_INVALID_OBJECT_SIZE
;
447 case GUID_LANGUAGE_LIST
:
449 return ASF_ERROR_INVALID_OBJECT_SIZE
;
451 case GUID_EXTENDED_STREAM_PROPERTIES
:
454 asf_stream_t
*stream
;
458 return ASF_ERROR_INVALID_OBJECT_SIZE
;
460 stream_num
= GetWLE(current
->data
+ 48);
461 stream
= &file
->streams
[stream_num
];
463 ret
= asf_parse_header_extended_stream_properties(stream
,
472 case GUID_ADVANCED_MUTUAL_EXCLUSION
:
474 return ASF_ERROR_INVALID_OBJECT_SIZE
;
476 case GUID_STREAM_PRIORITIZATION
:
478 return ASF_ERROR_INVALID_OBJECT_SIZE
;
481 /* unknown guid type */
484 /* identified type in wrong place */
488 current
= current
->next
;
492 if (!fileprop
|| !streamprop
|| !header
->ext
) {
493 /* mandatory subobject missing */
494 return ASF_ERROR_INVALID_OBJECT
;
501 * Destroy the header and all subobjects
504 asf_free_header(asf_object_header_t
*header
)
510 asfint_object_t
*current
= header
->first
, *next
;
512 next
= current
->next
;
519 asfint_object_t
*current
= header
->ext
->first
, *next
;
521 next
= current
->next
;
532 * Allocates a metadata struct and parses the contents from
533 * the header object raw data. All strings are in UTF-8 encoded
534 * format. The returned struct needs to be freed using the
535 * asf_header_metadata_destroy function. Returns NULL on failure.
538 asf_header_metadata(asf_object_header_t
*header
)
540 asfint_object_t
*current
;
543 /* allocate the metadata struct */
544 ret
= calloc(1, sizeof(asf_metadata_t
));
549 current
= asf_header_get_object(header
, GUID_CONTENT_DESCRIPTION
);
555 /* The validity of the object is already checked so we can assume
556 * there's always enough data to read and there are no overflows */
557 for (i
=0; i
<5; i
++) {
558 strlen
= GetWLE(current
->data
+ i
*2);
562 str
= asf_utf8_from_utf16le(current
->data
+ 10 + read
, strlen
);
573 ret
->copyright
= str
;
576 ret
->description
= str
;
588 current
= asf_header_get_object(header
, GUID_EXTENDED_CONTENT_DESCRIPTION
);
592 ret
->extended_count
= GetWLE(current
->data
);
593 ret
->extended
= calloc(ret
->extended_count
, sizeof(asf_metadata_entry_t
));
594 if (!ret
->extended
) {
595 /* Clean up the already allocated parts and return */
598 free(ret
->copyright
);
599 free(ret
->description
);
607 for (i
=0; i
<ret
->extended_count
; i
++) {
608 uint16_t length
, type
;
610 length
= GetWLE(current
->data
+ position
);
613 ret
->extended
[i
].key
= asf_utf8_from_utf16le(current
->data
+ position
, length
);
616 type
= GetWLE(current
->data
+ position
);
619 length
= GetWLE(current
->data
+ position
);
624 /* type of the value is a string */
625 ret
->extended
[i
].value
= asf_utf8_from_utf16le(current
->data
+ position
, length
);
628 /* type of the value is a data block */
629 ret
->extended
[i
].value
= malloc((length
*2 + 1) * sizeof(char));
630 for (j
=0; j
<length
; j
++) {
631 static const char hex
[16] = "0123456789ABCDEF";
632 ret
->extended
[i
].value
[j
*2+0] = hex
[current
->data
[position
]>>4];
633 ret
->extended
[i
].value
[j
*2+1] = hex
[current
->data
[position
]&0x0f];
635 ret
->extended
[i
].value
[j
*2] = '\0';
638 /* type of the value is a boolean */
639 ret
->extended
[i
].value
= malloc(6 * sizeof(char));
640 sprintf(ret
->extended
[i
].value
, "%s",
641 *current
->data
? "true" : "false");
644 /* type of the value is a signed 32-bit integer */
645 ret
->extended
[i
].value
= malloc(11 * sizeof(char));
646 sprintf(ret
->extended
[i
].value
, "%u",
647 GetDWLE(current
->data
+ position
));
650 /* FIXME: This doesn't print whole 64-bit integer */
651 ret
->extended
[i
].value
= malloc(21 * sizeof(char));
652 sprintf(ret
->extended
[i
].value
, "%u",
653 (uint32_t) GetQWLE(current
->data
+ position
));
656 /* type of the value is a signed 16-bit integer */
657 ret
->extended
[i
].value
= malloc(6 * sizeof(char));
658 sprintf(ret
->extended
[i
].value
, "%u",
659 GetWLE(current
->data
+ position
));
662 /* Unknown value type... */
663 ret
->extended
[i
].value
= NULL
;
674 * Free the metadata struct and all fields it includes
677 asf_header_free_metadata(asf_metadata_t
*metadata
)
681 free(metadata
->title
);
682 free(metadata
->artist
);
683 free(metadata
->copyright
);
684 free(metadata
->description
);
685 free(metadata
->rating
);
686 for (i
=0; i
<metadata
->extended_count
; i
++) {
687 free(metadata
->extended
[i
].key
);
688 free(metadata
->extended
[i
].value
);
690 free(metadata
->extended
);