1 // ***************************************************************** -*- C++ -*-
2 /*
3  * Copyright (C) 2004-2021 Exiv2 authors
4  * This program is part of the Exiv2 distribution.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
19  */
20 /*
21   File:      asfvideo.cpp
22   Author(s): Abhinav Badola for GSoC 2012 (AB) <mail.abu.to@gmail.com>
23   History:   08-Aug-12, AB: created
24   Credits:   See header file
25  */
26 // *****************************************************************************
27 // included header files
28 #include "config.h"
29 
30 #ifdef EXV_ENABLE_VIDEO
31 #include "error.hpp"
32 #include "tags.hpp"
33 #include "tags_int.hpp"
34 #include "asfvideo.hpp"
35 #include "futils.hpp"
36 #include "basicio.hpp"
37 #include "types.hpp"
38 #include "riffvideo.hpp"
39 #include "convert.hpp"
40 
41 // + standard includes
42 #include <cmath>
43 #include <cstring>
44 #include <ctype.h>
45 #include <cassert>
46 
47 // *****************************************************************************
48 // class member definitions
49 namespace Exiv2 {
50     namespace Internal {
51 
52     /*!
53       Tag Look-up list for ASF Type Video Files
54       Associates the GUID of a Tag with its Tag Name(i.e. Human Readable Form)
55       Tags have been diferentiated into Various Categories.
56       The categories have been listed above the Tag Groups
57 
58      */
59     extern const TagVocabulary GUIDReferenceTags[] =  {
60         /// Top-level ASF object GUIDS
61         {   "75B22630-668E-11CF-A6D9-00AA0062CE6C", "Header" },
62         {   "75B22636-668E-11CF-A6D9-00AA0062CE6C", "Data" },
63         {   "33000890-E5B1-11CF-89F4-00A0C90349CB", "Simple_Index" },
64         {   "D6E229D3-35DA-11D1-9034-00A0C90349BE", "Index" },
65         {   "FEB103F8-12AD-4C64-840F-2A1D2F7AD48C", "Media_Index" },
66         {   "3CB73FD0-0C4A-4803-953D-EDF7B6228F0C", "Timecode_Index" },
67 
68         /// Header Object GUIDs
69         {   "8CABDCA1-A947-11CF-8EE4-00C00C205365", "File_Properties" },
70         {   "B7DC0791-A9B7-11CF-8EE6-00C00C205365", "Stream_Properties" },
71         {   "5FBF03B5-A92E-11CF-8EE3-00C00C205365", "Header_Extension" },
72         {   "86D15240-311D-11D0-A3A4-00A0C90348F6", "Codec_List" },
73         {   "1EFB1A30-0B62-11D0-A39B-00A0C90348F6", "Script_Command" },
74         {   "F487CD01-A951-11CF-8EE6-00C00C205365", "Marker" },
75         {   "D6E229DC-35DA-11D1-9034-00A0C90349BE", "Bitrate_Mutual_Exclusion" },
76         {   "75B22635-668E-11CF-A6D9-00AA0062CE6C", "Error_Correction" },
77         {   "75B22633-668E-11CF-A6D9-00AA0062CE6C", "Content_Description" },
78         {   "D2D0A440-E307-11D2-97F0-00A0C95EA850", "Extended_Content_Description" },
79         {   "2211B3FA-BD23-11D2-B4B7-00A0C955FC6E", "Content_Branding" },
80         {   "7BF875CE-468D-11D1-8D82-006097C9A2B2", "Stream_Bitrate_Properties" },
81         {   "2211B3FB-BD23-11D2-B4B7-00A0C955FC6E", "Content_Encryption" },
82         {   "298AE614-2622-4C17-B935-DAE07EE9289C", "Extended_Content_Encryption" },
83         {   "2211B3FC-BD23-11D2-B4B7-00A0C955FC6E", "Digital_Signature" },
84         {   "1806D474-CADF-4509-A4BA-9AABCB96AAE8", "Padding" },
85 
86         /// Header Extension Object GUIDs
87         {   "14E6A5CB-C672-4332-8399-A96952065B5A", "Extended_Stream_Properties" },
88         {   "A08649CF-4775-4670-8A16-6E35357566CD", "Advanced_Mutual_Exclusion" },
89         {   "D1465A40-5A79-4338-B71B-E36B8FD6C249", "Group_Mutual_Exclusion" },
90         {   "D4FED15B-88D3-454F-81F0-ED5C45999E24", "Stream_Prioritization" },
91         {   "A69609E6-517B-11D2-B6AF-00C04FD908E9", "Bandwidth_Sharing" },
92         {   "7C4346A9-EFE0-4BFC-B229-393EDE415C85", "Language_List" },
93         {   "C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA", "Metadata" },
94         {   "44231C94-9498-49D1-A141-1D134E457054", "Metadata_Library" },
95         {   "D6E229DF-35DA-11D1-9034-00A0C90349BE", "Index_Parameters" },
96         {   "6B203BAD-3F11-48E4-ACA8-D7613DE2CFA7", "Media_Index_Parameters" },
97         {   "F55E496D-9797-4B5D-8C8B-604DFE9BFB24", "Timecode_Index_Parameters" },
98         {   "26F18B5D-4584-47EC-9F5F-0E651F0452C9", "Compatibility" },
99         {   "43058533-6981-49E6-9B74-AD12CB86D58C", "Advanced_Content_Encryption" },
100 
101         /// Stream Properties Object Stream Type GUIDs
102         {   "F8699E40-5B4D-11CF-A8FD-00805F5C442B", "Audio_Media" },
103         {   "BC19EFC0-5B4D-11CF-A8FD-00805F5C442B", "Video_Media" },
104         {   "59DACFC0-59E6-11D0-A3AC-00A0C90348F6", "Command_Media" },
105         {   "B61BE100-5B4E-11CF-A8FD-00805F5C442B", "JFIF_Media" },
106         {   "35907DE0-E415-11CF-A917-00805F5C442B", "Degradable_JPEG_Media" },
107         {   "91BD222C-F21C-497A-8B6D-5AA86BFC0185", "File_Transfer_Media" },
108         {   "3AFB65E2-47EF-40F2-AC2C-70A90D71D343", "Binary_Media" },
109 
110         /// Web stream Type-Specific Data GUIDs
111         {   "776257D4-C627-41CB-8F81-7AC7FF1C40CC", "Web_Stream_Media_Subtype" },
112         {   "DA1E6B13-8359-4050-B398-388E965BF00C", "Web_Stream_Format" },
113 
114         /// Stream Properties Object Error Correction Type GUIDs
115         {   "20FB5700-5B55-11CF-A8FD-00805F5C442B", "No_Error_Correction" },
116         {   "BFC3CD50-618F-11CF-8BB2-00AA00B4E220", "Audio_Spread" },
117 
118         /// Header Extension Object GUIDs
119         {   "ABD3D211-A9BA-11cf-8EE6-00C00C205365", "Reserved_1" },
120 
121         /// Advanced Content Encryption Object System ID GUIDs
122         {   "7A079BB6-DAA4-4e12-A5CA-91D38DC11A8D", "Content_Encryption_System_Windows_Media_DRM_Network_Devices" },
123 
124         /// Codec List Object GUIDs
125         {   "86D15241-311D-11D0-A3A4-00A0C90348F6", "Reserved_2" },
126 
127         /// Script Command Object GUIDs
128         {   "4B1ACBE3-100B-11D0-A39B-00A0C90348F6", "Reserved_3" },
129 
130         /// Marker Object GUIDs
131         {   "4CFEDB20-75F6-11CF-9C0F-00A0C90349CB", "Reserved_4" },
132 
133         /// Mutual Exclusion Object Exclusion Type GUIDs
134         {   "D6E22A00-35DA-11D1-9034-00A0C90349BE", "Mutex_Language" },
135         {   "D6E22A01-35DA-11D1-9034-00A0C90349BE", "Mutex_Bitrate" },
136         {   "D6E22A02-35DA-11D1-9034-00A0C90349BE", "Mutex_Unknown" },
137 
138         /// Bandwidth Sharing Object GUIDs
139         {   "AF6060AA-5197-11D2-B6AF-00C04FD908E9", "Bandwidth_Sharing_Exclusive" },
140         {   "AF6060AB-5197-11D2-B6AF-00C04FD908E9", "Bandwidth_Sharing_Partial" },
141 
142         /// Standard Payload Extension System GUIDs
143         {   "399595EC-8667-4E2D-8FDB-98814CE76C1E", "Payload_Extension_System_Timecode" },
144         {   "E165EC0E-19ED-45D7-B4A7-25CBD1E28E9B", "Payload_Extension_System_File_Name" },
145         {   "D590DC20-07BC-436C-9CF7-F3BBFBF1A4DC", "Payload_Extension_System_Content_Type" },
146         {   "1B1EE554-F9EA-4BC8-821A-376B74E4C4B8", "Payload_Extension_System_Pixel_Aspect_Ratio" },
147         {   "C6BD9450-867F-4907-83A3-C77921B733AD", "Payload_Extension_System_Sample_Duration" },
148         {   "6698B84E-0AFA-4330-AEB2-1C0A98D7A44D", "Payload_Extension_System_Encryption_Sample_ID" },
149         {   "00E1AF06-7BEC-11D1-A582-00C04FC29CFB", "Payload_Extension_System_Degradable_JPEG" }
150     };
151 
152     //! Audio codec type-specific data in ASF
153     extern const TagDetails audioCodec[] =  {
154         {    0x161, "Windows Media Audio (7, 8, and 9 Series)" },
155         {    0x162, "Windows Media Audio 9 Professional" },
156         {    0x163, "Windows Media Audio 9 Lossless" },
157         {   0x7A21, "GSM-AMR (CBR, no SID)" },
158         {   0x7A22, "GSM-AMR (VBR including SID)" }
159     };
160 
161     extern const TagDetails filePropertiesTags[] =  {
162         {    7, "Xmp.video.FileLength" },
163         {    6, "Xmp.video.CreationDate" },
164         {    5, "Xmp.video.DataPackets" },
165         {    4, "Xmp.video.Duration" },
166         {    3, "Xmp.video.SendDuration" },
167         {    2, "Xmp.video.Preroll" },
168         {    1, "Xmp.video.MaxBitRate" }
169     };
170 
171     extern const TagDetails contentDescriptionTags[] =  {
172         {    0, "Xmp.video.Title" },
173         {    1, "Xmp.video.Author" },
174         {    2, "Xmp.video.Copyright" },
175         {    3, "Xmp.video.Description" },
176         {    4, "Xmp.video.Rating" }
177     };
178 
179     /*!
180       @brief Function used to read data from data buffer, reads 16-bit character
181           array and stores it in std::string object.
182       @param buf Exiv2 data buffer, which stores the information
183       @return Returns std::string object .
184      */
toString16(Exiv2::DataBuf & buf)185     std::string toString16(Exiv2::DataBuf& buf)
186     {
187         std::ostringstream os; char t;
188 
189         for(int i = 0; i <= buf.size_; i += 2 ) {
190             t = buf.pData_[i] + 16 * buf.pData_[i + 1];
191             if(t == 0) {
192                 if(i)
193                     os << '\0';
194                 break;
195             }
196             os<< t;
197         }
198         return os.str();
199     }
200 
201     /*!
202       @brief Function used to check equality of two Tags (ignores case).
203       @param str1 char* Pointer to First Tag
204       @param str2 char* Pointer to Second Tag
205       @return Returns true if both are equal.
206      */
compareTag(const char * str1,const char * str2)207     bool compareTag(const char* str1, const char* str2) {
208         if ( strlen(str1) != strlen(str2))
209             return false;
210 
211         for ( uint64_t i = 0 ; i < strlen(str1); ++i )
212             if (tolower(str1[i]) != tolower(str2[i]))
213                 return false;
214 
215         return true;
216     }
217 
218     /*!
219       @brief Function used to convert a decimal number to its Hexadecimal
220           equivalent, then parsed into a character
221       @param n Integer which is to be parsed as Hexadecimal character
222       @return Return a Hexadecimal number, in character
223      */
returnHEX(int n)224     char returnHEX(int n) {
225         if(n >= 0 && n <= 9)
226             return (char)(n + 48);
227         else
228             return (char)(n + 55);
229     }
230 
231     /*!
232       @brief Function used to calculate GUID, Tags comprises of 16 bytes.
233           The Buffer contains the Tag in Binary Form. The information is then
234           parsed into a character array GUID.
235      */
getGUID(byte buf[],char GUID[])236     void getGUID (byte buf[], char GUID[]) {
237         int i;
238         for (i = 0; i < 4; ++i) {
239             GUID[(3 - i) * 2]      = returnHEX(buf[i] / 0x10);
240             GUID[(3 - i) * 2 + 1]  = returnHEX(buf[i] % 0x10);
241         }
242         for (i = 4; i < 6; ++i) {
243             GUID[(9 - i) * 2 + 1]  = returnHEX(buf[i] / 0x10);
244             GUID[(9 - i) * 2 + 2]  = returnHEX(buf[i] % 0x10);
245         }
246         for (i = 6; i < 8; ++i) {
247             GUID[(14 - i) * 2]     = returnHEX(buf[i] / 0x10);
248             GUID[(14 - i) * 2 + 1] = returnHEX(buf[i] % 0x10);
249         }
250         for (i = 8; i < 10; ++i) {
251             GUID[ i * 2 + 3]       = returnHEX(buf[i] / 0x10);
252             GUID[ i * 2 + 4]       = returnHEX(buf[i] % 0x10);
253         }
254         for (i = 10; i < 16; ++i) {
255             GUID[ i * 2 + 4]       = returnHEX(buf[i] / 0x10);
256             GUID[ i * 2 + 5]       = returnHEX(buf[i] % 0x10);
257         }
258         GUID[36] = '\0'; GUID[8] = GUID[13] = GUID[18] = GUID[23] = '-';
259     }
260 
261     /*!
262       @brief Function used to check if data stored in buf is equivalent to
263           ASF Header Tag's GUID.
264       @param buf Exiv2 byte buffer
265       @return Returns true if the buffer data is equivalent to Header GUID.
266      */
isASFType(byte buf[])267     bool isASFType (byte buf[]) {
268 
269         if(buf[0]  == 0x30   && buf[1]  == 0x26   && buf[2]  == 0xb2   && buf[3]  == 0x75 &&
270            buf[4]  == 0x8e   && buf[5]  == 0x66   && buf[6]  == 0xcf   && buf[7]  == 0x11 &&
271            buf[8]  == 0xa6   && buf[9]  == 0xd9   && buf[10] == 0x00   && buf[11] == 0xaa &&
272            buf[12] == 0x00   && buf[13] == 0x62   && buf[14] == 0xce   && buf[15] == 0x6c )
273                 return true;
274 
275         return false;
276     }
277 
278     //! Function used to convert buffer data into 64-bit Integer, information stored in littleEndian format
getUint64_t(Exiv2::DataBuf & buf)279     uint64_t getUint64_t(Exiv2::DataBuf& buf) {
280         uint64_t temp = 0;
281 
282         for(int i = 0; i < 8; ++i){
283             temp = temp + static_cast<uint64_t>(buf.pData_[i]*(pow(static_cast<float>(256), i)));
284         }
285         return temp;
286     }
287 
288 }}                                      // namespace Internal, Exiv2
289 
290 namespace Exiv2 {
291 
292     using namespace Exiv2::Internal;
293 
AsfVideo(BasicIo::AutoPtr io)294     AsfVideo::AsfVideo(BasicIo::AutoPtr io)
295         : Image(ImageType::asf, mdNone, io)
296     {
297     } // AsfVideo::AsfVideo
298 
mimeType() const299     std::string AsfVideo::mimeType() const
300     {
301         return "video/asf";
302     }
303 
writeMetadata()304     void AsfVideo::writeMetadata()
305     {
306     }
307 
readMetadata()308     void AsfVideo::readMetadata()
309     {
310         if (io_->open() != 0) throw Error(kerDataSourceOpenFailed, io_->path(), strError());
311 
312         // Ensure that this is the correct image type
313         if (!isAsfType(*io_, false)) {
314             if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
315             throw Error(kerNotAnImage, "ASF");
316         }
317 
318         IoCloser closer(*io_);
319         clearMetadata();
320         continueTraversing_ = true;
321         io_->seek(0, BasicIo::beg);
322         height_ = width_ = 1;
323 
324         xmpData_["Xmp.video.FileSize"] = (double)io_->size()/(double)1048576;
325         xmpData_["Xmp.video.FileName"] = io_->path();
326         xmpData_["Xmp.video.MimeType"] = mimeType();
327 
328         while (continueTraversing_) decodeBlock();
329 
330         aspectRatio();
331     } // AsfVideo::readMetadata
332 
decodeBlock()333     void AsfVideo::decodeBlock()
334     {
335         const long bufMinSize = 9;
336         DataBuf buf(bufMinSize);
337         uint64_t size = 0;
338         buf.pData_[8] = '\0' ;
339         const TagVocabulary* tv;
340         uint64_t cur_pos = io_->tell();
341 
342         byte guidBuf[16];
343         io_->read(guidBuf, 16);
344 
345         if(io_->eof()) {
346             continueTraversing_ = false;
347             return;
348         }
349 
350         char GUID[37] = ""; //the getGUID function write the GUID[36],
351 
352         getGUID(guidBuf, GUID);
353         tv = find( GUIDReferenceTags, GUID);
354 
355         std::memset(buf.pData_, 0x0, buf.size_);
356         io_->read(buf.pData_, 8);
357         size = getUint64_t(buf);
358 
359         if(tv) {
360             tagDecoder(tv,size-24);
361         }
362         else
363             io_->seek(cur_pos + size, BasicIo::beg);
364 
365         localPosition_ = io_->tell();
366     } // AsfVideo::decodeBlock
367 
tagDecoder(const TagVocabulary * tv,uint64_t size)368     void AsfVideo::tagDecoder(const TagVocabulary *tv, uint64_t size)
369     {
370         uint64_t cur_pos = io_->tell();
371         DataBuf buf(1000);
372         unsigned long count = 0, tempLength = 0;
373         buf.pData_[4] = '\0' ;
374         Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpSeq);
375 
376         if(compareTag( exvGettext(tv->label_), "Header")) {
377             localPosition_ = 0;
378             io_->read(buf.pData_, 4);
379             io_->read(buf.pData_, 2);
380 
381             while(localPosition_ < cur_pos + size) decodeBlock();
382         }
383 
384         else if(compareTag( exvGettext(tv->label_), "File_Properties"))
385             fileProperties();
386 
387         else if(compareTag( exvGettext(tv->label_), "Stream_Properties"))
388             streamProperties();
389 
390         else if(compareTag( exvGettext(tv->label_), "Metadata"))
391             metadataHandler(1);
392 
393         else if(compareTag( exvGettext(tv->label_), "Extended_Content_Description"))
394             metadataHandler(2);
395 
396         else if(compareTag( exvGettext(tv->label_), "Metadata_Library"))
397             metadataHandler(3);
398 
399         else if(compareTag( exvGettext(tv->label_), "Codec_List"))
400             codecList();
401 
402         else if(compareTag( exvGettext(tv->label_), "Content_Description"))
403             contentDescription(size);
404 
405         else if(compareTag( exvGettext(tv->label_), "Extended_Stream_Properties"))
406             extendedStreamProperties(size);
407 
408         else if(compareTag( exvGettext(tv->label_), "Header_Extension")) {
409             localPosition_ = 0;
410             headerExtension(size);
411         }
412 
413         else if(compareTag( exvGettext(tv->label_), "Language_List")) {
414             std::memset(buf.pData_, 0x0, buf.size_);
415             io_->read(buf.pData_, 2);
416             count = Exiv2::getUShort(buf.pData_, littleEndian);
417 
418             while(count--) {
419                 std::memset(buf.pData_, 0x0, buf.size_);
420                 io_->read(buf.pData_, 1);   tempLength = (int)buf.pData_[0];
421 
422                 io_->read(buf.pData_, tempLength);
423                 v->read(toString16(buf));
424             }
425             xmpData_.add(Exiv2::XmpKey("Xmp.video.TrackLang"), v.get());
426         }
427 
428         io_->seek(cur_pos + size, BasicIo::beg);
429         localPosition_ = io_->tell();
430     } // AsfVideo::tagDecoder
431 
extendedStreamProperties(uint64_t size)432     void AsfVideo::extendedStreamProperties(uint64_t size)
433     {
434         uint64_t cur_pos = io_->tell(), avgTimePerFrame = 0;
435         DataBuf buf(8);
436         static int previousStream;
437         io_->seek(cur_pos + 48, BasicIo::beg);
438 
439         std::memset(buf.pData_, 0x0, buf.size_);
440         io_->read(buf.pData_, 2);
441         streamNumber_ = Exiv2::getUShort(buf.pData_, littleEndian);
442 
443         io_->read(buf.pData_, 2);
444         io_->read(buf.pData_, 8);
445         avgTimePerFrame = getUint64_t(buf);
446 
447         if(previousStream < streamNumber_  &&  avgTimePerFrame != 0)
448             xmpData_["Xmp.video.FrameRate"] = (double)10000000/(double)avgTimePerFrame;
449 
450         previousStream = streamNumber_;
451         io_->seek(cur_pos + size, BasicIo::beg);
452     } // AsfVideo::extendedStreamProperties
453 
contentDescription(uint64_t size)454     void AsfVideo::contentDescription(uint64_t size)
455     {
456         const long pos = io_->tell();
457         if (pos == -1) throw Error(kerFailedToReadImageData);
458         long length[5];
459         for (int i = 0 ; i < 5 ; ++i) {
460             byte buf[2];
461             io_->read(buf, 2);
462             if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
463             length[i] = getUShort(buf, littleEndian);
464         }
465         for (int i = 0 ; i < 5 ; ++i) {
466             DataBuf buf(length[i]);
467             std::memset(buf.pData_, 0x0, buf.size_);
468             io_->read(buf.pData_, length[i]);
469             if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
470             const TagDetails* td = find(contentDescriptionTags, i);
471             assert(td);
472             std::string str((const char*)buf.pData_, length[i]);
473             if (convertStringCharset(str, "UCS-2LE", "UTF-8")) {
474                 xmpData_[td->label_] = str;
475             }
476             else {
477                 xmpData_[td->label_] = toString16(buf);
478             }
479         }
480         if (io_->seek(pos + size, BasicIo::beg)) throw Error(kerFailedToReadImageData);
481     } // AsfVideo::contentDescription
482 
streamProperties()483     void AsfVideo::streamProperties()
484     {
485         DataBuf buf(20);
486         buf.pData_[8] = '\0' ;
487         byte guidBuf[16]; int stream = 0;
488         io_->read(guidBuf, 16);
489         char streamType[37] = "";
490         Exiv2::RiffVideo *test = NULL;
491 
492         getGUID(guidBuf, streamType);
493         const TagVocabulary* tv;
494         tv = find( GUIDReferenceTags, streamType);
495         io_->read(guidBuf, 16);
496 
497         if(compareTag( exvGettext(tv->label_), "Audio_Media"))
498             stream = 1;
499         else if(compareTag( exvGettext(tv->label_), "Video_Media"))
500             stream = 2;
501 
502         io_->read(buf.pData_, 8);
503         if(stream == 2)
504             xmpData_["Xmp.video.TimeOffset"] = getUint64_t(buf);
505         else if(stream == 1)
506             xmpData_["Xmp.audio.TimeOffset"] = getUint64_t(buf);
507 
508         io_->read(buf.pData_, 8);
509         std::memset(buf.pData_, 0x0, buf.size_);
510         io_->read(buf.pData_, 1);
511         streamNumber_ = (int)buf.pData_[0] & 127;
512 
513         io_->read(buf.pData_, 5);
514         std::memset(buf.pData_, 0x0, buf.size_);
515         io_->read(buf.pData_, 2);
516         long temp = Exiv2::getUShort(buf.pData_, littleEndian);
517 
518         if(stream == 2) {
519             xmpData_["Xmp.video.Width"] = temp;
520             width_ = temp;
521         }
522         else if(stream == 1) {
523             xmpData_["Xmp.audio.Codec"] = test->printAudioEncoding(temp);
524         }
525 
526         io_->read(buf.pData_, 2);
527         temp = Exiv2::getUShort(buf.pData_, littleEndian);
528         if(stream == 1)
529             xmpData_["Xmp.audio.ChannelType"] = temp;
530 
531         io_->read(buf.pData_, 4);
532         temp = Exiv2::getULong(buf.pData_, littleEndian);
533 
534         if(stream == 2) {
535             xmpData_["Xmp.video.Height"] = temp;
536             height_ = temp;
537         }
538         else if(stream == 1) {
539             xmpData_["Xmp.audio.SampleRate"] = temp;
540         }
541     } // AsfVideo::streamProperties
542 
codecList()543     void AsfVideo::codecList()
544     {
545         DataBuf buf(200);
546         io_->read(buf.pData_, 16);
547         std::memset(buf.pData_, 0x0, buf.size_);
548         io_->read(buf.pData_, 4);
549         int codecCount = Exiv2::getULong(buf.pData_, littleEndian), descLength = 0, codecType = 0;
550 
551         while(codecCount--) {
552             std::memset(buf.pData_, 0x0, buf.size_);
553             io_->read(buf.pData_, 2);
554             codecType = Exiv2::getUShort(buf.pData_, littleEndian);
555 
556             io_->read(buf.pData_, 2);
557             descLength = Exiv2::getUShort(buf.pData_, littleEndian) * 2;
558 
559             if (descLength < 0) {
560             #ifndef SUPPRESS_WARNINGS
561                     EXV_ERROR   << " Description found in this ASF file is not of valid size ."
562                                 << " Entries considered invalid. Not Processed.\n";
563             #endif
564             }
565             else {
566                 io_->read(buf.pData_, descLength);
567                 if(codecType == 1)
568                     xmpData_["Xmp.video.Codec"] = toString16(buf);
569                 else if(codecType == 2)
570                     xmpData_["Xmp.audio.Compressor"] = toString16(buf);
571             }
572 
573             std::memset(buf.pData_, 0x0, buf.size_);
574             io_->read(buf.pData_, 2);
575             descLength = Exiv2::getUShort(buf.pData_, littleEndian) * 2;
576 
577             if (descLength < 0) {
578             #ifndef SUPPRESS_WARNINGS
579                     EXV_ERROR   << " Description found in this ASF file is not of valid size ."
580                                 << " Entries considered invalid. Not Processed.\n";
581             #endif
582             }
583             else {
584                 io_->read(buf.pData_, descLength);
585 
586                 if(codecType == 1)
587                     xmpData_["Xmp.video.CodecDescription"] = toString16(buf);
588                 else if(codecType == 2)
589                     xmpData_["Xmp.audio.CodecDescription"] = toString16(buf);
590             }
591 
592             std::memset(buf.pData_, 0x0, buf.size_);
593             io_->read(buf.pData_, 2);
594             descLength = Exiv2::getUShort(buf.pData_, littleEndian);
595 
596             if (descLength < 0) {
597             #ifndef SUPPRESS_WARNINGS
598                     EXV_ERROR   << " Description found in this ASF file is not of valid size ."
599                                 << " Entries considered invalid. Not Processed.\n";
600             #endif
601             }
602             else {
603                 io_->read(buf.pData_, descLength);
604             }
605         }
606     } // AsfVideo::codecList
607 
headerExtension(uint64_t size)608     void AsfVideo::headerExtension(uint64_t size)
609     {
610         uint64_t cur_pos = io_->tell();
611         DataBuf buf(20);
612         io_->read(buf.pData_, 18);
613         buf.pData_[4] = '\0' ;
614         io_->read(buf.pData_, 4);
615 
616         while(localPosition_ < cur_pos + size) decodeBlock();
617 
618         io_->seek(cur_pos + size, BasicIo::beg);
619     } // AsfVideo::headerExtension
620 
metadataHandler(int meta)621     void AsfVideo::metadataHandler(int meta)
622     {
623         DataBuf buf(5000);
624         io_->read(buf.pData_, 2);
625         int recordCount = Exiv2::getUShort(buf.pData_, littleEndian), nameLength = 0, dataLength = 0, dataType = 0;
626         Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpSeq);
627         byte guidBuf[16];   char fileID[37] = "";
628 
629         while(recordCount--) {
630             std::memset(buf.pData_, 0x0, buf.size_);
631 
632             if(meta == 1 || meta == 3) {
633                 io_->read(buf.pData_, 4);
634                 io_->read(buf.pData_, 2);
635                 nameLength = Exiv2::getUShort(buf.pData_, littleEndian);
636                 io_->read(buf.pData_, 2);
637                 dataType = Exiv2::getUShort(buf.pData_, littleEndian);
638                 io_->read(buf.pData_, 4);
639                 dataLength = Exiv2::getULong(buf.pData_, littleEndian);
640 
641                 if (nameLength > 5000) {
642 #ifndef SUPPRESS_WARNINGS
643                     EXV_ERROR << "Xmp.video.Metadata nameLength was found to be larger than 5000 "
644                               << " entries considered invalid; not read.\n";
645 #endif
646                     io_->seek(io_->tell() + nameLength, BasicIo::beg);
647                 } else {
648                     io_->read(buf.pData_, nameLength);
649                 }
650 
651                 v->read(toString16(buf));
652                 if(dataType == 6) {
653                     io_->read(guidBuf, 16);
654                     getGUID(guidBuf, fileID);
655                 }
656                 else
657                     // Sanity check with an "unreasonably" large number
658                     if (dataLength > 5000) {
659 #ifndef SUPPRESS_WARNINGS
660                         EXV_ERROR << "Xmp.video.Metadata dataLength was found to be larger than 5000 "
661                                   << " entries considered invalid; not read.\n";
662 #endif
663                         io_->seek(io_->tell() + dataLength, BasicIo::beg);
664                     }
665                 else
666                         io_->read(buf.pData_, dataLength);
667             }
668 
669             else if(meta == 2) {
670                 io_->read(buf.pData_, 2);
671                 nameLength = Exiv2::getUShort(buf.pData_, littleEndian);
672 
673                 if (nameLength > 5000) {
674 #ifndef SUPPRESS_WARNINGS
675                     EXV_ERROR << "Xmp.video.Metadata nameLength was found to be larger than 5000 "
676                               << " entries considered invalid; not read.\n";
677 #endif
678                     io_->seek(io_->tell() + nameLength, BasicIo::beg);
679                 } else {
680                     io_->read(buf.pData_, nameLength);
681                 }
682 
683                 v->read(toString16(buf));
684 
685                 io_->read(buf.pData_, 2);
686                 dataType = Exiv2::getUShort(buf.pData_, littleEndian);
687 
688                 io_->read(buf.pData_, 2);
689                 dataLength = Exiv2::getUShort(buf.pData_, littleEndian);
690 
691                 // Sanity check with an "unreasonably" large number
692                 if (dataLength > 5000) {
693 #ifndef SUPPRESS_WARNINGS
694                     EXV_ERROR << "Xmp.video.Metadata dataLength was found to be larger than 5000 "
695                               << " entries considered invalid; not read.\n";
696 #endif
697                     io_->seek(io_->tell() + dataLength, BasicIo::beg);
698                 }
699             else
700                 io_->read(buf.pData_, dataLength);
701             }
702 
703             if(dataType == 0) {                       // Unicode String
704                 v->read(toString16(buf));
705             }
706             else if(dataType == 2 || dataType == 5) { // 16-bit Unsigned Integer
707                 v->read( Exiv2::toString( Exiv2::getUShort(buf.pData_, littleEndian)));
708             }
709             else if(dataType == 3) {                  // 32-bit Unsigned Integer
710                 v->read( Exiv2::toString( Exiv2::getULong( buf.pData_, littleEndian)));
711             }
712             else if(dataType == 4) {                  // 64-bit Unsigned Integer
713                 v->read(Exiv2::toString(getUint64_t(buf)));
714             }
715             else if(dataType == 6) {                  // 128-bit GUID
716                 v->read(Exiv2::toString(fileID));
717             }
718             else {                                    // Byte array
719                 v->read( Exiv2::toString(buf.pData_));
720             }
721         }
722 
723         if(meta == 1) {
724             xmpData_.add(Exiv2::XmpKey("Xmp.video.Metadata"), v.get());
725         }
726         else if(meta == 2) {
727             xmpData_.add(Exiv2::XmpKey("Xmp.video.ExtendedContentDescription"), v.get());
728         }
729         else {
730             xmpData_.add(Exiv2::XmpKey("Xmp.video.MetadataLibrary"), v.get());
731         }
732     } // AsfVideo::metadataHandler
733 
fileProperties()734     void AsfVideo::fileProperties()
735     {
736         DataBuf buf(9);
737         buf.pData_[8] = '\0' ;
738 
739         byte guidBuf[16];
740         io_->read(guidBuf, 16);
741         char fileID[37] = ""; int count = 7;
742         getGUID(guidBuf, fileID);
743         xmpData_["Xmp.video.FileID"] = fileID;
744 
745         const TagDetails* td;
746 
747         while(count--) {
748             td = find(filePropertiesTags , (count + 1));
749             io_->read(buf.pData_, 8);
750 
751             if(count == 0) {
752                 buf.pData_[4] = '\0' ;
753                 io_->read(buf.pData_, 4); io_->read(buf.pData_, 4);
754             }
755 
756             if(count == 3 || count == 2) {
757                 xmpData_[exvGettext(td->label_)] = getUint64_t(buf) / 10000;
758             }
759             else {
760                 xmpData_[exvGettext(td->label_)] = getUint64_t(buf);
761             }
762         }
763     } // AsfVideo::fileProperties
764 
aspectRatio()765     void AsfVideo::aspectRatio()
766     {
767         //TODO - Make a better unified method to handle all cases of Aspect Ratio
768 
769         double aspectRatio = (double)width_ / (double)height_;
770         aspectRatio = floor(aspectRatio*10) / 10;
771         xmpData_["Xmp.video.AspectRatio"] = aspectRatio;
772 
773         int aR = (int) ((aspectRatio*10.0)+0.1);
774 
775         switch  (aR) {
776             case 13 : xmpData_["Xmp.video.AspectRatio"] = "4:3"     ; break;
777             case 17 : xmpData_["Xmp.video.AspectRatio"] = "16:9"    ; break;
778             case 10 : xmpData_["Xmp.video.AspectRatio"] = "1:1"     ; break;
779             case 16 : xmpData_["Xmp.video.AspectRatio"] = "16:10"   ; break;
780             case 22 : xmpData_["Xmp.video.AspectRatio"] = "2.21:1"  ; break;
781             case 23 : xmpData_["Xmp.video.AspectRatio"] = "2.35:1"  ; break;
782             case 12 : xmpData_["Xmp.video.AspectRatio"] = "5:4"     ; break;
783             default : xmpData_["Xmp.video.AspectRatio"] = aspectRatio;break;
784         }
785     } // AsfVideo::aspectRatio
786 
787 
newAsfInstance(BasicIo::AutoPtr io,bool)788     Image::AutoPtr newAsfInstance(BasicIo::AutoPtr io, bool /*create*/)
789     {
790         Image::AutoPtr image(new AsfVideo(io));
791         if (!image->good()) {
792             image.reset();
793         }
794         return image;
795     }
796 
isAsfType(BasicIo & iIo,bool advance)797     bool isAsfType(BasicIo& iIo, bool advance)
798     {
799         const int32_t len = 16;
800         byte buf[len];
801         iIo.read(buf, len);
802 
803         if (iIo.error() || iIo.eof()) {
804             return false;
805         }
806 
807         bool matched = isASFType(buf);
808         if (!advance || !matched) {
809             iIo.seek(0, BasicIo::beg);
810         }
811 
812         return matched;
813     }
814 
815 }                                       // namespace Exiv2
816 #endif // EXV_ENABLE_VIDEO
817