You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
exiv2/src/quicktimevideo.cpp

2912 lines
119 KiB
C++

// ***************************************************************** -*- C++ -*-
/*
* Copyright (C) 2004-2013 Andreas Huggel <ahuggel@gmx.net>
*
* 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) <mail.abu.to@gmail.com>
Mahesh Hegde for GSoC 2013 <maheshmhegade at gmail dot com>
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"
#include "utilsvideo.hpp"
// + standard includes
#include <cmath>
#include <iostream>
#ifndef _MSC_VER
#define stricmp strcasecmp
#endif
typedef void (Exiv2::QuickTimeVideo::*decFunc)(uint32_t);
// *****************************************************************************
// class member definitions
namespace Exiv2{
namespace Internal{
//! File type information.
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" },
};
//! hadler Class Information.
extern const TagVocabulary handlerClassTags[] ={
{ "dhlr", "Data Handler" },
{ "mhlr", "Media Handler" }
};
//! Handler Type Information.
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" }
};
//! Vendor Id information.
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" }
};
//! Camera Information.
extern const TagVocabulary cameraByteOrderTags[] ={
{ "II", "Little-endian (Intel, II)" },
{ "MM", "Big-endian (Motorola, MM)" }
};
//! Graphics related Information.
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" }
};
//! Media Language Codes.
extern const TagDetails mediaLanguageCode[] ={
{ 0x0, "English" },
{ 0x1, "French" },
{ 0x2, "German" },
{ 0x3, "Italian" },
{ 0x4, "Dutch" },
{ 0x5, "Swedish" },
{ 0x6, "Spanish" },
{ 0x7, "Danish" },
{ 0x8, "Portuguese" },
{ 0x9, "Norwegian" },
{ 0xa, "Hebrew" },
{ 0xb, "Japanese" },
{ 0xc, "Arabic" },
{ 0xd, "Finnish" },
{ 0xe, "Greek" },
{ 0xf, "Icelandic" },
{ 0x10, "Maltese" },
{ 0x11, "Turkish" },
{ 0x12, "Croatian" },
{ 0x13, "Traditional Chinese" },
{ 0x14, "Urdu" },
{ 0x15, "Hindi" },
{ 0x16, "Thai" },
{ 0x17, "Korean" },
{ 0x18, "Lithuanian" },
{ 0x19, "Polish" },
{ 0x1a, "Hungarian" },
{ 0x1b, "Estonian" },
{ 0x1c, "Lettish" },
{ 0x1c, "Latvian" },
{ 0x1d, "Saami" },
{ 0x1d, "Sami" },
{ 0x1e, "Faroese" },
{ 0x1f, "Farsi" },
{ 0x20, "Russian" },
{ 0x21, "Simplified Chinese" },
{ 0x22, "Flemish" },
{ 0x23, "Irish" },
{ 0x24, "Albanian" },
{ 0x25, "Romanian" },
{ 0x26, "Czech" },
{ 0x27, "Slovak" },
{ 0x28, "Slovenian" },
{ 0x29, "Yiddish" },
{ 0x2a, "Serbian" },
{ 0x2b, "Macedonian" },
{ 0x2c, "Bulgarian" },
{ 0x2d, "Ukrainian" },
{ 0x2e, "Belarusian" },
{ 0x2f, "Uzbek" },
{ 0x30, "Kazakh" },
{ 0x31, "Azerbaijani" },
{ 0x32, "AzerbaijanAr" },
{ 0x33, "Armenian" },
{ 0x34, "Georgian" },
{ 0x35, "Moldavian" },
{ 0x36, "Kirghiz" },
{ 0x37, "Tajiki" },
{ 0x38, "Turkmen" },
{ 0x39, "Mongolian" },
{ 0x3a, "MongolianCyr" },
{ 0x3b, "Pashto" },
{ 0x3c, "Kurdish" },
{ 0x3d, "Kashmiri" },
{ 0x3e, "Sindhi" },
{ 0x3f, "Tibetan" },
{ 0x40, "Nepali" },
{ 0x41, "Sanskrit" },
{ 0x42, "Marathi" },
{ 0x43, "Bengali" },
{ 0x44, "Assamese" },
{ 0x45, "Gujarati" },
{ 0x46, "Punjabi" },
{ 0x47, "Oriya" },
{ 0x48, "Malayalam" },
{ 0x49, "Kannada" },
{ 0x4a, "Tamil" },
{ 0x4b, "Telugu" },
{ 0x4c, "Sinhala" },
{ 0x4d, "Burmese" },
{ 0x4e, "Khmer" },
{ 0x4f, "Lao" },
{ 0x50, "Vietnamese" },
{ 0x51, "Indonesian" },
{ 0x52, "Tagalog" },
{ 0x53, "MalayRoman" },
{ 0x54, "MalayArabic" },
{ 0x55, "Amharic" },
{ 0x56, "Galla" },
{ 0x57, "Oromo" },
{ 0x58, "Somali" },
{ 0x59, "Swahili" },
{ 0x5a, "Kinyarwanda" },
{ 0x5b, "Rundi" },
{ 0x5c, "Nyanja" },
{ 0x5d, "Malagasy" },
{ 0x5e, "Esperanto" },
{ 0x80, "Welsh" },
{ 0x81, "Basque" },
{ 0x82, "Catalan" },
{ 0x83, "Latin" },
{ 0x84, "Quechua" },
{ 0x85, "Guarani" },
{ 0x86, "Aymara" },
{ 0x87, "Tatar" },
{ 0x88, "Uighur" },
{ 0x89, "Dzongkha" },
{ 0x8a, "JavaneseRom" }
};
//! User Date related Information.
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" },
};
//! User Data related Information.
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" },
{ "FFMV", "Xmp.video.StreamName" },
{ "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" },
};
//! Nikon Tags Information.
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" },
};
//! Nikon Color space Information.
extern const TagDetails NikonColorSpace[] ={
{ 1, "sRGB" },
{ 2, "Adobe RGB" },
};
//! Nikon Tags.
extern const TagVocabulary NikonGPS_Latitude_Longitude_ImgDirection_Reference[] =
{
{ "N", "North" },
{ "S", "South" },
{ "E", "East" },
{ "W", "West" },
{ "M", "Magnetic North" },
{ "T", "True North" },
};
//! Nikon Tags
extern const TagDetails NikonGPSAltitudeRef[] ={
{ 0, "Above Sea Level" },
{ 1, "Below Sea Level" },
};
//! Nikon Tags.
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" },
};
//! Nikon Tags.
extern const TagDetails NikonMeteringMode[] ={
{ 0, "Unknown" },
{ 1, "Average" },
{ 2, "Center-weighted average" },
{ 3, "Spot" },
{ 4, "Multi-spot" },
{ 5, "Multi-segment" },
{ 6, "Partial" },
{ 255, "Other" },
};
//! picture tags.
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" }
};
//! Filter Effects.
extern const TagDetails FilterEffect[] ={
{ 0x80, "Off" },
{ 0x81, "Yellow" },
{ 0x82, "Orange" },
{ 0x83, "Red" },
{ 0x84, "Green" },
{ 0xff, "n/a" },
};
//! Toning Effect.
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" },
};
//! White balance Information.
extern const TagDetails whiteBalance[] ={
{ 0, "Auto" },
{ 1, "Daylight" },
{ 2, "Shade" },
{ 3, "Fluorescent" },
{ 4, "Tungsten" },
{ 5, "Manual" },
};
//! To select specific bytes with in a chunk.
enum movieHeaderTags{
MovieHeaderVersion, CreateDate, ModifyDate, TimeScale, Duration, PreferredRate, PreferredVolume,
PreviewTime = 18, PreviewDuration,PosterTime, SelectionTime, SelectionDuration, CurrentTime, NextTrackID
};
//! To select specific bytes with in a chunk.
enum trackHeaderTags{
TrackHeaderVersion, TrackCreateDate, TrackModifyDate, TrackID, TrackDuration = 5, TrackLayer = 8,
TrackVolume, ImageWidth = 19, ImageHeight
};
//! To select specific bytes with in a chunk.
enum mediaHeaderTags{
MediaHeaderVersion, MediaCreateDate, MediaModifyDate, MediaTimeScale, MediaDuration, MediaLanguageCode
};
//! To select specific bytes with in a chunk.
enum handlerTags{
HandlerClass = 1, HandlerType, HandlerVendorID
};
//! To select specific bytes with in a chunk.
enum videoHeaderTags{
GraphicsMode = 2, OpColor
};
//! To select specific bytes with in a chunk.
enum stream{
Video, Audio, Hint, Null, GenMediaHeader
};
//! To select specific bytes with in a chunk.
enum imageDescTags{
codec, VendorID = 4, SourceImageWidth_Height = 7, XResolution,
YResolution, CompressorName = 10, BitDepth
};
//! To select specific bytes with in a chunk.
enum audioDescTags{
AudioFormat, AudioVendorID = 4, AudioChannels, AudioSampleRate = 7, MOV_AudioFormat = 13
};
/*!
* \brief equalsQTimeTag overloaded function to handle tag Array
* It will compare all the tags in a array.
* \param buf Data buffer that will contain Tag to compare.
* \param arr Contains multiple tags to compare from.
* \param arraysize Number of tags in arr.
* \return true if buf matches any one tag in arr.
*/
bool equalsQTimeTag(Exiv2::DataBuf& buf,const char arr[][5],int32_t arraysize)
{
bool result = false;
for ( int32_t i=0; !result && i< arraysize; i++)
result = (bool)(stricmp((const char*)buf.pData_,arr[i+1])==0);
return result;
}
/*!
@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",
};
int32_t i;
for(i = 0 ; i < 13 ; ++i)
if(UtilsVideo::compareTagValue(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[8][5] ={
"moov", "mdia", "minf", "dinf", "alis", "stbl", "cmov",
"meta",
};
int32_t i;
for(i = 0 ; i < 8 ; ++i)
if(UtilsVideo::compareTagValue(buf, ignoreList[i]))
return true;
return false;
}
/*!
* \brief reverseTagVocabulary: reverse attributed of array elements.To perform reverse
* operation of getting keys from description
* \param inputTagVocabulary
* \param outputTagVocabulary
* \param size
*/
void reverseTagVocabulary(const TagVocabulary inputTagVocabulary[],TagVocabulary outputTagVocabulary[] ,int size)
{
int32_t i;
for (i=0; i<size ;i++){
outputTagVocabulary[i].label_ = inputTagVocabulary[i].voc_;
outputTagVocabulary[i].voc_ = inputTagVocabulary[i].label_;
}
}
/*!
* \brief getNumberFromString
* \param stringData: stringdata which is formed by concatinating two distinct
* numerical values
* \param seperator
* \return
*/
const std::vector<uint16_t> getNumberFromString(const std::string stringData,char seperator )
{
int32_t counter = (stringData.size() - 1);
vector<uint16_t> shortValues;
int32_t i;
for(i=0; i<2; i++){
int32_t tmpValueIndex = 3;
Exiv2::byte tmpValue[4] = {};
while((counter >= 0) && (stringData[counter] != seperator) && (tmpValueIndex >=0)){
tmpValue[tmpValueIndex] = stringData[counter];
counter--;tmpValueIndex--;
}
counter--;
shortValues.push_back((int16_t)Exiv2::getShort(tmpValue, bigEndian));
}
return shortValues;
}
/*!
* \brief getLongFromString functionality is same as above method,with return type
* long vector
* \param stringData: stringdata which is formed by concatinating two distinct
* numerical values
* \param seperator: seperation character like . , etc
* \return
*/
const std::vector<int32_t> getLongFromString(const std::string stringData,char seperator )
{
int32_t counter = (stringData.size() - 1);
vector<int32_t> longValues;
for(int32_t i=0; i<2; i++){
int32_t tmpValueIndex = 3;
Exiv2::byte tmpValue[4]={};
while((counter >= 0) && (stringData[counter] != seperator) && (tmpValueIndex >=0)){
tmpValue[tmpValueIndex] = stringData[counter];
counter--;tmpValueIndex--;
}
counter--;
longValues.push_back((int32_t)Exiv2::getLong(tmpValue, bigEndian));
}
return longValues;
}
/*!
@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
@param n
@return Returns a signed 64-bit integer
*/
int64_t returnBufValue(Exiv2::DataBuf& buf, int32_t n = 4)
{
int64_t temp = 0;
for(int32_t i = n - 1; i >= 0; i--)
temp +=
#ifdef _MSC_VER
static_cast<int64_t>(buf.pData_[i]*(pow(256.0, n-i-1)));
#else
buf.pData_[i]*(pow((float)256,n-i-1));
#endif
return temp;
}
/*!
* \brief returnBuf: converts integer value to byte value
* which can be written to the file.
* \param intValue
* \param n
* \return
*/
DataBuf returnBuf(int64_t intValue,int32_t n=4)
{
DataBuf buf((uint32_t)(n+1));
for(int32_t i = n - 1; i >= 0; i--)
buf.pData_[i] = (Exiv2::byte)(int16_t) ((int32_t)(intValue / (int32_t)pow(256.0,n-i-1)) % 256);
return buf;
}
/*!
@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
@param n
@return Returns an unsigned 64-bit integer
*/
uint64_t returnUnsignedBufValue(Exiv2::DataBuf& buf, int32_t n = 4)
{
uint64_t temp = 0;
for(int32_t i = n-1; i >= 0; i--)
#if _MSC_VER
temp = temp + static_cast<uint64_t>(buf.pData_[i]*(pow(static_cast<float>(256), n-i-1)));
#else
temp = temp + buf.pData_[i]*(pow((float)256,n-i-1));
#endif
return temp;
}
/*!
* \brief returnUnsignedBuf converts integer value to byte value
* which can be written to the file.
* \param intValue
* \param n
* \return
*/
DataBuf returnUnsignedBuf(uint64_t intValue,int32_t n=4)
{
DataBuf buf(n+1);
for(int32_t i = n - 1; i >= 0; i--)
buf.pData_[i] = (Exiv2::byte)(uint16_t) ((int32_t)(intValue / (int32_t)pow(256.0,n-i-1)) % 256);
return buf;
}
/*!
@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"
};
int32_t i;
for( 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;
} // isQuickTime
} // namespace Internal
} // namespace Exiv2
namespace Exiv2{
using namespace Exiv2::Internal;
class QuickTimeVideo::Private
{
public:
Private(){
timeScale_ = 0;
currentStream_ = 0;
continueTraversing_ = true;
height_ = 1;
width_ = 1;
m_modifyMetadata = false;
m_decodeMetadata = true;
decoders.insert(std::make_pair("ftyp",&QuickTimeVideo::fileTypeDecoder));
decoders.insert(std::make_pair("trak",&QuickTimeVideo::setMediaStream));
decoders.insert(std::make_pair("mvhd",&QuickTimeVideo::movieHeaderDecoder));
decoders.insert(std::make_pair("tkhd",&QuickTimeVideo::trackHeaderDecoder));
decoders.insert(std::make_pair("mdhd",&QuickTimeVideo::mediaHeaderDecoder));
decoders.insert(std::make_pair("hdlr",&QuickTimeVideo::handlerDecoder));
decoders.insert(std::make_pair("vmhd",&QuickTimeVideo::videoHeaderDecoder));
decoders.insert(std::make_pair("udta",&QuickTimeVideo::userDataDecoder));
decoders.insert(std::make_pair("stsd",&QuickTimeVideo::sampleDesc));
decoders.insert(std::make_pair("dref",&QuickTimeVideo::multipleEntriesDecoder));
decoders.insert(std::make_pair("stts",&QuickTimeVideo::timeToSampleDecoder));
decoders.insert(std::make_pair("pnot",&QuickTimeVideo::previewTagDecoder));
decoders.insert(std::make_pair("tapt",&QuickTimeVideo::trackApertureTagDecoder));
decoders.insert(std::make_pair("keys",&QuickTimeVideo::keysTagDecoder));
}
public:
//! 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_;
//! Variable to decide write(True)/read(False) metadata
bool m_modifyMetadata;
//! Variable to decide decode/skip metadata
bool m_decodeMetadata;
std::map<std::string, decFunc> decoders;
/*!
* \brief m_QuickSkeleton
* A container which hold the entire information about all premitive atoms inside quicktime
* structure
*/
std::vector<QuickAtom*> m_QuickSkeleton;
}; // class QuickTimeVideo::Private
QuickTimeVideo::QuickTimeVideo(BasicIo::AutoPtr io)
: Image(ImageType::qtime, mdNone, io), d(new Private)
{
d->m_decodeMetadata = true;
d->m_modifyMetadata = false;
}
QuickTimeVideo::~QuickTimeVideo()
{
delete d;
}
/*!
* \brief QuickTimeVideo::findAtomPositions:
* This method can be called only after read operation,it will return
* list atom(multiple atoms with same atom Id)
* location and atom size,for a given atomId
* \param atomId
* \return
*/
std::vector< pair<uint32_t,uint32_t> > QuickTimeVideo::findAtomPositions(const char* atomId)
{
DataBuf hdrId((uint32_t)5);
hdrId.pData_[4] = '\0';
std::vector< pair<uint32_t,uint32_t> > atomsDetails;
for (uint32_t i=0; i < d->m_QuickSkeleton.size();i++){
io_->seek((d->m_QuickSkeleton[i]->m_AtomLocation - 4),BasicIo::beg);
io_->read(hdrId.pData_,4);
if(UtilsVideo::compareTagValue(hdrId,atomId))
atomsDetails.push_back(make_pair((uint32_t)io_->tell(),
(uint32_t)(d->m_QuickSkeleton[i]->m_AtomSize)));
}
return atomsDetails;
}
std::string QuickTimeVideo::mimeType() const
{
return "video/quicktime";
}
void QuickTimeVideo::writeMetadata()
{
if (io_->open() != 0) throw Error(9, io_->path(), strError());
IoCloser closer(*io_);
doWriteMetadata();
io_->close();
}
/*!
* \brief QuickTimeVideo::doWriteMetadata
* Search for all the tags(effectively all supported tags in Exiv2)
* present in a file(in m_riffskeleton) and call corresponding handler
* method to write modified metadata.
*/
void QuickTimeVideo::doWriteMetadata()
{
if (!io_->isopen()) throw Error(20);
// Ensure that this is the correct image type
if (!isQTimeType(*io_, false)){
if (io_->error() || io_->eof()) throw Error(14);
throw Error(3, "QuickTime");
}
d->m_decodeMetadata = false;
d->m_modifyMetadata = true;
std::vector<std::string> atomFlags;
//Add new tags to the beginning of the container prmitiveFlags and handle conditions to the end of case statements.
atomFlags.push_back("stsd");
atomFlags.push_back("keys");
atomFlags.push_back("tapt");
atomFlags.push_back("pnot");
atomFlags.push_back("udta");
atomFlags.push_back("vmhd");
atomFlags.push_back("hdlr");
atomFlags.push_back("mdhd");
atomFlags.push_back("tkhd");
atomFlags.push_back("mvhd");
atomFlags.push_back("ftyp");
uint32_t iMaxCount = atomFlags.size();
std::vector< pair<uint32_t,uint32_t> > trakPositions = findAtomPositions("trak");
for (uint32_t j=0; j<iMaxCount; j++){
std::vector<pair<uint32_t,uint32_t> > atomPositions = findAtomPositions(atomFlags.back().c_str());
atomFlags.pop_back();
for (uint32_t i=0; i< atomPositions.size(); i++){
if(j == 2 || j == 3 || j == 4 || j == 10){
io_->seek(trakPositions[i].first,BasicIo::beg);
setMediaStream(0);
}
io_->seek(atomPositions[i].first,BasicIo::beg);
uint32_t uiAtomPosition = atomPositions[i].second;
switch (j){
case 0: fileTypeDecoder(uiAtomPosition) ; break;
case 1: movieHeaderDecoder(uiAtomPosition) ; break;
case 2: trackHeaderDecoder(uiAtomPosition) ; break;
case 3: mediaHeaderDecoder(uiAtomPosition) ; break;
case 4: break;
// Fixme:Editing dandler data is known to corrupt file data
// handlerDecoder(hdlrPositions[i].second);
case 5: videoHeaderDecoder(uiAtomPosition) ; break;
case 6: userDataDecoder(uiAtomPosition) ; break;
case 7: previewTagDecoder(uiAtomPosition) ; break;
case 8: trackApertureTagDecoder(uiAtomPosition); break;
case 9:keysTagDecoder(uiAtomPosition) ; break;
case 10: sampleDesc(uiAtomPosition) ; break;
}
}
atomPositions.clear();
}
}
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();
d->continueTraversing_ = true;
d->height_ = d->width_ = 1;
xmpData_["Xmp.video.FileSize"] = (double)io_->size()/(double)1048576;
xmpData_["Xmp.video.FileName"] = io_->path();
xmpData_["Xmp.video.MimeType"] = mimeType();
while (d->continueTraversing_) decodeBlock();
aspectRatio();
}
void QuickTimeVideo::decodeBlock()
{
const int32_t bufMinSize = 4;
DataBuf buf(bufMinSize+1);
io_->read(buf.pData_, 4);
if(io_->eof()){
d->continueTraversing_ = false;
return;
}
uint32_t size = Exiv2::getULong(buf.pData_, bigEndian);
io_->read(buf.pData_, 4);
if(size < 8) return;
tagDecoder(buf,size-8);
}
void QuickTimeVideo::tagDecoder(Exiv2::DataBuf &buf, uint32_t size)
{
const char allAtomFlags[][5]={"ftyp","mvhd","trak","tkhd","mdhd","hdlr","vmhd","udta","dref","stsd","stts","pnot",
"tapt","keys","url ","urn ","dcom","smhd"};
if(equalsQTimeTag(buf,allAtomFlags,(int)(sizeof(allAtomFlags)/5))){
QuickAtom* tmpAtom = new QuickAtom();
tmpAtom->m_AtomLocation = io_->tell();
tmpAtom->m_AtomSize = size;
memcpy((Exiv2::byte* )tmpAtom->m_AtomId,(const Exiv2::byte*)buf.pData_,5);
d->m_QuickSkeleton.push_back(tmpAtom);
}
decFunc decodetIter = d->decoders[std::string(reinterpret_cast<const char*>(buf.pData_))];
if (ignoreList(buf) ) io_->seek(size, BasicIo::cur);
else if (dataIgnoreList(buf)) decodeBlock();
else if (decodetIter ) (this->*decodetIter)(size);
// Decoders with lesser metadata
else if (UtilsVideo::compareTagValue(buf, "url ")){
if(!d->m_modifyMetadata) readAudVidStrData("URL", size);
else {
if(xmpData_["Xmp.video.URL"].count() > 0) writeStringData(xmpData_["Xmp.video.URL"],size);
else if ((d->currentStream_ == Audio) && (xmpData_["Xmp.audio.URL"].count() >0))
writeStringData(xmpData_["Xmp.audio.URL"],size);
else io_->seek(size,BasicIo::cur);
}
}
else if (UtilsVideo::compareTagValue(buf, "urn ")){
if(!d->m_modifyMetadata) readAudVidStrData("URN", size);
else {
if ((d->currentStream_ == Video) && (xmpData_["Xmp.video.URN"].count() >0)) writeStringData(xmpData_["Xmp.video.URN"],size);
else if ((d->currentStream_ == Audio) && (xmpData_["Xmp.audio.URN"].count() >0)) writeStringData(xmpData_["Xmp.audio.URN"],size);
else io_->seek(size,BasicIo::cur);
}
}
else if (UtilsVideo::compareTagValue(buf, "dcom")){
if(!d->m_modifyMetadata){
io_->read(buf.pData_, size);
xmpData_["Xmp.video.Compressor"] = Exiv2::toString(buf.pData_);
}
else writeStringData(xmpData_["Xmp.video.Compressor"],size);
}
else if (UtilsVideo::compareTagValue(buf, "smhd")){
io_->seek(4,BasicIo::cur);
if(!d->m_modifyMetadata){
io_->read(buf.pData_, 4);
xmpData_["Xmp.audio.Balance"] = returnBufValue(buf, 2);
} else {
DataBuf tmpBuf = returnBuf(xmpData_["Xmp.audio.Balance"].toLong());
io_->write(tmpBuf.pData_,2);
}
}
else io_->seek(size, BasicIo::cur);
}
void QuickTimeVideo::previewTagDecoder(uint32_t size)
{
DataBuf buf(4);
uint64_t cur_pos = io_->tell();
if(!d->m_modifyMetadata){
io_->read(buf.pData_, 4);
xmpData_["Xmp.video.PreviewDate"] = getULong(buf.pData_, bigEndian);
io_->read(buf.pData_, 2);
xmpData_["Xmp.video.PreviewVersion"] = getShort(buf.pData_, bigEndian);
io_->read(buf.pData_, 4);
if(UtilsVideo::compareTagValue(buf, "PICT"))
xmpData_["Xmp.video.PreviewAtomType"] = "QuickDraw Picture";
else
xmpData_["Xmp.video.PreviewAtomType"] = Exiv2::toString(buf.pData_);
io_->seek(cur_pos + size, BasicIo::beg);
} else {
writeLongData(xmpData_["Xmp.video.PreviewDate"]);
writeShortData(xmpData_["Xmp.video.PreviewVersion"]);
//Variation
if(xmpData_["Xmp.video.PreviewAtomType"].count() >0){
Exiv2::byte rawPreviewAtomType[4] = {'P','I','C','T'};
const std::string previewAtomType = xmpData_["Xmp.video.PreviewAtomType"].toString();
if(!(previewAtomType.compare("QuickDraw Picture") == 0))
for(int32_t j=0; j<4; j++) rawPreviewAtomType[j] = previewAtomType[j];
writeMultibyte(rawPreviewAtomType, 4);
}
io_->seek(cur_pos + size, BasicIo::beg);
}
}
void QuickTimeVideo::keysTagDecoder(uint32_t size)
{
DataBuf buf(4);
uint64_t cur_pos = io_->tell();
if(!d->m_modifyMetadata){
io_->read(buf.pData_, 4);
xmpData_["Xmp.video.PreviewDate"] = getULong(buf.pData_, bigEndian);
io_->read(buf.pData_, 2);
xmpData_["Xmp.video.PreviewVersion"] = getShort(buf.pData_, bigEndian);
io_->read(buf.pData_, 4);
if(UtilsVideo::compareTagValue(buf, "PICT")) xmpData_["Xmp.video.PreviewAtomType"] = "QuickDraw Picture";
else xmpData_["Xmp.video.PreviewAtomType"] = Exiv2::toString(buf.pData_);
io_->seek(cur_pos + size, BasicIo::beg);
} else {
writeLongData(xmpData_["Xmp.video.PreviewDate"]);
writeShortData(xmpData_["Xmp.video.PreviewVersion"]);
//Variation
if(xmpData_["Xmp.video.PreviewAtomType"].count() >0){
Exiv2::byte rawPreviewAtomType[4] = {'P','I','C','T'};
const std::string previewAtomType = xmpData_["Xmp.video.PreviewAtomType"].toString();
if(!(previewAtomType.compare("QuickDraw Picture") == 0))
for(int32_t j=0; j<4; j++)
rawPreviewAtomType[j] = previewAtomType[j];
writeMultibyte(rawPreviewAtomType, 4);
}
io_->seek(cur_pos + size, BasicIo::beg);
}
}
void QuickTimeVideo::trackApertureTagDecoder(uint32_t size)
{
DataBuf buf(4), buf2(2);
uint64_t cur_pos = io_->tell();
if(!d->m_modifyMetadata){
Exiv2::byte n = 3;
while(n--){
io_->seek(static_cast<int32_t>(4), BasicIo::cur); io_->read(buf.pData_, 4);
if(UtilsVideo::compareTagValue(buf, "clef")){
io_->seek(static_cast<int32_t>(4), BasicIo::cur);
io_->read(buf.pData_, 2); io_->read(buf2.pData_, 2);
xmpData_["Xmp.video.CleanApertureWidth"] = Exiv2::toString(getUShort(buf.pData_, bigEndian))
+ "." + Exiv2::toString(getUShort(buf2.pData_, bigEndian));
io_->read(buf.pData_, 2); io_->read(buf2.pData_, 2);
xmpData_["Xmp.video.CleanApertureHeight"] = Exiv2::toString(getUShort(buf.pData_, bigEndian))
+ "." + Exiv2::toString(getUShort(buf2.pData_, bigEndian));
}
else if(UtilsVideo::compareTagValue(buf, "prof")){
io_->seek(static_cast<int32_t>(4), BasicIo::cur);
io_->read(buf.pData_, 2); io_->read(buf2.pData_, 2);
xmpData_["Xmp.video.ProductionApertureWidth"] = Exiv2::toString(getUShort(buf.pData_, bigEndian))
+ "." + Exiv2::toString(getUShort(buf2.pData_, bigEndian));
io_->read(buf.pData_, 2); io_->read(buf2.pData_, 2);
xmpData_["Xmp.video.ProductionApertureHeight"] = Exiv2::toString(getUShort(buf.pData_, bigEndian))
+ "." + Exiv2::toString(getUShort(buf2.pData_, bigEndian));
}
else if(UtilsVideo::compareTagValue(buf, "enof")){
io_->seek(static_cast<int32_t>(4), BasicIo::cur);
io_->read(buf.pData_, 2); io_->read(buf2.pData_, 2);
xmpData_["Xmp.video.EncodedPixelsWidth"] = Exiv2::toString(getUShort(buf.pData_, bigEndian))
+ "." + Exiv2::toString(getUShort(buf2.pData_, bigEndian));
io_->read(buf.pData_, 2); io_->read(buf2.pData_, 2);
xmpData_["Xmp.video.EncodedPixelsHeight"] = Exiv2::toString(getUShort(buf.pData_, bigEndian))
+ "." + Exiv2::toString(getUShort(buf2.pData_, bigEndian));
}
}
io_->seek(static_cast<int32_t>(cur_pos + size), BasicIo::beg);
} else {
Exiv2::byte n = 3;
while(n--){
io_->seek(static_cast<int32_t>(4), BasicIo::cur); io_->read(buf.pData_, 4);
if(UtilsVideo::compareTagValue(buf, "clef")){
io_->seek(static_cast<int32_t>(4), BasicIo::cur);
writeApertureData(xmpData_["Xmp.video.CleanApertureWidth"],4);
writeApertureData(xmpData_["Xmp.video.CleanApertureHeight"],4);
}
else if(UtilsVideo::compareTagValue(buf, "prof")){
io_->seek(static_cast<int32_t>(4), BasicIo::cur);
writeApertureData(xmpData_["Xmp.video.ProductionApertureWidth"],4);
writeApertureData(xmpData_["Xmp.video.ProductionApertureHeight"],4);
}
else if(UtilsVideo::compareTagValue(buf, "enof")){
io_->seek(static_cast<int32_t>(4), BasicIo::cur);
writeApertureData(xmpData_["Xmp.video.EncodedPixelsWidth"],4);
writeApertureData(xmpData_["Xmp.video.EncodedPixelsHeight"],4);
}
}
io_->seek(static_cast<int32_t>(cur_pos + size), BasicIo::beg);
}
}
void QuickTimeVideo::CameraTagsDecoder(uint32_t size_external)
{
uint64_t cur_pos = io_->tell();
DataBuf buf(50), buf2(4);
const TagDetails* td;
const RevTagDetails* rtd;
io_->read(buf.pData_, 4);
if(UtilsVideo::compareTagValue(buf, "NIKO")){
io_->seek(cur_pos, BasicIo::beg);
if(!d->m_modifyMetadata){
readStrData("Xmp.video.Make", 24);
readStrData("Xmp.video.Model", 14);
io_->read(buf.pData_, 4);
xmpData_["Xmp.video.ExposureTime"] = "1/" + Exiv2::toString( ceil( getULong(buf.pData_, littleEndian) / (double)10));
io_->read(buf.pData_, 4);
io_->read(buf2.pData_, 4);
xmpData_["Xmp.video.FNumber"] = getULong(buf.pData_, littleEndian) / (double)getULong(buf2.pData_, littleEndian) ;
io_->read(buf.pData_, 4);
io_->read(buf2.pData_, 4);
xmpData_["Xmp.video.ExposureCompensation"] = getULong(buf.pData_, littleEndian) / (double)getULong(buf2.pData_, littleEndian) ;
io_->read(buf.pData_, 10);
io_->read(buf.pData_, 4);
td = find(whiteBalance, getULong(buf.pData_, littleEndian));
if (td) xmpData_["Xmp.video.WhiteBalance"] = exvGettext(td->label_);
io_->read(buf.pData_, 4);
io_->read(buf2.pData_, 4);
xmpData_["Xmp.video.FocalLength"] = getULong(buf.pData_, littleEndian) / (double)getULong(buf2.pData_, littleEndian) ;
io_->seek(static_cast<int32_t>(95), BasicIo::cur);
readStrData("Xmp.video.Software", 48);
io_->read(buf.pData_, 4);
xmpData_["Xmp.video.ISO"] = getULong(buf.pData_, littleEndian);
io_->seek(cur_pos + size_external, BasicIo::beg);
} else {
bool bDataWritten = false;
RevTagDetails revTagDetails[(sizeof(whiteBalance)/sizeof(whiteBalance[0]))];
reverseTagDetails(whiteBalance,revTagDetails,((sizeof(whiteBalance)/sizeof(whiteBalance[0]))));
writeShortData(xmpData_["Xmp.video.Make"],24);
writeShortData(xmpData_["Xmp.video.Model"],24);
//Variation
if(xmpData_["Xmp.video.ExposureTime"].count() >0){
const std::vector<int32_t> d_exposureTime =
getLongFromString(xmpData_["Xmp.video.ExposureTime"].toString(),'/');
const int32_t tmpValue = (d_exposureTime.at(1)*10);
io_->write((Exiv2::byte*)&tmpValue,4);
}
else io_->seek(4,BasicIo::cur);
//Variation
if(xmpData_["Xmp.video.FNumber"].count() >0){
io_->seek(4,BasicIo::cur);
io_->read(buf2.pData_,4);
io_->seek(-8,BasicIo::cur);
const int32_t fNumber =(int32_t)
((double)xmpData_["Xmp.video.FNumber"].toLong()
*(double)getULong(buf2.pData_, littleEndian));
writeMultibyte((Exiv2::byte*)&fNumber, 4, 4);
}
else io_->seek(8,BasicIo::cur);
if(xmpData_["Xmp.video.ExposureCompensation"].count() >0){
io_->seek(4,BasicIo::cur);
io_->read(buf2.pData_,4);
io_->seek(-8,BasicIo::cur);
const int32_t exposureCompensation =(int32_t)
((double)xmpData_["Xmp.video.ExposureCompensation"].toLong()
*(double)getULong(buf2.pData_, littleEndian));
writeMultibyte((Exiv2::byte*)&exposureCompensation, 4, 14);
}
else io_->seek(18,BasicIo::cur);
if(xmpData_["Xmp.video.WhiteBalance"].count() >0){
rtd = find(revTagDetails,xmpData_["Xmp.video.WhiteBalance"].toString());
if(rtd){
const int32_t sWhiteBalance = (int32_t)rtd->val_;
bDataWritten = writeMultibyte((Exiv2::byte*)&sWhiteBalance,4);
}
}
if(!bDataWritten) io_->seek(4,BasicIo::cur);
if(xmpData_["Xmp.video.FocalLength"].count() >0){
io_->seek(4,BasicIo::cur);
io_->read(buf2.pData_,4);
io_->seek(-8,BasicIo::cur);
const int32_t focalLength =(int32_t)((double)xmpData_["Xmp.video.FocalLength"].toLong()
*(double)getULong(buf2.pData_, littleEndian));
writeMultibyte((Exiv2::byte*)&focalLength, 4, 99);
}
else io_->seek(103,BasicIo::cur);
writeStringData(xmpData_["Xmp.video.Software"],48);
writeLongData(xmpData_["Xmp.video.ISO"]);
}
io_->seek(cur_pos + size_external, BasicIo::beg);
}
}
void QuickTimeVideo::userDataDecoder(uint32_t size_external)
{
uint64_t cur_pos = io_->tell();
const TagVocabulary* td;
const TagVocabulary* tv;
const TagVocabulary* tv_internal;
const int32_t bufMinSize = 100;
DataBuf buf(bufMinSize);
uint32_t size = 0;
uint32_t size_internal = size_external;
if(!d->m_modifyMetadata){
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(UtilsVideo::compareTagValue(buf, "DcMD") || UtilsVideo::compareTagValue(buf, "NCDT")) userDataDecoder(size - 8);
else if(UtilsVideo::compareTagValue(buf, "NCTG")) NikonTagsDecoder(size - 8);
else if(UtilsVideo::compareTagValue(buf, "TAGS")) CameraTagsDecoder(size - 8);
else if(UtilsVideo::compareTagValue(buf, "CNCV") || UtilsVideo::compareTagValue(buf, "CNFV")
|| UtilsVideo::compareTagValue(buf, "CNMN") || UtilsVideo::compareTagValue(buf, "NCHD")
|| UtilsVideo::compareTagValue(buf, "FFMV")){
readStrData((std::string)exvGettext(tv->label_) , size - 8);
}
else if(UtilsVideo::compareTagValue(buf, "CMbo") || UtilsVideo::compareTagValue(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_, size-8);
xmpData_[exvGettext(tv->label_)] = Exiv2::toString(buf.pData_);
}
else if(td) tagDecoder(buf,size-8);
}
io_->seek(cur_pos + size_external, BasicIo::beg);
} else {
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; // size <= 12?
else if(UtilsVideo::compareTagValue(buf, "DcMD") || UtilsVideo::compareTagValue(buf, "NCDT")) userDataDecoder(size - 8);
else if(UtilsVideo::compareTagValue(buf, "NCTG")) NikonTagsDecoder(size - 8);
else if(UtilsVideo::compareTagValue(buf, "TAGS")) CameraTagsDecoder(size - 8);
else if(UtilsVideo::compareTagValue(buf, "CNCV") || UtilsVideo::compareTagValue(buf, "CNFV")
|| UtilsVideo::compareTagValue(buf, "CNMN") || UtilsVideo::compareTagValue(buf, "NCHD")
|| UtilsVideo::compareTagValue(buf, "FFMV")
) writeShortData(xmpData_[exvGettext(tv->label_)],(size-8));
else if(UtilsVideo::compareTagValue(buf, "CMbo") || UtilsVideo::compareTagValue(buf, "Cmbo")) io_->seek(2,BasicIo::cur);
else if(tv) writeShortData(xmpData_[exvGettext(tv->label_)],(size-8));
}
io_->seek(cur_pos + size_external, BasicIo::beg);
}
}
void QuickTimeVideo::NikonTagsDecoder(uint32_t size_external)
{
uint64_t cur_pos = io_->tell();
DataBuf buf(200);
DataBuf buf2(4+1);
uint64_t TagID = 0 ;
uint16_t dataLength = 0 ;
uint16_t dataType = 2 ;
const TagDetails* td ;
const TagDetails* td2 ;
const RevTagDetails* rtd2;
if(!d->m_modifyMetadata){
for(int32_t 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);
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_);
// Sanity check with an "unreasonably" large number
if (dataLength > 200){
#ifndef SUPPRESS_WARNINGS
EXV_ERROR << "Xmp.video Nikon Tags, dataLength was found to be larger than 200."
<< " Entries considered invalid. Not Processed.\n";
#endif
io_->seek(io_->tell() + dataLength, BasicIo::beg);
}
else 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));
// Sanity check with an "unreasonably" large number
if (dataLength > 200 || dataLength < 4){
#ifndef SUPPRESS_WARNINGS
EXV_ERROR << "Xmp.video Nikon Tags, dataLength was found to be of inapropriate size."
<< " Entries considered invalid. Not Processed.\n";
#endif
io_->seek(io_->tell() + dataLength - 4, BasicIo::beg);
}
else 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));
// Sanity check with an "unreasonably" large number
if (dataLength > 200 || dataLength < 2){
#ifndef SUPPRESS_WARNINGS
EXV_ERROR << "Xmp.video Nikon Tags, dataLength was found to be of inapropriate size."
<< " Entries considered invalid. Not Processed.\n";
#endif
io_->seek(io_->tell() + dataLength - 2, BasicIo::beg);
}
else 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));
// Sanity check with an "unreasonably" large number
if (dataLength > 200 || dataLength < 8){
#ifndef SUPPRESS_WARNINGS
EXV_ERROR << "Xmp.video Nikon Tags, dataLength was found to be of inapropriate size."
<< " Entries considered invalid. Not Processed.\n";
#endif
io_->seek(io_->tell() + dataLength - 8, BasicIo::beg);
}
else 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));
// Sanity check with an "unreasonably" large number
if (dataLength > 200 || dataLength < 4){
#ifndef SUPPRESS_WARNINGS
EXV_ERROR << "Xmp.video Nikon Tags, dataLength was found to be of inapropriate size."
<< " Entries considered invalid. Not Processed.\n";
#endif
io_->seek(io_->tell() + dataLength - 4, BasicIo::beg);
}
else io_->read(buf.pData_, dataLength - 4);
}
}
io_->seek(cur_pos + size_external, BasicIo::beg);
} else {
RevTagDetails revNikonNCTGTags[(sizeof(NikonNCTGTags)/sizeof(NikonNCTGTags[0]))];
reverseTagDetails(NikonNCTGTags,revNikonNCTGTags,((sizeof(NikonNCTGTags)/sizeof(NikonNCTGTags[0]))));
bool bDataWriten = false;
for(int32_t 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);
writeShortData(xmpData_["Xmp.video.PictureControlVersion"],4);
writeShortData(xmpData_["Xmp.video.PictureControlName"],20);
writeShortData(xmpData_["Xmp.video.PictureControlBase"],20);
io_->seek(4,BasicIo::cur);
std::memset(buf.pData_, 0x0, buf.size_);
if(xmpData_["Xmp.video.PictureControlAdjust"].count() >0){
RevTagDetails revTagDetails[(sizeof(PictureControlAdjust)/sizeof(PictureControlAdjust[0]))];
reverseTagDetails(PictureControlAdjust,revTagDetails,((sizeof(PictureControlAdjust)/sizeof(PictureControlAdjust[0]))));
rtd2 = find(revTagDetails,xmpData_["Xmp.video.PictureControlAdjust"].toString());
if(rtd2){
Exiv2::byte rawPictureControlAdjust = (Exiv2::byte)rtd2->val_;
bDataWriten = writeMultibyte(&rawPictureControlAdjust, 1);
}
}
if(!bDataWriten) io_->seek(1,BasicIo::cur);
bDataWriten = false;
if(xmpData_["Xmp.video.PictureControlQuickAdjust"].count() >0){
RevTagDetails revTagDetails[(sizeof(NormalSoftHard)/sizeof(NormalSoftHard[0]))];
reverseTagDetails(NormalSoftHard,revTagDetails,((sizeof(NormalSoftHard)/sizeof(NormalSoftHard[0]))));
rtd2 = find(revTagDetails, xmpData_["Xmp.video.PictureControlQuickAdjust"].toString() );
if(rtd2){
Exiv2::byte rawPictureControlQuickAdjust = (Exiv2::byte)rtd2->val_;
bDataWriten = writeMultibyte(&rawPictureControlQuickAdjust,1);
}
}
if(!bDataWriten) io_->seek(1,BasicIo::cur);
bDataWriten = false;
if(xmpData_["Xmp.video.Sharpness"].count() >0){
RevTagDetails revTagDetails[(sizeof(NormalSoftHard)/sizeof(NormalSoftHard[0]))];
reverseTagDetails(NormalSoftHard,revTagDetails,((sizeof(NormalSoftHard)/sizeof(NormalSoftHard[0]))));
rtd2 = find(revTagDetails, xmpData_["Xmp.video.Sharpness"].toString() );
if(rtd2){
Exiv2::byte rawSharpness = (Exiv2::byte)rtd2->val_;
bDataWriten = writeMultibyte(&rawSharpness,1);
}
}
if(!bDataWriten) io_->seek(1,BasicIo::cur);
bDataWriten = false;
if(xmpData_["Xmp.video.Contrast"].count() >0){
RevTagDetails revTagDetails[(sizeof(NormalSoftHard)/sizeof(NormalSoftHard[0]))];
reverseTagDetails(NormalSoftHard,revTagDetails,((sizeof(NormalSoftHard)/sizeof(NormalSoftHard[0]))));
rtd2 = find(revTagDetails, xmpData_["Xmp.video.Contrast"].toString() );
if(rtd2){
Exiv2::byte rawContrast = (Exiv2::byte)rtd2->val_;
bDataWriten = writeMultibyte(&rawContrast,1);
}
}
if(!bDataWriten) io_->seek(1,BasicIo::cur);
bDataWriten = false;
if(xmpData_["Xmp.video.Brightness"].count() >0){
RevTagDetails revTagDetails[(sizeof(NormalSoftHard)/sizeof(NormalSoftHard[0]))];
reverseTagDetails(NormalSoftHard,revTagDetails,((sizeof(NormalSoftHard)/sizeof(NormalSoftHard[0]))));
rtd2 = find(revTagDetails, xmpData_["Xmp.video.Brightness"].toString() );
if(rtd2){
Exiv2::byte rawBrightness = (Exiv2::byte)rtd2->val_;
bDataWriten = writeMultibyte(&rawBrightness,1);
}
}
if(!bDataWriten) io_->seek(1,BasicIo::cur);
bDataWriten = false;
if(xmpData_["Xmp.video.Saturation"].count() >0){
RevTagDetails revTagDetails[(sizeof(Saturation)/sizeof(Saturation[0]))];
reverseTagDetails(Saturation,revTagDetails,((sizeof(Saturation)/sizeof(Saturation[0]))));
rtd2 = find(revTagDetails, xmpData_["Xmp.video.Saturation"].toString() );
if(rtd2){
Exiv2::byte rawSaturation = (Exiv2::byte)rtd2->val_;
bDataWriten = writeMultibyte(&rawSaturation,1);
}
}
if(!bDataWriten) io_->seek(1,BasicIo::cur);
bDataWriten = false;
if(xmpData_["Xmp.video.HueAdjustment"].count() >0){
Exiv2::byte rawHueAdjustment = (Exiv2::byte)xmpData_["Xmp.video.HueAdjustment"].toString()[0];
bDataWriten = writeMultibyte(&rawHueAdjustment,1);
}
if(!bDataWriten) io_->seek(1,BasicIo::cur);
bDataWriten = false;
if(xmpData_["Xmp.video.FilterEffect"].count() >0){
RevTagDetails revTagDetails[(sizeof(FilterEffect)/sizeof(FilterEffect[0]))];
reverseTagDetails(FilterEffect,revTagDetails,((sizeof(FilterEffect)/sizeof(FilterEffect[0]))));
rtd2 = find(revTagDetails, xmpData_["Xmp.video.FilterEffect"].toString() );
if(rtd2){
Exiv2::byte rawFilterEffect = (Exiv2::byte)rtd2->val_;
bDataWriten = writeMultibyte(&rawFilterEffect,1);
}
}
if(!bDataWriten) io_->seek(1,BasicIo::cur);
bDataWriten = false;
if(xmpData_["Xmp.video.ToningEffect"].count() >0){
RevTagDetails revTagDetails[(sizeof(ToningEffect)/sizeof(ToningEffect[0]))];
reverseTagDetails(ToningEffect,revTagDetails,((sizeof(ToningEffect)/sizeof(ToningEffect[0]))));
rtd2 = find(revTagDetails, xmpData_["Xmp.video.ToningEffect"].toString() );
if(rtd2){
Exiv2::byte rawToningEffect = (Exiv2::byte)rtd2->val_;
bDataWriten = writeMultibyte(&rawToningEffect,1);
}
}
if(!bDataWriten) io_->seek(1,BasicIo::cur);
bDataWriten = false;
if(xmpData_["Xmp.video.ToningSaturation"].count() >0){
Exiv2::byte rawToningSaturation = (Exiv2::byte)xmpData_["Xmp.video.ToningSaturation"].toString()[0];
bDataWriten = writeMultibyte(&rawToningSaturation,1);
}
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_);
writeShortData(xmpData_["Xmp.video.TimeZone"]);
if(xmpData_["Xmp.video.DayLightSavings"].count() >0){
RevTagDetails revTagDetails[(sizeof(YesNo)/sizeof(YesNo[0]))];
reverseTagDetails(YesNo,revTagDetails,((sizeof(YesNo)/sizeof(YesNo[0]))));
rtd2 = find(revTagDetails, xmpData_["Xmp.video.DayLightSavings"].toString() );
if(rtd2){
Exiv2::byte rawDayLightSavings = (Exiv2::byte)rtd2->val_;
bDataWriten = writeMultibyte(&rawDayLightSavings,1);
}
}
if(!bDataWriten) io_->seek(1,BasicIo::cur);
bDataWriten = false;
if(xmpData_["Xmp.video.DateDisplayFormat"].count() >0){
RevTagDetails revTagDetails[(sizeof(DateDisplayFormat)/sizeof(DateDisplayFormat[0]))];
reverseTagDetails(DateDisplayFormat,revTagDetails,((sizeof(DateDisplayFormat)/sizeof(DateDisplayFormat[0]))));
rtd2 = find(revTagDetails, xmpData_["Xmp.video.DateDisplayFormat"].toString() );
if(rtd2){
Exiv2::byte rawDateDisplayFormat = (Exiv2::byte)rtd2->val_;
bDataWriten = writeMultibyte(&rawDateDisplayFormat,1);
}
}
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_);
// Sanity check with an "unreasonably" large number
if (dataLength > 200){
#ifndef SUPPRESS_WARNINGS
EXV_ERROR << "Xmp.video Nikon Tags, dataLength was found to be larger than 200."
<< " Entries considered invalid. Not Processed.\n";
#endif
io_->seek(io_->tell() + dataLength, BasicIo::beg);
} else {
if(td && (xmpData_[exvGettext(td->label_)].count() >0)) writeStringData(xmpData_[exvGettext(td->label_)],dataLength);
else io_->seek(dataLength,BasicIo::cur);
}
}
else if(dataType == 4){ // what is 4? would it be better to have an enum with a name for each dataType?
dataLength = Exiv2::getUShort(buf.pData_, bigEndian) * 4;
if(td && (xmpData_[exvGettext(td->label_)].count() >0) && (dataLength < 200)) writeLongData(xmpData_[exvGettext(td->label_)],4,dataLength-4);
else io_->seek(dataLength,BasicIo::cur);
}
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_)].count() >0) && (dataLength < 200)) writeShortData(xmpData_[exvGettext(td->label_)],2,dataLength-2);
else io_->seek(dataLength,BasicIo::cur);
}
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_)].count() >0) &&(dataLength < 200)){
io_->seek(-8,BasicIo::cur);
const int32_t tagData = (int32_t)((double)xmpData_[exvGettext(td->label_)].toLong()*
(double)Exiv2::getULong( buf2.pData_, bigEndian));
io_->write((Exiv2::byte*)&tagData,4);
io_->seek((dataLength-4),BasicIo::cur);
}
else io_->seek(dataLength,BasicIo::cur);
}
else if(dataType == 8){
dataLength = Exiv2::getUShort(buf.pData_, bigEndian) * 2;
std::memset(buf.pData_, 0x0, buf.size_);
if(td && (xmpData_[exvGettext(td->label_)].count() >0) &&(dataLength < 200)){
vector<uint16_t> dataEight = getNumberFromString(xmpData_[exvGettext(td->label_)].toString(),' ');
io_->write((Exiv2::byte*)&dataEight[0],2);
io_->write((Exiv2::byte*)&dataEight[1],2);
io_->seek(dataLength - 4,BasicIo::cur);
}
else io_->seek(dataLength - 4,BasicIo::cur);
}
}
io_->seek(cur_pos + size_external, BasicIo::beg);
}
}
void QuickTimeVideo::setMediaStream(uint32_t /* iDummySize*/)
{
uint64_t current_position = io_->tell();
DataBuf buf(4+1);
while( !io_->eof() ) {
io_->read(buf.pData_, 4);
if (UtilsVideo::compareTagValue(buf, "hdlr")) {
io_->seek(8,BasicIo::cur);
io_->read(buf.pData_, 4);
if (UtilsVideo::compareTagValue(buf, "vide")) d->currentStream_ = Video;
else if (UtilsVideo::compareTagValue(buf, "soun")) d->currentStream_ = Audio;
else if (UtilsVideo::compareTagValue(buf, "hint")) d->currentStream_ = Hint;
else d->currentStream_ = GenMediaHeader;
break;
}
}
io_->seek(current_position, BasicIo::beg);
}
void QuickTimeVideo::timeToSampleDecoder(uint32_t/* iDummySize*/)
{
DataBuf buf(4+1);
io_->read(buf.pData_, 4);
io_->read(buf.pData_, 4);
uint64_t totalframes = 0;
uint64_t timeOfFrames = 0;
uint64_t noOfEntries = returnUnsignedBufValue(buf);
for(uint32_t i=1; i <= noOfEntries; i++){
io_->read(buf.pData_, 4);
uint64_t temp = returnBufValue(buf);
totalframes += temp;
io_->read(buf.pData_, 4);
timeOfFrames += temp * returnBufValue(buf);
}
if(!d->m_modifyMetadata) {
if (d->currentStream_ == Video) {
xmpData_["Xmp.video.FrameRate"] = (double)totalframes * (double)d->timeScale_ / (double)timeOfFrames;
} else {
if(xmpData_["Xmp.video.FrameRate"].count() >0) { /* TODO:implement write */ }
// Really? We're not modifying!!
}
}
}
void QuickTimeVideo::sampleDesc(uint32_t size)
{
DataBuf buf(100);
uint64_t cur_pos = io_->tell();
io_->read(buf.pData_, 4);
io_->read(buf.pData_, 4);
uint32_t noOfEntries = returnUnsignedBufValue(buf);
for(uint32_t i = 1; i <= noOfEntries; i++){
if (d->currentStream_ == Video) imageDescDecoder();
else if (d->currentStream_ == Audio) audioDescDecoder();
}
io_->seek(cur_pos + size, BasicIo::beg);
}
void QuickTimeVideo::audioDescDecoder()
{
DataBuf buf(40);
io_->read(buf.pData_, 4);
uint64_t size = 82;
const TagVocabulary* td;
if(!d->m_modifyMetadata){
for (int32_t 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_, static_cast<int32_t>(size % 4)); //cause size is so small, this cast should be right.
} else {
TagVocabulary revTagVocabulary[(sizeof(qTimeFileType)/sizeof(qTimeFileType[0]))];
reverseTagVocabulary(qTimeFileType,revTagVocabulary,((sizeof(qTimeFileType)/sizeof(qTimeFileType[0]))));
TagVocabulary revVendorIDTags[(sizeof(vendorIDTags)/sizeof(vendorIDTags[0]))];
reverseTagVocabulary(vendorIDTags,revVendorIDTags,((sizeof(vendorIDTags)/sizeof(vendorIDTags[0]))));
for (int32_t i = 0; size/4 != 0 ; size -= 4, i++){
bool bDataWriten = false;
switch(i){
case AudioFormat:
if(xmpData_["Xmp.audio.Compressor"].count() > 0){
Exiv2::byte rawCompressor[4];
td = find(revTagVocabulary,xmpData_["Xmp.audio.Compressor"].toString());
if(td){
const char* compressor = td->label_;
for(int32_t j=0; j<4; j++) rawCompressor[j] = compressor[j];
bDataWriten = writeMultibyte(rawCompressor, 4);
}
}
break;
case AudioVendorID:
if(xmpData_["Xmp.audio.VendorID"].count() >0){
Exiv2::byte rawVendorID[4];
td = find(revVendorIDTags,xmpData_["Xmp.audio.VendorID"].toString());
if(td){
const char* vendorID = td->label_;
for(int32_t j=0; j<4; j++) rawVendorID[j] = vendorID[j];
bDataWriten = writeMultibyte(rawVendorID, 4);
}
}
break;
case AudioChannels:
if(xmpData_["Xmp.audio.ChannelType"].count() >0){
DataBuf rawBuf(2);
rawBuf = returnBuf((int64_t)xmpData_["Xmp.audio.ChannelType"].toLong(),2);
bDataWriten = writeMultibyte(rawBuf.pData_, 2);
}
if(!bDataWriten) io_->seek(2, BasicIo::cur);
bDataWriten = false;
if(xmpData_["Xmp.audio.BitsPerSample"].count() >0){
DataBuf rawBuf(2);
rawBuf = returnBuf((int64_t)xmpData_["Xmp.audio.BitsPerSample"].toLong(),2);
bDataWriten = writeMultibyte(rawBuf.pData_,2);
}
if(!bDataWriten) io_->seek(2, BasicIo::cur);
bDataWriten = true;
break;
case AudioSampleRate:
if(xmpData_["Xmp.audio.SampleRate"].count() >0){
DataBuf rawSampleRate(5);
rawSampleRate.pData_[4] = '\0';
const int64_t sampleRate = (int64_t)xmpData_["Xmp.audio.SampleRate"].toLong();
rawSampleRate = returnBuf(sampleRate,4);
bDataWriten = writeMultibyte(rawSampleRate.pData_+2,2);
bDataWriten = writeMultibyte(rawSampleRate.pData_,2);
}
break;
default:
io_->seek(4,BasicIo::cur);
bDataWriten = true;
break;
}
if(!bDataWriten) io_->seek(4, BasicIo::cur);
}
io_->seek(static_cast<int32_t>(size % 4),BasicIo::cur);
}
}
void QuickTimeVideo::imageDescDecoder()
{
DataBuf buf(40);
io_->read(buf.pData_, 4);
uint64_t size = 82;
const TagVocabulary* td;
if(!d->m_modifyMetadata){
for (int32_t 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;
}
}
io_->read(buf.pData_, static_cast<int32_t>(size % 4));
xmpData_["Xmp.video.BitDepth"] = returnBufValue(buf, 1);
} else {
TagVocabulary revTagVocabulary[(sizeof(qTimeFileType)/sizeof(qTimeFileType[0]))];
reverseTagVocabulary(qTimeFileType,revTagVocabulary,((sizeof(qTimeFileType)/sizeof(qTimeFileType[0]))));
TagVocabulary revVendorIDTags[(sizeof(vendorIDTags)/sizeof(vendorIDTags[0]))];
reverseTagVocabulary(vendorIDTags,revVendorIDTags,((sizeof(vendorIDTags)/sizeof(vendorIDTags[0]))));
for (int32_t i = 0; size/4 != 0 ; size -= 4, i++){
bool bDataWritten = false;
switch(i){
case codec:
if(xmpData_["Xmp.video.Codec"].count() >0){
std::string codecName = xmpData_["Xmp.video.Codec"].toString();
td = find(revTagVocabulary, codecName);
Exiv2::byte rawCodec[4];
if(td){
const char* codecNameTag = td->label_;
for(int32_t j=0; j<4 ;j++) rawCodec[j] = (Exiv2::byte)codecNameTag[j];
}
else for(int32_t j=0; j<4 ;j++) rawCodec[j] = (Exiv2::byte)codecName[j];
bDataWritten = writeMultibyte(rawCodec, 4);
}
break;
case VendorID:
if(xmpData_["Xmp.video.VendorID"].count() >0){
td = find(revVendorIDTags, xmpData_["Xmp.video.VendorID"].toString());
if(td){
Exiv2::byte rawVendorID[4];
const char* vendorID = td->label_;
for(int32_t j=0; j<4 ;j++) rawVendorID[j] = vendorID[j];
bDataWritten = writeMultibyte(rawVendorID,4);
}
}
break;
case SourceImageWidth_Height:
if(xmpData_["Xmp.video.SourceImageWidth"].count() >0){
DataBuf imageWidth = returnBuf((int64_t)xmpData_["Xmp.video.SourceImageWidth"].toLong(),2);
bDataWritten = writeMultibyte(imageWidth.pData_,2);
}
if(!bDataWritten) io_->seek(2,BasicIo::cur);
bDataWritten = false;
if(xmpData_["Xmp.video.SourceImageHeight"].count() >0){
DataBuf imageHeight = returnBuf(((int64_t)(xmpData_["Xmp.video.SourceImageHeight"].toLong() -
xmpData_["Xmp.video.SourceImageHeight"].toLong()/65536)*65536));
bDataWritten = writeMultibyte(imageHeight.pData_,2);
}
if(!bDataWritten) io_->seek(2,BasicIo::cur);
bDataWritten = true;
break;
case XResolution:
//This field should not be edited
break;
case YResolution:
io_->seek(7,BasicIo::cur);
size -= 3;
bDataWritten = true;
break;
case CompressorName:
if(xmpData_["Xmp.video.Compressor"].count() >0){
Exiv2::byte rawCompressor[32] = {};
const std::string compressor = xmpData_["Xmp.video.Compressor"].toString();
for(int32_t j=0;j<min((int32_t)32,(int32_t)compressor.size()); j++)
rawCompressor[j] = (Exiv2::byte)compressor[j];
io_->seek(4,BasicIo::cur);
bDataWritten = writeMultibyte(rawCompressor,32);
} else io_->seek(36,BasicIo::cur);
size -= 32;
bDataWritten = true;
break;
}
if(!bDataWritten) io_->seek(4,BasicIo::cur);
}
if(xmpData_["Xmp.video.BitDepth"].count() > 0)
{
DataBuf rawBitDepth(1);
rawBitDepth = returnBuf((int64_t)xmpData_["Xmp.video.BitDepth"].toLong(),1);
io_->write(rawBitDepth.pData_,1);
}
io_->seek(static_cast<int32_t>(size % 4),BasicIo::cur);
}
}
void QuickTimeVideo::multipleEntriesDecoder(uint32_t /*iDummySize*/)
{
DataBuf buf(5);
io_->read(buf.pData_, 4);
io_->read(buf.pData_, 4);
uint64_t noOfEntries = returnUnsignedBufValue(buf);
for(uint32_t i = 1; i <= noOfEntries; i++) decodeBlock();
}
void QuickTimeVideo::videoHeaderDecoder(uint32_t size)
{
DataBuf buf(3);
d->currentStream_ = Video;
const RevTagDetails* rtd;
const TagDetails* td;
if(!d->m_modifyMetadata){
for (int32_t 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;
}
}
io_->read(buf.pData_, size % 2);
} else {
RevTagDetails revTagDetails[(sizeof(graphicsModetags)/sizeof(graphicsModetags[0]))];
reverseTagDetails(graphicsModetags,revTagDetails,((sizeof(graphicsModetags)/sizeof(graphicsModetags[0]))));
for (int32_t i = 0; size/2 != 0 ; size -= 2, i++){
bool bDataWritten = false;
switch(i){
case GraphicsMode:
if(xmpData_["Xmp.video.GraphicsMode"].count() >0){
DataBuf rawGraphicsMode(2);
rtd = find(revTagDetails,xmpData_["Xmp.video.GraphicsMode"].toString());
if(rtd){
rawGraphicsMode = returnBuf((int64_t)rtd->val_,2);
bDataWritten = writeMultibyte(rawGraphicsMode.pData_,2);
}
}
break;
case OpColor:
if(xmpData_["Xmp.video.OpColor"].count() >0){
DataBuf rawOpColor((uint32_t)3);
rawOpColor.pData_[2] = '\0';
const int64_t opColor = xmpData_["Xmp.video.OpColor"].toLong();
rawOpColor = returnBuf(opColor,2);
bDataWritten = writeMultibyte(rawOpColor.pData_,2);
}
break;
}
if(!bDataWritten) io_->seek(2,BasicIo::cur);
}
io_->seek(size % 2,BasicIo::cur);
}
}
void QuickTimeVideo::handlerDecoder(uint32_t size)
{
uint64_t cur_pos = io_->tell();
DataBuf buf(100);
const TagVocabulary* tv;
if(!d->m_modifyMetadata){
for (int32_t i = 0; i < 5 ; i++){
io_->read(buf.pData_, 4);
switch(i){
case HandlerClass:
tv = find(handlerClassTags, Exiv2::toString( buf.pData_));
if(tv){
if (d->currentStream_ == Video) xmpData_["Xmp.video.HandlerClass"] = exvGettext(tv->label_);
else if (d->currentStream_ == Audio) xmpData_["Xmp.audio.HandlerClass"] = exvGettext(tv->label_);
}
break;
case HandlerType:
tv = find(handlerTypeTags, Exiv2::toString( buf.pData_));
if(tv){
if (d->currentStream_ == Video) xmpData_["Xmp.video.HandlerType"] = exvGettext(tv->label_);
else if (d->currentStream_ == Audio) xmpData_["Xmp.audio.HandlerType"] = exvGettext(tv->label_);
}
break;
case HandlerVendorID:
tv = find(vendorIDTags, Exiv2::toString( buf.pData_));
if(tv){
if (d->currentStream_ == Video) xmpData_["Xmp.video.HandlerVendorID"] = exvGettext(tv->label_);
else if (d->currentStream_ == Audio) xmpData_["Xmp.audio.HandlerVendorID"] = exvGettext(tv->label_);
}
break;
}
}
io_->seek(cur_pos + size, BasicIo::beg);
} else {
for (int32_t i = 0; i < 5 ; i++){
bool bDataWritten = false;
switch(i){
case HandlerClass : bDataWritten = writeAudVidData(0); break;
case HandlerType : bDataWritten = writeAudVidData(1); break;
case HandlerVendorID: bDataWritten = writeAudVidData(2); break;
}
if(!bDataWritten) io_->seek(4, BasicIo::cur);
}
io_->seek(cur_pos + size, BasicIo::beg);
}
}
bool QuickTimeVideo::writeAudVidData(int64_t iTagType)
{
bool bReturnVal = false;
const TagVocabulary* tv;
TagVocabulary revTagVocabulary[(sizeof(handlerClassTags)/sizeof(handlerClassTags[0]))];
reverseTagVocabulary(handlerClassTags,revTagVocabulary,((sizeof(handlerClassTags)/sizeof(handlerClassTags[0]))));
TagVocabulary revHandlerType[(sizeof(handlerTypeTags)/sizeof(handlerTypeTags[0]))];
reverseTagVocabulary(handlerTypeTags,revHandlerType,((sizeof(handlerTypeTags)/sizeof(handlerTypeTags[0]))));
TagVocabulary revVendorIDTags[(sizeof(vendorIDTags)/sizeof(vendorIDTags[0]))];
reverseTagVocabulary(vendorIDTags,revVendorIDTags,((sizeof(vendorIDTags)/sizeof(vendorIDTags[0]))));
if(d->currentStream_ == Video){
switch (iTagType) {
case 0: tv = find(revTagVocabulary, xmpData_["Xmp.video.HandlerClass"].toString()) ; break;
case 1: tv = find(revHandlerType, xmpData_["Xmp.video.HandlerType"].toString()) ; break;
case 2: tv = find(revVendorIDTags, xmpData_["Xmp.video.HandlerVendorID"].toString()) ; break;
}
}
if(d->currentStream_ == Audio){
switch (iTagType) {
case 0: tv = find(revTagVocabulary, xmpData_["Xmp.audio.HandlerClass"].toString()) ; break;
case 1: tv = find(revHandlerType, xmpData_["Xmp.audio.HandlerType"].toString()) ; break;
case 2: tv = find(revVendorIDTags, xmpData_["Xmp.audio.HandlerVendorID"].toString()) ; break;
}
}
if(tv){
Exiv2::byte sRawXmpTagVal[4];
const std::string sXmpTagVal = tv->label_;
for(int32_t j=0; j<4; j++) sRawXmpTagVal[j] = sXmpTagVal[j];
bReturnVal = writeMultibyte(sRawXmpTagVal,4);
}
return bReturnVal;
}
void QuickTimeVideo::fileTypeDecoder(uint32_t size)
{
DataBuf buf(5);
Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpSeq);
const TagVocabulary* td;
if(!d->m_modifyMetadata){
for (int32_t 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);
}
} else {
TagVocabulary revTagVocabulary[(sizeof(qTimeFileType)/sizeof(qTimeFileType[0]))];
reverseTagVocabulary(qTimeFileType,revTagVocabulary,((sizeof(qTimeFileType)/sizeof(qTimeFileType[0]))));
DataBuf buf;
for (int32_t i = 0; size/4 != 0; size -=4, i++){
bool bDataWritten = false;
switch(i){
case 0:
if(xmpData_["Xmp.video.MajorBrand"].count() > 0){
Exiv2::byte rawMajorBrand[4];
td = find(revTagVocabulary, xmpData_["Xmp.video.MajorBrand"].toString());
const char* majorBrandVoc = td->label_;
for(int32_t j=0; j<4 ;j++)rawMajorBrand[j] = majorBrandVoc[i];
bDataWritten = writeMultibyte(rawMajorBrand,4);
}
break;
case 1:
if(xmpData_["Xmp.video.MinorVersion"].count() >0){
buf = returnBuf((int64_t)xmpData_["Xmp.video.MinorVersion"].toLong());
bDataWritten = writeMultibyte(buf.pData_,4);
}
break;
default:
if(xmpData_["Xmp.video.CompatibleBrands"].count() >0){
Exiv2::byte rawCompatibleBrand[4];
td = find(revTagVocabulary, xmpData_["Xmp.video.CompatibleBrands"].toString());
if(td){
const char* compatibleBrandVoc = td->label_;
for(int32_t j=0; j<4 ;j++) rawCompatibleBrand[i] = compatibleBrandVoc[i];
} else {
const std::string compatibleBrand = xmpData_["Xmp.video.CompatibleBrands"].toString();
for(int32_t j=0; j<4; j++) rawCompatibleBrand[i] = compatibleBrand[i];
}
bDataWritten = writeMultibyte(rawCompatibleBrand,4);
}
break;
}
if(!bDataWritten) io_->seek(4, BasicIo::cur);
io_->seek(size%4,BasicIo::cur);
}
}
}
void QuickTimeVideo::mediaHeaderDecoder(uint32_t size)
{
DataBuf buf(5);
int64_t time_scale = 1;
const TagDetails* td;
const RevTagDetails* rtd;
if(!d->m_modifyMetadata){
for (int32_t i = 0; size/4 != 0 ; size -=4, i++){
switch(i){
case MediaHeaderVersion: readAudVidData("MediaHeaderVersion", 1, 1, 0); break;
case MediaCreateDate: //A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created.
readAudVidData("MediaCreateDate", 4, 1, 1); break;
case MediaModifyDate: //A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created.
readAudVidData("MediaModifyDate", 4, 1, 1); break;
case MediaTimeScale: io_->read(buf.pData_, 4) ;if(d->currentStream_ == Video) xmpData_["Xmp.video.MediaTimeScale"] = returnBufValue(buf);
else if (d->currentStream_ == Audio) xmpData_["Xmp.audio.MediaTimeScale"] = returnBufValue(buf);
time_scale = returnBufValue(buf);
break;
case MediaDuration: io_->read(buf.pData_, 4);
if(d->currentStream_ == Video) xmpData_["Xmp.video.MediaDuration"] = returnBufValue(buf)/time_scale;
else if (d->currentStream_ == Audio) xmpData_["Xmp.audio.MediaDuration"] = returnBufValue(buf)/time_scale;
break;
case MediaLanguageCode: io_->read(buf.pData_, 4);
if(d->currentStream_ == Video){
DataBuf tmpBuf((uint32_t)5);
tmpBuf.pData_[1] = buf.pData_[2];
tmpBuf.pData_[0] = buf.pData_[3];
xmpData_["Xmp.video.Quality"] = returnUnsignedBufValue(tmpBuf,2);
xmpData_["Xmp.video.MediaLangCode"] = returnUnsignedBufValue(buf,2);
td = find(mediaLanguageCode,returnUnsignedBufValue(buf,2));
if(td) xmpData_["Xmp.video.MediaLanguage"] = Exiv2::StringValue(td->label_);
} else if (d->currentStream_ == Audio){
DataBuf tmpBuf((uint32_t)5);
tmpBuf.pData_[1] = buf.pData_[2];
tmpBuf.pData_[0] = buf.pData_[3];
xmpData_["Xmp.audio.Quality"] = returnUnsignedBufValue(tmpBuf,2);
xmpData_["Xmp.audio.MediaLangCode"] = returnUnsignedBufValue(buf,2);
td = find(mediaLanguageCode,returnUnsignedBufValue(buf,2));
if(td) xmpData_["Xmp.audio.MediaLanguage"] = Exiv2::StringValue(td->label_);
}
break;
default: io_->seek(4, BasicIo::cur); break;
}
}
io_->read(buf.pData_, size%4);
} else {
RevTagDetails revTagDetails[(sizeof(mediaLanguageCode)/sizeof(mediaLanguageCode[0]))];
reverseTagDetails(mediaLanguageCode,revTagDetails,((sizeof(mediaLanguageCode)/sizeof(mediaLanguageCode[0]))));
for (int32_t i = 0; size/4 != 0 ; size -=4, i++){
bool bDataWritten = false;
switch(i){
case MediaHeaderVersion:
bDataWritten = writeAudVidData("MediaHeaderVersion", 1, 1, 3, 0);
break;
case MediaCreateDate: bDataWritten = writeAudVidData("MediaCreateDate", 1, 4, 0, 1); break;
case MediaModifyDate: bDataWritten = writeAudVidData("MediaModifyDate", 1, 4, 0, 1); break;
case MediaTimeScale: bDataWritten = writeAudVidData("MediaTimeScale", 1, 4, -4, 0); break;
case MediaDuration:
io_->read(buf.pData_,4);
time_scale = returnBufValue(buf);
bDataWritten = writeAudVidData("MediaDuration", time_scale, 4, 0, 0); break;
case MediaLanguageCode:
if(d->currentStream_ == Video){
if(xmpData_["Xmp.video.MediaLanguage"].count() > 0){
rtd = find(revTagDetails,xmpData_["Xmp.video.MediaLanguage"].toString());
if(rtd){
buf = returnBuf(rtd->val_,2);
bDataWritten = writeMultibyte(buf.pData_,2);
}
}
if(!bDataWritten) io_->seek(2,BasicIo::cur);
bDataWritten = false;
if(xmpData_["Xmp.video.Quality"].count() >0){
buf = returnBuf((uint64_t)xmpData_["Xmp.video.Quality"].toLong(),2);
bDataWritten = writeMultibyte(&buf.pData_[1],1);
bDataWritten = writeMultibyte(&buf.pData_[0],1);
}
if(!bDataWritten) io_->seek(2,BasicIo::cur);
bDataWritten = true;
} else if (d->currentStream_ == Audio){
if(xmpData_["Xmp.audio.MediaLanguage"].count() > 0){
rtd = find(revTagDetails,xmpData_["Xmp.audio.MediaLanguage"].toString());
if(rtd){
buf = returnBuf(rtd->val_,2);
bDataWritten = writeMultibyte(buf.pData_,2);
}
}
if(!bDataWritten) io_->seek(2,BasicIo::cur);
bDataWritten = false;
if(xmpData_["Xmp.audio.Quality"].count() >0){
buf = returnBuf((uint64_t)xmpData_["Xmp.audio.Quality"].toLong(),2);
bDataWritten = writeMultibyte(&buf.pData_[1],1);
bDataWritten = writeMultibyte(&buf.pData_[0],1);
}
}
if(!bDataWritten) io_->seek(2,BasicIo::cur);
bDataWritten = true;
break;
}
if(!bDataWritten) io_->seek(4, BasicIo::cur);
}
io_->seek(size%4,BasicIo::cur);
}
}
void QuickTimeVideo::trackHeaderDecoder(uint32_t size)
{
DataBuf buf(5);
int64_t temp = 0;
if(!d->m_modifyMetadata){
for (int32_t i = 0; size/4 != 0 ; size -=4, i++){
switch(i){
case TrackHeaderVersion: readAudVidData("TrackHeaderVersion", 1, 1, 0); break;
case TrackCreateDate://A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created.
readAudVidData("TrackCreateDate", 4, 1, 1); break;
case TrackModifyDate: //A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created.
readAudVidData("TrackModifyDate", 4, 1, 1); break;
case TrackID: readAudVidData("TrackID", 4, 1, 0); break;
case TrackDuration: readAudVidData("TrackDuration", 4, (double)(1000/(double)d->timeScale_), 0); break;
case TrackLayer: readAudVidData("TrackLayer", 2, 1, 0); break;
case TrackVolume: readAudVidData("TrackVolume", 2, (double)(1/(double)2), 0); break;
case ImageWidth: io_->read(buf.pData_, 4);
if(d->currentStream_ == Video){
temp = (returnBufValue(buf, 4)/(256*256));
xmpData_["Xmp.video.Width"] = temp;
d->width_ = temp;
}
break;
case ImageHeight: io_->read(buf.pData_, 4);
if(d->currentStream_ == Video){
temp = (returnBufValue(buf, 4)/(256*256));
xmpData_["Xmp.video.Height"] = temp;
d->height_ = temp;
}
break;
default: io_->seek(4 ,BasicIo::cur); break;
}
}
io_->read(buf.pData_, size%4);
} else {
for (int32_t i = 0; size/4 != 0 ; size -=4, i++){
bool bDataWritten = false;
bool bVideo = d->currentStream_ == Video;
switch(i){
case TrackHeaderVersion: bDataWritten = writeAudVidData("TrackHeaderVersion", 1, 1, 3, 0); break;
case TrackCreateDate: bDataWritten = writeAudVidData("TrackCreateDate", 1, 4, 0, 1) ; break;
case TrackModifyDate: bDataWritten = writeAudVidData("TrackModifyDate", 1, 4, 0, 1) ; break;
case TrackID: bDataWritten = writeAudVidData("TrackID", 1, 4, 0, 0) ; break;
case TrackDuration: bDataWritten = writeAudVidData("TrackDuration", (double)(d->timeScale_/1000), 4, 0, 0); break;
case TrackLayer: bDataWritten = writeAudVidData("TrackLayer", 11, 2, 2, 0) ; break;
case TrackVolume: bDataWritten = writeAudVidData("TrackVolume", 2, 2, 2, 0) ; break;
case ImageWidth: if( bVideo ) bDataWritten = writeAudVidData("Width", (256*256), 4, 0, 0) ; break;
case ImageHeight:if( bVideo ) bDataWritten = writeAudVidData("Height", (256*256), 4, 0, 0) ; break;
}
if(!bDataWritten) io_->seek(4,BasicIo::cur);
}
io_->read(buf.pData_, size%4);
}
}
bool QuickTimeVideo::writeAudVidData
( std::string sXmpTag, double iMulFactor, int64_t iBytCnt, int64_t iOffset, int64_t iRetFuncCall)
{
DataBuf buf(5);
bool bRetVal = false;
int64_t iXmpTagVal = 0;
if(d->currentStream_ == Video){
if(xmpData_["Xmp.video." + sXmpTag].count() > 0){
iXmpTagVal = xmpData_["Xmp.video." + sXmpTag].toLong()*iMulFactor;
if(iRetFuncCall == 0) buf = returnBuf(iXmpTagVal, iBytCnt);
else buf = returnUnsignedBuf(iXmpTagVal);
bRetVal = writeMultibyte(buf.pData_, iBytCnt ,iOffset);
}
}
else if(d->currentStream_ == Audio){
if(xmpData_["Xmp.audio." + sXmpTag].count() > 0){
iXmpTagVal = xmpData_["Xmp.audio." + sXmpTag].toLong()*iMulFactor;
if(iRetFuncCall == 0) buf = returnBuf(iXmpTagVal, iBytCnt);
else buf = returnUnsignedBuf(iXmpTagVal);
bRetVal = writeMultibyte(buf.pData_, iBytCnt ,iOffset);
}
}
return bRetVal;
}
void QuickTimeVideo::readAudVidData(std::string sTag, int32_t iNumRdBytes, double dMulFact ,int32_t iFunc2Call){
DataBuf buf(5);
io_->read(buf.pData_, 4);
if(d->currentStream_ == Video){
if(iFunc2Call == 0) xmpData_["Xmp.video."+ sTag] = (int64_t)(returnBufValue(buf, iNumRdBytes) * dMulFact) ;
else if(iFunc2Call == 1) xmpData_["Xmp.video."+ sTag] = (uint64_t)(returnUnsignedBufValue(buf, iNumRdBytes) * dMulFact);
}
else if(d->currentStream_ == Audio){
if(iFunc2Call == 0) xmpData_["Xmp.audio."+ sTag] = (int64_t)(returnBufValue(buf, iNumRdBytes) * dMulFact);
else if(iFunc2Call == 1)xmpData_["Xmp.audio."+ sTag] = (uint64_t)(returnUnsignedBufValue(buf, iNumRdBytes) * dMulFact);
}
}
void QuickTimeVideo::readAudVidStrData(std::string sTag, int32_t iNumRdBytes){
DataBuf buf(iNumRdBytes);
io_->read(buf.pData_, iNumRdBytes);
if(d->currentStream_ == Video) xmpData_["Xmp.video."+ sTag] = Exiv2::toString(buf.pData_);
else if(d->currentStream_ == Audio) xmpData_["Xmp.audio."+ sTag] = Exiv2::toString(buf.pData_);
}
void QuickTimeVideo::readStrData(std::string sTag, int32_t iNumRdBytes){
DataBuf buf(iNumRdBytes);
io_->read(buf.pData_, iNumRdBytes);
xmpData_[sTag] = Exiv2::toString(buf.pData_);
}
void QuickTimeVideo::movieHeaderDecoder(uint32_t size)
{
DataBuf buf(5);
if(!d->m_modifyMetadata){
for (int32_t 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: xmpData_["Xmp.video.DateUTC"] = returnUnsignedBufValue(buf) ; break;
case ModifyDate: xmpData_["Xmp.video.ModificationDate"] = returnUnsignedBufValue(buf) ; break;
case Duration: xmpData_["Xmp.video.Duration"] = (int64_t)((double)returnBufValue(buf)
* (double)1000/ (double)d->timeScale_); break;
case PreferredRate: xmpData_["Xmp.video.PreferredRate"] = returnBufValue(buf, 4)/(256*256) ; break;
case PreferredVolume: xmpData_["Xmp.video.PreferredVolume"] = (returnUnsignedBufValue(buf, 2)/2) ; break;
case PreviewTime: xmpData_["Xmp.video.PreviewTime"] = (int64_t)((double)returnBufValue(buf)
* (double)1000/ (double)d->timeScale_); break;
case PreviewDuration: xmpData_["Xmp.video.PreviewDuration"] = (int64_t)((double)returnBufValue(buf)
* (double)1000/ (double)d->timeScale_); 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)*1000/ d->timeScale_; break;
case CurrentTime: xmpData_["Xmp.video.CurrentTime"] = returnBufValue(buf) ; break;
case NextTrackID: xmpData_["Xmp.video.NextTrackID"] = returnBufValue(buf) ; break;
case TimeScale:
xmpData_["Xmp.video.TimeScale"] = returnBufValue(buf);
d->timeScale_ = returnBufValue(buf);
break;
}
}
io_->read(buf.pData_, size%4);
} else {
for (int32_t i = 0; size/4 != 0 ; size -=4, i++){
bool bDataWritten = false;
std::memset(buf.pData_, 0x0, buf.size_);
switch(i){
//Fixme:There are known roundoff error while typecasting the number to int64_t
//for example Duration (double to int64_t)
case MovieHeaderVersion:
if(xmpData_["Xmp.video.MovieHeaderVersion"].count() > 0){
int64_t movieHeaderVersion = (int64_t)xmpData_["Xmp.video.MovieHeaderVersion"].toLong();
buf = returnBuf(movieHeaderVersion,1);
bDataWritten = writeMultibyte(&buf.pData_[0], 1, 3);
}
break;
case CreateDate:
if(xmpData_["Xmp.video.DateUTC"].count() > 0){
uint64_t dateUTC = (uint64_t)xmpData_["Xmp.video.DateUTC"].toLong();
buf = returnUnsignedBuf(dateUTC);
bDataWritten = writeMultibyte(buf.pData_,4);
}
break;
case ModifyDate:
if(xmpData_["Xmp.video.ModificationDate"].count() > 0){
uint64_t modificationDate = (uint64_t)xmpData_["Xmp.video.ModificationDate"].toLong();
buf = returnUnsignedBuf(modificationDate);
bDataWritten = writeMultibyte(buf.pData_,4);
}
break;
case TimeScale:
if(xmpData_["Xmp.video.TimeScale"].count() > 0){
io_->read(buf.pData_,4);
d->timeScale_ = returnBufValue(buf);
io_->seek(-4,BasicIo::cur);
int64_t tmpTimeScale = (int64_t)xmpData_["Xmp.video.TimeScale"].toLong();
buf = returnBuf(tmpTimeScale);
bDataWritten = writeMultibyte(buf.pData_,4);
} else {
io_->read(buf.pData_,4);
d->timeScale_ = returnBufValue(buf);
bDataWritten = true;
}
break;
case Duration:
if(xmpData_["Xmp.video.Duration"].count() > 0){
int64_t duration = (int64_t)((double)xmpData_["Xmp.video.Duration"].
toLong()*(double)d->timeScale_/(double)1000);
buf = returnBuf(duration);
bDataWritten = writeMultibyte(buf.pData_,4);
}
break;
case PreferredRate:
if(xmpData_["Xmp.video.PreferredRate"].count() > 0){
buf = returnBuf((int64_t)xmpData_["Xmp.video.PreferredRate"].toLong()
*(256*256),4);
bDataWritten = writeMultibyte(buf.pData_,4);
}
break;
case PreferredVolume:
if(xmpData_["Xmp.video.PreferredVolume"].count() > 0){
uint64_t preferredVolume = (uint64_t)(xmpData_["Xmp.video.PreferredVolume"]
.toLong()*2);//128 = 100%
buf = returnBuf(preferredVolume,2);
bDataWritten = writeMultibyte(buf.pData_, 2, 2);
}
break;
case PreviewTime:
if(xmpData_["Xmp.video.PreviewTime"].count() > 0){
int64_t previewTime = (int64_t)((double)xmpData_["Xmp.video.PreviewTime"].toLong()
*(double)d->timeScale_/(double)1000);//1024= 1 second
buf = returnBuf(previewTime);
bDataWritten = writeMultibyte(buf.pData_,4);
}
break;
case PreviewDuration:
if(xmpData_["Xmp.video.PreviewDuration"].count() > 0){
int64_t previewDuration = (int64_t)((double)xmpData_["Xmp.video.PreviewDuration"]
.toLong()*(double)d->timeScale_/(double)1000);
buf = returnBuf(previewDuration);
bDataWritten = writeMultibyte(buf.pData_,4);
}
break;
case PosterTime:
if(xmpData_["Xmp.video.PosterTime"].count() > 0){
int64_t posterTime = (int64_t)xmpData_["Xmp.video.PosterTime"].toLong();
buf = returnBuf(posterTime);
bDataWritten = writeMultibyte(buf.pData_,4);
}
break;
case SelectionTime:
if(xmpData_["Xmp.video.SelectionTime"].count() > 0){
int64_t selectionTime = (int64_t)xmpData_["Xmp.video.SelectionTime"].toLong();
buf = returnBuf(selectionTime);
bDataWritten = writeMultibyte(buf.pData_,4);
}
break;
case SelectionDuration:
if(xmpData_["Xmp.video.SelectionDuration"].count() > 0){
int64_t selectionDuration = (int64_t)((double)xmpData_["Xmp.video.SelectionDuration"]
.toLong()*(double)d->timeScale_/(double)1000);
buf = returnBuf(selectionDuration);
bDataWritten = writeMultibyte(buf.pData_,4);
}
break;
case CurrentTime:
if(xmpData_["Xmp.video.CurrentTime"].count() > 0){
int64_t currentTime = (int64_t)xmpData_["Xmp.video.CurrentTime"].toLong();
buf = returnBuf(currentTime);
bDataWritten = writeMultibyte(buf.pData_,4);
}
break;
case NextTrackID:
if(xmpData_["Xmp.video.NextTrackID"].count() > 0){
int64_t nextTrackID = (int64_t)xmpData_["Xmp.video.NextTrackID"].toLong();
buf = returnBuf(nextTrackID);
bDataWritten = writeMultibyte(buf.pData_,4);
}
break;
}
if(!bDataWritten) io_->seek(4, BasicIo::cur);
}
io_->seek(size%4,BasicIo::cur);
}
}
void QuickTimeVideo::aspectRatio()
{
//TODO - Make a better unified method to handle all cases of Aspect Ratio
double aspectRatio = (double)d->width_ / (double)d->height_;
aspectRatio = floor(aspectRatio*10) / 10;
xmpData_["Xmp.video.AspectRatio"] = aspectRatio;
int32_t aR = (int32_t) ((aspectRatio*10.0)+0.1);
switch (aR) {
case 13 : xmpData_["Xmp.video.AspectRatio"] = "4:3" ; break;
case 17 : xmpData_["Xmp.video.AspectRatio"] = "16:9" ; break;
case 10 : xmpData_["Xmp.video.AspectRatio"] = "1:1" ; break;
case 16 : xmpData_["Xmp.video.AspectRatio"] = "16:10" ; break;
case 22 : xmpData_["Xmp.video.AspectRatio"] = "2.21:1" ; break;
case 23 : xmpData_["Xmp.video.AspectRatio"] = "2.35:1" ; break;
case 12 : xmpData_["Xmp.video.AspectRatio"] = "5:4" ; break;
default : xmpData_["Xmp.video.AspectRatio"] = aspectRatio;break; // needed?
}
}
void QuickTimeVideo::reverseTagDetails
(const TagDetails inputTagVocabulary[], RevTagDetails outputTagVocabulary[] ,int32_t size)
{
for (int32_t i=0; i<size ;i++){
outputTagVocabulary[i].label_ = inputTagVocabulary[i].label_;
outputTagVocabulary[i].val_ = inputTagVocabulary[i].val_;
}
}
Image::AutoPtr newQTimeInstance(BasicIo::AutoPtr io, bool /*create*/)
{
Image::AutoPtr image(new QuickTimeVideo(io));
if (!image->good()) image.reset();
return image;
}
void QuickTimeVideo::writeStringData(Exiv2::Xmpdatum xmpStringData, int32_t size, int32_t skipOffset)
{
if(xmpStringData.count() > 0){
std::string dataString = xmpStringData.toString();
Exiv2::byte* rawData = new byte[(uint8_t)size];
int32_t newSize = (int32_t)dataString.size();
for(int32_t i=0; i<min(newSize,size); i++) rawData[i] = (Exiv2::byte)dataString[i];
for(int32_t i=newSize; i<size; i++) rawData[i] = (Exiv2::byte)0;
io_->write(rawData,size);
io_->seek((int)skipOffset,BasicIo::cur);
delete[] rawData;
} else io_->seek((size+skipOffset),BasicIo::cur);
}
void QuickTimeVideo::writeLongData(Exiv2::Xmpdatum xmpIntData, int32_t size, int32_t skipOffset)
{
if(xmpIntData.count() > 0){
int32_t rawIntData = (int32_t)xmpIntData.toLong();
io_->write((Exiv2::byte*)&rawIntData,size);
io_->seek(skipOffset,BasicIo::cur);
}
else io_->seek((size+skipOffset),BasicIo::cur);
}
void QuickTimeVideo::writeShortData(Exiv2::Xmpdatum xmpIntData, int16_t size, int32_t skipOffset)
{
if(xmpIntData.count() > 0){
int16_t rawIntData = (int16_t)xmpIntData.toLong();
io_->write((Exiv2::byte*)&rawIntData,size);
io_->seek(skipOffset,BasicIo::cur);
}
else io_->seek((size+skipOffset),BasicIo::cur);
}
/*!
* \brief QuickTimeVideo::writeMultibyte Write data directly from acurrent position of basicIo poiter with the bRawData upto size number of bytes
* and then skips iOffset bytes.
* \param bRawData
* \param iSize
* \param iOffset
* \return
*/
bool QuickTimeVideo::writeMultibyte(Exiv2::byte * bRawData ,int64_t iSize, int64_t iOffset)
{
int32_t iCurrPos = io_->tell();
if (iSize != 0 && bRawData != NULL){
if ((io_->write(bRawData, iSize) > 0)) io_->seek(iOffset , BasicIo::cur);
else io_->seek(iCurrPos + iSize + iOffset, BasicIo::beg);
}
else io_->seek(iCurrPos + iOffset, BasicIo::beg);
//Return value is just for assignment to a variable at calling location.
return true;
}
void QuickTimeVideo::writeApertureData(Exiv2::Xmpdatum xmpApertureData, int16_t size, int32_t skipOffset)
{
if(xmpApertureData.count() >0){
Exiv2::byte rawApertureData[4];
Exiv2::byte rawApertureDataTmp[2];
const std::string apertureData = xmpApertureData.toString();
vector<uint16_t> apertureDataValue = getNumberFromString(apertureData,'.');
memcpy(rawApertureDataTmp,&apertureDataValue.at(0),2);
rawApertureData[0] = rawApertureDataTmp[0];
rawApertureData[1] = rawApertureDataTmp[1];
memcpy(rawApertureDataTmp,&apertureDataValue.at(1),2);
rawApertureData[2] = rawApertureDataTmp[0];
rawApertureData[3] = rawApertureDataTmp[1];
io_->write(rawApertureData,size);
io_->seek(skipOffset,BasicIo::cur);
}
else io_->seek((size+skipOffset),BasicIo::cur);
}
/*!
* \brief isQTimeType Cheecks if file belongs to Quicktime container type.
* \param iIo
* \param advance
* \return
*/
bool isQTimeType(BasicIo& iIo, bool advance){
const int32_t len = 4;
Exiv2::byte* buf = new byte[len];
iIo.read(buf, len);
iIo.read(buf, len);
if (iIo.error() || iIo.eof()){
delete[] buf;
return false;
}
bool matched = isQuickTimeType(buf[0], buf[1], buf[2], buf[3]);
if (!advance || !matched) iIo.seek(static_cast<int32_t>(0), BasicIo::beg);
delete[] buf;
return matched;
} // isQTimeType
} // namespace Exiv2