diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6b1bf023..51908a4b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,7 +32,8 @@ SET( LIBEXIV2_PRIVATE_HDR canonmn_int.hpp ) # Add standalone C++ header files to this list -SET( LIBEXIV2_HDR basicio.hpp +SET( LIBEXIV2_HDR asfvideo.hpp + basicio.hpp bmpimage.hpp convert.hpp cr2image.hpp @@ -49,6 +50,7 @@ SET( LIBEXIV2_HDR basicio.hpp iptc.hpp jp2image.hpp jpgimage.hpp + matroskavideo.hpp metadatum.hpp mrwimage.hpp orfimage.hpp @@ -56,7 +58,9 @@ SET( LIBEXIV2_HDR basicio.hpp preview.hpp properties.hpp psdimage.hpp + quicktimevideo.hpp rafimage.hpp + riffvideo.hpp rw2image.hpp tags.hpp tgaimage.hpp @@ -69,7 +73,8 @@ SET( LIBEXIV2_HDR basicio.hpp ) # Add library C++ source files to this list -SET( LIBEXIV2_SRC basicio.cpp +SET( LIBEXIV2_SRC asfvideo.cpp + basicio.cpp bmpimage.cpp canonmn.cpp convert.cpp @@ -89,6 +94,7 @@ SET( LIBEXIV2_SRC basicio.cpp jp2image.cpp jpgimage.cpp makernote.cpp + matroskavideo.cpp metadatum.cpp minoltamn.cpp mrwimage.cpp @@ -101,7 +107,9 @@ SET( LIBEXIV2_SRC basicio.cpp preview.cpp properties.cpp psdimage.cpp + quicktimevideo.cpp rafimage.cpp + riffvideo.cpp rw2image.cpp samsungmn.cpp sigmamn.cpp @@ -182,13 +190,16 @@ msvc_runtime_configure(${EXIV2_ENABLE_SHARED}) # ****************************************************************************** # exiv2lib library + ADD_LIBRARY( exiv2lib ${STATIC_FLAG} ${LIBEXIV2_SRC} ${LIBEXIV2_HDR} ) + SET_TARGET_PROPERTIES( exiv2lib PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} DEFINE_SYMBOL EXV_BUILDING_LIB OUTPUT_NAME exiv2 ) + if ( MSVC ) source_group("Header Files" FILES ${LIBEXIV2_HDR} ) endif() diff --git a/src/Makefile b/src/Makefile index 78b736d0..277770b1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -65,7 +65,8 @@ CCHDR = exiv2.hpp \ version.hpp # Add library C++ source files to this list -CCSRC = basicio.cpp \ +CCSRC = asfvideo.cpp \ + basicio.cpp \ bmpimage.cpp \ canonmn.cpp \ convert.cpp \ @@ -84,6 +85,7 @@ CCSRC = basicio.cpp \ jp2image.cpp \ jpgimage.cpp \ makernote.cpp \ + matroskavideo.cpp \ metadatum.cpp \ minoltamn.cpp \ mrwimage.cpp \ @@ -99,7 +101,9 @@ endif CCSRC += preview.cpp \ properties.cpp \ psdimage.cpp \ + quicktimevideo.cpp \ rafimage.cpp \ + riffvideo.cpp \ rw2image.cpp \ samsungmn.cpp \ sigmamn.cpp \ diff --git a/src/asfvideo.cpp b/src/asfvideo.cpp new file mode 100644 index 00000000..7e160c61 --- /dev/null +++ b/src/asfvideo.cpp @@ -0,0 +1,746 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004-2012 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/* + File: asfvideo.cpp + Version: $Rev$ + Author(s): Abhinav Badola for GSoC 2012 (AB) + History: 08-Aug-12, AB: created + Credits: See header file + */ +// ***************************************************************************** +#include "rcsid_int.hpp" +EXIV2_RCSID("@(#) $Id$") + +// ***************************************************************************** +// included header files +#include "asfvideo.hpp" +#include "futils.hpp" +#include "basicio.hpp" +#include "tags.hpp" +#include "tags_int.hpp" +#include "types.hpp" +#include "riffvideo.hpp" +#include "convert.hpp" + +// + standard includes +#include +#include +#include + +// ***************************************************************************** +// class member definitions +namespace Exiv2 { + namespace Internal { + + /*! + Tag Look-up list for ASF Type Video Files + Associates the GUID of a Tag with its Tag Name(i.e. Human Readable Form) + Tags have been diferentiated into Various Categories. + The categories have been listed above the Tag Groups + + */ + extern const TagVocabulary GUIDReferenceTags[] = { + /// Top-level ASF object GUIDS + { "75B22630-668E-11CF-A6D9-00AA0062CE6C", "Header" }, + { "75B22636-668E-11CF-A6D9-00AA0062CE6C", "Data" }, + { "33000890-E5B1-11CF-89F4-00A0C90349CB", "Simple_Index" }, + { "D6E229D3-35DA-11D1-9034-00A0C90349BE", "Index" }, + { "FEB103F8-12AD-4C64-840F-2A1D2F7AD48C", "Media_Index" }, + { "3CB73FD0-0C4A-4803-953D-EDF7B6228F0C", "Timecode_Index" }, + + /// Header Object GUIDs + { "8CABDCA1-A947-11CF-8EE4-00C00C205365", "File_Properties" }, + { "B7DC0791-A9B7-11CF-8EE6-00C00C205365", "Stream_Properties" }, + { "5FBF03B5-A92E-11CF-8EE3-00C00C205365", "Header_Extension" }, + { "86D15240-311D-11D0-A3A4-00A0C90348F6", "Codec_List" }, + { "1EFB1A30-0B62-11D0-A39B-00A0C90348F6", "Script_Command" }, + { "F487CD01-A951-11CF-8EE6-00C00C205365", "Marker" }, + { "D6E229DC-35DA-11D1-9034-00A0C90349BE", "Bitrate_Mutual_Exclusion" }, + { "75B22635-668E-11CF-A6D9-00AA0062CE6C", "Error_Correction" }, + { "75B22633-668E-11CF-A6D9-00AA0062CE6C", "Content_Description" }, + { "D2D0A440-E307-11D2-97F0-00A0C95EA850", "Extended_Content_Description" }, + { "2211B3FA-BD23-11D2-B4B7-00A0C955FC6E", "Content_Branding" }, + { "7BF875CE-468D-11D1-8D82-006097C9A2B2", "Stream_Bitrate_Properties" }, + { "2211B3FB-BD23-11D2-B4B7-00A0C955FC6E", "Content_Encryption" }, + { "298AE614-2622-4C17-B935-DAE07EE9289C", "Extended_Content_Encryption" }, + { "2211B3FC-BD23-11D2-B4B7-00A0C955FC6E", "Digital_Signature" }, + { "1806D474-CADF-4509-A4BA-9AABCB96AAE8", "Padding" }, + + /// Header Extension Object GUIDs + { "14E6A5CB-C672-4332-8399-A96952065B5A", "Extended_Stream_Properties" }, + { "A08649CF-4775-4670-8A16-6E35357566CD", "Advanced_Mutual_Exclusion" }, + { "D1465A40-5A79-4338-B71B-E36B8FD6C249", "Group_Mutual_Exclusion" }, + { "D4FED15B-88D3-454F-81F0-ED5C45999E24", "Stream_Prioritization" }, + { "A69609E6-517B-11D2-B6AF-00C04FD908E9", "Bandwidth_Sharing" }, + { "7C4346A9-EFE0-4BFC-B229-393EDE415C85", "Language_List" }, + { "C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA", "Metadata" }, + { "44231C94-9498-49D1-A141-1D134E457054", "Metadata_Library" }, + { "D6E229DF-35DA-11D1-9034-00A0C90349BE", "Index_Parameters" }, + { "6B203BAD-3F11-48E4-ACA8-D7613DE2CFA7", "Media_Index_Parameters" }, + { "F55E496D-9797-4B5D-8C8B-604DFE9BFB24", "Timecode_Index_Parameters" }, + { "26F18B5D-4584-47EC-9F5F-0E651F0452C9", "Compatibility" }, + { "43058533-6981-49E6-9B74-AD12CB86D58C", "Advanced_Content_Encryption" }, + + /// Stream Properties Object Stream Type GUIDs + { "F8699E40-5B4D-11CF-A8FD-00805F5C442B", "Audio_Media" }, + { "BC19EFC0-5B4D-11CF-A8FD-00805F5C442B", "Video_Media" }, + { "59DACFC0-59E6-11D0-A3AC-00A0C90348F6", "Command_Media" }, + { "B61BE100-5B4E-11CF-A8FD-00805F5C442B", "JFIF_Media" }, + { "35907DE0-E415-11CF-A917-00805F5C442B", "Degradable_JPEG_Media" }, + { "91BD222C-F21C-497A-8B6D-5AA86BFC0185", "File_Transfer_Media" }, + { "3AFB65E2-47EF-40F2-AC2C-70A90D71D343", "Binary_Media" }, + + /// Web stream Type-Specific Data GUIDs + { "776257D4-C627-41CB-8F81-7AC7FF1C40CC", "Web_Stream_Media_Subtype" }, + { "DA1E6B13-8359-4050-B398-388E965BF00C", "Web_Stream_Format" }, + + /// Stream Properties Object Error Correction Type GUIDs + { "20FB5700-5B55-11CF-A8FD-00805F5C442B", "No_Error_Correction" }, + { "BFC3CD50-618F-11CF-8BB2-00AA00B4E220", "Audio_Spread" }, + + /// Header Extension Object GUIDs + { "ABD3D211-A9BA-11cf-8EE6-00C00C205365", "Reserved_1" }, + + /// Advanced Content Encryption Object System ID GUIDs + { "7A079BB6-DAA4-4e12-A5CA-91D38DC11A8D", "Content_Encryption_System_Windows_Media_DRM_Network_Devices" }, + + /// Codec List Object GUIDs + { "86D15241-311D-11D0-A3A4-00A0C90348F6", "Reserved_2" }, + + /// Script Command Object GUIDs + { "4B1ACBE3-100B-11D0-A39B-00A0C90348F6", "Reserved_3" }, + + /// Marker Object GUIDs + { "4CFEDB20-75F6-11CF-9C0F-00A0C90349CB", "Reserved_4" }, + + /// Mutual Exclusion Object Exclusion Type GUIDs + { "D6E22A00-35DA-11D1-9034-00A0C90349BE", "Mutex_Language" }, + { "D6E22A01-35DA-11D1-9034-00A0C90349BE", "Mutex_Bitrate" }, + { "D6E22A02-35DA-11D1-9034-00A0C90349BE", "Mutex_Unknown" }, + + /// Bandwidth Sharing Object GUIDs + { "AF6060AA-5197-11D2-B6AF-00C04FD908E9", "Bandwidth_Sharing_Exclusive" }, + { "AF6060AB-5197-11D2-B6AF-00C04FD908E9", "Bandwidth_Sharing_Partial" }, + + /// Standard Payload Extension System GUIDs + { "399595EC-8667-4E2D-8FDB-98814CE76C1E", "Payload_Extension_System_Timecode" }, + { "E165EC0E-19ED-45D7-B4A7-25CBD1E28E9B", "Payload_Extension_System_File_Name" }, + { "D590DC20-07BC-436C-9CF7-F3BBFBF1A4DC", "Payload_Extension_System_Content_Type" }, + { "1B1EE554-F9EA-4BC8-821A-376B74E4C4B8", "Payload_Extension_System_Pixel_Aspect_Ratio" }, + { "C6BD9450-867F-4907-83A3-C77921B733AD", "Payload_Extension_System_Sample_Duration" }, + { "6698B84E-0AFA-4330-AEB2-1C0A98D7A44D", "Payload_Extension_System_Encryption_Sample_ID" }, + { "00E1AF06-7BEC-11D1-A582-00C04FC29CFB", "Payload_Extension_System_Degradable_JPEG" } + }; + + //! Audio codec type-specific data in ASF + extern const TagDetails audioCodec[] = { + { 0x161, "Windows Media Audio (7, 8, and 9 Series)" }, + { 0x162, "Windows Media Audio 9 Professional" }, + { 0x163, "Windows Media Audio 9 Lossless" }, + { 0x7A21, "GSM-AMR (CBR, no SID)" }, + { 0x7A22, "GSM-AMR (VBR including SID)" } + }; + + extern const TagDetails filePropertiesTags[] = { + { 7, "Xmp.video.FileLength" }, + { 6, "Xmp.video.CreationDate" }, + { 5, "Xmp.video.DataPackets" }, + { 4, "Xmp.video.Duration" }, + { 3, "Xmp.video.SendDuration" }, + { 2, "Xmp.video.Preroll" }, + { 1, "Xmp.video.MaxBitRate" } + }; + + extern const TagDetails contentDescriptionTags[] = { + { 1, "Xmp.video.Title" }, + { 2, "Xmp.video.Author" }, + { 3, "Xmp.video.Copyright" }, + { 4, "Xmp.video.Description" }, + { 5, "Xmp.video.Rating" } + }; + + /*! + @brief Function used to read data from data buffer, reads 16-bit character + array and stores it in std::string object. + @param buf Exiv2 data buffer, which stores the information + @return Returns std::string object . + */ + std::string toString16(Exiv2::DataBuf& buf) + { + std::ostringstream os; char t; + + for(int i = 0; i <= buf.size_; i += 2 ) { + t = buf.pData_[i] + 16 * buf.pData_[i + 1]; + if(t == 0) { + if(i) + os << '\0'; + break; + } + os<< t; + } + return os.str(); + } + + /*! + @brief Function used to check equality of two Tags (ignores case). + @param str1 char* Pointer to First Tag + @param str2 char* Pointer to Second Tag + @return Returns true if both are equal. + */ + bool compareTag(const char* str1, const char* str2) { + if ( strlen(str1) != strlen(str2)) + return false; + + for ( uint64_t i = 0 ; i < strlen(str1); ++i ) + if (tolower(str1[i]) != tolower(str2[i])) + return false; + + return true; + } + + /*! + @brief Function used to convert a decimal number to its Hexadecimal + equivalent, then parsed into a character + @param n Integer which is to be parsed as Hexadecimal character + @return Return a Hexadecimal number, in character + */ + char returnHEX(int n) { + if(n >= 0 && n <= 9) + return (char)(n + 48); + else + return (char)(n + 55); + } + + /*! + @brief Function used to calulate GUID, Tags comprises of 16 bytes. + The Buffer contains the Tag in Binary Form. The information is then + parsed into a character array GUID. + */ + void getGUID (byte buf[], char GUID[]) { + int i; + for (i = 0; i < 4; ++i) { + GUID[(3 - i) * 2] = returnHEX(buf[i] / 0x10); + GUID[(3 - i) * 2 + 1] = returnHEX(buf[i] % 0x10); + } + for (i = 4; i < 6; ++i) { + GUID[(9 - i) * 2 + 1] = returnHEX(buf[i] / 0x10); + GUID[(9 - i) * 2 + 2] = returnHEX(buf[i] % 0x10); + } + for (i = 6; i < 8; ++i) { + GUID[(14 - i) * 2] = returnHEX(buf[i] / 0x10); + GUID[(14 - i) * 2 + 1] = returnHEX(buf[i] % 0x10); + } + for (i = 8; i < 10; ++i) { + GUID[ i * 2 + 3] = returnHEX(buf[i] / 0x10); + GUID[ i * 2 + 4] = returnHEX(buf[i] % 0x10); + } + for (i = 10; i < 16; ++i) { + GUID[ i * 2 + 4] = returnHEX(buf[i] / 0x10); + GUID[ i * 2 + 5] = returnHEX(buf[i] % 0x10); + } + GUID[36] = '\0'; GUID[8] = GUID[13] = GUID[18] = GUID[23] = '-'; + } + + /*! + @brief Function used to check if data stored in buf is equivalent to + ASF Header Tag's GUID. + @param buf Exiv2 byte buffer + @return Returns true if the buffer data is equivalent to Header GUID. + */ + bool isASFType (byte buf[]) { + + if(buf[0] == 0x30 && buf[1] == 0x26 && buf[2] == 0xb2 && buf[3] == 0x75 && + buf[4] == 0x8e && buf[5] == 0x66 && buf[6] == 0xcf && buf[7] == 0x11 && + buf[8] == 0xa6 && buf[9] == 0xd9 && buf[10] == 0x00 && buf[11] == 0xaa && + buf[12] == 0x00 && buf[13] == 0x62 && buf[14] == 0xce && buf[15] == 0x6c ) + return true; + + return false; + } + + //! Function used to convert buffer data into 64-bit Integer, information stored in littleEndian format + uint64_t getUint64_t(Exiv2::DataBuf& buf) { + uint64_t temp = 0; + + for(int i = 0; i < 8; ++i) + temp = temp + buf.pData_[i]*(pow(256,i)); + + return temp; + } + +}} // namespace Internal, Exiv2 + +namespace Exiv2 { + + using namespace Exiv2::Internal; + + AsfVideo::AsfVideo(BasicIo::AutoPtr io) + : Image(ImageType::asf, mdNone, io) + { + } // AsfVideo::AsfVideo + + std::string AsfVideo::mimeType() const + { + return "video/asf"; + } + + void AsfVideo::writeMetadata() + { + } + + void AsfVideo::readMetadata() + { + if (io_->open() != 0) throw Error(9, io_->path(), strError()); + + // Ensure that this is the correct image type + if (!isAsfType(*io_, false)) { + if (io_->error() || io_->eof()) throw Error(14); + throw Error(3, "ASF"); + } + + IoCloser closer(*io_); + clearMetadata(); + continueTraversing_ = true; + io_->seek(0, BasicIo::beg); + height_ = width_ = 1; + + xmpData_["Xmp.video.FileSize"] = (double)io_->size()/(double)1048576; + xmpData_["Xmp.video.FileName"] = io_->path(); + xmpData_["Xmp.video.MimeType"] = mimeType(); + + while (continueTraversing_) decodeBlock(); + + aspectRatio(); + } // AsfVideo::readMetadata + + void AsfVideo::decodeBlock() + { + const long bufMinSize = 8; + DataBuf buf(bufMinSize); + unsigned long size = 0; + buf.pData_[8] = '\0' ; + const TagVocabulary* tv; + uint64_t cur_pos = io_->tell(); + + byte guidBuf[16]; + io_->read(guidBuf, 16); + + if(io_->eof()) { + continueTraversing_ = false; + return; + } + + char GUID[33] = ""; + + getGUID(guidBuf, GUID); + tv = find( GUIDReferenceTags, GUID); + + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 8); + size = getUint64_t(buf); + + if(tv) + tagDecoder(tv,size-24); + else + io_->seek(cur_pos + size, BasicIo::beg); + + localPosition_ = io_->tell(); + } // AsfVideo::decodeBlock + + void AsfVideo::tagDecoder(const TagVocabulary *tv, uint64_t size) + { + uint64_t cur_pos = io_->tell(); + DataBuf buf(1000); + unsigned long count = 0, tempLength = 0; + buf.pData_[4] = '\0' ; + Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpSeq); + + if(compareTag( exvGettext(tv->label_), "Header")) { + localPosition_ = 0; + io_->read(buf.pData_, 4); + io_->read(buf.pData_, 2); + + while(localPosition_ < cur_pos + size) decodeBlock(); + } + + else if(compareTag( exvGettext(tv->label_), "File_Properties")) + fileProperties(); + + else if(compareTag( exvGettext(tv->label_), "Stream_Properties")) + streamProperties(); + + else if(compareTag( exvGettext(tv->label_), "Metadata")) + metadataHandler(1); + + else if(compareTag( exvGettext(tv->label_), "Extended_Content_Description")) + metadataHandler(2); + + else if(compareTag( exvGettext(tv->label_), "Metadata_Library")) + metadataHandler(3); + + else if(compareTag( exvGettext(tv->label_), "Codec_List")) + codecList(); + + else if(compareTag( exvGettext(tv->label_), "Content_Description")) + contentDescription(size); + + else if(compareTag( exvGettext(tv->label_), "Extended_Stream_Properties")) + extendedStreamProperties(size); + + else if(compareTag( exvGettext(tv->label_), "Header_Extension")) { + localPosition_ = 0; + headerExtension(size); + } + + else if(compareTag( exvGettext(tv->label_), "Language_List")) { + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 2); + count = Exiv2::getUShort(buf.pData_, littleEndian); + + while(count--) { + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 1); tempLength = (int)buf.pData_[0]; + + io_->read(buf.pData_, tempLength); + v->read(toString16(buf)); + } + xmpData_.add(Exiv2::XmpKey("Xmp.video.TrackLang"), v.get()); + } + + io_->seek(cur_pos + size, BasicIo::beg); + localPosition_ = io_->tell(); + } // AsfVideo::tagDecoder + + void AsfVideo::extendedStreamProperties(uint64_t size) + { + uint64_t cur_pos = io_->tell(), avgTimePerFrame = 0; + DataBuf buf(8); + static int previousStream; + io_->seek(cur_pos + 48, BasicIo::beg); + + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 2); + streamNumber_ = Exiv2::getUShort(buf.pData_, littleEndian); + + io_->read(buf.pData_, 2); + io_->read(buf.pData_, 8); + avgTimePerFrame = getUint64_t(buf); + + if(previousStream < streamNumber_ && avgTimePerFrame != 0) + xmpData_["Xmp.video.FrameRate"] = (double)10000000/(double)avgTimePerFrame; + + previousStream = streamNumber_; + io_->seek(cur_pos + size, BasicIo::beg); + } // AsfVideo::extendedStreamProperties + + void AsfVideo::contentDescription(uint64_t size) + { + long pos = io_->tell(); + long length[5]; + const TagDetails* td; + + for (int i = 0 ; i < 5 ; ++i) { + byte buf[2]; + io_->read(buf, 2); + length[i] = (long)buf[0] + 16 * (long)buf[1]; + } + + for (int i = 0 ; i < 5 ; ++i) { + DataBuf buf(length[i]); + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, length[i]); + td = find(contentDescriptionTags, i + 1); + std::string str((const char*)buf.pData_, length[i]); + if (convertStringCharset(str, "UCS-2LE", "UTF-8")) { + xmpData_[td->label_] = str; + } + else { + xmpData_[td->label_] = toString16(buf); + } + } + io_->seek(pos + size, BasicIo::beg); + } // AsfVideo::contentDescription + + void AsfVideo::streamProperties() + { + DataBuf buf(20); + buf.pData_[8] = '\0' ; + byte guidBuf[16]; int stream = 0; + io_->read(guidBuf, 16); + char streamType[33] = ""; + Exiv2::RiffVideo *test = NULL; + + getGUID(guidBuf, streamType); + const TagVocabulary* tv; + tv = find( GUIDReferenceTags, streamType); + io_->read(guidBuf, 16); + + if(compareTag( exvGettext(tv->label_), "Audio_Media")) + stream = 1; + else if(compareTag( exvGettext(tv->label_), "Video_Media")) + stream = 2; + + io_->read(buf.pData_, 8); + if(stream == 2) + xmpData_["Xmp.video.TimeOffset"] = getUint64_t(buf); + else if(stream == 1) + xmpData_["Xmp.audio.TimeOffset"] = getUint64_t(buf); + + io_->read(buf.pData_, 8); + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 1); + streamNumber_ = (int)buf.pData_[0] & 127; + + io_->read(buf.pData_, 5); + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 2); + long temp = Exiv2::getUShort(buf.pData_, littleEndian); + + if(stream == 2) { + xmpData_["Xmp.video.Width"] = temp; + width_ = temp; + } + else if(stream == 1) { + xmpData_["Xmp.audio.Codec"] = test->printAudioEncoding(temp); + } + + io_->read(buf.pData_, 2); + temp = Exiv2::getUShort(buf.pData_, littleEndian); + if(stream == 1) + xmpData_["Xmp.audio.ChannelType"] = temp; + + io_->read(buf.pData_, 4); + temp = Exiv2::getULong(buf.pData_, littleEndian); + + if(stream == 2) { + xmpData_["Xmp.video.Height"] = temp; + height_ = temp; + } + else if(stream == 1) { + xmpData_["Xmp.audio.SampleRate"] = temp; + } + } // AsfVideo::streamProperties + + void AsfVideo::codecList() + { + DataBuf buf(200); + io_->read(buf.pData_, 16); + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 4); + int codecCount = Exiv2::getULong(buf.pData_, littleEndian), descLength = 0, codecType = 0; + + while(codecCount--) { + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 2); + codecType = Exiv2::getUShort(buf.pData_, littleEndian); + + io_->read(buf.pData_, 2); + descLength = Exiv2::getUShort(buf.pData_, littleEndian) * 2; + io_->read(buf.pData_, descLength); + + if(codecType == 1) + xmpData_["Xmp.video.Codec"] = toString16(buf); + else if(codecType == 2) + xmpData_["Xmp.audio.Compressor"] = toString16(buf); + + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 2); + descLength = Exiv2::getUShort(buf.pData_, littleEndian) * 2; + io_->read(buf.pData_, descLength); + + if(codecType == 1) + xmpData_["Xmp.video.CodecDescription"] = toString16(buf); + else if(codecType == 2) + xmpData_["Xmp.audio.CodecDescription"] = toString16(buf); + + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 2); + descLength = Exiv2::getUShort(buf.pData_, littleEndian); + io_->read(buf.pData_, descLength); + } + } // AsfVideo::codecList + + void AsfVideo::headerExtension(uint64_t size) + { + uint64_t cur_pos = io_->tell(); + DataBuf buf(20); + io_->read(buf.pData_, 18); + buf.pData_[4] = '\0' ; + io_->read(buf.pData_, 4); + + while(localPosition_ < cur_pos + size) decodeBlock(); + + io_->seek(cur_pos + size, BasicIo::beg); + } // AsfVideo::headerExtension + + void AsfVideo::metadataHandler(int meta) + { + DataBuf buf(500); + io_->read(buf.pData_, 2); + int recordCount = Exiv2::getUShort(buf.pData_, littleEndian), nameLength = 0, dataLength = 0, dataType = 0; + Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpSeq); + byte guidBuf[16]; char fileID[33] = ""; + + while(recordCount--) { + std::memset(buf.pData_, 0x0, buf.size_); + + if(meta == 1 || meta == 3) { + io_->read(buf.pData_, 4); + io_->read(buf.pData_, 2); + nameLength = Exiv2::getUShort(buf.pData_, littleEndian); + io_->read(buf.pData_, 2); + dataType = Exiv2::getUShort(buf.pData_, littleEndian); + io_->read(buf.pData_, 4); + dataLength = Exiv2::getULong(buf.pData_, littleEndian); + + io_->read(buf.pData_, nameLength); + v->read(toString16(buf)); + if(dataType == 6) { + io_->read(guidBuf, 16); + getGUID(guidBuf, fileID); + } + else + io_->read(buf.pData_, dataLength); + } + + else if(meta == 2) { + io_->read(buf.pData_, 2); + nameLength = Exiv2::getUShort(buf.pData_, littleEndian); + io_->read(buf.pData_, nameLength); + v->read(toString16(buf)); + + io_->read(buf.pData_, 2); + dataType = Exiv2::getUShort(buf.pData_, littleEndian); + + io_->read(buf.pData_, 2); + dataLength = Exiv2::getUShort(buf.pData_, littleEndian); + io_->read(buf.pData_, dataLength); + } + + if(dataType == 0) { // Unicode String + v->read(toString16(buf)); + } + else if(dataType == 2 || dataType == 5) { // 16-bit Unsigned Integer + v->read( Exiv2::toString( Exiv2::getUShort(buf.pData_, littleEndian))); + } + else if(dataType == 3) { // 32-bit Unsigned Integer + v->read( Exiv2::toString( Exiv2::getULong( buf.pData_, littleEndian))); + } + else if(dataType == 4) { // 64-bit Unsigned Integer + v->read(Exiv2::toString(getUint64_t(buf))); + } + else if(dataType == 6) { // 128-bit GUID + v->read(Exiv2::toString(fileID)); + } + else { // Byte array + v->read( Exiv2::toString(buf.pData_)); + } + } + + if(meta == 1) { + xmpData_.add(Exiv2::XmpKey("Xmp.video.Metadata"), v.get()); + } + else if(meta == 2) { + xmpData_.add(Exiv2::XmpKey("Xmp.video.ExtendedContentDescription"), v.get()); + } + else { + xmpData_.add(Exiv2::XmpKey("Xmp.video.MetadataLibrary"), v.get()); + } + } // AsfVideo::metadataHandler + + void AsfVideo::fileProperties() + { + DataBuf buf(8); + buf.pData_[8] = '\0' ; + + byte guidBuf[16]; + io_->read(guidBuf, 16); + char fileID[33] = ""; int count = 7; + getGUID(guidBuf, fileID); + xmpData_["Xmp.video.FileID"] = fileID; + + const TagDetails* td; + + while(count--) { + td = find(filePropertiesTags , (count + 1)); + io_->read(buf.pData_, 8); + + if(count == 0) { + buf.pData_[4] = '\0' ; + io_->read(buf.pData_, 4); io_->read(buf.pData_, 4); + } + + if(count == 3 || count == 2) { + xmpData_[exvGettext(td->label_)] = getUint64_t(buf) / 10000; + } + else { + xmpData_[exvGettext(td->label_)] = getUint64_t(buf); + } + } + } // AsfVideo::fileProperties + + void AsfVideo::aspectRatio() + { + //TODO - Make a better unified method to handle all cases of Aspect Ratio + + double aspectRatio = (double)width_ / (double)height_; + aspectRatio = floor(aspectRatio*10) / 10; + xmpData_["Xmp.video.AspectRatio"] = aspectRatio; + + if(aspectRatio == 1.3) xmpData_["Xmp.video.AspectRatio"] = "4:3"; + else if(aspectRatio == 1.7) xmpData_["Xmp.video.AspectRatio"] = "16:9"; + else if(aspectRatio == 1.0) xmpData_["Xmp.video.AspectRatio"] = "1:1"; + else if(aspectRatio == 1.6) xmpData_["Xmp.video.AspectRatio"] = "16:10"; + else if(aspectRatio == 2.2) xmpData_["Xmp.video.AspectRatio"] = "2.21:1"; + else if(aspectRatio == 2.3) xmpData_["Xmp.video.AspectRatio"] = "2.35:1"; + else if(aspectRatio == 1.2) xmpData_["Xmp.video.AspectRatio"] = "5:4"; + else xmpData_["Xmp.video.AspectRatio"] = aspectRatio; + } // AsfVideo::aspectRatio + + + Image::AutoPtr newAsfInstance(BasicIo::AutoPtr io, bool /*create*/) + { + Image::AutoPtr image(new AsfVideo(io)); + if (!image->good()) { + image.reset(); + } + return image; + } + + bool isAsfType(BasicIo& iIo, bool advance) + { + const int32_t len = 16; + byte buf[len]; + iIo.read(buf, len); + + if (iIo.error() || iIo.eof()) { + return false; + } + + bool matched = isASFType(buf); + if (!advance || !matched) { + iIo.seek(0, BasicIo::beg); + } + + return matched; + } + +} // namespace Exiv2 diff --git a/src/asfvideo.hpp b/src/asfvideo.hpp new file mode 100644 index 00000000..ee2fd95a --- /dev/null +++ b/src/asfvideo.hpp @@ -0,0 +1,185 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004-2012 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/*! + @file asfvideo.hpp + @brief An Image subclass to support ASF video files + @version $Rev$ + @author Abhinav Badola for GSoC 2012 + mail.abu.to@gmail.com + @date 08-Aug-12, AB: created + */ +#ifndef ASFVIDEO_HPP +#define ASFVIDEO_HPP + +// ***************************************************************************** +// included header files +#include "exif.hpp" +#include "image.hpp" +#include "tags_int.hpp" + +// ***************************************************************************** +// namespace extensions +using namespace Exiv2::Internal; + +namespace Exiv2 { + +// ***************************************************************************** +// class definitions + + // Add ASF to the supported image formats + namespace ImageType { + const int asf = 24; //!< Treating asf as an image type> + } + + /*! + @brief Class to access ASF video files. + */ + class EXIV2API AsfVideo:public Image + { + public: + //! @name Creators + //@{ + /*! + @brief Constructor for a ASF video. Since the constructor + can not return a result, callers should check the good() method + after object construction to determine success or failure. + @param io An auto-pointer that owns a BasicIo instance used for + reading and writing image metadata. \b Important: The constructor + takes ownership of the passed in BasicIo instance through the + auto-pointer. Callers should not continue to use the BasicIo + instance after it is passed to this method. Use the Image::io() + method to get a temporary reference. + */ + AsfVideo(BasicIo::AutoPtr io); + //@} + + //! @name Manipulators + //@{ + void readMetadata(); + void writeMetadata(); + //@} + + //! @name Accessors + //@{ + std::string mimeType() const; + //@} + + protected: + /*! + @brief Check for a valid tag and decode the block at the current IO + position. Calls tagDecoder() or skips to next tag, if required. + */ + void decodeBlock(); + /*! + @brief Interpret tag information, and call the respective function + to save it in the respective XMP container. Decodes a Tag + Information and saves it in the respective XMP container, if + the block size is small. + @param tv Pointer to current tag, + @param size Size of the data block used to store Tag Information. + */ + void tagDecoder(const TagVocabulary* tv, uint64_t size); + /*! + @brief Interpret File_Properties tag information, and save it in + the respective XMP container. + */ + void fileProperties(); + /*! + @brief Interpret Stream_Properties tag information, and save it + in the respective XMP container. + */ + void streamProperties(); + /*! + @brief Interpret Codec_List tag information, and save it in + the respective XMP container. + */ + void codecList(); + /*! + @brief Interpret Content_Description tag information, and save it + in the respective XMP container. + @param size Size of the data block used to store Tag Data. + */ + void contentDescription(uint64_t size); + /*! + @brief Interpret Extended_Stream_Properties tag information, and + save it in the respective XMP container. + @param size Size of the data block used to store Tag Data. + */ + void extendedStreamProperties(uint64_t size); + /*! + @brief Interpret Header_Extension tag information, and save it in + the respective XMP container. + @param size Size of the data block used to store Tag Data. + */ + void headerExtension(uint64_t size); + /*! + @brief Interpret Metadata, Extended_Content_Description, + Metadata_Library tag information, and save it in the respective + XMP container. + @param meta A default integer which helps to overload the function + for various Tags that have a similar method of decoding. + */ + void metadataHandler(int meta = 1); + /*! + @brief Calculates Aspect Ratio of a video, and stores it in the + respective XMP container. + */ + void aspectRatio(); + + private: + //! @name NOT Implemented + //@{ + //! Copy constructor + AsfVideo(const AsfVideo& rhs); + //! Assignment operator + AsfVideo& operator=(const AsfVideo& rhs); + //@} + + private: + //! Variable to check the end of metadata traversing. + bool continueTraversing_; + //! Variable which stores current position of the read pointer. + uint64_t localPosition_; + //! Variable which stores current stream being processsed. + int streamNumber_; + //! Variable to store height and width of a video frame. + uint64_t height_, width_; + + }; //Class AsfVideo + +// ***************************************************************************** +// template, inline and free functions + + // These could be static private functions on Image subclasses but then + // ImageFactory needs to be made a friend. + /*! + @brief Create a new AsfVideo instance and return an auto-pointer to it. + Caller owns the returned object and the auto-pointer ensures that + it will be deleted. + */ + EXIV2API Image::AutoPtr newAsfInstance(BasicIo::AutoPtr io, bool create); + + //! Check if the file iIo is a Windows Asf Video. + EXIV2API bool isAsfType(BasicIo& iIo, bool advance); + +} // namespace Exiv2 + +#endif // #ifndef ASFVIDEO_HPP_ diff --git a/src/canonmn.cpp b/src/canonmn.cpp index 83e1eeb5..61029fc5 100644 --- a/src/canonmn.cpp +++ b/src/canonmn.cpp @@ -173,6 +173,13 @@ namespace Exiv2 { { 0x80000261, N_("EOS 50D") }, { 0x80000270, N_("EOS Rebel T2i / 550D / Kiss X4") }, { 0x80000281, N_("EOS-1D Mark IV") }, + { 0x80000285, N_("EOS 5D Mark III") }, + { 0x80000286, N_("EOS Rebel T3i / 600D / Kiss X5") }, + { 0x80000287, N_("EOS 60D") }, + { 0x80000288, N_("EOS Rebel T3 / 1100D / Kiss X50") }, + { 0x80000297, N_("WFT-E2 II") }, + { 0x80000298, N_("WFT-E4 II") }, + { 0x80000301, N_("EOS Rebel T4i / 650D / Kiss X6i") }, }; //! SerialNumberFormat, tag 0x0015 @@ -653,11 +660,16 @@ namespace Exiv2 { { 249, "Canon EF 800mm f/5.6L IS" }, { 250, "Canon EF 24 f/1.4L II" }, { 251, "Canon EF 70-200mm f/2.8L IS II USM" }, + { 252, "Canon EF 70-200mm f/2.8L IS II USM + 1.4x" }, + { 253, "Canon EF 70-200mm f/2.8L IS II USM + 2x" }, { 254, "Canon EF 100mm f/2.8L Macro IS USM" }, { 488, "Canon EF-S 15-85mm f/3.5-5.6 IS USM" }, { 489, "Canon EF 70-300mm f/4-5.6L IS USM" }, { 490, "Canon EF 8-15mm f/4L USM" }, - { 491, "Canon EF 300mm f/2.8L IS II USM" } + { 491, "Canon EF 300mm f/2.8L IS II USM" }, + { 494, "Canon EF 600mm f/4.0L IS II USM" }, + { 495, "Canon EF 24-70mm f/2.8L II USM" }, + { 4144,"Canon EF 40mm f/2.8 STM" } }; //! A lens id and a pretty-print function for special treatment of the id. diff --git a/src/image.cpp b/src/image.cpp index 70ea3ff1..5cf3827a 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -60,6 +60,10 @@ EXIV2_RCSID("@(#) $Id$") #include "tgaimage.hpp" #include "bmpimage.hpp" #include "jp2image.hpp" +#include "matroskavideo.hpp" +#include "quicktimevideo.hpp" +#include "riffvideo.hpp" +#include "asfvideo.hpp" #include "rw2image.hpp" #include "pgfimage.hpp" #include "xmpsidecar.hpp" @@ -127,6 +131,10 @@ namespace { { ImageType::tga, newTgaInstance, isTgaType, amNone, amNone, amNone, amNone }, { ImageType::bmp, newBmpInstance, isBmpType, amNone, amNone, amNone, amNone }, { ImageType::jp2, newJp2Instance, isJp2Type, amReadWrite, amReadWrite, amReadWrite, amNone }, + { ImageType::qtime,newQTimeInstance,isQTimeType,amReadWrite, amNone, amNone, amNone }, + { ImageType::riff, newRiffInstance, isRiffType, amReadWrite, amNone, amNone, amNone }, + { ImageType::asf, newAsfInstance, isAsfType, amReadWrite, amNone, amNone, amNone }, + { ImageType::mkv, newMkvInstance, isMkvType, amReadWrite, amNone, amNone, amNone }, // End of list marker { ImageType::none, 0, 0, amNone, amNone, amNone, amNone } }; diff --git a/src/matroskavideo.cpp b/src/matroskavideo.cpp new file mode 100644 index 00000000..67cd4e8b --- /dev/null +++ b/src/matroskavideo.cpp @@ -0,0 +1,731 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004-2012 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/* + File: matroskavideo.cpp + Version: $Rev$ + Author(s): Abhinav Badola for GSoC 2012 (AB) + History: 18-Jun-12, AB: created + Credits: See header file + */ +// ***************************************************************************** +#include "rcsid_int.hpp" +EXIV2_RCSID("@(#) $Id$") + +// ***************************************************************************** +// included header files +#include "matroskavideo.hpp" +#include "futils.hpp" +#include "basicio.hpp" +#include "tags.hpp" +#include "tags_int.hpp" + +// + standard includes +#include + +// ***************************************************************************** +// class member definitions +namespace Exiv2 { + namespace Internal { + + //! List of composite tags. They are skipped and the child tags are read immediately + uint32_t compositeTagsList[] = { + 0x0000, 0x000e, 0x000f, 0x0020, 0x0026, 0x002e, 0x0036, + 0x0037, 0x003b, 0x005b, 0x0060, 0x0061, 0x0068, 0x05b9, + 0x0dbb, 0x1034, 0x1035, 0x1854, 0x21a7, 0x2240, 0x23c0, + 0x2624, 0x27c8, 0x2911, 0x2924, 0x2944, 0x2d80, 0x3373, + 0x35a1, 0x3e5b, 0x3e7b, + 0x14d9b74, 0x254c367, 0x549a966, 0x654ae6b, 0x8538067, + 0x941a469, 0xa45dfa3, 0xb538667, 0xc53bb6b, 0xf43b675 + }; + + //! List of tags which are ignored, i.e., tag and value won't be read + uint32_t ignoredTagsList[] = { + 0x0021, 0x0023, 0x0033, 0x0071, 0x0077, 0x006c, 0x0067, 0x007b, 0x02f2, 0x02f3, + 0x1031, 0x1032, 0x13ab, 0x13ac, 0x15ee, 0x23a2, 0x23c6, 0x2e67, 0x33a4, 0x33c5, + 0x3446, 0x2de7, 0x2df8, 0x26bf, 0x28ca, 0x3384, 0x13b8, 0x037e, 0x0485, 0x18d7, + 0x0005, 0x0009, 0x0011, 0x0012, 0x0016, 0x0017, 0x0018, 0x0022, 0x0024, 0x0025, + 0x0027, 0x002b, 0x002f, 0x003f, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x006a, + 0x006b, 0x006e, 0x007a, 0x007d, 0x0255, 0x3eb5, 0x3ea5, 0x3d7b, 0x33c4, 0x2fab, + 0x2ebc, 0x29fc, 0x29a5, 0x2955, 0x2933, 0x135f, 0x2922, 0x26a5, 0x26fc, 0x2532, + 0x23c9, 0x23c4, 0x23c5, 0x137f, 0x1378, 0x07e2, 0x07e3, 0x07e4, 0x0675, 0x05bc, + 0x05bd, 0x05db, 0x05dd, 0x0598, 0x050d, 0x0444, 0x037c, + + 0x3314f, 0x43a770, 0x1eb923, 0x1cb923, 0xeb524, 0x1c83ab, 0x1e83bb + }; + + /*! + Tag Look-up list for Matroska Type Video Files + The Tags have been categorized in 4 categories. Which are + mentioned as a comment in front of them. + s -- Tag to be Skipped + sd -- Tag to be Skipped along with its data + u -- Tag used directly for storing metadata + ui -- Tag used only internally + */ + extern const TagDetails matroskaTags[] = { + { 0x0000, "ChapterDisplay" }, //s + { 0x0003, "TrackType" }, //ui + { 0x0005, "ChapterString" }, //sd + { 0x0006, "VideoCodecID/AudioCodecID/CodecID" }, //ui + { 0x0008, "TrackDefault" }, //ui + { 0x0009, "ChapterTrackNumber" }, //sd + { 0x000e, "Slices" }, //s + { 0x000f, "ChapterTrack" }, //s + { 0x0011, "ChapterTimeStart" }, //sd + { 0x0012, "ChapterTimeEnd" }, //sd + { 0x0016, "CueRefTime" }, //sd + { 0x0017, "CueRefCluster" }, //sd + { 0x0018, "ChapterFlagHidden" }, //sd + { 0x001a, "Xmp.video.VideoScanTpye" }, //u + { 0x001b, "BlockDuration" }, //s + { 0x001c, "TrackLacing" }, //ui + { 0x001f, "Xmp.audio.ChannelType" }, //u + { 0x0020, "BlockGroup" }, //s + { 0x0021, "Block" }, //sd + { 0x0022, "BlockVirtual" }, //sd + { 0x0023, "SimpleBlock" }, //sd + { 0x0024, "CodecState" }, //sd + { 0x0025, "BlockAdditional" }, //sd + { 0x0026, "BlockMore" }, //s + { 0x0027, "Position" }, //sd + { 0x002a, "CodecDecodeAll" }, //ui + { 0x002b, "PrevSize" }, //sd + { 0x002e, "TrackEntry" }, //s + { 0x002f, "EncryptedBlock" }, //sd + { 0x0030, "Xmp.video.Width" }, //u + { 0x0033, "CueTime" }, //sd + { 0x0035, "Xmp.audio.SampleRate" }, //u + { 0x0036, "ChapterAtom" }, //s + { 0x0037, "CueTrackPositions" }, //s + { 0x0039, "TrackUsed" }, //ui + { 0x003a, "Xmp.video.Height" }, //u + { 0x003b, "CuePoint" }, //s + { 0x003f, "CRC-32" }, //sd + { 0x004b, "BlockAdditionalID" }, //sd + { 0x004c, "LaceNumber" }, //sd + { 0x004d, "FrameNumber" }, //sd + { 0x004e, "Delay" }, //sd + { 0x004f, "ClusterDuration" }, //sd + { 0x0057, "TrackNumber" }, //ui + { 0x005b, "CueReference" }, //s + { 0x0060, "Video" }, //s + { 0x0061, "Audio" }, //s + { 0x0067, "Timecode" }, //sd + { 0x0068, "TimeSlice" }, //s + { 0x006a, "CueCodecState" }, //sd + { 0x006b, "CueRefCodecState" }, //sd + { 0x006c, "Void" }, //sd + { 0x006e, "BlockAddID" }, //sd + { 0x0071, "CueClusterPosition" }, //sd + { 0x0077, "CueTrack" }, //sd + { 0x007a, "ReferencePriority" }, //sd + { 0x007b, "ReferenceBlock" }, //sd + { 0x007d, "ReferenceVirtual" }, //sd + { 0x0254, "Xmp.video.ContentCompressAlgo" }, //u + { 0x0255, "ContentCompressionSettings" }, //sd + { 0x0282, "Xmp.video.DocType" }, //u + { 0x0285, "Xmp.video.DocTypeReadVersion" }, //u + { 0x0286, "Xmp.video.EBMLVersion" }, //u + { 0x0287, "Xmp.video.DocTypeVersion" }, //u + { 0x02f2, "EBMLMaxIDLength" }, //sd + { 0x02f3, "EBMLMaxSizeLength" }, //sd + { 0x02f7, "Xmp.video.EBMLReadVersion" }, //u + { 0x037c, "ChapterLanguage" }, //sd + { 0x037e, "ChapterCountry" }, //sd + { 0x0444, "SegmentFamily" }, //sd + { 0x0461, "Xmp.video.DateUTC" }, //Date Time Original - measured in seconds relatively to Jan 01, 2001, 0:00:00 GMT+0h + { 0x047a, "Xmp.video.TagLanguage" }, //u + { 0x0484, "Xmp.video.TagDefault" }, //u + { 0x0485, "TagBinary" }, //sd + { 0x0487, "Xmp.video.TagString" }, //u + { 0x0489, "Xmp.video.Duration" }, //u + { 0x050d, "ChapterProcessPrivate" }, //sd + { 0x0598, "ChapterFlagEnabled" }, //sd + { 0x05a3, "Xmp.video.TagName" }, //u + { 0x05b9, "EditionEntry" }, //s + { 0x05bc, "EditionUID" }, //sd + { 0x05bd, "EditionFlagHidden" }, //sd + { 0x05db, "EditionFlagDefault" }, //sd + { 0x05dd, "EditionFlagOrdered" }, //sd + { 0x065c, "Xmp.video.AttachFileData" }, //u + { 0x0660, "Xmp.video.AttachFileMIME" }, //u + { 0x066e, "Xmp.video.AttachFileName" }, //u + { 0x0675, "AttachedFileReferral" }, //sd + { 0x067e, "Xmp.video.AttachFileDesc" }, //u + { 0x06ae, "Xmp.video.AttachFileUID" }, //u + { 0x07e1, "Xmp.video.ContentEncryptAlgo" }, //u + { 0x07e2, "ContentEncryptionKeyID" }, //sd + { 0x07e3, "ContentSignature" }, //sd + { 0x07e4, "ContentSignatureKeyID" }, //sd + { 0x07e5, "Xmp.video.ContentSignAlgo" }, //u + { 0x07e6, "Xmp.video.ContentSignHashAlgo" }, //u + { 0x0d80, "Xmp.video.MuxingApp" }, //u + { 0x0dbb, "Seek" }, //s + { 0x1031, "ContentEncodingOrder" }, //sd + { 0x1032, "ContentEncodingScope" }, //sd + { 0x1033, "Xmp.video.ContentEncodingType" }, //u + { 0x1034, "ContentCompression" }, //s + { 0x1035, "ContentEncryption" }, //s + { 0x135f, "CueRefNumber" }, //sd + { 0x136e, "Xmp.video.TrackName" }, //u + { 0x1378, "CueBlockNumber" }, //sd + { 0x137f, "TrackOffset" }, //sd + { 0x13ab, "SeekID" }, //sd + { 0x13ac, "SeekPosition" }, //sd + { 0x13b8, "Stereo3DMode" }, //sd + { 0x14aa, "Xmp.video.CropBottom" }, //ui + { 0x14b0, "Xmp.video.Width" }, //u + { 0x14b2, "Xmp.video.DisplayUnit" }, //u + { 0x14b3, "Xmp.video.AspectRatioType" }, //u + { 0x14ba, "Xmp.video.Height" }, //u + { 0x14bb, "Xmp.video.CropTop" }, //ui + { 0x14cc, "Xmp.video.CropLeft" }, //ui + { 0x14dd, "Xmp.video.CropRight" }, //ui + { 0x15aa, "TrackForced" }, //ui + { 0x15ee, "MaxBlockAdditionID" }, //sd + { 0x1741, "Xmp.video.WritingApp" }, //u + { 0x1854, "SilentTracks" }, //s + { 0x18d7, "SilentTrackNumber" }, //sd + { 0x21a7, "AttachedFile" }, //s + { 0x2240, "ContentEncoding" }, //s + { 0x2264, "Xmp.audio.BitsPerSample" }, //u + { 0x23a2, "CodecPrivate" }, //sd + { 0x23c0, "Targets" }, //s + { 0x23c3, "Xmp.video.PhysicalEquivalent" }, //u + { 0x23c4, "TagChapterUID" }, //sd + { 0x23c5, "TagTrackUID" }, //sd + { 0x23c6, "TagAttachmentUID" }, //sd + { 0x23c9, "TagEditionUID" }, //sd + { 0x23ca, "Xmp.video.TargetType" }, //u + { 0x2532, "SignedElement" }, //sd + { 0x2624, "TrackTranslate" }, //s + { 0x26a5, "TrackTranslateTrackID" }, //sd + { 0x26bf, "TrackTranslateCodec" }, //sd + { 0x26fc, "TrackTranslateEditionUID" }, //sd + { 0x27c8, "SimpleTag" }, //s + { 0x28ca, "TargetTypeValue" }, //sd + { 0x2911, "ChapterProcessCommand" }, //s + { 0x2922, "ChapterProcessTime" }, //sd + { 0x2924, "ChapterTranslate" }, //s + { 0x2933, "ChapterProcessData" }, //sd + { 0x2944, "ChapterProcess" }, //s + { 0x2955, "ChapterProcessCodecID" }, //sd + { 0x29a5, "ChapterTranslateID" }, //sd + { 0x29bf, "Xmp.video.TranslateCodec" }, //u + { 0x29fc, "ChapterTranslateEditionUID" }, //sd + { 0x2d80, "ContentEncodings" }, //s + { 0x2de7, "MinCache" }, //sd + { 0x2df8, "MaxCache" }, //sd + { 0x2e67, "ChapterSegmentUID" }, //sd + { 0x2ebc, "ChapterSegmentEditionUID" }, //sd + { 0x2fab, "TrackOverlay" }, //sd + { 0x3373, "Tag" }, //s + { 0x3384, "SegmentFileName" }, //sd + { 0x33a4, "SegmentUID" }, //sd + { 0x33c4, "ChapterUID" }, //sd + { 0x33c5, "TrackUID" }, //sd + { 0x3446, "TrackAttachmentUID" }, //sd + { 0x35a1, "BlockAdditions" }, //s + { 0x38b5, "Xmp.audio.OutputSampleRate" }, //u + { 0x3ba9, "Xmp.video.Title" }, //u + { 0x3d7b, "ChannelPositions" }, //sd + { 0x3e5b, "SignatureElements" }, //s + { 0x3e7b, "SignatureElementList" }, //s + { 0x3e8a, "Xmp.video.ContentSignAlgo" }, //u + { 0x3e9a, "Xmp.video.ContentSignHashAlgo" }, //u + { 0x3ea5, "SignaturePublicKey" }, //sd + { 0x3eb5, "Signature" }, //sd + { 0x2b59c, "TrackLanguage" }, //ui + { 0x3314f, "TrackTimecodeScale" }, //sd + { 0x383e3, "Xmp.video.FrameRate" }, //u + { 0x3e383, "VideoFrameRate/DefaultDuration" }, //ui + { 0x58688, "VideoCodecName/AudioCodecName/CodecName" }, //ui + { 0x6b240, "CodecDownloadURL" }, //ui + { 0xad7b1, "TimecodeScale" }, //ui + { 0xeb524, "ColorSpace" }, //sd + { 0xfb523, "Xmp.video.OpColor" }, //u + { 0x1a9697, "CodecSettings" }, //ui + { 0x1b4040, "CodecInfoURL" }, //ui + { 0x1c83ab, "PrevFileName" }, //sd + { 0x1cb923, "PrevUID" }, //sd + { 0x1e83bb, "NextFileName" }, //sd + { 0x1eb923, "NextUID" }, //sd + { 0x43a770, "Chapters" }, //sd + { 0x14d9b74, "SeekHead" }, //s + { 0x254c367, "Tags" }, //s + { 0x549a966, "Info" }, //s + { 0x654ae6b, "Tracks" }, //s + { 0x8538067, "SegmentHeader" }, //s + { 0x941a469, "Attachments" }, //s + { 0xa45dfa3, "EBMLHeader" }, //s + { 0xb538667, "SignatureSlot" }, //s + { 0xc53bb6b, "Cues" }, //s + { 0xf43b675, "Cluster" }, //s + }; + + extern const TagDetails matroskaTrackType[] = { + { 0x1, "Video" }, + { 0x2, "Audio" }, + { 0x3, "Complex" }, + { 0x10, "Logo" }, + { 0x11, "Subtitle" }, + { 0x12, "Buttons" }, + { 0x20, "Control" } + }; + + extern const TagDetails compressionAlgorithm[] = { + { 0, "zlib " }, + { 1, "bzlib" }, + { 2, "lzo1x" }, + { 3, "Header Stripping" } + }; + + extern const TagDetails audioChannels[] = { + { 1, "Mono" }, + { 2, "Stereo" }, + { 5, "5.1 Surround Sound" }, + { 7, "7.1 Surround Sound" } + }; + + extern const TagDetails displayUnit[] = { + { 0x0, "Pixels" }, + { 0x1, "cm" }, + { 0x2, "inches" } + }; + + extern const TagDetails encryptionAlgorithm[] = { + { 0, "Not Encrypted" }, + { 1, "DES" }, + { 2, "3DES" }, + { 3, "Twofish" }, + { 4, "Blowfish" }, + { 5, "AES" } + }; + + extern const TagDetails chapterPhysicalEquivalent[] = { + { 10, "Index" }, + { 20, "Track" }, + { 30, "Session" }, + { 40, "Layer" }, + { 50, "Side" }, + { 60, "CD / DVD" }, + { 70, "Set / Package" }, + }; + + extern const TagDetails encodingType[] = { + { 0, "Compression" }, + { 1, "Encryption" } + }; + + extern const TagDetails videoScanType[] = { + { 0, "Progressive" }, + { 1, "Interlaced" } + }; + + extern const TagDetails chapterTranslateCodec[] = { + { 0, "Matroska Script" }, + { 1, "DVD Menu" } + }; + + extern const TagDetails aspectRatioType[] = { + { 0, "Free Resizing" }, + { 1, "Keep Aspect Ratio" }, + { 2, "Fixed" } + }; + + extern const TagDetails contentSignatureAlgorithm[] = { + { 0, "Not Signed" }, + { 1, "RSA" } + }; + + extern const TagDetails contentSignatureHashAlgorithm[] = { + { 0, "Not Signed" }, + { 1, "SHA1-160" }, + { 2, "MD5" } + }; + + extern const TagDetails trackEnable[] = { + { 0x1, "Xmp.video.Enabled" }, + { 0x2, "Xmp.audio.Enabled" }, + { 0x11, "Xmp.video.SubTEnabled" } + }; + + extern const TagDetails defaultOn[] = { + { 0x1, "Xmp.video.DefaultOn" }, + { 0x2, "Xmp.audio.DefaultOn" }, + { 0x11, "Xmp.video.SubTDefaultOn" } + }; + + extern const TagDetails trackForced[] = { + { 0x1, "Xmp.video.TrackForced" }, + { 0x2, "Xmp.audio.TrackForced" }, + { 0x11, "Xmp.video.SubTTrackForced" } + }; + + extern const TagDetails trackLacing[] = { + { 0x1, "Xmp.video.TrackLacing" }, + { 0x2, "Xmp.audio.TrackLacing" }, + { 0x11, "Xmp.video.SubTTrackLacing" } + }; + + extern const TagDetails codecDecodeAll[] = { + { 0x1, "Xmp.video.CodecDecodeAll" }, + { 0x2, "Xmp.audio.CodecDecodeAll" }, + { 0x11, "Xmp.video.SubTCodecDecodeAll" } + }; + + extern const TagDetails codecDownloadUrl[] = { + { 0x1, "Xmp.video.CodecDownloadUrl" }, + { 0x2, "Xmp.audio.CodecDownloadUrl" }, + { 0x11, "Xmp.video.SubTCodecDownloadUrl" } + }; + + extern const TagDetails codecSettings[] = { + { 0x1, "Xmp.video.CodecSettings" }, + { 0x2, "Xmp.audio.CodecSettings" }, + { 0x11, "Xmp.video.SubTCodecSettings" } + }; + + extern const TagDetails trackCodec[] = { + { 0x1, "Xmp.video.Codec" }, + { 0x2, "Xmp.audio.Compressor" }, + { 0x11, "Xmp.video.SubTCodec" } + }; + + extern const TagDetails trackLanguage[] = { + { 0x1, "Xmp.video.TrackLang" }, + { 0x2, "Xmp.audio.TrackLang" }, + { 0x11, "Xmp.video.SubTLang" } + }; + + extern const TagDetails codecInfo[] = { + { 0x1, "Xmp.video.CodecInfo" }, + { 0x2, "Xmp.audio.CodecInfo" }, + { 0x11, "Xmp.video.SubTCodecInfo" } + }; + + extern const TagDetails streamRate[] = { + { 0x1, "Xmp.video.FrameRate" }, + { 0x2, "Xmp.audio.DefaultDuration" } + }; + + /*! + @brief Function used to calulate Tags, Tags may comprise of more than + one byte, that is why two buffers are to be provided. + The first buffer calculates size of the Tag and the second buffer + is used to calculate the rest of the Tag. + Returns Tag Value in unsinged long. + */ + unsigned long returnTagValue(byte b, Exiv2::DataBuf& buf2, int n ) { + long temp = 0; + long reg1 = 0; + reg1 = (b & (int)(pow(2,8-n)-1)); + + for(int i = n-2; i >= 0; i--) { + temp = temp + buf2.pData_[i]*(pow(256,n-i-2)); + } + temp += reg1 * pow(256,n-1); + return temp; + } + + //! Function used to convert buffer data into numerical information, information stored in BigEndian format + int64_t returnValue(Exiv2::DataBuf& buf, int n ) { + int64_t temp = 0; + + for(int i = n-1; i >= 0; i--) { + temp = temp + buf.pData_[i]*(pow(256,n-i-1)); + } + return temp; + } + +}} // namespace Internal, Exiv2 + +namespace Exiv2 { + + using namespace Exiv2::Internal; + + MatroskaVideo::MatroskaVideo(BasicIo::AutoPtr io) + : Image(ImageType::mkv, mdNone, io) + { + } // MatroskaVideo::MatroskaVideo + + std::string MatroskaVideo::mimeType() const + { + return "video/matroska"; + } + + void MatroskaVideo::writeMetadata() + { + } + + void MatroskaVideo::readMetadata() + { + if (io_->open() != 0) throw Error(9, io_->path(), strError()); + + // Ensure that this is the correct image type + if (!isMkvType(*io_, false)) { + if (io_->error() || io_->eof()) throw Error(14); + throw Error(3, "Matroska"); + } + + IoCloser closer(*io_); + clearMetadata(); + height_ = width_ = 1; + + xmpData_["Xmp.video.FileName"] = io_->path(); + xmpData_["Xmp.video.FileSize"] = (double)io_->size()/(double)1048576; + xmpData_["Xmp.video.MimeType"] = mimeType(); + + while (continueTraversing_) decodeBlock(); + + aspectRatio(); + } // MatroskaVideo::readMetadata + + void MatroskaVideo::decodeBlock() + { + const long bufMinSize = 200; + DataBuf buf2(bufMinSize); + + byte b; + io_->read(&b, 1); + + if(io_->eof()) { + continueTraversing_ = false; + return; + } + + long sz = findBlockSize(b); + if (sz > 0) io_->read(buf2.pData_, sz - 1); + + const TagDetails* td; + td = find(matroskaTags, returnTagValue(b, buf2, sz)); + + if(td->val_ == 0xc53bb6b || td->val_ == 0xf43b675) { + continueTraversing_ = false; + return; + } + + bool skip = find(compositeTagsList, (uint32_t)td->val_); + bool ignore = find(ignoredTagsList, (uint32_t)td->val_); + + io_->read(&b, 1); + sz = findBlockSize(b); + + if (sz > 0) io_->read(buf2.pData_, sz - 1); + long size = returnTagValue(b, buf2, sz); + + if (skip && !ignore) return; + + if (ignore || size > bufMinSize) { + io_->seek(size, BasicIo::cur); + return; + } + + DataBuf buf(bufMinSize); + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, size); + contentManagement(td, buf, size); + } // MatroskaVideo::decodeBlock + + void MatroskaVideo::contentManagement(const TagDetails* td, Exiv2::DataBuf& buf, long size) + { + int64_t duration_in_ms = 0; + static double time_code_scale = 1.0, temp = 0; + static long stream = 0, track_count = 0; + char str[4] = "No"; + const TagDetails* internal_td = NULL; + + switch (td->val_) { + + case 0x0282: case 0x0d80: case 0x1741: case 0x3ba9: case 0x066e: case 0x0660: + case 0x065c: case 0x067e: case 0x047a: case 0x0487: case 0x05a3: case 0x136e: + case 0x23ca: case 0xeb524: + xmpData_[exvGettext(td->label_)] = buf.pData_; + break; + + case 0x0030: case 0x003a: case 0x0287: case 0x14b0: case 0x14ba: case 0x285: + case 0x06ae: case 0x0286: case 0x02f7: case 0x2264: case 0x14aa: case 0x14bb: + case 0x14cc: case 0x14dd: + xmpData_[exvGettext(td->label_)] = returnValue(buf, size); + + if(td->val_ == 0x0030 || td->val_ == 0x14b0) + width_ = returnValue(buf, size); + else if(td->val_ == 0x003a || td->val_ == 0x14ba) + height_ = returnValue(buf, size); + break; + + case 0x001a: case 0x001f: case 0x0254: case 0x07e1: case 0x07e5: case 0x07e6: + case 0x1033: case 0x14b2: case 0x14b3: case 0x23c3: case 0x29bf: case 0x3e8a: + case 0x3e9a: + switch (td->val_) { + case 0x001a: internal_td = find(videoScanType, returnValue(buf, size)); break; + case 0x001f: internal_td = find(audioChannels, returnValue(buf, size)); break; + case 0x0254: internal_td = find(compressionAlgorithm, returnValue(buf, size)); break; + case 0x07e1: internal_td = find(encryptionAlgorithm, returnValue(buf, size)); break; + case 0x1033: internal_td = find(encodingType, returnValue(buf, size)); break; + case 0x3e8a: + case 0x07e5: internal_td = find(contentSignatureAlgorithm, returnValue(buf, size)); break; + case 0x3e9a: + case 0x07e6: internal_td = find(contentSignatureHashAlgorithm, returnValue(buf, size)); break; + case 0x14b2: internal_td = find(displayUnit, returnValue(buf, size)); break; + case 0x14b3: internal_td = find(aspectRatioType, returnValue(buf, size)); break; + case 0x23c3: internal_td = find(chapterPhysicalEquivalent, returnValue(buf, size)); break; + case 0x29bf: internal_td = find(chapterTranslateCodec, returnValue(buf, size)); break; + } + if(internal_td) + xmpData_[exvGettext(td->label_)] = exvGettext(internal_td->label_); + break; + + case 0x0035: case 0x38b5: + xmpData_[exvGettext(td->label_)] = Exiv2::getFloat(buf.pData_, bigEndian); + break; + + case 0x0039: case 0x0008: case 0x15aa: case 0x001c: case 0x002a: case 0x1a9697: + case 0x0484: + if (returnValue(buf, size)) strcpy(str, "Yes"); + switch (td->val_) { + case 0x0039: internal_td = find(trackEnable, stream); break; + case 0x0008: internal_td = find(defaultOn, stream); break; + case 0x15aa: internal_td = find(trackForced, stream); break; + case 0x001c: internal_td = find(trackLacing, stream); break; + case 0x002a: internal_td = find(codecDecodeAll, stream); break; + case 0x1a9697: internal_td = find(codecSettings, stream); break; + case 0x0484: internal_td = td; break; + } + if (internal_td) xmpData_[exvGettext(internal_td->label_)] = str; + break; + + case 0x0006: case 0x2b59c: case 0x58688: case 0x6b240: case 0x1b4040: + switch (td->val_) { + case 0x0006: internal_td = find(trackCodec, stream); break; + case 0x2b59c: internal_td = find(trackLanguage, stream); break; + case 0x58688: internal_td = find(codecInfo, stream); break; + case 0x6b240: + case 0x1b4040: internal_td = find(codecDownloadUrl, stream); break; + } + if (internal_td) xmpData_[exvGettext(internal_td->label_)] = buf.pData_; + break; + + case 0x0489: case 0x0461: + switch (td->val_) { + case 0x0489: + if(size <= 4) { + duration_in_ms = Exiv2::getFloat(buf.pData_, bigEndian) * time_code_scale * 1000; + } + else { + duration_in_ms = Exiv2::getDouble(buf.pData_, bigEndian) * time_code_scale * 1000; + } + break; + case 0x0461: duration_in_ms = returnValue(buf, size)/1000000000; break; + } + xmpData_[exvGettext(td->label_)] = duration_in_ms; + break; + + case 0x0057: + track_count++; + xmpData_["Xmp.video.TotalStream"] = track_count; + break; + + case 0xad7b1: + time_code_scale = (double)returnValue(buf, size)/(double)1000000000; + xmpData_["Xmp.video.TimecodeScale"] = time_code_scale; + break; + + case 0x0003: + internal_td = find(matroskaTrackType, returnValue(buf, size)); + stream = internal_td->val_; + break; + + case 0x3e383: case 0x383e3: + internal_td = find(streamRate, stream); + if (returnValue(buf, size)) { + switch (stream) { + case 1: temp = (double)1000000000/(double)returnValue(buf, size); break; + case 2: temp = returnValue(buf, size)/1000; break; + } + if (internal_td) xmpData_[exvGettext(internal_td->label_)] = temp; + } + else + if (internal_td) xmpData_[exvGettext(internal_td->label_)] = "Variable Bit Rate"; + break; + + default: + break; + } + } // MatroskaVideo::contentManagement + + void MatroskaVideo::aspectRatio() + { + //TODO - Make a better unified method to handle all cases of Aspect Ratio + + double aspectRatio = (double)width_ / (double)height_; + aspectRatio = floor(aspectRatio*10) / 10; + xmpData_["Xmp.video.AspectRatio"] = aspectRatio; + + if(aspectRatio == 1.3) xmpData_["Xmp.video.AspectRatio"] = "4:3"; + else if(aspectRatio == 1.7) xmpData_["Xmp.video.AspectRatio"] = "16:9"; + else if(aspectRatio == 1.0) xmpData_["Xmp.video.AspectRatio"] = "1:1"; + else if(aspectRatio == 1.6) xmpData_["Xmp.video.AspectRatio"] = "16:10"; + else if(aspectRatio == 2.2) xmpData_["Xmp.video.AspectRatio"] = "2.21:1"; + else if(aspectRatio == 2.3) xmpData_["Xmp.video.AspectRatio"] = "2.35:1"; + else if(aspectRatio == 1.2) xmpData_["Xmp.video.AspectRatio"] = "5:4"; + else xmpData_["Xmp.video.AspectRatio"] = aspectRatio; + } // MatroskaVideo::aspectRatio + + long MatroskaVideo::findBlockSize(byte b) + { + if (b & 128) return 1; + else if (b & 64) return 2; + else if (b & 32) return 3; + else if (b & 16) return 4; + else if (b & 8) return 5; + else if (b & 4) return 6; + else if (b & 2) return 7; + else if (b & 1) return 8; + else return 0; + } // MatroskaVideo::findBlockSize + + Image::AutoPtr newMkvInstance(BasicIo::AutoPtr io, bool /*create*/) + { + Image::AutoPtr image(new MatroskaVideo(io)); + if (!image->good()) { + image.reset(); + } + return image; + } + + bool isMkvType(BasicIo& iIo, bool advance) + { + bool result = true; + byte tmpBuf[4]; + iIo.read(tmpBuf, 4); + + if (iIo.error() || iIo.eof()) return false; + + if (0x1a != tmpBuf[0] || 0x45 != tmpBuf[1] || 0xdf != tmpBuf[2] || 0xa3 != tmpBuf[3]) { + result = false; + } + + if (!advance || !result ) iIo.seek(0, BasicIo::beg); + return result; + } + +} // namespace Exiv2 diff --git a/src/matroskavideo.hpp b/src/matroskavideo.hpp new file mode 100644 index 00000000..a13e3870 --- /dev/null +++ b/src/matroskavideo.hpp @@ -0,0 +1,145 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004-2012 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/*! + @file matroskavideo.hpp + @brief An Image subclass to support Matroska video files + @version $Rev$ + @author Abhinav Badola for GSoC 2012 + mail.abu.to@gmail.com + @date 18-Jun-12, AB: created + */ +#ifndef MATROSKAVIDEO_HPP_ +#define MATROSKAVIDEO_HPP_ + +// ***************************************************************************** +// included header files +#include "exif.hpp" +#include "image.hpp" +#include "tags_int.hpp" + +// ***************************************************************************** +// namespace extensions +namespace Exiv2 { + +// ***************************************************************************** +// class definitions + + // Add MKV to the supported image formats + namespace ImageType { + const int mkv = 21; //!< Treating mkv as an image type> + } + + /*! + @brief Class to access Matroska video files. + */ + class EXIV2API MatroskaVideo : public Image { + public: + //! @name Creators + //@{ + /*! + @brief Constructor for a Matroska video. Since the constructor + can not return a result, callers should check the good() method + after object construction to determine success or failure. + @param io An auto-pointer that owns a BasicIo instance used for + reading and writing image metadata. \b Important: The constructor + takes ownership of the passed in BasicIo instance through the + auto-pointer. Callers should not continue to use the BasicIo + instance after it is passed to this method. Use the Image::io() + method to get a temporary reference. + */ + MatroskaVideo(BasicIo::AutoPtr io); + //@} + + //! @name Manipulators + //@{ + void readMetadata(); + void writeMetadata(); + //@} + + //! @name Accessors + //@{ + std::string mimeType() const; + //@} + + protected: + /*! + @brief Function used to calulate the size of a block. + This information is only stored in one byte. + The size of the block is calculated by counting + the number of leading zeros in the binary code of the byte. + Size = (No. of leading zeros + 1) bytes + @param b The byte, which stores the information to calculate the size + @return Return the size of the block. + */ + long findBlockSize(byte b); + /*! + @brief Check for a valid tag and decode the block at the current IO position. + Calls contentManagement() or skips to next tag, if required. + */ + void decodeBlock(); + /*! + @brief Interpret tag information, and save it in the respective XMP container. + @param td Pointer to current tag, + @param buf Data buffer with the tag information. + @param size Size of buf. + */ + void contentManagement(const Exiv2::Internal::TagDetails* td, Exiv2::DataBuf& buf, long size); + /*! + @brief Calculates Aspect Ratio of a video, and stores it in the + respective XMP container. + */ + void aspectRatio(); + + private: + //! @name NOT Implemented + //@{ + //! Copy constructor + MatroskaVideo(const MatroskaVideo& rhs); + //! Assignment operator + MatroskaVideo& operator=(const MatroskaVideo& rhs); + //@} + + private: + //! Variable to check the end of metadata traversing. + bool continueTraversing_; + //! Variable to store height and width of a video frame. + uint64_t height_, width_; + + }; // class MatroskaVideo + +// ***************************************************************************** +// template, inline and free functions + + // These could be static private functions on Image subclasses but then + // ImageFactory needs to be made a friend. + /*! + @brief Create a new MatroskaVideo instance and return an auto-pointer to it. + Caller owns the returned object and the auto-pointer ensures that + it will be deleted. + */ + EXIV2API Image::AutoPtr newMkvInstance(BasicIo::AutoPtr io, bool create); + + //! Check if the file iIo is a Matroska Video. + EXIV2API bool isMkvType(BasicIo& iIo, bool advance); + +} // namespace Exiv2 + +#endif // #ifndef MATROSKAVIDEO_HPP_ diff --git a/src/properties.cpp b/src/properties.cpp index d63ecfa3..1e565747 100644 --- a/src/properties.cpp +++ b/src/properties.cpp @@ -96,6 +96,8 @@ namespace Exiv2 { extern const XmpPropertyInfo xmpMicrosoftPhotoRegionInfoInfo[]; extern const XmpPropertyInfo xmpMicrosoftPhotoRegionInfo[]; extern const XmpPropertyInfo xmpMWGRegionsInfo[]; + extern const XmpPropertyInfo xmpVideoInfo[]; + extern const XmpPropertyInfo xmpAudioInfo[]; extern const XmpNsInfo xmpNsInfo[] = { // Schemas - NOTE: Schemas which the XMP-SDK doesn't know must be registered in XmpParser::initialize - Todo: Automate this @@ -126,12 +128,15 @@ namespace Exiv2 { { "http://ns.microsoft.com/photo/1.2/t/RegionInfo#", "MPRI", xmpMicrosoftPhotoRegionInfoInfo, N_("Microsoft Photo RegionInfo schema")}, { "http://ns.microsoft.com/photo/1.2/t/Region#", "MPReg", xmpMicrosoftPhotoRegionInfo, N_("Microsoft Photo Region schema") }, { "http://www.metadataworkinggroup.com/schemas/regions/", "mwg-rs", xmpMWGRegionsInfo,N_("Metadata Working Group Regions schema") }, + { "http://www.video", "video", xmpVideoInfo, N_("XMP Extended Video schema") }, + { "http://www.audio", "audio", xmpAudioInfo, N_("XMP Extended Audio schema") }, + // Structures { "http://ns.adobe.com/xap/1.0/g/", "xapG", 0, N_("Colorant structure") }, { "http://ns.adobe.com/xap/1.0/sType/Dimensions#", "stDim", 0, N_("Dimensions structure") }, { "http://ns.adobe.com/xap/1.0/sType/Font#", "stFnt", 0, N_("Font structure") }, - { "http://ns.adobe.com/xap/1.0/g/img/", "xapGImg", 0, N_("Thumbnail structure") }, + { "http://ns.adobe.com/xap/1.0/g/img/", "xmpGImg", 0, N_("Thumbnail structure") }, { "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#", "stEvt", 0, N_("Resource Event structure") }, { "http://ns.adobe.com/xap/1.0/sType/ResourceRef#", "stRef", 0, N_("ResourceRef structure") }, { "http://ns.adobe.com/xap/1.0/sType/Version#", "stVer", 0, N_("Version structure") }, @@ -986,6 +991,390 @@ namespace Exiv2 { { 0, 0, 0, invalidTypeId, xmpInternal, 0 } }; + extern const XmpPropertyInfo xmpVideoInfo[] = { + { "Album", N_("Album"), "Text", xmpText, xmpExternal, N_("The name of the album.") }, + { "ArchivalLocation", N_("Archival Location"), "Text", xmpText, xmpExternal, N_("Information about the Archival Location.") }, + { "Arranger", N_("Arranger"), "Text", xmpText, xmpExternal, N_("Information about the Arranger.") }, + { "ArrangerKeywords", N_("Arranger Keywords"), "Text", xmpText, xmpExternal, N_("Information about the Arranger Keywords.") }, + { "Artist", N_("Artist"), "Text", xmpText, xmpExternal, N_("The name of the artist or artists.") }, + { "AspectRatio", N_("Video Aspect Ratio"), "Ratio", xmpText, xmpExternal, N_("Ratio of Width:Height, helps to determine how a video would be displayed on a screen") }, + { "AspectRatioType", N_("Video Aspect Ratio Type"), "Text", xmpText, xmpExternal, N_("Aspect Ratio Type. Eg - Free-Resizing or Fixed") }, + { "AttachFileData", N_("Attached File Data"), "Text", xmpText, xmpExternal, N_("Attached File Data") }, + { "AttachFileDesc", N_("Attached File Description"), "Text", xmpText, xmpExternal, N_("Attached File Description") }, + { "AttachFileMIME", N_("Attached File MIME Type"), "Text", xmpText, xmpExternal, N_("Attached File MIME Type") }, + { "AttachFileName", N_("Attached File Name"), "Text", xmpText, xmpExternal, N_("Attached File Name") }, + { "AttachFileUID", N_("Attached File UID"), "Integer", xmpText, xmpExternal, N_("Attached File Universal ID") }, + { "BaseURL", N_("Base URL"), "Text", xmpText, xmpExternal, N_("A C string that specifies a Base URL.") }, + { "BitDepth", N_("Bit Depth"), "Integer", xmpText, xmpExternal, N_("A 16-bit integer that indicates the pixel depth of the compressed image. Values of 1, 2, 4, 8 , 16, 24, and 32 indicate the depth of color images") }, + { "Brightness", N_("Brightness"), "Integer", xmpText, xmpExternal, N_("Brightness setting.") }, + { "CameraByteOrder", N_("Camera Byte Order"), "Text", xmpText, xmpExternal, N_("Byte Order used by the Video Capturing device.") }, + { "Cinematographer", N_("Video Cinematographer"), "Text", xmpText, xmpExternal, N_("The video Cinematographer information.") }, + { "Codec", N_("Video Codec"), "Text", xmpText, xmpExternal, N_("The video codec information. Informs about the encoding algorithm of video. Codec Info is required for video playback.") }, + { "CodecDecodeAll", N_("Video Codec Decode Info"), "Text", xmpText, xmpExternal, N_("Contains information the video Codec Decode All, i.e. Enabled/Disabled") }, + { "CodecDescription", N_("Video Codec Description"), "Text", xmpText, xmpExternal, N_("Contains description the codec.") }, + { "CodecInfo", N_("Video Codec Information"), "Text", xmpText, xmpExternal, N_("Contains information the codec needs before decoding can be started.") }, + { "CodecDownloadUrl", N_("Video Codec Download URL"), "Text", xmpText, xmpExternal, N_("Video Codec Download URL.") }, + { "CodecSettings", N_("Video Codec Settings"), "Text", xmpText, xmpExternal, N_("Contains settings the codec needs before decoding can be started.") }, + { "ColorMode", N_("Color Mode"), "Text", xmpText, xmpExternal, N_("Color Mode") }, + { "ColorNoiseReduction", N_("Color Noise Reduction"), "Integer", xmpText, xmpExternal, N_("\"Color Noise Reducton\" setting. Range 0 to +100.") }, + { "ColorSpace", N_("Video Color Space"), "closed Choice of Text", xmpText, xmpInternal, N_("The color space. One of: sRGB (used by Photoshop), CCIR-601 (used for NTSC), " + "CCIR-709 (used for HD).") }, + { "Comment", N_("Comment"), "Text", xmpText, xmpExternal, N_("Information about the Comment.") }, + { "Commissioned", N_("Commissioned"), "Text", xmpText, xmpExternal, N_("Commissioned.") }, + { "CompatibleBrands", N_("QTime Compatible FileType Brand"), "Text", xmpText, xmpExternal, N_("Other QuickTime Compatible FileType Brand") }, + { "Composer", N_("Composer"), "Text", xmpText, xmpExternal, N_("Information about the Composer.") }, + { "ComposerKeywords", N_("Composer Keywords"), "Text", xmpText, xmpExternal, N_("Information about the Composer Keywords.") }, + { "Compressor", N_("Compressor"), "Text", xmpText, xmpExternal, N_("Video Compression Library Used") }, + { "CompressorID", N_("Video Compressor ID"), "Text", xmpText, xmpExternal, N_("Video Compression ID of Technology/Codec Used") }, + { "CompressorVersion", N_("Compressor Version"), "Text", xmpText, xmpExternal, N_("Information about the Compressor Version.") }, + { "Container", N_("Container Type"), "Text", xmpText, xmpExternal, N_("Primary Metadata Container") }, + { "ContentCompressAlgo", N_("Content Compression Algorithm"), "Text", xmpText, xmpExternal, N_("Content Compression Algorithm. Eg: zlib") }, + { "ContentEncodingType", N_("Content Encoding Type"), "Text", xmpText, xmpExternal, N_("Content Encoding Type. Eg: Encryption or Compression") }, + { "ContentEncryptAlgo", N_("Content Encryption Algorithm"), "Text", xmpText, xmpExternal, N_("Content Encryption Algorithm. Eg: Blowfish") }, + { "ContentSignAlgo", N_("Content Signature Algorithm"), "Text", xmpText, xmpExternal, N_("Content Signature Algorithm. Eg: RSA") }, + { "ContentSignHashAlgo", N_("Content Sign Hash Algorithm"), "Text", xmpText, xmpExternal, N_("Content Signature Hash Algorithm. Eg: SHA1-160 or MD5") }, + { "Contrast", N_("Contrast"), "Closed Choice of Integer", xmpText, xmpInternal, N_("Indicates the direction of contrast processing applied by the camera.") }, + { "Copyright", N_("Copyright"), "Text", xmpText, xmpExternal, N_("Copyright, can be name of an organization or an individual.") }, + { "CostumeDesigner", N_("Costume Designer"), "Text", xmpText, xmpExternal, N_("Costume Designer associated with the video.") }, + { "Country", N_("Country"), "Text", xmpText, xmpExternal, N_("Name of the country where the video was created.") }, + { "CreationDate", N_("Creation Date"), "Integer", xmpText, xmpExternal, N_("Specifies the date and time of the initial creation of the file. The value is given as the " + "number of 100-nanosecond intervals since January 1, 1601, according to Coordinated Universal Time (Greenwich Mean Time).") }, + { "CropBottom", N_("Pixel Crop Bottom"), "Integer", xmpText, xmpExternal, N_("Number of Pixels to be cropped from the bottom.") }, + { "CropLeft", N_("Pixel Crop Left"), "Integer", xmpText, xmpExternal, N_("Number of Pixels to be cropped from the left.") }, + { "CropRight", N_("Pixel Crop Right"), "Integer", xmpText, xmpExternal, N_("Number of Pixels to be cropped from the right.") }, + { "Cropped", N_("Cropped"), "Integer", xmpText, xmpExternal, N_("Field that indicates if a video is cropped.") }, + { "CropTop", N_("Pixel Crop Top"), "Integer", xmpText, xmpExternal, N_("Number of Pixels to be cropped from the top.") }, + { "CurrentTime", N_("Current Time"), "Integer", xmpText, xmpExternal, N_("The time value for current time position within the movie.") }, + { "DataPackets", N_("Data Packets"), "Integer", xmpText, xmpExternal, N_("Specifies the number of Data Packet entries that exist within the Data Object.") }, + { "DateTimeOriginal", N_("Date and Time Original"), "Date", xmpText, xmpInternal, N_("Date and time when original video was generated, in ISO 8601 format. ") }, + { "DateTimeDigitized", N_("Date and Time Digitized"), "Date", xmpText, xmpInternal, N_("Date and time when video was stored as digital data, can be the same " + "as DateTimeOriginal if originally stored in digital form. Stored in ISO 8601 format.") }, + { "DateUTC", N_("Date-Time Original"), "Text", xmpText, xmpExternal, N_("Contains the production date") }, + { "DefaultOn", N_("Video Track Default On"), "Text", xmpText, xmpExternal, N_("Video Track Default On , i.e. Enabled/Disabled") }, + { "DigitalZoomRatio", N_("Digital Zoom Ratio"), "Rational", xmpText, xmpInternal, N_("Indicates the digital zoom ratio when the video was shot.") }, + { "Dimensions", N_("Dimensions"), "Text", xmpText, xmpExternal, N_("Information about the Dimensions of the video frame.") }, + { "Director", N_("Director"), "Text", xmpText, xmpExternal, N_("Information about the Director.") }, + { "DisplayUnit", N_("Video Display Unit"), "Text", xmpText, xmpExternal, N_("Video display unit. Eg - cm, pixels, inch") }, + { "DistributedBy", N_("Distributed By"), "Text", xmpText, xmpExternal, N_("Distributed By, i.e. name of person or organization.") }, + { "DocType", N_("Doc Type"), "Text", xmpText, xmpExternal, N_("Describes the contents of the file. In the case of a MATROSKA file, its value is 'matroska'") }, + { "DocTypeReadVersion", N_("Doc Type Read Version"), "Integer", xmpText, xmpExternal, N_("A Matroska video specific property, helps in determining the compatibility of file with a particular version of a video player") }, + { "DocTypeVersion", N_("Doc Type Version"), "Integer", xmpText, xmpExternal, N_("A Matroska video specific property, indicated the version of filetype, helps in determining the compatibility") }, + { "DotsPerInch", N_("Dots Per Inch"), "Integer", xmpText, xmpExternal, N_("Dots Per Inch") }, + { "duration", N_("Duration"), "Integer", xmpText, xmpExternal, N_("The duration of the media file. Measured in milli-seconds.") }, + { "EBMLReadVersion", N_("EBML Read Version"), "Integer", xmpText, xmpExternal, N_("Extensible Binary Meta Language Read Version") }, + { "EBMLVersion", N_("EBML Version"), "Integer", xmpText, xmpExternal, N_("Extensible Binary Meta Language Version") }, + { "Edit1", N_("Edit Block 1 / Language"), "Text", xmpText, xmpExternal, N_("Information about the Edit / Language.") }, + { "Edit2", N_("Edit Block 2 / Language"), "Text", xmpText, xmpExternal, N_("Information about the Edit / Language.") }, + { "Edit3", N_("Edit Block 3 / Language"), "Text", xmpText, xmpExternal, N_("Information about the Edit / Language.") }, + { "Edit4", N_("Edit Block 4 / Language"), "Text", xmpText, xmpExternal, N_("Information about the Edit / Language.") }, + { "Edit5", N_("Edit Block 5 / Language"), "Text", xmpText, xmpExternal, N_("Information about the Edit / Language.") }, + { "Edit6", N_("Edit Block 6 / Language"), "Text", xmpText, xmpExternal, N_("Information about the Edit / Language.") }, + { "Edit7", N_("Edit Block 7 / Language"), "Text", xmpText, xmpExternal, N_("Information about the Edit / Language.") }, + { "Edit8", N_("Edit Block 8 / Language"), "Text", xmpText, xmpExternal, N_("Information about the Edit / Language.") }, + { "Edit9", N_("Edit Block 9 / Language"), "Text", xmpText, xmpExternal, N_("Information about the Edit / Language.") }, + { "EditedBy", N_("Edited By"), "Text", xmpText, xmpExternal, N_("Edited By, i.e. name of person or organization.") }, + { "Enabled", N_("Video Track Enabled"), "Text", xmpText, xmpExternal, N_("Status of Video Track, i.e. Enabled/Disabled") }, + { "EncodedBy", N_("Encoded By"), "Text", xmpText, xmpExternal, N_("Encoded By, i.e. name of person or organization.") }, + { "Encoder", N_("Encoder"), "Text", xmpText, xmpExternal, N_("Information about the Encoder.") }, + { "EndTimecode", N_("End Timecode"), "Integer", xmpText, xmpExternal, N_("End Timecode") }, + { "Engineer", N_("Engineer"), "Text", xmpText, xmpExternal, N_("Engineer, in most cases name of person.") }, + { "Equipment", N_("Equipment"), "Text", xmpText, xmpExternal, N_("Information about the Equipment used for recording Video.") }, + { "ExposureCompensation", N_("Exposure Compensation"), "Text", xmpText, xmpExternal, N_("Exposure Compensation Information.") }, + { "ExposureProgram", N_("Exposure Program"), "Text", xmpText, xmpExternal, N_("Exposure Program Information.") }, + { "ExposureTime", N_("Exposure Time"), "Rational", xmpText, xmpInternal, N_("Exposure time in seconds.") }, + { "ExtendedContentDescription",N_("Extended Content Description"), "Text", xmpSeq, xmpExternal, N_("Extended Content Description,ususally found in ASF type files.") }, + { "FileDataRate", N_("File Data Rate"), "Rational", xmpText, xmpExternal, N_("The file data rate in megabytes per second. For example: \"36/10\" = 3.6 MB/sec") }, + { "FileID", N_("File ID"), "Text", xmpText, xmpExternal, N_("File ID.") }, + { "FileLength", N_("File Length"), "Integer", xmpText, xmpInternal, N_("File length.") }, + { "FileName", N_("File Name"), "Text", xmpText, xmpExternal, N_("File Name or Absolute File Path") }, + { "FileSize", N_("File Size"), "Integer", xmpText, xmpExternal, N_("File Size, in MB") }, + { "FileType", N_("File Type"), "Text", xmpText, xmpExternal, N_("Extension of File or Type of File") }, + { "FilterEffect", N_("Filter Effect"), "Text", xmpText, xmpExternal, N_("Filter Effect Settings Applied.") }, + { "FirmwareVersion", N_("Firmware Version"), "Text", xmpText, xmpExternal, N_("Firmware Version of the Camera/Video device.") }, + { "FNumber", N_("F Number"), "Rational", xmpText, xmpInternal, N_("F number. Camera Lens specific data.") }, + { "FocalLength", N_("Focal Length"), "Rational", xmpText, xmpInternal, N_("Focal length of the lens, in millimeters.") }, + { "FocusMode", N_("Focus Mode"), "Text", xmpText, xmpExternal, N_("Focus Mode of the Lens. Eg - AF for Auto Focus") }, + { "Format", N_("Format"), "Text", xmpText, xmpExternal, N_("Indication of movie format (computer-generated, digitized, and so on).") }, + { "FrameCount", N_("Frame Count"), "Integer", xmpText, xmpExternal, N_("Total number of frames in a video") }, + { "FrameHeight", N_("Frame Height"), "Integer", xmpText, xmpExternal, N_("Height of frames in a video") }, + { "FrameRate", N_("Video Frame Rate"), "Frames per Second", xmpText, xmpExternal, N_("Rate at which frames are presented in a video (Expressed in fps(Frames per Second))") }, + { "FrameSize", N_("Video Frame Size"), "Dimensions", xmpText, xmpExternal, N_("The frame size. For example: w:720, h: 480, unit:pixels") }, + { "FrameWidth", N_("Frame Width"), "Integer", xmpText, xmpExternal, N_("Width of frames in a video") }, + { "Genre", N_("Genre"), "Text", xmpText, xmpExternal, N_("The name of the genre.") }, + { "GPSAltitude", N_("GPS Altitude"), "Rational", xmpText, xmpInternal, N_("GPS tag 6, 0x06. Indicates altitude in meters.") }, + { "GPSAltitudeRef", N_("GPS Altitude Reference"), "Closed Choice of Integer", xmpText, xmpInternal, N_("GPS tag 5, 0x05. Indicates whether the altitude is above or below sea level.") }, + { "GPSCoordinates", N_("GPS Coordinates"), "Text", xmpText, xmpExternal, N_("Information about the GPS Coordinates.") }, + { "GPSDateStamp", N_("GPS Time Stamp"), "Date", xmpText, xmpInternal, N_("Date stamp of GPS data, ") }, + { "GPSImgDirection", N_("GPS Image Direction"), "Rational", xmpText, xmpInternal, N_("Direction of image when captured, values range from 0 to 359.99.") }, + { "GPSImgDirectionRef", N_("GPS Image Direction Reference"), "Closed Choice of Text", xmpText, xmpInternal, N_("Reference for image direction.") }, + { "GPSLatitude", N_("GPS Latitude"), "GPSCoordinate", xmpText, xmpInternal, N_("(North/South). Indicates latitude.") }, + { "GPSLongitude", N_("GPS Longitude"), "GPSCoordinate", xmpText, xmpInternal, N_("(East/West). Indicates longitude.") }, + { "GPSMapDatum", N_("GPS Map Datum"), "Text", xmpText, xmpInternal, N_("Geodetic survey data.") }, + { "GPSSatellites", N_("GPS Satellites"), "Text", xmpText, xmpInternal, N_("Satellite information, format is unspecified.") }, + { "GPSTimeStamp", N_("GPS Time Stamp"), "Date", xmpText, xmpInternal, N_("Time stamp of GPS data, ") }, + { "GPSVersionID", N_("GPS Version ID"), "Text", xmpText, xmpInternal, N_("A decimal encoding with period separators. ") }, + { "GraphicsMode", N_("Graphcs Mode"), "Text", xmpText, xmpExternal, N_("A 16-bit integer that specifies the transfer mode. The transfer mode specifies which Boolean" + "operation QuickDraw should performwhen drawing ortransferring an image fromone location to another.") }, + { "Grouping", N_("Grouping"), "Text", xmpText, xmpExternal, N_("Information about the Grouping.") }, + { "HandlerClass", N_("Handler Class"), "Text", xmpText, xmpExternal, N_("A four-character code that identifies the type of the handler. Only two values are valid for this field: 'mhlr' for media handlers and 'dhlr' for data handlers.") }, + { "HandlerDescription", N_("Handler Description"), "Text", xmpText, xmpExternal, N_("A (counted) string that specifies the name of the component—that is, the media handler used when this media was created..") }, + { "HandlerType", N_("Handler Type"), "Text", xmpText, xmpExternal, N_("A four-character code that identifies the type of the media handler or data handler.") }, + { "HandlerVendorID", N_("Handler Vendor ID"), "Text", xmpText, xmpExternal, N_("Component manufacturer.") }, + { "Height", N_("Video Height"), "Integer", xmpText, xmpExternal, N_("Video height in pixels") }, + { "HueAdjustment", N_("Hue Adjustment"), "Integer", xmpText, xmpExternal, N_("Hue Adjustment Settings Information.") }, + { "ImageLength", N_("Image Length"), "Integer", xmpText, xmpExternal, N_("Image Length, a property inherited from BitMap format") }, + { "InfoBannerImage", N_("Info Banner Image"), "Text", xmpText, xmpExternal, N_("Information Banner Image.") }, + { "InfoBannerURL", N_("Info Banner URL"), "Text", xmpText, xmpExternal, N_("Information Banner URL.") }, + { "Information", N_("Information"), "Text", xmpText, xmpExternal, N_("Additional Movie Information.") }, + { "InfoText", N_("Info Text"), "Text", xmpText, xmpExternal, N_("Information Text.") }, + { "InfoURL", N_("Info URL"), "Text", xmpText, xmpExternal, N_("Information URL.") }, + { "ISRCCode", N_("ISRC Code"), "Text", xmpText, xmpExternal, N_("Information about the ISRC Code.") }, + { "Junk", N_("Junk Data"), "Text", xmpText, xmpExternal, N_("Video Junk data") }, + { "Language", N_("Language"), "Text", xmpText, xmpExternal, N_("Language.") }, + { "Length", N_("Length"), "Integer", xmpText, xmpExternal, N_("The length of the media file.") }, + { "LensModel", N_("Lens Model"), "Text", xmpText, xmpExternal, N_("Lens Model.") }, + { "LensType", N_("Lens Type"), "Text", xmpText, xmpExternal, N_("Lens Type.") }, + { "Lightness", N_("Lightness"), "Text", xmpText, xmpExternal, N_("Lightness.") }, + { "LocationInfo", N_("Location Information"), "Text", xmpText, xmpExternal, N_("Location Information.") }, + { "LogoIconURL", N_("Logo Icon URL"), "Text", xmpText, xmpExternal, N_("A C string that specifies Logo Icon URL.") }, + { "LogoURL", N_("Logo URL"), "Text", xmpText, xmpExternal, N_("A C string that specifies a Logo URL.") }, + { "Lyrics", N_("Lyrics"), "Text", xmpText, xmpExternal, N_("Lyrics of a Song/Video.") }, + { "MajorBrand", N_("QTime Major FileType Brand"), "Text", xmpText, xmpExternal, N_("QuickTime Major File Type Brand") }, + { "Make", N_("Equipment Make"), "Text", xmpText, xmpExternal, N_("Manufacturer of recording equipment") }, + { "MakerNoteType", N_("Camera Maker Note Type"), "Text", xmpText, xmpExternal, N_("Maker Note Type of the camera.") }, + { "MakerNoteVersion", N_("Camera Maker Note Version"), "Text", xmpText, xmpExternal, N_("Maker Note Version of the camera.") }, + { "MakerURL", N_("Maker URL"), "Text", xmpText, xmpExternal, N_("Camera Manufacturer's URL.") }, + { "MaxApertureValue", N_("Maximum Aperture Value"), "Rational", xmpText, xmpInternal, N_("Smallest F number of lens, in APEX.") }, + { "MaxBitRate", N_("Maximum Bit Rate"), "Integer", xmpText, xmpExternal, N_("Specifies the maximum instantaneous bit rate in bits per second for the entire file. This shall equal the sum of the bit rates of the individual digital media streams.") }, + { "MaxDataRate", N_("Maximum Data Rate"), "kiloBytes per Second", xmpText, xmpExternal, N_("Peak rate at which data is presented in a video (Expressed in kB/s(kiloBytes per Second))") }, + { "MediaCreateDate", N_("Media Track Create Date"), "Integer", xmpText, xmpExternal, N_("A 32-bit integer that indicates (in seconds since midnight, January 1, 1904) when the media header was created.") }, + { "MediaDuration", N_("Media Track Duration"), "Integer", xmpText, xmpExternal, N_("A time value that indicates the duration of this media (in the movie’s time coordinate system).") }, + { "MediaHeaderVersion", N_("Media Header Version"), "Text", xmpText, xmpExternal, N_("A 1-byte specification of the version of this media header") }, + { "MediaLangCode", N_("Media Language Code"), "Integer", xmpText, xmpExternal, N_("A 16-bit integer that specifies the language code for this media.") }, + { "MediaModifyDate", N_("Media Track Modify Date"), "Integer", xmpText, xmpExternal, N_("A 32-bit integer that indicates (in seconds since midnight, January 1, 1904) when the media header was last modified.") }, + { "MediaTimeScale", N_("Media Time Scale"), "Integer", xmpText, xmpExternal, N_("A time value that indicates the time scale for this media—that is, the number of time units that pass per second in its time coordinate system." ) }, + { "Medium", N_("Medium"), "Text", xmpSeq, xmpExternal, N_("Medium.") }, + { "Metadata", N_("Metadata"), "Text", xmpSeq, xmpExternal, N_("An array of Unknown / Unregistered Metadata Tags and their values.") }, + { "MetadataLibrary", N_("Metadata Library"), "Text", xmpSeq, xmpExternal, N_("An array of Unregistered Metadata Library Tags and their values.") }, + { "MeteringMode", N_("Metering Mode"), "Closed Choice of Integer", xmpText, xmpInternal, N_("Metering mode.") }, + { "MicroSecPerFrame", N_("Micro Seconds Per Frame"), "Integer", xmpText, xmpExternal, N_("Number of micro seconds per frame, or frame rate") }, + { "MimeType", N_("Mime Type"), "Text", xmpText, xmpExternal, N_("Tells about the video format") }, + { "MinorVersion", N_("QTime Minor FileType Version"), "Text", xmpText, xmpExternal, N_("QuickTime Minor File Type Version") }, + { "Model", N_("Equipment Model"), "Text", xmpText, xmpExternal, N_("Model name or number of equipment.") }, + { "ModificationDate", N_("Modification Date-Time"), "Text", xmpText, xmpExternal, N_("Contains the modification date of the video") }, + { "MovieHeaderVersion", N_("Movie Header Version"), "Integer", xmpText, xmpExternal, N_("Movie Header Version") }, + { "MusicBy", N_("Music By"), "Text", xmpText, xmpExternal, N_("Music By, i.e. name of person or organization.") }, + { "MuxingApp", N_("Muxing App"), "Text", xmpText, xmpExternal, N_("Contains the name of the library that has been used to create the file (like ”libmatroska 0.7.0“)") }, + { "Name", N_("Name"), "Text", xmpText, xmpExternal, N_("Name of song or the event.") }, + { "NextTrackID", N_("Next Track ID"), "Integer", xmpText, xmpExternal, N_("A 32-bit integer that indicates a value to use for the track ID number of the next track added to this movie. Note that 0 is not a valid track ID value.") }, + { "NumOfColours", N_("Number Of Colours"), "Integer/Text", xmpText, xmpExternal, N_("Total number of colours used") }, + { "NumOfImpColours", N_("Number Of Important Colours"), "Integer/Text", xmpText, xmpExternal, N_("Number Of Important Colours, a property inherited from BitMap format") }, + { "NumOfParts", N_("Number Of Parts"), "Integer", xmpText, xmpExternal, N_("Total number of parts in the video.") }, + { "OpColor", N_("Operation Colours"), "Integer/Text", xmpText, xmpExternal, N_("Three 16-bit values that specify the red, green, and blue colors for the transfer mode operation indicated in the graphics mode field.") }, + { "Organization", N_("Organization"), "Text", xmpText, xmpExternal, N_("Name of organization associated with the video.") }, + { "Orientation", N_("Orientation"), "Closed Choice of Integer", xmpText, xmpInternal, N_("Video Orientation:" + "1 = Horizontal (normal) " + "2 = Mirror horizontal " + "3 = Rotate 180 " + "4 = Mirror vertical " + "5 = Mirror horizontal and rotate 270 CW " + "6 = Rotate 90 CW " + "7 = Mirror horizontal and rotate 90 CW " + "8 = Rotate 270 CW") }, + { "Part", N_("Part"), "Text", xmpText, xmpExternal, N_("Part.") }, + { "Performers", N_("Performers"), "Text", xmpText, xmpExternal, N_("Performers involved in the video.") }, + { "PerformerKeywords", N_("Performer Keywords"), "Text", xmpText, xmpExternal, N_("Performer Keywords.") }, + { "PerformerURL", N_("Performer URL"), "Text", xmpText, xmpExternal, N_("Performer's dedicated URL.") }, + { "PictureControlData", N_("Picture Control Data"), "Text", xmpText, xmpExternal, N_("Picture Control Data.") }, + { "PictureControlVersion", N_("Picture Control Version"), "Text", xmpText, xmpExternal, N_("Picture Control Data Version.") }, + { "PictureControlName", N_("Picture Control Name"), "Text", xmpText, xmpExternal, N_("Picture Control Name.") }, + { "PictureControlBase", N_("Picture Control Base"), "Text", xmpText, xmpExternal, N_("Picture Control Data Base.") }, + { "PictureControlAdjust", N_("Picture Control Adjust"), "Text", xmpText, xmpExternal, N_("Picture Control Adjust Information.") }, + { "PictureControlQuickAdjust",N_("Picture Control Quick Adjust"), "Text", xmpText, xmpExternal, N_("Picture Control Quick Adjustment Settings.") }, + { "PlaySelection", N_("Play Selection"), "Text", xmpText, xmpExternal, N_("Play Selection.") }, + { "PlayMode", N_("PlayMode"), "Text", xmpText, xmpExternal, N_("Information about the Play Mode.") }, + { "Producer", N_("Producer"), "Text", xmpText, xmpExternal, N_("Producer involved with the video.") }, + { "ProducerKeywords", N_("Producer Keywords"), "Text", xmpText, xmpExternal, N_("Information about the Producer Keywords.") }, + { "ProductionDesigner", N_("Production Designer"), "Text", xmpText, xmpExternal, N_("Information about the Production Designer.") }, + { "ProductionStudio", N_("Production Studio"), "Text", xmpText, xmpExternal, N_("Information about the Production Studio.") }, + { "Product", N_("Product"), "Text", xmpText, xmpExternal, N_("Product.") }, + { "PhysicalEquivalent", N_("Chapter Physical Equivalent"), "Text", xmpText, xmpExternal, N_("Contains the information of External media.") }, + { "PixelDepth", N_("Video Pixel Depth"), "closed Choice of Text", xmpText, xmpExternal, N_("The size in bits of each color component of a pixel. Standard Windows 32-bit " + "pixels have 8 bits per component. One of: 8Int, 16Int, 32Int, 32Float.") }, + { "PixelPerMeterX", N_("Pixels Per Meter X"), "Integer", xmpText, xmpExternal, N_("Pixels Per Meter X, a property inherited from BitMap format") }, + { "PixelPerMeterY", N_("Pixels Per Meter Y"), "Integer", xmpText, xmpExternal, N_("Pixels Per Meter Y, a property inherited from BitMap format") }, + { "Planes", N_("Planes"), "Integer", xmpText, xmpExternal, N_("The number of Image Planes in the video") }, + { "PosterTime", N_("Poster Time"), "Integer", xmpText, xmpExternal, N_("The time value of the time of the movie poster.") }, + { "PreferredRate", N_("Preferred Rate"), "Rational", xmpText, xmpExternal, N_("A 32-bit fixed-point number that specifies the rate at which to play this movie. A value of 1.0 indicates normal rate.") }, + { "PreferredVolume", N_("Preferred Volume"), "Rational", xmpText, xmpExternal, N_("A 16-bit fixed-point number that specifies how loud to play this movie’s sound. A value of 1.0 indicates full volume.") }, + { "Preroll", N_("Preroll"), "Integer", xmpText, xmpExternal, N_("Specifies the amount of time to buffer data before starting to play the file, in millisecond units. If this value is nonzero," + "the Play Duration field and all of the payload Presentation Time fields have been offset by this amount. Therefore, player software " + "must subtract the value in the preroll field from the play duration and presentation times to calculate their actual values.") }, + { "PreviewDuration", N_("Preview Duration"), "Integer", xmpText, xmpExternal, N_("The duration of the movie preview in movie time scale units") }, + { "PreviewTime", N_("Preview Time"), "Integer", xmpText, xmpExternal, N_("The time value in the movie at which the preview begins.") }, + { "ProducedBy", N_("Produced By"), "Text", xmpText, xmpExternal, N_("Produced By, i.e. name of person or organization.") }, + { "ProjectRef", N_("Project Reference"), "ProjectLink", xmpText, xmpExternal, N_("A reference to the project that created this file.") }, + { "Rate", N_("Rate"), "Integer", xmpText, xmpExternal, N_("Rate.") }, + { "Rated", N_("Rated"), "Text", xmpText, xmpExternal, N_("The age circle required for viewing the video.") }, + { "Rating", N_("Rating"), "Text", xmpText, xmpExternal, N_("Rating, eg. 7 or 8 (generally out of 10).") }, + { "RecordLabelName", N_("Record Label Name"), "Text", xmpText, xmpExternal, N_("Record Label Name, or the name of the organization recording the video.") }, + { "RecordLabelURL", N_("Record Label URL"), "Text", xmpText, xmpExternal, N_("Record Label URL.") }, + { "RecordingCopyright", N_("Recording Copyright"), "Text", xmpText, xmpExternal, N_("Recording Copyright.") }, + { "Requirements", N_("Requirements"), "Text", xmpText, xmpExternal, N_("Information about the Requirements.") }, + { "ResolutionUnit", N_("Resolution Unit"), "Closed Choice of Integer", xmpText, xmpInternal, N_("Unit used for XResolution and YResolution. Value is one of: 2 = inches; 3 = centimeters.") }, + { "RippedBy", N_("Ripped By"), "Text", xmpText, xmpExternal, N_("Ripped By, i.e. name of person or organization.") }, + { "Saturation", N_("Saturation"), "Closed Choice of Integer", xmpText, xmpInternal, N_("Indicates the direction of saturation processing applied by the camera.") }, + { "SecondaryGenre", N_("Secondary Genre"), "Text", xmpText, xmpExternal, N_("The name of the secondary genre..") }, + { "SelectionTime", N_("Selection Time"), "Integer", xmpText, xmpExternal, N_("The time value for the start time of the current selection.") }, + { "SelectionDuration", N_("Selection Duration"), "Integer", xmpText, xmpExternal, N_("The duration of the current selection in movie time scale units.") }, + { "SendDuration", N_("Send Duration"), "Integer", xmpText, xmpExternal, N_("Specifies the time needed to send the file in 100-nanosecond units. This value should " + "include the duration of the last packet in the content.") }, + { "Sharpness", N_("Sharpness"), "Integer", xmpText, xmpExternal, N_("\"Sharpness\" setting. Range 0 to +100.") }, + { "Software", N_("Software"), "Text", xmpText, xmpExternal, N_("Software used to generate / create Video data.") }, + { "SoftwareVersion", N_("Software Version"), "Text", xmpText, xmpExternal, N_("The Version of the software used.") }, + { "SongWriter", N_("Song Writer"), "Text", xmpText, xmpExternal, N_("The name of the song writer.") }, + { "SongWriterKeywords", N_("Song Writer Keywords"), "Text", xmpText, xmpExternal, N_("Song Writer Keywords.") }, + { "Source", N_("Source"), "Text", xmpText, xmpExternal, N_("Source.") }, + { "SourceCredits", N_("Source Credits"), "Text", xmpText, xmpExternal, N_("Source Credits.") }, + { "SourceForm", N_("Source Form"), "Text", xmpText, xmpExternal, N_("Source Form.") }, + { "SourceImageHeight", N_("Source Image Height"), "Integer", xmpText, xmpExternal, N_("Video height in pixels") }, + { "SourceImageWidth", N_("Source Image Width"), "Integer", xmpText, xmpExternal, N_("Video width in pixels") }, + { "Starring", N_("Starring"), "Text", xmpText, xmpExternal, N_("Starring, name of famous people appearing in the video.") }, + { "StartTimecode", N_("Start Timecode"), "Integer", xmpText, xmpExternal, N_("Start Timecode") }, + { "Statistics", N_("Statistics"), "Text", xmpText, xmpExternal, N_("Statistics.") }, + { "StreamCount", N_("Stream Count"), "Integer", xmpText, xmpExternal, N_("Total Number Of Streams") }, + { "StreamName", N_("Stream Name"), "Text", xmpText, xmpExternal, N_("Describes the Stream Name. Eg - FUJIFILM AVI STREAM 0100") }, + { "StreamQuality", N_("Stream Quality"), "Integer", xmpText, xmpExternal, N_("Generral Stream Quality") }, + { "StreamSampleRate", N_("Stream Sample Rate"), "Rational", xmpText, xmpExternal, N_("Stream Sample Rate") }, + { "StreamSampleCount", N_("Stream Sample Count"), "Integer", xmpText, xmpExternal, N_("Stream Sample Count") }, + { "StreamSampleSize", N_("Stream Sample Size"), "Integer", xmpText, xmpExternal, N_("General Stream Sample Size") }, + { "StreamType", N_("Stream Type"), "Text", xmpText, xmpExternal, N_("Describes the Stream Type. Eg - Video, Audio or Subtitles") }, + { "SubTCodec", N_("Subtitles Codec"), "Text", xmpText, xmpExternal, N_("Subtitles stream codec, for general purpose") }, + { "SubTCodecDecodeAll", N_("Subtitle Codec Decode Info"), "Text", xmpText, xmpExternal, N_("Contains information the Subtitles codec decode all, i.e. Enabled/Disabled") }, + { "SubTCodecInfo", N_("Subtitles Codec Information"), "Text", xmpText, xmpExternal, N_("Contains additional information about subtitles.") }, + { "SubTCodecDownloadUrl", N_("Subtitle Codec Download URL"), "Text", xmpText, xmpExternal, N_("Video Subtitle Codec Download URL.") }, + { "SubTCodecSettings", N_("Subtitle Codec Settings"), "Text", xmpText, xmpExternal, N_("Contains settings the codec needs before decoding can be started.") }, + { "SubTDefaultOn", N_("Subtitle Track Default On"), "Text", xmpText, xmpExternal, N_("Subtitles Track Default On , i.e. Enabled/Disabled") }, + { "SubTEnabled", N_("Subtitle Track Enabled"), "Text", xmpText, xmpExternal, N_("Status of Subtitles Track, i.e. Enabled/Disabled") }, + { "Subtitle", N_("Subtitle"), "Text", xmpText, xmpExternal, N_("Subtitle of the video.") }, + { "SubtitleKeywords", N_("Subtitle Keywords"), "Text", xmpText, xmpExternal, N_("Subtitle Keywords.") }, + { "SubTLang", N_("Subtitles Language"), "Text", xmpText, xmpExternal, N_("The Language in which the subtitles is recorded in.") }, + { "SubTTrackForced", N_("Subtitle Track Forced"), "Text", xmpText, xmpExternal, N_("Subtitles Track Forced , i.e. Enabled/Disabled") }, + { "SubTTrackLacing", N_("Subtitle Track Lacing"), "Text", xmpText, xmpExternal, N_("Subtitles Track Lacing , i.e. Enabled/Disabled") }, + { "Subject", N_("Subject"), "Text", xmpText, xmpExternal, N_("Subject. ") }, + { "TapeName", N_("Tape Name"), "Text", xmpText, xmpExternal, N_("TapeName.") }, + { "TagDefault", N_("Tag Default Setting"), "Text", xmpText, xmpExternal, N_("If Tag is Default enabled, this value is Yes, else No ") }, + { "TagLanguage", N_("Tag Language"), "Text", xmpText, xmpExternal, N_("Language that has been used to define tags") }, + { "TagName", N_("Tag Name"), "Text", xmpText, xmpExternal, N_("Tags could be used to define several titles for a segment.") }, + { "TagString", N_("Tag String"), "Text", xmpText, xmpExternal, N_("Information contained in a Tags") }, + { "TargetType", N_("Target Type"), "Text", xmpText, xmpExternal, N_("A string describing the logical level of the object the Tag is refering to.") }, + { "Technician", N_("Technician"), "Text", xmpText, xmpExternal, N_("Technician, in most cases name of person.") }, + { "ThumbnailHeight", N_("Thumbnail Height"), "Integer", xmpText, xmpExternal, N_("Preview Image Thumbnail Height.") }, + { "ThumbnailLength", N_("Thumbnail Length"), "Integer", xmpText, xmpExternal, N_("Preview Image Thumbnail Length.") }, + { "ThumbnailWidth", N_("Thumbnail Width"), "Integer", xmpText, xmpExternal, N_("Preview Image Thumbnail Width.") }, + { "TimecodeScale", N_("Timecode Scale"), "Rational", xmpText, xmpExternal, N_("Multiplying factor which is helpful in calculation of a particular timecode") }, + { "TimeOffset", N_("Time Offset"), "Integer", xmpText, xmpExternal, N_("Specifies the presentation time offset of the stream in 100-nanosecond units. This value shall be equal to the send time of the first interleaved packet in the data section.") }, + { "TimeScale", N_("Time Scale"), "Integer", xmpText, xmpExternal, N_("A time value that indicates the time scale for this movie—that is, the number of time units that" + "pass per second in its time coordinate system. A time coordinate system that measures time" + "in sixtieths of a second, for example, has a time scale of 60.") }, + { "Title", N_("Title"), "Text", xmpText, xmpExternal, N_("Contains a general name of the SEGMENT, like 'Lord of the Rings - The Two Towers', however, Tags could be used to define several titles for a segment.") }, + { "ToningEffect", N_("Toning Effect"), "Text", xmpText, xmpExternal, N_("Toning Effect Settings Applied.") }, + { "TotalFrameCount", N_("Total Frame Count"), "Integer", xmpText, xmpExternal, N_("Total number of frames in a video") }, + { "TotalStream", N_("Number Of Streams"), "Integer", xmpText, xmpExternal, N_("Total number of streams present in a video. Eg - Video, Audio or Subtitles") }, + { "Track", N_("Track"), "Text", xmpText, xmpExternal, N_("Information about the Track.") }, + { "TrackCreateDate", N_("Video Track Create Date"), "Integer", xmpText, xmpExternal, N_("A 32-bit integer that indicates (in seconds since midnight, January 1, 1904) when the track header was created.") }, + { "TrackDuration", N_("Video Track Duration"), "Integer", xmpText, xmpExternal, N_("A time value that indicates the duration of this track (in the movie’s time coordinate system).") }, + { "TrackForced", N_("Video Track Forced"), "Text", xmpText, xmpExternal, N_("Video Track Forced , i.e. Enabled/Disabled") }, + { "TrackID", N_("Track ID"), "Integer", xmpText, xmpExternal, N_("A 32-bit integer that uniquely identifies the track. The value 0 cannot be used.") }, + { "TrackHeaderVersion", N_("Track Header Version"), "Text", xmpText, xmpExternal, N_("A 1-byte specification of the version of this track header") }, + { "TrackLacing", N_("Video Track Lacing"), "Text", xmpText, xmpExternal, N_("Video Track Lacing , i.e. Enabled/Disabled") }, + { "TrackLang", N_("Track Language"), "Text", xmpText, xmpExternal, N_("The Language in which a particular stream is recorded in.") }, + { "TrackLayer", N_("Video Track Layer"), "Integer", xmpText, xmpExternal, N_("A 16-bit integer that indicates this track’s spatial priority in its movie. The QuickTime Movie" + "Toolbox uses this value to determine how tracks overlay one another. Tracks with lower layer" + "values are displayed in front of tracks with higher layer values.") }, + { "TrackModifyDate", N_("Video Track Modify Date"), "Integer", xmpText, xmpExternal, N_("A 32-bit integer that indicates (in seconds since midnight, January 1, 1904) when the track header was last modified.") }, + { "TrackName", N_("Track Name"), "Text", xmpText, xmpExternal, N_("Track Name could be used to define titles for a segment.") }, + { "TrackNumber", N_("Track Number"), "Integer", xmpText, xmpExternal, N_("Track Number.") }, + { "TrackVolume", N_("Track Volume"), "Rational", xmpText, xmpExternal, N_("A 16-bit fixed-point number that specifies how loud to play this track’s sound. A value of 1.0 indicates full volume.") }, + { "TranslateCodec", N_("Chapter Translate Codec"), "Text", xmpText, xmpExternal, N_("Chapter Translate Codec information. Usually used in Matroska filr type.") }, + { "UnknownInfo", N_("Unknown Information"), "Text", xmpText, xmpExternal, N_("Unknown / Unregistered Metadata Tags and their values.") }, + { "UnknownInfo2", N_("Unknown Information"), "Text", xmpText, xmpExternal, N_("Unknown / Unregistered Metadata Tags and their values.") }, + { "URL", N_("Video URL"), "Text", xmpText, xmpExternal, N_("A C string that specifies a URL. There may be additional data after the C string.") }, + { "URN", N_("Video URN"), "Text", xmpText, xmpExternal, N_("A C string that specifies a URN. There may be additional data after the C string.") }, + { "VariProgram", N_("Vari Program"), "Text", xmpText, xmpExternal, N_("Software settings used to generate / create Video data.") }, + { "VegasVersionMajor", N_("Vegas Version Major"), "Text", xmpText, xmpExternal, N_("Vegas Version Major.") }, + { "VegasVersionMinor", N_("Vegas Version Minor"), "Text", xmpText, xmpExternal, N_("Vegas Version Minor.") }, + { "Vendor", N_("Vendor"), "Text", xmpText, xmpExternal, N_("The developer of the compressor that generated the compressed data.") }, + { "VendorID", N_("Vendor ID"), "Text", xmpText, xmpExternal, N_("A 32-bit integer that specifies the developer of the compressor that generated the compressed data. Often this field contains 'appl' to indicate Apple Computer, Inc.") }, + { "VideoQuality", N_("Video Quality"), "Integer", xmpText, xmpExternal, N_("Video Stream Quality") }, + { "VideoSampleSize", N_("Video Sample Size"), "Integer", xmpText, xmpExternal, N_("Video Stream Sample Size") }, + { "VideoScanType", N_("Video Scan Type"), "Text", xmpText, xmpExternal, N_("Video Scan Type, it can be Progressive or Interlaced") }, + { "WatermarkURL", N_("Watermark URL"), "Text", xmpText, xmpExternal, N_("A C string that specifies a Watermark URL.") }, + { "WhiteBalance", N_("White Balance"), "Closed Choice Text", xmpText, xmpExternal, N_("\"White Balance\" setting. One of: As Shot, Auto, Daylight, Cloudy, Shade, Tungsten, " + "Fluorescent, Flash, Custom") }, + { "WhiteBalanceFineTune", N_("White Balance Fine Tune"), "Integer", xmpText, xmpExternal, N_("White Balance Fine Tune.") }, + { "Width", N_("Video Width"), "Integer", xmpText, xmpExternal, N_("Video width in pixels") }, + { "WindowLocation", N_("Window Location"), "Text", xmpText, xmpExternal, N_("Information about the Window Location.") }, + { "WorldTime", N_("WorldTime"), "Integer", xmpText, xmpExternal, N_("World Time") }, + { "WrittenBy", N_("Written By"), "Text", xmpText, xmpExternal, N_("Written By, i.e. name of person or organization.") }, + { "WritingApp", N_("Writing App"), "Text", xmpText, xmpExternal, N_("Contains the name of the application used to create the file (like ”mkvmerge 0.8.1“)") }, + { "XResolution", N_("X Resolution"), "Rational", xmpText, xmpInternal, N_("Horizontal resolution in pixels per unit.") }, + { "Year", N_("Year"), "Integer", xmpText, xmpExternal, N_("Year in which the video was made.") }, + { "YResolution", N_("Y Resolution"), "Rational", xmpText, xmpInternal, N_("Vertical resolution in pixels per unit.") }, + { 0, 0, 0, invalidTypeId, xmpInternal, 0 } + }; + + extern const XmpPropertyInfo xmpAudioInfo[] = { + { "AvgBytePerSec", N_("Average Bytes Per Second"), "Integer", xmpText, xmpExternal, N_("Average Bytes Per Second found in audio stream") }, + { "Balance", N_("Balance"), "Integer", xmpText, xmpExternal, N_("Indicates the left-right balance of the audio") }, + { "BitsPerSample", N_("Bits Per Sample/ Bit Rate"), "Integer", xmpText, xmpExternal, N_("Bits per test sample") }, + { "ChannelType", N_("Audio Channel Type"), "Integers", xmpText, xmpExternal, N_("The audio channel type. One of: Mono, Stereo, 5.1, 7.1.") }, + { "Codec", N_("Audio Codec"), "Text", xmpText, xmpExternal, N_("Codec used for Audio Encoding/Decoding") }, + { "CodecDecodeAll", N_("Audio Codec Decode Info"), "Text", xmpText, xmpExternal, N_("Contains information the audio codec decode all, i.e. Enabled/Disabled") }, + { "CodecDescription", N_("Audio Codec Description"), "Text", xmpText, xmpExternal, N_("Contains description the codec.") }, + { "CodecDownloadUrl", N_("Audio Codec Download URL"), "Text", xmpText, xmpExternal, N_("Audio Codec Download URL.") }, + { "CodecInfo", N_("Audio Codec Information"), "Text", xmpText, xmpExternal, N_("Contains information the codec needs before decoding can be started. An example is the Vorbis initialization packets for Vorbis audio.") }, + { "CodecSettings", N_("Audio Codec Settings"), "Text", xmpText, xmpExternal, N_("Contains settings the codec needs before decoding can be started.") }, + { "Compressor", N_("Audio Compressor"), "Text", xmpText, xmpExternal, N_("The audio compression used. For example, MP3.") }, + { "DefaultDuration", N_("MicroSec audio chunk lasts"), "Text", xmpText, xmpExternal, N_("The number of micro seconds an audio chunk plays.") }, + { "DefaultStream", N_("Default Stream"), "Text", xmpText, xmpExternal, N_("Audio Stream that would be played by default.") }, + { "DefaultOn", N_("Audio Track Default On"), "Text", xmpText, xmpExternal, N_("Audio Track Default On , i.e. Enabled/Disabled") }, + { "Enabled", N_("Audio Track Enabled"), "Text", xmpText, xmpExternal, N_("Status of Audio Track, i.e. Enabled/Disabled") }, + { "Format", N_("Audio Format"), "Text", xmpText, xmpExternal, N_("A four-character code that identifies the format of the audio.") }, + { "HandlerClass", N_("Handler Class"), "Text", xmpText, xmpExternal, N_("A four-character code that identifies the type of the handler. Only two values are valid for this field: 'mhlr' for media handlers and 'dhlr' for data handlers.") }, + { "HandlerDescription", N_("Handler Description"), "Text", xmpText, xmpExternal, N_("A (counted) string that specifies the name of the component—that is, the media handler used when this media was created..") }, + { "HandlerType", N_("Handler Type"), "Text", xmpText, xmpExternal, N_("A four-character code that identifies the type of the media handler or data handler.") }, + { "HandlerVendorID", N_("Handler Vendor ID"), "Text", xmpText, xmpExternal, N_("Component manufacturer.") }, + { "MediaCreateDate", N_("Media Track Create Date"), "Integer", xmpText, xmpExternal, N_("A 32-bit integer that indicates (in seconds since midnight, January 1, 1904) when the media header was created.") }, + { "MediaDuration", N_("Media Track Duration"), "Integer", xmpText, xmpExternal, N_("A time value that indicates the duration of this media (in the movie’s time coordinate system).") }, + { "MediaHeaderVersion", N_("Media Header Version"), "Text", xmpText, xmpExternal, N_("A 1-byte specification of the version of this media header") }, + { "MediaLangCode", N_("Media Language Code"), "Integer", xmpText, xmpExternal, N_("A 16-bit integer that specifies the language code for this media.") }, + { "MediaModifyDate", N_("Media Track Modify Date"), "Integer", xmpText, xmpExternal, N_("A 32-bit integer that indicates (in seconds since midnight, January 1, 1904) when the media header was last modified.") }, + { "MediaTimeScale", N_("Media Time Scale"), "Integer", xmpText, xmpExternal, N_("A time value that indicates the time scale for this media—that is, the number of time units that pass per second in its time coordinate system." ) }, + { "OutputSampleRate", N_("Output Audio Sample Rate"), "Integer", xmpText, xmpExternal, N_("The ouput audio sample rate. Can be any value, but commonly 32000, 41100, or 48000.") }, + { "SampleCount", N_("Audio Sample Count"), "Integer", xmpText, xmpExternal, N_("Sample taken for Analyzing Audio Stream") }, + { "SampleRate", N_("Audio Sample Rate"), "Integer", xmpText, xmpExternal, N_("The audio sample rate. Can be any value, but commonly 32000, 41100, or 48000.") }, + { "SampleType", N_("Audio Sample Type"), "closed Choice of Text", xmpText, xmpExternal, N_("The audio sample type. One of: 8Int, 16Int, 32Int, 32Float.") }, + { "SchemeTitle", N_("Sound Scheme Title"), "Text", xmpText, xmpExternal, N_("Sound Scheme Title.") }, + { "TimeOffset", N_("Time Offset"), "Integer", xmpText, xmpExternal, N_("Specifies the presentation time offset of the stream in 100-nanosecond units. This value shall be equal to the send time of the first interleaved packet in the data section.") }, + { "TrackCreateDate", N_("Audio Track Create Date"), "Integer", xmpText, xmpExternal, N_("A 32-bit integer that indicates (in seconds since midnight, January 1, 1904) when the track header was created.") }, + { "TrackDuration", N_("Audio Track Duration"), "Integer", xmpText, xmpExternal, N_("A time value that indicates the duration of this track (in the movie’s time coordinate system).") }, + { "TrackForced", N_("Audio Track Forced"), "Text", xmpText, xmpExternal, N_("Audio Track Forced , i.e. Enabled/Disabled") }, + { "TrackID", N_("Track ID"), "Integer", xmpText, xmpExternal, N_("A 32-bit integer that uniquely identifies the track. The value 0 cannot be used.") }, + { "TrackHeaderVersion", N_("Track Header Version"), "Text", xmpText, xmpExternal, N_("A 1-byte specification of the version of this track header") }, + { "TrackLacing", N_("Audio Track Lacing"), "Text", xmpText, xmpExternal, N_("Audio Track Lacing , i.e. Enabled/Disabled") }, + { "TrackLang", N_("Track Language"), "Text", xmpText, xmpExternal, N_("The Language in which a particular stream is recorded in.") }, + { "TrackLayer", N_("Audio Track Layer"), "Integer", xmpText, xmpExternal, N_("A 16-bit integer that indicates this track’s spatial priority in its movie. The QuickTime Movie" + "Toolbox uses this value to determine how tracks overlay one another. Tracks with lower layer" + "values are displayed in front of tracks with higher layer values.") }, + { "TrackModifyDate", N_("Audio Track Modify Date"), "Integer", xmpText, xmpExternal, N_("A 32-bit integer that indicates (in seconds since midnight, January 1, 1904) when the track header was last modified.") }, + { "TrackVolume", N_("Track Volume"), "Rational", xmpText, xmpExternal, N_("A 16-bit fixed-point number that specifies how loud to play this track’s sound. A value of 1.0 indicates full volume.") }, + { "URL", N_("Audio URL"), "Text", xmpText, xmpExternal, N_("A C string that specifies a URL. There may be additional data after the C string.") }, + { "URN", N_("Audio URN"), "Text", xmpText, xmpExternal, N_("A C string that specifies a URN. There may be additional data after the C string.") }, + { "VendorID", N_("Vendor ID"), "Text", xmpText, xmpExternal, N_("A 32-bit integer that specifies the developer of the compressor that generated the compressed data. Often this field contains 'appl' to indicate Apple Computer, Inc.") }, + { 0, 0, 0, invalidTypeId, xmpInternal, 0 } + }; + extern const XmpPrintInfo xmpPrintInfo[] = { {"Xmp.crs.CropUnits", EXV_PRINT_TAG(crsCropUnits) }, {"Xmp.exif.ApertureValue", print0x9202 }, diff --git a/src/quicktimevideo.cpp b/src/quicktimevideo.cpp new file mode 100644 index 00000000..29e44149 --- /dev/null +++ b/src/quicktimevideo.cpp @@ -0,0 +1,1451 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004-2012 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/* + File: quicktimevideo.cpp + Version: $Rev$ + Author(s): Abhinav Badola for GSoC 2012 (AB) + History: 28-Jun-12, AB: created + Credits: See header file + */ +// ***************************************************************************** +#include "rcsid_int.hpp" +EXIV2_RCSID("@(#) $Id$") + +// ***************************************************************************** +// included header files +#include "quicktimevideo.hpp" +#include "futils.hpp" +#include "basicio.hpp" +#include "tags.hpp" +// + standard includes +#include + +// ***************************************************************************** +// class member definitions +namespace Exiv2 { + namespace Internal { + + extern const TagVocabulary qTimeFileType[] = { + { "3g2a", "3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-0 V1.0" }, + { "3g2b", "3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-A V1.0.0" }, + { "3g2c", "3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-B v1.0" }, + { "3ge6", "3GPP (.3GP) Release 6 MBMS Extended Presentations" }, + { "3ge7", "3GPP (.3GP) Release 7 MBMS Extended Presentations" }, + { "3gg6", "3GPP Release 6 General Profile" }, + { "3gp1", "3GPP Media (.3GP) Release 1 (probably non-existent)" }, + { "3gp2", "3GPP Media (.3GP) Release 2 (probably non-existent)" }, + { "3gp3", "3GPP Media (.3GP) Release 3 (probably non-existent)" }, + { "3gp4", "3GPP Media (.3GP) Release 4" }, + { "3gp5", "3GPP Media (.3GP) Release 5" }, + { "3gp6", "3GPP Media (.3GP) Release 6 Streaming Servers" }, + { "3gs7", "3GPP Media (.3GP) Release 7 Streaming Servers" }, + { "CAEP", "Canon Digital Camera" }, + { "CDes", "Convergent Design" }, + { "F4A ", "Audio for Adobe Flash Player 9+ (.F4A)" }, + { "F4B ", "Audio Book for Adobe Flash Player 9+ (.F4B)" }, + { "F4P ", "Protected Video for Adobe Flash Player 9+ (.F4P)" }, + { "F4V ", "Video for Adobe Flash Player 9+ (.F4V)" }, + { "JP2 ", "JPEG 2000 Image (.JP2) [ISO 15444-1 ?]" }, + { "JP20", "Unknown, from GPAC samples (prob non-existent)" }, + { "KDDI", "3GPP2 EZmovie for KDDI 3G cellphones" }, + { "M4A ", "Apple iTunes AAC-LC (.M4A) Audio" }, + { "M4B ", "Apple iTunes AAC-LC (.M4B) Audio Book" }, + { "M4P ", "Apple iTunes AAC-LC (.M4P) AES Protected Audio" }, + { "M4V ", "Apple iTunes Video (.M4V) Video" }, + { "M4VH", "Apple TV (.M4V)" }, + { "M4VP", "Apple iPhone (.M4V)" }, + { "MPPI", "Photo Player, MAF [ISO/IEC 23000-3]" }, + { "MSNV", "MPEG-4 (.MP4) for SonyPSP" }, + { "NDAS", "MP4 v2 [ISO 14496-14] Nero Digital AAC Audio" }, + { "NDSC", "MPEG-4 (.MP4) Nero Cinema Profile" }, + { "NDSH", "MPEG-4 (.MP4) Nero HDTV Profile" }, + { "NDSM", "MPEG-4 (.MP4) Nero Mobile Profile" }, + { "NDSP", "MPEG-4 (.MP4) Nero Portable Profile" }, + { "NDSS", "MPEG-4 (.MP4) Nero Standard Profile" }, + { "NDXC", "H.264/MPEG-4 AVC (.MP4) Nero Cinema Profile" }, + { "NDXH", "H.264/MPEG-4 AVC (.MP4) Nero HDTV Profile" }, + { "NDXM", "H.264/MPEG-4 AVC (.MP4) Nero Mobile Profile" }, + { "NDXP", "H.264/MPEG-4 AVC (.MP4) Nero Portable Profile" }, + { "NDXS", "H.264/MPEG-4 AVC (.MP4) Nero Standard Profile" }, + { "NIKO", "Nikon" }, + { "ROSS", "Ross Video" }, + { "avc1", "MP4 Base w/ AVC ext [ISO 14496-12:2005]" }, + { "caqv", "Casio Digital Camera" }, + { "da0a", "DMB MAF w/ MPEG Layer II aud, MOT slides, DLS, JPG/PNG/MNG images" }, + { "da0b", "DMB MAF, extending DA0A, with 3GPP timed text, DID, TVA, REL, IPMP" }, + { "da1a", "DMB MAF audio with ER-BSAC audio, JPG/PNG/MNG images" }, + { "da1b", "DMB MAF, extending da1a, with 3GPP timed text, DID, TVA, REL, IPMP" }, + { "da2a", "DMB MAF aud w/ HE-AAC v2 aud, MOT slides, DLS, JPG/PNG/MNG images" }, + { "da2b", "DMB MAF, extending da2a, with 3GPP timed text, DID, TVA, REL, IPMP" }, + { "da3a", "DMB MAF aud with HE-AAC aud, JPG/PNG/MNG images" }, + { "da3b", "DMB MAF, extending da3a w/ BIFS, 3GPP timed text, DID, TVA, REL, IPMP" }, + { "dmb1", "DMB MAF supporting all the components defined in the specification" }, + { "dmpf", "Digital Media Project" }, + { "drc1", "Dirac (wavelet compression), encapsulated in ISO base media (MP4)" }, + { "dv1a", "DMB MAF vid w/ AVC vid, ER-BSAC aud, BIFS, JPG/PNG/MNG images, TS" }, + { "dv1b", "DMB MAF, extending dv1a, with 3GPP timed text, DID, TVA, REL, IPMP" }, + { "dv2a", "DMB MAF vid w/ AVC vid, HE-AAC v2 aud, BIFS, JPG/PNG/MNG images, TS" }, + { "dv2b", "DMB MAF, extending dv2a, with 3GPP timed text, DID, TVA, REL, IPMP" }, + { "dv3a", "DMB MAF vid w/ AVC vid, HE-AAC aud, BIFS, JPG/PNG/MNG images, TS" }, + { "dv3b", "DMB MAF, extending dv3a, with 3GPP timed text, DID, TVA, REL, IPMP" }, + { "dvr1", "DVB (.DVB) over RTP" }, + { "dvt1", "DVB (.DVB) over MPEG-2 Transport Stream" }, + { "isc2", "ISMACryp 2.0 Encrypted File" }, + { "iso2", "MP4 Base Media v2 [ISO 14496-12:2005]" }, + { "isom", "MP4 Base Media v1 [IS0 14496-12:2003]" }, + { "jpm ", "JPEG 2000 Compound Image (.JPM) [ISO 15444-6]" }, + { "jpx ", "JPEG 2000 with extensions (.JPX) [ISO 15444-2]" }, + { "mj2s", "Motion JPEG 2000 [ISO 15444-3] Simple Profile" }, + { "mjp2", "Motion JPEG 2000 [ISO 15444-3] General Profile" }, + { "mmp4", "MPEG-4/3GPP Mobile Profile (.MP4/3GP) (for NTT)" }, + { "mp21", "MPEG-21 [ISO/IEC 21000-9]" }, + { "mp41", "MP4 v1 [ISO 14496-1:ch13]" }, + { "mp42", "MP4 v2 [ISO 14496-14]" }, + { "mp71", "MP4 w/ MPEG-7 Metadata [per ISO 14496-12]" }, + { "mqt ", "Sony / Mobile QuickTime (.MQV) US Patent 7,477,830 (Sony Corp)" }, + { "niko", "Nikon" }, + { "odcf", "OMA DCF DRM Format 2.0 (OMA-TS-DRM-DCF-V2_0-20060303-A)" }, + { "opf2", "OMA PDCF DRM Format 2.1 (OMA-TS-DRM-DCF-V2_1-20070724-C)" }, + { "opx2", "OMA PDCF DRM + XBS extensions (OMA-TS-DRM_XBS-V1_0-20070529-C)" }, + { "pana", "Panasonic Digital Camera" }, + { "qt ", "Apple QuickTime (.MOV/QT)" }, + { "sdv ", "SD Memory Card Video" }, + { "ssc1", "Samsung stereoscopic, single stream" }, + { "ssc2", "Samsung stereoscopic, dual stream" } + }; + + extern const TagVocabulary handlerClassTags[] = { + { "dhlr", "Data Handler" }, + { "mhlr", "Media Handler" } + }; + + extern const TagVocabulary handlerTypeTags[] = { + { "alis", "Alias Data" }, + { "crsm", "Clock Reference" }, + { "hint", "Hint Track" }, + { "ipsm", "IPMP" }, + { "m7sm", "MPEG-7 Stream" }, + { "mdir", "Metadata" }, + { "mdta", "Metadata Tags" }, + { "mjsm", "MPEG-J" }, + { "ocsm", "Object Content" }, + { "odsm", "Object Descriptor" }, + { "sdsm", "Scene Description" }, + { "soun", "Audio Track" }, + { "text", "Text" }, + { "tmcd", "Time Code" }, + { "url ", "URL" }, + { "vide", "Video Track" } + }; + + extern const TagVocabulary vendorIDTags[] = { + { "FFMP", "FFmpeg" }, + { "appl", "Apple" }, + { "olym", "Olympus" }, + { "GIC ", "General Imaging Co." }, + { "fe20", "Olympus (fe20)" }, + { "pana", "Panasonic" }, + { "KMPI", "Konica-Minolta" }, + { "kdak", "Kodak" }, + { "pent", "Pentax" }, + { "NIKO", "Nikon" }, + { "leic", "Leica" }, + { "pr01", "Olympus (pr01)" }, + { "SMI ", "Sorenson Media Inc." }, + { "mino", "Minolta" }, + { "sany", "Sanyo" }, + { "ZORA", "Zoran Corporation" }, + { "niko", "Nikon" } + }; + + extern const TagVocabulary cameraByteOrderTags[] = { + { "II", "Little-endian (Intel, II)" }, + { "MM", "Big-endian (Motorola, MM)" } + }; + + extern const TagDetails graphicsModetags[] = { + { 0x0, "srcCopy" }, + { 0x1, "srcOr" }, + { 0x2, "srcXor" }, + { 0x3, "srcBic" }, + { 0x4, "notSrcCopy" }, + { 0x5, "notSrcOr" }, + { 0x6, "notSrcXor" }, + { 0x7, "notSrcBic" }, + { 0x8, "patCopy" }, + { 0x9, "patOr" }, + { 0xa, "patXor" }, + { 0xb, "patBic" }, + { 0xc, "notPatCopy" }, + { 0xd, "notPatOr" }, + { 0xe, "notPatXor" }, + { 0xf, "notPatBic" }, + { 0x20, "blend" }, + { 0x21, "addPin" }, + { 0x22, "addOver" }, + { 0x23, "subPin" }, + { 0x24, "transparent" }, + { 0x25, "addMax" }, + { 0x26, "subOver" }, + { 0x27, "addMin" }, + { 0x31, "grayishTextOr" }, + { 0x32, "hilite" }, + { 0x40, "ditherCopy" }, + { 0x100, "Alpha" }, + { 0x101, "White Alpha" }, + { 0x102, "Pre-multiplied Black Alpha" }, + { 0x110, "Component Alpha" } + }; + + + extern const TagVocabulary userDatatags[] = { + { "AllF", "PlayAllFrames" }, + { "CNCV", "CompressorVersion" }, + { "CNFV", "FirmwareVersion" }, + { "CNMN", "Model" }, + { "CNTH", "CanonCNTH" }, + { "DcMD", "DcMD" }, + { "FFMV", "FujiFilmFFMV" }, + { "INFO", "SamsungINFO" }, + { "LOOP", "LoopStyle" }, + { "MMA0", "MinoltaMMA0" }, + { "MMA1", "MinoltaMMA1" }, + { "MVTG", "FujiFilmMVTG" }, + { "NCDT", "NikonNCDT" }, + { "PANA", "PanasonicPANA" }, + { "PENT", "PentaxPENT" }, + { "PXMN", "MakerNotePentax5b" }, + { "PXTH", "PentaxPreview" }, + { "QVMI", "CasioQVMI" }, + { "SDLN", "PlayMode" }, + { "SelO", "PlaySelection" }, + { "TAGS", "KodakTags/KonicaMinoltaTags/MinoltaTags/NikonTags/OlympusTags/PentaxTags/SamsungTags/SanyoMOV/SanyoMP4" }, + { "WLOC", "WindowLocation" }, + { "XMP_", "XMP" }, + { "Xtra", "Xtra" }, + { "hinf", "HintTrackInfo" }, + { "hinv", "HintVersion" }, + { "hnti", "Hint" }, + { "meta", "Meta" }, + { "name", "Name" }, + { "ptv ", "PrintToVideo" }, + { "scrn", "OlympusPreview" }, + { "thmb", "MakerNotePentax5a/OlympusThumbnail" }, + }; + + extern const TagVocabulary userDataReferencetags[] = { + { "CNCV", "Xmp.video.CompressorVersion" }, + { "CNFV", "Xmp.video.FirmwareVersion" }, + { "CNMN", "Xmp.video.Model" }, + { "NCHD", "Xmp.video.MakerNoteType" }, + { "WLOC", "Xmp.video.WindowLocation" }, + { "SDLN", "Xmp.video.PlayMode" }, + { "SelO", "Xmp.video.PlaySelection" }, + { "name", "Xmp.video.Name" }, + { "vndr", "Xmp.video.Vendor" }, + { " ART", "Xmp.video.Artist" }, + { " alb", "Xmp.video.Album" }, + { " arg", "Xmp.video.Arranger" }, + { " ark", "Xmp.video.ArrangerKeywords" }, + { " cmt", "Xmp.video.Comment" }, + { " cok", "Xmp.video.ComposerKeywords" }, + { " com", "Xmp.video.Composer" }, + { " cpy", "Xmp.video.Copyright" }, + { " day", "Xmp.video.CreateDate" }, + { " dir", "Xmp.video.Director" }, + { " ed1", "Xmp.video.Edit1" }, + { " ed2", "Xmp.video.Edit2" }, + { " ed3", "Xmp.video.Edit3" }, + { " ed4", "Xmp.video.Edit4" }, + { " ed5", "Xmp.video.Edit5" }, + { " ed6", "Xmp.video.Edit6" }, + { " ed7", "Xmp.video.Edit7" }, + { " ed8", "Xmp.video.Edit8" }, + { " ed9", "Xmp.video.Edit9" }, + { " enc", "Xmp.video.Encoder" }, + { " fmt", "Xmp.video.Format" }, + { " gen", "Xmp.video.Genre" }, + { " grp", "Xmp.video.Grouping" }, + { " inf", "Xmp.video.Information" }, + { " isr", "Xmp.video.ISRCCode" }, + { " lab", "Xmp.video.RecordLabelName" }, + { " lal", "Xmp.video.RecordLabelURL" }, + { " lyr", "Xmp.video.Lyrics" }, + { " mak", "Xmp.video.Make" }, + { " mal", "Xmp.video.MakerURL" }, + { " mod", "Xmp.video.Model" }, + { " nam", "Xmp.video.Title" }, + { " pdk", "Xmp.video.ProducerKeywords" }, + { " phg", "Xmp.video.RecordingCopyright" }, + { " prd", "Xmp.video.Producer" }, + { " prf", "Xmp.video.Performers" }, + { " prk", "Xmp.video.PerformerKeywords" }, + { " prl", "Xmp.video.PerformerURL" }, + { " req", "Xmp.video.Requirements" }, + { " snk", "Xmp.video.SubtitleKeywords" }, + { " snm", "Xmp.video.Subtitle" }, + { " src", "Xmp.video.SourceCredits" }, + { " swf", "Xmp.video.SongWriter" }, + { " swk", "Xmp.video.SongWriterKeywords" }, + { " swr", "Xmp.video.SoftwareVersion" }, + { " too", "Xmp.video.Encoder" }, + { " trk", "Xmp.video.Track" }, + { " wrt", "Xmp.video.Composer" }, + { " xyz", "Xmp.video.GPSCoordinates" }, + { "CMbo", "Xmp.video.CameraByteOrder" }, + { "Cmbo", "Xmp.video.CameraByteOrder" }, + }; + + extern const TagDetails NikonNCTGTags[] = { + { 0x0001, "Xmp.video.Make" }, + { 0x0002, "Xmp.video.Model" }, + { 0x0003, "Xmp.video.Software" }, + { 0x0011, "Xmp.video.CreationDate" }, + { 0x0012, "Xmp.video.DateTimeOriginal" }, + { 0x0013, "Xmp.video.FrameCount" }, + { 0x0016, "Xmp.video.FrameRate" }, + { 0x0022, "Xmp.video.FrameWidth" }, + { 0x0023, "Xmp.video.FrameHeight" }, + { 0x0032, "Xmp.audio.channelType" }, + { 0x0033, "Xmp.audio.BitsPerSample" }, + { 0x0034, "Xmp.audio.sampleRate" }, + { 0x1108822, "Xmp.video.ExposureProgram" }, + { 0x1109204, "Xmp.video.ExposureCompensation" }, + { 0x1109207, "Xmp.video.MeteringMode" }, + { 0x110a434, "Xmp.video.LensModel" }, + { 0x1200000, "Xmp.video.GPSVersionID" }, + { 0x1200001, "Xmp.video.GPSLatitudeRef" }, + { 0x1200002, "Xmp.video.GPSLatitude" }, + { 0x1200003, "Xmp.video.GPSLongitudeRef" }, + { 0x1200004, "Xmp.video.GPSLongitude" }, + { 0x1200005, "Xmp.video.GPSAltitudeRef" }, + { 0x1200006, "Xmp.video.GPSAltitude" }, + { 0x1200007, "Xmp.video.GPSTimeStamp" }, + { 0x1200008, "Xmp.video.GPSSatellites" }, + { 0x1200010, "Xmp.video.GPSImgDirectionRef" }, + { 0x1200011, "Xmp.video.GPSImgDirection" }, + { 0x1200012, "Xmp.video.GPSMapDatum" }, + { 0x120001d, "Xmp.video.GPSDateStamp" }, + { 0x2000001, "Xmp.video.MakerNoteVersion" }, + { 0x2000005, "Xmp.video.WhiteBalance" }, + { 0x200000b, "Xmp.video.WhiteBalanceFineTune" }, + { 0x200001e, "Xmp.video.ColorSpace" }, + { 0x2000023, "Xmp.video.PictureControlData" }, + { 0x2000024, "Xmp.video.WorldTime" }, + { 0x200002c, "Xmp.video.UnknownInfo" }, + { 0x2000032, "Xmp.video.UnknownInfo2" }, + { 0x2000039, "Xmp.video.LocationInfo" }, + { 0x2000083, "Xmp.video.LensType" }, + { 0x2000084, "Xmp.video.LensModel" }, + { 0x20000ab, "Xmp.video.VariProgram" }, + }; + + extern const TagDetails NikonColorSpace[] = { + { 1, "sRGB" }, + { 2, "Adobe RGB" }, + }; + + extern const TagVocabulary NikonGPS_Latitude_Longitude_ImgDirection_Reference[] = { + { "N", "North" }, + { "S", "South" }, + { "E", "East" }, + { "W", "West" }, + { "M", "Magnetic North" }, + { "T", "True North" }, + }; + + extern const TagDetails NikonGPSAltitudeRef[] = { + { 0, "Above Sea Level" }, + { 1, "Below Sea Level" }, + }; + + extern const TagDetails NikonExposureProgram[] = { + { 0, "Not Defined" }, + { 1, "Manual" }, + { 2, "Program AE" }, + { 3, "Aperture-priority AE" }, + { 4, "Shutter speed priority AE" }, + { 5, "Creative (Slow speed)" }, + { 6, "Action (High speed)" }, + { 7, "Portrait" }, + { 8, "Landscape" }, + }; + + extern const TagDetails NikonMeteringMode[] = { + { 0, "Unknown" }, + { 1, "Average" }, + { 2, "Center-weighted average" }, + { 3, "Spot" }, + { 4, "Multi-spot" }, + { 5, "Multi-segment" }, + { 6, "Partial" }, + { 255, "Other" }, + }; + + extern const TagDetails PictureControlAdjust[] = { + { 0, "Default Settings" }, + { 1, "Quick Adjust" }, + { 2, "Full Control" }, + }; + + //! Contrast and Sharpness + extern const TagDetails NormalSoftHard[] = { + { 0, "Normal" }, + { 1, "Soft" }, + { 2, "Hard" } + }; + + //! Saturation + extern const TagDetails Saturation[] = { + { 0, "Normal" }, + { 1, "Low" }, + { 2, "High" } + }; + + //! YesNo, used for DaylightSavings + extern const TagDetails YesNo[] = { + { 0, "No" }, + { 1, "Yes" } + }; + + //! DateDisplayFormat + extern const TagDetails DateDisplayFormat[] = { + { 0, "Y/M/D" }, + { 1, "M/D/Y" }, + { 2, "D/M/Y" } + }; + + extern const TagDetails FilterEffect[] = { + { 0x80, "Off" }, + { 0x81, "Yellow" }, + { 0x82, "Orange" }, + { 0x83, "Red" }, + { 0x84, "Green" }, + { 0xff, "n/a" }, + }; + + extern const TagDetails ToningEffect[] = { + { 0x80, "B&W" }, + { 0x81, "Sepia" }, + { 0x82, "Cyanotype" }, + { 0x83, "Red" }, + { 0x84, "Yellow" }, + { 0x85, "Green" }, + { 0x86, "Blue-green" }, + { 0x87, "Blue" }, + { 0x88, "Purple-blue" }, + { 0x89, "Red-purple" }, + { 0xff, "n/a" }, + }; + + enum movieHeaderTags { + MovieHeaderVersion, CreateDate, ModifyDate, TimeScale, Duration, PreferredRate, PreferredVolume, + PreviewTime = 18, PreviewDuration,PosterTime, SelectionTime, SelectionDuration, CurrentTime, NextTrackID + }; + enum trackHeaderTags { + TrackHeaderVersion, TrackCreateDate, TrackModifyDate, TrackID, TrackDuration = 5, TrackLayer = 8, + TrackVolume, ImageWidth = 19, ImageHeight + }; + enum mediaHeaderTags { + MediaHeaderVersion, MediaCreateDate, MediaModifyDate, MediaTimeScale, MediaDuration, MediaLanguageCode + }; + enum handlerTags { + HandlerClass = 1, HandlerType, HandlerVendorID + }; + enum videoHeaderTags { + GraphicsMode = 2, OpColor + }; + enum stream { + Video, Audio, Hint, Null, GenMediaHeader + }; + enum imageDescTags { + codec, VendorID = 4, SourceImageWidth_Height = 7, XResolution, + YResolution, CompressorName = 10, BitDepth + }; + enum audioDescTags { + AudioFormat, AudioVendorID = 4, AudioChannels, AudioSampleRate = 7, MOV_AudioFormat = 13 + }; + + /*! + @brief Function used to check equality of a Tags with a + particular string (ignores case while comparing). + @param buf Data buffer that will contain Tag to compare + @param str char* Pointer to string + @return Returns true if the buffer value is equal to string. + */ + bool equalsQTimeTag(Exiv2::DataBuf& buf ,const char* str) { + for(int i = 0; i < 4; ++i) + if(tolower(buf.pData_[i]) != tolower(str[i])) + return false; + return true; + } + + /*! + @brief Function used to ignore Tags and values stored in them, + since they are not necessary as metadata information + @param buf Data buffer that will contain Tag to compare + @return Returns true, if Tag is found in the ignoreList[] + */ + bool ignoreList (Exiv2::DataBuf& buf) { + const char ignoreList[13][5] = { + "mdat", "edts", "junk", "iods", "alis", "stsc", "stsz", "stco", "ctts", "stss", + "skip", "wide", "cmvd", + }; + + for(int i = 0 ; i < 13 ; ++i) + if(equalsQTimeTag(buf, ignoreList[i])) + return true; + + return false; + } + + /*! + @brief Function used to ignore Tags, basically Tags which + contain other tags inside them, since they are not necessary + as metadata information + @param buf Data buffer that will contain Tag to compare + @return Returns true, if Tag is found in the ignoreList[] + */ + bool dataIgnoreList (Exiv2::DataBuf& buf) { + const char ignoreList[7][5] = { + "moov", "mdia", "minf", "dinf", "alis", "stbl", "cmov", + }; + + for(int i = 0 ; i < 7 ; ++i) + if(equalsQTimeTag(buf, ignoreList[i])) + return true; + + return false; + } + + /*! + @brief Function used to convert buffer data into 64-bit + signed integer, information stored in Big Endian format + @param buf Data buffer that will contain data to be converted + @return Returns a signed 64-bit integer + */ + int64_t returnBufValue(Exiv2::DataBuf& buf, int n = 4) { + int64_t temp = 0; + for(int i = n - 1; i >= 0; i--) + temp = temp + buf.pData_[i]*(pow(256,n-i-1)); + + return temp; + } + + /*! + @brief Function used to convert buffer data into 64-bit + unsigned integer, information stored in Big Endian format + @param buf Data buffer that will contain data to be converted + @return Returns an unsigned 64-bit integer + */ + uint64_t returnUnsignedBufValue(Exiv2::DataBuf& buf, int n = 4) { + uint64_t temp = 0; + for(int i = n-1; i >= 0; i--) + temp = temp + buf.pData_[i]*(pow(256,n-i-1)); + + return temp; + } + + /*! + @brief Function used to quicktime files, by checking the + the tags at the start of the file. If the Tag is any one + of the tags listed below, then it is of Quicktime Type. + @param a, b, c, d - characters used to compare + @return Returns true, if Tag is found in the list qTimeTags + */ + bool isQuickTimeType (char a, char b, char c, char d) { + char qTimeTags[][5] = { + "PICT", "free", "ftyp", "junk", "mdat", + "moov", "pict", "pnot", "skip", "uuid", "wide" + }; + + for(int i = 0; i <= 10; i++) + if(a == qTimeTags[i][0] && b == qTimeTags[i][1] && c == qTimeTags[i][2] && d == qTimeTags[i][3]) + return true; + return false; + } + +}} // namespace Internal, Exiv2 + +namespace Exiv2 { + + using namespace Exiv2::Internal; + + QuickTimeVideo::QuickTimeVideo(BasicIo::AutoPtr io) + : Image(ImageType::qtime, mdNone, io) + { + } // QuickTimeVideo::QuickTimeVideo + + std::string QuickTimeVideo::mimeType() const + { + return "video/quicktime"; + } + + void QuickTimeVideo::writeMetadata() + { + } + + + void QuickTimeVideo::readMetadata() + { + if (io_->open() != 0) throw Error(9, io_->path(), strError()); + + // Ensure that this is the correct image type + if (!isQTimeType(*io_, false)) { + if (io_->error() || io_->eof()) throw Error(14); + throw Error(3, "QuickTime"); + } + + IoCloser closer(*io_); + clearMetadata(); + continueTraversing_ = true; + height_ = width_ = 1; + + xmpData_["Xmp.video.FileSize"] = (double)io_->size()/(double)1048576; + xmpData_["Xmp.video.FileName"] = io_->path(); + xmpData_["Xmp.video.MimeType"] = mimeType(); + + while (continueTraversing_) decodeBlock(); + + aspectRatio(); + } // QuickTimeVideo::readMetadata + + void QuickTimeVideo::decodeBlock() + { + const long bufMinSize = 4; + DataBuf buf(bufMinSize); + unsigned long size = 0; + buf.pData_[4] = '\0' ; + + std::memset(buf.pData_, 0x0, buf.size_); + + io_->read(buf.pData_, 4); + if(io_->eof()) { + continueTraversing_ = false; + return; + } + + size = Exiv2::getULong(buf.pData_, bigEndian); + + io_->read(buf.pData_, 4); + if(size < 8) + return; + + tagDecoder(buf,size-8); + } // QuickTimeVideo::decodeBlock + + void QuickTimeVideo::tagDecoder(Exiv2::DataBuf &buf, unsigned long size) + { + if (ignoreList(buf)) + discard(size); + + else if (dataIgnoreList(buf)) + decodeBlock(); + + else if (equalsQTimeTag(buf, "ftyp")) + fileTypeDecoder(size); + + else if (equalsQTimeTag(buf, "trak")) + setMediaStream(); + + else if (equalsQTimeTag(buf, "mvhd")) + movieHeaderDecoder(size); + + else if (equalsQTimeTag(buf, "tkhd")) + trackHeaderDecoder(size); + + else if (equalsQTimeTag(buf, "mdhd")) + mediaHeaderDecoder(size); + + else if (equalsQTimeTag(buf, "hdlr")) + handlerDecoder(size); + + else if (equalsQTimeTag(buf, "vmhd")) + videoHeaderDecoder(size); + + else if (equalsQTimeTag(buf, "udta")) + userDataDecoder(size); + + else if (equalsQTimeTag(buf, "dref")) + multipleEntriesDecoder(); + + else if (equalsQTimeTag(buf, "stsd")) + sampleDesc(size); + + else if (equalsQTimeTag(buf, "stts")) + timeToSampleDecoder(); + + else if (equalsQTimeTag(buf, "url ")) { + io_->read(buf.pData_, size); + if (currentStream_ == Video) + xmpData_["Xmp.video.URL"] = Exiv2::toString(buf.pData_); + else if (currentStream_ == Audio) + xmpData_["Xmp.audio.URL"] = Exiv2::toString(buf.pData_); + } + + else if (equalsQTimeTag(buf, "urn ")) { + io_->read(buf.pData_, size); + if (currentStream_ == Video) + xmpData_["Xmp.video.URN"] = Exiv2::toString(buf.pData_); + else if (currentStream_ == Audio) + xmpData_["Xmp.audio.URN"] = Exiv2::toString(buf.pData_); + } + + else if (equalsQTimeTag(buf, "dcom")) { + io_->read(buf.pData_, size); + xmpData_["Xmp.video.Compressor"] = Exiv2::toString(buf.pData_); + } + + else if (equalsQTimeTag(buf, "smhd")) { + io_->read(buf.pData_, 4); + io_->read(buf.pData_, 4); + xmpData_["Xmp.audio.Balance"] = returnBufValue(buf, 2); + } + + else { + discard(size); + } + } // QuickTimeVideo::tagDecoder + + void QuickTimeVideo::discard(unsigned long size) + { + uint64_t cur_pos = io_->tell(); + io_->seek(cur_pos + size, BasicIo::beg); + } // QuickTimeVideo::discard + + void QuickTimeVideo::userDataDecoder(unsigned long size_external) + { + uint64_t cur_pos = io_->tell(); + const TagVocabulary* td; + const TagVocabulary* tv, *tv_internal; + + const long bufMinSize = 100; + DataBuf buf(bufMinSize); + unsigned long size = 0, size_internal = size_external; + std::memset(buf.pData_, 0x0, buf.size_); + + while((size_internal/4 != 0) && (size_internal > 0)) { + + buf.pData_[4] = '\0' ; + io_->read(buf.pData_, 4); + size = Exiv2::getULong(buf.pData_, bigEndian); + if(size > size_internal) + break; + size_internal -= size; + io_->read(buf.pData_, 4); + + if(buf.pData_[0] == 169) + buf.pData_[0] = ' '; + td = find(userDatatags, Exiv2::toString( buf.pData_)); + + tv = find(userDataReferencetags, Exiv2::toString( buf.pData_)); + + if(size == 0 || (size - 12) <= 0) + break; + + else if(equalsQTimeTag(buf, "DcMD") || equalsQTimeTag(buf, "NCDT")) + userDataDecoder(size - 8); + + else if(equalsQTimeTag(buf, "NCTG")) + NikonTagsDecoder(size - 8); + + else if(equalsQTimeTag(buf, "CNCV") || equalsQTimeTag(buf, "CNFV") + || equalsQTimeTag(buf, "CNMN") || equalsQTimeTag(buf, "NCHD")) { + io_->read(buf.pData_, size - 8); + xmpData_[exvGettext(tv->label_)] = Exiv2::toString(buf.pData_); + } + + + else if(equalsQTimeTag(buf, "CMbo") || equalsQTimeTag(buf, "Cmbo")) { + io_->read(buf.pData_, 2); + buf.pData_[2] = '\0' ; + tv_internal = find(cameraByteOrderTags, Exiv2::toString( buf.pData_)); + + if (tv_internal) + xmpData_[exvGettext(tv->label_)] = exvGettext(tv_internal->label_); + else + xmpData_[exvGettext(tv->label_)] = Exiv2::toString(buf.pData_); + } + + else if(tv) { + io_->read(buf.pData_, 4); + io_->read(buf.pData_, size-12); + xmpData_[exvGettext(tv->label_)] = Exiv2::toString(buf.pData_); + } + + else if(td) + tagDecoder(buf,size-8); + } + + io_->seek(cur_pos + size_external, BasicIo::beg); + } // QuickTimeVideo::userDataDecoder + + void QuickTimeVideo::NikonTagsDecoder(unsigned long size_external) + { + uint64_t cur_pos = io_->tell(); + DataBuf buf(100), buf2(4); + unsigned long TagID = 0; + unsigned short dataLength = 0, dataType = 2; + const TagDetails* td, *td2; + + for(int i = 0 ; i < 100 ; i++) { + io_->read(buf.pData_, 4); + TagID = Exiv2::getULong(buf.pData_, bigEndian); + td = find(NikonNCTGTags, TagID); + + io_->read(buf.pData_, 2); + dataType = Exiv2::getUShort(buf.pData_, bigEndian); + + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 2); + + if(TagID == 0x2000023) { + uint64_t local_pos = io_->tell(); + dataLength = Exiv2::getUShort(buf.pData_, bigEndian); + std::memset(buf.pData_, 0x0, buf.size_); + + io_->read(buf.pData_, 4); xmpData_["Xmp.video.PictureControlVersion"] = Exiv2::toString(buf.pData_); + io_->read(buf.pData_, 20); xmpData_["Xmp.video.PictureControlName"] = Exiv2::toString(buf.pData_); + io_->read(buf.pData_, 20); xmpData_["Xmp.video.PictureControlBase"] = Exiv2::toString(buf.pData_); + io_->read(buf.pData_, 4); std::memset(buf.pData_, 0x0, buf.size_); + + io_->read(buf.pData_, 1); + td2 = find(PictureControlAdjust, (int)buf.pData_[0] & 7 ); + if(td2) + xmpData_["Xmp.video.PictureControlAdjust"] = exvGettext(td2->label_); + else + xmpData_["Xmp.video.PictureControlAdjust"] = (int)buf.pData_[0] & 7 ; + + io_->read(buf.pData_, 1); + td2 = find(NormalSoftHard, (int)buf.pData_[0] & 7 ); + if(td2) + xmpData_["Xmp.video.PictureControlQuickAdjust"] = exvGettext(td2->label_); + + io_->read(buf.pData_, 1); + td2 = find(NormalSoftHard, (int)buf.pData_[0] & 7 ); + if(td2) + xmpData_["Xmp.video.Sharpness"] = exvGettext(td2->label_); + else + xmpData_["Xmp.video.Sharpness"] = (int)buf.pData_[0] & 7; + + io_->read(buf.pData_, 1); + td2 = find(NormalSoftHard, (int)buf.pData_[0] & 7 ); + if(td2) + xmpData_["Xmp.video.Contrast"] = exvGettext(td2->label_); + else + xmpData_["Xmp.video.Contrast"] = (int)buf.pData_[0] & 7; + + io_->read(buf.pData_, 1); + td2 = find(NormalSoftHard, (int)buf.pData_[0] & 7 ); + if(td2) + xmpData_["Xmp.video.Brightness"] = exvGettext(td2->label_); + else + xmpData_["Xmp.video.Brightness"] = (int)buf.pData_[0] & 7; + + io_->read(buf.pData_, 1); + td2 = find(Saturation, (int)buf.pData_[0] & 7 ); + if(td2) + xmpData_["Xmp.video.Saturation"] = exvGettext(td2->label_); + else + xmpData_["Xmp.video.Saturation"] = (int)buf.pData_[0] & 7; + + io_->read(buf.pData_, 1); + xmpData_["Xmp.video.HueAdjustment"] = (int)buf.pData_[0] & 7; + + io_->read(buf.pData_, 1); + td2 = find(FilterEffect, (int)buf.pData_[0]); + if(td2) + xmpData_["Xmp.video.FilterEffect"] = exvGettext(td2->label_); + else + xmpData_["Xmp.video.FilterEffect"] = (int)buf.pData_[0]; + + io_->read(buf.pData_, 1); + td2 = find(ToningEffect, (int)buf.pData_[0]); + if(td2) + xmpData_["Xmp.video.ToningEffect"] = exvGettext(td2->label_); + else + xmpData_["Xmp.video.ToningEffect"] = (int)buf.pData_[0]; + + io_->read(buf.pData_, 1); xmpData_["Xmp.video.ToningSaturation"] = (int)buf.pData_[0]; + + io_->seek(local_pos + dataLength, BasicIo::beg); + } + + else if(TagID == 0x2000024) { + uint64_t local_pos = io_->tell(); + dataLength = Exiv2::getUShort(buf.pData_, bigEndian); + std::memset(buf.pData_, 0x0, buf.size_); + + io_->read(buf.pData_, 2); xmpData_["Xmp.video.TimeZone"] = Exiv2::getShort(buf.pData_, bigEndian); + io_->read(buf.pData_, 1); + td2 = find(YesNo, (int)buf.pData_[0]); + if(td2) + xmpData_["Xmp.video.DayLightSavings"] = exvGettext(td2->label_); + + io_->read(buf.pData_, 1); + td2 = find(DateDisplayFormat, (int)buf.pData_[0]); + if(td2) + xmpData_["Xmp.video.DateDisplayFormat"] = exvGettext(td2->label_); + + io_->seek(local_pos + dataLength, BasicIo::beg); + } + + else if(dataType == 2 || dataType == 7) { + dataLength = Exiv2::getUShort(buf.pData_, bigEndian); + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, dataLength); + if(td) + xmpData_[exvGettext(td->label_)] = Exiv2::toString(buf.pData_); + } + else if(dataType == 4) { + dataLength = Exiv2::getUShort(buf.pData_, bigEndian) * 4; + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 4); + if(td) + xmpData_[exvGettext(td->label_)] = Exiv2::toString(Exiv2::getULong( buf.pData_, bigEndian)); + io_->read(buf.pData_, dataLength - 4); + } + else if(dataType == 3) { + dataLength = Exiv2::getUShort(buf.pData_, bigEndian) * 2; + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 2); + if(td) + xmpData_[exvGettext(td->label_)] = Exiv2::toString(Exiv2::getUShort( buf.pData_, bigEndian)); + io_->read(buf.pData_, dataLength - 2); + } + else if(dataType == 5) { + dataLength = Exiv2::getUShort(buf.pData_, bigEndian) * 8; + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 4); + io_->read(buf2.pData_, 4); + if(td) + xmpData_[exvGettext(td->label_)] = Exiv2::toString((double)Exiv2::getULong( buf.pData_, bigEndian) / (double)Exiv2::getULong( buf2.pData_, bigEndian)); + io_->read(buf.pData_, dataLength - 8); + } + else if(dataType == 8) { + dataLength = Exiv2::getUShort(buf.pData_, bigEndian) * 2; + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 2); + io_->read(buf2.pData_, 2); + if(td) + xmpData_[exvGettext(td->label_)] = Exiv2::toString(Exiv2::getUShort( buf.pData_, bigEndian) ) + " " + Exiv2::toString(Exiv2::getUShort( buf2.pData_, bigEndian)); + io_->read(buf.pData_, dataLength - 4); + } + } + + io_->seek(cur_pos + size_external, BasicIo::beg); + } // QuickTimeVideo::NikonTagsDecoder + + void QuickTimeVideo::setMediaStream() + { + uint64_t current_position = io_->tell(); + DataBuf buf(4); + + while(!io_->eof()) { + io_->read(buf.pData_, 4); + if (equalsQTimeTag(buf, "hdlr")) { + io_->read(buf.pData_, 4); + io_->read(buf.pData_, 4); + io_->read(buf.pData_, 4); + + if (equalsQTimeTag(buf, "vide")) + currentStream_ = Video; + else if(equalsQTimeTag(buf, "soun")) + currentStream_ = Audio; + else if (equalsQTimeTag(buf, "hint")) + currentStream_ = Hint; + else + currentStream_ = GenMediaHeader; + break; + } + } + + io_->seek(current_position, BasicIo::beg); + } // QuickTimeVideo::setMediaStream + + void QuickTimeVideo::timeToSampleDecoder() + { + DataBuf buf(4); + io_->read(buf.pData_, 4); + io_->read(buf.pData_, 4); + uint64_t noOfEntries, totalframes = 0, timeOfFrames = 0; + noOfEntries = returnUnsignedBufValue(buf); + std::cerr<<": "<read(buf.pData_, 4); + temp = returnBufValue(buf); + totalframes += temp; + io_->read(buf.pData_, 4); + timeOfFrames += temp * returnBufValue(buf); + } + if (currentStream_ == Video) + xmpData_["Xmp.video.FrameRate"] = (double)totalframes * (double)timeScale_ / (double)timeOfFrames; + } // QuickTimeVideo::timeToSampleDecoder + + void QuickTimeVideo::sampleDesc(unsigned long size) + { + DataBuf buf(100); + uint64_t cur_pos = io_->tell(); + io_->read(buf.pData_, 4); + io_->read(buf.pData_, 4); + uint64_t noOfEntries; + noOfEntries = returnUnsignedBufValue(buf); + + for(unsigned long i = 1; i <= noOfEntries; i++) { + if (currentStream_ == Video) + imageDescDecoder(); + else if (currentStream_ == Audio) + audioDescDecoder(); + } + io_->seek(cur_pos + size, BasicIo::beg); + } // QuickTimeVideo::sampleDesc + + void QuickTimeVideo::audioDescDecoder() + { + DataBuf buf(40); + std::memset(buf.pData_, 0x0, buf.size_); + buf.pData_[4] = '\0'; + io_->read(buf.pData_, 4); + uint64_t size = 82; + + const TagVocabulary* td; + + for (int i = 0; size/4 != 0 ; size -= 4, i++) { + io_->read(buf.pData_, 4); + switch(i) { + case AudioFormat: + td = find(qTimeFileType, Exiv2::toString( buf.pData_)); + if(td) + xmpData_["Xmp.audio.Compressor"] = exvGettext(td->label_); + else + xmpData_["Xmp.audio.Compressor"] = Exiv2::toString( buf.pData_); + break; + case AudioVendorID: + td = find(vendorIDTags, Exiv2::toString( buf.pData_)); + if(td) + xmpData_["Xmp.audio.VendorID"] = exvGettext(td->label_); + break; + case AudioChannels: + xmpData_["Xmp.audio.ChannelType"] = returnBufValue(buf, 2); + xmpData_["Xmp.audio.BitsPerSample"] = (buf.pData_[2] * 256 + buf.pData_[3]); + break; + case AudioSampleRate: + xmpData_["Xmp.audio.SampleRate"] = returnBufValue(buf, 2) + ((buf.pData_[2] * 256 + buf.pData_[3]) * 0.01); + break; + default: + break; + } + } + io_->read(buf.pData_, size % 4); + } // QuickTimeVideo::audioDescDecoder + + void QuickTimeVideo::imageDescDecoder() + { + DataBuf buf(40); + std::memset(buf.pData_, 0x0, buf.size_); + buf.pData_[4] = '\0'; + io_->read(buf.pData_, 4); + uint64_t size = 82; + + const TagVocabulary* td; + + for (int i = 0; size/4 != 0 ; size -= 4, i++) { + io_->read(buf.pData_, 4); + + switch(i) { + case codec: + td = find(qTimeFileType, Exiv2::toString( buf.pData_)); + if(td) + xmpData_["Xmp.video.Codec"] = exvGettext(td->label_); + else + xmpData_["Xmp.video.Codec"] = Exiv2::toString( buf.pData_); + break; + case VendorID: + td = find(vendorIDTags, Exiv2::toString( buf.pData_)); + if(td) + xmpData_["Xmp.video.VendorID"] = exvGettext(td->label_); + break; + case SourceImageWidth_Height: + xmpData_["Xmp.video.SourceImageWidth"] = returnBufValue(buf, 2); + xmpData_["Xmp.video.SourceImageHeight"] = (buf.pData_[2] * 256 + buf.pData_[3]); + break; + case XResolution: + xmpData_["Xmp.video.XResolution"] = returnBufValue(buf, 2) + ((buf.pData_[2] * 256 + buf.pData_[3]) * 0.01); + break; + case YResolution: + xmpData_["Xmp.video.YResolution"] = returnBufValue(buf, 2) + ((buf.pData_[2] * 256 + buf.pData_[3]) * 0.01); + io_->read(buf.pData_, 3); size -= 3; + break; + case CompressorName: + io_->read(buf.pData_, 32); size -= 32; + xmpData_["Xmp.video.Compressor"] = Exiv2::toString( buf.pData_); + break; + default: + break; + } + } + io_->read(buf.pData_, size % 4); + xmpData_["Xmp.video.BitDepth"] = returnBufValue(buf, 1); + } // QuickTimeVideo::imageDescDecoder + + void QuickTimeVideo::multipleEntriesDecoder() + { + DataBuf buf(4); + io_->read(buf.pData_, 4); + io_->read(buf.pData_, 4); + uint64_t noOfEntries; + + noOfEntries = returnUnsignedBufValue(buf); + + for(unsigned long i = 1; i <= noOfEntries; i++) + decodeBlock(); + } // QuickTimeVideo::multipleEntriesDecoder + + void QuickTimeVideo::videoHeaderDecoder(unsigned long size) { + DataBuf buf(2); + std::memset(buf.pData_, 0x0, buf.size_); + buf.pData_[2] = '\0'; + currentStream_ = Video; + + const TagDetails* td; + + for (int i = 0; size/2 != 0 ; size -= 2, i++) { + io_->read(buf.pData_, 2); + + switch(i) { + case GraphicsMode: + td = find(graphicsModetags, returnBufValue(buf,2)); + if(td) + xmpData_["Xmp.video.GraphicsMode"] = exvGettext(td->label_); + break; + case OpColor: + xmpData_["Xmp.video.OpColor"] = returnBufValue(buf,2); + break; + default: + break; + } + } + io_->read(buf.pData_, size % 2); + } // QuickTimeVideo::videoHeaderDecoder + + void QuickTimeVideo::handlerDecoder(unsigned long size) + { + uint64_t cur_pos = io_->tell(); + DataBuf buf(100); + std::memset(buf.pData_, 0x0, buf.size_); + buf.pData_[4] = '\0'; + + const TagVocabulary* tv; + + for (int i = 0; i < 5 ; i++) { + io_->read(buf.pData_, 4); + + switch(i) { + case HandlerClass: + tv = find(handlerClassTags, Exiv2::toString( buf.pData_)); + if(tv) { + if (currentStream_ == Video) + xmpData_["Xmp.video.HandlerClass"] = exvGettext(tv->label_); + else if (currentStream_ == Audio) + xmpData_["Xmp.audio.HandlerClass"] = exvGettext(tv->label_); + } + break; + case HandlerType: + tv = find(handlerTypeTags, Exiv2::toString( buf.pData_)); + if(tv) { + if (currentStream_ == Video) + xmpData_["Xmp.video.HandlerType"] = exvGettext(tv->label_); + else if (currentStream_ == Audio) + xmpData_["Xmp.audio.HandlerType"] = exvGettext(tv->label_); + } + break; + case HandlerVendorID: + tv = find(vendorIDTags, Exiv2::toString( buf.pData_)); + if(tv) { + if (currentStream_ == Video) + xmpData_["Xmp.video.HandlerVendorID"] = exvGettext(tv->label_); + else if (currentStream_ == Audio) + xmpData_["Xmp.audio.HandlerVendorID"] = exvGettext(tv->label_); + } + break; + } + } + io_->seek(cur_pos + size, BasicIo::beg); + } // QuickTimeVideo::handlerDecoder + + void QuickTimeVideo::fileTypeDecoder(unsigned long size) { + DataBuf buf(4); + std::memset(buf.pData_, 0x0, buf.size_); + buf.pData_[4] = '\0'; + Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpSeq); + const TagVocabulary* td; + + for (int i = 0; size/4 != 0; size -=4, i++) { + io_->read(buf.pData_, 4); + td = find(qTimeFileType, Exiv2::toString( buf.pData_)); + + switch(i) { + case 0: + if(td) + xmpData_["Xmp.video.MajorBrand"] = exvGettext(td->label_); + break; + case 1: + xmpData_["Xmp.video.MinorVersion"] = returnBufValue(buf); + break; + default: + if(td) + v->read(exvGettext(td->label_)); + else + v->read(Exiv2::toString(buf.pData_)); + break; + } + } + xmpData_.add(Exiv2::XmpKey("Xmp.video.CompatibleBrands"), v.get()); + io_->read(buf.pData_, size%4); + } // QuickTimeVideo::fileTypeDecoder + + void QuickTimeVideo::mediaHeaderDecoder(unsigned long size) { + DataBuf buf(4); + std::memset(buf.pData_, 0x0, buf.size_); + buf.pData_[4] = '\0'; + int time_scale = 1; + + for (int i = 0; size/4 != 0 ; size -=4, i++) { + io_->read(buf.pData_, 4); + + switch(i) { + case MediaHeaderVersion: + if(currentStream_ == Video) + xmpData_["Xmp.video.MediaHeaderVersion"] = returnBufValue(buf,1); + else if (currentStream_ == Audio) + xmpData_["Xmp.audio.MediaHeaderVersion"] = returnBufValue(buf,1); + break; + case MediaCreateDate: + //A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created. + if(currentStream_ == Video) + xmpData_["Xmp.video.MediaCreateDate"] = returnUnsignedBufValue(buf); + else if (currentStream_ == Audio) + xmpData_["Xmp.audio.MediaCreateDate"] = returnUnsignedBufValue(buf); + break; + case MediaModifyDate: + //A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created. + if(currentStream_ == Video) + xmpData_["Xmp.video.MediaModifyDate"] = returnUnsignedBufValue(buf); + else if (currentStream_ == Audio) + xmpData_["Xmp.audio.MediaModifyDate"] = returnUnsignedBufValue(buf); + break; + case MediaTimeScale: + if(currentStream_ == Video) + xmpData_["Xmp.video.MediaTimeScale"] = returnBufValue(buf); + else if (currentStream_ == Audio) + xmpData_["Xmp.audio.MediaTimeScale"] = returnBufValue(buf); + time_scale = returnBufValue(buf); + break; + case MediaDuration: + if(currentStream_ == Video) + xmpData_["Xmp.video.MediaDuration"] = returnBufValue(buf)/time_scale; + else if (currentStream_ == Audio) + xmpData_["Xmp.audio.mediaDuration"] = returnBufValue(buf)/time_scale; + break; + case MediaLanguageCode: + if(currentStream_ == Video) + xmpData_["Xmp.video.MediaLangCode"] = returnUnsignedBufValue(buf,2); + else if (currentStream_ == Audio) + xmpData_["Xmp.audio.MediaLangCode"] = returnUnsignedBufValue(buf,2); + break; + + default: + break; + } + } + io_->read(buf.pData_, size%4); + } // QuickTimeVideo::mediaHeaderDecoder + + void QuickTimeVideo::trackHeaderDecoder(unsigned long size) { + DataBuf buf(4); + std::memset(buf.pData_, 0x0, buf.size_); + buf.pData_[4] = '\0'; + uint64_t temp = 0; + + for (int i = 0; size/4 != 0 ; size -=4, i++) { + io_->read(buf.pData_, 4); + + switch(i) { + case TrackHeaderVersion: + if(currentStream_ == Video) + xmpData_["Xmp.video.TrackHeaderVersion"] = returnBufValue(buf,1); + else if(currentStream_ == Audio) + xmpData_["Xmp.audio.TrackHeaderVersion"] = returnBufValue(buf,1); + break; + case TrackCreateDate: + //A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created. + if(currentStream_ == Video) + xmpData_["Xmp.video.TrackCreateDate"] = returnUnsignedBufValue(buf); + else if(currentStream_ == Audio) + xmpData_["Xmp.audio.TrackCreateDate"] = returnUnsignedBufValue(buf); + break; + case TrackModifyDate: + //A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created. + if(currentStream_ == Video) + xmpData_["Xmp.video.TrackModifyDate"] = returnUnsignedBufValue(buf); + else if(currentStream_ == Audio) + xmpData_["Xmp.audio.TrackModifyDate"] = returnUnsignedBufValue(buf); + break; + case TrackID: + if(currentStream_ == Video) + xmpData_["Xmp.video.TrackID"] = returnBufValue(buf); + else if(currentStream_ == Audio) + xmpData_["Xmp.audio.TrackID"] = returnBufValue(buf); + break; + case TrackDuration: + if(currentStream_ == Video) + xmpData_["Xmp.video.TrackDuration"] = returnBufValue(buf)/timeScale_; + else if(currentStream_ == Audio) + xmpData_["Xmp.audio.TrackDuration"] = returnBufValue(buf)/timeScale_; + break; + case TrackLayer: + if(currentStream_ == Video) + xmpData_["Xmp.video.TrackLayer"] = returnBufValue(buf, 2); + else if(currentStream_ == Audio) + xmpData_["Xmp.audio.TrackLayer"] = returnBufValue(buf, 2); + break; + case TrackVolume: + if(currentStream_ == Video) + xmpData_["Xmp.video.TrackVolume"] = (returnBufValue(buf, 1) + (buf.pData_[2] * 0.1)) * 100; + else if(currentStream_ == Audio) + xmpData_["Xmp.video.TrackVolume"] = (returnBufValue(buf, 1) + (buf.pData_[2] * 0.1)) * 100; + break; + case ImageWidth: + if(currentStream_ == Video) { + temp = returnBufValue(buf, 2) + ((buf.pData_[2] * 256 + buf.pData_[3]) * 0.01); + xmpData_["Xmp.video.Width"] = temp; + width_ = temp; + } + break; + case ImageHeight: + if(currentStream_ == Video) { + temp = returnBufValue(buf, 2) + ((buf.pData_[2] * 256 + buf.pData_[3]) * 0.01); + xmpData_["Xmp.video.Height"] = temp; + height_ = temp; + } + break; + default: + break; + } + } + io_->read(buf.pData_, size%4); + } // QuickTimeVideo::trackHeaderDecoder + + void QuickTimeVideo::movieHeaderDecoder(unsigned long size) { + DataBuf buf(4); + std::memset(buf.pData_, 0x0, buf.size_); + buf.pData_[4] = '\0'; + + for (int i = 0; size/4 != 0 ; size -=4, i++) { + io_->read(buf.pData_, 4); + + switch(i) { + case MovieHeaderVersion: + xmpData_["Xmp.video.MovieHeaderVersion"] = returnBufValue(buf,1); break; + case CreateDate: + //A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created. + xmpData_["Xmp.video.DateUTC"] = returnUnsignedBufValue(buf); break; + case ModifyDate: + //A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created. + xmpData_["Xmp.video.ModificationDate"] = returnUnsignedBufValue(buf); break; + case TimeScale: + xmpData_["Xmp.video.TimeScale"] = returnBufValue(buf); + timeScale_ = returnBufValue(buf); break; + case Duration: + xmpData_["Xmp.video.Duration"] = returnBufValue(buf) * 1000 / timeScale_; break; + case PreferredRate: + xmpData_["Xmp.video.PreferredRate"] = returnBufValue(buf, 2) + ((buf.pData_[2] * 256 + buf.pData_[3]) * 0.01); break; + case PreferredVolume: + xmpData_["Xmp.video.PreferredVolume"] = (returnBufValue(buf, 1) + (buf.pData_[2] * 0.1)) * 100; break; + case PreviewTime: + xmpData_["Xmp.video.PreviewTime"] = returnBufValue(buf); break; + case PreviewDuration: + xmpData_["Xmp.video.PreviewDuration"] = returnBufValue(buf); break; + case PosterTime: + xmpData_["Xmp.video.PosterTime"] = returnBufValue(buf); break; + case SelectionTime: + xmpData_["Xmp.video.SelectionTime"] = returnBufValue(buf); break; + case SelectionDuration: + xmpData_["Xmp.video.SelectionDuration"] = returnBufValue(buf); break; + case CurrentTime: + xmpData_["Xmp.video.CurrentTime"] = returnBufValue(buf); break; + case NextTrackID: + xmpData_["Xmp.video.NextTrackID"] = returnBufValue(buf); break; + default: + break; + } + } + io_->read(buf.pData_, size%4); + } // QuickTimeVideo::movieHeaderDecoder + + void QuickTimeVideo::aspectRatio() + { + //TODO - Make a better unified method to handle all cases of Aspect Ratio + + double aspectRatio = (double)width_ / (double)height_; + aspectRatio = floor(aspectRatio*10) / 10; + xmpData_["Xmp.video.AspectRatio"] = aspectRatio; + + if(aspectRatio == 1.3) xmpData_["Xmp.video.AspectRatio"] = "4:3"; + else if(aspectRatio == 1.7) xmpData_["Xmp.video.AspectRatio"] = "16:9"; + else if(aspectRatio == 1.0) xmpData_["Xmp.video.AspectRatio"] = "1:1"; + else if(aspectRatio == 1.6) xmpData_["Xmp.video.AspectRatio"] = "16:10"; + else if(aspectRatio == 2.2) xmpData_["Xmp.video.AspectRatio"] = "2.21:1"; + else if(aspectRatio == 2.3) xmpData_["Xmp.video.AspectRatio"] = "2.35:1"; + else if(aspectRatio == 1.2) xmpData_["Xmp.video.AspectRatio"] = "5:4"; + else xmpData_["Xmp.video.AspectRatio"] = aspectRatio; + } // QuickTimeVideo::aspectRatio + + + Image::AutoPtr newQTimeInstance(BasicIo::AutoPtr io, bool /*create*/) { + Image::AutoPtr image(new QuickTimeVideo(io)); + if (!image->good()) { + image.reset(); + } + return image; + } + + bool isQTimeType(BasicIo& iIo, bool advance) { + const int32_t len = 4; + byte buf[len]; + iIo.read(buf, len); + iIo.read(buf, len); + + if (iIo.error() || iIo.eof()) { + return false; + } + + bool matched = isQuickTimeType(buf[0], buf[1], buf[2], buf[3]); + if (!advance || !matched) { + iIo.seek(0, BasicIo::beg); + } + + return matched; + } + +} // namespace Exiv2 diff --git a/src/quicktimevideo.hpp b/src/quicktimevideo.hpp new file mode 100644 index 00000000..a4879cce --- /dev/null +++ b/src/quicktimevideo.hpp @@ -0,0 +1,229 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004-2012 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/*! + @file quicktimevideo.hpp + @brief An Image subclass to support Quick Time video files + @version $Rev$ + @author Abhinav Badola for GSoC 2012 + mail.abu.to@gmail.com + @date 28-Jun-12, AB: created + */ +#ifndef QUICKTIMEVIDEO_HPP +#define QUICKTIMEVIDEO_HPP + +// ***************************************************************************** +// included header files +#include "exif.hpp" +#include "image.hpp" +#include "tags_int.hpp" + +// ***************************************************************************** +// namespace extensions +namespace Exiv2 { + +// ***************************************************************************** +// class definitions + + // Add qtime to the supported image formats + namespace ImageType { + const int qtime = 22; //!< Treating qtime as an image type> + } + + /*! + @brief Class to access QuickTime video files. + */ + class EXIV2API QuickTimeVideo:public Image + { + public: + //! @name Creators + //@{ + /*! + @brief Constructor for a QuickTime video. Since the constructor + can not return a result, callers should check the good() method + after object construction to determine success or failure. + @param io An auto-pointer that owns a BasicIo instance used for + reading and writing image metadata. \b Important: The constructor + takes ownership of the passed in BasicIo instance through the + auto-pointer. Callers should not continue to use the BasicIo + instance after it is passed to this method. Use the Image::io() + method to get a temporary reference. + */ + QuickTimeVideo(BasicIo::AutoPtr io); + //@} + + //! @name Manipulators + //@{ + void readMetadata(); + void writeMetadata(); + //@} + + //! @name Accessors + //@{ + std::string mimeType() const; + //@} + + protected: + /*! + @brief Check for a valid tag and decode the block at the current IO + position. Calls tagDecoder() or skips to next tag, if required. + */ + void decodeBlock(); + /*! + @brief Interpret tag information, and call the respective function + to save it in the respective XMP container. Decodes a Tag + Information and saves it in the respective XMP container, if + the block size is small. + @param buf Data buffer which cotains tag ID. + @param size Size of the data block used to store Tag Information. + */ + void tagDecoder(Exiv2::DataBuf & buf, unsigned long size); + + private: + /*! + @brief Interpret file type of the video, and save it + in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void fileTypeDecoder(unsigned long size); + /*! + @brief Interpret Media Header Tag, and save it + in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void mediaHeaderDecoder(unsigned long size); + /*! + @brief Interpret Video Header Tag, and save it + in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void videoHeaderDecoder(unsigned long size); + /*! + @brief Interpret Movie Header Tag, and save it + in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void movieHeaderDecoder(unsigned long size); + /*! + @brief Interpret Track Header Tag, and save it + in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void trackHeaderDecoder(unsigned long size); + /*! + @brief Interpret Handler Tag, and save it + in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void handlerDecoder(unsigned long size); + /*! + @brief Interpret Tag which contain other sub-tags, + and save it in the respective XMP container. + */ + void multipleEntriesDecoder(); + /*! + @brief Interpret Sample Description Tag, and save it + in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void sampleDesc(unsigned long size); + /*! + @brief Interpret Image Description Tag, and save it + in the respective XMP container. + */ + void imageDescDecoder(); + /*! + @brief Interpret User Data Tag, and save it + in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void userDataDecoder(unsigned long size); + /*! + @brief Interpret Nikon Tag, and save it + in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void NikonTagsDecoder(unsigned long size); + /*! + @brief Interpret Audio Description Tag, and save it + in the respective XMP container. + */ + void audioDescDecoder(); + /*! + @brief Helps to calculate Frame Rate from timeToSample chunk, + and save it in the respective XMP container. + */ + void timeToSampleDecoder(); + /*! + @brief Recognizes which stream is currently under processing, + and save its information in currentStream_ . + */ + void setMediaStream(); + /*! + @brief Used to discard a tag along with its data. The Tag will + be skipped and not decoded. + @param size Size of the data block that is to skipped. + */ + void discard(unsigned long size); + /*! + @brief Calculates Aspect Ratio of a video, and stores it in the + respective XMP container. + */ + void aspectRatio(); + + private: + //! @name NOT Implemented + //@{ + //! Copy constructor + QuickTimeVideo(const QuickTimeVideo& rhs); + //! Assignment operator + QuickTimeVideo& operator=(const QuickTimeVideo& rhs); + //@} + + private: + //! Variable which stores Time Scale unit, used to calculate time. + uint64_t timeScale_; + //! Variable which stores current stream being processsed. + int currentStream_; + //! Variable to check the end of metadata traversing. + bool continueTraversing_; + //! Variable to store height and width of a video frame. + uint64_t height_, width_; + + }; //QuickTimeVideo End + +// ***************************************************************************** +// template, inline and free functions + + // These could be static private functions on Image subclasses but then + // ImageFactory needs to be made a friend. + /*! + @brief Create a new QuicktimeVideo instance and return an auto-pointer to it. + Caller owns the returned object and the auto-pointer ensures that + it will be deleted. + */ + EXIV2API Image::AutoPtr newQTimeInstance(BasicIo::AutoPtr io, bool create); + + //! Check if the file iIo is a Quick Time Video. + EXIV2API bool isQTimeType(BasicIo& iIo, bool advance); + +} // namespace Exiv2 + +#endif // QUICKTIMEVIDEO_HPP diff --git a/src/riffvideo.cpp b/src/riffvideo.cpp new file mode 100644 index 00000000..2fd001a0 --- /dev/null +++ b/src/riffvideo.cpp @@ -0,0 +1,1196 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004-2012 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/* + File: riffvideo.cpp + Version: $Rev$ + Author(s): Abhinav Badola for GSoC 2012 (AB) + History: 18-Jun-12, AB: created + Credits: See header file + */ +// ***************************************************************************** +#include "rcsid_int.hpp" +EXIV2_RCSID("@(#) $Id$") + +// ***************************************************************************** +// included header files +#include "riffvideo.hpp" +#include "futils.hpp" +#include "basicio.hpp" +#include "tags.hpp" +#include "tags_int.hpp" +#include "types.hpp" +#include "tiffimage_int.hpp" +// + standard includes +#include + +// ***************************************************************************** +// class member definitions +namespace Exiv2 { + namespace Internal { + + /*! + @brief Dummy TIFF header structure. + */ + class DummyTiffHeader : public TiffHeaderBase { + public: + //! @name Creators + //@{ + //! Default constructor + DummyTiffHeader(ByteOrder byteOrder); + //! Destructor + ~DummyTiffHeader(); + //@} + + //! @name Manipulators + //@{ + //! Dummy read function. Does nothing and returns true. + bool read(const byte* pData, uint32_t size); + //@} + + }; // class TiffHeader + + DummyTiffHeader::DummyTiffHeader(ByteOrder byteOrder) + : TiffHeaderBase(42, 0, byteOrder, 0) + { + } + + DummyTiffHeader::~DummyTiffHeader() + { + } + + bool DummyTiffHeader::read(const byte* /*pData*/, uint32_t /*size*/) + { + return true; + } + + extern const TagVocabulary infoTags[] = { + { "AGES", "Xmp.video.Rated" }, + { "CMNT", "Xmp.video.Comment" }, + { "CODE", "Xmp.video.EncodedBy" }, + { "COMM", "Xmp.video.Comment" }, + { "DIRC", "Xmp.video.Director" }, + { "DISP", "Xmp.audio.SchemeTitle" }, + { "DTIM", "Xmp.video.DateTimeOriginal" }, + { "GENR", "Xmp.video.Genre" }, + { "IARL", "Xmp.video.ArchivalLocation" }, + { "IART", "Xmp.video.Artist" }, + { "IAS1", "Xmp.video.Edit1" }, + { "IAS2", "Xmp.video.Edit2" }, + { "IAS3", "Xmp.video.Edit3" }, + { "IAS4", "Xmp.video.Edit4" }, + { "IAS5", "Xmp.video.Edit5" }, + { "IAS6", "Xmp.video.Edit6" }, + { "IAS7", "Xmp.video.Edit7" }, + { "IAS8", "Xmp.video.Edit8" }, + { "IAS9", "Xmp.video.Edit9" }, + { "IBSU", "Xmp.video.BaseURL" }, + { "ICAS", "Xmp.audio.DefaultStream" }, + { "ICDS", "Xmp.video.CostumeDesigner" }, + { "ICMS", "Xmp.video.Commissioned" }, + { "ICMT", "Xmp.video.Comment" }, + { "ICNM", "Xmp.video.Cinematographer" }, + { "ICNT", "Xmp.video.Country" }, + { "ICOP", "Xmp.video.Copyright" }, + { "ICRD", "Xmp.video.DateTimeDigitized" }, + { "ICRP", "Xmp.video.Cropped" }, + { "IDIM", "Xmp.video.Dimensions " }, + { "IDPI", "Xmp.video.DotsPerInch" }, + { "IDST", "Xmp.video.DistributedBy" }, + { "IEDT", "Xmp.video.EditedBy" }, + { "IENC", "Xmp.video.EncodedBy" }, + { "IENG", "Xmp.video.Engineer" }, + { "IGNR", "Xmp.video.Genre" }, + { "IKEY", "Xmp.video.PerformerKeywords" }, + { "ILGT", "Xmp.video.Lightness" }, + { "ILGU", "Xmp.video.LogoURL" }, + { "ILIU", "Xmp.video.LogoIconURL" }, + { "ILNG", "Xmp.video.Language" }, + { "IMBI", "Xmp.video.InfoBannerImage" }, + { "IMBU", "Xmp.video.InfoBannerURL" }, + { "IMED", "Xmp.video.Medium" }, + { "IMIT", "Xmp.video.InfoText" }, + { "IMIU", "Xmp.video.InfoURL" }, + { "IMUS", "Xmp.video.MusicBy" }, + { "INAM", "Xmp.video.Title" }, + { "IPDS", "Xmp.video.ProductionDesigner" }, + { "IPLT", "Xmp.video.NumOfColors" }, + { "IPRD", "Xmp.video.Product" }, + { "IPRO", "Xmp.video.ProducedBy" }, + { "IRIP", "Xmp.video.RippedBy" }, + { "IRTD", "Xmp.video.Rating" }, + { "ISBJ", "Xmp.video.Subject" }, + { "ISFT", "Xmp.video.Software" }, + { "ISGN", "Xmp.video.SecondaryGenre" }, + { "ISHP", "Xmp.video.Sharpness" }, + { "ISRC", "Xmp.video.Source" }, + { "ISRF", "Xmp.video.SourceForm" }, + { "ISTD", "Xmp.video.ProductionStudio" }, + { "ISTR", "Xmp.video.Starring" }, + { "ITCH", "Xmp.video.Technician" }, + { "IWMU", "Xmp.video.WatermarkURL" }, + { "IWRI", "Xmp.video.WrittenBy" }, + { "LANG", "Xmp.video.Language" }, + { "LOCA", "Xmp.video.LocationInfo" }, + { "PRT1", "Xmp.video.Part" }, + { "PRT2", "Xmp.video.NumOfParts" }, + { "RATE", "Xmp.video.Rate" }, + { "STAR", "Xmp.video.Starring" }, + { "STAT", "Xmp.video.Statistics" }, + { "TAPE", "Xmp.video.TapeName" }, + { "TCDO", "Xmp.video.EndTimecode" }, + { "TCOD", "Xmp.video.StartTimecode" }, + { "TITL", "Xmp.video.Title" }, + { "TLEN", "Xmp.video.Length" }, + { "TORG", "Xmp.video.Organization" }, + { "TRCK", "Xmp.video.TrackNumber" }, + { "TURL", "Xmp.video.URL" }, + { "TVER", "Xmp.video.SoftwareVersion" }, + { "VMAJ", "Xmp.video.VegasVersionMajor" }, + { "VMIN", "Xmp.video.VegasVersionMinor" }, + { "YEAR", "Xmp.video.Year" } + }; + + extern const TagDetails audioEncodingValues[] = { + { 0x1, "Microsoft PCM" }, + { 0x2, "Microsoft ADPCM" }, + { 0x3, "Microsoft IEEE float" }, + { 0x4, "Compaq VSELP" }, + { 0x5, "IBM CVSD" }, + { 0x6, "Microsoft a-Law" }, + { 0x7, "Microsoft u-Law" }, + { 0x8, "Microsoft DTS" }, + { 0x9, "DRM" }, + { 0xa, "WMA 9 Speech" }, + { 0xb, "Microsoft Windows Media RT Voice" }, + { 0x10, "OKI-ADPCM" }, + { 0x11, "Intel IMA/DVI-ADPCM" }, + { 0x12, "Videologic Mediaspace ADPCM" }, + { 0x13, "Sierra ADPCM" }, + { 0x14, "Antex G.723 ADPCM" }, + { 0x15, "DSP Solutions DIGISTD" }, + { 0x16, "DSP Solutions DIGIFIX" }, + { 0x17, "Dialoic OKI ADPCM" }, + { 0x18, "Media Vision ADPCM" }, + { 0x19, "HP CU" }, + { 0x1a, "HP Dynamic Voice" }, + { 0x20, "Yamaha ADPCM" }, + { 0x21, "SONARC Speech Compression" }, + { 0x22, "DSP Group True Speech" }, + { 0x23, "Echo Speech Corp." }, + { 0x24, "Virtual Music Audiofile AF36" }, + { 0x25, "Audio Processing Tech." }, + { 0x26, "Virtual Music Audiofile AF10" }, + { 0x27, "Aculab Prosody 1612" }, + { 0x28, "Merging Tech. LRC" }, + { 0x30, "Dolby AC2" }, + { 0x31, "Microsoft GSM610" }, + { 0x32, "MSN Audio" }, + { 0x33, "Antex ADPCME" }, + { 0x34, "Control Resources VQLPC" }, + { 0x35, "DSP Solutions DIGIREAL" }, + { 0x36, "DSP Solutions DIGIADPCM" }, + { 0x37, "Control Resources CR10" }, + { 0x38, "Natural MicroSystems VBX ADPCM" }, + { 0x39, "Crystal Semiconductor IMA ADPCM" }, + { 0x3a, "Echo Speech ECHOSC3" }, + { 0x3b, "Rockwell ADPCM" }, + { 0x3c, "Rockwell DIGITALK" }, + { 0x3d, "Xebec Multimedia" }, + { 0x40, "Antex G.721 ADPCM" }, + { 0x41, "Antex G.728 CELP" }, + { 0x42, "Microsoft MSG723" }, + { 0x43, "IBM AVC ADPCM" }, + { 0x45, "ITU-T G.726" }, + { 0x50, "Microsoft MPEG" }, + { 0x51, "RT23 or PAC" }, + { 0x52, "InSoft RT24" }, + { 0x53, "InSoft PAC" }, + { 0x55, "MP3" }, + { 0x59, "Cirrus" }, + { 0x60, "Cirrus Logic" }, + { 0x61, "ESS Tech. PCM" }, + { 0x62, "Voxware Inc." }, + { 0x63, "Canopus ATRAC" }, + { 0x64, "APICOM G.726 ADPCM" }, + { 0x65, "APICOM G.722 ADPCM" }, + { 0x66, "Microsoft DSAT" }, + { 0x67, "Micorsoft DSAT DISPLAY" }, + { 0x69, "Voxware Byte Aligned" }, + { 0x70, "Voxware AC8" }, + { 0x71, "Voxware AC10" }, + { 0x72, "Voxware AC16" }, + { 0x73, "Voxware AC20" }, + { 0x74, "Voxware MetaVoice" }, + { 0x75, "Voxware MetaSound" }, + { 0x76, "Voxware RT29HW" }, + { 0x77, "Voxware VR12" }, + { 0x78, "Voxware VR18" }, + { 0x79, "Voxware TQ40" }, + { 0x7a, "Voxware SC3" }, + { 0x7b, "Voxware SC3" }, + { 0x80, "Soundsoft" }, + { 0x81, "Voxware TQ60" }, + { 0x82, "Microsoft MSRT24" }, + { 0x83, "AT&T G.729A" }, + { 0x84, "Motion Pixels MVI MV12" }, + { 0x85, "DataFusion G.726" }, + { 0x86, "DataFusion GSM610" }, + { 0x88, "Iterated Systems Audio" }, + { 0x89, "Onlive" }, + { 0x8a, "Multitude, Inc. FT SX20" }, + { 0x8b, "Infocom ITS A/S G.721 ADPCM" }, + { 0x8c, "Convedia G729" }, + { 0x8d, "Not specified congruency, Inc." }, + { 0x91, "Siemens SBC24" }, + { 0x92, "Sonic Foundry Dolby AC3 APDIF" }, + { 0x93, "MediaSonic G.723" }, + { 0x94, "Aculab Prosody 8kbps" }, + { 0x97, "ZyXEL ADPCM" }, + { 0x98, "Philips LPCBB" }, + { 0x99, "Studer Professional Audio Packed" }, + { 0xa0, "Malden PhonyTalk" }, + { 0xa1, "Racal Recorder GSM" }, + { 0xa2, "Racal Recorder G720.a" }, + { 0xa3, "Racal G723.1" }, + { 0xa4, "Racal Tetra ACELP" }, + { 0xb0, "NEC AAC NEC Corporation" }, + { 0xff, "AAC" }, + { 0x100, "Rhetorex ADPCM" }, + { 0x101, "IBM u-Law" }, + { 0x102, "IBM a-Law" }, + { 0x103, "IBM ADPCM" }, + { 0x111, "Vivo G.723" }, + { 0x112, "Vivo Siren" }, + { 0x120, "Philips Speech Processing CELP" }, + { 0x121, "Philips Speech Processing GRUNDIG" }, + { 0x123, "Digital G.723" }, + { 0x125, "Sanyo LD ADPCM" }, + { 0x130, "Sipro Lab ACEPLNET" }, + { 0x131, "Sipro Lab ACELP4800" }, + { 0x132, "Sipro Lab ACELP8V3" }, + { 0x133, "Sipro Lab G.729" }, + { 0x134, "Sipro Lab G.729A" }, + { 0x135, "Sipro Lab Kelvin" }, + { 0x136, "VoiceAge AMR" }, + { 0x140, "Dictaphone G.726 ADPCM" }, + { 0x150, "Qualcomm PureVoice" }, + { 0x151, "Qualcomm HalfRate" }, + { 0x155, "Ring Zero Systems TUBGSM" }, + { 0x160, "Microsoft Audio1" }, + { 0x161, "Windows Media Audio V2 V7 V8 V9 / DivX audio (WMA) / Alex AC3 Audio" }, + { 0x162, "Windows Media Audio Professional V9" }, + { 0x163, "Windows Media Audio Lossless V9" }, + { 0x164, "WMA Pro over S/PDIF" }, + { 0x170, "UNISYS NAP ADPCM" }, + { 0x171, "UNISYS NAP ULAW" }, + { 0x172, "UNISYS NAP ALAW" }, + { 0x173, "UNISYS NAP 16K" }, + { 0x174, "MM SYCOM ACM SYC008 SyCom Technologies" }, + { 0x175, "MM SYCOM ACM SYC701 G726L SyCom Technologies" }, + { 0x176, "MM SYCOM ACM SYC701 CELP54 SyCom Technologies" }, + { 0x177, "MM SYCOM ACM SYC701 CELP68 SyCom Technologies" }, + { 0x178, "Knowledge Adventure ADPCM" }, + { 0x180, "Fraunhofer IIS MPEG2AAC" }, + { 0x190, "Digital Theater Systems DTS DS" }, + { 0x200, "Creative Labs ADPCM" }, + { 0x202, "Creative Labs FASTSPEECH8" }, + { 0x203, "Creative Labs FASTSPEECH10" }, + { 0x210, "UHER ADPCM" }, + { 0x215, "Ulead DV ACM" }, + { 0x216, "Ulead DV ACM" }, + { 0x220, "Quarterdeck Corp." }, + { 0x230, "I-Link VC" }, + { 0x240, "Aureal Semiconductor Raw Sport" }, + { 0x241, "ESST AC3" }, + { 0x250, "Interactive Products HSX" }, + { 0x251, "Interactive Products RPELP" }, + { 0x260, "Consistent CS2" }, + { 0x270, "Sony SCX" }, + { 0x271, "Sony SCY" }, + { 0x272, "Sony ATRAC3" }, + { 0x273, "Sony SPC" }, + { 0x280, "TELUM Telum Inc." }, + { 0x281, "TELUMIA Telum Inc." }, + { 0x285, "Norcom Voice Systems ADPCM" }, + { 0x300, "Fujitsu FM TOWNS SND" }, + { 0x301, "Fujitsu (not specified)" }, + { 0x302, "Fujitsu (not specified)" }, + { 0x303, "Fujitsu (not specified)" }, + { 0x304, "Fujitsu (not specified)" }, + { 0x305, "Fujitsu (not specified)" }, + { 0x306, "Fujitsu (not specified)" }, + { 0x307, "Fujitsu (not specified)" }, + { 0x308, "Fujitsu (not specified)" }, + { 0x350, "Micronas Semiconductors, Inc. Development" }, + { 0x351, "Micronas Semiconductors, Inc. CELP833" }, + { 0x400, "Brooktree Digital" }, + { 0x401, "Intel Music Coder (IMC)" }, + { 0x402, "Ligos Indeo Audio" }, + { 0x450, "QDesign Music" }, + { 0x500, "On2 VP7 On2 Technologies" }, + { 0x501, "On2 VP6 On2 Technologies" }, + { 0x680, "AT&T VME VMPCM" }, + { 0x681, "AT&T TCP" }, + { 0x700, "YMPEG Alpha (dummy for MPEG-2 compressor)" }, + { 0x8ae, "ClearJump LiteWave (lossless)" }, + { 0x1000, "Olivetti GSM" }, + { 0x1001, "Olivetti ADPCM" }, + { 0x1002, "Olivetti CELP" }, + { 0x1003, "Olivetti SBC" }, + { 0x1004, "Olivetti OPR" }, + { 0x1100, "Lernout & Hauspie" }, + { 0x1101, "Lernout & Hauspie CELP codec" }, + { 0x1102, "Lernout & Hauspie SBC codec" }, + { 0x1103, "Lernout & Hauspie SBC codec" }, + { 0x1104, "Lernout & Hauspie SBC codec" }, + { 0x1400, "Norris Comm. Inc." }, + { 0x1401, "ISIAudio" }, + { 0x1500, "AT&T Soundspace Music Compression" }, + { 0x181c, "VoxWare RT24 speech codec" }, + { 0x181e, "Lucent elemedia AX24000P Music codec" }, + { 0x1971, "Sonic Foundry LOSSLESS" }, + { 0x1979, "Innings Telecom Inc. ADPCM" }, + { 0x1c07, "Lucent SX8300P speech codec" }, + { 0x1c0c, "Lucent SX5363S G.723 compliant codec" }, + { 0x1f03, "CUseeMe DigiTalk (ex-Rocwell)" }, + { 0x1fc4, "NCT Soft ALF2CD ACM" }, + { 0x2000, "FAST Multimedia DVM" }, + { 0x2001, "Dolby DTS (Digital Theater System)" }, + { 0x2002, "RealAudio 1 / 2 14.4" }, + { 0x2003, "RealAudio 1 / 2 28.8" }, + { 0x2004, "RealAudio G2 / 8 Cook (low bitrate)" }, + { 0x2005, "RealAudio 3 / 4 / 5 Music (DNET)" }, + { 0x2006, "RealAudio 10 AAC (RAAC)" }, + { 0x2007, "RealAudio 10 AAC+ (RACP)" }, + { 0x2500, "Reserved range to 0x2600 Microsoft" }, + { 0x3313, "makeAVIS (ffvfw fake AVI sound from AviSynth scripts)" }, + { 0x4143, "Divio MPEG-4 AAC audio" }, + { 0x4201, "Nokia adaptive multirate" }, + { 0x4243, "Divio G726 Divio, Inc." }, + { 0x434c, "LEAD Speech" }, + { 0x564c, "LEAD Vorbis" }, + { 0x5756, "WavPack Audio" }, + { 0x674f, "Ogg Vorbis (mode 1)" }, + { 0x6750, "Ogg Vorbis (mode 2)" }, + { 0x6751, "Ogg Vorbis (mode 3)" }, + { 0x676f, "Ogg Vorbis (mode 1+)" }, + { 0x6770, "Ogg Vorbis (mode 2+)" }, + { 0x6771, "Ogg Vorbis (mode 3+)" }, + { 0x7000, "3COM NBX 3Com Corporation" }, + { 0x706d, "FAAD AAC" }, + { 0x7a21, "GSM-AMR (CBR, no SID)" }, + { 0x7a22, "GSM-AMR (VBR, including SID)" }, + { 0xa100, "Comverse Infosys Ltd. G723 1" }, + { 0xa101, "Comverse Infosys Ltd. AVQSBC" }, + { 0xa102, "Comverse Infosys Ltd. OLDSBC" }, + { 0xa103, "Symbol Technologies G729A" }, + { 0xa104, "VoiceAge AMR WB VoiceAge Corporation" }, + { 0xa105, "Ingenient Technologies Inc. G726" }, + { 0xa106, "ISO/MPEG-4 advanced audio Coding" }, + { 0xa107, "Encore Software Ltd G726" }, + { 0xa109, "Speex ACM Codec xiph.org" }, + { 0xdfac, "DebugMode SonicFoundry Vegas FrameServer ACM Codec" }, + { 0xe708, "Unknown -" }, + { 0xf1ac, "Free Lossless Audio Codec FLAC" }, + { 0xfffe, "Extensible" }, + { 0xffff, "Development" } + }; + + extern const TagDetails nikonAVITags[] = { + { 0x0003, "Xmp.video.Make" }, + { 0x0004, "Xmp.video.Model" }, + { 0x0005, "Xmp.video.Software" }, + { 0x0006, "Xmp.video.Equipment" }, + { 0x0007, "Xmp.video.Orientation" }, + { 0x0008, "Xmp.video.ExposureTime" }, + { 0x0009, "Xmp.video.FNumber" }, + { 0x000a, "Xmp.video.ExposureCompensation" }, + { 0x000b, "Xmp.video.MaxApertureValue" }, + { 0x000c, "Xmp.video.MeteringMode" }, + { 0x000f, "Xmp.video.FocalLength" }, + { 0x0010, "Xmp.video.XResolution" }, + { 0x0011, "Xmp.video.YResolution" }, + { 0x0012, "Xmp.video.ResolutionUnit" }, + { 0x0013, "Xmp.video.DateTimeOriginal" }, + { 0x0014, "Xmp.video.DateTimeDigitized" }, + { 0x0016, "Xmp.video.duration" }, + { 0x0018, "Xmp.video.FocusMode" }, + { 0x001b, "Xmp.video.DigitalZoomRatio" }, + { 0x001d, "Xmp.video.ColorMode" }, + { 0x001e, "Xmp.video.Sharpness" }, + { 0x001f, "Xmp.video.WhiteBalance" }, + { 0x0020, "Xmp.video.ColorNoiseReduction" } + }; + + /* + extern const TagDetails orientation[] = { + { 1, "Horizontal (normal)" }, + { 2, "Mirror horizontal" }, + { 3, "Rotate 180" }, + { 4, "Mirror vertical" }, + { 5, "Mirror horizontal and rotate 270 CW" }, + { 6, "Rotate 90 CW" }, + { 7, "Mirror horizontal and rotate 90 CW" }, + { 8, "Rotate 270 CW" } + }; + */ + extern const TagDetails meteringMode[] = { + { 0, "Unknown" }, + { 1, "Average" }, + { 2, "Center-weighted average" }, + { 3, "Spot" }, + { 4, "Multi-spot" }, + { 5, "Multi-segment" }, + { 6, "Partial" }, + { 255, "Other" } + }; + + extern const TagDetails resolutionUnit[] = { + { 1, "None" }, + { 2, "inches" }, + { 3, "cm" } + }; + + /*! + @brief Function used to check equality of a Tags with a + particular string (ignores case while comparing). + @param buf Data buffer that will contain Tag to compare + @param str char* Pointer to string + @return Returns true if the buffer value is equal to string. + */ + bool equalsRiffTag(Exiv2::DataBuf& buf ,const char* str) { + for(int i = 0; i < 4; i++ ) + if(toupper(buf.pData_[i]) != str[i]) + return false; + return true; + } + + enum streamTypeInfo { + Audio = 1, MIDI, Text, Video + }; + enum streamHeaderTags { + codec = 1, sampleRate = 5, sampleCount = 8, quality = 10, sampleSize + }; + enum bmptags { + imageWidth, imageHeight, planes, bitDepth, compression, imageLength, pixelsPerMeterX, pixelsPerMeterY, numColors, numImportantColors + }; + enum audioFormatTags { + encoding, numberOfChannels, audioSampleRate, avgBytesPerSec = 4, bitsPerSample = 7 + }; + enum aviHeaderTags { + frameRate, maxDataRate, frameCount = 4, streamCount = 6, imageWidth_h = 8, imageHeight_h + }; +}} // namespace Internal, Exiv2 + +namespace Exiv2 { + using namespace Exiv2::Internal; + + RiffVideo::RiffVideo(BasicIo::AutoPtr io) + : Image(ImageType::riff, mdNone, io) + { + } // RiffVideo::RiffVideo + + std::string RiffVideo::mimeType() const + { + return "video/riff"; + } + + void RiffVideo::writeMetadata() + { + } // RiffVideo::writeMetadata + + void RiffVideo::readMetadata() + { + if (io_->open() != 0) throw Error(9, io_->path(), strError()); + + // Ensure that this is the correct image type + if (!isRiffType(*io_, false)) { + if (io_->error() || io_->eof()) throw Error(14); + throw Error(3, "RIFF"); + } + + IoCloser closer(*io_); + clearMetadata(); + continueTraversing_ = true; + + xmpData_["Xmp.video.FileSize"] = (double)io_->size()/(double)1048576; + xmpData_["Xmp.video.FileName"] = io_->path(); + xmpData_["Xmp.video.MimeType"] = mimeType(); + + const long bufMinSize = 4; + DataBuf buf(bufMinSize); + buf.pData_[4] = '\0'; + + io_->read(buf.pData_, bufMinSize); + xmpData_["Xmp.video.Container"] = buf.pData_; + + io_->read(buf.pData_, bufMinSize); + io_->read(buf.pData_, bufMinSize); + xmpData_["Xmp.video.FileType"] = buf.pData_; + + while (continueTraversing_) decodeBlock(); + } // RiffVideo::readMetadata + + void RiffVideo::decodeBlock() + { + const long bufMinSize = 4; + DataBuf buf(bufMinSize); + DataBuf buf2(bufMinSize); + unsigned long size = 0; + buf.pData_[4] = '\0' ; + buf2.pData_[4] = '\0' ; + + io_->read(buf2.pData_, 4); + + if(io_->eof() || equalsRiffTag(buf2, "MOVI") || equalsRiffTag(buf2, "DATA")) { + continueTraversing_ = false; + return; + } + else if(equalsRiffTag(buf2, "HDRL") || equalsRiffTag(buf2, "STRL")) { + decodeBlock(); + } + else { + io_->read(buf.pData_, 4); + size = Exiv2::getULong(buf.pData_, littleEndian); + + tagDecoder(buf2, size); + } + } // RiffVideo::decodeBlock + + void RiffVideo::tagDecoder(Exiv2::DataBuf& buf, unsigned long size) + { + uint64_t cur_pos = io_->tell(); + static bool listFlag = false, listEnd = false; + + if(equalsRiffTag(buf, "LIST")) { + listFlag = true; + listEnd = false; + + while((uint64_t)(io_->tell()) < cur_pos + size) decodeBlock(); + + listEnd = true; + io_->seek(cur_pos + size, BasicIo::beg); + } + else if(equalsRiffTag(buf, "JUNK") && listEnd) { + junkHandler(size); + } + else if(equalsRiffTag(buf, "AVIH")) { + listFlag = false; + aviHeaderTagsHandler(size); + } + else if(equalsRiffTag(buf, "STRH")) { + listFlag = false; + streamHandler(size); + } + else if(equalsRiffTag(buf,"STRF") || equalsRiffTag(buf, "FMT ")) { + listFlag = false; + if(equalsRiffTag(buf,"FMT ")) + streamType_ = Audio; + streamFormatHandler(size); + } + else if(equalsRiffTag(buf, "STRN")) { + listFlag = false; + dateTimeOriginal(size, 1); + } + else if(equalsRiffTag(buf, "STRD")) { + listFlag = false; + streamDataTagHandler(size); + } + else if(equalsRiffTag(buf, "IDIT")) { + listFlag = false; + dateTimeOriginal(size); + } + else if(equalsRiffTag(buf, "INFO")) { + listFlag = false; + infoTagsHandler(); + } + else if(equalsRiffTag(buf, "NCDT")) { + listFlag = false; + nikonTagsHandler(); + } + else if(equalsRiffTag(buf, "ODML")) { + listFlag = false; + odmlTagsHandler(); + } + else if (listFlag) { + // std::cout<<"|unprocessed|"<seek(cur_pos + size, BasicIo::beg); + } + } // RiffVideo::tagDecoder + + void RiffVideo::streamDataTagHandler(long size) + { + const long bufMinSize = 20000; + DataBuf buf(bufMinSize); + buf.pData_[4] = '\0'; + uint64_t cur_pos = io_->tell(); + + io_->read(buf.pData_, 8); + + if(equalsRiffTag(buf, "AVIF")) { + + io_->read(buf.pData_, size - 4); + + IptcData iptcData; + XmpData xmpData; + DummyTiffHeader tiffHeader(littleEndian); + TiffParserWorker::decode(exifData_, + iptcData, + xmpData, + buf.pData_, + buf.size_, + Tag::root, + TiffMapping::findDecoder, + &tiffHeader); + + #ifndef SUPPRESS_WARNINGS + if (!iptcData.empty()) { + EXV_WARNING << "Ignoring IPTC information encoded in the Exif data.\n"; + } + if (!xmpData.empty()) { + EXV_WARNING << "Ignoring XMP information encoded in the Exif data.\n"; + } + #endif + } + // TODO decode CasioData and ZORA Tag + io_->seek(cur_pos + size, BasicIo::beg); + + } // RiffVideo::streamDataTagHandler + + void RiffVideo::dateTimeOriginal(long size, int i) + { + uint64_t cur_pos = io_->tell(); + const long bufMinSize = 100; + DataBuf buf(bufMinSize); + io_->read(buf.pData_, size); + if(!i) + xmpData_["Xmp.video.DateUTC"] = buf.pData_; + else + xmpData_["Xmp.video.StreamName"] = buf.pData_; + io_->seek(cur_pos + size, BasicIo::beg); + } // RiffVideo::dateTimeOriginal + + void RiffVideo::odmlTagsHandler() + { + const long bufMinSize = 100; + DataBuf buf(bufMinSize); + buf.pData_[4] = '\0'; + io_->seek(-12, BasicIo::cur); + io_->read(buf.pData_, 4); + unsigned long size = Exiv2::getULong(buf.pData_, littleEndian); + unsigned long size2 = size; + + uint64_t cur_pos = io_->tell(); + io_->read(buf.pData_, 4); size -= 4; + + while(size > 0) { + io_->read(buf.pData_, 4); size -= 4; + if(equalsRiffTag(buf,"DMLH")) { + io_->read(buf.pData_, 4); size -= 4; + io_->read(buf.pData_, 4); size -= 4; + xmpData_["Xmp.video.TotalFrameCount"] = Exiv2::getULong(buf.pData_, littleEndian); + } + } + io_->seek(cur_pos + size2, BasicIo::beg); + } // RiffVideo::odmlTagsHandler + + void RiffVideo::skipListData() + { + const long bufMinSize = 4; + DataBuf buf(bufMinSize); + buf.pData_[4] = '\0'; + io_->seek(-12, BasicIo::cur); + io_->read(buf.pData_, 4); + unsigned long size = Exiv2::getULong(buf.pData_, littleEndian); + + uint64_t cur_pos = io_->tell(); + io_->seek(cur_pos + size, BasicIo::beg); + } // RiffVideo::skipListData + + void RiffVideo::nikonTagsHandler() + { + const long bufMinSize = 100; + DataBuf buf(bufMinSize), buf2(4); + buf.pData_[4] = '\0'; + io_->seek(-12, BasicIo::cur); + io_->read(buf.pData_, 4); + + unsigned long internal_size = 0, tagID = 0, dataSize = 0, tempSize, size = Exiv2::getULong(buf.pData_, littleEndian); + tempSize = size; char str[9] = " . . . "; + uint64_t internal_pos, cur_pos; internal_pos = cur_pos = io_->tell(); + const TagDetails* td; + io_->read(buf.pData_, 4); tempSize -= 4; + + while(tempSize > 0) { + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 4); + io_->read(buf2.pData_, 4); + int temp = internal_size = Exiv2::getULong(buf2.pData_, littleEndian); + internal_pos = io_->tell(); tempSize -= (internal_size + 8); + + if(equalsRiffTag(buf, "NCVR")) { + while(temp > 3) { + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 2); + tagID = Exiv2::getULong(buf.pData_, littleEndian); + io_->read(buf.pData_, 2); + dataSize = Exiv2::getULong(buf.pData_, littleEndian); + temp -= (4 + dataSize); + + if(tagID == 0x0001) { + io_->read(buf.pData_, dataSize); + xmpData_["Xmp.video.MakerNoteType"] = buf.pData_; + } + else if (tagID == 0x0002) { + while(dataSize) { + std::memset(buf.pData_, 0x0, buf.size_); io_->read(buf.pData_, 1); + str[(4 - dataSize) * 2] = (char)(Exiv2::getULong(buf.pData_, littleEndian) + 48); + --dataSize; + } + xmpData_["Xmp.video.MakerNoteVersion"] = str; + } + } + } + else if(equalsRiffTag(buf, "NCTG")) { + while(temp > 3) { + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 2); + tagID = Exiv2::getULong(buf.pData_, littleEndian); + io_->read(buf.pData_, 2); + dataSize = Exiv2::getULong(buf.pData_, littleEndian); + temp -= (4 + dataSize); + td = find(nikonAVITags , tagID); + io_->read(buf.pData_, dataSize); + + switch (tagID) { + case 0x0003: case 0x0004: case 0x0005: case 0x0006: + case 0x0013: case 0x0014: case 0x0018: case 0x001d: + case 0x001e: case 0x001f: case 0x0020: + xmpData_[exvGettext(td->label_)] = buf.pData_; break; + + case 0x0007: case 0x0010: case 0x0011: case 0x000c: + case 0x0012: + xmpData_[exvGettext(td->label_)] = Exiv2::getULong(buf.pData_, littleEndian); break; + + case 0x0008: case 0x0009: case 0x000a: case 0x000b: + case 0x000f: case 0x001b: case 0x0016: + buf2.pData_[0] = buf.pData_[4]; buf2.pData_[1] = buf.pData_[5]; + buf2.pData_[2] = buf.pData_[6]; buf2.pData_[3] = buf.pData_[7]; + xmpData_[exvGettext(td->label_)] = (double)Exiv2::getLong(buf.pData_, littleEndian) / (double)Exiv2::getLong(buf2.pData_, littleEndian);; + break; + + default: + break; + } + } + } + + else if(equalsRiffTag(buf, "NCTH")) {//TODO Nikon Thumbnail Image + } + + else if(equalsRiffTag(buf, "NCVW")) {//TODO Nikon Preview Image + } + + io_->seek(internal_pos + internal_size, BasicIo::beg); + } + io_->seek(cur_pos + size, BasicIo::beg); + } // RiffVideo::nikonTagsHandler + + void RiffVideo::infoTagsHandler() + { + const long bufMinSize = 100; + DataBuf buf(bufMinSize); + buf.pData_[4] = '\0'; + io_->seek(-12, BasicIo::cur); + io_->read(buf.pData_, 4); + unsigned long infoSize, size = Exiv2::getULong(buf.pData_, littleEndian); + unsigned long size_external = size; + const TagVocabulary* tv; + + uint64_t cur_pos = io_->tell(); + io_->read(buf.pData_, 4); size -= 4; + + while(size > 3) { + io_->read(buf.pData_, 4); size -= 4; + if(!Exiv2::getULong(buf.pData_, littleEndian)) + break; + tv = find(infoTags , Exiv2::toString( buf.pData_)); + io_->read(buf.pData_, 4); size -= 4; + infoSize = Exiv2::getULong(buf.pData_, littleEndian); + + if(infoSize) { + size -= infoSize; + io_->read(buf.pData_, infoSize); + } + + if(tv) + xmpData_[exvGettext(tv->label_)] = buf.pData_; + } + io_->seek(cur_pos + size_external, BasicIo::beg); + } // RiffVideo::infoTagsHandler + + void RiffVideo::junkHandler(long size) + { + const long bufMinSize = size; + DataBuf buf(bufMinSize), buf2(4); + std::memset(buf.pData_, 0x0, buf.size_); + buf.pData_[4] = '\0'; + uint64_t cur_pos = io_->tell(); + + io_->read(buf.pData_, 4); + //! Pentax Metadata and Tags + if(equalsRiffTag(buf, "PENT")) { + + io_->seek(cur_pos + 18, BasicIo::beg); + io_->read(buf.pData_, 26); + xmpData_["Xmp.video.Make"] = buf.pData_; + + io_->read(buf.pData_, 50); + xmpData_["Xmp.video.Model"] = buf.pData_; + + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, 8); + buf2.pData_[0] = buf.pData_[4]; buf2.pData_[1] = buf.pData_[5]; + buf2.pData_[2] = buf.pData_[6]; buf2.pData_[3] = buf.pData_[7]; + xmpData_["Xmp.video.FNumber"] = (double)Exiv2::getLong(buf.pData_, littleEndian) / (double)Exiv2::getLong(buf2.pData_, littleEndian);; + + io_->seek(cur_pos + 131, BasicIo::beg); + io_->read(buf.pData_, 26); + xmpData_["Xmp.video.DateTimeOriginal"] = buf.pData_; + + io_->read(buf.pData_, 26); + xmpData_["Xmp.video.DateTimeDigitized"] = buf.pData_; + + io_->seek(cur_pos + 299, BasicIo::beg); + std::memset(buf.pData_, 0x0, buf.size_); + + io_->read(buf.pData_, 2); + Exiv2::XmpTextValue tv(Exiv2::toString(Exiv2::getLong(buf.pData_, littleEndian))); + xmpData_.add(Exiv2::XmpKey("Xmp.xmp.Thumbnails/xmpGImg:width"), &tv); + + io_->read(buf.pData_, 2); + tv.read(Exiv2::toString(Exiv2::getLong(buf.pData_, littleEndian))); + xmpData_.add(Exiv2::XmpKey("Xmp.xmp.Thumbnails/xmpGImg:height"), &tv); + + io_->read(buf.pData_, 4); + + /* TODO - Storing the image Thumbnail in Base64 Format + + + uint64_t length = Exiv2::getLong(buf.pData_, littleEndian); + io_->read(buf.pData_, length); + + char *rawStr = Exiv2::toString(buf.pData_); + char *encodedStr; + + SXMPUtils::EncodeToBase64(rawStr, encodedStr); + + tv.read(Exiv2::toString(encodedStr)); + xmpData_.add(Exiv2::XmpKey("Xmp.xmp.Thumbnails/xmpGImg:image"), &tv); + */ + } + else { + io_->seek(cur_pos, BasicIo::beg); + io_->read(buf.pData_, size); + xmpData_["Xmp.video.Junk"] = buf.pData_; + } + + io_->seek(cur_pos + size, BasicIo::beg); + } // RiffVideo::junkHandler + + void RiffVideo::aviHeaderTagsHandler(long size) + { + const long bufMinSize = 4; + DataBuf buf(bufMinSize); + buf.pData_[4] = '\0'; + long width = 0, height = 0, frame_count = 0; + double frame_rate = 1; + + uint64_t cur_pos = io_->tell(); + + for(int i = 0; i <= 9; i++) { + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, bufMinSize); + + switch(i) { + case frameRate: + xmpData_["Xmp.video.MicroSecPerFrame"] = Exiv2::getULong(buf.pData_, littleEndian); + frame_rate = (double)1000000/(double)Exiv2::getULong(buf.pData_, littleEndian); + break; + case (maxDataRate): + xmpData_["Xmp.video.MaxDataRate"] = (double)Exiv2::getULong(buf.pData_, littleEndian)/(double)1024; + break; + case frameCount: + xmpData_["Xmp.video.FrameCount"] = Exiv2::getULong(buf.pData_, littleEndian); + frame_count = Exiv2::getULong(buf.pData_, littleEndian); + break; + case streamCount: + xmpData_["Xmp.video.StreamCount"] = Exiv2::getULong(buf.pData_, littleEndian); + break; + case imageWidth_h: + width = Exiv2::getULong(buf.pData_, littleEndian); + xmpData_["Xmp.video.Width"] = width; + break; + case imageHeight_h: + height = Exiv2::getULong(buf.pData_, littleEndian); + xmpData_["Xmp.video.Height"] = height; + break; + } + } + + fillAspectRatio(width, height); + fillDuration(frame_rate, frame_count); + + io_->seek(cur_pos + size, BasicIo::beg); + } // RiffVideo::aviHeaderTagsHandler + + void RiffVideo::streamHandler(long size) + { + const long bufMinSize = 4; + DataBuf buf(bufMinSize); + buf.pData_[4]='\0'; + long divisor = 1; + uint64_t cur_pos = io_->tell(); + + io_->read(buf.pData_, bufMinSize); + if(equalsRiffTag(buf, "VIDS")) + streamType_ = Video; + else if (equalsRiffTag(buf, "AUDS")) + streamType_ = Audio; + + for(int i=1; i<=25; i++) { + std::memset(buf.pData_, 0x0, buf.size_); + io_->read(buf.pData_, bufMinSize); + + switch(i) { + case codec: + if(streamType_ == Video) + xmpData_["Xmp.video.Codec"] = buf.pData_; + else if (streamType_ == Audio) + xmpData_["Xmp.audio.Codec"] = buf.pData_; + else + xmpData_["Xmp.video.Codec"] = buf.pData_; + break; + case sampleRate: + divisor=Exiv2::getULong(buf.pData_, littleEndian); + break; + case (sampleRate+1): + if(streamType_ == Video) + xmpData_["Xmp.video.FrameRate"] = returnSampleRate(buf,divisor); + else if (streamType_ == Audio) + xmpData_["Xmp.audio.SampleRate"] = returnSampleRate(buf,divisor); + else + xmpData_["Xmp.video.StreamSampleRate"] = returnSampleRate(buf,divisor); + break; + case sampleCount: + if(streamType_ == Video) + xmpData_["Xmp.video.FrameCount"] = Exiv2::getULong(buf.pData_, littleEndian); + else if (streamType_ == Audio) + xmpData_["Xmp.audio.SampleCount"] = Exiv2::getULong(buf.pData_, littleEndian); + else + xmpData_["Xmp.video.StreamSampleCount"] = Exiv2::getULong(buf.pData_, littleEndian); + break; + case quality: + if(streamType_ == Video) + xmpData_["Xmp.video.VideoQuality"] = Exiv2::getULong(buf.pData_, littleEndian); + else if(streamType_ != Audio) + xmpData_["Xmp.video.StreamQuality"] = Exiv2::getULong(buf.pData_, littleEndian); + break; + case sampleSize: + if(streamType_ == Video) + xmpData_["Xmp.video.VideoSampleSize"] = Exiv2::getULong(buf.pData_, littleEndian); + else if(streamType_ != Audio) + xmpData_["Xmp.video.StreamSampleSize"] = Exiv2::getULong(buf.pData_, littleEndian); + break; + } + + } + io_->seek(cur_pos + size, BasicIo::beg); + } // RiffVideo::streamHandler + + void RiffVideo::streamFormatHandler(long size) + { + const long bufMinSize = 4; + DataBuf buf(bufMinSize); + buf.pData_[4] = '\0'; + uint64_t cur_pos = io_->tell(); + + if(streamType_ == Video) { + io_->read(buf.pData_, bufMinSize); + + for(int i = 0; i <= 9; i++) { + std::memset(buf.pData_, 0x0, buf.size_); + + switch(i) { + case imageWidth: //Will be used in case of debugging + io_->read(buf.pData_, bufMinSize); break; + case imageHeight: //Will be used in case of debugging + io_->read(buf.pData_, bufMinSize); break; + case planes: + io_->read(buf.pData_, 2); + xmpData_["Xmp.video.Planes"] = Exiv2::getUShort(buf.pData_, littleEndian); break; + case bitDepth: + io_->read(buf.pData_, 2); + xmpData_["Xmp.video.PixelDepth"] = Exiv2::getUShort(buf.pData_, littleEndian); break; + case compression: + io_->read(buf.pData_, bufMinSize); + xmpData_["Xmp.video.Compressor"] = buf.pData_; break; + case imageLength: + io_->read(buf.pData_, bufMinSize); + xmpData_["Xmp.video.ImageLength"] = Exiv2::getULong(buf.pData_, littleEndian); break; + case pixelsPerMeterX: + io_->read(buf.pData_, bufMinSize); + xmpData_["Xmp.video.PixelPerMeterX"] = Exiv2::getULong(buf.pData_, littleEndian); break; + case pixelsPerMeterY: + io_->read(buf.pData_, bufMinSize); + xmpData_["Xmp.video.PixelPerMeterY"] = Exiv2::getULong(buf.pData_, littleEndian); break; + case numColors: + io_->read(buf.pData_, bufMinSize); + if(Exiv2::getULong(buf.pData_, littleEndian) == 0) { + xmpData_["Xmp.video.NumOfColours"] = "Unspecified"; + } + else { + xmpData_["Xmp.video.NumOfColours"] = Exiv2::getULong(buf.pData_, littleEndian); + } + break; + case numImportantColors: + io_->read(buf.pData_, bufMinSize); + if(Exiv2::getULong(buf.pData_, littleEndian)) { + xmpData_["Xmp.video.NumIfImpColours"] = Exiv2::getULong(buf.pData_, littleEndian); + } + else { + xmpData_["Xmp.video.NumOfImpColours"] = "All"; + } + break; + } + } + } + else if(streamType_ == Audio) { + int c = 0; + const TagDetails* td; + for(int i = 0; i <= 7; i++) { + io_->read(buf.pData_, 2); + + switch(i) { + case encoding: + td = find(audioEncodingValues , Exiv2::getUShort(buf.pData_, littleEndian)); + if(td) { + xmpData_["Xmp.audio.Compressor"] = exvGettext(td->label_); + } + else { + xmpData_["Xmp.audio.Compressor"] = Exiv2::getUShort(buf.pData_, littleEndian); + } + break; + case numberOfChannels: + c = Exiv2::getUShort(buf.pData_, littleEndian); + if(c == 1) xmpData_["Xmp.audio.ChannelType"] = "Mono"; + else if(c == 2) xmpData_["Xmp.audio.ChannelType"] = "Stereo"; + else if(c == 5) xmpData_["Xmp.audio.ChannelType"] = "5.1 Surround Sound"; + else if(c == 7) xmpData_["Xmp.audio.ChannelType"] = "7.1 Surround Sound"; + else xmpData_["Xmp.audio.ChannelType"] = "Mono"; + break; + case audioSampleRate: + xmpData_["Xmp.audio.SampleRate"] = Exiv2::getUShort(buf.pData_, littleEndian); + break; + case avgBytesPerSec: + xmpData_["Xmp.audio.SampleType"] = Exiv2::getUShort(buf.pData_, littleEndian); + break; + case bitsPerSample: + xmpData_["Xmp.audio.BitsPerSample"] = Exiv2::getUShort(buf.pData_,littleEndian); + io_->read(buf.pData_, 2); + break; + } + } + } + io_->seek(cur_pos + size, BasicIo::beg); + } // RiffVideo::streamFormatHandler + + double RiffVideo::returnSampleRate(Exiv2::DataBuf& buf, long divisor) + { + return ((double)Exiv2::getULong(buf.pData_, littleEndian) / (double)divisor); + } // RiffVideo::returnSampleRate + + const char* RiffVideo::printAudioEncoding(uint64_t i) + { + const TagDetails* td; + td = find(audioEncodingValues , i); + if(td) + return exvGettext(td->label_); + + return "Undefined"; + } // RiffVideo::printAudioEncoding + + void RiffVideo::fillAspectRatio(long width, long height) + { + double aspectRatio = (double)width / (double)height; + aspectRatio = floor(aspectRatio*10) / 10; + xmpData_["Xmp.video.AspectRatio"] = aspectRatio; + + if(aspectRatio == 1.3) xmpData_["Xmp.video.AspectRatio"] = "4:3"; + else if(aspectRatio == 1.7) xmpData_["Xmp.video.AspectRatio"] = "16:9"; + else if(aspectRatio == 1.0) xmpData_["Xmp.video.AspectRatio"] = "1:1"; + else if(aspectRatio == 1.6) xmpData_["Xmp.video.AspectRatio"] = "16:10"; + else if(aspectRatio == 2.2) xmpData_["Xmp.video.AspectRatio"] = "2.21:1"; + else if(aspectRatio == 2.3) xmpData_["Xmp.video.AspectRatio"] = "2.35:1"; + else if(aspectRatio == 1.2) xmpData_["Xmp.video.AspectRatio"] = "5:4"; + else xmpData_["Xmp.video.AspectRatio"] = aspectRatio; + } // RiffVideo::fillAspectRatio + + void RiffVideo::fillDuration(double frame_rate, long frame_count) + { + if(frame_rate == 0) + return; + + uint64_t duration = (double)frame_count * (double)1000 / (double)frame_rate; + xmpData_["Xmp.video.FileDataRate"] = (double)io_->size()/(double)(1048576*duration); + xmpData_["Xmp.video.Duration"] = duration; //Duration in number of seconds + } // RiffVideo::fillDuration + + Image::AutoPtr newRiffInstance(BasicIo::AutoPtr io, bool /*create*/) + { + Image::AutoPtr image(new RiffVideo(io)); + if (!image->good()) { + image.reset(); + } + return image; + } + + bool isRiffType(BasicIo& iIo, bool advance) + { + const int32_t len = 2; + const unsigned char RiffVideoId[4] = { 'R', 'I', 'F' ,'F'}; + byte buf[len]; + iIo.read(buf, len); + if (iIo.error() || iIo.eof()) { + return false; + } + bool matched = (memcmp(buf, RiffVideoId, len) == 0); + if (!advance || !matched) { + iIo.seek(-len, BasicIo::cur); + } + return matched; + } + +} // namespace Exiv2 diff --git a/src/riffvideo.hpp b/src/riffvideo.hpp new file mode 100644 index 00000000..65f03e01 --- /dev/null +++ b/src/riffvideo.hpp @@ -0,0 +1,215 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004-2012 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/*! + @file riffvideo.hpp + @brief An Image subclass to support RIFF video files + @version $Rev$ + @author Abhinav Badola for GSoC 2012 + mail.abu.to@gmail.com + @date 18-Jun-12, AB: created + */ +#ifndef RIFFVIDEO_HPP +#define RIFFVIDEO_HPP + +// ***************************************************************************** +// included header files +#include "exif.hpp" +#include "image.hpp" +#include "tags_int.hpp" + +// ***************************************************************************** +// namespace extensions +namespace Exiv2 { + +// ***************************************************************************** +// class definitions + + // Add RIFF to the supported image formats + namespace ImageType { + const int riff = 20; //!< Treating riff as an image type> + } + + /*! + @brief Class to access RIFF video files. + */ + class EXIV2API RiffVideo:public Image + { + public: + //! @name Creators + //@{ + /*! + @brief Constructor for a Riff video. Since the constructor + can not return a result, callers should check the good() method + after object construction to determine success or failure. + @param io An auto-pointer that owns a BasicIo instance used for + reading and writing image metadata. \b Important: The constructor + takes ownership of the passed in BasicIo instance through the + auto-pointer. Callers should not continue to use the BasicIo + instance after it is passed to this method. Use the Image::io() + method to get a temporary reference. + */ + RiffVideo(BasicIo::AutoPtr io); + //@} + + //! @name Manipulators + //@{ + void readMetadata(); + void writeMetadata(); + //@} + + //! @name Accessors + //@{ + std::string mimeType() const; + const char* printAudioEncoding(uint64_t i); + //@} + + protected: + /*! + @brief Check for a valid tag and decode the block at the current IO + position. Calls tagDecoder() or skips to next tag, if required. + */ + void decodeBlock(); + /*! + @brief Interpret tag information, and call the respective function + to save it in the respective XMP container. Decodes a Tag + Information and saves it in the respective XMP container, if + the block size is small. + @param buf Data buffer which cotains tag ID. + @param size Size of the data block used to store Tag Information. + */ + void tagDecoder(Exiv2::DataBuf& buf, unsigned long size); + /*! + @brief Interpret Junk tag information, and save it + in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void junkHandler(long size); + /*! + @brief Interpret Stream tag information, and save it + in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void streamHandler(long size); + /*! + @brief Interpret Stream Format tag information, and save it + in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void streamFormatHandler(long size); + /*! + @brief Interpret Riff Header tag information, and save it + in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void aviHeaderTagsHandler(long size); + /*! + @brief Interpret Riff List tag information, and save it + in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void listHandler(long size); + /*! + @brief Interpret Riff Stream Data tag information, and save it + in the respective XMP container. + @param size Size of the data block used to store Tag Information. + */ + void streamDataTagHandler(long size); + /*! + @brief Interpret INFO tag information, and save it + in the respective XMP container. + */ + void infoTagsHandler(); + /*! + @brief Interpret Nikon Tags related to Video information, and + save it in the respective XMP container. + */ + void nikonTagsHandler(); + /*! + @brief Interpret OpenDML tag information, and save it + in the respective XMP container. + */ + void odmlTagsHandler(); + //! @brief Skips Particular Blocks of Metadata List. + void skipListData(); + /*! + @brief Interprets DateTimeOriginal tag or stream name tag + information, and save it in the respective XMP container. + @param size Size of the data block used to store Tag Information. + @param i parameter used to overload function + */ + void dateTimeOriginal(long size, int i = 0); + /*! + @brief Calculates Sample Rate of a particular stream. + @param buf Data buffer with the dividend. + @param divisor The Divisor required to calculate sample rate. + @return Return the sample rate of the stream. + */ + double returnSampleRate(Exiv2::DataBuf& buf, long divisor = 1); + /*! + @brief Calculates Aspect Ratio of a video, and stores it in the + respective XMP container. + @param width Width of the video. + @param height Height of the video. + */ + void fillAspectRatio(long width = 1,long height = 1); + /*! + @brief Calculates Duration of a video, and stores it in the + respective XMP container. + @param frame_rate Frame rate of the video. + @param frame_count Total number of frames present in the video. + */ + void fillDuration(double frame_rate, long frame_count); + + private: + //! @name NOT Implemented + //@{ + //! Copy constructor + RiffVideo(const RiffVideo& rhs); + //! Assignment operator + RiffVideo& operator=(const RiffVideo& rhs); + //@} + + private: + //! Variable to check the end of metadata traversing. + bool continueTraversing_; + //! Variable which stores current stream being processsed. + int streamType_; + + }; //Class RiffVideo + +// ***************************************************************************** +// template, inline and free functions + + // These could be static private functions on Image subclasses but then + // ImageFactory needs to be made a friend. + /*! + @brief Create a new RiffVideo instance and return an auto-pointer to it. + Caller owns the returned object and the auto-pointer ensures that + it will be deleted. + */ + EXIV2API Image::AutoPtr newRiffInstance(BasicIo::AutoPtr io, bool create); + + //! Check if the file iIo is a Riff Video. + EXIV2API bool isRiffType(BasicIo& iIo, bool advance); + +} // namespace Exiv2 + +#endif // RIFFVIDEO_HPP diff --git a/src/tags_int.hpp b/src/tags_int.hpp index 6eaddca4..07d87d0d 100644 --- a/src/tags_int.hpp +++ b/src/tags_int.hpp @@ -186,7 +186,7 @@ namespace Exiv2 { const char* label_; //!< Translation of the tag value //! Comparison operator for use with the find template - bool operator==(long key) const { return val_ == key; } + bool operator==(long key) const { return val_ == key; } }; // struct TagDetails /*! diff --git a/test/Makefile b/test/Makefile index ee3d67fe..6d223542 100644 --- a/test/Makefile +++ b/test/Makefile @@ -73,7 +73,8 @@ TESTS = addmoddel.sh \ write-test.sh \ write2-test.sh \ xmpparser-test.sh \ - conversions.sh + conversions.sh \ + video-test.sh test: ./mingfixup.sh diff --git a/test/data/video/video-asf-unicode.asf b/test/data/video/video-asf-unicode.asf new file mode 100644 index 00000000..16c85397 Binary files /dev/null and b/test/data/video/video-asf-unicode.asf differ diff --git a/test/data/video/video-asf-with-chapters.wmv b/test/data/video/video-asf-with-chapters.wmv new file mode 100644 index 00000000..aa2d2725 Binary files /dev/null and b/test/data/video/video-asf-with-chapters.wmv differ diff --git a/test/data/video/video-asf.wmv b/test/data/video/video-asf.wmv new file mode 100644 index 00000000..3a3df3b3 Binary files /dev/null and b/test/data/video/video-asf.wmv differ diff --git a/test/data/video/video-avi.avi b/test/data/video/video-avi.avi new file mode 100644 index 00000000..deed9097 Binary files /dev/null and b/test/data/video/video-avi.avi differ diff --git a/test/data/video/video-matroska.mkv b/test/data/video/video-matroska.mkv new file mode 100644 index 00000000..e264d7bd Binary files /dev/null and b/test/data/video/video-matroska.mkv differ diff --git a/test/data/video/video-quicktime.mp4 b/test/data/video/video-quicktime.mp4 new file mode 100644 index 00000000..efe3df5d Binary files /dev/null and b/test/data/video/video-quicktime.mp4 differ diff --git a/test/data/video/video-test.out b/test/data/video/video-test.out new file mode 100644 index 00000000..91fab2d7 Binary files /dev/null and b/test/data/video/video-test.out differ diff --git a/test/video-test.sh b/test/video-test.sh new file mode 100755 index 00000000..a52cf8fb --- /dev/null +++ b/test/video-test.sh @@ -0,0 +1,62 @@ +#! /bin/sh +# Test driver for video files +# +# video-asf.wmv http://www.educationalquestions.com/video/DLP_PART_2_768k.wmv +# video-avi.avi http://redmine.yorba.org/attachments/631/Nikon_Coolpix_S3000.AVI +# video-matroska.mkv http://www.bunkus.org/videotools/mkvtoolnix/samples/vsshort-vorbis-subs.mkv +# video-quicktime.mp4 http://dev.exiv2.org/attachments/362/20100709_002.mp4 + +# ---------------------------------------------------------------------- +# Setup +export LC_ALL=C +cd tmp/ +if [ -z "$EXIV2_BINDIR" ] ; then + bin="$VALGRIND ../../src" + samples="$VALGRIND ../../samples" +else + bin="$VALGRIND $EXIV2_BINDIR" + samples="$VALGRIND $EXIV2_BINDIR" +fi +diffargs="--strip-trailing-cr" +if ! diff -q $diffargs /dev/null /dev/null 2>/dev/null ; then + diffargs="" +fi + +# ---------------------------------------------------------------------- +# Tests +( + for file in ../data/video/video-*; do + video="`basename "$file"`" + if [ $video = "video-test.out" ] ; then + continue + fi + + printf "." >&3 + + echo + echo "-----> $video <-----" + + cp "../data/video/$video" ./ + + echo + echo "Command: exiv2 -u -pa $video" + $bin/exiv2 -u -pa "$video" + exitcode="$?" + echo "Exit code: $exitcode" + + if [ "$exitcode" -ne 0 -a "$exitcode" -ne 253 ] ; then + continue + fi + + done +) 3>&1 > "video-test.out" 2>&1 + +echo "." + +# ---------------------------------------------------------------------- +# Result +if ! diff -q $diffargs "../data/video/video-test.out" "video-test.out" ; then + diff -u -a $diffargs "../data/video/video-test.out" "video-test.out" + exit 1 +fi +echo "All testcases passed." diff --git a/xmpsdk/CMakeLists.txt b/xmpsdk/CMakeLists.txt index 30526eff..fc2396f4 100644 --- a/xmpsdk/CMakeLists.txt +++ b/xmpsdk/CMakeLists.txt @@ -9,6 +9,12 @@ include(../CMake_msvc.txt) msvc_runtime_configure(${EXIV2_ENABLE_SHARED}) +FOREACH(_currentfile ${XMPSRC}) + IF(NOT MSVC) + SET_SOURCE_FILES_PROPERTIES(${_currentfile} PROPERTIES COMPILE_FLAGS "-fPIC") + ENDIF(NOT MSVC) +ENDFOREACH(_currentfile ${XMPSRC}) + IF( EXIV2_ENABLE_XMP AND EXIV2_ENABLE_LIBXMP ) ADD_LIBRARY( xmp STATIC ${XMPSRC} ) GET_TARGET_PROPERTY( XMPLIB xmp LOCATION )