Read track statistics from Matroska tags
This commit is contained in:
parent
c120b117c0
commit
66532353c7
|
@ -472,7 +472,7 @@ inline byte AbstractTrack::channelConfig() const
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Returns the number of samples if known; otherwise returns 0.
|
* \brief Returns the number of samples/frames if known; otherwise returns 0.
|
||||||
*/
|
*/
|
||||||
inline uint64 AbstractTrack::sampleCount() const
|
inline uint64 AbstractTrack::sampleCount() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -610,6 +610,21 @@ void MatroskaContainer::parseSegmentInfo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Reads track-specific statistics from tags.
|
||||||
|
* \remarks Tags and tracks must have been parsed before calling this method.
|
||||||
|
* \sa MatroskaTrack::readStatisticsFromTags()
|
||||||
|
*/
|
||||||
|
void MatroskaContainer::readTrackStatisticsFromTags()
|
||||||
|
{
|
||||||
|
if(tracks().empty() || tags().empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(const auto &track : tracks()) {
|
||||||
|
track->readStatisticsFromTags(tags());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MatroskaContainer::internalParseTags()
|
void MatroskaContainer::internalParseTags()
|
||||||
{
|
{
|
||||||
static const string context("parsing tags of Matroska container");
|
static const string context("parsing tags of Matroska container");
|
||||||
|
@ -622,8 +637,8 @@ void MatroskaContainer::internalParseTags()
|
||||||
case MatroskaIds::Tag:
|
case MatroskaIds::Tag:
|
||||||
m_tags.emplace_back(make_unique<MatroskaTag>());
|
m_tags.emplace_back(make_unique<MatroskaTag>());
|
||||||
try {
|
try {
|
||||||
m_tags.back()->parse(*subElement);
|
m_tags.back()->parse(*subElement);
|
||||||
} catch(NoDataFoundException &) {
|
} catch(const NoDataFoundException &) {
|
||||||
m_tags.pop_back();
|
m_tags.pop_back();
|
||||||
} catch(const Failure &) {
|
} catch(const Failure &) {
|
||||||
addNotification(NotificationType::Critical, argsToString("Unable to parse tag ", m_tags.size(), '.'), context);
|
addNotification(NotificationType::Critical, argsToString("Unable to parse tag ", m_tags.size(), '.'), context);
|
||||||
|
@ -638,9 +653,11 @@ void MatroskaContainer::internalParseTags()
|
||||||
}
|
}
|
||||||
} catch(const Failure &) {
|
} catch(const Failure &) {
|
||||||
addNotification(NotificationType::Critical, "Element structure seems to be invalid.", context);
|
addNotification(NotificationType::Critical, "Element structure seems to be invalid.", context);
|
||||||
|
readTrackStatisticsFromTags();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
readTrackStatisticsFromTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MatroskaContainer::internalParseTracks()
|
void MatroskaContainer::internalParseTracks()
|
||||||
|
@ -672,9 +689,11 @@ void MatroskaContainer::internalParseTracks()
|
||||||
}
|
}
|
||||||
} catch(const Failure &) {
|
} catch(const Failure &) {
|
||||||
addNotification(NotificationType::Critical, "Element structure seems to be invalid.", context);
|
addNotification(NotificationType::Critical, "Element structure seems to be invalid.", context);
|
||||||
|
readTrackStatisticsFromTags();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
readTrackStatisticsFromTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MatroskaContainer::internalParseChapters()
|
void MatroskaContainer::internalParseChapters()
|
||||||
|
|
|
@ -60,7 +60,7 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void parseSegmentInfo();
|
void parseSegmentInfo();
|
||||||
void fetchEditionEntryElements();
|
void readTrackStatisticsFromTags();
|
||||||
|
|
||||||
uint64 m_maxIdLength;
|
uint64 m_maxIdLength;
|
||||||
uint64 m_maxSizeLength;
|
uint64 m_maxSizeLength;
|
||||||
|
|
|
@ -337,6 +337,35 @@ inline TAG_PARSER_EXPORT const char *termsOfUse() {
|
||||||
return "TERMS_OF_USE";
|
return "TERMS_OF_USE";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Encapsulates track-specific Matroska tag IDs written by mkvmerge 7.0.0 or newer.
|
||||||
|
* \sa https://github.com/mbunkus/mkvtoolnix/wiki/Automatic-tag-generation
|
||||||
|
*/
|
||||||
|
namespace TrackSpecific {
|
||||||
|
inline TAG_PARSER_EXPORT const char *numberOfBytes() {
|
||||||
|
return "NUMBER_OF_BYTES";
|
||||||
|
}
|
||||||
|
inline TAG_PARSER_EXPORT const char *numberOfFrames() {
|
||||||
|
return "NUMBER_OF_FRAMES";
|
||||||
|
}
|
||||||
|
inline TAG_PARSER_EXPORT const char *duration() {
|
||||||
|
return "DURATION";
|
||||||
|
}
|
||||||
|
/// \brief The track's bit rate in bits per second.
|
||||||
|
inline TAG_PARSER_EXPORT const char *bitrate() {
|
||||||
|
return "BPS";
|
||||||
|
}
|
||||||
|
inline TAG_PARSER_EXPORT const char *writingApp() {
|
||||||
|
return "_STATISTICS_WRITING_APP";
|
||||||
|
}
|
||||||
|
inline TAG_PARSER_EXPORT const char *writingDate() {
|
||||||
|
return "_STATISTICS_WRITING_DATE_UTC";
|
||||||
|
}
|
||||||
|
inline TAG_PARSER_EXPORT const char *statisticsTags() {
|
||||||
|
return "_STATISTICS_TAGS";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "./matroskatrack.h"
|
#include "./matroskatrack.h"
|
||||||
#include "./matroskacontainer.h"
|
#include "./matroskacontainer.h"
|
||||||
#include "./matroskaid.h"
|
#include "./matroskaid.h"
|
||||||
|
#include "./matroskatag.h"
|
||||||
|
|
||||||
#include "../avi/bitmapinfoheader.h"
|
#include "../avi/bitmapinfoheader.h"
|
||||||
|
|
||||||
|
@ -201,6 +202,81 @@ MediaFormat MatroskaTrack::codecIdToMediaFormat(const string &codecId)
|
||||||
return fmt;
|
return fmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \cond
|
||||||
|
|
||||||
|
template<typename PropertyType, typename ConversionFunction>
|
||||||
|
void MatroskaTrack::assignPropertyFromTagValue(const std::unique_ptr<MatroskaTag> &tag, const char *fieldId, PropertyType &property, const ConversionFunction &conversionFunction)
|
||||||
|
{
|
||||||
|
const TagValue &value = tag->value(fieldId);
|
||||||
|
if(!value.isEmpty()) {
|
||||||
|
try {
|
||||||
|
property = conversionFunction(value);
|
||||||
|
} catch(const ConversionException &) {
|
||||||
|
string message;
|
||||||
|
try {
|
||||||
|
message = argsToString("Ignoring invalid value \"", value.toString(TagTextEncoding::Utf8), "\" of \"", fieldId, '\"', '.');
|
||||||
|
} catch(const ConversionException &) {
|
||||||
|
message = argsToString("Ignoring invalid value of \"", fieldId, '\"', '.');
|
||||||
|
}
|
||||||
|
addNotification(NotificationType::Warning, message, argsToString("reading track statatistic from \"", tag->toString(), '\"'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename NumberType, Traits::EnableIf<std::is_integral<NumberType>>...>
|
||||||
|
NumberType tagValueToNumber(const TagValue &tagValue)
|
||||||
|
{
|
||||||
|
// optimization for Latin1/UTF-8 strings
|
||||||
|
if(tagValue.type() == TagDataType::Text) {
|
||||||
|
switch(tagValue.dataEncoding()) {
|
||||||
|
case TagTextEncoding::Latin1:
|
||||||
|
case TagTextEncoding::Utf8:
|
||||||
|
return bufferToNumber<NumberType>(tagValue.dataPointer(), tagValue.dataSize());
|
||||||
|
default:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// generic conversion
|
||||||
|
return stringToNumber<NumberType>(tagValue.toString(TagTextEncoding::Utf8));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename NumberType, Traits::EnableIf<std::is_floating_point<NumberType>>...>
|
||||||
|
NumberType tagValueToBitrate(const TagValue &tagValue)
|
||||||
|
{
|
||||||
|
return stringToNumber<NumberType>(tagValue.toString(TagTextEncoding::Utf8)) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \endcond
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Reads track-specific statistics from the specified \a tags.
|
||||||
|
* \remarks
|
||||||
|
* - Those statistics are generated might be generated by some Muxers, eg. mkvmerge 7.0.0 or newer.
|
||||||
|
* - Only tags targeting the track are considered. Hence the track ID must have been determined
|
||||||
|
* before (either by calling parseHeader() or setId()).
|
||||||
|
* \sa https://github.com/mbunkus/mkvtoolnix/wiki/Automatic-tag-generation for list of track-specific
|
||||||
|
* tag fields written by mkvmerge
|
||||||
|
*/
|
||||||
|
void MatroskaTrack::readStatisticsFromTags(const std::vector<std::unique_ptr<MatroskaTag> > &tags)
|
||||||
|
{
|
||||||
|
using namespace std::placeholders;
|
||||||
|
using namespace MatroskaTagIds::TrackSpecific;
|
||||||
|
for(const auto &tag : tags) {
|
||||||
|
const TagTarget &target = tag->target();
|
||||||
|
if(find(target.tracks().cbegin(), target.tracks().cend(), id()) == target.tracks().cend()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assignPropertyFromTagValue(tag, numberOfBytes(), m_size, &tagValueToNumber<uint64>);
|
||||||
|
assignPropertyFromTagValue(tag, numberOfFrames(), m_sampleCount, &tagValueToNumber<uint64>);
|
||||||
|
assignPropertyFromTagValue(tag, MatroskaTagIds::TrackSpecific::duration(), m_duration, bind(&TagValue::toTimeSpan, _1));
|
||||||
|
assignPropertyFromTagValue(tag, MatroskaTagIds::TrackSpecific::bitrate(), m_bitrate, &tagValueToBitrate<double>);
|
||||||
|
assignPropertyFromTagValue(tag, writingDate(), m_modificationTime, bind(&TagValue::toDateTime, _1));
|
||||||
|
if(m_creationTime.isNull()) {
|
||||||
|
m_creationTime = m_modificationTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MatroskaTrack::internalParseHeader()
|
void MatroskaTrack::internalParseHeader()
|
||||||
{
|
{
|
||||||
static const string context("parsing header of Matroska track");
|
static const string context("parsing header of Matroska track");
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace Media {
|
||||||
class EbmlElement;
|
class EbmlElement;
|
||||||
class MatroskaContainer;
|
class MatroskaContainer;
|
||||||
class MatroskaTrack;
|
class MatroskaTrack;
|
||||||
|
class MatroskaTag;
|
||||||
|
|
||||||
class TAG_PARSER_EXPORT MatroskaTrackHeaderMaker
|
class TAG_PARSER_EXPORT MatroskaTrackHeaderMaker
|
||||||
{
|
{
|
||||||
|
@ -55,6 +56,7 @@ public:
|
||||||
TrackType type() const;
|
TrackType type() const;
|
||||||
|
|
||||||
static MediaFormat codecIdToMediaFormat(const std::string &codecId);
|
static MediaFormat codecIdToMediaFormat(const std::string &codecId);
|
||||||
|
void readStatisticsFromTags(const std::vector<std::unique_ptr<MatroskaTag> > &tags);
|
||||||
MatroskaTrackHeaderMaker prepareMakingHeader() const;
|
MatroskaTrackHeaderMaker prepareMakingHeader() const;
|
||||||
void makeHeader(std::ostream &stream) const;
|
void makeHeader(std::ostream &stream) const;
|
||||||
|
|
||||||
|
@ -62,6 +64,9 @@ protected:
|
||||||
void internalParseHeader();
|
void internalParseHeader();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
template<typename PropertyType, typename ConversionFunction>
|
||||||
|
void assignPropertyFromTagValue(const std::unique_ptr<MatroskaTag> &tag, const char *fieldId, PropertyType &integer, const ConversionFunction &conversionFunction);
|
||||||
|
|
||||||
EbmlElement *m_trackElement;
|
EbmlElement *m_trackElement;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue