Read track statistics from Matroska tags

This commit is contained in:
Martchus 2017-06-27 00:36:32 +02:00
parent c120b117c0
commit 66532353c7
6 changed files with 133 additions and 4 deletions

View File

@ -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
{

View File

@ -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()

View File

@ -60,7 +60,7 @@ protected:
private:
void parseSegmentInfo();
void fetchEditionEntryElements();
void readTrackStatisticsFromTags();
uint64 m_maxIdLength;
uint64 m_maxSizeLength;

View File

@ -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";
}
}
}
/*!

View File

@ -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");

View File

@ -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;
};