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.
763 lines
34 KiB
C++
763 lines
34 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: matroskavideo.cpp
|
|
Version: $Rev$
|
|
Author(s): Abhinav Badola for GSoC 2012 (AB) <mail.abu.to@gmail.com>
|
|
History: 18-Jun-12, AB: created
|
|
Credits: See header file
|
|
*/
|
|
// *****************************************************************************
|
|
#include "rcsid_int.hpp"
|
|
EXIV2_RCSID("@(#) $Id$")
|
|
|
|
// *****************************************************************************
|
|
// included header files
|
|
#include "matroskavideo.hpp"
|
|
#include "futils.hpp"
|
|
#include "basicio.hpp"
|
|
#include "tags.hpp"
|
|
#include "tags_int.hpp"
|
|
|
|
// + standard includes
|
|
#include <cmath>
|
|
#include <cassert>
|
|
|
|
// *****************************************************************************
|
|
// class member definitions
|
|
namespace Exiv2 {
|
|
namespace Internal {
|
|
|
|
//! List of composite tags. They are skipped and the child tags are read immediately
|
|
uint64_t compositeTagsList[] = {
|
|
0x0000, 0x000e, 0x000f, 0x0020, 0x0026, 0x002e, 0x0036,
|
|
0x0037, 0x003b, 0x005b, 0x0060, 0x0061, 0x0068, 0x05b9,
|
|
0x0dbb, 0x1034, 0x1035, 0x1854, 0x21a7, 0x2240, 0x23c0,
|
|
0x2624, 0x27c8, 0x2911, 0x2924, 0x2944, 0x2d80, 0x3373,
|
|
0x35a1, 0x3e5b, 0x3e7b,
|
|
0x14d9b74, 0x254c367, 0x549a966, 0x654ae6b, 0x8538067,
|
|
0x941a469, 0xa45dfa3, 0xb538667, 0xc53bb6b, 0xf43b675
|
|
};
|
|
|
|
//! List of tags which are ignored, i.e., tag and value won't be read
|
|
uint64_t ignoredTagsList[] = {
|
|
0x0021, 0x0023, 0x0033, 0x0071, 0x0077, 0x006c, 0x0067, 0x007b, 0x02f2, 0x02f3,
|
|
0x1031, 0x1032, 0x13ab, 0x13ac, 0x15ee, 0x23a2, 0x23c6, 0x2e67, 0x33a4, 0x33c5,
|
|
0x3446, 0x2de7, 0x2df8, 0x26bf, 0x28ca, 0x3384, 0x13b8, 0x037e, 0x0485, 0x18d7,
|
|
0x0005, 0x0009, 0x0011, 0x0012, 0x0016, 0x0017, 0x0018, 0x0022, 0x0024, 0x0025,
|
|
0x0027, 0x002b, 0x002f, 0x003f, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x006a,
|
|
0x006b, 0x006e, 0x007a, 0x007d, 0x0255, 0x3eb5, 0x3ea5, 0x3d7b, 0x33c4, 0x2fab,
|
|
0x2ebc, 0x29fc, 0x29a5, 0x2955, 0x2933, 0x135f, 0x2922, 0x26a5, 0x26fc, 0x2532,
|
|
0x23c9, 0x23c4, 0x23c5, 0x137f, 0x1378, 0x07e2, 0x07e3, 0x07e4, 0x0675, 0x05bc,
|
|
0x05bd, 0x05db, 0x05dd, 0x0598, 0x050d, 0x0444, 0x037c,
|
|
|
|
0x3314f, 0x43a770, 0x1eb923, 0x1cb923, 0xeb524, 0x1c83ab, 0x1e83bb
|
|
};
|
|
|
|
/*!
|
|
Tag Look-up list for Matroska Type Video Files
|
|
The Tags have been categorized in 4 categories. Which are
|
|
mentioned as a comment in front of them.
|
|
s -- Tag to be Skipped
|
|
sd -- Tag to be Skipped along with its data
|
|
u -- Tag used directly for storing metadata
|
|
ui -- Tag used only internally
|
|
*/
|
|
extern const MatroskaTags matroskaTags[] = {
|
|
{ 0x0000, "ChapterDisplay" }, //s
|
|
{ 0x0003, "TrackType" }, //ui
|
|
{ 0x0005, "ChapterString" }, //sd
|
|
{ 0x0006, "VideoCodecID/AudioCodecID/CodecID" }, //ui
|
|
{ 0x0008, "TrackDefault" }, //ui
|
|
{ 0x0009, "ChapterTrackNumber" }, //sd
|
|
{ 0x000e, "Slices" }, //s
|
|
{ 0x000f, "ChapterTrack" }, //s
|
|
{ 0x0011, "ChapterTimeStart" }, //sd
|
|
{ 0x0012, "ChapterTimeEnd" }, //sd
|
|
{ 0x0016, "CueRefTime" }, //sd
|
|
{ 0x0017, "CueRefCluster" }, //sd
|
|
{ 0x0018, "ChapterFlagHidden" }, //sd
|
|
{ 0x001a, "Xmp.video.VideoScanTpye" }, //u
|
|
{ 0x001b, "BlockDuration" }, //s
|
|
{ 0x001c, "TrackLacing" }, //ui
|
|
{ 0x001f, "Xmp.audio.ChannelType" }, //u
|
|
{ 0x0020, "BlockGroup" }, //s
|
|
{ 0x0021, "Block" }, //sd
|
|
{ 0x0022, "BlockVirtual" }, //sd
|
|
{ 0x0023, "SimpleBlock" }, //sd
|
|
{ 0x0024, "CodecState" }, //sd
|
|
{ 0x0025, "BlockAdditional" }, //sd
|
|
{ 0x0026, "BlockMore" }, //s
|
|
{ 0x0027, "Position" }, //sd
|
|
{ 0x002a, "CodecDecodeAll" }, //ui
|
|
{ 0x002b, "PrevSize" }, //sd
|
|
{ 0x002e, "TrackEntry" }, //s
|
|
{ 0x002f, "EncryptedBlock" }, //sd
|
|
{ 0x0030, "Xmp.video.Width" }, //u
|
|
{ 0x0033, "CueTime" }, //sd
|
|
{ 0x0035, "Xmp.audio.SampleRate" }, //u
|
|
{ 0x0036, "ChapterAtom" }, //s
|
|
{ 0x0037, "CueTrackPositions" }, //s
|
|
{ 0x0039, "TrackUsed" }, //ui
|
|
{ 0x003a, "Xmp.video.Height" }, //u
|
|
{ 0x003b, "CuePoint" }, //s
|
|
{ 0x003f, "CRC-32" }, //sd
|
|
{ 0x004b, "BlockAdditionalID" }, //sd
|
|
{ 0x004c, "LaceNumber" }, //sd
|
|
{ 0x004d, "FrameNumber" }, //sd
|
|
{ 0x004e, "Delay" }, //sd
|
|
{ 0x004f, "ClusterDuration" }, //sd
|
|
{ 0x0057, "TrackNumber" }, //ui
|
|
{ 0x005b, "CueReference" }, //s
|
|
{ 0x0060, "Video" }, //s
|
|
{ 0x0061, "Audio" }, //s
|
|
{ 0x0067, "Timecode" }, //sd
|
|
{ 0x0068, "TimeSlice" }, //s
|
|
{ 0x006a, "CueCodecState" }, //sd
|
|
{ 0x006b, "CueRefCodecState" }, //sd
|
|
{ 0x006c, "Void" }, //sd
|
|
{ 0x006e, "BlockAddID" }, //sd
|
|
{ 0x0071, "CueClusterPosition" }, //sd
|
|
{ 0x0077, "CueTrack" }, //sd
|
|
{ 0x007a, "ReferencePriority" }, //sd
|
|
{ 0x007b, "ReferenceBlock" }, //sd
|
|
{ 0x007d, "ReferenceVirtual" }, //sd
|
|
{ 0x0254, "Xmp.video.ContentCompressAlgo" }, //u
|
|
{ 0x0255, "ContentCompressionSettings" }, //sd
|
|
{ 0x0282, "Xmp.video.DocType" }, //u
|
|
{ 0x0285, "Xmp.video.DocTypeReadVersion" }, //u
|
|
{ 0x0286, "Xmp.video.EBMLVersion" }, //u
|
|
{ 0x0287, "Xmp.video.DocTypeVersion" }, //u
|
|
{ 0x02f2, "EBMLMaxIDLength" }, //sd
|
|
{ 0x02f3, "EBMLMaxSizeLength" }, //sd
|
|
{ 0x02f7, "Xmp.video.EBMLReadVersion" }, //u
|
|
{ 0x037c, "ChapterLanguage" }, //sd
|
|
{ 0x037e, "ChapterCountry" }, //sd
|
|
{ 0x0444, "SegmentFamily" }, //sd
|
|
{ 0x0461, "Xmp.video.DateUTC" }, //Date Time Original - measured in seconds relatively to Jan 01, 2001, 0:00:00 GMT+0h
|
|
{ 0x047a, "Xmp.video.TagLanguage" }, //u
|
|
{ 0x0484, "Xmp.video.TagDefault" }, //u
|
|
{ 0x0485, "TagBinary" }, //sd
|
|
{ 0x0487, "Xmp.video.TagString" }, //u
|
|
{ 0x0489, "Xmp.video.Duration" }, //u
|
|
{ 0x050d, "ChapterProcessPrivate" }, //sd
|
|
{ 0x0598, "ChapterFlagEnabled" }, //sd
|
|
{ 0x05a3, "Xmp.video.TagName" }, //u
|
|
{ 0x05b9, "EditionEntry" }, //s
|
|
{ 0x05bc, "EditionUID" }, //sd
|
|
{ 0x05bd, "EditionFlagHidden" }, //sd
|
|
{ 0x05db, "EditionFlagDefault" }, //sd
|
|
{ 0x05dd, "EditionFlagOrdered" }, //sd
|
|
{ 0x065c, "Xmp.video.AttachFileData" }, //u
|
|
{ 0x0660, "Xmp.video.AttachFileMIME" }, //u
|
|
{ 0x066e, "Xmp.video.AttachFileName" }, //u
|
|
{ 0x0675, "AttachedFileReferral" }, //sd
|
|
{ 0x067e, "Xmp.video.AttachFileDesc" }, //u
|
|
{ 0x06ae, "Xmp.video.AttachFileUID" }, //u
|
|
{ 0x07e1, "Xmp.video.ContentEncryptAlgo" }, //u
|
|
{ 0x07e2, "ContentEncryptionKeyID" }, //sd
|
|
{ 0x07e3, "ContentSignature" }, //sd
|
|
{ 0x07e4, "ContentSignatureKeyID" }, //sd
|
|
{ 0x07e5, "Xmp.video.ContentSignAlgo" }, //u
|
|
{ 0x07e6, "Xmp.video.ContentSignHashAlgo" }, //u
|
|
{ 0x0d80, "Xmp.video.MuxingApp" }, //u
|
|
{ 0x0dbb, "Seek" }, //s
|
|
{ 0x1031, "ContentEncodingOrder" }, //sd
|
|
{ 0x1032, "ContentEncodingScope" }, //sd
|
|
{ 0x1033, "Xmp.video.ContentEncodingType" }, //u
|
|
{ 0x1034, "ContentCompression" }, //s
|
|
{ 0x1035, "ContentEncryption" }, //s
|
|
{ 0x135f, "CueRefNumber" }, //sd
|
|
{ 0x136e, "Xmp.video.TrackName" }, //u
|
|
{ 0x1378, "CueBlockNumber" }, //sd
|
|
{ 0x137f, "TrackOffset" }, //sd
|
|
{ 0x13ab, "SeekID" }, //sd
|
|
{ 0x13ac, "SeekPosition" }, //sd
|
|
{ 0x13b8, "Stereo3DMode" }, //sd
|
|
{ 0x14aa, "Xmp.video.CropBottom" }, //ui
|
|
{ 0x14b0, "Xmp.video.Width" }, //u
|
|
{ 0x14b2, "Xmp.video.DisplayUnit" }, //u
|
|
{ 0x14b3, "Xmp.video.AspectRatioType" }, //u
|
|
{ 0x14ba, "Xmp.video.Height" }, //u
|
|
{ 0x14bb, "Xmp.video.CropTop" }, //ui
|
|
{ 0x14cc, "Xmp.video.CropLeft" }, //ui
|
|
{ 0x14dd, "Xmp.video.CropRight" }, //ui
|
|
{ 0x15aa, "TrackForced" }, //ui
|
|
{ 0x15ee, "MaxBlockAdditionID" }, //sd
|
|
{ 0x1741, "Xmp.video.WritingApp" }, //u
|
|
{ 0x1854, "SilentTracks" }, //s
|
|
{ 0x18d7, "SilentTrackNumber" }, //sd
|
|
{ 0x21a7, "AttachedFile" }, //s
|
|
{ 0x2240, "ContentEncoding" }, //s
|
|
{ 0x2264, "Xmp.audio.BitsPerSample" }, //u
|
|
{ 0x23a2, "CodecPrivate" }, //sd
|
|
{ 0x23c0, "Targets" }, //s
|
|
{ 0x23c3, "Xmp.video.PhysicalEquivalent" }, //u
|
|
{ 0x23c4, "TagChapterUID" }, //sd
|
|
{ 0x23c5, "TagTrackUID" }, //sd
|
|
{ 0x23c6, "TagAttachmentUID" }, //sd
|
|
{ 0x23c9, "TagEditionUID" }, //sd
|
|
{ 0x23ca, "Xmp.video.TargetType" }, //u
|
|
{ 0x2532, "SignedElement" }, //sd
|
|
{ 0x2624, "TrackTranslate" }, //s
|
|
{ 0x26a5, "TrackTranslateTrackID" }, //sd
|
|
{ 0x26bf, "TrackTranslateCodec" }, //sd
|
|
{ 0x26fc, "TrackTranslateEditionUID" }, //sd
|
|
{ 0x27c8, "SimpleTag" }, //s
|
|
{ 0x28ca, "TargetTypeValue" }, //sd
|
|
{ 0x2911, "ChapterProcessCommand" }, //s
|
|
{ 0x2922, "ChapterProcessTime" }, //sd
|
|
{ 0x2924, "ChapterTranslate" }, //s
|
|
{ 0x2933, "ChapterProcessData" }, //sd
|
|
{ 0x2944, "ChapterProcess" }, //s
|
|
{ 0x2955, "ChapterProcessCodecID" }, //sd
|
|
{ 0x29a5, "ChapterTranslateID" }, //sd
|
|
{ 0x29bf, "Xmp.video.TranslateCodec" }, //u
|
|
{ 0x29fc, "ChapterTranslateEditionUID" }, //sd
|
|
{ 0x2d80, "ContentEncodings" }, //s
|
|
{ 0x2de7, "MinCache" }, //sd
|
|
{ 0x2df8, "MaxCache" }, //sd
|
|
{ 0x2e67, "ChapterSegmentUID" }, //sd
|
|
{ 0x2ebc, "ChapterSegmentEditionUID" }, //sd
|
|
{ 0x2fab, "TrackOverlay" }, //sd
|
|
{ 0x3373, "Tag" }, //s
|
|
{ 0x3384, "SegmentFileName" }, //sd
|
|
{ 0x33a4, "SegmentUID" }, //sd
|
|
{ 0x33c4, "ChapterUID" }, //sd
|
|
{ 0x33c5, "TrackUID" }, //sd
|
|
{ 0x3446, "TrackAttachmentUID" }, //sd
|
|
{ 0x35a1, "BlockAdditions" }, //s
|
|
{ 0x38b5, "Xmp.audio.OutputSampleRate" }, //u
|
|
{ 0x3ba9, "Xmp.video.Title" }, //u
|
|
{ 0x3d7b, "ChannelPositions" }, //sd
|
|
{ 0x3e5b, "SignatureElements" }, //s
|
|
{ 0x3e7b, "SignatureElementList" }, //s
|
|
{ 0x3e8a, "Xmp.video.ContentSignAlgo" }, //u
|
|
{ 0x3e9a, "Xmp.video.ContentSignHashAlgo" }, //u
|
|
{ 0x3ea5, "SignaturePublicKey" }, //sd
|
|
{ 0x3eb5, "Signature" }, //sd
|
|
{ 0x2b59c, "TrackLanguage" }, //ui
|
|
{ 0x3314f, "TrackTimecodeScale" }, //sd
|
|
{ 0x383e3, "Xmp.video.FrameRate" }, //u
|
|
{ 0x3e383, "VideoFrameRate/DefaultDuration" }, //ui
|
|
{ 0x58688, "VideoCodecName/AudioCodecName/CodecName" }, //ui
|
|
{ 0x6b240, "CodecDownloadURL" }, //ui
|
|
{ 0xad7b1, "TimecodeScale" }, //ui
|
|
{ 0xeb524, "ColorSpace" }, //sd
|
|
{ 0xfb523, "Xmp.video.OpColor" }, //u
|
|
{ 0x1a9697, "CodecSettings" }, //ui
|
|
{ 0x1b4040, "CodecInfoURL" }, //ui
|
|
{ 0x1c83ab, "PrevFileName" }, //sd
|
|
{ 0x1cb923, "PrevUID" }, //sd
|
|
{ 0x1e83bb, "NextFileName" }, //sd
|
|
{ 0x1eb923, "NextUID" }, //sd
|
|
{ 0x43a770, "Chapters" }, //sd
|
|
{ 0x14d9b74, "SeekHead" }, //s
|
|
{ 0x254c367, "Tags" }, //s
|
|
{ 0x549a966, "Info" }, //s
|
|
{ 0x654ae6b, "Tracks" }, //s
|
|
{ 0x8538067, "SegmentHeader" }, //s
|
|
{ 0x941a469, "Attachments" }, //s
|
|
{ 0xa45dfa3, "EBMLHeader" }, //s
|
|
{ 0xb538667, "SignatureSlot" }, //s
|
|
{ 0xc53bb6b, "Cues" }, //s
|
|
{ 0xf43b675, "Cluster" }, //s
|
|
};
|
|
|
|
extern const MatroskaTags matroskaTrackType[] = {
|
|
{ 0x1, "Video" },
|
|
{ 0x2, "Audio" },
|
|
{ 0x3, "Complex" },
|
|
{ 0x10, "Logo" },
|
|
{ 0x11, "Subtitle" },
|
|
{ 0x12, "Buttons" },
|
|
{ 0x20, "Control" }
|
|
};
|
|
|
|
extern const MatroskaTags compressionAlgorithm[] = {
|
|
{ 0, "zlib " },
|
|
{ 1, "bzlib" },
|
|
{ 2, "lzo1x" },
|
|
{ 3, "Header Stripping" }
|
|
};
|
|
|
|
extern const MatroskaTags audioChannels[] = {
|
|
{ 1, "Mono" },
|
|
{ 2, "Stereo" },
|
|
{ 5, "5.1 Surround Sound" },
|
|
{ 7, "7.1 Surround Sound" }
|
|
};
|
|
|
|
extern const MatroskaTags displayUnit[] = {
|
|
{ 0x0, "Pixels" },
|
|
{ 0x1, "cm" },
|
|
{ 0x2, "inches" }
|
|
};
|
|
|
|
extern const MatroskaTags encryptionAlgorithm[] = {
|
|
{ 0, "Not Encrypted" },
|
|
{ 1, "DES" },
|
|
{ 2, "3DES" },
|
|
{ 3, "Twofish" },
|
|
{ 4, "Blowfish" },
|
|
{ 5, "AES" }
|
|
};
|
|
|
|
extern const MatroskaTags chapterPhysicalEquivalent[] = {
|
|
{ 10, "Index" },
|
|
{ 20, "Track" },
|
|
{ 30, "Session" },
|
|
{ 40, "Layer" },
|
|
{ 50, "Side" },
|
|
{ 60, "CD / DVD" },
|
|
{ 70, "Set / Package" },
|
|
};
|
|
|
|
extern const MatroskaTags encodingType[] = {
|
|
{ 0, "Compression" },
|
|
{ 1, "Encryption" }
|
|
};
|
|
|
|
extern const MatroskaTags videoScanType[] = {
|
|
{ 0, "Progressive" },
|
|
{ 1, "Interlaced" }
|
|
};
|
|
|
|
extern const MatroskaTags chapterTranslateCodec[] = {
|
|
{ 0, "Matroska Script" },
|
|
{ 1, "DVD Menu" }
|
|
};
|
|
|
|
extern const MatroskaTags aspectRatioType[] = {
|
|
{ 0, "Free Resizing" },
|
|
{ 1, "Keep Aspect Ratio" },
|
|
{ 2, "Fixed" }
|
|
};
|
|
|
|
extern const MatroskaTags contentSignatureAlgorithm[] = {
|
|
{ 0, "Not Signed" },
|
|
{ 1, "RSA" }
|
|
};
|
|
|
|
extern const MatroskaTags contentSignatureHashAlgorithm[] = {
|
|
{ 0, "Not Signed" },
|
|
{ 1, "SHA1-160" },
|
|
{ 2, "MD5" }
|
|
};
|
|
|
|
extern const MatroskaTags trackEnable[] = {
|
|
{ 0x1, "Xmp.video.Enabled" },
|
|
{ 0x2, "Xmp.audio.Enabled" },
|
|
{ 0x11, "Xmp.video.SubTEnabled" }
|
|
};
|
|
|
|
extern const MatroskaTags defaultOn[] = {
|
|
{ 0x1, "Xmp.video.DefaultOn" },
|
|
{ 0x2, "Xmp.audio.DefaultOn" },
|
|
{ 0x11, "Xmp.video.SubTDefaultOn" }
|
|
};
|
|
|
|
extern const MatroskaTags trackForced[] = {
|
|
{ 0x1, "Xmp.video.TrackForced" },
|
|
{ 0x2, "Xmp.audio.TrackForced" },
|
|
{ 0x11, "Xmp.video.SubTTrackForced" }
|
|
};
|
|
|
|
extern const MatroskaTags trackLacing[] = {
|
|
{ 0x1, "Xmp.video.TrackLacing" },
|
|
{ 0x2, "Xmp.audio.TrackLacing" },
|
|
{ 0x11, "Xmp.video.SubTTrackLacing" }
|
|
};
|
|
|
|
extern const MatroskaTags codecDecodeAll[] = {
|
|
{ 0x1, "Xmp.video.CodecDecodeAll" },
|
|
{ 0x2, "Xmp.audio.CodecDecodeAll" },
|
|
{ 0x11, "Xmp.video.SubTCodecDecodeAll" }
|
|
};
|
|
|
|
extern const MatroskaTags codecDownloadUrl[] = {
|
|
{ 0x1, "Xmp.video.CodecDownloadUrl" },
|
|
{ 0x2, "Xmp.audio.CodecDownloadUrl" },
|
|
{ 0x11, "Xmp.video.SubTCodecDownloadUrl" }
|
|
};
|
|
|
|
extern const MatroskaTags codecSettings[] = {
|
|
{ 0x1, "Xmp.video.CodecSettings" },
|
|
{ 0x2, "Xmp.audio.CodecSettings" },
|
|
{ 0x11, "Xmp.video.SubTCodecSettings" }
|
|
};
|
|
|
|
extern const MatroskaTags trackCodec[] = {
|
|
{ 0x1, "Xmp.video.Codec" },
|
|
{ 0x2, "Xmp.audio.Compressor" },
|
|
{ 0x11, "Xmp.video.SubTCodec" }
|
|
};
|
|
|
|
extern const MatroskaTags trackLanguage[] = {
|
|
{ 0x1, "Xmp.video.TrackLang" },
|
|
{ 0x2, "Xmp.audio.TrackLang" },
|
|
{ 0x11, "Xmp.video.SubTLang" }
|
|
};
|
|
|
|
extern const MatroskaTags codecInfo[] = {
|
|
{ 0x1, "Xmp.video.CodecInfo" },
|
|
{ 0x2, "Xmp.audio.CodecInfo" },
|
|
{ 0x11, "Xmp.video.SubTCodecInfo" }
|
|
};
|
|
|
|
extern const MatroskaTags streamRate[] = {
|
|
{ 0x1, "Xmp.video.FrameRate" },
|
|
{ 0x2, "Xmp.audio.DefaultDuration" }
|
|
};
|
|
|
|
/*!
|
|
@brief Function used to calculate Tags, Tags may comprise of more than
|
|
one byte. The first byte calculates size of the Tag and the remaining
|
|
bytes are used to calculate the rest of the Tag.
|
|
Returns Tag Value.
|
|
*/
|
|
uint64_t returnTagValue(const byte* buf, long size)
|
|
{
|
|
assert(size > 0 && size <= 8);
|
|
|
|
uint64_t b0 = buf[0] & (0xff >> size);
|
|
uint64_t tag = b0 << ((size - 1) * 8);
|
|
for (long i = 1; i < size; ++i) {
|
|
tag |= static_cast<uint64_t>(buf[i]) << ((size - i - 1) * 8);
|
|
}
|
|
|
|
return tag;
|
|
}
|
|
|
|
/*!
|
|
@brief Function used to convert buffer data into numerical information,
|
|
information stored in BigEndian format
|
|
*/
|
|
int64_t returnValue(const byte* buf, long size)
|
|
{
|
|
|
|
int64_t temp = 0;
|
|
|
|
for(int i = size-1; i >= 0; i--) {
|
|
temp = temp + static_cast<int64_t>(buf[i]*(pow(256.0, (double)size-i-1)));
|
|
}
|
|
// Todo: remove debug output
|
|
// std::cerr << "size = " << size << ", val = " << temp << std::hex << " (0x" << temp << std::dec << ")";
|
|
|
|
uint64_t ret = 0;
|
|
for (long i = 0; i < size; ++i) {
|
|
ret |= static_cast<uint64_t>(buf[i]) << ((size - i - 1) * 8);
|
|
}
|
|
|
|
// Todo: remove debug output
|
|
// std::cerr << ", ret = " << ret << std::hex << " (0x" << ret << std::dec << ")\n";
|
|
|
|
return ret;
|
|
}
|
|
|
|
}} // namespace Internal, Exiv2
|
|
|
|
namespace Exiv2 {
|
|
|
|
using namespace Exiv2::Internal;
|
|
|
|
MatroskaVideo::MatroskaVideo(BasicIo::AutoPtr io)
|
|
: Image(ImageType::mkv, mdNone, io)
|
|
{
|
|
} // MatroskaVideo::MatroskaVideo
|
|
|
|
std::string MatroskaVideo::mimeType() const
|
|
{
|
|
return "video/matroska";
|
|
}
|
|
|
|
void MatroskaVideo::writeMetadata()
|
|
{
|
|
}
|
|
|
|
void MatroskaVideo::readMetadata()
|
|
{
|
|
if (io_->open() != 0) throw Error(9, io_->path(), strError());
|
|
|
|
// Ensure that this is the correct image type
|
|
if (!isMkvType(*io_, false)) {
|
|
if (io_->error() || io_->eof()) throw Error(14);
|
|
throw Error(3, "Matroska");
|
|
}
|
|
|
|
IoCloser closer(*io_);
|
|
clearMetadata();
|
|
continueTraversing_ = true;
|
|
height_ = width_ = 1;
|
|
|
|
xmpData_["Xmp.video.FileName"] = io_->path();
|
|
xmpData_["Xmp.video.FileSize"] = (double)io_->size()/(double)1048576;
|
|
xmpData_["Xmp.video.MimeType"] = mimeType();
|
|
|
|
while (continueTraversing_) decodeBlock();
|
|
|
|
aspectRatio();
|
|
} // MatroskaVideo::readMetadata
|
|
|
|
void MatroskaVideo::decodeBlock()
|
|
{
|
|
byte buf[8];
|
|
io_->read(buf, 1);
|
|
|
|
if(io_->eof()) {
|
|
continueTraversing_ = false;
|
|
return;
|
|
}
|
|
|
|
uint32_t sz = findBlockSize(buf[0]); // 0-8
|
|
if (sz > 0) io_->read(buf + 1, sz - 1);
|
|
|
|
const MatroskaTags* mt = find(matroskaTags, returnTagValue(buf, sz));
|
|
|
|
if(mt->val_ == 0xc53bb6b || mt->val_ == 0xf43b675) {
|
|
continueTraversing_ = false;
|
|
return;
|
|
}
|
|
|
|
bool skip = find(compositeTagsList, mt->val_) != 0;
|
|
bool ignore = find(ignoredTagsList, mt->val_) != 0;
|
|
|
|
io_->read(buf, 1);
|
|
sz = findBlockSize(buf[0]); // 0-8
|
|
|
|
if (sz > 0) io_->read(buf + 1, sz - 1);
|
|
uint64_t size = returnTagValue(buf, sz);
|
|
|
|
if (skip && !ignore) return;
|
|
|
|
const uint64_t bufMinSize = 200;
|
|
|
|
#ifndef SUPPRESS_WARNINGS
|
|
if (!ignore && size > bufMinSize) {
|
|
EXV_WARNING << "Size " << size << " of Matroska tag 0x"
|
|
<< std::hex << mt->val_ << std::dec
|
|
<< " is greater than " << bufMinSize << ": ignoring it.\n";
|
|
}
|
|
#endif
|
|
if (ignore || size > bufMinSize) {
|
|
io_->seek(size, BasicIo::cur);
|
|
return;
|
|
}
|
|
|
|
DataBuf buf2(bufMinSize+1);
|
|
std::memset(buf2.pData_, 0x0, buf2.size_);
|
|
long s = static_cast<long>(size) ;
|
|
io_->read(buf2.pData_,s);
|
|
contentManagement(mt, buf2.pData_,s);
|
|
} // MatroskaVideo::decodeBlock
|
|
|
|
void MatroskaVideo::contentManagement(const MatroskaTags* mt, const byte* buf, long size)
|
|
{
|
|
int64_t duration_in_ms = 0;
|
|
static double time_code_scale = 1.0, temp = 0;
|
|
static long stream = 0, track_count = 0;
|
|
char str[4] = "No";
|
|
const MatroskaTags* internalMt = 0;
|
|
|
|
switch (mt->val_) {
|
|
|
|
case 0x0282: case 0x0d80: case 0x1741: case 0x3ba9: case 0x066e: case 0x0660:
|
|
case 0x065c: case 0x067e: case 0x047a: case 0x0487: case 0x05a3: case 0x136e:
|
|
case 0x23ca: case 0xeb524:
|
|
xmpData_[mt->label_] = buf;
|
|
break;
|
|
|
|
case 0x0030: case 0x003a: case 0x0287: case 0x14b0: case 0x14ba: case 0x285:
|
|
case 0x06ae: case 0x0286: case 0x02f7: case 0x2264: case 0x14aa: case 0x14bb:
|
|
case 0x14cc: case 0x14dd:
|
|
xmpData_[mt->label_] = returnValue(buf, size);
|
|
|
|
if (mt->val_ == 0x0030 || mt->val_ == 0x14b0) {
|
|
width_ = returnValue(buf, size);
|
|
}
|
|
else if (mt->val_ == 0x003a || mt->val_ == 0x14ba) {
|
|
height_ = returnValue(buf, size);
|
|
}
|
|
break;
|
|
|
|
case 0x001a: case 0x001f: case 0x0254: case 0x07e1: case 0x07e5: case 0x07e6:
|
|
case 0x1033: case 0x14b2: case 0x14b3: case 0x23c3: case 0x29bf: case 0x3e8a:
|
|
case 0x3e9a:
|
|
switch (mt->val_) {
|
|
case 0x001a: internalMt = find(videoScanType, returnValue(buf, size)); break;
|
|
case 0x001f: internalMt = find(audioChannels, returnValue(buf, size)); break;
|
|
case 0x0254: internalMt = find(compressionAlgorithm, returnValue(buf, size)); break;
|
|
case 0x07e1: internalMt = find(encryptionAlgorithm, returnValue(buf, size)); break;
|
|
case 0x1033: internalMt = find(encodingType, returnValue(buf, size)); break;
|
|
case 0x3e8a:
|
|
case 0x07e5: internalMt = find(contentSignatureAlgorithm, returnValue(buf, size)); break;
|
|
case 0x3e9a:
|
|
case 0x07e6: internalMt = find(contentSignatureHashAlgorithm, returnValue(buf, size)); break;
|
|
case 0x14b2: internalMt = find(displayUnit, returnValue(buf, size)); break;
|
|
case 0x14b3: internalMt = find(aspectRatioType, returnValue(buf, size)); break;
|
|
case 0x23c3: internalMt = find(chapterPhysicalEquivalent, returnValue(buf, size)); break;
|
|
case 0x29bf: internalMt = find(chapterTranslateCodec, returnValue(buf, size)); break;
|
|
}
|
|
if (internalMt) xmpData_[mt->label_] = internalMt->label_;
|
|
break;
|
|
|
|
case 0x0035: case 0x38b5:
|
|
xmpData_[mt->label_] = getFloat(buf, bigEndian);
|
|
break;
|
|
|
|
case 0x0039: case 0x0008: case 0x15aa: case 0x001c: case 0x002a: case 0x1a9697:
|
|
case 0x0484:
|
|
if (returnValue(buf, size)) strcpy(str, "Yes");
|
|
switch (mt->val_) {
|
|
case 0x0039: internalMt = find(trackEnable, stream); break;
|
|
case 0x0008: internalMt = find(defaultOn, stream); break;
|
|
case 0x15aa: internalMt = find(trackForced, stream); break;
|
|
case 0x001c: internalMt = find(trackLacing, stream); break;
|
|
case 0x002a: internalMt = find(codecDecodeAll, stream); break;
|
|
case 0x1a9697: internalMt = find(codecSettings, stream); break;
|
|
case 0x0484: internalMt = mt; break;
|
|
}
|
|
if (internalMt) xmpData_[internalMt->label_] = str;
|
|
break;
|
|
|
|
case 0x0006: case 0x2b59c: case 0x58688: case 0x6b240: case 0x1b4040:
|
|
switch (mt->val_) {
|
|
case 0x0006: internalMt = find(trackCodec, stream); break;
|
|
case 0x2b59c: internalMt = find(trackLanguage, stream); break;
|
|
case 0x58688: internalMt = find(codecInfo, stream); break;
|
|
case 0x6b240:
|
|
case 0x1b4040: internalMt = find(codecDownloadUrl, stream); break;
|
|
}
|
|
if (internalMt) xmpData_[internalMt->label_] = buf;
|
|
break;
|
|
|
|
case 0x0489: case 0x0461:
|
|
switch (mt->val_) {
|
|
case 0x0489:
|
|
if(size <= 4) {
|
|
duration_in_ms = static_cast<int64_t>(getFloat(buf, bigEndian) * time_code_scale * 1000);
|
|
}
|
|
else {
|
|
duration_in_ms = static_cast<int64_t>(getDouble(buf, bigEndian) * time_code_scale * 1000);
|
|
}
|
|
break;
|
|
case 0x0461: {
|
|
duration_in_ms = returnValue(buf, size)/1000000000; break;
|
|
}
|
|
}
|
|
xmpData_[mt->label_] = duration_in_ms;
|
|
break;
|
|
|
|
case 0x0057:
|
|
track_count++;
|
|
xmpData_["Xmp.video.TotalStream"] = track_count;
|
|
break;
|
|
|
|
case 0xad7b1:
|
|
time_code_scale = (double)returnValue(buf, size)/(double)1000000000;
|
|
xmpData_["Xmp.video.TimecodeScale"] = time_code_scale;
|
|
break;
|
|
|
|
case 0x0003:
|
|
internalMt = find(matroskaTrackType, returnValue(buf, size));
|
|
stream = static_cast<long>(internalMt->val_);
|
|
break;
|
|
|
|
case 0x3e383: case 0x383e3:
|
|
internalMt = find(streamRate, stream);
|
|
if (returnValue(buf, size)) {
|
|
switch (stream) {
|
|
case 1: temp = (double)1000000000/(double)returnValue(buf, size); break;
|
|
case 2: temp = static_cast<double>(returnValue(buf, size) / 1000); break;
|
|
}
|
|
if (internalMt) xmpData_[internalMt->label_] = temp;
|
|
}
|
|
else
|
|
if (internalMt) xmpData_[internalMt->label_] = "Variable Bit Rate";
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
} // MatroskaVideo::contentManagement
|
|
|
|
void MatroskaVideo::aspectRatio()
|
|
{
|
|
//TODO - Make a better unified method to handle all cases of Aspect Ratio
|
|
|
|
double aspectRatio = (double)width_ / (double)height_;
|
|
aspectRatio = floor(aspectRatio*10) / 10;
|
|
xmpData_["Xmp.video.AspectRatio"] = aspectRatio;
|
|
|
|
int aR = (int) ((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;
|
|
}
|
|
} // MatroskaVideo::aspectRatio
|
|
|
|
uint32_t MatroskaVideo::findBlockSize(byte b)
|
|
{
|
|
if (b & 128) return 1;
|
|
else if (b & 64) return 2;
|
|
else if (b & 32) return 3;
|
|
else if (b & 16) return 4;
|
|
else if (b & 8) return 5;
|
|
else if (b & 4) return 6;
|
|
else if (b & 2) return 7;
|
|
else if (b & 1) return 8;
|
|
else return 0;
|
|
} // MatroskaVideo::findBlockSize
|
|
|
|
Image::AutoPtr newMkvInstance(BasicIo::AutoPtr io, bool /*create*/)
|
|
{
|
|
Image::AutoPtr image(new MatroskaVideo(io));
|
|
if (!image->good()) {
|
|
image.reset();
|
|
}
|
|
return image;
|
|
}
|
|
|
|
bool isMkvType(BasicIo& iIo, bool advance)
|
|
{
|
|
bool result = true;
|
|
byte tmpBuf[4];
|
|
iIo.read(tmpBuf, 4);
|
|
|
|
if (iIo.error() || iIo.eof()) return false;
|
|
|
|
if (0x1a != tmpBuf[0] || 0x45 != tmpBuf[1] || 0xdf != tmpBuf[2] || 0xa3 != tmpBuf[3]) {
|
|
result = false;
|
|
}
|
|
|
|
if (!advance || !result ) iIo.seek(0, BasicIo::beg);
|
|
return result;
|
|
}
|
|
|
|
} // namespace Exiv2
|