Improve handling time values of MP4 files
* Cache result of `verifyPresentTrackHeader()` instead of running the code unnecassarily multiple times * Do not tamper with existing/raw values by default to avoid inconsistencies through rounding errors and possibly fix https://github.com/Martchus/tageditor/issues/80 * Avoid conversions to double (depending on the time scale rounding errors might still occur)
This commit is contained in:
parent
d19a09db5b
commit
503fb725a2
|
@ -8,9 +8,9 @@ set(META_APP_NAME "Tag Parser")
|
||||||
set(META_APP_AUTHOR "Martchus")
|
set(META_APP_AUTHOR "Martchus")
|
||||||
set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}")
|
set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}")
|
||||||
set(META_APP_DESCRIPTION "C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags")
|
set(META_APP_DESCRIPTION "C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags")
|
||||||
set(META_VERSION_MAJOR 10)
|
set(META_VERSION_MAJOR 11)
|
||||||
set(META_VERSION_MINOR 4)
|
set(META_VERSION_MINOR 0)
|
||||||
set(META_VERSION_PATCH 1)
|
set(META_VERSION_PATCH 0)
|
||||||
set(META_REQUIRED_CPP_UNIT_VERSION 1.14.0)
|
set(META_REQUIRED_CPP_UNIT_VERSION 1.14.0)
|
||||||
set(META_ADD_DEFAULT_CPP_UNIT_TEST_APPLICATION ON)
|
set(META_ADD_DEFAULT_CPP_UNIT_TEST_APPLICATION ON)
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ set(RES_FILES "${LANGUAGE_HEADER_ISO_639_2}")
|
||||||
set(CONFIGURATION_PACKAGE_SUFFIX
|
set(CONFIGURATION_PACKAGE_SUFFIX
|
||||||
""
|
""
|
||||||
CACHE STRING "sets the suffix for find_package() calls to packages configured via c++utilities")
|
CACHE STRING "sets the suffix for find_package() calls to packages configured via c++utilities")
|
||||||
find_package(c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.10.0 REQUIRED)
|
find_package(c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.13.0 REQUIRED)
|
||||||
use_cpp_utilities(VISIBILITY PUBLIC)
|
use_cpp_utilities(VISIBILITY PUBLIC)
|
||||||
|
|
||||||
# link against a possibly required extra library for std::filesystem
|
# link against a possibly required extra library for std::filesystem
|
||||||
|
|
|
@ -91,7 +91,7 @@ MediaFileInfo::MediaFileInfo(std::string &&path)
|
||||||
, m_tagPosition(ElementPosition::BeforeData)
|
, m_tagPosition(ElementPosition::BeforeData)
|
||||||
, m_indexPosition(ElementPosition::BeforeData)
|
, m_indexPosition(ElementPosition::BeforeData)
|
||||||
, m_fileHandlingFlags(MediaFileHandlingFlags::ForceRewrite | MediaFileHandlingFlags::ForceTagPosition | MediaFileHandlingFlags::ForceIndexPosition
|
, m_fileHandlingFlags(MediaFileHandlingFlags::ForceRewrite | MediaFileHandlingFlags::ForceTagPosition | MediaFileHandlingFlags::ForceIndexPosition
|
||||||
| MediaFileHandlingFlags::NormalizeKnownTagFieldIds)
|
| MediaFileHandlingFlags::NormalizeKnownTagFieldIds | MediaFileHandlingFlags::PreserveRawTimingValues)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ enum class MediaFileHandlingFlags : std::uint64_t {
|
||||||
ForceTagPosition = (1 << 2), /**< enforces the tag position when applying changes, see remarks of MediaFileInfo::setTagPosition() */
|
ForceTagPosition = (1 << 2), /**< enforces the tag position when applying changes, see remarks of MediaFileInfo::setTagPosition() */
|
||||||
ForceIndexPosition = (1 << 3), /**< enforces the index position when applying changes, see remarks of MediaFileInfo::setIndexPosition() */
|
ForceIndexPosition = (1 << 3), /**< enforces the index position when applying changes, see remarks of MediaFileInfo::setIndexPosition() */
|
||||||
NormalizeKnownTagFieldIds = (1 << 4), /**< normalizes known tag field IDs when parsing to match the tag specification's recommendations */
|
NormalizeKnownTagFieldIds = (1 << 4), /**< normalizes known tag field IDs when parsing to match the tag specification's recommendations */
|
||||||
|
PreserveRawTimingValues = (1 << 8), /**< preverves raw timing values (so far only used when making MP4 tracks) */
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace TagParser
|
} // namespace TagParser
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#define CHRONO_UTILITIES_TIMESPAN_INTEGER_SCALE_OVERLOADS
|
||||||
|
|
||||||
#include "./mp4container.h"
|
#include "./mp4container.h"
|
||||||
#include "./mp4ids.h"
|
#include "./mp4ids.h"
|
||||||
|
|
||||||
|
@ -130,16 +132,22 @@ void Mp4Container::internalParseTracks(Diagnostics &diag, AbortableProgressFeedb
|
||||||
stream().seekg(3, ios_base::cur); // skip flags
|
stream().seekg(3, ios_base::cur); // skip flags
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case 0:
|
case 0:
|
||||||
m_creationTime = DateTime::fromDate(1904, 1, 1) + TimeSpan::fromSeconds(reader().readUInt32BE());
|
m_creationTime
|
||||||
m_modificationTime = DateTime::fromDate(1904, 1, 1) + TimeSpan::fromSeconds(reader().readUInt32BE());
|
= DateTime::fromDate(1904, 1, 1) + TimeSpan::fromSeconds(static_cast<TimeSpan::TickType>(reader().readUInt32BE()));
|
||||||
|
m_modificationTime
|
||||||
|
= DateTime::fromDate(1904, 1, 1) + TimeSpan::fromSeconds(static_cast<TimeSpan::TickType>(reader().readUInt32BE()));
|
||||||
m_timeScale = reader().readUInt32BE();
|
m_timeScale = reader().readUInt32BE();
|
||||||
m_duration = TimeSpan::fromSeconds(static_cast<double>(reader().readUInt32BE()) / static_cast<double>(m_timeScale));
|
m_duration = TimeSpan::fromSeconds(static_cast<TimeSpan::TickType>(reader().readUInt32BE()))
|
||||||
|
/ static_cast<TimeSpan::TickType>(m_timeScale);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
m_creationTime = DateTime::fromDate(1904, 1, 1) + TimeSpan::fromSeconds(static_cast<double>(reader().readUInt64BE()));
|
m_creationTime
|
||||||
m_modificationTime = DateTime::fromDate(1904, 1, 1) + TimeSpan::fromSeconds(static_cast<double>(reader().readUInt64BE()));
|
= DateTime::fromDate(1904, 1, 1) + TimeSpan::fromSeconds(static_cast<TimeSpan::TickType>(reader().readUInt64BE()));
|
||||||
|
m_modificationTime
|
||||||
|
= DateTime::fromDate(1904, 1, 1) + TimeSpan::fromSeconds(static_cast<TimeSpan::TickType>(reader().readUInt64BE()));
|
||||||
m_timeScale = reader().readUInt32BE();
|
m_timeScale = reader().readUInt32BE();
|
||||||
m_duration = TimeSpan::fromSeconds(static_cast<double>(reader().readUInt64BE()) / static_cast<double>(m_timeScale));
|
m_duration = TimeSpan::fromSeconds(static_cast<TimeSpan::TickType>(reader().readUInt64BE()))
|
||||||
|
/ static_cast<TimeSpan::TickType>(m_timeScale);
|
||||||
break;
|
break;
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
|
|
123
mp4/mp4track.cpp
123
mp4/mp4track.cpp
|
@ -1,3 +1,5 @@
|
||||||
|
#define CHRONO_UTILITIES_TIMESPAN_INTEGER_SCALE_OVERLOADS
|
||||||
|
|
||||||
#include "./mp4track.h"
|
#include "./mp4track.h"
|
||||||
#include "./mp4atom.h"
|
#include "./mp4atom.h"
|
||||||
#include "./mp4container.h"
|
#include "./mp4container.h"
|
||||||
|
@ -12,6 +14,7 @@
|
||||||
#include "../mpegaudio/mpegaudioframestream.h"
|
#include "../mpegaudio/mpegaudioframestream.h"
|
||||||
|
|
||||||
#include "../exceptions.h"
|
#include "../exceptions.h"
|
||||||
|
#include "../mediafileinfo.h"
|
||||||
#include "../mediaformat.h"
|
#include "../mediaformat.h"
|
||||||
|
|
||||||
#include <c++utilities/conversion/stringbuilder.h>
|
#include <c++utilities/conversion/stringbuilder.h>
|
||||||
|
@ -31,10 +34,11 @@ namespace TagParser {
|
||||||
* \brief The Mp4Timings struct holds timing values found in multiple MP4 atoms.
|
* \brief The Mp4Timings struct holds timing values found in multiple MP4 atoms.
|
||||||
*/
|
*/
|
||||||
struct Mp4Timings {
|
struct Mp4Timings {
|
||||||
std::uint64_t creationTime = 0;
|
std::uint64_t tkhdCreationTime, mdhdCreationTime = 0;
|
||||||
std::uint64_t modificationTime = 0;
|
std::uint64_t tkhdModificationTime, mdhdModificationTime = 0;
|
||||||
std::uint64_t duration = 0;
|
std::uint64_t tkhdDuration, mdhdDuration = 0;
|
||||||
constexpr std::uint8_t requiredVersion() const;
|
constexpr std::uint8_t requiredTkhdVersion() const;
|
||||||
|
constexpr std::uint8_t requiredMdhdVersion() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -69,12 +73,20 @@ private:
|
||||||
bool discardBuffer = false;
|
bool discardBuffer = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr std::uint8_t Mp4Timings::requiredVersion() const
|
constexpr std::uint8_t Mp4Timings::requiredTkhdVersion() const
|
||||||
{
|
{
|
||||||
return (creationTime > std::numeric_limits<std::uint32_t>::max() || modificationTime > std::numeric_limits<std::uint32_t>::max()
|
return (tkhdCreationTime > std::numeric_limits<std::uint32_t>::max() || tkhdModificationTime > std::numeric_limits<std::uint32_t>::max()
|
||||||
|| duration > std::numeric_limits<std::uint32_t>::max())
|
|| tkhdDuration > std::numeric_limits<std::uint32_t>::max())
|
||||||
? 1
|
? 1
|
||||||
: 0;
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::uint8_t Mp4Timings::requiredMdhdVersion() const
|
||||||
|
{
|
||||||
|
return (mdhdCreationTime > std::numeric_limits<std::uint32_t>::max() || mdhdModificationTime > std::numeric_limits<std::uint32_t>::max()
|
||||||
|
|| mdhdDuration > std::numeric_limits<std::uint32_t>::max())
|
||||||
|
? 1
|
||||||
|
: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Dates within MP4 tracks are expressed as the number of seconds since this date.
|
/// \brief Dates within MP4 tracks are expressed as the number of seconds since this date.
|
||||||
|
@ -156,6 +168,12 @@ Mp4Track::Mp4Track(Mp4Atom &trakAtom)
|
||||||
, m_chunkOffsetSize(4)
|
, m_chunkOffsetSize(4)
|
||||||
, m_chunkCount(0)
|
, m_chunkCount(0)
|
||||||
, m_sampleToChunkEntryCount(0)
|
, m_sampleToChunkEntryCount(0)
|
||||||
|
, m_rawTkhdCreationTime(0)
|
||||||
|
, m_rawMdhdCreationTime(0)
|
||||||
|
, m_rawTkhdModificationTime(0)
|
||||||
|
, m_rawMdhdModificationTime(0)
|
||||||
|
, m_rawTkhdDuration(0)
|
||||||
|
, m_rawMdhdDuration(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,11 +423,14 @@ void Mp4Track::addChunkSizeEntries(
|
||||||
* \brief Verifies the present track header (tkhd atom) and returns relevant information for making a new track header
|
* \brief Verifies the present track header (tkhd atom) and returns relevant information for making a new track header
|
||||||
* based on it.
|
* based on it.
|
||||||
*/
|
*/
|
||||||
TrackHeaderInfo Mp4Track::verifyPresentTrackHeader() const
|
const TrackHeaderInfo &Mp4Track::verifyPresentTrackHeader() const
|
||||||
{
|
{
|
||||||
TrackHeaderInfo info;
|
if (m_trackHeaderInfo) {
|
||||||
|
return *m_trackHeaderInfo;
|
||||||
|
}
|
||||||
|
|
||||||
// return the default TrackHeaderInfo in case there is no track header prsent
|
// return the default TrackHeaderInfo in case there is no track header prsent
|
||||||
|
auto &info = *(m_trackHeaderInfo = std::make_unique<TrackHeaderInfo>());
|
||||||
if (!m_tkhdAtom) {
|
if (!m_tkhdAtom) {
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
@ -447,7 +468,7 @@ TrackHeaderInfo Mp4Track::verifyPresentTrackHeader() const
|
||||||
// determine required size
|
// determine required size
|
||||||
info.requiredSize = m_tkhdAtom->dataSize() + 8;
|
info.requiredSize = m_tkhdAtom->dataSize() + 8;
|
||||||
info.timings = computeTimings();
|
info.timings = computeTimings();
|
||||||
info.timingsVersion = info.timings.requiredVersion();
|
info.timingsVersion = info.timings.requiredTkhdVersion();
|
||||||
if (info.version == 0) {
|
if (info.version == 0) {
|
||||||
info.writeVersion = info.timingsVersion;
|
info.writeVersion = info.timingsVersion;
|
||||||
// add 12 byte to size if update from version 0 to version 1 is required (which needs 12 byte more)
|
// add 12 byte to size if update from version 0 to version 1 is required (which needs 12 byte more)
|
||||||
|
@ -470,9 +491,18 @@ TrackHeaderInfo Mp4Track::verifyPresentTrackHeader() const
|
||||||
Mp4Timings Mp4Track::computeTimings() const
|
Mp4Timings Mp4Track::computeTimings() const
|
||||||
{
|
{
|
||||||
auto timings = Mp4Timings();
|
auto timings = Mp4Timings();
|
||||||
timings.creationTime = static_cast<std::uint64_t>((m_creationTime - startDate).totalSeconds());
|
if (m_trakAtom && (m_trakAtom->container().fileInfo().fileHandlingFlags() & MediaFileHandlingFlags::PreserveRawTimingValues)) {
|
||||||
timings.modificationTime = static_cast<std::uint64_t>((m_modificationTime - startDate).totalSeconds());
|
timings.tkhdCreationTime = m_rawTkhdCreationTime;
|
||||||
timings.duration = static_cast<std::uint64_t>(m_duration.totalTicks() * m_timeScale / TimeSpan::ticksPerSecond);
|
timings.tkhdModificationTime = m_rawTkhdModificationTime;
|
||||||
|
timings.tkhdDuration = m_rawTkhdDuration;
|
||||||
|
timings.mdhdCreationTime = m_rawMdhdCreationTime;
|
||||||
|
timings.mdhdModificationTime = m_rawMdhdModificationTime;
|
||||||
|
timings.mdhdDuration = m_rawMdhdDuration;
|
||||||
|
} else {
|
||||||
|
timings.tkhdCreationTime = timings.mdhdCreationTime = static_cast<std::uint64_t>((m_creationTime - startDate).totalSeconds());
|
||||||
|
timings.tkhdModificationTime = timings.mdhdModificationTime = static_cast<std::uint64_t>((m_modificationTime - startDate).totalSeconds());
|
||||||
|
timings.tkhdDuration = timings.mdhdDuration = static_cast<std::uint64_t>(m_duration.totalTicks() * m_timeScale / TimeSpan::ticksPerSecond);
|
||||||
|
}
|
||||||
return timings;
|
return timings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1115,11 +1145,11 @@ std::uint64_t Mp4Track::requiredSize(Diagnostics &diag) const
|
||||||
{
|
{
|
||||||
CPP_UTILITIES_UNUSED(diag)
|
CPP_UTILITIES_UNUSED(diag)
|
||||||
|
|
||||||
const auto info = verifyPresentTrackHeader();
|
const auto &info = verifyPresentTrackHeader();
|
||||||
// add size of
|
// add size of
|
||||||
// ... trak header
|
// ... trak header
|
||||||
std::uint64_t size = 8;
|
std::uint64_t size = 8;
|
||||||
// ... tkhd atom (TODO: buffer TrackHeaderInfo in next major release)
|
// ... tkhd atom
|
||||||
size += info.requiredSize;
|
size += info.requiredSize;
|
||||||
// ... children beside tkhd and mdia
|
// ... children beside tkhd and mdia
|
||||||
for (Mp4Atom *trakChild = m_trakAtom->firstChild(); trakChild; trakChild = trakChild->nextSibling()) {
|
for (Mp4Atom *trakChild = m_trakAtom->firstChild(); trakChild; trakChild = trakChild->nextSibling()) {
|
||||||
|
@ -1195,7 +1225,7 @@ void Mp4Track::makeTrack(Diagnostics &diag)
|
||||||
void Mp4Track::makeTrackHeader(Diagnostics &diag)
|
void Mp4Track::makeTrackHeader(Diagnostics &diag)
|
||||||
{
|
{
|
||||||
// verify the existing track header to make the new one based on it (if possible)
|
// verify the existing track header to make the new one based on it (if possible)
|
||||||
const TrackHeaderInfo info(verifyPresentTrackHeader());
|
const auto &info = verifyPresentTrackHeader();
|
||||||
|
|
||||||
// add notifications in case the present track header could not be parsed
|
// add notifications in case the present track header could not be parsed
|
||||||
if (info.versionUnknown) {
|
if (info.versionUnknown) {
|
||||||
|
@ -1234,20 +1264,20 @@ void Mp4Track::makeTrackHeader(Diagnostics &diag)
|
||||||
|
|
||||||
// make creation and modification time
|
// make creation and modification time
|
||||||
if (info.writeVersion != 0) {
|
if (info.writeVersion != 0) {
|
||||||
writer().writeUInt64BE(info.timings.creationTime);
|
writer().writeUInt64BE(info.timings.tkhdCreationTime);
|
||||||
writer().writeUInt64BE(info.timings.modificationTime);
|
writer().writeUInt64BE(info.timings.tkhdModificationTime);
|
||||||
} else {
|
} else {
|
||||||
writer().writeUInt32BE(static_cast<std::uint32_t>(info.timings.creationTime));
|
writer().writeUInt32BE(static_cast<std::uint32_t>(info.timings.tkhdCreationTime));
|
||||||
writer().writeUInt32BE(static_cast<std::uint32_t>(info.timings.modificationTime));
|
writer().writeUInt32BE(static_cast<std::uint32_t>(info.timings.tkhdModificationTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
// make track ID and duration
|
// make track ID and duration
|
||||||
writer().writeUInt32BE(static_cast<std::uint32_t>(m_id));
|
writer().writeUInt32BE(static_cast<std::uint32_t>(m_id));
|
||||||
writer().writeUInt32BE(0); // reserved
|
writer().writeUInt32BE(0); // reserved
|
||||||
if (info.writeVersion != 0) {
|
if (info.writeVersion != 0) {
|
||||||
writer().writeUInt64BE(info.timings.duration);
|
writer().writeUInt64BE(info.timings.tkhdDuration);
|
||||||
} else {
|
} else {
|
||||||
writer().writeUInt32BE(static_cast<std::uint32_t>(info.timings.duration));
|
writer().writeUInt32BE(static_cast<std::uint32_t>(info.timings.tkhdDuration));
|
||||||
}
|
}
|
||||||
writer().writeUInt32BE(0); // reserved
|
writer().writeUInt32BE(0); // reserved
|
||||||
writer().writeUInt32BE(0); // reserved
|
writer().writeUInt32BE(0); // reserved
|
||||||
|
@ -1286,24 +1316,25 @@ void Mp4Track::makeMedia(Diagnostics &diag)
|
||||||
writer().writeUInt32BE(0); // write size later
|
writer().writeUInt32BE(0); // write size later
|
||||||
writer().writeUInt32BE(Mp4AtomIds::Media);
|
writer().writeUInt32BE(Mp4AtomIds::Media);
|
||||||
// write mdhd atom
|
// write mdhd atom
|
||||||
const auto timings = computeTimings();
|
const auto &info = verifyPresentTrackHeader();
|
||||||
const auto timingsVersion = timings.requiredVersion();
|
const auto &timings = info.timings;
|
||||||
|
const auto timingsVersion = timings.requiredMdhdVersion();
|
||||||
writer().writeUInt32BE(timingsVersion != 0 ? 44 : 32); // size
|
writer().writeUInt32BE(timingsVersion != 0 ? 44 : 32); // size
|
||||||
writer().writeUInt32BE(Mp4AtomIds::MediaHeader);
|
writer().writeUInt32BE(Mp4AtomIds::MediaHeader);
|
||||||
writer().writeByte(timingsVersion); // version
|
writer().writeByte(timingsVersion); // version
|
||||||
writer().writeUInt24BE(0); // flags
|
writer().writeUInt24BE(0); // flags
|
||||||
if (timingsVersion != 0) {
|
if (timingsVersion != 0) {
|
||||||
writer().writeUInt64BE(timings.creationTime);
|
writer().writeUInt64BE(timings.mdhdCreationTime);
|
||||||
writer().writeUInt64BE(timings.modificationTime);
|
writer().writeUInt64BE(timings.mdhdModificationTime);
|
||||||
} else {
|
} else {
|
||||||
writer().writeUInt32BE(static_cast<std::uint32_t>(timings.creationTime));
|
writer().writeUInt32BE(static_cast<std::uint32_t>(timings.mdhdCreationTime));
|
||||||
writer().writeUInt32BE(static_cast<std::uint32_t>(timings.modificationTime));
|
writer().writeUInt32BE(static_cast<std::uint32_t>(timings.mdhdModificationTime));
|
||||||
}
|
}
|
||||||
writer().writeUInt32BE(m_timeScale);
|
writer().writeUInt32BE(m_timeScale);
|
||||||
if (timingsVersion != 0) {
|
if (timingsVersion != 0) {
|
||||||
writer().writeUInt64BE(timings.duration);
|
writer().writeUInt64BE(timings.mdhdDuration);
|
||||||
} else {
|
} else {
|
||||||
writer().writeUInt32BE(static_cast<std::uint32_t>(timings.duration));
|
writer().writeUInt32BE(static_cast<std::uint32_t>(timings.mdhdDuration));
|
||||||
}
|
}
|
||||||
// convert and write language
|
// convert and write language
|
||||||
const std::string &language = m_locale.abbreviatedName(LocaleFormat::ISO_639_2_T, LocaleFormat::Unknown);
|
const std::string &language = m_locale.abbreviatedName(LocaleFormat::ISO_639_2_T, LocaleFormat::Unknown);
|
||||||
|
@ -1556,19 +1587,22 @@ void Mp4Track::internalParseHeader(Diagnostics &diag, AbortableProgressFeedback
|
||||||
modFlagEnum(m_flags, TrackFlags::UsedWhenPreviewing, flags & 0x000004);
|
modFlagEnum(m_flags, TrackFlags::UsedWhenPreviewing, flags & 0x000004);
|
||||||
switch (atomVersion) {
|
switch (atomVersion) {
|
||||||
case 0:
|
case 0:
|
||||||
m_creationTime = startDate + TimeSpan::fromSeconds(reader.readUInt32BE());
|
m_rawTkhdCreationTime = reader.readUInt32BE();
|
||||||
m_modificationTime = startDate + TimeSpan::fromSeconds(reader.readUInt32BE());
|
m_rawTkhdModificationTime = reader.readUInt32BE();
|
||||||
m_id = reader.readUInt32BE();
|
m_id = reader.readUInt32BE();
|
||||||
|
m_rawTkhdDuration = reader.readUInt32BE();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
m_creationTime = startDate + TimeSpan::fromSeconds(static_cast<double>(reader.readUInt64BE()));
|
m_rawTkhdCreationTime = reader.readUInt64BE();
|
||||||
m_modificationTime = startDate + TimeSpan::fromSeconds(static_cast<double>(reader.readUInt64BE()));
|
m_rawTkhdModificationTime = reader.readUInt64BE();
|
||||||
m_id = reader.readUInt32BE();
|
m_id = reader.readUInt32BE();
|
||||||
|
m_rawTkhdDuration = reader.readUInt64BE();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
diag.emplace_back(DiagLevel::Critical,
|
diag.emplace_back(DiagLevel::Critical,
|
||||||
"Version of \"tkhd\"-atom not supported. It will be ignored. Track ID, creation time and modification time might not be be determined.",
|
"Version of \"tkhd\"-atom not supported. It will be ignored. Track ID, creation time and modification time might not be be determined.",
|
||||||
context);
|
context);
|
||||||
|
m_rawTkhdCreationTime = m_rawTkhdModificationTime = m_rawTkhdDuration = 0;
|
||||||
m_creationTime = DateTime();
|
m_creationTime = DateTime();
|
||||||
m_modificationTime = DateTime();
|
m_modificationTime = DateTime();
|
||||||
m_id = 0;
|
m_id = 0;
|
||||||
|
@ -1580,25 +1614,30 @@ void Mp4Track::internalParseHeader(Diagnostics &diag, AbortableProgressFeedback
|
||||||
m_istream->seekg(3, ios_base::cur); // skip flags
|
m_istream->seekg(3, ios_base::cur); // skip flags
|
||||||
switch (atomVersion) {
|
switch (atomVersion) {
|
||||||
case 0:
|
case 0:
|
||||||
m_creationTime = startDate + TimeSpan::fromSeconds(reader.readUInt32BE());
|
m_rawMdhdCreationTime = reader.readUInt32BE();
|
||||||
m_modificationTime = startDate + TimeSpan::fromSeconds(reader.readUInt32BE());
|
m_rawMdhdModificationTime = reader.readUInt32BE();
|
||||||
m_timeScale = reader.readUInt32BE();
|
m_timeScale = reader.readUInt32BE();
|
||||||
m_duration = TimeSpan::fromSeconds(static_cast<double>(reader.readUInt32BE()) / static_cast<double>(m_timeScale));
|
m_rawMdhdDuration = reader.readUInt32BE();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
m_creationTime = startDate + TimeSpan::fromSeconds(static_cast<double>(reader.readUInt64BE()));
|
m_rawMdhdCreationTime = reader.readUInt64BE();
|
||||||
m_modificationTime = startDate + TimeSpan::fromSeconds(static_cast<double>(reader.readUInt64BE()));
|
m_rawMdhdModificationTime = reader.readUInt64BE();
|
||||||
m_timeScale = reader.readUInt32BE();
|
m_timeScale = reader.readUInt32BE();
|
||||||
m_duration = TimeSpan::fromSeconds(static_cast<double>(reader.readUInt64BE()) / static_cast<double>(m_timeScale));
|
m_rawMdhdDuration = reader.readUInt64BE();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
diag.emplace_back(DiagLevel::Warning,
|
diag.emplace_back(DiagLevel::Warning,
|
||||||
"Version of \"mdhd\"-atom not supported. It will be ignored. Creation time, modification time, time scale and duration might not be "
|
"Version of \"mdhd\"-atom not supported. It will be ignored. Creation time, modification time, time scale and duration might not be "
|
||||||
"determined.",
|
"determined.",
|
||||||
context);
|
context);
|
||||||
|
m_rawMdhdCreationTime = m_rawMdhdModificationTime = m_rawMdhdDuration = 0;
|
||||||
m_timeScale = 0;
|
m_timeScale = 0;
|
||||||
m_duration = TimeSpan();
|
m_duration = TimeSpan();
|
||||||
}
|
}
|
||||||
|
m_creationTime = startDate + TimeSpan::fromSeconds(static_cast<TimeSpan::TickType>(m_rawMdhdCreationTime));
|
||||||
|
m_modificationTime = startDate + TimeSpan::fromSeconds(static_cast<TimeSpan::TickType>(m_rawMdhdModificationTime));
|
||||||
|
m_duration = TimeSpan::fromSeconds(static_cast<TimeSpan::TickType>(m_rawMdhdDuration)) / static_cast<TimeSpan::TickType>(m_timeScale);
|
||||||
|
|
||||||
std::uint16_t tmp = reader.readUInt16BE();
|
std::uint16_t tmp = reader.readUInt16BE();
|
||||||
if (tmp) {
|
if (tmp) {
|
||||||
const char buff[] = {
|
const char buff[] = {
|
||||||
|
|
|
@ -170,7 +170,7 @@ private:
|
||||||
std::uint64_t accumulateSampleSizes(std::size_t &sampleIndex, std::size_t count, Diagnostics &diag);
|
std::uint64_t accumulateSampleSizes(std::size_t &sampleIndex, std::size_t count, Diagnostics &diag);
|
||||||
void addChunkSizeEntries(
|
void addChunkSizeEntries(
|
||||||
std::vector<std::uint64_t> &chunkSizeTable, std::size_t count, std::size_t &sampleIndex, std::uint32_t sampleCount, Diagnostics &diag);
|
std::vector<std::uint64_t> &chunkSizeTable, std::size_t count, std::size_t &sampleIndex, std::uint32_t sampleCount, Diagnostics &diag);
|
||||||
TrackHeaderInfo verifyPresentTrackHeader() const;
|
const TrackHeaderInfo &verifyPresentTrackHeader() const;
|
||||||
Mp4Timings computeTimings() const;
|
Mp4Timings computeTimings() const;
|
||||||
|
|
||||||
Mp4Atom *m_trakAtom;
|
Mp4Atom *m_trakAtom;
|
||||||
|
@ -190,9 +190,16 @@ private:
|
||||||
unsigned int m_chunkOffsetSize;
|
unsigned int m_chunkOffsetSize;
|
||||||
std::uint32_t m_chunkCount;
|
std::uint32_t m_chunkCount;
|
||||||
std::uint32_t m_sampleToChunkEntryCount;
|
std::uint32_t m_sampleToChunkEntryCount;
|
||||||
|
std::uint64_t m_rawTkhdCreationTime;
|
||||||
|
std::uint64_t m_rawMdhdCreationTime;
|
||||||
|
std::uint64_t m_rawTkhdModificationTime;
|
||||||
|
std::uint64_t m_rawMdhdModificationTime;
|
||||||
|
std::uint64_t m_rawTkhdDuration;
|
||||||
|
std::uint64_t m_rawMdhdDuration;
|
||||||
std::unique_ptr<Mpeg4ElementaryStreamInfo> m_esInfo;
|
std::unique_ptr<Mpeg4ElementaryStreamInfo> m_esInfo;
|
||||||
std::unique_ptr<AvcConfiguration> m_avcConfig;
|
std::unique_ptr<AvcConfiguration> m_avcConfig;
|
||||||
std::unique_ptr<Av1Configuration> m_av1Config;
|
std::unique_ptr<Av1Configuration> m_av1Config;
|
||||||
|
mutable std::unique_ptr<TrackHeaderInfo> m_trackHeaderInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
Loading…
Reference in New Issue