| // Copyright (c) 2016 The WebM project authors. All Rights Reserved. |
| // |
| // Use of this source code is governed by a BSD-style license |
| // that can be found in the LICENSE file in the root of the source |
| // tree. An additional intellectual property rights grant can be found |
| // in the file PATENTS. All contributing project authors may |
| // be found in the AUTHORS file in the root of the source tree. |
| #include <cstdio> |
| #include <cstdlib> |
| #include <iomanip> |
| #include <iostream> |
| |
| #include "webm/callback.h" |
| #include "webm/file_reader.h" |
| #include "webm/status.h" |
| #include "webm/webm_parser.h" |
| |
| // We use pretty much everything in the webm namespace. Just pull |
| // it all in. |
| using namespace webm; // NOLINT |
| |
| template <typename T> |
| std::ostream& PrintUnknownEnumValue(std::ostream& os, T value) { |
| return os << std::to_string(static_cast<std::uint64_t>(value)) << " (?)"; |
| } |
| |
| // Overloads for operator<< for pretty printing enums. |
| std::ostream& operator<<(std::ostream& os, Id id) { |
| switch (id) { |
| case Id::kEbml: |
| return os << "EBML"; |
| case Id::kEbmlVersion: |
| return os << "EBMLVersion"; |
| case Id::kEbmlReadVersion: |
| return os << "EBMLReadVersion"; |
| case Id::kEbmlMaxIdLength: |
| return os << "EBMLMaxIDLength"; |
| case Id::kEbmlMaxSizeLength: |
| return os << "EBMLMaxSizeLength"; |
| case Id::kDocType: |
| return os << "DocType"; |
| case Id::kDocTypeVersion: |
| return os << "DocTypeVersion"; |
| case Id::kDocTypeReadVersion: |
| return os << "DocTypeReadVersion"; |
| case Id::kVoid: |
| return os << "Void"; |
| case Id::kSegment: |
| return os << "Segment"; |
| case Id::kSeekHead: |
| return os << "SeekHead"; |
| case Id::kSeek: |
| return os << "Seek"; |
| case Id::kSeekId: |
| return os << "SeekID"; |
| case Id::kSeekPosition: |
| return os << "SeekPosition"; |
| case Id::kInfo: |
| return os << "Info"; |
| case Id::kTimecodeScale: |
| return os << "TimecodeScale"; |
| case Id::kDuration: |
| return os << "Duration"; |
| case Id::kDateUtc: |
| return os << "DateUTC"; |
| case Id::kTitle: |
| return os << "Title"; |
| case Id::kMuxingApp: |
| return os << "MuxingApp"; |
| case Id::kWritingApp: |
| return os << "WritingApp"; |
| case Id::kCluster: |
| return os << "Cluster"; |
| case Id::kTimecode: |
| return os << "Timecode"; |
| case Id::kPrevSize: |
| return os << "PrevSize"; |
| case Id::kSimpleBlock: |
| return os << "SimpleBlock"; |
| case Id::kBlockGroup: |
| return os << "BlockGroup"; |
| case Id::kBlock: |
| return os << "Block"; |
| case Id::kBlockVirtual: |
| return os << "BlockVirtual"; |
| case Id::kBlockAdditions: |
| return os << "BlockAdditions"; |
| case Id::kBlockMore: |
| return os << "BlockMore"; |
| case Id::kBlockAddId: |
| return os << "BlockAddID"; |
| case Id::kBlockAdditional: |
| return os << "BlockAdditional"; |
| case Id::kBlockDuration: |
| return os << "BlockDuration"; |
| case Id::kReferenceBlock: |
| return os << "ReferenceBlock"; |
| case Id::kDiscardPadding: |
| return os << "DiscardPadding"; |
| case Id::kSlices: |
| return os << "Slices"; |
| case Id::kTimeSlice: |
| return os << "TimeSlice"; |
| case Id::kLaceNumber: |
| return os << "LaceNumber"; |
| case Id::kTracks: |
| return os << "Tracks"; |
| case Id::kTrackEntry: |
| return os << "TrackEntry"; |
| case Id::kTrackNumber: |
| return os << "TrackNumber"; |
| case Id::kTrackUid: |
| return os << "TrackUID"; |
| case Id::kTrackType: |
| return os << "TrackType"; |
| case Id::kFlagEnabled: |
| return os << "FlagEnabled"; |
| case Id::kFlagDefault: |
| return os << "FlagDefault"; |
| case Id::kFlagForced: |
| return os << "FlagForced"; |
| case Id::kFlagLacing: |
| return os << "FlagLacing"; |
| case Id::kDefaultDuration: |
| return os << "DefaultDuration"; |
| case Id::kName: |
| return os << "Name"; |
| case Id::kLanguage: |
| return os << "Language"; |
| case Id::kCodecId: |
| return os << "CodecID"; |
| case Id::kCodecPrivate: |
| return os << "CodecPrivate"; |
| case Id::kCodecName: |
| return os << "CodecName"; |
| case Id::kCodecDelay: |
| return os << "CodecDelay"; |
| case Id::kSeekPreRoll: |
| return os << "SeekPreRoll"; |
| case Id::kVideo: |
| return os << "Video"; |
| case Id::kFlagInterlaced: |
| return os << "FlagInterlaced"; |
| case Id::kStereoMode: |
| return os << "StereoMode"; |
| case Id::kAlphaMode: |
| return os << "AlphaMode"; |
| case Id::kPixelWidth: |
| return os << "PixelWidth"; |
| case Id::kPixelHeight: |
| return os << "PixelHeight"; |
| case Id::kPixelCropBottom: |
| return os << "PixelCropBottom"; |
| case Id::kPixelCropTop: |
| return os << "PixelCropTop"; |
| case Id::kPixelCropLeft: |
| return os << "PixelCropLeft"; |
| case Id::kPixelCropRight: |
| return os << "PixelCropRight"; |
| case Id::kDisplayWidth: |
| return os << "DisplayWidth"; |
| case Id::kDisplayHeight: |
| return os << "DisplayHeight"; |
| case Id::kDisplayUnit: |
| return os << "DisplayUnit"; |
| case Id::kAspectRatioType: |
| return os << "AspectRatioType"; |
| case Id::kFrameRate: |
| return os << "FrameRate"; |
| case Id::kColour: |
| return os << "Colour"; |
| case Id::kMatrixCoefficients: |
| return os << "MatrixCoefficients"; |
| case Id::kBitsPerChannel: |
| return os << "BitsPerChannel"; |
| case Id::kChromaSubsamplingHorz: |
| return os << "ChromaSubsamplingHorz"; |
| case Id::kChromaSubsamplingVert: |
| return os << "ChromaSubsamplingVert"; |
| case Id::kCbSubsamplingHorz: |
| return os << "CbSubsamplingHorz"; |
| case Id::kCbSubsamplingVert: |
| return os << "CbSubsamplingVert"; |
| case Id::kChromaSitingHorz: |
| return os << "ChromaSitingHorz"; |
| case Id::kChromaSitingVert: |
| return os << "ChromaSitingVert"; |
| case Id::kRange: |
| return os << "Range"; |
| case Id::kTransferCharacteristics: |
| return os << "TransferCharacteristics"; |
| case Id::kPrimaries: |
| return os << "Primaries"; |
| case Id::kMaxCll: |
| return os << "MaxCLL"; |
| case Id::kMaxFall: |
| return os << "MaxFALL"; |
| case Id::kMasteringMetadata: |
| return os << "MasteringMetadata"; |
| case Id::kPrimaryRChromaticityX: |
| return os << "PrimaryRChromaticityX"; |
| case Id::kPrimaryRChromaticityY: |
| return os << "PrimaryRChromaticityY"; |
| case Id::kPrimaryGChromaticityX: |
| return os << "PrimaryGChromaticityX"; |
| case Id::kPrimaryGChromaticityY: |
| return os << "PrimaryGChromaticityY"; |
| case Id::kPrimaryBChromaticityX: |
| return os << "PrimaryBChromaticityX"; |
| case Id::kPrimaryBChromaticityY: |
| return os << "PrimaryBChromaticityY"; |
| case Id::kWhitePointChromaticityX: |
| return os << "WhitePointChromaticityX"; |
| case Id::kWhitePointChromaticityY: |
| return os << "WhitePointChromaticityY"; |
| case Id::kLuminanceMax: |
| return os << "LuminanceMax"; |
| case Id::kLuminanceMin: |
| return os << "LuminanceMin"; |
| case Id::kProjection: |
| return os << "Projection"; |
| case Id::kProjectionType: |
| return os << "kProjectionType"; |
| case Id::kProjectionPrivate: |
| return os << "kProjectionPrivate"; |
| case Id::kProjectionPoseYaw: |
| return os << "kProjectionPoseYaw"; |
| case Id::kProjectionPosePitch: |
| return os << "kProjectionPosePitch"; |
| case Id::kProjectionPoseRoll: |
| return os << "ProjectionPoseRoll"; |
| case Id::kAudio: |
| return os << "Audio"; |
| case Id::kSamplingFrequency: |
| return os << "SamplingFrequency"; |
| case Id::kOutputSamplingFrequency: |
| return os << "OutputSamplingFrequency"; |
| case Id::kChannels: |
| return os << "Channels"; |
| case Id::kBitDepth: |
| return os << "BitDepth"; |
| case Id::kContentEncodings: |
| return os << "ContentEncodings"; |
| case Id::kContentEncoding: |
| return os << "ContentEncoding"; |
| case Id::kContentEncodingOrder: |
| return os << "ContentEncodingOrder"; |
| case Id::kContentEncodingScope: |
| return os << "ContentEncodingScope"; |
| case Id::kContentEncodingType: |
| return os << "ContentEncodingType"; |
| case Id::kContentEncryption: |
| return os << "ContentEncryption"; |
| case Id::kContentEncAlgo: |
| return os << "ContentEncAlgo"; |
| case Id::kContentEncKeyId: |
| return os << "ContentEncKeyID"; |
| case Id::kContentEncAesSettings: |
| return os << "ContentEncAESSettings"; |
| case Id::kAesSettingsCipherMode: |
| return os << "AESSettingsCipherMode"; |
| case Id::kCues: |
| return os << "Cues"; |
| case Id::kCuePoint: |
| return os << "CuePoint"; |
| case Id::kCueTime: |
| return os << "CueTime"; |
| case Id::kCueTrackPositions: |
| return os << "CueTrackPositions"; |
| case Id::kCueTrack: |
| return os << "CueTrack"; |
| case Id::kCueClusterPosition: |
| return os << "CueClusterPosition"; |
| case Id::kCueRelativePosition: |
| return os << "CueRelativePosition"; |
| case Id::kCueDuration: |
| return os << "CueDuration"; |
| case Id::kCueBlockNumber: |
| return os << "CueBlockNumber"; |
| case Id::kChapters: |
| return os << "Chapters"; |
| case Id::kEditionEntry: |
| return os << "EditionEntry"; |
| case Id::kChapterAtom: |
| return os << "ChapterAtom"; |
| case Id::kChapterUid: |
| return os << "ChapterUID"; |
| case Id::kChapterStringUid: |
| return os << "ChapterStringUID"; |
| case Id::kChapterTimeStart: |
| return os << "ChapterTimeStart"; |
| case Id::kChapterTimeEnd: |
| return os << "ChapterTimeEnd"; |
| case Id::kChapterDisplay: |
| return os << "ChapterDisplay"; |
| case Id::kChapString: |
| return os << "ChapString"; |
| case Id::kChapLanguage: |
| return os << "ChapLanguage"; |
| case Id::kChapCountry: |
| return os << "ChapCountry"; |
| case Id::kTags: |
| return os << "Tags"; |
| case Id::kTag: |
| return os << "Tag"; |
| case Id::kTargets: |
| return os << "Targets"; |
| case Id::kTargetTypeValue: |
| return os << "TargetTypeValue"; |
| case Id::kTargetType: |
| return os << "TargetType"; |
| case Id::kTagTrackUid: |
| return os << "TagTrackUID"; |
| case Id::kSimpleTag: |
| return os << "SimpleTag"; |
| case Id::kTagName: |
| return os << "TagName"; |
| case Id::kTagLanguage: |
| return os << "TagLanguage"; |
| case Id::kTagDefault: |
| return os << "TagDefault"; |
| case Id::kTagString: |
| return os << "TagString"; |
| case Id::kTagBinary: |
| return os << "TagBinary"; |
| default: |
| return PrintUnknownEnumValue(os, id); |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& os, Lacing value) { |
| switch (value) { |
| case Lacing::kNone: |
| return os << "0 (none)"; |
| case Lacing::kXiph: |
| return os << "2 (Xiph)"; |
| case Lacing::kFixed: |
| return os << "4 (fixed)"; |
| case Lacing::kEbml: |
| return os << "6 (EBML)"; |
| default: |
| return PrintUnknownEnumValue(os, value); |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& os, MatrixCoefficients value) { |
| switch (value) { |
| case MatrixCoefficients::kRgb: |
| return os << "0 (identity, RGB/XYZ)"; |
| case MatrixCoefficients::kBt709: |
| return os << "1 (Rec. ITU-R BT.709-5)"; |
| case MatrixCoefficients::kUnspecified: |
| return os << "2 (unspecified)"; |
| case MatrixCoefficients::kFcc: |
| return os << "4 (US FCC)"; |
| case MatrixCoefficients::kBt470Bg: |
| return os << "5 (Rec. ITU-R BT.470-6 System B, G)"; |
| case MatrixCoefficients::kSmpte170M: |
| return os << "6 (SMPTE 170M)"; |
| case MatrixCoefficients::kSmpte240M: |
| return os << "7 (SMPTE 240M)"; |
| case MatrixCoefficients::kYCgCo: |
| return os << "8 (YCgCo)"; |
| case MatrixCoefficients::kBt2020NonconstantLuminance: |
| return os << "9 (Rec. ITU-R BT.2020, non-constant luma)"; |
| case MatrixCoefficients::kBt2020ConstantLuminance: |
| return os << "10 (Rec. ITU-R BT.2020 , constant luma)"; |
| default: |
| return PrintUnknownEnumValue(os, value); |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& os, Range value) { |
| switch (value) { |
| case Range::kUnspecified: |
| return os << "0 (unspecified)"; |
| case Range::kBroadcast: |
| return os << "1 (broadcast)"; |
| case Range::kFull: |
| return os << "2 (full)"; |
| case Range::kDerived: |
| return os << "3 (defined by MatrixCoefficients/TransferCharacteristics)"; |
| default: |
| return PrintUnknownEnumValue(os, value); |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& os, TransferCharacteristics value) { |
| switch (value) { |
| case TransferCharacteristics::kBt709: |
| return os << "1 (Rec. ITU-R BT.709-6)"; |
| case TransferCharacteristics::kUnspecified: |
| return os << "2 (unspecified)"; |
| case TransferCharacteristics::kGamma22curve: |
| return os << "4 (gamma 2.2, Rec. ITU‑R BT.470‑6 System M)"; |
| case TransferCharacteristics::kGamma28curve: |
| return os << "5 (gamma 2.8, Rec. ITU‑R BT.470-6 System B, G)"; |
| case TransferCharacteristics::kSmpte170M: |
| return os << "6 (SMPTE 170M)"; |
| case TransferCharacteristics::kSmpte240M: |
| return os << "7 (SMPTE 240M)"; |
| case TransferCharacteristics::kLinear: |
| return os << "8 (linear)"; |
| case TransferCharacteristics::kLog: |
| return os << "9 (log, 100:1 range)"; |
| case TransferCharacteristics::kLogSqrt: |
| return os << "10 (log, 316.2:1 range)"; |
| case TransferCharacteristics::kIec6196624: |
| return os << "11 (IEC 61966-2-4)"; |
| case TransferCharacteristics::kBt1361ExtendedColourGamut: |
| return os << "12 (Rec. ITU-R BT.1361, extended colour gamut)"; |
| case TransferCharacteristics::kIec6196621: |
| return os << "13 (IEC 61966-2-1, sRGB or sYCC)"; |
| case TransferCharacteristics::k10BitBt2020: |
| return os << "14 (Rec. ITU-R BT.2020-2, 10-bit)"; |
| case TransferCharacteristics::k12BitBt2020: |
| return os << "15 (Rec. ITU-R BT.2020-2, 12-bit)"; |
| case TransferCharacteristics::kSmpteSt2084: |
| return os << "16 (SMPTE ST 2084)"; |
| case TransferCharacteristics::kSmpteSt4281: |
| return os << "17 (SMPTE ST 428-1)"; |
| case TransferCharacteristics::kAribStdB67Hlg: |
| return os << "18 (ARIB STD-B67/Rec. ITU-R BT.[HDR-TV] HLG)"; |
| default: |
| return PrintUnknownEnumValue(os, value); |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& os, Primaries value) { |
| switch (value) { |
| case Primaries::kBt709: |
| return os << "1 (Rec. ITU‑R BT.709-6)"; |
| case Primaries::kUnspecified: |
| return os << "2 (unspecified)"; |
| case Primaries::kBt470M: |
| return os << "4 (Rec. ITU‑R BT.470‑6 System M)"; |
| case Primaries::kBt470Bg: |
| return os << "5 (Rec. ITU‑R BT.470‑6 System B, G)"; |
| case Primaries::kSmpte170M: |
| return os << "6 (SMPTE 170M)"; |
| case Primaries::kSmpte240M: |
| return os << "7 (SMPTE 240M)"; |
| case Primaries::kFilm: |
| return os << "8 (generic film)"; |
| case Primaries::kBt2020: |
| return os << "9 (Rec. ITU-R BT.2020-2)"; |
| case Primaries::kSmpteSt4281: |
| return os << "10 (SMPTE ST 428-1)"; |
| case Primaries::kJedecP22Phosphors: |
| return os << "22 (EBU Tech. 3213-E/JEDEC P22 phosphors)"; |
| default: |
| return PrintUnknownEnumValue(os, value); |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& os, ProjectionType value) { |
| switch (value) { |
| case ProjectionType::kRectangular: |
| return os << "0 (rectangular)"; |
| case ProjectionType::kEquirectangular: |
| return os << "1 (equirectangular)"; |
| case ProjectionType::kCubeMap: |
| return os << "2 (cube map)"; |
| default: |
| return PrintUnknownEnumValue(os, value); |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& os, FlagInterlaced value) { |
| switch (value) { |
| case FlagInterlaced::kUnspecified: |
| return os << "0 (unspecified)"; |
| case FlagInterlaced::kInterlaced: |
| return os << "1 (interlaced)"; |
| case FlagInterlaced::kProgressive: |
| return os << "2 (progressive)"; |
| default: |
| return PrintUnknownEnumValue(os, value); |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& os, StereoMode value) { |
| switch (value) { |
| case StereoMode::kMono: |
| return os << "0 (mono)"; |
| case StereoMode::kSideBySideLeftFirst: |
| return os << "1 (side-by-side, left eye first)"; |
| case StereoMode::kTopBottomRightFirst: |
| return os << "2 (top-bottom, right eye first)"; |
| case StereoMode::kTopBottomLeftFirst: |
| return os << "3 (top-bottom, left eye first)"; |
| case StereoMode::kCheckboardRightFirst: |
| return os << "4 (checkboard, right eye first)"; |
| case StereoMode::kCheckboardLeftFirst: |
| return os << "5 (checkboard, left eye first)"; |
| case StereoMode::kRowInterleavedRightFirst: |
| return os << "6 (row interleaved, right eye first)"; |
| case StereoMode::kRowInterleavedLeftFirst: |
| return os << "7 (row interleaved, left eye first)"; |
| case StereoMode::kColumnInterleavedRightFirst: |
| return os << "8 (column interleaved, right eye first)"; |
| case StereoMode::kColumnInterleavedLeftFirst: |
| return os << "9 (column interleaved, left eye first)"; |
| case StereoMode::kAnaglyphCyanRed: |
| return os << "10 (anaglyph, cyan/red)"; |
| case StereoMode::kSideBySideRightFirst: |
| return os << "11 (side-by-side, right eye first)"; |
| case StereoMode::kAnaglyphGreenMagenta: |
| return os << "12 (anaglyph, green/magenta)"; |
| case StereoMode::kBlockLacedLeftFirst: |
| return os << "13 (block laced, left eye first)"; |
| case StereoMode::kBlockLacedRightFirst: |
| return os << "14 (block laced, right eye first)"; |
| default: |
| return PrintUnknownEnumValue(os, value); |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& os, DisplayUnit value) { |
| switch (value) { |
| case DisplayUnit::kPixels: |
| return os << "0 (pixels)"; |
| case DisplayUnit::kCentimeters: |
| return os << "1 (centimeters)"; |
| case DisplayUnit::kInches: |
| return os << "2 (inches)"; |
| case DisplayUnit::kDisplayAspectRatio: |
| return os << "3 (display aspect ratio)"; |
| default: |
| return PrintUnknownEnumValue(os, value); |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& os, AspectRatioType value) { |
| switch (value) { |
| case AspectRatioType::kFreeResizing: |
| return os << "0 (free resizing)"; |
| case AspectRatioType::kKeep: |
| return os << "1 (keep aspect ratio)"; |
| case AspectRatioType::kFixed: |
| return os << "2 (fixed)"; |
| default: |
| return PrintUnknownEnumValue(os, value); |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& os, AesSettingsCipherMode value) { |
| switch (value) { |
| case AesSettingsCipherMode::kCtr: |
| return os << "1 (CTR)"; |
| default: |
| return PrintUnknownEnumValue(os, value); |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& os, ContentEncAlgo value) { |
| switch (value) { |
| case ContentEncAlgo::kOnlySigned: |
| return os << "0 (only signed, not encrypted)"; |
| case ContentEncAlgo::kDes: |
| return os << "1 (DES)"; |
| case ContentEncAlgo::k3Des: |
| return os << "2 (3DES)"; |
| case ContentEncAlgo::kTwofish: |
| return os << "3 (Twofish)"; |
| case ContentEncAlgo::kBlowfish: |
| return os << "4 (Blowfish)"; |
| case ContentEncAlgo::kAes: |
| return os << "5 (AES)"; |
| default: |
| return PrintUnknownEnumValue(os, value); |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& os, ContentEncodingType value) { |
| switch (value) { |
| case ContentEncodingType::kCompression: |
| return os << "0 (compression)"; |
| case ContentEncodingType::kEncryption: |
| return os << "1 (encryption)"; |
| default: |
| return PrintUnknownEnumValue(os, value); |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& os, TrackType value) { |
| switch (value) { |
| case TrackType::kVideo: |
| return os << "1 (video)"; |
| case TrackType::kAudio: |
| return os << "2 (audio)"; |
| case TrackType::kComplex: |
| return os << "3 (complex)"; |
| case TrackType::kLogo: |
| return os << "16 (logo)"; |
| case TrackType::kSubtitle: |
| return os << "17 (subtitle)"; |
| case TrackType::kButtons: |
| return os << "18 (buttons)"; |
| case TrackType::kControl: |
| return os << "32 (control)"; |
| default: |
| return PrintUnknownEnumValue(os, value); |
| } |
| } |
| |
| // For binary elements, just print out its size. |
| std::ostream& operator<<(std::ostream& os, |
| const std::vector<std::uint8_t>& value) { |
| return os << '<' << value.size() << " bytes>"; |
| } |
| |
| class DemoCallback : public Callback { |
| public: |
| int indent = 0; |
| int spaces_per_indent = 2; |
| |
| void PrintElementMetadata(const std::string& name, |
| const ElementMetadata& metadata) { |
| // Since we aren't doing any seeking in this demo, we don't have to worry |
| // about kUnknownHeaderSize or kUnknownElementPosition when adding the |
| // position and sizes. |
| const std::uint64_t header_start = metadata.position; |
| const std::uint64_t header_end = header_start + metadata.header_size; |
| const std::uint64_t body_start = header_end; |
| std::cout << std::string(indent * spaces_per_indent, ' ') << name; |
| // The ContentEncAESSettings element has the longest name (out of all other |
| // master elements) at 21 characters. It's also the deepest master element |
| // at a level of 6. Insert enough whitespace so there's room for it. |
| std::cout << std::string(21 + 6 * spaces_per_indent - |
| indent * spaces_per_indent - name.size(), |
| ' ') |
| << " header: [" << header_start << ", " << header_end |
| << ") body: [" << body_start << ", "; |
| if (metadata.size != kUnknownElementSize) { |
| const std::uint64_t body_end = body_start + metadata.size; |
| std::cout << body_end; |
| } else { |
| std::cout << '?'; |
| } |
| std::cout << ")\n"; |
| } |
| |
| template <typename T> |
| void PrintMandatoryElement(const std::string& name, |
| const Element<T>& element) { |
| std::cout << std::string(indent * spaces_per_indent, ' ') << name; |
| if (!element.is_present()) { |
| std::cout << " (implicit)"; |
| } |
| std::cout << ": " << element.value() << '\n'; |
| } |
| |
| template <typename T> |
| void PrintMandatoryElement(const std::string& name, |
| const std::vector<Element<T>>& elements) { |
| for (const Element<T>& element : elements) { |
| PrintMandatoryElement(name, element); |
| } |
| } |
| |
| template <typename T> |
| void PrintOptionalElement(const std::string& name, |
| const Element<T>& element) { |
| if (element.is_present()) { |
| std::cout << std::string(indent * spaces_per_indent, ' ') << name << ": " |
| << element.value() << '\n'; |
| } |
| } |
| |
| template <typename T> |
| void PrintOptionalElement(const std::string& name, |
| const std::vector<Element<T>>& elements) { |
| for (const Element<T>& element : elements) { |
| PrintOptionalElement(name, element); |
| } |
| } |
| |
| void PrintMasterElement(const BlockAdditions& block_additions) { |
| PrintMasterElement("BlockMore", block_additions.block_mores); |
| } |
| |
| void PrintMasterElement(const BlockMore& block_more) { |
| PrintMandatoryElement("BlockAddID", block_more.id); |
| PrintMandatoryElement("BlockAdditional", block_more.data); |
| } |
| |
| void PrintMasterElement(const Slices& slices) { |
| PrintMasterElement("TimeSlice", slices.slices); |
| } |
| |
| void PrintMasterElement(const TimeSlice& time_slice) { |
| PrintOptionalElement("LaceNumber", time_slice.lace_number); |
| } |
| |
| void PrintMasterElement(const Video& video) { |
| PrintMandatoryElement("FlagInterlaced", video.interlaced); |
| PrintOptionalElement("StereoMode", video.stereo_mode); |
| PrintOptionalElement("AlphaMode", video.alpha_mode); |
| PrintMandatoryElement("PixelWidth", video.pixel_width); |
| PrintMandatoryElement("PixelHeight", video.pixel_height); |
| PrintOptionalElement("PixelCropBottom", video.pixel_crop_bottom); |
| PrintOptionalElement("PixelCropTop", video.pixel_crop_top); |
| PrintOptionalElement("PixelCropLeft", video.pixel_crop_left); |
| PrintOptionalElement("PixelCropRight", video.pixel_crop_right); |
| PrintOptionalElement("DisplayWidth", video.display_width); |
| PrintOptionalElement("DisplayHeight", video.display_height); |
| PrintOptionalElement("DisplayUnit", video.display_unit); |
| PrintOptionalElement("AspectRatioType", video.aspect_ratio_type); |
| PrintOptionalElement("FrameRate", video.frame_rate); |
| PrintMasterElement("Colour", video.colour); |
| PrintMasterElement("Projection", video.projection); |
| } |
| |
| void PrintMasterElement(const Colour& colour) { |
| PrintOptionalElement("MatrixCoefficients", colour.matrix_coefficients); |
| PrintOptionalElement("BitsPerChannel", colour.bits_per_channel); |
| PrintOptionalElement("ChromaSubsamplingHorz", colour.chroma_subsampling_x); |
| PrintOptionalElement("ChromaSubsamplingVert", colour.chroma_subsampling_y); |
| PrintOptionalElement("CbSubsamplingHorz", colour.cb_subsampling_x); |
| PrintOptionalElement("CbSubsamplingVert", colour.cb_subsampling_y); |
| PrintOptionalElement("ChromaSitingHorz", colour.chroma_siting_x); |
| PrintOptionalElement("ChromaSitingVert", colour.chroma_siting_y); |
| PrintOptionalElement("Range", colour.range); |
| PrintOptionalElement("TransferCharacteristics", |
| colour.transfer_characteristics); |
| PrintOptionalElement("Primaries", colour.primaries); |
| PrintOptionalElement("MaxCLL", colour.max_cll); |
| PrintOptionalElement("MaxFALL", colour.max_fall); |
| PrintMasterElement("MasteringMetadata", colour.mastering_metadata); |
| } |
| |
| void PrintMasterElement(const MasteringMetadata& mastering_metadata) { |
| PrintOptionalElement("PrimaryRChromaticityX", |
| mastering_metadata.primary_r_chromaticity_x); |
| PrintOptionalElement("PrimaryRChromaticityY", |
| mastering_metadata.primary_r_chromaticity_y); |
| PrintOptionalElement("PrimaryGChromaticityX", |
| mastering_metadata.primary_g_chromaticity_x); |
| PrintOptionalElement("PrimaryGChromaticityY", |
| mastering_metadata.primary_g_chromaticity_y); |
| PrintOptionalElement("PrimaryBChromaticityX", |
| mastering_metadata.primary_b_chromaticity_x); |
| PrintOptionalElement("PrimaryBChromaticityY", |
| mastering_metadata.primary_b_chromaticity_y); |
| PrintOptionalElement("WhitePointChromaticityX", |
| mastering_metadata.white_point_chromaticity_x); |
| PrintOptionalElement("WhitePointChromaticityY", |
| mastering_metadata.white_point_chromaticity_y); |
| PrintOptionalElement("LuminanceMax", mastering_metadata.luminance_max); |
| PrintOptionalElement("LuminanceMin", mastering_metadata.luminance_min); |
| } |
| |
| void PrintMasterElement(const Projection& projection) { |
| PrintMandatoryElement("ProjectionType", projection.type); |
| PrintOptionalElement("ProjectionPrivate", projection.projection_private); |
| PrintMandatoryElement("ProjectionPoseYaw", projection.pose_yaw); |
| PrintMandatoryElement("ProjectionPosePitch", projection.pose_pitch); |
| PrintMandatoryElement("ProjectionPoseRoll", projection.pose_roll); |
| } |
| |
| void PrintMasterElement(const Audio& audio) { |
| PrintMandatoryElement("SamplingFrequency", audio.sampling_frequency); |
| PrintOptionalElement("OutputSamplingFrequency", audio.output_frequency); |
| PrintMandatoryElement("Channels", audio.channels); |
| PrintOptionalElement("BitDepth", audio.bit_depth); |
| } |
| |
| void PrintMasterElement(const ContentEncodings& content_encodings) { |
| PrintMasterElement("ContentEncoding", content_encodings.encodings); |
| } |
| |
| void PrintMasterElement(const ContentEncoding& content_encoding) { |
| PrintMandatoryElement("ContentEncodingOrder", content_encoding.order); |
| PrintMandatoryElement("ContentEncodingScope", content_encoding.scope); |
| PrintMandatoryElement("ContentEncodingType", content_encoding.type); |
| PrintMasterElement("ContentEncryption", content_encoding.encryption); |
| } |
| |
| void PrintMasterElement(const ContentEncryption& content_encryption) { |
| PrintOptionalElement("ContentEncAlgo", content_encryption.algorithm); |
| PrintOptionalElement("ContentEncKeyID", content_encryption.key_id); |
| PrintMasterElement("ContentEncAESSettings", |
| content_encryption.aes_settings); |
| } |
| |
| void PrintMasterElement( |
| const ContentEncAesSettings& content_enc_aes_settings) { |
| PrintMandatoryElement("AESSettingsCipherMode", |
| content_enc_aes_settings.aes_settings_cipher_mode); |
| } |
| |
| void PrintMasterElement(const CueTrackPositions& cue_track_positions) { |
| PrintMandatoryElement("CueTrack", cue_track_positions.track); |
| PrintMandatoryElement("CueClusterPosition", |
| cue_track_positions.cluster_position); |
| PrintOptionalElement("CueRelativePosition", |
| cue_track_positions.relative_position); |
| PrintOptionalElement("CueDuration", cue_track_positions.duration); |
| PrintOptionalElement("CueBlockNumber", cue_track_positions.block_number); |
| } |
| |
| void PrintMasterElement(const ChapterAtom& chapter_atom) { |
| PrintMandatoryElement("ChapterUID", chapter_atom.uid); |
| PrintOptionalElement("ChapterStringUID", chapter_atom.string_uid); |
| PrintMandatoryElement("ChapterTimeStart", chapter_atom.time_start); |
| PrintOptionalElement("ChapterTimeEnd", chapter_atom.time_end); |
| PrintMasterElement("ChapterDisplay", chapter_atom.displays); |
| PrintMasterElement("ChapterAtom", chapter_atom.atoms); |
| } |
| |
| void PrintMasterElement(const ChapterDisplay& chapter_display) { |
| PrintMandatoryElement("ChapString", chapter_display.string); |
| PrintMandatoryElement("ChapLanguage", chapter_display.languages); |
| PrintOptionalElement("ChapCountry", chapter_display.countries); |
| } |
| |
| void PrintMasterElement(const Targets& targets) { |
| PrintOptionalElement("TargetTypeValue", targets.type_value); |
| PrintOptionalElement("TargetType", targets.type); |
| PrintMandatoryElement("TagTrackUID", targets.track_uids); |
| } |
| |
| void PrintMasterElement(const SimpleTag& simple_tag) { |
| PrintMandatoryElement("TagName", simple_tag.name); |
| PrintMandatoryElement("TagLanguage", simple_tag.language); |
| PrintMandatoryElement("TagDefault", simple_tag.is_default); |
| PrintOptionalElement("TagString", simple_tag.string); |
| PrintOptionalElement("TagBinary", simple_tag.binary); |
| PrintMasterElement("SimpleTag", simple_tag.tags); |
| } |
| |
| // When printing a master element that's wrapped in Element<>, peel off the |
| // Element<> wrapper and print the underlying master element if it's present. |
| template <typename T> |
| void PrintMasterElement(const std::string& name, const Element<T>& element) { |
| if (element.is_present()) { |
| std::cout << std::string(indent * spaces_per_indent, ' ') << name << "\n"; |
| ++indent; |
| PrintMasterElement(element.value()); |
| --indent; |
| } |
| } |
| |
| template <typename T> |
| void PrintMasterElement(const std::string& name, |
| const std::vector<Element<T>>& elements) { |
| for (const Element<T>& element : elements) { |
| PrintMasterElement(name, element); |
| } |
| } |
| |
| template <typename T> |
| void PrintValue(const std::string& name, const T& value) { |
| std::cout << std::string(indent * spaces_per_indent, ' ') << name << ": " |
| << value << '\n'; |
| } |
| |
| Status OnElementBegin(const ElementMetadata& metadata, |
| Action* action) override { |
| // Print out metadata for some level 1 elements that don't have explicit |
| // callbacks. |
| switch (metadata.id) { |
| case Id::kSeekHead: |
| indent = 1; |
| PrintElementMetadata("SeekHead", metadata); |
| break; |
| case Id::kTracks: |
| indent = 1; |
| PrintElementMetadata("Tracks", metadata); |
| break; |
| case Id::kCues: |
| indent = 1; |
| PrintElementMetadata("Cues", metadata); |
| break; |
| case Id::kChapters: |
| indent = 1; |
| PrintElementMetadata("Chapters", metadata); |
| break; |
| case Id::kTags: |
| indent = 1; |
| PrintElementMetadata("Tags", metadata); |
| break; |
| default: |
| break; |
| } |
| |
| *action = Action::kRead; |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnUnknownElement(const ElementMetadata& metadata, Reader* reader, |
| std::uint64_t* bytes_remaining) override { |
| // Output unknown elements without any indentation because we aren't |
| // tracking which element contains them. |
| int original_indent = indent; |
| indent = 0; |
| PrintElementMetadata("UNKNOWN_ELEMENT!", metadata); |
| indent = original_indent; |
| // The base class's implementation will just skip the element via |
| // Reader::Skip(). |
| return Callback::OnUnknownElement(metadata, reader, bytes_remaining); |
| } |
| |
| Status OnEbml(const ElementMetadata& metadata, const Ebml& ebml) override { |
| indent = 0; |
| PrintElementMetadata("EBML", metadata); |
| indent = 1; |
| PrintMandatoryElement("EBMLVersion", ebml.ebml_version); |
| PrintMandatoryElement("EBMLReadVersion", ebml.ebml_read_version); |
| PrintMandatoryElement("EBMLMaxIDLength", ebml.ebml_max_id_length); |
| PrintMandatoryElement("EBMLMaxSizeLength", ebml.ebml_max_size_length); |
| PrintMandatoryElement("DocType", ebml.doc_type); |
| PrintMandatoryElement("DocTypeVersion", ebml.doc_type_version); |
| PrintMandatoryElement("DocTypeReadVersion", ebml.doc_type_read_version); |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnVoid(const ElementMetadata& metadata, Reader* reader, |
| std::uint64_t* bytes_remaining) override { |
| // Output Void elements without any indentation because we aren't tracking |
| // which element contains them. |
| int original_indent = indent; |
| indent = 0; |
| PrintElementMetadata("Void", metadata); |
| indent = original_indent; |
| // The base class's implementation will just skip the element via |
| // Reader::Skip(). |
| return Callback::OnVoid(metadata, reader, bytes_remaining); |
| } |
| |
| Status OnSegmentBegin(const ElementMetadata& metadata, |
| Action* action) override { |
| indent = 0; |
| PrintElementMetadata("Segment", metadata); |
| indent = 1; |
| *action = Action::kRead; |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnSeek(const ElementMetadata& metadata, const Seek& seek) override { |
| indent = 2; |
| PrintElementMetadata("Seek", metadata); |
| indent = 3; |
| PrintMandatoryElement("SeekID", seek.id); |
| PrintMandatoryElement("SeekPosition", seek.position); |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnInfo(const ElementMetadata& metadata, const Info& info) override { |
| indent = 1; |
| PrintElementMetadata("Info", metadata); |
| indent = 2; |
| PrintMandatoryElement("TimecodeScale", info.timecode_scale); |
| PrintOptionalElement("Duration", info.duration); |
| PrintOptionalElement("DateUTC", info.date_utc); |
| PrintOptionalElement("Title", info.title); |
| PrintOptionalElement("MuxingApp", info.muxing_app); |
| PrintOptionalElement("WritingApp", info.writing_app); |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnClusterBegin(const ElementMetadata& metadata, const Cluster& cluster, |
| Action* action) override { |
| indent = 1; |
| PrintElementMetadata("Cluster", metadata); |
| // A properly muxed file will have Timecode and PrevSize first before any |
| // SimpleBlock or BlockGroups. The parser takes advantage of this and delays |
| // calling OnClusterBegin() until it hits the first SimpleBlock or |
| // BlockGroup child (or the Cluster ends if it's empty). It's possible for |
| // an improperly muxed file to have Timecode or PrevSize after the first |
| // block, in which case they'll be absent here and may be accessed in |
| // OnClusterEnd() when the Cluster and all its children have been fully |
| // parsed. In this demo we assume the file has been properly muxed. |
| indent = 2; |
| PrintMandatoryElement("Timecode", cluster.timecode); |
| PrintOptionalElement("PrevSize", cluster.previous_size); |
| *action = Action::kRead; |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnSimpleBlockBegin(const ElementMetadata& metadata, |
| const SimpleBlock& simple_block, |
| Action* action) override { |
| indent = 2; |
| PrintElementMetadata("SimpleBlock", metadata); |
| indent = 3; |
| PrintValue("track number", simple_block.track_number); |
| PrintValue("frames", simple_block.num_frames); |
| PrintValue("timecode", simple_block.timecode); |
| PrintValue("lacing", simple_block.lacing); |
| std::string flags = (simple_block.is_visible) ? "visible" : "invisible"; |
| if (simple_block.is_key_frame) |
| flags += ", key frame"; |
| if (simple_block.is_discardable) |
| flags += ", discardable"; |
| PrintValue("flags", flags); |
| *action = Action::kRead; |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnSimpleBlockEnd(const ElementMetadata& /* metadata */, |
| const SimpleBlock& /* simple_block */) override { |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnBlockGroupBegin(const ElementMetadata& metadata, |
| Action* /* action */) override { |
| indent = 2; |
| PrintElementMetadata("BlockGroup", metadata); |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnBlockBegin(const ElementMetadata& metadata, const Block& block, |
| Action* action) override { |
| indent = 3; |
| PrintElementMetadata("Block", metadata); |
| indent = 4; |
| PrintValue("track number", block.track_number); |
| PrintValue("frames", block.num_frames); |
| PrintValue("timecode", block.timecode); |
| PrintValue("lacing", block.lacing); |
| PrintValue("flags", (block.is_visible) ? "visible" : "invisible"); |
| *action = Action::kRead; |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnBlockEnd(const ElementMetadata& /* metadata */, |
| const Block& /* block */) override { |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnBlockGroupEnd(const ElementMetadata& /* metadata */, |
| const BlockGroup& block_group) override { |
| if (block_group.virtual_block.is_present()) { |
| std::cout << std::string(indent * spaces_per_indent, ' ') |
| << "BlockVirtual\n"; |
| indent = 4; |
| PrintValue("track number", |
| block_group.virtual_block.value().track_number); |
| PrintValue("timecode", block_group.virtual_block.value().timecode); |
| } |
| indent = 3; |
| PrintMasterElement("BlockAdditions", block_group.additions); |
| PrintOptionalElement("BlockDuration", block_group.duration); |
| PrintOptionalElement("ReferenceBlock", block_group.references); |
| PrintOptionalElement("DiscardPadding", block_group.discard_padding); |
| PrintMasterElement("Slices", block_group.slices); |
| // BlockGroup::block has been set, but we've already printed it in |
| // OnBlockBegin(). |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnFrame(const FrameMetadata& metadata, Reader* reader, |
| std::uint64_t* bytes_remaining) override { |
| PrintValue("frame byte range", |
| '[' + std::to_string(metadata.position) + ", " + |
| std::to_string(metadata.position + metadata.size) + ')'); |
| // The base class's implementation will just skip the frame via |
| // Reader::Skip(). |
| return Callback::OnFrame(metadata, reader, bytes_remaining); |
| } |
| |
| Status OnClusterEnd(const ElementMetadata& /* metadata */, |
| const Cluster& /* cluster */) override { |
| // The Cluster and all its children have been fully parsed at this point. If |
| // the file wasn't properly muxed and Timecode or PrevSize were missing in |
| // OnClusterBegin(), they'll be set here (if the Cluster contained them). In |
| // this demo we already handled them, though. |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnTrackEntry(const ElementMetadata& metadata, |
| const TrackEntry& track_entry) override { |
| indent = 2; |
| PrintElementMetadata("TrackEntry", metadata); |
| indent = 3; |
| PrintMandatoryElement("TrackNumber", track_entry.track_number); |
| PrintMandatoryElement("TrackUID", track_entry.track_uid); |
| PrintMandatoryElement("TrackType", track_entry.track_type); |
| PrintMandatoryElement("FlagEnabled", track_entry.is_enabled); |
| PrintMandatoryElement("FlagDefault", track_entry.is_default); |
| PrintMandatoryElement("FlagForced", track_entry.is_forced); |
| PrintMandatoryElement("FlagLacing", track_entry.uses_lacing); |
| PrintOptionalElement("DefaultDuration", track_entry.default_duration); |
| PrintOptionalElement("Name", track_entry.name); |
| PrintOptionalElement("Language", track_entry.language); |
| PrintMandatoryElement("CodecID", track_entry.codec_id); |
| PrintOptionalElement("CodecPrivate", track_entry.codec_private); |
| PrintOptionalElement("CodecName", track_entry.codec_name); |
| PrintOptionalElement("CodecDelay", track_entry.codec_delay); |
| PrintMandatoryElement("SeekPreRoll", track_entry.seek_pre_roll); |
| PrintMasterElement("Video", track_entry.video); |
| PrintMasterElement("Audio", track_entry.audio); |
| PrintMasterElement("ContentEncodings", track_entry.content_encodings); |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnCuePoint(const ElementMetadata& metadata, |
| const CuePoint& cue_point) override { |
| indent = 2; |
| PrintElementMetadata("CuePoint", metadata); |
| indent = 3; |
| PrintMandatoryElement("CueTime", cue_point.time); |
| PrintMasterElement("CueTrackPositions", cue_point.cue_track_positions); |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnEditionEntry(const ElementMetadata& metadata, |
| const EditionEntry& edition_entry) override { |
| indent = 2; |
| PrintElementMetadata("EditionEntry", metadata); |
| indent = 3; |
| PrintMasterElement("ChapterAtom", edition_entry.atoms); |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnTag(const ElementMetadata& metadata, const Tag& tag) override { |
| indent = 2; |
| PrintElementMetadata("Tag", metadata); |
| indent = 3; |
| PrintMasterElement("Targets", tag.targets); |
| PrintMasterElement("SimpleTag", tag.tags); |
| return Status(Status::kOkCompleted); |
| } |
| |
| Status OnSegmentEnd(const ElementMetadata& /* metadata */) override { |
| return Status(Status::kOkCompleted); |
| } |
| }; |
| |
| int main(int argc, char* argv[]) { |
| if ((argc != 1 && argc != 2) || |
| (argc == 2 && argv[1] == std::string("--help"))) { |
| std::cerr << "Usage:\n" |
| << argv[0] << " [path-to-webm-file]\n\n" |
| << "Prints info for the WebM file specified in the command line. " |
| "If no file is\n" |
| << "specified, stdin is used as input.\n"; |
| return EXIT_FAILURE; |
| } |
| |
| FILE* file = (argc == 2) ? std::fopen(argv[1], "rb") : |
| std::freopen(nullptr, "rb", stdin); |
| if (!file) { |
| std::cerr << "File cannot be opened\n"; |
| return EXIT_FAILURE; |
| } |
| |
| FileReader reader(file); |
| DemoCallback callback; |
| WebmParser parser; |
| Status status = parser.Feed(&callback, &reader); |
| if (!status.completed_ok()) { |
| std::cerr << "Parsing error; status code: " << status.code << '\n'; |
| return EXIT_FAILURE; |
| } |
| |
| return 0; |
| } |