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
|
||||
{
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
static const string context("parsing tags of Matroska container");
|
||||
|
@ -622,8 +637,8 @@ void MatroskaContainer::internalParseTags()
|
|||
case MatroskaIds::Tag:
|
||||
m_tags.emplace_back(make_unique<MatroskaTag>());
|
||||
try {
|
||||
m_tags.back()->parse(*subElement);
|
||||
} catch(NoDataFoundException &) {
|
||||
m_tags.back()->parse(*subElement);
|
||||
} catch(const NoDataFoundException &) {
|
||||
m_tags.pop_back();
|
||||
} catch(const Failure &) {
|
||||
addNotification(NotificationType::Critical, argsToString("Unable to parse tag ", m_tags.size(), '.'), context);
|
||||
|
@ -638,9 +653,11 @@ void MatroskaContainer::internalParseTags()
|
|||
}
|
||||
} catch(const Failure &) {
|
||||
addNotification(NotificationType::Critical, "Element structure seems to be invalid.", context);
|
||||
readTrackStatisticsFromTags();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
readTrackStatisticsFromTags();
|
||||
}
|
||||
|
||||
void MatroskaContainer::internalParseTracks()
|
||||
|
@ -672,9 +689,11 @@ void MatroskaContainer::internalParseTracks()
|
|||
}
|
||||
} catch(const Failure &) {
|
||||
addNotification(NotificationType::Critical, "Element structure seems to be invalid.", context);
|
||||
readTrackStatisticsFromTags();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
readTrackStatisticsFromTags();
|
||||
}
|
||||
|
||||
void MatroskaContainer::internalParseChapters()
|
||||
|
|
|
@ -60,7 +60,7 @@ protected:
|
|||
|
||||
private:
|
||||
void parseSegmentInfo();
|
||||
void fetchEditionEntryElements();
|
||||
void readTrackStatisticsFromTags();
|
||||
|
||||
uint64 m_maxIdLength;
|
||||
uint64 m_maxSizeLength;
|
||||
|
|
|
@ -337,6 +337,35 @@ inline TAG_PARSER_EXPORT const char *termsOfUse() {
|
|||
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 "./matroskacontainer.h"
|
||||
#include "./matroskaid.h"
|
||||
#include "./matroskatag.h"
|
||||
|
||||
#include "../avi/bitmapinfoheader.h"
|
||||
|
||||
|
@ -201,6 +202,81 @@ MediaFormat MatroskaTrack::codecIdToMediaFormat(const string &codecId)
|
|||
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()
|
||||
{
|
||||
static const string context("parsing header of Matroska track");
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace Media {
|
|||
class EbmlElement;
|
||||
class MatroskaContainer;
|
||||
class MatroskaTrack;
|
||||
class MatroskaTag;
|
||||
|
||||
class TAG_PARSER_EXPORT MatroskaTrackHeaderMaker
|
||||
{
|
||||
|
@ -55,6 +56,7 @@ public:
|
|||
TrackType type() const;
|
||||
|
||||
static MediaFormat codecIdToMediaFormat(const std::string &codecId);
|
||||
void readStatisticsFromTags(const std::vector<std::unique_ptr<MatroskaTag> > &tags);
|
||||
MatroskaTrackHeaderMaker prepareMakingHeader() const;
|
||||
void makeHeader(std::ostream &stream) const;
|
||||
|
||||
|
@ -62,6 +64,9 @@ protected:
|
|||
void internalParseHeader();
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue