// ***************************************************************** -*- C++ -*- /* * Copyright (C) 2004-2012 Andreas Huggel * * This program is part of the Exiv2 distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. */ /* File: riffvideo.cpp Version: $Rev$ Author(s): Abhinav Badola for GSoC 2012 (AB) Mahesh Hegde for GSoC 2013 mailto:maheshmhegade at gmail dot com History: 18-Jun-12, AB: created Credits: See header file */ // ***************************************************************************** #include "rcsid_int.hpp" EXIV2_RCSID("@(#) $Id$") // ***************************************************************************** // included header files #include "riffvideo.hpp" #include "futils.hpp" #include "basicio.hpp" #include "tags.hpp" #include "tags_int.hpp" #include "types.hpp" #include "tiffimage_int.hpp" #include "string.h" #include "utilsvideo.hpp" // + standard includes #include // ***************************************************************************** // class member definitions namespace Exiv2{ namespace Internal{ /*! @brief Dummy TIFF header structure. */ class DummyTiffHeader : public TiffHeaderBase{ public: //! @name Creators //@{ //! Default constructor DummyTiffHeader(ByteOrder byteOrder); //! Destructor ~DummyTiffHeader(); //@} //! @name Manipulators //@{ //! Dummy read function. Does nothing and returns true. bool read(const byte* pData, uint64_t size); //@} }; // class TiffHeader DummyTiffHeader::DummyTiffHeader(ByteOrder byteOrder) : TiffHeaderBase(42, 0, byteOrder, 0){ } DummyTiffHeader::~DummyTiffHeader(){ } bool DummyTiffHeader::read(const byte* /*pData*/, uint64_t /*size*/){ return true; } //! RIFF INFO tags supported Under Exiv2. extern const TagVocabulary infoTags[] ={ { "AGES", "Xmp.video.Rated" }, { "CMNT", "Xmp.video.Comment" }, { "CODE", "Xmp.video.EncodedBy" }, { "COMM", "Xmp.video.Comment" }, { "DIRC", "Xmp.video.Director" }, { "DISP", "Xmp.audio.SchemeTitle" }, { "DTIM", "Xmp.video.DateTimeOriginal" }, { "GENR", "Xmp.video.Genre" }, { "IARL", "Xmp.video.ArchivalLocation" }, { "IART", "Xmp.video.Artist" }, { "IAS1", "Xmp.video.Edit1" }, { "IAS2", "Xmp.video.Edit2" }, { "IAS3", "Xmp.video.Edit3" }, { "IAS4", "Xmp.video.Edit4" }, { "IAS5", "Xmp.video.Edit5" }, { "IAS6", "Xmp.video.Edit6" }, { "IAS7", "Xmp.video.Edit7" }, { "IAS8", "Xmp.video.Edit8" }, { "IAS9", "Xmp.video.Edit9" }, { "IBSU", "Xmp.video.BaseURL" }, { "ICAS", "Xmp.audio.DefaultStream" }, { "ICDS", "Xmp.video.CostumeDesigner" }, { "ICMS", "Xmp.video.Commissioned" }, { "ICMT", "Xmp.video.Comment" }, { "ICNM", "Xmp.video.Cinematographer" }, { "ICNT", "Xmp.video.Country" }, { "ICOP", "Xmp.video.Copyright" }, { "ICRD", "Xmp.video.DateTimeDigitized" }, { "ICRP", "Xmp.video.Cropped" }, { "IDIM", "Xmp.video.Dimensions " }, { "IDPI", "Xmp.video.DotsPerInch" }, { "IDST", "Xmp.video.DistributedBy" }, { "IEDT", "Xmp.video.EditedBy" }, { "IENC", "Xmp.video.EncodedBy" }, { "IENG", "Xmp.video.Engineer" }, { "IGNR", "Xmp.video.Genre" }, { "IKEY", "Xmp.video.PerformerKeywords" }, { "ILGT", "Xmp.video.Lightness" }, { "ILGU", "Xmp.video.LogoURL" }, { "ILIU", "Xmp.video.LogoIconURL" }, { "ILNG", "Xmp.video.Language" }, { "IMBI", "Xmp.video.InfoBannerImage" }, { "IMBU", "Xmp.video.InfoBannerURL" }, { "IMED", "Xmp.video.Medium" }, { "IMIT", "Xmp.video.InfoText" }, { "IMIU", "Xmp.video.InfoURL" }, { "IMUS", "Xmp.video.MusicBy" }, { "INAM", "Xmp.video.Title" }, { "IPDS", "Xmp.video.ProductionDesigner" }, { "IPLT", "Xmp.video.NumOfColors" }, { "IPRD", "Xmp.video.Product" }, { "IPRO", "Xmp.video.ProducedBy" }, { "IRIP", "Xmp.video.RippedBy" }, { "IRTD", "Xmp.video.Rating" }, { "ISBJ", "Xmp.video.Subject" }, { "ISFT", "Xmp.video.Software" }, { "ISGN", "Xmp.video.SecondaryGenre" }, { "ISHP", "Xmp.video.Sharpness" }, { "ISRC", "Xmp.video.Source" }, { "ISRF", "Xmp.video.SourceForm" }, { "ISTD", "Xmp.video.ProductionStudio" }, { "ISTR", "Xmp.video.Starring" }, { "ITCH", "Xmp.video.Technician" }, { "IWMU", "Xmp.video.WatermarkURL" }, { "IWRI", "Xmp.video.WrittenBy" }, { "LANG", "Xmp.video.Language" }, { "LOCA", "Xmp.video.LocationInfo" }, { "PRT1", "Xmp.video.Part" }, { "PRT2", "Xmp.video.NumOfParts" }, { "RATE", "Xmp.video.Rate" }, { "STAR", "Xmp.video.Starring" }, { "STAT", "Xmp.video.Statistics" }, { "TAPE", "Xmp.video.TapeName" }, { "TCDO", "Xmp.video.EndTimecode" }, { "TCOD", "Xmp.video.StartTimecode" }, { "TITL", "Xmp.video.Title" }, { "TLEN", "Xmp.video.Length" }, { "TORG", "Xmp.video.Organization" }, { "TRCK", "Xmp.video.TrackNumber" }, { "TURL", "Xmp.video.URL" }, { "TVER", "Xmp.video.SoftwareVersion" }, { "VMAJ", "Xmp.video.VegasVersionMajor" }, { "VMIN", "Xmp.video.VegasVersionMinor" }, { "YEAR", "Xmp.video.Year" } }; //! Audio Encoding Information. extern const TagDetails audioEncodingValues[] ={ { 0x1, "Microsoft PCM" }, { 0x2, "Microsoft ADPCM" }, { 0x3, "Microsoft IEEE float" }, { 0x4, "Compaq VSELP" }, { 0x5, "IBM CVSD" }, { 0x6, "Microsoft a-Law" }, { 0x7, "Microsoft u-Law" }, { 0x8, "Microsoft DTS" }, { 0x9, "DRM" }, { 0xa, "WMA 9 Speech" }, { 0xb, "Microsoft Windows Media RT Voice" }, { 0x10, "OKI-ADPCM" }, { 0x11, "Intel IMA/DVI-ADPCM" }, { 0x12, "Videologic Mediaspace ADPCM" }, { 0x13, "Sierra ADPCM" }, { 0x14, "Antex G.723 ADPCM" }, { 0x15, "DSP Solutions DIGISTD" }, { 0x16, "DSP Solutions DIGIFIX" }, { 0x17, "Dialoic OKI ADPCM" }, { 0x18, "Media Vision ADPCM" }, { 0x19, "HP CU" }, { 0x1a, "HP Dynamic Voice" }, { 0x20, "Yamaha ADPCM" }, { 0x21, "SONARC Speech Compression" }, { 0x22, "DSP Group True Speech" }, { 0x23, "Echo Speech Corp." }, { 0x24, "Virtual Music Audiofile AF36" }, { 0x25, "Audio Processing Tech." }, { 0x26, "Virtual Music Audiofile AF10" }, { 0x27, "Aculab Prosody 1612" }, { 0x28, "Merging Tech. LRC" }, { 0x30, "Dolby AC2" }, { 0x31, "Microsoft GSM610" }, { 0x32, "MSN Audio" }, { 0x33, "Antex ADPCME" }, { 0x34, "Control Resources VQLPC" }, { 0x35, "DSP Solutions DIGIREAL" }, { 0x36, "DSP Solutions DIGIADPCM" }, { 0x37, "Control Resources CR10" }, { 0x38, "Natural MicroSystems VBX ADPCM" }, { 0x39, "Crystal Semiconductor IMA ADPCM" }, { 0x3a, "Echo Speech ECHOSC3" }, { 0x3b, "Rockwell ADPCM" }, { 0x3c, "Rockwell DIGITALK" }, { 0x3d, "Xebec Multimedia" }, { 0x40, "Antex G.721 ADPCM" }, { 0x41, "Antex G.728 CELP" }, { 0x42, "Microsoft MSG723" }, { 0x43, "IBM AVC ADPCM" }, { 0x45, "ITU-T G.726" }, { 0x50, "Microsoft MPEG" }, { 0x51, "RT23 or PAC" }, { 0x52, "InSoft RT24" }, { 0x53, "InSoft PAC" }, { 0x55, "MP3" }, { 0x59, "Cirrus" }, { 0x60, "Cirrus Logic" }, { 0x61, "ESS Tech. PCM" }, { 0x62, "Voxware Inc." }, { 0x63, "Canopus ATRAC" }, { 0x64, "APICOM G.726 ADPCM" }, { 0x65, "APICOM G.722 ADPCM" }, { 0x66, "Microsoft DSAT" }, { 0x67, "Micorsoft DSAT DISPLAY" }, { 0x69, "Voxware Byte Aligned" }, { 0x70, "Voxware AC8" }, { 0x71, "Voxware AC10" }, { 0x72, "Voxware AC16" }, { 0x73, "Voxware AC20" }, { 0x74, "Voxware MetaVoice" }, { 0x75, "Voxware MetaSound" }, { 0x76, "Voxware RT29HW" }, { 0x77, "Voxware VR12" }, { 0x78, "Voxware VR18" }, { 0x79, "Voxware TQ40" }, { 0x7a, "Voxware SC3" }, { 0x7b, "Voxware SC3" }, { 0x80, "Soundsoft" }, { 0x81, "Voxware TQ60" }, { 0x82, "Microsoft MSRT24" }, { 0x83, "AT&T G.729A" }, { 0x84, "Motion Pixels MVI MV12" }, { 0x85, "DataFusion G.726" }, { 0x86, "DataFusion GSM610" }, { 0x88, "Iterated Systems Audio" }, { 0x89, "Onlive" }, { 0x8a, "Multitude, Inc. FT SX20" }, { 0x8b, "Infocom ITS A/S G.721 ADPCM" }, { 0x8c, "Convedia G729" }, { 0x8d, "Not specified congruency, Inc." }, { 0x91, "Siemens SBC24" }, { 0x92, "Sonic Foundry Dolby AC3 APDIF" }, { 0x93, "MediaSonic G.723" }, { 0x94, "Aculab Prosody 8kbps" }, { 0x97, "ZyXEL ADPCM" }, { 0x98, "Philips LPCBB" }, { 0x99, "Studer Professional Audio Packed" }, { 0xa0, "Malden PhonyTalk" }, { 0xa1, "Racal Recorder GSM" }, { 0xa2, "Racal Recorder G720.a" }, { 0xa3, "Racal G723.1" }, { 0xa4, "Racal Tetra ACELP" }, { 0xb0, "NEC AAC NEC Corporation" }, { 0xff, "AAC" }, { 0x100, "Rhetorex ADPCM" }, { 0x101, "IBM u-Law" }, { 0x102, "IBM a-Law" }, { 0x103, "IBM ADPCM" }, { 0x111, "Vivo G.723" }, { 0x112, "Vivo Siren" }, { 0x120, "Philips Speech Processing CELP" }, { 0x121, "Philips Speech Processing GRUNDIG" }, { 0x123, "Digital G.723" }, { 0x125, "Sanyo LD ADPCM" }, { 0x130, "Sipro Lab ACEPLNET" }, { 0x131, "Sipro Lab ACELP4800" }, { 0x132, "Sipro Lab ACELP8V3" }, { 0x133, "Sipro Lab G.729" }, { 0x134, "Sipro Lab G.729A" }, { 0x135, "Sipro Lab Kelvin" }, { 0x136, "VoiceAge AMR" }, { 0x140, "Dictaphone G.726 ADPCM" }, { 0x150, "Qualcomm PureVoice" }, { 0x151, "Qualcomm HalfRate" }, { 0x155, "Ring Zero Systems TUBGSM" }, { 0x160, "Microsoft Audio1" }, { 0x161, "Windows Media Audio V2 V7 V8 V9 / DivX audio (WMA) / Alex AC3 Audio" }, { 0x162, "Windows Media Audio Professional V9" }, { 0x163, "Windows Media Audio Lossless V9" }, { 0x164, "WMA Pro over S/PDIF" }, { 0x170, "UNISYS NAP ADPCM" }, { 0x171, "UNISYS NAP ULAW" }, { 0x172, "UNISYS NAP ALAW" }, { 0x173, "UNISYS NAP 16K" }, { 0x174, "MM SYCOM ACM SYC008 SyCom Technologies" }, { 0x175, "MM SYCOM ACM SYC701 G726L SyCom Technologies" }, { 0x176, "MM SYCOM ACM SYC701 CELP54 SyCom Technologies" }, { 0x177, "MM SYCOM ACM SYC701 CELP68 SyCom Technologies" }, { 0x178, "Knowledge Adventure ADPCM" }, { 0x180, "Fraunhofer IIS MPEG2AAC" }, { 0x190, "Digital Theater Systems DTS DS" }, { 0x200, "Creative Labs ADPCM" }, { 0x202, "Creative Labs FASTSPEECH8" }, { 0x203, "Creative Labs FASTSPEECH10" }, { 0x210, "UHER ADPCM" }, { 0x215, "Ulead DV ACM" }, { 0x216, "Ulead DV ACM" }, { 0x220, "Quarterdeck Corp." }, { 0x230, "I-Link VC" }, { 0x240, "Aureal Semiconductor Raw Sport" }, { 0x241, "ESST AC3" }, { 0x250, "Interactive Products HSX" }, { 0x251, "Interactive Products RPELP" }, { 0x260, "Consistent CS2" }, { 0x270, "Sony SCX" }, { 0x271, "Sony SCY" }, { 0x272, "Sony ATRAC3" }, { 0x273, "Sony SPC" }, { 0x280, "TELUM Telum Inc." }, { 0x281, "TELUMIA Telum Inc." }, { 0x285, "Norcom Voice Systems ADPCM" }, { 0x300, "Fujitsu FM TOWNS SND" }, { 0x301, "Fujitsu (not specified)" }, { 0x302, "Fujitsu (not specified)" }, { 0x303, "Fujitsu (not specified)" }, { 0x304, "Fujitsu (not specified)" }, { 0x305, "Fujitsu (not specified)" }, { 0x306, "Fujitsu (not specified)" }, { 0x307, "Fujitsu (not specified)" }, { 0x308, "Fujitsu (not specified)" }, { 0x350, "Micronas Semiconductors, Inc. Development" }, { 0x351, "Micronas Semiconductors, Inc. CELP833" }, { 0x400, "Brooktree Digital" }, { 0x401, "Intel Music Coder (IMC)" }, { 0x402, "Ligos Indeo Audio" }, { 0x450, "QDesign Music" }, { 0x500, "On2 VP7 On2 Technologies" }, { 0x501, "On2 VP6 On2 Technologies" }, { 0x680, "AT&T VME VMPCM" }, { 0x681, "AT&T TCP" }, { 0x700, "YMPEG Alpha (dummy for MPEG-2 compressor)" }, { 0x8ae, "ClearJump LiteWave (lossless)" }, { 0x1000, "Olivetti GSM" }, { 0x1001, "Olivetti ADPCM" }, { 0x1002, "Olivetti CELP" }, { 0x1003, "Olivetti SBC" }, { 0x1004, "Olivetti OPR" }, { 0x1100, "Lernout & Hauspie" }, { 0x1101, "Lernout & Hauspie CELP codec" }, { 0x1102, "Lernout & Hauspie SBC codec" }, { 0x1103, "Lernout & Hauspie SBC codec" }, { 0x1104, "Lernout & Hauspie SBC codec" }, { 0x1400, "Norris Comm. Inc." }, { 0x1401, "ISIAudio" }, { 0x1500, "AT&T Soundspace Music Compression" }, { 0x181c, "VoxWare RT24 speech codec" }, { 0x181e, "Lucent elemedia AX24000P Music codec" }, { 0x1971, "Sonic Foundry LOSSLESS" }, { 0x1979, "Innings Telecom Inc. ADPCM" }, { 0x1c07, "Lucent SX8300P speech codec" }, { 0x1c0c, "Lucent SX5363S G.723 compliant codec" }, { 0x1f03, "CUseeMe DigiTalk (ex-Rocwell)" }, { 0x1fc4, "NCT Soft ALF2CD ACM" }, { 0x2000, "FAST Multimedia DVM" }, { 0x2001, "Dolby DTS (Digital Theater System)" }, { 0x2002, "RealAudio 1 / 2 14.4" }, { 0x2003, "RealAudio 1 / 2 28.8" }, { 0x2004, "RealAudio G2 / 8 Cook (low bitrate)" }, { 0x2005, "RealAudio 3 / 4 / 5 Music (DNET)" }, { 0x2006, "RealAudio 10 AAC (RAAC)" }, { 0x2007, "RealAudio 10 AAC+ (RACP)" }, { 0x2500, "Reserved range to 0x2600 Microsoft" }, { 0x3313, "makeAVIS (ffvfw fake AVI sound from AviSynth scripts)" }, { 0x4143, "Divio MPEG-4 AAC audio" }, { 0x4201, "Nokia adaptive multirate" }, { 0x4243, "Divio G726 Divio, Inc." }, { 0x434c, "LEAD Speech" }, { 0x564c, "LEAD Vorbis" }, { 0x5756, "WavPack Audio" }, { 0x674f, "Ogg Vorbis (mode 1)" }, { 0x6750, "Ogg Vorbis (mode 2)" }, { 0x6751, "Ogg Vorbis (mode 3)" }, { 0x676f, "Ogg Vorbis (mode 1+)" }, { 0x6770, "Ogg Vorbis (mode 2+)" }, { 0x6771, "Ogg Vorbis (mode 3+)" }, { 0x7000, "3COM NBX 3Com Corporation" }, { 0x706d, "FAAD AAC" }, { 0x7a21, "GSM-AMR (CBR, no SID)" }, { 0x7a22, "GSM-AMR (VBR, including SID)" }, { 0xa100, "Comverse Infosys Ltd. G723 1" }, { 0xa101, "Comverse Infosys Ltd. AVQSBC" }, { 0xa102, "Comverse Infosys Ltd. OLDSBC" }, { 0xa103, "Symbol Technologies G729A" }, { 0xa104, "VoiceAge AMR WB VoiceAge Corporation" }, { 0xa105, "Ingenient Technologies Inc. G726" }, { 0xa106, "ISO/MPEG-4 advanced audio Coding" }, { 0xa107, "Encore Software Ltd G726" }, { 0xa109, "Speex ACM Codec xiph.org" }, { 0xdfac, "DebugMode SonicFoundry Vegas FrameServer ACM Codec" }, { 0xe708, "Unknown -" }, { 0xf1ac, "Free Lossless Audio Codec FLAC" }, { 0xfffe, "Extensible" }, { 0xffff, "Development" } }; //! Nikon tags. extern const TagDetails nikonAVITags[] ={ { 0x0003, "Xmp.video.Make" }, { 0x0004, "Xmp.video.Model" }, { 0x0005, "Xmp.video.Software" }, { 0x0006, "Xmp.video.Equipment" }, { 0x0007, "Xmp.video.Orientation" }, { 0x0008, "Xmp.video.ExposureTime" }, { 0x0009, "Xmp.video.FNumber" }, { 0x000a, "Xmp.video.ExposureCompensation" }, { 0x000b, "Xmp.video.MaxApertureValue" }, { 0x000c, "Xmp.video.MeteringMode" }, { 0x000f, "Xmp.video.FocalLength" }, { 0x0010, "Xmp.video.XResolution" }, { 0x0011, "Xmp.video.YResolution" }, { 0x0012, "Xmp.video.ResolutionUnit" }, { 0x0013, "Xmp.video.DateTimeOriginal" }, { 0x0014, "Xmp.video.DateTimeDigitized" }, { 0x0016, "Xmp.video.duration" }, { 0x0018, "Xmp.video.FocusMode" }, { 0x001b, "Xmp.video.DigitalZoomRatio" }, { 0x001d, "Xmp.video.ColorMode" }, { 0x001e, "Xmp.video.Sharpness" }, { 0x001f, "Xmp.video.WhiteBalance" }, { 0x0020, "Xmp.video.ColorNoiseReduction" } }; /* extern const TagDetails orientation[] = { { 1, "Horizontal (normal)" }, { 2, "Mirror horizontal" }, { 3, "Rotate 180" }, { 4, "Mirror vertical" }, { 5, "Mirror horizontal and rotate 270 CW" }, { 6, "Rotate 90 CW" }, { 7, "Mirror horizontal and rotate 90 CW" }, { 8, "Rotate 270 CW" } }; */ //! Metering tags. extern const TagDetails meteringMode[] ={ { 0, "Unknown" }, { 1, "Average" }, { 2, "Center-weighted average" }, { 3, "Spot" }, { 4, "Multi-spot" }, { 5, "Multi-segment" }, { 6, "Partial" }, { 255, "Other" } }; //!Resolution tag. extern const TagDetails resolutionUnit[] ={ { 1, "None" }, { 2, "inches" }, { 3, "cm" } }; int64_t i; //!< temporary variable. int64_t j; //!< temporary variable. //! To select specific bytes with in a chunk. enum streamTypeInfo{ Audio = 1, MIDI, Text, Video }; //! To select specific bytes with in a chunk. enum streamHeaderTags{ codec = 1, sampleRate = 5, sampleCount = 8, quality = 10, sampleSize }; //! To select specific bytes with in a chunk. enum bmptags{ imageWidth, imageHeight, planes, bitDepth, compression, imageLength, pixelsPerMeterX, pixelsPerMeterY, numColors, numImportantColors }; //! To select specific bytes with in a chunk. enum audioFormatTags{ encoding, numberOfChannels, audioSampleRate, avgBytesPerSec = 4, bitsPerSample = 7 }; //! To select specific bytes with in a chunk. enum aviHeaderTags{ frameRate, maxDataRate, frameCount = 4, initialFrames ,streamCount, suggestedBufferSize ,imageWidth_h, imageHeight_h }; } } // namespace Internal, Exiv2 namespace Exiv2{ using namespace Exiv2::Internal; class RiffVideo::Private{ public: Private(){ streamType_ = 0; m_decodeMetaData = true; } //! Variable which stores current stream being processsed. int streamType_; //! Variable to decide whether to decode the metadata or just reveal file structure (Lists inside RIFF) bool m_decodeMetaData; //!variable to decide whether write metadata is possible or not (by default false) bool m_modifyMetadata; //!abstract metadata holding variable (its data about data about data!) RiffMetaSkeleton m_riffFileSkeleton; }; RiffVideo::RiffVideo(BasicIo::AutoPtr io) : Image(ImageType::riff, mdNone, io), d(new Private){ d->m_modifyMetadata = false; d->m_decodeMetaData = true; } // RiffVideo::RiffVideo RiffVideo::~RiffVideo(){ delete d; } std::string RiffVideo::mimeType() const{ return "video/riff"; } /*! * \brief RiffVideo::findChunkPositions: * Return a list of chunk positions for a given chunk Id * \param chunkId * \return: if No chunk of chunkId exists,then size of returned * vector will be zero */ std::vector RiffVideo::findChunkPositions(const char* chunkId){ DataBuf chkId((uint64_t)5); chkId.pData_[4] = '\0'; uint64_t i; std::vector positions; for ( i=0; im_riffFileSkeleton.m_primitiveChunks.size(); i++){ io_->seek(d->m_riffFileSkeleton.m_primitiveChunks[i]->m_chunkLocation,BasicIo::beg); io_->read(chkId.pData_,4); if(UtilsVideo::compareTagValue(chkId,chunkId)) positions.push_back((int32_t)io_->tell()); } return positions; } //Same as above but returns only header locations std::vector RiffVideo::findHeaderPositions(const char* headerId){ DataBuf hdrId((uint64_t)5); hdrId.pData_[4] = '\0'; uint64_t i; std::vector positions; for (i=0;im_riffFileSkeleton.m_headerChunks.size();i++){ io_->seek((d->m_riffFileSkeleton.m_headerChunks[i]->m_headerLocation + 4),BasicIo::beg); io_->read(hdrId.pData_,4); if(UtilsVideo::compareTagValue(hdrId,headerId)) positions.push_back((int32_t)io_->tell() - 8); } return positions; } void RiffVideo::writeMetadata(){ if (io_->open() != 0) throw Error(9, io_->path(), strError()); IoCloser closer(*io_); doWriteMetadata(); io_->close(); return; } // RiffVideo::writeMetadata /*! * \brief RiffVideo::doWriteMetadata: * Search for all the supported ChunkIds in Exiv2 and * write the modified data to the file. */ void RiffVideo::doWriteMetadata(){ //pre-write checks to make sure that we are writing to provided Riff file only if (!io_->isopen()) throw Error(20); if (!isRiffType(*io_, false)){ if (io_->error() || io_->eof()) throw Error(14); throw Error(3, "RIFF"); } uint64_t bufMinSize = 4; //some initial variable declarations and initialization before traversing through chunks DataBuf chkId(bufMinSize+1); DataBuf chkSize(bufMinSize+1); chkId.pData_[4] = '\0'; chkSize.pData_[4] = '\0'; //skip "RIFF" "chkSize" "AVI" io_->read(chkId.pData_, bufMinSize); io_->read(chkSize.pData_, bufMinSize); io_->read(chkId.pData_, bufMinSize); d->m_decodeMetaData = false; d->m_modifyMetadata = true; //write metadata in order //find and move to avih position int32_t dummyLong = 0; uint64_t i,j,maxCount; std::vector strhChunkPositions = findChunkPositions("STRH"); std::vector primitiveFlags; //Add new tags to the beginning of the container prmitiveFlags and handle conditions to the end of case statements. primitiveFlags.push_back("STRN"); primitiveFlags.push_back("FMT "); primitiveFlags.push_back("STRF"); primitiveFlags.push_back("STRH"); primitiveFlags.push_back("AVIH"); maxCount = (uint64_t) primitiveFlags.size(); for (j=0; j primitivePositions = findChunkPositions(primitiveFlags.back().c_str()); primitiveFlags.pop_back(); for (i=0; i<(uint64_t)primitivePositions.size(); i++){ io_->seek(primitivePositions[i]+4,BasicIo::beg); switch (j){ case 0: aviHeaderTagsHandler(dummyLong); break; case 1: setStreamType(); streamHandler(dummyLong); break; case 2: io_->seek(strhChunkPositions[i]+4,BasicIo::beg); setStreamType(); io_->seek(primitivePositions[i]+4,BasicIo::beg); streamFormatHandler(dummyLong); break; case 3: d->streamType_ = Audio; streamHandler(dummyLong); break; case 4: dateTimeOriginal(dummyLong); break; default:break; } } primitivePositions.clear(); } std::vector headerFlags; //Add new tags to the beginning of the container headerFlags and handle conditions to the end of case statements. headerFlags.push_back("INFO"); headerFlags.push_back("NCDT"); headerFlags.push_back("ODML"); headerFlags.push_back("IDIT"); maxCount = (uint64_t) headerFlags.size(); for (j=0; j headerPositions = findHeaderPositions(headerFlags.back().c_str()); headerFlags.pop_back(); for (i=0; i<(uint64_t)headerPositions.size(); i++){ io_->seek(headerPositions[i],BasicIo::beg); switch (j){ case 0: io_->seek(headerPositions[i]+8,BasicIo::beg); dateTimeOriginal(dummyLong); break; case 1: odmlTagsHandler(); break; case 2: nikonTagsHandler(); break; case 3: infoTagsHandler(); break; //TODO :implement write functionality for header chunks and exif and iptc metadata default: break; } } headerPositions.clear(); } return; }// RiffVideo::doWriteMetadata void RiffVideo::readMetadata(){ if (io_->open() != 0) throw Error(9, io_->path(), strError()); // Ensure that this is the correct image type if (!isRiffType(*io_, false)){ if (io_->error() || io_->eof()) throw Error(14); throw Error(3, "RIFF"); } IoCloser closer(*io_); clearMetadata(); xmpData_["Xmp.video.FileSize"] = (double)io_->size()/(double)1048576; xmpData_["Xmp.video.FileName"] = io_->path(); xmpData_["Xmp.video.MimeType"] = mimeType(); DataBuf chkId((const int32_t)5); DataBuf chkSize((const int32_t)5); DataBuf chkHeader((const int32_t)5); chkId.pData_[4] = '\0'; chkHeader.pData_[4] = '\0'; io_->read(chkId.pData_, 4); xmpData_["Xmp.video.Container"] = chkId.pData_; //skip file size io_->read(chkSize.pData_, 4); io_->read(chkHeader.pData_, 4); xmpData_["Xmp.video.FileType"] = chkHeader.pData_; d->m_decodeMetaData = true; decodeBlock(); return; } // RiffVideo::readMetadata void RiffVideo::decodeBlock(){ DataBuf chkId((const int32_t)5); chkId.pData_[4] = '\0' ; io_->read(chkId.pData_, 4); if(io_->eof()) return; else if(UtilsVideo::compareTagValue(chkId, "LIST") || UtilsVideo::compareTagValue(chkId, "JUNK")){ io_->seek(-4,BasicIo::cur); tagDecoder(); } return; } // RiffVideo::decodeBlock void RiffVideo::tagDecoder(){ DataBuf chkMainId((const int32_t)5); chkMainId.pData_[4] = '\0'; io_->read(chkMainId.pData_, 4); if(io_->eof()) return; if (UtilsVideo::compareTagValue(chkMainId, "LIST")){ DataBuf listSize((const int32_t)5); DataBuf chkHeader((const int32_t)5); listSize.pData_[4] = '\0'; chkHeader.pData_[4] = '\0'; io_->read(listSize.pData_, 4); uint64_t listsize = Exiv2::getULong(listSize.pData_, littleEndian); io_->read(chkHeader.pData_, 4); HeaderChunk* tmpHeaderChunk = new HeaderChunk(); memcpy((Exiv2::byte* )tmpHeaderChunk->m_headerId,(const Exiv2::byte*)chkHeader.pData_,5); int32_t listend = io_->tell() + listsize - 4 ; IoPosition position = PremitiveChunk; while( (position == PremitiveChunk) && (io_->tell() < listend) ){ DataBuf chkId((const int32_t)5); DataBuf chkSize((const int32_t)5); chkId.pData_[4] = '\0'; chkSize.pData_[4] = '\0'; io_->read(chkId.pData_, 4); io_->read(chkSize.pData_, 4); uint64_t size = Exiv2::getULong(chkSize.pData_, littleEndian); const char allPrimitiveFlags[][5]={"JUNK","AVIH","FMT ", "STRH","STRF","STRN","STRD"}; const char allHeaderFlags[][5]={"NCDT","ODML","MOVI","INFO"}; if(UtilsVideo::compareTagValue(chkId,allPrimitiveFlags,(int32_t)(sizeof(allPrimitiveFlags)/5))){ PrimitiveChunk* tmpPremitiveChunk = new PrimitiveChunk(); memcpy((Exiv2::byte* )tmpPremitiveChunk->m_chunkId,(const Exiv2::byte*)chkId.pData_,5); tmpPremitiveChunk->m_chunkLocation = io_->tell() - 8; tmpPremitiveChunk->m_chunkSize = size; d->m_riffFileSkeleton.m_primitiveChunks.push_back(tmpPremitiveChunk); } if(UtilsVideo::compareTagValue(chkHeader,allHeaderFlags,(int32_t)(sizeof(allHeaderFlags)/5))){ tmpHeaderChunk->m_headerLocation = io_->tell() - 16; tmpHeaderChunk->m_headerSize = listsize ; d->m_riffFileSkeleton.m_headerChunks.push_back(tmpHeaderChunk); position = RiffVideo::TraversingChunk; } //to handle AVI file formats if(!d->m_decodeMetaData && UtilsVideo::compareTagValue(chkId,allPrimitiveFlags,(int32_t)(sizeof(allPrimitiveFlags)/5))) io_->seek(size,BasicIo::cur); else if(!d->m_decodeMetaData && UtilsVideo::compareTagValue(chkHeader,allHeaderFlags,(int)(sizeof(allHeaderFlags)/5))) io_->seek((listsize - 12),BasicIo::cur); else if(UtilsVideo::compareTagValue(chkId, "JUNK")) junkHandler(size); //primitive chunks note:compare chkId else if(UtilsVideo::compareTagValue(chkId, "AVIH")) aviHeaderTagsHandler(size); else if(UtilsVideo::compareTagValue(chkId, "STRH")){ //since strh and strf exist sequential stream type can be set once setStreamType(); streamHandler(size); } else if(UtilsVideo::compareTagValue(chkId,"STRF") || UtilsVideo::compareTagValue(chkId, "FMT ")){ if(UtilsVideo::compareTagValue(chkId,"FMT ")) d->streamType_ = Audio; streamFormatHandler(size); } else if(UtilsVideo::compareTagValue(chkId, "STRN")) dateTimeOriginal(size, 1); else if(UtilsVideo::compareTagValue(chkId, "STRD")) streamDataTagHandler(size); //add more else if to support more primitive chunks below if any //TODO:add indx support //recursive call to decode LIST by breaking out of the loop else if(UtilsVideo::compareTagValue(chkId, "LIST")){ position = RiffVideo::TraversingChunk; io_->seek(-8,BasicIo::cur); } //header chunks note:compare chkHeader else if(UtilsVideo::compareTagValue(chkHeader, "INFO")){ io_->seek(-16,BasicIo::cur); infoTagsHandler(); } else if(UtilsVideo::compareTagValue(chkHeader, "NCDT")){ io_->seek(-16,BasicIo::cur); nikonTagsHandler(); } else if(UtilsVideo::compareTagValue(chkHeader, "ODML")){ io_->seek(-16,BasicIo::cur); odmlTagsHandler(); } //only way to exit from readMetadata() else if(UtilsVideo::compareTagValue(chkHeader, "MOVI") || UtilsVideo::compareTagValue(chkMainId, "DATA")) io_->seek(listsize-12,BasicIo::cur); //Add read functionality for custom chunk headers or user created tags here with more (else if) before else. //skip block with unknown chunk ID (primitive chunk) else if(io_->tell() < listend){ position = RiffVideo::TraversingChunk; io_->seek((listend - (io_->tell()+4)) ,BasicIo::cur); } //skip chunk with unknown headerId else io_->seek(io_->tell()+size, BasicIo::cur); if(UtilsVideo::compareTagValue(chkId,allPrimitiveFlags,(int32_t)(sizeof(allPrimitiveFlags)/5)) && (size % 2 != 0)) io_->seek(1,BasicIo::cur); if(UtilsVideo::compareTagValue(chkId,allHeaderFlags,(int32_t)(sizeof(allHeaderFlags)/5)) && (listsize % 2 != 0)) io_->seek(1,BasicIo::cur); } } //AVIX can have JUNK chunk directly inside RIFF chunk else if(UtilsVideo::compareTagValue(chkMainId, "JUNK")){ DataBuf junkSize((const int32_t)5); junkSize.pData_[4] = '\0'; io_->read(junkSize.pData_, 4); uint64_t size = Exiv2::getULong(junkSize.pData_, littleEndian); if(!d->m_decodeMetaData){ HeaderChunk* tmpHeaderChunk = new HeaderChunk(); tmpHeaderChunk->m_headerLocation = io_->tell() - 8; tmpHeaderChunk->m_headerSize = size; d->m_riffFileSkeleton.m_headerChunks.push_back(tmpHeaderChunk); io_->seek(size,BasicIo::cur); } else junkHandler(size); } else if(UtilsVideo::compareTagValue(chkMainId, "IDIT")){ DataBuf dataSize((const int32_t)5); dataSize.pData_[4] = '\0'; io_->read(dataSize.pData_, 4); uint64_t size = Exiv2::getULong(dataSize.pData_, littleEndian); if(!d->m_decodeMetaData){ HeaderChunk* tmpHeaderChunk = new HeaderChunk(); tmpHeaderChunk->m_headerLocation = io_->tell() - 8; tmpHeaderChunk->m_headerSize = size; d->m_riffFileSkeleton.m_headerChunks.push_back(tmpHeaderChunk); io_->seek(size,BasicIo::cur); } else dateTimeOriginal(size); } tagDecoder(); return; } // RiffVideo::tagDecoder void RiffVideo::streamDataTagHandler(int32_t size){ const int32_t bufMinSize = 20000; DataBuf buf(bufMinSize); buf.pData_[4] = '\0'; uint64_t cur_pos = io_->tell(); io_->read(buf.pData_, 8); if(UtilsVideo::compareTagValue(buf, "AVIF")){ if (size - 4 < 0){ #ifndef SUPPRESS_WARNINGS EXV_ERROR << " Exif Tags found in this RIFF file are not of valid size ." << " Entries considered invalid. Not Processed.\n"; #endif } else{ io_->read(buf.pData_, size - 4); IptcData iptcData; XmpData xmpData; DummyTiffHeader tiffHeader(littleEndian); TiffParserWorker::decode(exifData_, iptcData, xmpData, buf.pData_, buf.size_, Tag::root, TiffMapping::findDecoder, &tiffHeader); #ifndef SUPPRESS_WARNINGS if (!iptcData.empty()) EXV_WARNING << "Ignoring IPTC information encoded in the Exif data.\n"; if (!xmpData.empty()) EXV_WARNING << "Ignoring XMP information encoded in the Exif data.\n"; #endif } } // TODO decode CasioData and ZORA Tag io_->seek(cur_pos + size, BasicIo::beg); return; } // RiffVideo::streamDataTagHandler void RiffVideo::dateTimeOriginal(int32_t size, int32_t i){ uint64_t cur_pos = io_->tell(); if(!d->m_modifyMetadata){ io_->seek(-4,BasicIo::cur); DataBuf dataLength(2); dataLength.pData_[1] = '\0'; io_->read(dataLength.pData_,1); uint64_t bufMinSize = (uint64_t)Exiv2::getShort(dataLength.pData_, littleEndian); DataBuf buf((uint64_t)bufMinSize); io_->seek(3,BasicIo::cur); io_->read(buf.pData_, bufMinSize); if(!i) xmpData_["Xmp.video.DateUT"] = buf.pData_; else xmpData_["Xmp.video.StreamName"] = buf.pData_; io_->seek(cur_pos + size, BasicIo::beg); } else{ DataBuf dataLength(2); dataLength.pData_[1] = '\0'; io_->read(dataLength.pData_,1); io_->seek(3,BasicIo::cur); int32_t bufMinSize = (uint64_t)Exiv2::getShort(dataLength.pData_, littleEndian); if(xmpData_["Xmp.video.DateUT"].count() > 0) writeStringData(xmpData_["Xmp.video.DateUT"],bufMinSize); else if(xmpData_["Xmp.video.StreamName"].count() >0) writeStringData(xmpData_["Xmp.video.StreamName"],bufMinSize); io_->seek(cur_pos + size, BasicIo::beg); } return; } // RiffVideo::dateTimeOriginal void RiffVideo::odmlTagsHandler(){ const int32_t bufMinSize = 4; DataBuf buf(bufMinSize + 1); buf.pData_[4] = '\0'; io_->read(buf.pData_,4); if(!d->m_modifyMetadata){ uint64_t size = Exiv2::getULong(buf.pData_, littleEndian); uint64_t size2 = size; uint64_t cur_pos = io_->tell(); io_->read(buf.pData_, 4); size -= 4; while(size > 0){ io_->read(buf.pData_, 4); size -= 4; if(UtilsVideo::compareTagValue(buf,"DMLH")){ io_->read(buf.pData_, 4); size -= 4; io_->read(buf.pData_, 4); size -= 4; xmpData_["Xmp.video.TotalFrameCount"] = Exiv2::getULong(buf.pData_, littleEndian); } } io_->seek(cur_pos + size2, BasicIo::beg); } else{ if(xmpData_["Xmp.video.TotalFrameCount"].count() > 0){ uint64_t tmpSize = Exiv2::getULong(buf.pData_, littleEndian); while(tmpSize > 0){ io_->read(buf.pData_,4); tmpSize -= 4; if(UtilsVideo::compareTagValue(buf,"DMLH")){ io_->seek(4,BasicIo::cur); writeLongData(xmpData_["Xmp.video.TotalFrameCount"]); return; } } } } return; } // RiffVideo::odmlTagsHandler void RiffVideo::skipListData(){ const int32_t bufMinSize = 4; DataBuf buf(bufMinSize+1); buf.pData_[4] = '\0'; io_->seek(-12, BasicIo::cur); io_->read(buf.pData_, 4); uint64_t size = Exiv2::getULong(buf.pData_, littleEndian); uint64_t cur_pos = io_->tell(); io_->seek(cur_pos + size, BasicIo::beg); return; } // RiffVideo::skipListData void RiffVideo::nikonTagsHandler(){ const int32_t bufMinSize = 100; DataBuf buf(bufMinSize), buf2(4+1); buf.pData_[4] = '\0'; io_->read(buf.pData_, 4); int32_t internal_size = 0, tagID = 0, dataSize = 0, tempSize, size = Exiv2::getULong(buf.pData_, littleEndian); tempSize = size; char str[9] = " . . . "; uint64_t internal_pos, cur_pos; internal_pos = cur_pos = io_->tell(); const TagDetails* td; double denominator = 1; io_->read(buf.pData_, 4); tempSize -= 4; while((int32_t)tempSize > 0){ std::memset(buf.pData_, 0x0, buf.size_); io_->read(buf.pData_, 4); io_->read(buf2.pData_, 4); int32_t temp = internal_size = Exiv2::getULong(buf2.pData_, littleEndian); internal_pos = io_->tell(); tempSize -= (internal_size + 8); if(UtilsVideo::compareTagValue(buf, "NCVR")){ while((int32_t)temp > 3){ std::memset(buf.pData_, 0x0, buf.size_); io_->read(buf.pData_, 2); tagID = Exiv2::getULong(buf.pData_, littleEndian); io_->read(buf.pData_, 2); dataSize = Exiv2::getULong(buf.pData_, littleEndian); temp -= (4 + dataSize); if(tagID == 0x0001){ if (dataSize <= 0){ #ifndef SUPPRESS_WARNINGS EXV_ERROR << " Makernotes found in this RIFF file are not of valid size ." << " Entries considered invalid. Not Processed.\n"; #endif } else if(!d->m_modifyMetadata){ io_->read(buf.pData_, dataSize); xmpData_["Xmp.video.MakerNoteType"] = buf.pData_; } else writeStringData(xmpData_["Xmp.video.MakerNoteType"],dataSize); } else if (tagID == 0x0002){ if (dataSize <= 0){ } else if(!d->m_modifyMetadata){ while(dataSize){ std::memset(buf.pData_, 0x0, buf.size_); io_->read(buf.pData_, 1); str[(4 - dataSize) * 2] = (char)(Exiv2::getULong(buf.pData_, littleEndian) + 48); --dataSize; } xmpData_["Xmp.video.MakerNoteVersion"] = str; } //this is necessary block else if(xmpData_["Xmp.video.MakerNoteVersion"].count() > 0){ int32_t originalDataSize = dataSize; std::string makerNoteVersion = xmpData_["Xmp.video.MakerNoteVersion"].toString(); Exiv2::byte* rawMakerNoteVersion = new byte[originalDataSize]; int32_t tmpCounter = 0; int32_t i; for(i=(int32_t)min((int32_t)dataSize,(int32_t)makerNoteVersion.size()); i>=(int32_t)0; i--){ rawMakerNoteVersion[tmpCounter] = (Exiv2::byte) (makerNoteVersion[(4 - i) * 2] - 48); tmpCounter++; } if((int32_t)originalDataSize > (int32_t)makerNoteVersion.size()){ for(i=(int32_t)(makerNoteVersion.size()); iwrite(rawMakerNoteVersion,originalDataSize); delete[] rawMakerNoteVersion; } else io_->seek(dataSize,BasicIo::cur); } } } else if(UtilsVideo::compareTagValue(buf, "NCTG")){ if(!d->m_modifyMetadata){ while((int32_t)temp > 3){ std::memset(buf.pData_, 0x0, buf.size_); io_->read(buf.pData_, 2); tagID = Exiv2::getULong(buf.pData_, littleEndian); io_->read(buf.pData_, 2); dataSize = Exiv2::getULong(buf.pData_, littleEndian); temp -= (4 + dataSize); td = find(nikonAVITags , tagID); if (dataSize <= 0){ } else{ switch (tagID) { case 0x0003: case 0x0004: case 0x0005: case 0x0006: case 0x0013: case 0x0014: case 0x0018: case 0x001d: case 0x001e: case 0x001f: case 0x0020: io_->read(buf.pData_, dataSize); xmpData_[exvGettext(td->label_)] = buf.pData_; break; case 0x0007: case 0x0010: case 0x0011: case 0x000c: case 0x0012: io_->read(buf.pData_, dataSize); xmpData_[exvGettext(td->label_)] = Exiv2::getULong(buf.pData_, littleEndian); break; case 0x0008: case 0x0009: case 0x000a: case 0x000b: case 0x000f: case 0x001b: case 0x0016: io_->read(buf.pData_, dataSize); buf2.pData_[0] = buf.pData_[4]; buf2.pData_[1] = buf.pData_[5]; buf2.pData_[2] = buf.pData_[6]; buf2.pData_[3] = buf.pData_[7]; denominator = (double)Exiv2::getLong(buf2.pData_, littleEndian); if (denominator != 0) xmpData_[exvGettext(td->label_)] = (double)Exiv2::getLong(buf.pData_, littleEndian) / denominator; else xmpData_[exvGettext(td->label_)] = 0; break; default: io_->read(buf.pData_, dataSize); break; } } } } else{ while((int32_t)temp > 3){ std::memset(buf.pData_, 0x0, buf.size_); io_->read(buf.pData_, 2); tagID = Exiv2::getULong(buf.pData_, littleEndian); io_->read(buf.pData_, 2); dataSize = Exiv2::getULong(buf.pData_, littleEndian); temp -= (4 + dataSize); td = find(nikonAVITags , tagID); if (dataSize <= 0){ } else{ switch (tagID){ case 0x0003: case 0x0004: case 0x0005: case 0x0006: case 0x0013: case 0x0014: case 0x0018: case 0x001d: case 0x001e: case 0x001f: case 0x0020: writeStringData(xmpData_[exvGettext(td->label_)],dataSize); break; case 0x0007: case 0x0010: case 0x0011: case 0x000c: case 0x0012: writeLongData(xmpData_[exvGettext(td->label_)],dataSize); break; case 0x0008: case 0x0009: case 0x000a: case 0x000b: case 0x000f: case 0x001b: case 0x0016: io_->read(buf.pData_, dataSize); buf2.pData_[0] = buf.pData_[4]; buf2.pData_[1] = buf.pData_[5]; buf2.pData_[2] = buf.pData_[6]; buf2.pData_[3] = buf.pData_[7]; denominator = (double)Exiv2::getLong(buf2.pData_, littleEndian); //Variation if(xmpData_[exvGettext(td->label_)].count() > 0){ io_->seek(-dataSize,BasicIo::cur); int32_t nikonData = (int32_t)(xmpData_[exvGettext(td->label_)].toFloat()*denominator); io_->write((Exiv2::byte*)&nikonData,4); io_->seek((dataSize-4),BasicIo::cur); } else io_->seek(dataSize,BasicIo::cur); break; default: io_->read(buf.pData_, dataSize); break; } } } } } else if(UtilsVideo::compareTagValue(buf, "NCTH")){//TODO Nikon Thumbnail Image } else if(UtilsVideo::compareTagValue(buf, "NCVW")){//TODO Nikon Preview Image } io_->seek(internal_pos + internal_size, BasicIo::beg); } if (size ==0) io_->seek(cur_pos + 4, BasicIo::beg); else io_->seek(cur_pos + size, BasicIo::beg); return; } // RiffVideo::nikonTagsHandler void RiffVideo::infoTagsHandler(){ const int32_t bufMinSize = 4; DataBuf buf(bufMinSize + 1); buf.pData_[4] = '\0'; const int32_t cur_pos = io_->tell(); io_->read(buf.pData_, 4); int32_t size = Exiv2::getULong(buf.pData_, littleEndian); io_->seek(4,BasicIo::cur); int32_t dataLenght = size; const TagVocabulary* tv; int32_t infoSize; if(!d->m_modifyMetadata){ const char allPrimitiveFlags[][5]={"GENR","DIRC","IART","ICNM","IEDT","IENG","ILNG", "IMUS","INAM","IPRO","ISRC","ISTR","LANG","LOCA", "TAPE","YEAR","IPRD","IKEY","ICOP","ICNT","CMNT"}; while(dataLenght > 12){ io_->read(buf.pData_, 4); if(UtilsVideo::compareTagValue(buf,allPrimitiveFlags,(int32_t)(sizeof(allPrimitiveFlags)/5))){ PrimitiveChunk* tmpPremitiveChunk = new PrimitiveChunk(); memcpy((Exiv2::byte*)tmpPremitiveChunk->m_chunkId,(const Exiv2::byte*)buf.pData_,5); tmpPremitiveChunk->m_chunkLocation = io_->tell() - 4; d->m_riffFileSkeleton.m_primitiveChunks.push_back(tmpPremitiveChunk); } tv = find(infoTags , Exiv2::toString(buf.pData_)); io_->read(buf.pData_, 4); infoSize = Exiv2::getULong(buf.pData_, littleEndian); dataLenght -= infoSize; if(tv && infoSize > 4){ DataBuf tmpBuf((uint64_t)infoSize+1); tmpBuf.pData_[infoSize] = '\0'; io_->read(tmpBuf.pData_,infoSize); xmpData_[exvGettext(tv->label_)] = tmpBuf.pData_; } } io_->seek(cur_pos + size, BasicIo::beg); } else{ //First write the tags which already exist in a file std::vector existingInfoTags; while(dataLenght > 0){ io_->read(buf.pData_, 4); tv = find(infoTags , Exiv2::toString( buf.pData_)); io_->read(buf.pData_, 4); infoSize = Exiv2::getULong(buf.pData_, littleEndian); dataLenght -= infoSize; if(tv){ if(xmpData_[exvGettext(tv->label_)].count() >0){ existingInfoTags.push_back((std::string)tv->voc_); writeStringData(xmpData_[exvGettext(tv->label_)],infoSize); } } } //Secondly write tags which are supported by Digikam, //Note that create new LIST chunk is created if tag did not exist before std::vector > writethis; for(int32_t i =0; i< (int32_t)(sizeof(infoTags)/sizeof(infoTags[0])); i++){ const std::string infoTagData = xmpData_[infoTags[i].label_].toString(); const std::string infoTagFlag = (std::string)infoTags[i].voc_; if((xmpData_[infoTags[i].label_].count() > 0) && ((findChunkPositions(infoTags[i].voc_)).size() == 0) && !(std::find(existingInfoTags.begin(), existingInfoTags.end(), infoTagFlag) != existingInfoTags.end())) writethis.push_back(make_pair(infoTagData,infoTagFlag)); } if(writethis.size() > 0){ io_->seek(cur_pos, BasicIo::beg); writeNewSubChunks(writethis); } io_->seek(cur_pos + size, BasicIo::beg); } return; } // RiffVideo::infoTagsHandler void RiffVideo::junkHandler(int32_t size){ const int32_t bufMinSize = size; if (size < 0){ #ifndef SUPPRESS_WARNINGS EXV_ERROR << " Junk Data found in this RIFF file are not of valid size ." << " Entries considered invalid. Not Processed.\n"; #endif io_->seek(io_->tell() + 4, BasicIo::beg); } else{ DataBuf buf(bufMinSize+1), buf2(4+1); uint64_t cur_pos = io_->tell(); io_->read(buf.pData_, 4); //! Pentax Metadata and Tags if(UtilsVideo::compareTagValue(buf, "PENT")){ io_->seek(cur_pos + 18, BasicIo::beg); io_->read(buf.pData_, 26); xmpData_["Xmp.video.Make"] = buf.pData_; io_->read(buf.pData_, 50); xmpData_["Xmp.video.Model"] = buf.pData_; std::memset(buf.pData_, 0x0, buf.size_); io_->read(buf.pData_, 8); buf2.pData_[0] = buf.pData_[4]; buf2.pData_[1] = buf.pData_[5]; buf2.pData_[2] = buf.pData_[6]; buf2.pData_[3] = buf.pData_[7]; xmpData_["Xmp.video.FNumber"] = (double)Exiv2::getLong(buf.pData_, littleEndian) / (double)Exiv2::getLong(buf2.pData_, littleEndian);; io_->seek(cur_pos + 131, BasicIo::beg); io_->read(buf.pData_, 26); xmpData_["Xmp.video.DateTimeOriginal"] = buf.pData_; io_->read(buf.pData_, 26); xmpData_["Xmp.video.DateTimeDigitized"] = buf.pData_; io_->seek(cur_pos + 299, BasicIo::beg); std::memset(buf.pData_, 0x0, buf.size_); io_->read(buf.pData_, 2); Exiv2::XmpTextValue tv(Exiv2::toString(Exiv2::getLong(buf.pData_, littleEndian))); xmpData_.add(Exiv2::XmpKey("Xmp.xmp.Thumbnails/xmpGImg:width"), &tv); io_->read(buf.pData_, 2); tv.read(Exiv2::toString(Exiv2::getLong(buf.pData_, littleEndian))); xmpData_.add(Exiv2::XmpKey("Xmp.xmp.Thumbnails/xmpGImg:height"), &tv); io_->read(buf.pData_, 4); /* TODO - Storing the image Thumbnail in Base64 Format uint64_t length = Exiv2::getLong(buf.pData_, littleEndian); io_->read(buf.pData_, length); char *rawStr = Exiv2::toString(buf.pData_); char *encodedStr; SXMPUtils::EncodeToBase64(rawStr, encodedStr); tv.read(Exiv2::toString(encodedStr)); xmpData_.add(Exiv2::XmpKey("Xmp.xmp.Thumbnails/xmpGImg:image"), &tv); */ } else{ io_->seek(cur_pos, BasicIo::beg); io_->read(buf.pData_, size); xmpData_["Xmp.video.Junk"] = buf.pData_; } io_->seek(cur_pos + size, BasicIo::beg); } return; } // RiffVideo::junkHandler void RiffVideo::aviHeaderTagsHandler(int32_t size){ if(!d->m_modifyMetadata){ const int32_t bufMinSize = 4; DataBuf buf(bufMinSize+1); buf.pData_[4] = '\0'; int32_t width = 0, height = 0, frame_count = 0; double frame_rate = 1; uint64_t cur_pos = io_->tell(),i; for( i = 0; i <= 9; i++){ std::memset(buf.pData_, 0x0, buf.size_); io_->read(buf.pData_, bufMinSize); switch(i){ case frameRate: xmpData_["Xmp.video.MicroSecPerFrame"] = Exiv2::getULong(buf.pData_, littleEndian); frame_rate = (double)1000000/(double)Exiv2::getULong(buf.pData_, littleEndian); break; case (maxDataRate): xmpData_["Xmp.video.MaxDataRate"] = (double)Exiv2::getULong(buf.pData_, littleEndian)/(double)1024; break; case frameCount: xmpData_["Xmp.video.FrameCount"] = Exiv2::getULong(buf.pData_, littleEndian); frame_count = Exiv2::getULong(buf.pData_, littleEndian); break; case initialFrames: xmpData_["Xmp.video.InitialFrames"] = Exiv2::getULong(buf.pData_, littleEndian); break; case streamCount: xmpData_["Xmp.video.StreamCount"] = Exiv2::getULong(buf.pData_, littleEndian); break; case suggestedBufferSize: xmpData_["Xmp.video.SuggestedBufferSize"] = Exiv2::getULong(buf.pData_, littleEndian); break; case imageWidth_h: width = Exiv2::getULong(buf.pData_, littleEndian); xmpData_["Xmp.video.Width"] = width; break; case imageHeight_h: height = Exiv2::getULong(buf.pData_, littleEndian); xmpData_["Xmp.video.Height"] = height; break; } } fillAspectRatio(width, height); fillDuration(frame_rate, frame_count); io_->seek(cur_pos + size, BasicIo::beg); } else//overwrite metadata from current xmpData_ container{ for(int32_t i = 0; i <= 9; i++){ switch(i){ case frameRate: writeLongData(xmpData_["Xmp.video.MicroSecPerFrame"]); break; case (maxDataRate): if(xmpData_["Xmp.video.MaxDataRate"].count() > 0 ){ int32_t maxDataRate = (int32_t)(xmpData_["Xmp.video.MaxDataRate"].toFloat()*1024); io_->write((Exiv2::byte*)&maxDataRate,4); } else io_->seek(4,BasicIo::cur); break; case frameCount: writeLongData(xmpData_["Xmp.video.FrameCount"]); break; case initialFrames: writeLongData(xmpData_["Xmp.video.InitialFrames"]); break; case streamCount: writeLongData(xmpData_["Xmp.video.StreamCount"]); break; case suggestedBufferSize: writeLongData(xmpData_["Xmp.video.SuggestedBufferSize"]); break; case imageWidth_h: writeLongData(xmpData_["Xmp.video.Width"]); break; case imageHeight_h: writeLongData(xmpData_["Xmp.video.Height"]); break; default: io_->seek(4,BasicIo::cur); break; } } return; } // RiffVideo::aviHeaderTagsHandler void RiffVideo::setStreamType(){ DataBuf chkId((uint64_t)5); io_->read(chkId.pData_,(uint64_t)4); if(UtilsVideo::compareTagValue(chkId, "VIDS")) d->streamType_ = Video; else if (UtilsVideo::compareTagValue(chkId, "AUDS")) d->streamType_ = Audio; return; }//RiffVideo::setStreamType void RiffVideo::streamHandler(int32_t size){ if (!d->m_modifyMetadata){ uint64_t bufMinSize = 4; DataBuf buf(bufMinSize+1); buf.pData_[4] = '\0'; int32_t divisor = 1; uint64_t cur_pos = io_->tell() - 4; int32_t i; for( i=1; i<=25; i++){ std::memset(buf.pData_, 0x0, buf.size_); io_->read(buf.pData_, bufMinSize); switch(i){ case codec: if(d->streamType_ == Video) xmpData_["Xmp.video.Codec"] = buf.pData_; else if (d->streamType_ == Audio) xmpData_["Xmp.audio.Codec"] = buf.pData_; else xmpData_["Xmp.video.Codec"] = buf.pData_; break; case sampleRate: divisor=Exiv2::getULong(buf.pData_, littleEndian); break; case (sampleRate+1): if(d->streamType_ == Video) xmpData_["Xmp.video.FrameRate"] = returnSampleRate(buf,divisor); else if (d->streamType_ == Audio) xmpData_["Xmp.audio.SampleRate"] = returnSampleRate(buf,divisor); else xmpData_["Xmp.video.StreamSampleRate"] = returnSampleRate(buf,divisor); break; case sampleCount: if(d->streamType_ == Video) xmpData_["Xmp.video.FrameCount"] = Exiv2::getULong(buf.pData_, littleEndian); else if (d->streamType_ == Audio) xmpData_["Xmp.audio.SampleCount"] = Exiv2::getULong(buf.pData_, littleEndian); else xmpData_["Xmp.video.StreamSampleCount"] = Exiv2::getULong(buf.pData_, littleEndian); break; case quality: if(d->streamType_ == Video) xmpData_["Xmp.video.VideoQuality"] = Exiv2::getULong(buf.pData_, littleEndian); else if(d->streamType_ != Audio) xmpData_["Xmp.video.StreamQuality"] = Exiv2::getULong(buf.pData_, littleEndian); break; case sampleSize: if(d->streamType_ == Video) xmpData_["Xmp.video.VideoSampleSize"] = Exiv2::getULong(buf.pData_, littleEndian); else if(d->streamType_ != Audio) xmpData_["Xmp.video.StreamSampleSize"] = Exiv2::getULong(buf.pData_, littleEndian); break; } } io_->seek(cur_pos + size, BasicIo::beg); } else{ DataBuf chkId((uint64_t)5); chkId.pData_[4] = '\0'; const int32_t bufMinSize = 4; int32_t multiplier; if(d->streamType_ == Video){ writeStringData(xmpData_["Xmp.video.Codec"],4,12); io_->read(chkId.pData_,bufMinSize); multiplier = Exiv2::getULong(chkId.pData_, littleEndian); //Variation if(xmpData_["Xmp.video.FrameRate"].count() > 0){ int32_t frameRate = (int32_t)xmpData_["Xmp.video.FrameRate"].toFloat()*multiplier; io_->write((Exiv2::byte*)&frameRate,4); io_->seek(4,BasicIo::cur); } else io_->seek(8,BasicIo::cur); writeLongData(xmpData_["Xmp.video.FrameCount"],4,4); writeLongData(xmpData_["Xmp.video.VideoQuality"],4); writeLongData(xmpData_["Xmp.video.VideoSampleSize"],4); } else if(d->streamType_ == Audio){ writeStringData(xmpData_["Xmp.audio.Codec"],4,12); io_->read(chkId.pData_,bufMinSize); multiplier = Exiv2::getULong(chkId.pData_, littleEndian); //Variation if(xmpData_["Xmp.audio.SampleRate"].count() > 0){ int32_t sampleRate = (int32_t)xmpData_["Xmp.audio.SampleRate"].toLong()*multiplier; io_->write((Exiv2::byte*)&sampleRate,4); io_->seek(4,BasicIo::cur); } else io_->seek(8,BasicIo::cur); writeLongData(xmpData_["Xmp.audio.SampleCount"],4); } else{ writeLongData(xmpData_["Xmp.video.Codec"],4,12); io_->read(chkId.pData_,bufMinSize); multiplier = Exiv2::getULong(chkId.pData_, littleEndian); //Variation if(xmpData_["Xmp.video.StreamSampleRate"].count() > 0){ int32_t streamSampleRate = (int32_t)xmpData_["Xmp.video.StreamSampleRate"].toLong()*multiplier; io_->write((Exiv2::byte*)&streamSampleRate,4); io_->seek(4,BasicIo::cur); } else io_->seek(8,BasicIo::cur); writeLongData(xmpData_["Xmp.video.StreamSampleCount"],4,4); writeLongData(xmpData_["Xmp.video.StreamQuality"],4); writeLongData(xmpData_["Xmp.video.StreamSampleSize"],4); } } return; } // RiffVideo::streamHandler void RiffVideo::streamFormatHandler(int32_t size){ if (!d->m_modifyMetadata){ const int32_t bufMinSize = 4; DataBuf buf(bufMinSize+1); buf.pData_[4] = '\0'; uint64_t cur_pos = io_->tell(); if(d->streamType_ == Video){ io_->read(buf.pData_, bufMinSize); for(int32_t i = 0; i <= 9; i++){ std::memset(buf.pData_, 0x0, buf.size_); switch(i){ //Will be used in case of debugging case imageWidth: io_->read(buf.pData_, bufMinSize); break; case imageHeight: io_->read(buf.pData_, bufMinSize); break; case planes: io_->read(buf.pData_, 2); xmpData_["Xmp.video.Planes"] = Exiv2::getUShort(buf.pData_, littleEndian); break; case bitDepth: io_->read(buf.pData_, 2); xmpData_["Xmp.video.PixelDepth"] = Exiv2::getUShort(buf.pData_, littleEndian); break; case compression: io_->read(buf.pData_, bufMinSize); xmpData_["Xmp.video.Compressor"] = buf.pData_; break; case imageLength: io_->read(buf.pData_, bufMinSize); xmpData_["Xmp.video.ImageLength"] = Exiv2::getULong(buf.pData_, littleEndian); break; case pixelsPerMeterX: io_->read(buf.pData_, bufMinSize); xmpData_["Xmp.video.PixelPerMeterX"] = Exiv2::getULong(buf.pData_, littleEndian); break; case pixelsPerMeterY: io_->read(buf.pData_, bufMinSize); xmpData_["Xmp.video.PixelPerMeterY"] = Exiv2::getULong(buf.pData_, littleEndian); break; case numColors: io_->read(buf.pData_, bufMinSize); if(Exiv2::getULong(buf.pData_, littleEndian) == 0) xmpData_["Xmp.video.NumOfColours"] = "Unspecified"; else xmpData_["Xmp.video.NumOfColours"] = Exiv2::getULong(buf.pData_, littleEndian); break; case numImportantColors: io_->read(buf.pData_, bufMinSize); if(Exiv2::getULong(buf.pData_, littleEndian)) xmpData_["Xmp.video.NumIfImpColours"] = Exiv2::getULong(buf.pData_, littleEndian); else xmpData_["Xmp.video.NumOfImpColours"] = "All"; break; } } } else if(d->streamType_ == Audio){ int32_t c = 0; const TagDetails* td; for(int32_t i = 0; i <= 7; i++){ io_->read(buf.pData_, 2); switch(i){ case encoding: td = find(audioEncodingValues , Exiv2::getUShort(buf.pData_, littleEndian)); if(td) xmpData_["Xmp.audio.Compressor"] = exvGettext(td->label_); else xmpData_["Xmp.audio.Compressor"] = Exiv2::getUShort(buf.pData_, littleEndian); break; case numberOfChannels: c = Exiv2::getUShort(buf.pData_, littleEndian); if(c == 1) xmpData_["Xmp.audio.ChannelType"] = "Mono"; else if(c == 2) xmpData_["Xmp.audio.ChannelType"] = "Stereo"; else if(c == 5) xmpData_["Xmp.audio.ChannelType"] = "5.1 Surround Sound"; else if(c == 7) xmpData_["Xmp.audio.ChannelType"] = "7.1 Surround Sound"; else xmpData_["Xmp.audio.ChannelType"] = "Mono"; break; case audioSampleRate: xmpData_["Xmp.audio.SampleRate"] = Exiv2::getUShort(buf.pData_, littleEndian); break; case avgBytesPerSec: xmpData_["Xmp.audio.SampleType"] = Exiv2::getUShort(buf.pData_, littleEndian); break; case bitsPerSample: xmpData_["Xmp.audio.BitsPerSample"] = Exiv2::getUShort(buf.pData_,littleEndian); io_->read(buf.pData_, 2); break; } } } io_->seek(cur_pos + size, BasicIo::beg); } else{ if(d->streamType_ == Video){ DataBuf chkId((uint64_t) 5); chkId.pData_[4] = '\0'; io_->read(chkId.pData_,4); writeLongData(xmpData_["Xmp.video.Width"],4); writeLongData(xmpData_["Xmp.video.Height"],4); writeShortData(xmpData_["Xmp.video.Planes"]); writeShortData(xmpData_["Xmp.video.PixelDepth"]); writeStringData(xmpData_["Xmp.video.Compressor"],4); writeLongData(xmpData_["Xmp.video.ImageLength"],4); writeLongData(xmpData_["Xmp.video.PixelPerMeterX"],4); writeLongData(xmpData_["Xmp.video.PixelPerMeterY"],4); writeLongData(xmpData_["Xmp.video.NumOfColours"],4); //Variation if(xmpData_["Xmp.video.NumOfImpColours"].count() > 0){ if(std::strcmp(xmpData_["Xmp.video.NumOfImpColours"].toString().c_str(),"All")){ const int32_t numImportantColours = (int32_t) 0; io_->write((Exiv2::byte*)&numImportantColours,4); } else writeLongData(xmpData_["Xmp.video.NumOfImpColours"],4); } else io_->seek(4,BasicIo::cur); } else if(d->streamType_ == Audio){ const RevTagDetails* rtd; //Variation if(xmpData_["Xmp.audio.Compressor"].count() > 0){ RevTagDetails revTagDetails[(sizeof(audioEncodingValues)/ sizeof(audioEncodingValues[0]))]; reverseTagDetails(audioEncodingValues,revTagDetails, (sizeof(audioEncodingValues) /sizeof(audioEncodingValues[0]))); rtd = find(revTagDetails,xmpData_["Xmp.audio.Compressor"].toString()); if(rtd){ uint16_t compressorCode= (uint16_t)rtd->val_; io_->write((Exiv2::byte*)&compressorCode,2); } else io_->seek(2,BasicIo::cur); } else io_->seek(2,BasicIo::cur); if(xmpData_["Xmp.audio.ChannelType"].count() > 0){ const int32_t sizeChan = xmpData_["Xmp.audio.ChannelType"].size(); DataBuf tmpBuf(sizeChan+1); tmpBuf.pData_[sizeChan+1] = '\0'; const std::string audioChannealType = xmpData_["Xmp.audio.ChannelType"].toString(); int32_t i; for( i=0; i<=sizeChan;i++) tmpBuf.pData_[i] = (Exiv2::byte)audioChannealType[i]; uint16_t channelType = 0; if(UtilsVideo::simpleBytesComparision(tmpBuf,"MONO",sizeChan)) channelType = 1; else if(UtilsVideo::simpleBytesComparision(tmpBuf,"STEREO",sizeChan)) channelType = 2; else if(UtilsVideo::simpleBytesComparision(tmpBuf,"5.1 SURROUND SOUND",sizeChan)) channelType = 5; else if(UtilsVideo::simpleBytesComparision(tmpBuf,"7.1 SURROUND SOUND",sizeChan)) channelType = 7; io_->write((Exiv2::byte*)&channelType,2); } else io_->seek(2,BasicIo::cur); writeShortData(xmpData_["Xmp.audio.SampleRate"],2,2); writeShortData(xmpData_["Xmp.audio.SampleType"],2,4); writeShortData(xmpData_["Xmp.audio.BitsPerSample"],2); } } return; } // RiffVideo::streamFormatHandler double RiffVideo::returnSampleRate(Exiv2::DataBuf& buf, int32_t divisor){ return ((double)Exiv2::getULong(buf.pData_, littleEndian) / (double)divisor); } // RiffVideo::returnSampleRate const char* RiffVideo::printAudioEncoding(uint64_t i){ const TagDetails* td; td = find(audioEncodingValues , i); if(td) return exvGettext(td->label_); return "Undefined"; } // RiffVideo::printAudioEncoding void RiffVideo::fillAspectRatio(int32_t width, int32_t height){ double aspectRatio = (double)width / (double)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; } return; } // RiffVideo::fillAspectRatio void RiffVideo::fillDuration(double frame_rate, int32_t frame_count){ if(frame_rate == 0) return; uint64_t duration = static_cast((double)frame_count * (double)1000 / (double)frame_rate); xmpData_["Xmp.video.FileDataRate"] = (double)io_->size()/(double)(1048576*duration); xmpData_["Xmp.video.Duration"] = duration; //Duration in number of seconds return; } // RiffVideo::fillDuration void RiffVideo::reverseTagDetails(const TagDetails inputTagVocabulary[], RevTagDetails outputTagVocabulary[] ,int32_t size){ for(int32_t i=0; igood()) image.reset(); return image; } /*! * \brief RiffVideo::copyRestOfTheFile * Move all data to next locations,Currently its blockwise copy,which is inefficient for a * huge video files,and should be changed to inserting technique without moving huge data * like Bundle technology as suggested by Robin. * \param oldSavedData * \return */ bool RiffVideo::copyRestOfTheFile(DataBuf oldSavedData){ int32_t writeSize = oldSavedData.size_; int32_t readSize = writeSize; DataBuf readBuf((int32_t)max(readSize,(int32_t)1024)); DataBuf writeBuf((int32_t)max(readSize,(int32_t)1024)); memcpy(writeBuf.pData_,oldSavedData.pData_,readSize); bool lastStream = false,hasData = true; while(hasData){ if((io_->size() - io_->tell()) < 1024){ hasData = false; lastStream = true; readSize = (io_->size() - io_->tell()); } io_->read(readBuf.pData_,readSize); io_->seek(-readSize,BasicIo::cur); io_->write(writeBuf.pData_,writeSize); memcpy(writeBuf.pData_,readBuf.pData_,readSize); writeSize = readSize; if(lastStream) io_->write(writeBuf.pData_,readSize); } return true; } /*! * \brief RiffVideo::writeNewSubChunks * precondition:INFO List chunk must exist,atleast with LIST tag, * and size field with size one(INFO tag which appear next) * * |----LIST(Must exist before calling this method) * |----Size(Must exist, atleast with size 1 if no INFO subchunks exist before) * |----INFO (Must exist,exactly with same HeaderChunkId INFO) * * \param chunkData * \return */ bool RiffVideo::writeNewSubChunks(std::vector > chunkData){ bool result = true; int32_t addSize = 0; for (vector >::iterator it = chunkData.begin(); it != chunkData.end(); it++){ std::pair unitChunk = (*it); addSize += (int32_t)unitChunk.first.size() + (int32_t)8; addSize += addSize%2; } const int32_t cur_pos = io_->tell(); DataBuf buf((uint64_t)5); buf.pData_[4] = '\0'; io_->seek(4,BasicIo::beg); io_->read(buf.pData_,4); io_->seek(-4,BasicIo::cur); const int32_t originalSize = Exiv2::getULong(buf.pData_, littleEndian); int32_t newRiffSize = originalSize + addSize ; io_->write((Exiv2::byte*)&newRiffSize,4); io_->seek(cur_pos,BasicIo::beg); io_->read(buf.pData_,4); io_->seek(-4,BasicIo::cur); const int32_t originalListSize = Exiv2::getULong(buf.pData_, littleEndian); int32_t newListSize = originalListSize + addSize ; io_->write((Exiv2::byte*)&newListSize,4); io_->read(buf.pData_,4); std::vector previousData; uint64_t i; for (vector >::iterator it = chunkData.begin(); it != chunkData.end(); it++){ std::pair unitChunk = (*it); std::string chunkId = unitChunk.second; std::string tmpChunkData = unitChunk.first; uint64_t chunkSize = tmpChunkData.size(); chunkSize += chunkSize%2; DataBuf copiedData((int32_t)(chunkSize+8)); io_->read(copiedData.pData_,(chunkSize+8)); io_->seek(((-1)*(chunkSize+8)),BasicIo::cur); previousData.push_back(copiedData); Exiv2::byte rawChunkId[4] = {(Exiv2::byte)chunkId[0],(Exiv2::byte)chunkId[1], (Exiv2::byte)chunkId[2],(Exiv2::byte)chunkId[3]}; Exiv2::byte* rawChunkData = new byte[(int32_t)chunkSize]; for(i=0; i < chunkSize; i++) rawChunkData[i] = (Exiv2::byte)tmpChunkData[i]; if(tmpChunkData.size()%2 != 0) rawChunkData[(int32_t)(chunkSize-1)] = (Exiv2::byte)0; io_->write(rawChunkId,4); io_->write((Exiv2::byte*)&chunkSize,4); io_->write(rawChunkData,chunkSize); delete[] rawChunkData; } int32_t accessIndex = 0; int32_t netNewMetadataSize = 0; for (i=0; i < (int32_t)previousData.size(); i++) netNewMetadataSize += previousData[i].size_; DataBuf metadataPacket((uint64_t)netNewMetadataSize); for (i=0; i < previousData.size(); i++){ for(int32_t assignIndex=0; assignIndex 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 newSize) for(int32_t i=newSize; iwrite(rawData,size); io_->seek((int)skipOffset,BasicIo::cur); delete[] rawData; } else io_->seek((size+skipOffset),BasicIo::cur); } void RiffVideo::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 RiffVideo::writeFloatData(Exiv2::Xmpdatum xmpIntData, int32_t size, int32_t skipOffset){ if(xmpIntData.count() > 0){ int32_t rawIntData = (int32_t)xmpIntData.toFloat(); io_->write((Exiv2::byte*)&rawIntData,size); io_->seek(skipOffset,BasicIo::cur); } else io_->seek((size+skipOffset),BasicIo::cur); } void RiffVideo::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); } bool isRiffType(BasicIo& iIo, bool advance){ const int32_t len = 2; const unsigned char RiffVideoId[4] = { 'R', 'I', 'F' ,'F'}; Exiv2::byte* buf = new byte[len]; iIo.read(buf, len); if (iIo.error() || iIo.eof()){ delete[] buf; return false; } bool matched = (memcmp(buf, RiffVideoId, len) == 0); if (!advance || !matched) iIo.seek(-len, BasicIo::cur); delete[] buf; return matched; } } // namespace Exiv2