mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2025-01-03 16:54:22 +00:00
e11b3452a5
The return value of that function is an unsigned 64bit integer. However, Matroska files can have negative timestamps as the relative timestamp fields in both the SimpleBlock and the BlockGroup structures are signed. Combined with a low ClusterTimecode element this can result in negative timestamps.
1884 lines
78 KiB
C++
1884 lines
78 KiB
C++
/*
|
|
mkvinfo -- utility for gathering information about Matroska files
|
|
|
|
Distributed under the GPL v2
|
|
see the file COPYING for details
|
|
or visit http://www.gnu.org/copyleft/gpl.html
|
|
|
|
retrieves and displays information about a Matroska file
|
|
|
|
Written by Moritz Bunkus <moritz@bunkus.org>.
|
|
*/
|
|
|
|
#include "common/common_pch.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <typeinfo>
|
|
|
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
|
|
|
#include <ebml/EbmlHead.h>
|
|
#include <ebml/EbmlSubHead.h>
|
|
#include <ebml/EbmlStream.h>
|
|
#include <ebml/EbmlVoid.h>
|
|
#include <ebml/EbmlCrc32.h>
|
|
#include <matroska/FileKax.h>
|
|
|
|
#include <matroska/KaxAttached.h>
|
|
#include <matroska/KaxAttachments.h>
|
|
#include <matroska/KaxBlock.h>
|
|
#include <matroska/KaxBlockData.h>
|
|
#include <matroska/KaxChapters.h>
|
|
#include <matroska/KaxCluster.h>
|
|
#include <matroska/KaxClusterData.h>
|
|
#include <matroska/KaxContentEncoding.h>
|
|
#include <matroska/KaxCues.h>
|
|
#include <matroska/KaxCuesData.h>
|
|
#include <matroska/KaxInfo.h>
|
|
#include <matroska/KaxInfoData.h>
|
|
#include <matroska/KaxSeekHead.h>
|
|
#include <matroska/KaxSegment.h>
|
|
#include <matroska/KaxTags.h>
|
|
#include <matroska/KaxTag.h>
|
|
#include <matroska/KaxTracks.h>
|
|
#include <matroska/KaxTrackEntryData.h>
|
|
#include <matroska/KaxTrackAudio.h>
|
|
#include <matroska/KaxTrackVideo.h>
|
|
#include <matroska/KaxVersion.h>
|
|
|
|
#if !defined(MATROSKA_VERSION)
|
|
#define MATROSKA_VERSION 2
|
|
#endif
|
|
|
|
#include "avilib.h"
|
|
#include "common/chapters/chapters.h"
|
|
#include "common/checksums/base.h"
|
|
#include "common/codec.h"
|
|
#include "common/command_line.h"
|
|
#include "common/date_time.h"
|
|
#include "common/ebml.h"
|
|
#include "common/endian.h"
|
|
#include "common/fourcc.h"
|
|
#include "common/hevc.h"
|
|
#include "common/kax_file.h"
|
|
#include "common/math.h"
|
|
#include "common/mm_io.h"
|
|
#include "common/mm_io_x.h"
|
|
#include "common/mpeg4_p10.h"
|
|
#include "common/stereo_mode.h"
|
|
#include "common/strings/editing.h"
|
|
#include "common/strings/formatting.h"
|
|
#include "common/translation.h"
|
|
#include "common/version.h"
|
|
#include "common/xml/ebml_chapters_converter.h"
|
|
#include "common/xml/ebml_tags_converter.h"
|
|
#include "info/mkvinfo.h"
|
|
#include "info/info_cli_parser.h"
|
|
|
|
using namespace libmatroska;
|
|
|
|
struct kax_track_t {
|
|
uint64_t tnum, tuid;
|
|
char type;
|
|
int64_t default_duration;
|
|
size_t mkvmerge_track_id;
|
|
|
|
kax_track_t();
|
|
};
|
|
|
|
struct track_info_t {
|
|
int64_t m_size, m_min_timecode, m_max_timecode, m_blocks, m_blocks_by_ref_num[3], m_add_duration_for_n_packets;
|
|
|
|
track_info_t();
|
|
bool min_timecode_unset();
|
|
bool max_timecode_unset();
|
|
};
|
|
|
|
kax_track_t::kax_track_t()
|
|
: tnum(0)
|
|
, tuid(0)
|
|
, type(' ')
|
|
, default_duration(0)
|
|
, mkvmerge_track_id(0)
|
|
{
|
|
}
|
|
using kax_track_cptr = std::shared_ptr<kax_track_t>;
|
|
|
|
track_info_t::track_info_t()
|
|
: m_size(0)
|
|
, m_min_timecode(LLONG_MAX)
|
|
, m_max_timecode(LLONG_MIN)
|
|
, m_blocks(0)
|
|
, m_add_duration_for_n_packets(0)
|
|
{
|
|
memset(m_blocks_by_ref_num, 0, sizeof(int64_t) * 3);
|
|
}
|
|
|
|
bool
|
|
track_info_t::min_timecode_unset() {
|
|
return LLONG_MAX == m_min_timecode;
|
|
}
|
|
|
|
bool
|
|
track_info_t::max_timecode_unset() {
|
|
return LLONG_MIN == m_max_timecode;
|
|
}
|
|
|
|
std::vector<kax_track_cptr> s_tracks;
|
|
std::map<unsigned int, kax_track_cptr> s_tracks_by_number;
|
|
std::map<unsigned int, track_info_t> s_track_info;
|
|
options_c g_options;
|
|
static uint64_t s_tc_scale = TIMECODE_SCALE;
|
|
std::vector<boost::format> g_common_boost_formats;
|
|
size_t s_mkvmerge_track_id = 0;
|
|
|
|
#define BF_DO(n) g_common_boost_formats[n]
|
|
#define BF_ADD(s) g_common_boost_formats.push_back(boost::format(s))
|
|
#define BF_SHOW_UNKNOWN_ELEMENT BF_DO( 0)
|
|
#define BF_EBMLVOID BF_DO( 1)
|
|
#define BF_FORMAT_BINARY_1 BF_DO( 2)
|
|
#define BF_FORMAT_BINARY_2 BF_DO( 3)
|
|
#define BF_BLOCK_GROUP_BLOCK_BASICS BF_DO( 4)
|
|
#define BF_BLOCK_GROUP_BLOCK_ADLER BF_DO( 3) // Intentional -- same format.
|
|
#define BF_BLOCK_GROUP_BLOCK_FRAME BF_DO( 5)
|
|
#define BF_BLOCK_GROUP_DURATION BF_DO( 6)
|
|
#define BF_BLOCK_GROUP_REFERENCE_1 BF_DO( 7)
|
|
#define BF_BLOCK_GROUP_REFERENCE_2 BF_DO( 8)
|
|
#define BF_BLOCK_GROUP_REFERENCE_PRIORITY BF_DO( 9)
|
|
#define BF_BLOCK_GROUP_VIRTUAL BF_DO(10)
|
|
#define BF_BLOCK_GROUP_REFERENCE_VIRTUAL BF_DO(11)
|
|
#define BF_BLOCK_GROUP_ADD_ID BF_DO(12)
|
|
#define BF_BLOCK_GROUP_ADDITIONAL BF_DO(13)
|
|
#define BF_BLOCK_GROUP_SLICE_LACE BF_DO(14)
|
|
#define BF_BLOCK_GROUP_SLICE_FRAME BF_DO(15)
|
|
#define BF_BLOCK_GROUP_SLICE_DELAY BF_DO(16)
|
|
#define BF_BLOCK_GROUP_SLICE_DURATION BF_DO(17)
|
|
#define BF_BLOCK_GROUP_SLICE_ADD_ID BF_DO(18)
|
|
#define BF_BLOCK_GROUP_SUMMARY_POSITION BF_DO(19)
|
|
#define BF_BLOCK_GROUP_SUMMARY_WITH_DURATION BF_DO(20)
|
|
#define BF_BLOCK_GROUP_SUMMARY_NO_DURATION BF_DO(21)
|
|
#define BF_BLOCK_GROUP_SUMMARY_V2 BF_DO(22)
|
|
#define BF_SIMPLE_BLOCK_BASICS BF_DO(23)
|
|
#define BF_SIMPLE_BLOCK_ADLER BF_DO( 3) // Intentional -- same format.
|
|
#define BF_SIMPLE_BLOCK_FRAME BF_DO(24)
|
|
#define BF_SIMPLE_BLOCK_POSITION BF_DO(19) // Intentional -- same format.
|
|
#define BF_SIMPLE_BLOCK_SUMMARY BF_DO(25)
|
|
#define BF_SIMPLE_BLOCK_SUMMARY_V2 BF_DO(26)
|
|
#define BF_CLUSTER_TIMECODE BF_DO(27)
|
|
#define BF_CLUSTER_POSITION BF_DO(28)
|
|
#define BF_CLUSTER_PREVIOUS_SIZE BF_DO(29)
|
|
#define BF_CODEC_STATE BF_DO(30)
|
|
#define BF_AT BF_DO(31)
|
|
#define BF_SIZE BF_DO(32)
|
|
#define BF_BLOCK_GROUP_DISCARD_PADDING BF_DO(33)
|
|
#define BF_AT_HEX BF_DO(34)
|
|
|
|
void
|
|
init_common_boost_formats() {
|
|
g_common_boost_formats.clear();
|
|
BF_ADD(Y("(Unknown element: %1%; ID: 0x%2% size: %3%)")); // 0 -- BF_SHOW_UNKNOWN_ELEMENT
|
|
BF_ADD(Y("EbmlVoid (size: %1%)")); // 1 -- BF_EBMLVOID
|
|
BF_ADD(Y("length %1%, data: %2%")); // 2 -- BF_FORMAT_BINARY_1
|
|
BF_ADD(Y(" (adler: 0x%|1$08x|)")); // 3 -- BF_FORMAT_BINARY_2
|
|
BF_ADD(Y("Block (track number %1%, %2% frame(s), timecode %|3$.3f|s = %4%)")); // 4 -- BF_BLOCK_GROUP_BLOCK_SUMMARY
|
|
BF_ADD(Y("Frame with size %1%%2%%3%")); // 5 -- BF_BLOCK_GROUP_BLOCK_FRAME
|
|
BF_ADD(Y("Block duration: %1%.%|2$06d|ms")); // 6 -- BF_BLOCK_GROUP_DURATION
|
|
BF_ADD(Y("Reference block: -%1%.%|2$06d|ms")); // 7 -- BF_BLOCK_GROUP_REFERENCE_1
|
|
BF_ADD(Y("Reference block: %1%.%|2$06d|ms")); // 8 -- BF_BLOCK_GROUP_REFERENCE_2
|
|
BF_ADD(Y("Reference priority: %1%")); // 9 -- BF_BLOCK_GROUP_REFERENCE_PRIORITY
|
|
BF_ADD(Y("Block virtual: %1%")); // 10 -- BF_BLOCK_GROUP_VIRTUAL
|
|
BF_ADD(Y("Reference virtual: %1%")); // 11 -- BF_BLOCK_GROUP_REFERENCE_VIRTUAL
|
|
BF_ADD(Y("AdditionalID: %1%")); // 12 -- BF_BLOCK_GROUP_ADD_ID
|
|
BF_ADD(Y("Block additional: %1%")); // 13 -- BF_BLOCK_GROUP_ADDITIONAL
|
|
BF_ADD(Y("Lace number: %1%")); // 14 -- BF_BLOCK_GROUP_SLICE_LACE
|
|
BF_ADD(Y("Frame number: %1%")); // 15 -- BF_BLOCK_GROUP_SLICE_FRAME
|
|
BF_ADD(Y("Delay: %|1$.3f|ms")); // 16 -- BF_BLOCK_GROUP_SLICE_DELAY
|
|
BF_ADD(Y("Duration: %|1$.3f|ms")); // 17 -- BF_BLOCK_GROUP_SLICE_DURATION
|
|
BF_ADD(Y("Block additional ID: %1%")); // 18 -- BF_BLOCK_GROUP_SLICE_ADD_ID
|
|
BF_ADD(Y(", position %1%")); // 19 -- BF_BLOCK_GROUP_SUMMARY_POSITION
|
|
BF_ADD(Y("%1% frame, track %2%, timecode %3% (%4%), duration %|5$.3f|, size %6%, adler 0x%|7$08x|%8%%9%\n")); // 20 -- BF_BLOCK_GROUP_SUMMARY_WITH_DURATION
|
|
BF_ADD(Y("%1% frame, track %2%, timecode %3% (%4%), size %5%, adler 0x%|6$08x|%7%%8%\n")); // 21 -- BF_BLOCK_GROUP_SUMMARY_NO_DURATION
|
|
BF_ADD(Y("[%1% frame for track %2%, timecode %3%]")); // 22 -- BF_BLOCK_GROUP_SUMMARY_V2
|
|
BF_ADD(Y("SimpleBlock (%1%track number %2%, %3% frame(s), timecode %|4$.3f|s = %5%)")); // 23 -- BF_SIMPLE_BLOCK_BASICS
|
|
BF_ADD(Y("Frame with size %1%%2%%3%")); // 24 -- BF_SIMPLE_BLOCK_FRAME
|
|
BF_ADD(Y("%1% frame, track %2%, timecode %3% (%4%), size %5%, adler 0x%|6$08x|%7%\n")); // 25 -- BF_SIMPLE_BLOCK_SUMMARY
|
|
BF_ADD(Y("[%1% frame for track %2%, timecode %3%]")); // 26 -- BF_SIMPLE_BLOCK_SUMMARY_V2
|
|
BF_ADD(Y("Cluster timecode: %|1$.3f|s")); // 27 -- BF_CLUSTER_TIMECODE
|
|
BF_ADD(Y("Cluster position: %1%")); // 28 -- BF_CLUSTER_POSITION
|
|
BF_ADD(Y("Cluster previous size: %1%")); // 29 -- BF_CLUSTER_PREVIOUS_SIZE
|
|
BF_ADD(Y("Codec state: %1%")); // 30 -- BF_CODEC_STATE
|
|
BF_ADD(Y(" at %1%")); // 31 -- BF_AT
|
|
BF_ADD(Y(" size %1%")); // 32 -- BF_SIZE
|
|
BF_ADD(Y("Discard padding: %|1$.3f|ms (%2%ns)")); // 33 -- BF_BLOCK_GROUP_DISCARD_PADDING
|
|
BF_ADD(Y(" at 0x%|1$x|")); // 34 -- BF_AT_HEX
|
|
}
|
|
|
|
std::string
|
|
create_element_text(const std::string &text,
|
|
int64_t position,
|
|
int64_t size) {
|
|
std::string additional_text;
|
|
|
|
if ((1 < g_options.m_verbose) && (0 <= position))
|
|
additional_text += ((g_options.m_hex_positions ? BF_AT_HEX : BF_AT) % position).str();
|
|
|
|
if (g_options.m_show_size && (-1 != size)) {
|
|
if (-2 != size)
|
|
additional_text += (BF_SIZE % size).str();
|
|
else
|
|
additional_text += Y(" size is unknown");
|
|
}
|
|
|
|
return text + additional_text;
|
|
}
|
|
|
|
void
|
|
add_track(kax_track_cptr t) {
|
|
s_tracks.push_back(t);
|
|
s_tracks_by_number[t->tnum] = t;
|
|
}
|
|
|
|
kax_track_t *
|
|
find_track(int tnum) {
|
|
return s_tracks_by_number[tnum].get();
|
|
}
|
|
|
|
#define show_error(error) ui_show_error(error)
|
|
#define show_warning(l, f) _show_element(nullptr, nullptr, false, l, f)
|
|
#define show_unknown_element(e, l) _show_unknown_element(es, e, l)
|
|
#define show_element(e, l, s) _show_element(e, es, false, l, s)
|
|
|
|
static void _show_element(EbmlElement *l, EbmlStream *es, bool skip, int level, const std::string &info);
|
|
|
|
static void
|
|
_show_unknown_element(EbmlStream *es,
|
|
EbmlElement *e,
|
|
int level) {
|
|
static boost::format s_bf_show_unknown_element("%|1$02x|");
|
|
|
|
int i;
|
|
std::string element_id;
|
|
for (i = EBML_ID_LENGTH(static_cast<const EbmlId &>(*e)) - 1; 0 <= i; --i)
|
|
element_id += (s_bf_show_unknown_element % ((EBML_ID_VALUE(static_cast<const EbmlId &>(*e)) >> (i * 8)) & 0xff)).str();
|
|
|
|
std::string s = (BF_SHOW_UNKNOWN_ELEMENT % EBML_NAME(e) % element_id % (e->GetSize() + e->HeadSize())).str();
|
|
_show_element(e, es, true, level, s);
|
|
}
|
|
|
|
static void
|
|
_show_element(EbmlElement *l,
|
|
EbmlStream *es,
|
|
bool skip,
|
|
int level,
|
|
const std::string &info) {
|
|
if (g_options.m_show_summary)
|
|
return;
|
|
|
|
ui_show_element(level, info,
|
|
!l ? -1
|
|
: static_cast<int64_t>(l->GetElementPosition()),
|
|
!l ? -1
|
|
: !l->IsFiniteSize() ? -2
|
|
: static_cast<int64_t>(l->GetSizeLength() + EBML_ID_LENGTH(static_cast<const EbmlId &>(*l)) + l->GetSize()));
|
|
|
|
if (!l || !skip)
|
|
return;
|
|
|
|
// Dump unknown elements recursively.
|
|
auto *m = dynamic_cast<EbmlMaster *>(l);
|
|
if (m)
|
|
for (auto child : *m)
|
|
show_unknown_element(child, level + 1);
|
|
|
|
l->SkipData(*es, EBML_CONTEXT(l));
|
|
}
|
|
|
|
inline void
|
|
_show_element(EbmlElement *l,
|
|
EbmlStream *es,
|
|
bool skip,
|
|
int level,
|
|
const boost::format &info) {
|
|
_show_element(l, es, skip, level, info.str());
|
|
}
|
|
|
|
static std::string
|
|
create_hexdump(const unsigned char *buf,
|
|
int size) {
|
|
static boost::format s_bf_create_hexdump(" %|1$02x|");
|
|
|
|
std::string hex(" hexdump");
|
|
int bmax = std::min(size, g_options.m_hexdump_max_size);
|
|
int b;
|
|
|
|
for (b = 0; b < bmax; ++b)
|
|
hex += (s_bf_create_hexdump % static_cast<int>(buf[b])).str();
|
|
|
|
return hex;
|
|
}
|
|
|
|
std::string
|
|
create_codec_dependent_private_info(KaxCodecPrivate &c_priv,
|
|
char track_type,
|
|
const std::string &codec_id) {
|
|
if ((codec_id == MKV_V_MSCOMP) && ('v' == track_type) && (c_priv.GetSize() >= sizeof(alBITMAPINFOHEADER))) {
|
|
auto bih = reinterpret_cast<alBITMAPINFOHEADER *>(c_priv.GetBuffer());
|
|
return (boost::format(Y(" (FourCC: %1%)")) % fourcc_c{&bih->bi_compression}.description()).str();
|
|
|
|
} else if ((codec_id == MKV_A_ACM) && ('a' == track_type) && (c_priv.GetSize() >= sizeof(alWAVEFORMATEX))) {
|
|
alWAVEFORMATEX *wfe = reinterpret_cast<alWAVEFORMATEX *>(c_priv.GetBuffer());
|
|
return (boost::format(Y(" (format tag: 0x%|1$04x|)")) % get_uint16_le(&wfe->w_format_tag)).str();
|
|
|
|
} else if ((codec_id == MKV_V_MPEG4_AVC) && ('v' == track_type) && (c_priv.GetSize() >= 4)) {
|
|
auto avcc = mpeg4::p10::avcc_c::unpack(memory_cptr{new memory_c(c_priv.GetBuffer(), c_priv.GetSize(), false)});
|
|
|
|
return (boost::format(Y(" (h.264 profile: %1% @L%2%.%3%)"))
|
|
% ( avcc.m_profile_idc == 44 ? "CAVLC 4:4:4 Intra"
|
|
: avcc.m_profile_idc == 66 ? "Baseline"
|
|
: avcc.m_profile_idc == 77 ? "Main"
|
|
: avcc.m_profile_idc == 83 ? "Scalable Baseline"
|
|
: avcc.m_profile_idc == 86 ? "Scalable High"
|
|
: avcc.m_profile_idc == 88 ? "Extended"
|
|
: avcc.m_profile_idc == 100 ? "High"
|
|
: avcc.m_profile_idc == 110 ? "High 10"
|
|
: avcc.m_profile_idc == 118 ? "Multiview High"
|
|
: avcc.m_profile_idc == 122 ? "High 4:2:2"
|
|
: avcc.m_profile_idc == 128 ? "Stereo High"
|
|
: avcc.m_profile_idc == 144 ? "High 4:4:4"
|
|
: avcc.m_profile_idc == 244 ? "High 4:4:4 Predictive"
|
|
: Y("Unknown"))
|
|
% (avcc.m_level_idc / 10) % (avcc.m_level_idc % 10)).str();
|
|
} else if ((codec_id == MKV_V_MPEGH_HEVC) && ('v' == track_type) && (c_priv.GetSize() >= 4)) {
|
|
auto hevcc = mtx::hevc::hevcc_c::unpack(std::make_shared<memory_c>(c_priv.GetBuffer(), c_priv.GetSize(), false));
|
|
|
|
return (boost::format(Y(" (HEVC profile: %1% @L%2%.%3%)"))
|
|
% ( hevcc.m_general_profile_idc == 1 ? "Main"
|
|
: hevcc.m_general_profile_idc == 2 ? "Main 10"
|
|
: hevcc.m_general_profile_idc == 3 ? "Main Still Picture"
|
|
: Y("Unknown"))
|
|
% (hevcc.m_general_level_idc / 3 / 10) % (hevcc.m_general_level_idc / 3 % 10)).str();
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
#define in_parent(p) \
|
|
(!p->IsFiniteSize() || \
|
|
(in->getFilePointer() < \
|
|
(p->GetElementPosition() + p->HeadSize() + p->GetSize())))
|
|
|
|
bool
|
|
is_global(EbmlStream *es,
|
|
EbmlElement *l,
|
|
int level) {
|
|
if (Is<EbmlVoid>(l)) {
|
|
show_element(l, level, (BF_EBMLVOID % (l->ElementSize() - l->HeadSize())).str());
|
|
return true;
|
|
|
|
} else if (Is<EbmlCrc32>(l)) {
|
|
show_element(l, level, "EbmlCrc32");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void
|
|
read_master(EbmlMaster *m,
|
|
EbmlStream *es,
|
|
const EbmlSemanticContext &ctx,
|
|
int &upper_lvl_el,
|
|
EbmlElement *&l2) {
|
|
|
|
m->Read(*es, ctx, upper_lvl_el, l2, true);
|
|
if (m->ListSize() == 0)
|
|
return;
|
|
|
|
brng::sort(m->GetElementList(), [](EbmlElement const *a, EbmlElement const *b) { return a->GetElementPosition() < b->GetElementPosition(); });
|
|
}
|
|
|
|
std::string
|
|
format_binary(EbmlBinary &bin,
|
|
size_t max_len = 16) {
|
|
size_t len = std::min(max_len, static_cast<size_t>(bin.GetSize()));
|
|
const binary *b = bin.GetBuffer();
|
|
std::string result = (BF_FORMAT_BINARY_1 % bin.GetSize() % to_hex(b, len)).str();
|
|
|
|
if (len < bin.GetSize())
|
|
result += "...";
|
|
|
|
if (g_options.m_calc_checksums)
|
|
result += (BF_FORMAT_BINARY_2 % mtx::checksum::calculate_as_uint(mtx::checksum::algorithm_e::adler32, bin.GetBuffer(), bin.GetSize())).str();
|
|
|
|
strip(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
inline std::string
|
|
format_binary(EbmlBinary *bin,
|
|
size_t max_len = 16) {
|
|
return format_binary(*bin, max_len);
|
|
}
|
|
|
|
void
|
|
handle_chaptertranslate(EbmlStream *&es,
|
|
EbmlElement *&l2) {
|
|
show_element(l2, 2, Y("Chapter Translate"));
|
|
|
|
for (auto l3 : *static_cast<EbmlMaster *>(l2))
|
|
if (Is<KaxChapterTranslateEditionUID>(l3))
|
|
show_element(l3, 3, boost::format(Y("Chapter Translate Edition UID: %1%")) % static_cast<KaxChapterTranslateEditionUID *>(l3)->GetValue());
|
|
|
|
else if (Is<KaxChapterTranslateCodec>(l3))
|
|
show_element(l3, 3, boost::format(Y("Chapter Translate Codec: %1%")) % static_cast<KaxChapterTranslateCodec *>(l3)->GetValue());
|
|
|
|
else if (Is<KaxChapterTranslateID>(l3))
|
|
show_element(l3, 3, boost::format(Y("Chapter Translate ID: %1%")) % format_binary(static_cast<EbmlBinary *>(l3)));
|
|
|
|
else if (!is_global(es, l3, 3))
|
|
show_unknown_element(l3, 3);
|
|
}
|
|
|
|
void
|
|
handle_info(EbmlStream *&es,
|
|
int &upper_lvl_el,
|
|
EbmlElement *&l1) {
|
|
// General info about this Matroska file
|
|
show_element(l1, 1, Y("Segment information"));
|
|
|
|
upper_lvl_el = 0;
|
|
EbmlElement *element_found = nullptr;
|
|
auto m1 = static_cast<EbmlMaster *>(l1);
|
|
read_master(m1, es, EBML_CONTEXT(l1), upper_lvl_el, element_found);
|
|
|
|
s_tc_scale = FindChildValue<KaxTimecodeScale, uint64_t>(m1, TIMECODE_SCALE);
|
|
|
|
for (auto l2 : *m1)
|
|
if (Is<KaxTimecodeScale>(l2)) {
|
|
s_tc_scale = static_cast<KaxTimecodeScale *>(l2)->GetValue();
|
|
show_element(l2, 2, boost::format(Y("Timecode scale: %1%")) % s_tc_scale);
|
|
|
|
} else if (Is<KaxDuration>(l2)) {
|
|
KaxDuration &duration = *static_cast<KaxDuration *>(l2);
|
|
show_element(l2, 2,
|
|
boost::format(Y("Duration: %|1$.3f|s (%2%)"))
|
|
% (duration.GetValue() * s_tc_scale / 1000000000.0)
|
|
% format_timestamp(static_cast<uint64_t>(duration.GetValue()) * s_tc_scale, 3));
|
|
|
|
} else if (Is<KaxMuxingApp>(l2))
|
|
show_element(l2, 2, boost::format(Y("Multiplexing application: %1%")) % static_cast<KaxMuxingApp *>(l2)->GetValueUTF8());
|
|
|
|
else if (Is<KaxWritingApp>(l2))
|
|
show_element(l2, 2, boost::format(Y("Writing application: %1%")) % static_cast<KaxWritingApp *>(l2)->GetValueUTF8());
|
|
|
|
else if (Is<KaxDateUTC>(l2)) {
|
|
auto epoch_time = boost::posix_time::from_time_t(static_cast<KaxDateUTC *>(l2)->GetEpochDate());
|
|
auto formatted = mtx::date_time::to_string(epoch_time, "%a %b %d %H:%M:%S %Y");
|
|
show_element(l2, 2, boost::format(Y("Date: %1% UTC")) % formatted);
|
|
|
|
} else if (Is<KaxSegmentUID>(l2))
|
|
show_element(l2, 2, boost::format(Y("Segment UID: %1%")) % to_hex(static_cast<KaxSegmentUID *>(l2)));
|
|
|
|
else if (Is<KaxSegmentFamily>(l2))
|
|
show_element(l2, 2, boost::format(Y("Family UID: %1%")) % to_hex(static_cast<KaxSegmentFamily *>(l2)));
|
|
|
|
else if (Is<KaxChapterTranslate>(l2))
|
|
handle_chaptertranslate(es, l2);
|
|
|
|
else if (Is<KaxPrevUID>(l2))
|
|
show_element(l2, 2, boost::format(Y("Previous segment UID: %1%")) % to_hex(static_cast<KaxPrevUID *>(l2)));
|
|
|
|
else if (Is<KaxPrevFilename>(l2))
|
|
show_element(l2, 2, boost::format(Y("Previous filename: %1%")) % static_cast<KaxPrevFilename *>(l2)->GetValueUTF8());
|
|
|
|
else if (Is<KaxNextUID>(l2))
|
|
show_element(l2, 2, boost::format(Y("Next segment UID: %1%")) % to_hex(static_cast<KaxNextUID *>(l2)));
|
|
|
|
else if (Is<KaxNextFilename>(l2))
|
|
show_element(l2, 2, boost::format(Y("Next filename: %1%")) % static_cast<KaxNextFilename *>(l2)->GetValueUTF8());
|
|
|
|
else if (Is<KaxSegmentFilename>(l2))
|
|
show_element(l2, 2, boost::format(Y("Segment filename: %1%")) % static_cast<KaxSegmentFilename *>(l2)->GetValueUTF8());
|
|
|
|
else if (Is<KaxTitle>(l2))
|
|
show_element(l2, 2, boost::format(Y("Title: %1%")) % static_cast<KaxTitle *>(l2)->GetValueUTF8());
|
|
|
|
else if (!is_global(es, l2, 2))
|
|
show_unknown_element(l2, 2);
|
|
}
|
|
|
|
void
|
|
handle_audio_track(EbmlStream *&es,
|
|
EbmlElement *&l3,
|
|
std::vector<std::string> &summary) {
|
|
show_element(l3, 3, "Audio track");
|
|
|
|
for (auto l4 : *static_cast<EbmlMaster *>(l3))
|
|
if (Is<KaxAudioSamplingFreq>(l4)) {
|
|
KaxAudioSamplingFreq &freq = *static_cast<KaxAudioSamplingFreq *>(l4);
|
|
show_element(l4, 4, boost::format(Y("Sampling frequency: %1%")) % freq.GetValue());
|
|
summary.push_back((boost::format(Y("sampling freq: %1%")) % freq.GetValue()).str());
|
|
|
|
} else if (Is<KaxAudioOutputSamplingFreq>(l4)) {
|
|
KaxAudioOutputSamplingFreq &ofreq = *static_cast<KaxAudioOutputSamplingFreq *>(l4);
|
|
show_element(l4, 4, boost::format(Y("Output sampling frequency: %1%")) % ofreq.GetValue());
|
|
summary.push_back((boost::format(Y("output sampling freq: %1%")) % ofreq.GetValue()).str());
|
|
|
|
} else if (Is<KaxAudioChannels>(l4)) {
|
|
KaxAudioChannels &channels = *static_cast<KaxAudioChannels *>(l4);
|
|
show_element(l4, 4, boost::format(Y("Channels: %1%")) % channels.GetValue());
|
|
summary.push_back((boost::format(Y("channels: %1%")) % channels.GetValue()).str());
|
|
|
|
#if MATROSKA_VERSION >= 2
|
|
} else if (Is<KaxAudioPosition>(l4)) {
|
|
KaxAudioPosition &positions = *static_cast<KaxAudioPosition *>(l4);
|
|
show_element(l4, 4, boost::format(Y("Channel positions: %1%")) % format_binary(positions));
|
|
#endif
|
|
|
|
} else if (Is<KaxAudioBitDepth>(l4)) {
|
|
KaxAudioBitDepth &bps = *static_cast<KaxAudioBitDepth *>(l4);
|
|
show_element(l4, 4, boost::format(Y("Bit depth: %1%")) % bps.GetValue());
|
|
summary.push_back((boost::format(Y("bits per sample: %1%")) % bps.GetValue()).str());
|
|
|
|
} else if (!is_global(es, l4, 4))
|
|
show_unknown_element(l4, 4);
|
|
}
|
|
|
|
void handle_video_colour_master_meta(EbmlStream *&es,
|
|
EbmlElement *&l5,
|
|
std::vector<std::string> &summary) {
|
|
show_element(l5, 5, Y("Video colour mastering metadata"));
|
|
for (auto l6: *static_cast<EbmlMaster *>(l5)) {
|
|
if (Is<KaxVideoRChromaX>(l6)) {
|
|
auto &x = *static_cast<KaxVideoRChromaX *>(l6);
|
|
show_element(l6, 6, boost::format(Y("Red colour coordinate x: %1%")) % x.GetValue());
|
|
summary.push_back((boost::format(Y("Red colour coordinate x: %1%")) % x.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoRChromaY>(l6)) {
|
|
auto &y = *static_cast<KaxVideoRChromaY *>(l6);
|
|
show_element(l6, 6, boost::format(Y("Red colour coordinate y: %1%")) % y.GetValue());
|
|
summary.push_back((boost::format(Y("Red colour coordinate y: %1%")) % y.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoGChromaX>(l6)) {
|
|
auto &x = *static_cast<KaxVideoGChromaX *>(l6);
|
|
show_element(l6, 6, boost::format(Y("Green colour coordinate x: %1%")) % x.GetValue());
|
|
summary.push_back((boost::format(Y("Green colour coordinate x: %1%")) % x.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoGChromaY>(l6)) {
|
|
auto &y = *static_cast<KaxVideoGChromaY *>(l6);
|
|
show_element(l6, 6, boost::format(Y("Green colour coordinate y: %1%")) % y.GetValue());
|
|
summary.push_back((boost::format(Y("Green colour coordinate y: %1%")) % y.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoBChromaX>(l6)) {
|
|
auto &x = *static_cast<KaxVideoBChromaX *>(l6);
|
|
show_element(l6, 6, boost::format(Y("Blue colour coordinate x: %1%")) % x.GetValue());
|
|
summary.push_back((boost::format(Y("Blue colour coordinate x: %1%")) % x.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoBChromaY>(l6)) {
|
|
auto &y = *static_cast<KaxVideoBChromaY *>(l6);
|
|
show_element(l6, 6, boost::format(Y("Blue colour coordinate y: %1%")) % y.GetValue());
|
|
summary.push_back((boost::format(Y("Blue colour coordinate y: %1%")) % y.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoWhitePointChromaX>(l6)) {
|
|
auto &x = *static_cast<KaxVideoWhitePointChromaX *>(l6);
|
|
show_element(l6, 6, boost::format(Y("White colour coordinate x: %1%")) % x.GetValue());
|
|
summary.push_back((boost::format(Y("White colour coordinate x: %1%")) % x.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoWhitePointChromaY>(l6)) {
|
|
auto &y = *static_cast<KaxVideoWhitePointChromaY *>(l6);
|
|
show_element(l6, 6, boost::format(Y("White colour coordinate y: %1%")) % y.GetValue());
|
|
summary.push_back((boost::format(Y("White colour coordinate y: %1%")) % y.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoLuminanceMax>(l6)) {
|
|
auto &luminance = *static_cast<KaxVideoLuminanceMax *>(l6);
|
|
show_element(l6, 6, boost::format(Y("Max luminance: %1%")) % luminance.GetValue());
|
|
summary.push_back((boost::format(Y("Max luminance: %1%")) % luminance.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoLuminanceMin>(l6)) {
|
|
auto &luminance = *static_cast<KaxVideoLuminanceMin *>(l6);
|
|
show_element(l6, 6, boost::format(Y("Min luminance: %1%")) % luminance.GetValue());
|
|
summary.push_back((boost::format(Y("Min luminance: %1%")) % luminance.GetValue()).str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
handle_video_colour(EbmlStream *&es,
|
|
EbmlElement *&l4,
|
|
std::vector<std::string> &summary) {
|
|
show_element(l4, 4, Y("Video colour information"));
|
|
for (auto l5 : *static_cast<EbmlMaster *>(l4)) {
|
|
if (Is<KaxVideoColourMatrix>(l5)) {
|
|
auto &colour_matrix = *static_cast<KaxVideoColourMatrix *>(l5);
|
|
show_element(l5, 5, boost::format(Y("Colour matrix: %1%")) % colour_matrix.GetValue());
|
|
summary.push_back((boost::format(Y("Colour matrix: %1%")) % colour_matrix.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoBitsPerChannel>(l5)) {
|
|
auto &bits_per_channel = *static_cast<KaxVideoBitsPerChannel *>(l5);
|
|
show_element(l5, 5, boost::format(Y("Bits per channel: %1%")) % bits_per_channel.GetValue());
|
|
summary.push_back((boost::format(Y("Bits per channel: %1%")) % bits_per_channel.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoChromaSubsampHorz>(l5)) {
|
|
auto &subsample_hori = *static_cast<KaxVideoChromaSubsampHorz *>(l5);
|
|
show_element(l5, 5, boost::format(Y("Horizontal chroma subsample: %1%")) % subsample_hori.GetValue());
|
|
summary.push_back((boost::format(Y("Horizontal chroma subsample: %1%")) % subsample_hori.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoChromaSubsampVert>(l5)) {
|
|
auto &subsample_vert = *static_cast<KaxVideoChromaSubsampVert *>(l5);
|
|
show_element(l5, 5, boost::format(Y("Vertical chroma subsample: %1%")) % subsample_vert.GetValue());
|
|
summary.push_back((boost::format(Y("Vertical chroma subsample: %1%")) % subsample_vert.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoCbSubsampHorz>(l5)) {
|
|
auto &subsample_hori = *static_cast<KaxVideoCbSubsampHorz *>(l5);
|
|
show_element(l5, 5, boost::format(Y("Horizontal Cb subsample: %1%")) % subsample_hori.GetValue());
|
|
summary.push_back((boost::format(Y("Horizontal Cb subsample: %1%")) % subsample_hori.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoCbSubsampVert>(l5)) {
|
|
auto &subsample_vert = *static_cast<KaxVideoCbSubsampVert *>(l5);
|
|
show_element(l5, 5, boost::format(Y("Vertical Cb subsample: %1%")) % subsample_vert.GetValue());
|
|
summary.push_back((boost::format(Y("Vertical Cb subsample: %1%")) % subsample_vert.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoChromaSitHorz>(l5)) {
|
|
auto &sit_hori = *static_cast<KaxVideoChromaSitHorz *>(l5);
|
|
show_element(l5, 5, boost::format(Y("Horizontal chroma siting: %1%")) % sit_hori.GetValue());
|
|
summary.push_back((boost::format(Y("Horizontal chroma siting: %1%")) % sit_hori.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoChromaSitVert>(l5)) {
|
|
auto &sit_vert = *static_cast<KaxVideoChromaSitVert *>(l5);
|
|
show_element(l5, 5, boost::format(Y("Vertical chroma siting: %1%")) % sit_vert.GetValue());
|
|
summary.push_back((boost::format(Y("Vertical chroma siting: %1%")) % sit_vert.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoColourRange>(l5)) {
|
|
auto &range = *static_cast<KaxVideoColourRange *>(l5);
|
|
show_element(l5, 5, boost::format(Y("Colour range: %1%")) % range.GetValue());
|
|
summary.push_back((boost::format(Y("Colour range: %1%")) % range.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoColourTransferCharacter>(l5)) {
|
|
auto &transfer = *static_cast<KaxVideoColourTransferCharacter *>(l5);
|
|
show_element(l5, 5, boost::format(Y("Colour transfer: %1%")) % transfer.GetValue());
|
|
summary.push_back((boost::format(Y("Colour transfer: %1%")) % transfer.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoColourPrimaries>(l5)) {
|
|
auto &primaries = *static_cast<KaxVideoColourPrimaries *>(l5);
|
|
show_element(l5, 5, boost::format(Y("Colour primaries: %1%")) % primaries.GetValue());
|
|
summary.push_back((boost::format(Y("Colour primaries: %1%")) % primaries.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoColourMaxCLL>(l5)) {
|
|
auto &max = *static_cast<KaxVideoColourMaxCLL *>(l5);
|
|
show_element(l5, 5, boost::format(Y("Max content light: %1%")) % max.GetValue());
|
|
summary.push_back((boost::format(Y("Max content light: %1%")) % max.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoColourMaxFALL>(l5)) {
|
|
auto &max = *static_cast<KaxVideoColourMaxFALL *>(l5);
|
|
show_element(l5, 5, boost::format(Y("Max frame light: %1%")) % max.GetValue());
|
|
summary.push_back((boost::format(Y("Max frame light: %1%")) % max.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoColourMasterMeta>(l5)) {
|
|
handle_video_colour_master_meta(es, l5, summary);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
handle_video_track(EbmlStream *&es,
|
|
EbmlElement *&l3,
|
|
std::vector<std::string> &summary) {
|
|
show_element(l3, 3, Y("Video track"));
|
|
|
|
for (auto l4 : *static_cast<EbmlMaster *>(l3))
|
|
if (Is<KaxVideoPixelWidth>(l4)) {
|
|
KaxVideoPixelWidth &width = *static_cast<KaxVideoPixelWidth *>(l4);
|
|
show_element(l4, 4, boost::format(Y("Pixel width: %1%")) % width.GetValue());
|
|
summary.push_back((boost::format(Y("pixel width: %1%")) % width.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoPixelHeight>(l4)) {
|
|
KaxVideoPixelHeight &height = *static_cast<KaxVideoPixelHeight *>(l4);
|
|
show_element(l4, 4, boost::format(Y("Pixel height: %1%")) % height.GetValue());
|
|
summary.push_back((boost::format(Y("pixel height: %1%")) % height.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoDisplayWidth>(l4)) {
|
|
KaxVideoDisplayWidth &width = *static_cast<KaxVideoDisplayWidth *>(l4);
|
|
show_element(l4, 4, boost::format(Y("Display width: %1%")) % width.GetValue());
|
|
summary.push_back((boost::format(Y("display width: %1%")) % width.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoDisplayHeight>(l4)) {
|
|
KaxVideoDisplayHeight &height = *static_cast<KaxVideoDisplayHeight *>(l4);
|
|
show_element(l4, 4, boost::format(Y("Display height: %1%")) % height.GetValue());
|
|
summary.push_back((boost::format(Y("display height: %1%")) % height.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoPixelCropLeft>(l4)) {
|
|
KaxVideoPixelCropLeft &left = *static_cast<KaxVideoPixelCropLeft *>(l4);
|
|
show_element(l4, 4, boost::format(Y("Pixel crop left: %1%")) % left.GetValue());
|
|
summary.push_back((boost::format(Y("pixel crop left: %1%")) % left.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoPixelCropTop>(l4)) {
|
|
KaxVideoPixelCropTop &top = *static_cast<KaxVideoPixelCropTop *>(l4);
|
|
show_element(l4, 4, boost::format(Y("Pixel crop top: %1%")) % top.GetValue());
|
|
summary.push_back((boost::format(Y("pixel crop top: %1%")) % top.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoPixelCropRight>(l4)) {
|
|
KaxVideoPixelCropRight &right = *static_cast<KaxVideoPixelCropRight *>(l4);
|
|
show_element(l4, 4, boost::format(Y("Pixel crop right: %1%")) % right.GetValue());
|
|
summary.push_back((boost::format(Y("pixel crop right: %1%")) % right.GetValue()).str());
|
|
|
|
} else if (Is<KaxVideoPixelCropBottom>(l4)) {
|
|
KaxVideoPixelCropBottom &bottom = *static_cast<KaxVideoPixelCropBottom *>(l4);
|
|
show_element(l4, 4, boost::format(Y("Pixel crop bottom: %1%")) % bottom.GetValue());
|
|
summary.push_back((boost::format(Y("pixel crop bottom: %1%")) % bottom.GetValue()).str());
|
|
|
|
#if MATROSKA_VERSION >= 2
|
|
} else if (Is<KaxVideoDisplayUnit>(l4)) {
|
|
auto unit = static_cast<KaxVideoDisplayUnit *>(l4)->GetValue();
|
|
show_element(l4, 4,
|
|
boost::format(Y("Display unit: %1%%2%"))
|
|
% unit
|
|
% ( 0 == unit ? Y(" (pixels)")
|
|
: 1 == unit ? Y(" (centimeters)")
|
|
: 2 == unit ? Y(" (inches)")
|
|
: 3 == unit ? Y(" (aspect ratio)")
|
|
: ""));
|
|
|
|
} else if (Is<KaxVideoGamma>(l4))
|
|
show_element(l4, 4, boost::format(Y("Gamma: %1%")) % static_cast<KaxVideoGamma *>(l4)->GetValue());
|
|
|
|
else if (Is<KaxVideoFlagInterlaced>(l4))
|
|
show_element(l4, 4, boost::format(Y("Interlaced: %1%")) % static_cast<KaxVideoFlagInterlaced *>(l4)->GetValue());
|
|
|
|
else if (Is<KaxVideoFieldOrder>(l4)) {
|
|
auto field_order = static_cast<KaxVideoFieldOrder *>(l4)->GetValue();
|
|
show_element(l4, 4,
|
|
boost::format(Y("Field order: %1% (%2%)"))
|
|
% field_order
|
|
% ( 0 == field_order ? Y("progressive")
|
|
: 1 == field_order ? Y("top field displayed first, top field stored first")
|
|
: 2 == field_order ? Y("unspecified")
|
|
: 6 == field_order ? Y("bottom field displayed first, bottom field stored first")
|
|
: 9 == field_order ? Y("bottom field displayed first, top field stored first")
|
|
: 14 == field_order ? Y("top field displayed first, bottom field stored first")
|
|
: "unknown"));
|
|
|
|
} else if (Is<KaxVideoStereoMode>(l4)) {
|
|
auto stereo_mode = static_cast<KaxVideoStereoMode *>(l4)->GetValue();
|
|
show_element(l4, 4,
|
|
boost::format(Y("Stereo mode: %1% (%2%)"))
|
|
% stereo_mode
|
|
% stereo_mode_c::translate(static_cast<stereo_mode_c::mode>(stereo_mode)));
|
|
|
|
} else if (Is<KaxVideoAspectRatio>(l4)) {
|
|
auto ar_type = static_cast<KaxVideoAspectRatio *>(l4)->GetValue();
|
|
show_element(l4, 4,
|
|
boost::format(Y("Aspect ratio type: %1%%2%"))
|
|
% ar_type
|
|
% ( 0 == ar_type ? Y(" (free resizing)")
|
|
: 1 == ar_type ? Y(" (keep aspect ratio)")
|
|
: 2 == ar_type ? Y(" (fixed)")
|
|
: ""));
|
|
#endif
|
|
} else if (Is<KaxVideoColourSpace>(l4))
|
|
show_element(l4, 4, boost::format(Y("Colour space: %1%")) % format_binary(static_cast<KaxVideoColourSpace *>(l4)));
|
|
|
|
else if (Is<KaxVideoFrameRate>(l4))
|
|
show_element(l4, 4, boost::format(Y("Frame rate: %1%")) % static_cast<KaxVideoFrameRate *>(l4)->GetValue());
|
|
|
|
else if (Is<KaxVideoColour>(l4))
|
|
handle_video_colour(es, l4, summary);
|
|
|
|
else if (!is_global(es, l4, 4))
|
|
show_unknown_element(l4, 4);
|
|
}
|
|
|
|
void
|
|
handle_content_encodings(EbmlStream *&es,
|
|
EbmlElement *&l3) {
|
|
show_element(l3, 3, Y("Content encodings"));
|
|
|
|
for (auto l4 : *static_cast<EbmlMaster *>(l3))
|
|
if (Is<KaxContentEncoding>(l4)) {
|
|
show_element(l4, 4, Y("Content encoding"));
|
|
|
|
for (auto l5 : *static_cast<EbmlMaster *>(l4))
|
|
if (Is<KaxContentEncodingOrder>(l5))
|
|
show_element(l5, 5, boost::format(Y("Order: %1%")) % static_cast<KaxContentEncodingOrder *>(l5)->GetValue());
|
|
|
|
else if (Is<KaxContentEncodingScope>(l5)) {
|
|
std::vector<std::string> scope;
|
|
auto ce_scope = static_cast<KaxContentEncodingScope *>(l5)->GetValue();
|
|
|
|
if ((ce_scope & 0x01) == 0x01)
|
|
scope.push_back(Y("1: all frames"));
|
|
if ((ce_scope & 0x02) == 0x02)
|
|
scope.push_back(Y("2: codec private data"));
|
|
if ((ce_scope & 0xfc) != 0x00)
|
|
scope.push_back(Y("rest: unknown"));
|
|
if (scope.empty())
|
|
scope.push_back(Y("unknown"));
|
|
show_element(l5, 5, boost::format(Y("Scope: %1% (%2%)")) % ce_scope % boost::join(scope, ", "));
|
|
|
|
} else if (Is<KaxContentEncodingType>(l5)) {
|
|
auto ce_type = static_cast<KaxContentEncodingType *>(l5)->GetValue();
|
|
show_element(l5, 5,
|
|
boost::format(Y("Type: %1% (%2%)"))
|
|
% ce_type
|
|
% ( 0 == ce_type ? Y("compression")
|
|
: 1 == ce_type ? Y("encryption")
|
|
: Y("unknown")));
|
|
|
|
} else if (Is<KaxContentCompression>(l5)) {
|
|
show_element(l5, 5, Y("Content compression"));
|
|
|
|
for (auto l6 : *static_cast<EbmlMaster *>(l5))
|
|
if (Is<KaxContentCompAlgo>(l6)) {
|
|
auto c_algo = static_cast<KaxContentCompAlgo *>(l6)->GetValue();
|
|
show_element(l6, 6,
|
|
boost::format(Y("Algorithm: %1% (%2%)"))
|
|
% c_algo
|
|
% ( 0 == c_algo ? "ZLIB"
|
|
: 1 == c_algo ? "bzLib"
|
|
: 2 == c_algo ? "lzo1x"
|
|
: 3 == c_algo ? Y("header removal")
|
|
: Y("unknown")));
|
|
|
|
} else if (Is<KaxContentCompSettings>(l6))
|
|
show_element(l6, 6, boost::format(Y("Settings: %1%")) % format_binary(static_cast<KaxContentCompSettings *>(l6)));
|
|
|
|
else if (!is_global(es, l6, 6))
|
|
show_unknown_element(l6, 6);
|
|
} else if (Is<KaxContentEncryption>(l5)) {
|
|
show_element(l5, 5, Y("Content encryption"));
|
|
|
|
for (auto l6 : *static_cast<EbmlMaster *>(l5))
|
|
if (Is<KaxContentEncAlgo>(l6)) {
|
|
auto e_algo = static_cast<KaxContentEncAlgo *>(l6)->GetValue();
|
|
show_element(l6, 6,
|
|
boost::format(Y("Encryption algorithm: %1% (%2%)"))
|
|
% e_algo
|
|
% ( 0 == e_algo ? Y("no encryption")
|
|
: 1 == e_algo ? "DES"
|
|
: 2 == e_algo ? "3DES"
|
|
: 3 == e_algo ? "Twofish"
|
|
: 4 == e_algo ? "Blowfish"
|
|
: 5 == e_algo ? "AES"
|
|
: Y("unknown")));
|
|
|
|
} else if (Is<KaxContentEncKeyID>(l6))
|
|
show_element(l6, 6, boost::format(Y("Encryption key ID: %1%")) % format_binary(static_cast<KaxContentEncKeyID *>(l6)));
|
|
|
|
else if (Is<KaxContentSigAlgo>(l6)) {
|
|
auto s_algo = static_cast<KaxContentSigAlgo *>(l6)->GetValue();
|
|
show_element(l6, 6,
|
|
boost::format(Y("Signature algorithm: %1% (%2%)"))
|
|
% s_algo
|
|
% ( 0 == s_algo ? Y("no signature algorithm")
|
|
: 1 == s_algo ? Y("RSA")
|
|
: Y("unknown")));
|
|
|
|
} else if (Is<KaxContentSigHashAlgo>(l6)) {
|
|
auto s_halgo = static_cast<KaxContentSigHashAlgo *>(l6)->GetValue();
|
|
show_element(l6, 6,
|
|
boost::format(Y("Signature hash algorithm: %1% (%2%)"))
|
|
% s_halgo
|
|
% ( 0 == s_halgo ? Y("no signature hash algorithm")
|
|
: 1 == s_halgo ? Y("SHA1-160")
|
|
: 2 == s_halgo ? Y("MD5")
|
|
: Y("unknown")));
|
|
|
|
} else if (Is<KaxContentSigKeyID>(l6))
|
|
show_element(l6, 6, boost::format(Y("Signature key ID: %1%")) % format_binary(static_cast<KaxContentSigKeyID *>(l6)));
|
|
|
|
else if (Is<KaxContentSignature>(l6))
|
|
show_element(l6, 6, boost::format(Y("Signature: %1%")) % format_binary(static_cast<KaxContentSignature *>(l6)));
|
|
|
|
else if (!is_global(es, l6, 6))
|
|
show_unknown_element(l6, 6);
|
|
|
|
} else if (!is_global(es, l5, 5))
|
|
show_unknown_element(l5, 5);
|
|
|
|
} else if (!is_global(es, l4, 4))
|
|
show_unknown_element(l4, 4);
|
|
}
|
|
|
|
void
|
|
handle_tracks(EbmlStream *&es,
|
|
int &upper_lvl_el,
|
|
EbmlElement *&l1) {
|
|
// Yep, we've found our KaxTracks element. Now find all tracks
|
|
// contained in this segment.
|
|
show_element(l1, 1, Y("Segment tracks"));
|
|
|
|
size_t s_mkvmerge_track_id = 0;
|
|
upper_lvl_el = 0;
|
|
EbmlElement *element_found = nullptr;
|
|
auto m1 = static_cast<EbmlMaster *>(l1);
|
|
read_master(m1, es, EBML_CONTEXT(l1), upper_lvl_el, element_found);
|
|
|
|
for (auto l2 : *m1)
|
|
if (Is<KaxTrackEntry>(l2)) {
|
|
// We actually found a track entry :) We're happy now.
|
|
show_element(l2, 2, Y("A track"));
|
|
|
|
std::vector<std::string> summary;
|
|
std::string kax_codec_id, fourcc_buffer;
|
|
auto track = std::make_shared<kax_track_t>();
|
|
|
|
for (auto l3 : *static_cast<EbmlMaster *>(l2))
|
|
// Now evaluate the data belonging to this track
|
|
if (Is<KaxTrackAudio>(l3))
|
|
handle_audio_track(es, l3, summary);
|
|
|
|
else if (Is<KaxTrackVideo>(l3))
|
|
handle_video_track(es, l3, summary);
|
|
|
|
else if (Is<KaxTrackNumber>(l3)) {
|
|
track->tnum = static_cast<KaxTrackNumber *>(l3)->GetValue();
|
|
|
|
auto existing_track = find_track(track->tnum);
|
|
size_t track_id = s_mkvmerge_track_id;
|
|
if (!existing_track) {
|
|
track->mkvmerge_track_id = s_mkvmerge_track_id;
|
|
++s_mkvmerge_track_id;
|
|
add_track(track);
|
|
|
|
} else
|
|
track_id = existing_track->mkvmerge_track_id;
|
|
|
|
show_element(l3, 3, boost::format(Y("Track number: %1% (track ID for mkvmerge & mkvextract: %2%)")) % track->tnum % track_id);
|
|
summary.push_back((boost::format(Y("mkvmerge/mkvextract track ID: %1%")) % track_id).str());
|
|
|
|
} else if (Is<KaxTrackUID>(l3)) {
|
|
track->tuid = static_cast<KaxTrackUID *>(l3)->GetValue();
|
|
show_element(l3, 3, boost::format(Y("Track UID: %1%")) % track->tuid);
|
|
|
|
} else if (Is<KaxTrackType>(l3)) {
|
|
auto ttype = static_cast<KaxTrackType *>(l3)->GetValue();
|
|
track->type = track_audio == ttype ? 'a'
|
|
: track_video == ttype ? 'v'
|
|
: track_subtitle == ttype ? 's'
|
|
: track_buttons == ttype ? 'b'
|
|
: '?';
|
|
show_element(l3, 3,
|
|
boost::format(Y("Track type: %1%"))
|
|
% ( 'a' == track->type ? "audio"
|
|
: 'v' == track->type ? "video"
|
|
: 's' == track->type ? "subtitles"
|
|
: 'b' == track->type ? "buttons"
|
|
: "unknown"));
|
|
|
|
#if MATROSKA_VERSION >= 2
|
|
} else if (Is<KaxTrackFlagEnabled>(l3))
|
|
show_element(l3, 3, boost::format(Y("Enabled: %1%")) % static_cast<KaxTrackFlagEnabled *>(l3)->GetValue());
|
|
#endif
|
|
|
|
else if (Is<KaxTrackName>(l3))
|
|
show_element(l3, 3, boost::format(Y("Name: %1%")) % static_cast<KaxTrackName *>(l3)->GetValueUTF8());
|
|
|
|
else if (Is<KaxCodecID>(l3)) {
|
|
kax_codec_id = static_cast<KaxCodecID *>(l3)->GetValue();
|
|
show_element(l3, 3, boost::format(Y("Codec ID: %1%")) % kax_codec_id);
|
|
|
|
} else if (Is<KaxCodecPrivate>(l3)) {
|
|
KaxCodecPrivate &c_priv = *static_cast<KaxCodecPrivate *>(l3);
|
|
fourcc_buffer = create_codec_dependent_private_info(c_priv, track->type, kax_codec_id);
|
|
|
|
if (g_options.m_calc_checksums && !g_options.m_show_summary)
|
|
fourcc_buffer += (boost::format(Y(" (adler: 0x%|1$08x|)")) % mtx::checksum::calculate_as_uint(mtx::checksum::algorithm_e::adler32, c_priv.GetBuffer(), c_priv.GetSize())).str();
|
|
|
|
if (g_options.m_show_hexdump)
|
|
fourcc_buffer += create_hexdump(c_priv.GetBuffer(), c_priv.GetSize());
|
|
|
|
show_element(l3, 3, boost::format(Y("CodecPrivate, length %1%%2%")) % c_priv.GetSize() % fourcc_buffer);
|
|
|
|
} else if (Is<KaxCodecName>(l3))
|
|
show_element(l3, 3, boost::format(Y("Codec name: %1%")) % static_cast<KaxCodecName *>(l3)->GetValueUTF8());
|
|
|
|
#if MATROSKA_VERSION >= 2
|
|
else if (Is<KaxCodecSettings>(l3))
|
|
show_element(l3, 3, boost::format(Y("Codec settings: %1%")) % static_cast<KaxCodecSettings *>(l3)->GetValueUTF8());
|
|
|
|
else if (Is<KaxCodecInfoURL>(l3))
|
|
show_element(l3, 3, boost::format(Y("Codec info URL: %1%")) % static_cast<KaxCodecInfoURL *>(l3)->GetValue());
|
|
|
|
else if (Is<KaxCodecDownloadURL>(l3))
|
|
show_element(l3, 3, boost::format(Y("Codec download URL: %1%")) % static_cast<KaxCodecDownloadURL *>(l3)->GetValue());
|
|
|
|
else if (Is<KaxCodecDecodeAll>(l3))
|
|
show_element(l3, 3, boost::format(Y("Codec decode all: %1%")) % static_cast<KaxCodecDecodeAll *>(l3)->GetValue());
|
|
|
|
else if (Is<KaxTrackOverlay>(l3))
|
|
show_element(l3, 3, boost::format(Y("Track overlay: %1%")) % static_cast<KaxTrackOverlay *>(l3)->GetValue());
|
|
#endif // MATROSKA_VERSION >= 2
|
|
|
|
else if (Is<KaxTrackMinCache>(l3))
|
|
show_element(l3, 3, boost::format(Y("MinCache: %1%")) % static_cast<KaxTrackMinCache *>(l3)->GetValue());
|
|
|
|
else if (Is<KaxTrackMaxCache>(l3))
|
|
show_element(l3, 3, boost::format(Y("MaxCache: %1%")) % static_cast<KaxTrackMaxCache *>(l3)->GetValue());
|
|
|
|
else if (Is<KaxTrackDefaultDuration>(l3)) {
|
|
track->default_duration = static_cast<KaxTrackDefaultDuration *>(l3)->GetValue();
|
|
show_element(l3, 3,
|
|
boost::format(Y("Default duration: %|1$.3f|ms (%|2$.3f| frames/fields per second for a video track)"))
|
|
% (static_cast<double>(track->default_duration) / 1000000.0)
|
|
% (1000000000.0 / static_cast<double>(track->default_duration)));
|
|
summary.push_back((boost::format(Y("default duration: %|1$.3f|ms (%|2$.3f| frames/fields per second for a video track)"))
|
|
% (static_cast<double>(track->default_duration) / 1000000.0)
|
|
% (1000000000.0 / static_cast<double>(track->default_duration))
|
|
).str());
|
|
|
|
} else if (Is<KaxTrackFlagLacing>(l3))
|
|
show_element(l3, 3, boost::format(Y("Lacing flag: %1%")) % static_cast<KaxTrackFlagLacing *>(l3)->GetValue());
|
|
|
|
else if (Is<KaxTrackFlagDefault>(l3))
|
|
show_element(l3, 3, boost::format(Y("Default flag: %1%")) % static_cast<KaxTrackFlagDefault *>(l3)->GetValue());
|
|
|
|
else if (Is<KaxTrackFlagForced>(l3))
|
|
show_element(l3, 3, boost::format(Y("Forced flag: %1%")) % static_cast<KaxTrackFlagForced *>(l3)->GetValue());
|
|
|
|
else if (Is<KaxTrackLanguage>(l3)) {
|
|
auto language = static_cast<KaxTrackLanguage *>(l3)->GetValue();
|
|
show_element(l3, 3, boost::format(Y("Language: %1%")) % language);
|
|
summary.push_back((boost::format(Y("language: %1%")) % language).str());
|
|
|
|
} else if (Is<KaxTrackTimecodeScale>(l3))
|
|
show_element(l3, 3, boost::format(Y("Timecode scale: %1%")) % static_cast<KaxTrackTimecodeScale *>(l3)->GetValue());
|
|
|
|
else if (Is<KaxMaxBlockAdditionID>(l3))
|
|
show_element(l3, 3, boost::format(Y("Max BlockAddition ID: %1%")) % static_cast<KaxMaxBlockAdditionID *>(l3)->GetValue());
|
|
|
|
else if (Is<KaxContentEncodings>(l3))
|
|
handle_content_encodings(es, l3);
|
|
|
|
else if (Is<KaxCodecDelay>(l3)) {
|
|
auto value = static_cast<KaxCodecDelay *>(l3)->GetValue();
|
|
show_element(l3, 3, boost::format(Y("Codec delay: %|1$.3f|ms (%2%ns)")) % (static_cast<double>(value) / 1000000.0) % value);
|
|
|
|
} else if (Is<KaxSeekPreRoll>(l3)) {
|
|
auto value = static_cast<KaxSeekPreRoll *>(l3)->GetValue();
|
|
show_element(l3, 3, boost::format(Y("Seek pre-roll: %|1$.3f|ms (%2%ns)")) % (static_cast<double>(value) / 1000000.0) % value);
|
|
|
|
} else if (!is_global(es, l3, 3))
|
|
show_unknown_element(l3, 3);
|
|
|
|
if (g_options.m_show_summary)
|
|
mxinfo(boost::format(Y("Track %1%: %2%, codec ID: %3%%4%%5%%6%\n"))
|
|
% track->tnum
|
|
% ( 'a' == track->type ? Y("audio")
|
|
: 'v' == track->type ? Y("video")
|
|
: 's' == track->type ? Y("subtitles")
|
|
: 'b' == track->type ? Y("buttons")
|
|
: Y("unknown"))
|
|
% kax_codec_id
|
|
% fourcc_buffer
|
|
% (summary.empty() ? "" : ", ")
|
|
% boost::join(summary, ", "));
|
|
|
|
} else if (!is_global(es, l2, 2))
|
|
show_unknown_element(l2, 2);
|
|
}
|
|
|
|
void
|
|
handle_seek_head(EbmlStream *&es,
|
|
int &upper_lvl_el,
|
|
EbmlElement *&l1) {
|
|
if ((g_options.m_verbose < 2) && !g_options.m_use_gui) {
|
|
show_element(l1, 1, Y("Seek head (subentries will be skipped)"));
|
|
return;
|
|
}
|
|
|
|
show_element(l1, 1, Y("Seek head"));
|
|
|
|
upper_lvl_el = 0;
|
|
EbmlElement *element_found = nullptr;
|
|
auto m1 = static_cast<EbmlMaster *>(l1);
|
|
read_master(m1, es, EBML_CONTEXT(l1), upper_lvl_el, element_found);
|
|
|
|
for (auto l2 : *m1)
|
|
if (Is<KaxSeek>(l2)) {
|
|
show_element(l2, 2, Y("Seek entry"));
|
|
|
|
for (auto l3 : *static_cast<EbmlMaster *>(l2))
|
|
if (Is<KaxSeekID>(l3)) {
|
|
KaxSeekID &seek_id = static_cast<KaxSeekID &>(*l3);
|
|
EbmlId id(seek_id.GetBuffer(), seek_id.GetSize());
|
|
|
|
show_element(l3, 3,
|
|
boost::format(Y("Seek ID: %1% (%2%)"))
|
|
% to_hex(seek_id)
|
|
% ( Is<KaxInfo>(id) ? "KaxInfo"
|
|
: Is<KaxCluster>(id) ? "KaxCluster"
|
|
: Is<KaxTracks>(id) ? "KaxTracks"
|
|
: Is<KaxCues>(id) ? "KaxCues"
|
|
: Is<KaxAttachments>(id) ? "KaxAttachments"
|
|
: Is<KaxChapters>(id) ? "KaxChapters"
|
|
: Is<KaxTags>(id) ? "KaxTags"
|
|
: Is<KaxSeekHead>(id) ? "KaxSeekHead"
|
|
: "unknown"));
|
|
|
|
} else if (Is<KaxSeekPosition>(l3))
|
|
show_element(l3, 3, boost::format(Y("Seek position: %1%")) % static_cast<KaxSeekPosition *>(l3)->GetValue());
|
|
|
|
else if (!is_global(es, l3, 3))
|
|
show_unknown_element(l3, 3);
|
|
|
|
} else if (!is_global(es, l2, 2))
|
|
show_unknown_element(l2, 2);
|
|
}
|
|
|
|
void
|
|
handle_cues(EbmlStream *&es,
|
|
int &upper_lvl_el,
|
|
EbmlElement *&l1) {
|
|
if (g_options.m_verbose < 2) {
|
|
show_element(l1, 1, Y("Cues (subentries will be skipped)"));
|
|
return;
|
|
}
|
|
|
|
show_element(l1, 1, "Cues");
|
|
|
|
upper_lvl_el = 0;
|
|
EbmlElement *element_found = nullptr;
|
|
auto m1 = static_cast<EbmlMaster *>(l1);
|
|
read_master(m1, es, EBML_CONTEXT(l1), upper_lvl_el, element_found);
|
|
|
|
for (auto l2 : *m1)
|
|
if (Is<KaxCuePoint>(l2)) {
|
|
show_element(l2, 2, Y("Cue point"));
|
|
|
|
for (auto l3 : *static_cast<EbmlMaster *>(l2))
|
|
if (Is<KaxCueTime>(l3))
|
|
show_element(l3, 3, boost::format(Y("Cue time: %|1$.3f|s")) % (s_tc_scale * static_cast<double>(static_cast<KaxCueTime *>(l3)->GetValue()) / 1000000000.0));
|
|
|
|
else if (Is<KaxCueTrackPositions>(l3)) {
|
|
show_element(l3, 3, Y("Cue track positions"));
|
|
|
|
for (auto l4 : *static_cast<EbmlMaster *>(l3))
|
|
if (Is<KaxCueTrack>(l4))
|
|
show_element(l4, 4, boost::format(Y("Cue track: %1%")) % static_cast<KaxCueTrack *>(l4)->GetValue());
|
|
|
|
else if (Is<KaxCueClusterPosition>(l4))
|
|
show_element(l4, 4, boost::format(Y("Cue cluster position: %1%")) % static_cast<KaxCueClusterPosition *>(l4)->GetValue());
|
|
|
|
else if (Is<KaxCueRelativePosition>(l4))
|
|
show_element(l4, 4, boost::format(Y("Cue relative position: %1%")) % static_cast<KaxCueRelativePosition *>(l4)->GetValue());
|
|
|
|
else if (Is<KaxCueDuration>(l4))
|
|
show_element(l4, 4, boost::format(Y("Cue duration: %1%")) % format_timestamp(static_cast<KaxCueDuration *>(l4)->GetValue() * s_tc_scale));
|
|
|
|
else if (Is<KaxCueBlockNumber>(l4))
|
|
show_element(l4, 4, boost::format(Y("Cue block number: %1%")) % static_cast<KaxCueBlockNumber *>(l4)->GetValue());
|
|
|
|
#if MATROSKA_VERSION >= 2
|
|
else if (Is<KaxCueCodecState>(l4))
|
|
show_element(l4, 4, boost::format(Y("Cue codec state: %1%")) % static_cast<KaxCueCodecState *>(l4)->GetValue());
|
|
|
|
else if (Is<KaxCueReference>(l4)) {
|
|
show_element(l4, 4, Y("Cue reference"));
|
|
|
|
for (auto l5 : *static_cast<EbmlMaster *>(l4))
|
|
if (Is<KaxCueRefTime>(l5))
|
|
show_element(l5, 5, boost::format(Y("Cue ref time: %|1$.3f|s")) % s_tc_scale % (static_cast<KaxCueRefTime *>(l5)->GetValue() / 1000000000.0));
|
|
|
|
else if (Is<KaxCueRefCluster>(l5))
|
|
show_element(l5, 5, boost::format(Y("Cue ref cluster: %1%")) % static_cast<KaxCueRefCluster *>(l5)->GetValue());
|
|
|
|
else if (Is<KaxCueRefNumber>(l5))
|
|
show_element(l5, 5, boost::format(Y("Cue ref number: %1%")) % static_cast<KaxCueRefNumber *>(l5)->GetValue());
|
|
|
|
else if (Is<KaxCueRefCodecState>(l5))
|
|
show_element(l5, 5, boost::format(Y("Cue ref codec state: %1%")) % static_cast<KaxCueRefCodecState *>(l5)->GetValue());
|
|
|
|
else if (!is_global(es, l5, 5))
|
|
show_unknown_element(l5, 5);
|
|
|
|
#endif // MATROSKA_VERSION >= 2
|
|
|
|
} else if (!is_global(es, l4, 4))
|
|
show_unknown_element(l4, 4);
|
|
|
|
} else if (!is_global(es, l3, 3))
|
|
show_unknown_element(l3, 3);
|
|
|
|
} else if (!is_global(es, l2, 2))
|
|
show_unknown_element(l2, 2);
|
|
}
|
|
|
|
void
|
|
handle_attachments(EbmlStream *&es,
|
|
int &upper_lvl_el,
|
|
EbmlElement *&l1) {
|
|
show_element(l1, 1, Y("Attachments"));
|
|
|
|
upper_lvl_el = 0;
|
|
EbmlElement *element_found = nullptr;
|
|
auto m1 = static_cast<EbmlMaster *>(l1);
|
|
read_master(m1, es, EBML_CONTEXT(l1), upper_lvl_el, element_found);
|
|
|
|
for (auto l2 : *m1)
|
|
if (Is<KaxAttached>(l2)) {
|
|
show_element(l2, 2, Y("Attached"));
|
|
|
|
for (auto l3 : *static_cast<EbmlMaster *>(l2))
|
|
if (Is<KaxFileDescription>(l3))
|
|
show_element(l3, 3, boost::format(Y("File description: %1%")) % static_cast<KaxFileDescription *>(l3)->GetValueUTF8());
|
|
|
|
else if (Is<KaxFileName>(l3))
|
|
show_element(l3, 3, boost::format(Y("File name: %1%")) % static_cast<KaxFileName *>(l3)->GetValueUTF8());
|
|
|
|
else if (Is<KaxMimeType>(l3))
|
|
show_element(l3, 3, boost::format(Y("Mime type: %1%")) % static_cast<KaxMimeType *>(l3)->GetValue());
|
|
|
|
else if (Is<KaxFileData>(l3))
|
|
show_element(l3, 3, boost::format(Y("File data, size: %1%")) % static_cast<KaxFileData *>(l3)->GetSize());
|
|
|
|
else if (Is<KaxFileUID>(l3))
|
|
show_element(l3, 3, boost::format(Y("File UID: %1%")) % static_cast<KaxFileUID *>(l3)->GetValue());
|
|
|
|
else if (!is_global(es, l3, 3))
|
|
show_unknown_element(l3, 3);
|
|
|
|
} else if (!is_global(es, l2, 2))
|
|
show_unknown_element(l2, 2);
|
|
}
|
|
|
|
void
|
|
handle_silent_track(EbmlStream *&es,
|
|
EbmlElement *&l2) {
|
|
show_element(l2, 2, "Silent Tracks");
|
|
|
|
for (auto l3 : *static_cast<EbmlMaster *>(l2))
|
|
if (Is<KaxClusterSilentTrackNumber>(l3))
|
|
show_element(l3, 3, boost::format(Y("Silent Track Number: %1%")) % static_cast<KaxClusterSilentTrackNumber *>(l3)->GetValue());
|
|
|
|
else if (!is_global(es, l3, 3))
|
|
show_unknown_element(l3, 3);
|
|
}
|
|
|
|
void
|
|
handle_block_group(EbmlStream *&es,
|
|
EbmlElement *&l2,
|
|
KaxCluster *&cluster) {
|
|
show_element(l2, 2, Y("Block group"));
|
|
|
|
std::vector<int> frame_sizes;
|
|
std::vector<uint32_t> frame_adlers;
|
|
std::vector<std::string> frame_hexdumps;
|
|
|
|
auto num_references = 0u;
|
|
int64_t lf_timecode = 0;
|
|
int64_t lf_tnum = 0;
|
|
int64_t frame_pos = 0;
|
|
|
|
float bduration = -1.0;
|
|
|
|
for (auto l3 : *static_cast<EbmlMaster *>(l2))
|
|
if (Is<KaxBlock>(l3)) {
|
|
KaxBlock &block = *static_cast<KaxBlock *>(l3);
|
|
block.SetParent(*cluster);
|
|
|
|
lf_timecode = block.GlobalTimecode();
|
|
lf_tnum = block.TrackNum();
|
|
bduration = -1.0;
|
|
frame_pos = block.GetElementPosition() + block.ElementSize();
|
|
|
|
show_element(l3, 3,
|
|
BF_BLOCK_GROUP_BLOCK_BASICS
|
|
% block.TrackNum()
|
|
% block.NumberFrames()
|
|
% (static_cast<double>(lf_timecode) / 1000000000.0)
|
|
% format_timestamp(lf_timecode, 3));
|
|
|
|
for (size_t i = 0; i < block.NumberFrames(); ++i) {
|
|
auto &data = block.GetBuffer(i);
|
|
auto adler = mtx::checksum::calculate_as_uint(mtx::checksum::algorithm_e::adler32, data.Buffer(), data.Size());
|
|
|
|
std::string adler_str;
|
|
if (g_options.m_calc_checksums)
|
|
adler_str = (BF_BLOCK_GROUP_BLOCK_ADLER % adler).str();
|
|
|
|
std::string hex;
|
|
if (g_options.m_show_hexdump)
|
|
hex = create_hexdump(data.Buffer(), data.Size());
|
|
|
|
show_element(nullptr, 4, BF_BLOCK_GROUP_BLOCK_FRAME % data.Size() % adler_str % hex);
|
|
|
|
frame_sizes.push_back(data.Size());
|
|
frame_adlers.push_back(adler);
|
|
frame_hexdumps.push_back(hex);
|
|
frame_pos -= data.Size();
|
|
}
|
|
|
|
} else if (Is<KaxBlockDuration>(l3)) {
|
|
auto duration = static_cast<KaxBlockDuration *>(l3)->GetValue();
|
|
bduration = static_cast<double>(duration) * s_tc_scale / 1000000.0;
|
|
show_element(l3, 3, BF_BLOCK_GROUP_DURATION % (duration * s_tc_scale / 1000000) % (duration * s_tc_scale % 1000000));
|
|
|
|
} else if (Is<KaxReferenceBlock>(l3)) {
|
|
++num_references;
|
|
|
|
int64_t reference = static_cast<KaxReferenceBlock *>(l3)->GetValue() * s_tc_scale;
|
|
|
|
if (0 >= reference)
|
|
show_element(l3, 3, BF_BLOCK_GROUP_REFERENCE_1 % (std::abs(reference) / 1000000) % (std::abs(reference) % 1000000));
|
|
|
|
else if (0 < reference)
|
|
show_element(l3, 3, BF_BLOCK_GROUP_REFERENCE_2 % (reference / 1000000) % (reference % 1000000));
|
|
|
|
} else if (Is<KaxReferencePriority>(l3))
|
|
show_element(l3, 3, BF_BLOCK_GROUP_REFERENCE_PRIORITY % static_cast<KaxReferencePriority *>(l3)->GetValue());
|
|
|
|
#if MATROSKA_VERSION >= 2
|
|
else if (Is<KaxBlockVirtual>(l3))
|
|
show_element(l3, 3, BF_BLOCK_GROUP_VIRTUAL % format_binary(static_cast<KaxBlockVirtual *>(l3)));
|
|
|
|
else if (Is<KaxReferenceVirtual>(l3))
|
|
show_element(l3, 3, BF_BLOCK_GROUP_REFERENCE_VIRTUAL % static_cast<KaxReferenceVirtual *>(l3)->GetValue());
|
|
|
|
else if (Is<KaxCodecState>(l3))
|
|
show_element(l3, 3, BF_CODEC_STATE % format_binary(static_cast<KaxCodecState *>(l3)));
|
|
|
|
else if (Is<KaxDiscardPadding>(l3)) {
|
|
auto value = static_cast<KaxDiscardPadding *>(l3)->GetValue();
|
|
show_element(l3, 3, BF_BLOCK_GROUP_DISCARD_PADDING % (static_cast<double>(value) / 1000000.0) % value);
|
|
}
|
|
|
|
#endif // MATROSKA_VERSION >= 2
|
|
else if (Is<KaxBlockAdditions>(l3)) {
|
|
show_element(l3, 3, Y("Additions"));
|
|
|
|
for (auto l4 : *static_cast<EbmlMaster *>(l3))
|
|
if (Is<KaxBlockMore>(l4)) {
|
|
show_element(l4, 4, Y("More"));
|
|
|
|
for (auto l5 : *static_cast<EbmlMaster *>(l4))
|
|
if (Is<KaxBlockAddID>(l5))
|
|
show_element(l5, 5, BF_BLOCK_GROUP_ADD_ID % static_cast<KaxBlockAddID *>(l5)->GetValue());
|
|
|
|
else if (Is<KaxBlockAdditional>(l5))
|
|
show_element(l5, 5, BF_BLOCK_GROUP_ADDITIONAL % format_binary(static_cast<KaxBlockAdditional *>(l5)));
|
|
|
|
else if (!is_global(es, l5, 5))
|
|
show_unknown_element(l5, 5);
|
|
|
|
} else if (!is_global(es, l4, 4))
|
|
show_unknown_element(l4, 4);
|
|
|
|
} else if (Is<KaxSlices>(l3)) {
|
|
show_element(l3, 3, Y("Slices"));
|
|
|
|
for (auto l4 : *static_cast<EbmlMaster *>(l3))
|
|
if (Is<KaxTimeSlice>(l4)) {
|
|
show_element(l4, 4, Y("Time slice"));
|
|
|
|
for (auto l5 : *static_cast<EbmlMaster *>(l4))
|
|
if (Is<KaxSliceLaceNumber>(l5))
|
|
show_element(l5, 5, BF_BLOCK_GROUP_SLICE_LACE % static_cast<KaxSliceLaceNumber *>(l5)->GetValue());
|
|
|
|
else if (Is<KaxSliceFrameNumber>(l5))
|
|
show_element(l5, 5, BF_BLOCK_GROUP_SLICE_FRAME % static_cast<KaxSliceFrameNumber *>(l5)->GetValue());
|
|
|
|
else if (Is<KaxSliceDelay>(l5))
|
|
show_element(l5, 5, BF_BLOCK_GROUP_SLICE_DELAY % (static_cast<double>(static_cast<KaxSliceDelay *>(l5)->GetValue()) * s_tc_scale / 1000000.0));
|
|
|
|
else if (Is<KaxSliceDuration>(l5))
|
|
show_element(l5, 5, BF_BLOCK_GROUP_SLICE_DURATION % (static_cast<double>(static_cast<KaxSliceDuration *>(l5)->GetValue()) * s_tc_scale / 1000000.0));
|
|
|
|
else if (Is<KaxSliceBlockAddID>(l5))
|
|
show_element(l5, 5, BF_BLOCK_GROUP_SLICE_ADD_ID % static_cast<KaxSliceBlockAddID *>(l5)->GetValue());
|
|
|
|
else if (!is_global(es, l5, 5))
|
|
show_unknown_element(l5, 5);
|
|
|
|
} else if (!is_global(es, l4, 4))
|
|
show_unknown_element(l4, 4);
|
|
|
|
} else if (!is_global(es, l3, 3))
|
|
show_unknown_element(l3, 3);
|
|
|
|
if (g_options.m_show_summary) {
|
|
std::string position;
|
|
size_t fidx;
|
|
|
|
for (fidx = 0; fidx < frame_sizes.size(); fidx++) {
|
|
if (1 <= g_options.m_verbose) {
|
|
position = (BF_BLOCK_GROUP_SUMMARY_POSITION % frame_pos).str();
|
|
frame_pos += frame_sizes[fidx];
|
|
}
|
|
|
|
if (bduration != -1.0)
|
|
mxinfo(BF_BLOCK_GROUP_SUMMARY_WITH_DURATION
|
|
% (num_references >= 2 ? 'B' : num_references == 1 ? 'P' : 'I')
|
|
% lf_tnum
|
|
% std::llround(lf_timecode / 1000000.0)
|
|
% format_timestamp(lf_timecode, 3)
|
|
% bduration
|
|
% frame_sizes[fidx]
|
|
% frame_adlers[fidx]
|
|
% frame_hexdumps[fidx]
|
|
% position);
|
|
else
|
|
mxinfo(BF_BLOCK_GROUP_SUMMARY_NO_DURATION
|
|
% (num_references >= 2 ? 'B' : num_references == 1 ? 'P' : 'I')
|
|
% lf_tnum
|
|
% std::llround(lf_timecode / 1000000.0)
|
|
% format_timestamp(lf_timecode, 3)
|
|
% frame_sizes[fidx]
|
|
% frame_adlers[fidx]
|
|
% frame_hexdumps[fidx]
|
|
% position);
|
|
}
|
|
|
|
} else if (g_options.m_verbose > 2)
|
|
show_element(nullptr, 2,
|
|
BF_BLOCK_GROUP_SUMMARY_V2
|
|
% (num_references >= 2 ? 'B' : num_references == 1 ? 'P' : 'I')
|
|
% lf_tnum
|
|
% std::llround(lf_timecode / 1000000.0));
|
|
|
|
track_info_t &tinfo = s_track_info[lf_tnum];
|
|
|
|
tinfo.m_blocks += frame_sizes.size();
|
|
tinfo.m_blocks_by_ref_num[std::min(num_references, 2u)] += frame_sizes.size();
|
|
tinfo.m_min_timecode = std::min(tinfo.m_min_timecode, lf_timecode);
|
|
tinfo.m_size += boost::accumulate(frame_sizes, 0);
|
|
|
|
if (!tinfo.max_timecode_unset() && (tinfo.m_max_timecode >= lf_timecode))
|
|
return;
|
|
|
|
tinfo.m_max_timecode = lf_timecode;
|
|
|
|
if (-1 == bduration)
|
|
tinfo.m_add_duration_for_n_packets = frame_sizes.size();
|
|
else {
|
|
tinfo.m_max_timecode += bduration * 1000000.0;
|
|
tinfo.m_add_duration_for_n_packets = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
handle_simple_block(EbmlStream *&es,
|
|
EbmlElement *&l2,
|
|
KaxCluster *&cluster) {
|
|
std::vector<int> frame_sizes;
|
|
std::vector<uint32_t> frame_adlers;
|
|
|
|
KaxSimpleBlock &block = *static_cast<KaxSimpleBlock *>(l2);
|
|
block.SetParent(*cluster);
|
|
|
|
int64_t frame_pos = block.GetElementPosition() + block.ElementSize();
|
|
auto timecode_ns = mtx::math::to_signed(block.GlobalTimecode());
|
|
auto timecode_ms = std::llround(static_cast<double>(timecode_ns) / 1000000.0);
|
|
track_info_t &tinfo = s_track_info[block.TrackNum()];
|
|
|
|
std::string info;
|
|
if (block.IsKeyframe())
|
|
info = Y("key, ");
|
|
if (block.IsDiscardable())
|
|
info += Y("discardable, ");
|
|
|
|
show_element(l2, 2,
|
|
BF_SIMPLE_BLOCK_BASICS
|
|
% info
|
|
% block.TrackNum()
|
|
% block.NumberFrames()
|
|
% (timecode_ns / 1000000000.0)
|
|
% format_timestamp(timecode_ns, 3));
|
|
|
|
int i;
|
|
for (i = 0; i < (int)block.NumberFrames(); i++) {
|
|
DataBuffer &data = block.GetBuffer(i);
|
|
uint32_t adler = mtx::checksum::calculate_as_uint(mtx::checksum::algorithm_e::adler32, data.Buffer(), data.Size());
|
|
|
|
std::string adler_str;
|
|
if (g_options.m_calc_checksums)
|
|
adler_str = (BF_SIMPLE_BLOCK_ADLER % adler).str();
|
|
|
|
std::string hex;
|
|
if (g_options.m_show_hexdump)
|
|
hex = create_hexdump(data.Buffer(), data.Size());
|
|
|
|
show_element(nullptr, 3, BF_SIMPLE_BLOCK_FRAME % data.Size() % adler_str % hex);
|
|
|
|
frame_sizes.push_back(data.Size());
|
|
frame_adlers.push_back(adler);
|
|
frame_pos -= data.Size();
|
|
}
|
|
|
|
if (g_options.m_show_summary) {
|
|
std::string position;
|
|
size_t fidx;
|
|
|
|
for (fidx = 0; fidx < frame_sizes.size(); fidx++) {
|
|
if (1 <= g_options.m_verbose) {
|
|
position = (BF_SIMPLE_BLOCK_POSITION % frame_pos).str();
|
|
frame_pos += frame_sizes[fidx];
|
|
}
|
|
|
|
mxinfo(BF_SIMPLE_BLOCK_SUMMARY
|
|
% (block.IsKeyframe() ? 'I' : block.IsDiscardable() ? 'B' : 'P')
|
|
% block.TrackNum()
|
|
% timecode_ms
|
|
% format_timestamp(timecode_ns, 3)
|
|
% frame_sizes[fidx]
|
|
% frame_adlers[fidx]
|
|
% position);
|
|
}
|
|
|
|
} else if (g_options.m_verbose > 2)
|
|
show_element(nullptr, 2,
|
|
BF_SIMPLE_BLOCK_SUMMARY_V2
|
|
% (block.IsKeyframe() ? 'I' : block.IsDiscardable() ? 'B' : 'P')
|
|
% block.TrackNum()
|
|
% timecode_ms);
|
|
|
|
tinfo.m_blocks += block.NumberFrames();
|
|
tinfo.m_blocks_by_ref_num[block.IsKeyframe() ? 0 : block.IsDiscardable() ? 2 : 1] += block.NumberFrames();
|
|
tinfo.m_min_timecode = std::min(tinfo.m_min_timecode, static_cast<int64_t>(timecode_ns));
|
|
tinfo.m_max_timecode = std::max(tinfo.max_timecode_unset() ? 0 : tinfo.m_max_timecode, static_cast<int64_t>(timecode_ns));
|
|
tinfo.m_add_duration_for_n_packets = block.NumberFrames();
|
|
tinfo.m_size += boost::accumulate(frame_sizes, 0);
|
|
}
|
|
|
|
void
|
|
handle_cluster(EbmlStream *&es,
|
|
int &upper_lvl_el,
|
|
EbmlElement *&l1,
|
|
int64_t file_size) {
|
|
auto cluster = static_cast<KaxCluster *>(l1);
|
|
|
|
if (g_options.m_use_gui)
|
|
ui_show_progress(100 * cluster->GetElementPosition() / file_size, Y("Parsing file"));
|
|
|
|
upper_lvl_el = 0;
|
|
EbmlElement *element_found = nullptr;
|
|
auto m1 = static_cast<EbmlMaster *>(l1);
|
|
read_master(m1, es, EBML_CONTEXT(l1), upper_lvl_el, element_found);
|
|
|
|
cluster->InitTimecode(FindChildValue<KaxClusterTimecode>(m1), s_tc_scale);
|
|
|
|
for (auto l2 : *m1)
|
|
if (Is<KaxClusterTimecode>(l2))
|
|
show_element(l2, 2, BF_CLUSTER_TIMECODE % (static_cast<double>(static_cast<KaxClusterTimecode *>(l2)->GetValue()) * s_tc_scale / 1000000000.0));
|
|
|
|
else if (Is<KaxClusterPosition>(l2))
|
|
show_element(l2, 2, BF_CLUSTER_POSITION % static_cast<KaxClusterPosition *>(l2)->GetValue());
|
|
|
|
else if (Is<KaxClusterPrevSize>(l2))
|
|
show_element(l2, 2, BF_CLUSTER_PREVIOUS_SIZE % static_cast<KaxClusterPrevSize *>(l2)->GetValue());
|
|
|
|
else if (Is<KaxClusterSilentTracks>(l2))
|
|
handle_silent_track(es, l2);
|
|
|
|
else if (Is<KaxBlockGroup>(l2))
|
|
handle_block_group(es, l2, cluster);
|
|
|
|
else if (Is<KaxSimpleBlock>(l2))
|
|
handle_simple_block(es, l2, cluster);
|
|
|
|
else if (!is_global(es, l2, 2))
|
|
show_unknown_element(l2, 2);
|
|
}
|
|
|
|
void
|
|
handle_elements_rec(EbmlStream *es,
|
|
int level,
|
|
EbmlElement *e,
|
|
mtx::xml::ebml_converter_c const &converter) {
|
|
static boost::format s_bf_handle_elements_rec("%1%: %2%");
|
|
static std::vector<std::string> const s_output_as_timecode{ "ChapterTimeStart", "ChapterTimeEnd" };
|
|
|
|
std::string elt_name = converter.get_tag_name(*e);
|
|
|
|
if (dynamic_cast<EbmlMaster *>(e)) {
|
|
show_element(e, level, elt_name);
|
|
for (auto child : *static_cast<EbmlMaster *>(e))
|
|
handle_elements_rec(es, level + 1, child, converter);
|
|
|
|
} else if (dynamic_cast<EbmlUInteger *>(e)) {
|
|
if (brng::find(s_output_as_timecode, elt_name) != s_output_as_timecode.end())
|
|
show_element(e, level, s_bf_handle_elements_rec % elt_name % format_timestamp(static_cast<EbmlUInteger *>(e)->GetValue()));
|
|
else
|
|
show_element(e, level, s_bf_handle_elements_rec % elt_name % static_cast<EbmlUInteger *>(e)->GetValue());
|
|
|
|
} else if (dynamic_cast<EbmlSInteger *>(e))
|
|
show_element(e, level, s_bf_handle_elements_rec % elt_name % static_cast<EbmlSInteger *>(e)->GetValue());
|
|
|
|
else if (dynamic_cast<EbmlString *>(e))
|
|
show_element(e, level, s_bf_handle_elements_rec % elt_name % static_cast<EbmlString *>(e)->GetValue());
|
|
|
|
else if (dynamic_cast<EbmlUnicodeString *>(e))
|
|
show_element(e, level, s_bf_handle_elements_rec % elt_name % static_cast<EbmlUnicodeString *>(e)->GetValueUTF8());
|
|
|
|
else if (dynamic_cast<EbmlBinary *>(e))
|
|
show_element(e, level, s_bf_handle_elements_rec % elt_name % format_binary(static_cast<EbmlBinary *>(e)));
|
|
|
|
else
|
|
assert(false);
|
|
}
|
|
|
|
void
|
|
handle_chapters(EbmlStream *&es,
|
|
int &upper_lvl_el,
|
|
EbmlElement *&l1) {
|
|
show_element(l1, 1, Y("Chapters"));
|
|
|
|
upper_lvl_el = 0;
|
|
EbmlMaster *m1 = static_cast<EbmlMaster *>(l1);
|
|
EbmlElement *element_found = nullptr;
|
|
read_master(m1, es, EBML_CONTEXT(l1), upper_lvl_el, element_found);
|
|
|
|
mtx::xml::ebml_chapters_converter_c converter;
|
|
for (auto l2 : *static_cast<EbmlMaster *>(l1))
|
|
handle_elements_rec(es, 2, l2, converter);
|
|
}
|
|
|
|
void
|
|
handle_tags(EbmlStream *&es,
|
|
int &upper_lvl_el,
|
|
EbmlElement *&l1) {
|
|
show_element(l1, 1, Y("Tags"));
|
|
|
|
upper_lvl_el = 0;
|
|
EbmlMaster *m1 = static_cast<EbmlMaster *>(l1);
|
|
EbmlElement *element_found = nullptr;
|
|
read_master(m1, es, EBML_CONTEXT(l1), upper_lvl_el, element_found);
|
|
|
|
mtx::xml::ebml_tags_converter_c converter;
|
|
for (auto l2 : *static_cast<EbmlMaster *>(l1))
|
|
handle_elements_rec(es, 2, l2, converter);
|
|
}
|
|
|
|
void
|
|
handle_ebml_head(EbmlElement *l0,
|
|
mm_io_cptr in,
|
|
EbmlStream *es) {
|
|
show_element(l0, 0, Y("EBML head"));
|
|
|
|
while (in_parent(l0)) {
|
|
int upper_lvl_el = 0;
|
|
EbmlElement *e = es->FindNextElement(EBML_CONTEXT(l0), upper_lvl_el, 0xFFFFFFFFL, true);
|
|
|
|
if (!e)
|
|
return;
|
|
|
|
e->ReadData(*in);
|
|
|
|
if (Is<EVersion>(e))
|
|
show_element(e, 1, boost::format(Y("EBML version: %1%")) % static_cast<EbmlUInteger *>(e)->GetValue());
|
|
|
|
else if (Is<EReadVersion>(e))
|
|
show_element(e, 1, boost::format(Y("EBML read version: %1%")) % static_cast<EbmlUInteger *>(e)->GetValue());
|
|
|
|
else if (Is<EMaxIdLength>(e))
|
|
show_element(e, 1, boost::format(Y("EBML maximum ID length: %1%")) % static_cast<EbmlUInteger *>(e)->GetValue());
|
|
|
|
else if (Is<EMaxSizeLength>(e))
|
|
show_element(e, 1, boost::format(Y("EBML maximum size length: %1%")) % static_cast<EbmlUInteger *>(e)->GetValue());
|
|
|
|
else if (Is<EDocType>(e))
|
|
show_element(e, 1, boost::format(Y("Doc type: %1%")) % std::string(*static_cast<EbmlString *>(e)));
|
|
|
|
else if (Is<EDocTypeVersion>(e))
|
|
show_element(e, 1, boost::format(Y("Doc type version: %1%")) % static_cast<EbmlUInteger *>(e)->GetValue());
|
|
|
|
else if (Is<EDocTypeReadVersion>(e))
|
|
show_element(e, 1, boost::format(Y("Doc type read version: %1%")) % static_cast<EbmlUInteger *>(e)->GetValue());
|
|
|
|
else
|
|
show_unknown_element(e, 1);
|
|
|
|
e->SkipData(*es, EBML_CONTEXT(e));
|
|
delete e;
|
|
}
|
|
}
|
|
|
|
void
|
|
handle_segment(EbmlElement *l0,
|
|
mm_io_cptr &in,
|
|
EbmlStream *es) {
|
|
auto file_size = in->get_size();
|
|
auto l1 = static_cast<EbmlElement *>(nullptr);
|
|
auto upper_lvl_el = 0;
|
|
auto kax_file = std::make_shared<kax_file_c>(*in);
|
|
|
|
kax_file->set_segment_end(*l0);
|
|
|
|
if (!l0->IsFiniteSize())
|
|
show_element(l0, 0, Y("Segment, size unknown"));
|
|
else
|
|
show_element(l0, 0, boost::format(Y("Segment, size %1%")) % l0->GetSize());
|
|
|
|
// Prevent reporting "first timecode after resync":
|
|
kax_file->set_timecode_scale(-1);
|
|
|
|
while ((l1 = kax_file->read_next_level1_element())) {
|
|
std::shared_ptr<EbmlElement> af_l1(l1);
|
|
|
|
if (Is<KaxInfo>(l1))
|
|
handle_info(es, upper_lvl_el, l1);
|
|
|
|
else if (Is<KaxTracks>(l1))
|
|
handle_tracks(es, upper_lvl_el, l1);
|
|
|
|
else if (Is<KaxSeekHead>(l1))
|
|
handle_seek_head(es, upper_lvl_el, l1);
|
|
|
|
else if (Is<KaxCluster>(l1)) {
|
|
show_element(l1, 1, Y("Cluster"));
|
|
if ((g_options.m_verbose == 0) && !g_options.m_show_summary)
|
|
return;
|
|
handle_cluster(es, upper_lvl_el, l1, file_size);
|
|
|
|
} else if (Is<KaxCues>(l1))
|
|
handle_cues(es, upper_lvl_el, l1);
|
|
|
|
// Weee! Attachments!
|
|
else if (Is<KaxAttachments>(l1))
|
|
handle_attachments(es, upper_lvl_el, l1);
|
|
|
|
else if (Is<KaxChapters>(l1))
|
|
handle_chapters(es, upper_lvl_el, l1);
|
|
|
|
// Let's handle some TAGS.
|
|
else if (Is<KaxTags>(l1))
|
|
handle_tags(es, upper_lvl_el, l1);
|
|
|
|
else if (!is_global(es, l1, 1))
|
|
show_unknown_element(l1, 1);
|
|
|
|
if (!in->setFilePointer2(l1->GetElementPosition() + kax_file->get_element_size(l1)))
|
|
break;
|
|
if (!in_parent(l0))
|
|
break;
|
|
} // while (l1)
|
|
}
|
|
|
|
void
|
|
display_track_info() {
|
|
if (!g_options.m_show_track_info)
|
|
return;
|
|
|
|
for (auto &track : s_tracks) {
|
|
track_info_t &tinfo = s_track_info[track->tnum];
|
|
|
|
if (tinfo.min_timecode_unset())
|
|
tinfo.m_min_timecode = 0;
|
|
if (tinfo.max_timecode_unset())
|
|
tinfo.m_max_timecode = tinfo.m_min_timecode;
|
|
|
|
int64_t duration = tinfo.m_max_timecode - tinfo.m_min_timecode;
|
|
duration += tinfo.m_add_duration_for_n_packets * track->default_duration;
|
|
|
|
mxinfo(boost::format(Y("Statistics for track number %1%: number of blocks: %2%; size in bytes: %3%; duration in seconds: %4%; approximate bitrate in bits/second: %5%\n"))
|
|
% track->tnum
|
|
% tinfo.m_blocks
|
|
% tinfo.m_size
|
|
% (duration / 1000000000.0)
|
|
% static_cast<uint64_t>(duration == 0 ? 0 : tinfo.m_size * 8000000000.0 / duration));
|
|
}
|
|
}
|
|
|
|
bool
|
|
process_file(const std::string &file_name) {
|
|
// Elements for different levels
|
|
|
|
s_tc_scale = TIMECODE_SCALE;
|
|
s_tracks.clear();
|
|
s_tracks_by_number.clear();
|
|
s_track_info.clear();
|
|
|
|
// open input file
|
|
mm_io_cptr in;
|
|
try {
|
|
in = mm_file_io_c::open(file_name);
|
|
} catch (mtx::mm_io::exception &ex) {
|
|
show_error((boost::format(Y("Error: Couldn't open source file %1% (%2%).")) % file_name % ex).str());
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
auto es_ptr = std::make_shared<EbmlStream>(*in);
|
|
auto es = es_ptr.get();
|
|
|
|
// Find the EbmlHead element. Must be the first one.
|
|
auto l0 = ebml_element_cptr{ es->FindNextID(EBML_INFO(EbmlHead), 0xFFFFFFFFL) };
|
|
if (!l0 || !Is<EbmlHead>(*l0)) {
|
|
show_error(Y("No EBML head found."));
|
|
return false;
|
|
}
|
|
|
|
handle_ebml_head(l0.get(), in, es);
|
|
l0->SkipData(*es, EBML_CONTEXT(l0));
|
|
|
|
while (1) {
|
|
// NEXT element must be a segment
|
|
l0 = ebml_element_cptr{ es->FindNextID(EBML_INFO(KaxSegment), 0xFFFFFFFFFFFFFFFFLL) };
|
|
if (!l0)
|
|
break;
|
|
|
|
if (!Is<KaxSegment>(*l0)) {
|
|
show_element(l0.get(), 0, Y("Unknown element"));
|
|
l0->SkipData(*es, EBML_CONTEXT(l0));
|
|
|
|
continue;
|
|
}
|
|
|
|
handle_segment(l0.get(), in, es);
|
|
|
|
l0->SkipData(*es, EBML_CONTEXT(l0));
|
|
|
|
if ((g_options.m_verbose == 0) && !g_options.m_show_summary)
|
|
break;
|
|
}
|
|
|
|
if (!g_options.m_use_gui && g_options.m_show_track_info)
|
|
display_track_info();
|
|
|
|
return true;
|
|
} catch (...) {
|
|
show_error(Y("Caught exception"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void
|
|
setup(char const *argv0,
|
|
std::string const &locale) {
|
|
mtx_common_init("mkvinfo", argv0);
|
|
|
|
init_locales(locale);
|
|
init_common_boost_formats();
|
|
|
|
version_info = get_version_info("mkvinfo", vif_full);
|
|
}
|
|
|
|
int
|
|
console_main() {
|
|
set_process_priority(-1);
|
|
|
|
if (g_options.m_file_name.empty())
|
|
mxerror(Y("No file name given.\n"));
|
|
|
|
return process_file(g_options.m_file_name.c_str()) ? 0 : 1;
|
|
}
|
|
|
|
int
|
|
main(int argc,
|
|
char **argv) {
|
|
setup(argv[0]);
|
|
|
|
g_options = info_cli_parser_c(command_line_utf8(argc, argv)).run();
|
|
|
|
init_common_boost_formats();
|
|
|
|
if (g_options.m_use_gui)
|
|
ui_run(argc, argv);
|
|
else
|
|
console_main();
|
|
|
|
mxexit();
|
|
}
|