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