Use private implementations for MP4::Atom, MP4::Atoms

This commit is contained in:
Urs Fleisch 2023-12-10 08:16:04 +01:00
parent 28baa03b23
commit 8a42e552aa
9 changed files with 244 additions and 163 deletions

View File

@ -40,62 +40,72 @@ namespace {
};
} // namespace
MP4::Atom::Atom(File *file)
: offset(file->tell())
class MP4::Atom::AtomPrivate
{
children.setAutoDelete(true);
public:
explicit AtomPrivate(offset_t ofs) : offset(ofs) {}
offset_t offset;
offset_t length { 0 };
TagLib::ByteVector name;
AtomList children;
};
MP4::Atom::Atom(File *file)
: d(std::make_unique<AtomPrivate>(file->tell()))
{
d->children.setAutoDelete(true);
ByteVector header = file->readBlock(8);
if(header.size() != 8) {
// The atom header must be 8 bytes long, otherwise there is either
// trailing garbage or the file is truncated
debug("MP4: Couldn't read 8 bytes of data for atom header");
length = 0;
d->length = 0;
file->seek(0, File::End);
return;
}
length = header.toUInt();
d->length = header.toUInt();
if(length == 0) {
if(d->length == 0) {
// The last atom which extends to the end of the file.
length = file->length() - offset;
d->length = file->length() - d->offset;
}
else if(length == 1) {
else if(d->length == 1) {
// The atom has a 64-bit length.
const long long longLength = file->readBlock(8).toLongLong();
if(longLength <= LONG_MAX) {
// The actual length fits in long. That's always the case if long is 64-bit.
length = static_cast<long>(longLength);
d->length = static_cast<long>(longLength);
}
else {
debug("MP4: 64-bit atoms are not supported");
length = 0;
d->length = 0;
file->seek(0, File::End);
return;
}
}
if(length < 8 || length > file->length() - offset) {
if(d->length < 8 || d->length > file->length() - d->offset) {
debug("MP4: Invalid atom size");
length = 0;
d->length = 0;
file->seek(0, File::End);
return;
}
name = header.mid(4, 4);
d->name = header.mid(4, 4);
for(int i = 0; i < 4; ++i) {
const char ch = name.at(i);
const char ch = d->name.at(i);
if((ch < ' ' || ch > '~') && ch != '\251') {
debug("MP4: Invalid atom type");
length = 0;
d->length = 0;
file->seek(0, File::End);
}
}
for(auto c : containers) {
if(name == c) {
if(name == "meta") {
if(d->name == c) {
if(d->name == "meta") {
offset_t posAfterMeta = file->tell();
static constexpr std::array metaChildrenNames {
"hdlr", "ilst", "mhdr", "ctry", "lang"
@ -108,20 +118,20 @@ MP4::Atom::Atom(File *file)
// is a full atom.
file->seek(posAfterMeta + (metaIsFullAtom ? 4 : 0));
}
else if(name == "stsd") {
else if(d->name == "stsd") {
file->seek(8, File::Current);
}
while(file->tell() < offset + length) {
while(file->tell() < d->offset + d->length) {
auto child = new MP4::Atom(file);
children.append(child);
if(child->length == 0)
d->children.append(child);
if(child->d->length == 0)
return;
}
return;
}
}
file->seek(offset + length);
file->seek(d->offset + d->length);
}
MP4::Atom::~Atom() = default;
@ -132,17 +142,17 @@ MP4::Atom::find(const char *name1, const char *name2, const char *name3, const c
if(name1 == nullptr) {
return this;
}
auto it = std::find_if(children.cbegin(), children.cend(),
[&name1](Atom *child) { return child->name == name1; });
return it != children.cend() ? (*it)->find(name2, name3, name4) : nullptr;
auto it = std::find_if(d->children.cbegin(), d->children.cend(),
[&name1](Atom *child) { return child->d->name == name1; });
return it != d->children.cend() ? (*it)->find(name2, name3, name4) : nullptr;
}
MP4::AtomList
MP4::Atom::findall(const char *name, bool recursive)
{
MP4::AtomList result;
for(const auto &child : std::as_const(children)) {
if(child->name == name) {
for(const auto &child : std::as_const(d->children)) {
if(child->d->name == name) {
result.append(child);
}
if(recursive) {
@ -159,22 +169,70 @@ MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const
if(name1 == nullptr) {
return true;
}
auto it = std::find_if(children.cbegin(), children.cend(),
[&name1](Atom *child) { return child->name == name1; });
return it != children.cend() ? (*it)->path(path, name2, name3) : false;
auto it = std::find_if(d->children.cbegin(), d->children.cend(),
[&name1](Atom *child) { return child->d->name == name1; });
return it != d->children.cend() ? (*it)->path(path, name2, name3) : false;
}
MP4::Atoms::Atoms(File *file)
void MP4::Atom::addToOffset(offset_t delta)
{
atoms.setAutoDelete(true);
d->offset += delta;
}
void MP4::Atom::prependChild(Atom *atom)
{
d->children.prepend(atom);
}
bool MP4::Atom::removeChild(Atom *meta)
{
auto it = d->children.find(meta);
if(it != d->children.end()) {
d->children.erase(it);
return true;
}
return false;
}
offset_t MP4::Atom::offset() const
{
return d->offset;
}
offset_t MP4::Atom::length() const
{
return d->length;
}
const ByteVector &MP4::Atom::name() const
{
return d->name;
}
const MP4::AtomList &MP4::Atom::children() const
{
return d->children;
}
class MP4::Atoms::AtomsPrivate
{
public:
AtomList atoms;
};
MP4::Atoms::Atoms(File *file) :
d(std::make_unique<AtomsPrivate>())
{
d->atoms.setAutoDelete(true);
file->seek(0, File::End);
offset_t end = file->tell();
file->seek(0);
while(file->tell() + 8 <= end) {
auto atom = new MP4::Atom(file);
atoms.append(atom);
if (atom->length == 0)
d->atoms.append(atom);
if (atom->length() == 0)
break;
}
}
@ -184,18 +242,18 @@ MP4::Atoms::~Atoms() = default;
MP4::Atom *
MP4::Atoms::find(const char *name1, const char *name2, const char *name3, const char *name4)
{
auto it = std::find_if(atoms.cbegin(), atoms.cend(),
[&name1](Atom *atom) { return atom->name == name1; });
return it != atoms.cend() ? (*it)->find(name2, name3, name4) : nullptr;
auto it = std::find_if(d->atoms.cbegin(), d->atoms.cend(),
[&name1](Atom *atom) { return atom->name() == name1; });
return it != d->atoms.cend() ? (*it)->find(name2, name3, name4) : nullptr;
}
MP4::AtomList
MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const char *name4)
{
MP4::AtomList path;
auto it = std::find_if(atoms.cbegin(), atoms.cend(),
[&name1](Atom *atom) { return atom->name == name1; });
if(it != atoms.cend()) {
auto it = std::find_if(d->atoms.cbegin(), d->atoms.cend(),
[&name1](Atom *atom) { return atom->name() == name1; });
if(it != d->atoms.cend()) {
if(!(*it)->path(path, name2, name3, name4)) {
path.clear();
}
@ -203,3 +261,45 @@ MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const
}
return path;
}
namespace
{
bool checkValid(const MP4::AtomList &list)
{
return std::none_of(list.begin(), list.end(),
[](const auto &a) { return a->length() == 0 || !checkValid(a->children()); });
}
} // namespace
bool MP4::Atoms::checkRootLevelAtoms()
{
bool moovValid = false;
for(auto it = d->atoms.begin(); it != d->atoms.end(); ++it) {
bool invalid = (*it)->length() == 0 || !checkValid((*it)->children());
if(!moovValid && !invalid && (*it)->name() == "moov") {
moovValid = true;
}
if(invalid) {
if(moovValid && (*it)->name() != "moof") {
// Only the root level atoms "moov" and (if present) "moof" are
// modified. If they are valid, ignore following invalid root level
// atoms as trailing garbage.
while(it != d->atoms.end()) {
delete *it;
it = d->atoms.erase(it);
}
return true;
}
else {
return false;
}
}
}
return true;
}
const MP4::AtomList &MP4::Atoms::atoms() const
{
return d->atoms;
};

View File

@ -84,10 +84,17 @@ namespace TagLib {
Atom *find(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr);
bool path(AtomList &path, const char *name1, const char *name2 = nullptr, const char *name3 = nullptr);
AtomList findall(const char *name, bool recursive = false);
offset_t offset;
offset_t length;
TagLib::ByteVector name;
AtomList children;
void addToOffset(offset_t delta);
void prependChild(Atom *atom);
bool removeChild(Atom *meta);
offset_t offset() const;
offset_t length() const;
const ByteVector &name() const;
const AtomList &children() const;
private:
class AtomPrivate;
std::unique_ptr<AtomPrivate> d;
};
//! Root-level atoms
@ -100,7 +107,12 @@ namespace TagLib {
Atoms &operator=(const Atoms &) = delete;
Atom *find(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr);
AtomList path(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr);
AtomList atoms;
bool checkRootLevelAtoms();
const AtomList &atoms() const;
private:
class AtomsPrivate;
std::unique_ptr<AtomsPrivate> d;
};
} // namespace MP4
} // namespace TagLib

View File

@ -33,44 +33,6 @@
using namespace TagLib;
namespace
{
bool checkValid(const MP4::AtomList &list)
{
return std::none_of(list.begin(), list.end(),
[](const auto &a) { return a->length == 0 || !checkValid(a->children); });
}
bool checkRootLevelAtoms(MP4::AtomList &list)
{
bool moovValid = false;
for(auto it = list.begin(); it != list.end(); ++it) {
bool invalid = (*it)->length == 0 || !checkValid((*it)->children);
if(!moovValid && !invalid && (*it)->name == "moov") {
moovValid = true;
}
if(invalid) {
if(moovValid && (*it)->name != "moof") {
// Only the root level atoms "moov" and (if present) "moof" are
// modified. If they are valid, ignore following invalid root level
// atoms as trailing garbage.
while(it != list.end()) {
delete *it;
it = list.erase(it);
}
return true;
}
else {
return false;
}
}
}
return true;
}
} // namespace
class MP4::File::FilePrivate
{
public:
@ -156,7 +118,7 @@ MP4::File::read(bool readProperties)
return;
d->atoms = std::make_unique<Atoms>(this);
if(!checkRootLevelAtoms(d->atoms->atoms)) {
if(!d->atoms->checkRootLevelAtoms()) {
setValid(false);
return;
}

View File

@ -57,7 +57,7 @@ ItemFactory *ItemFactory::instance()
std::pair<String, Item> ItemFactory::parseItem(
const Atom *atom, const ByteVector &data) const
{
auto handlerType = handlerTypeForName(atom->name);
auto handlerType = handlerTypeForName(atom->name());
switch(handlerType) {
case ItemHandlerType::Unknown:
break;
@ -87,7 +87,7 @@ std::pair<String, Item> ItemFactory::parseItem(
case ItemHandlerType::Text:
return parseText(atom, data);
}
return {atom->name, Item()};
return {atom->name(), Item()};
}
ByteVector ItemFactory::renderItem(
@ -211,7 +211,7 @@ std::pair<String, StringList> ItemFactory::itemToProperty(
case ItemHandlerType::UInt:
return {key, String::number(item.toUInt())};
case ItemHandlerType::LongLong:
return {key, String::number(item.toLongLong())};
return {key, String::fromLongLong(item.toLongLong())};
case ItemHandlerType::Byte:
return {key, String::number(item.toByte())};
case ItemHandlerType::Bool:
@ -449,7 +449,7 @@ std::pair<String, Item> ItemFactory::parseInt(
{
ByteVectorList data = parseData(atom, bytes);
return {
atom->name,
atom->name(),
!data.isEmpty() ? Item(static_cast<int>(data[0].toShort())) : Item()
};
}
@ -461,12 +461,12 @@ std::pair<String, Item> ItemFactory::parseTextOrInt(
if(!data.isEmpty()) {
AtomData val = data[0];
return {
atom->name,
atom->name(),
val.type == TypeUTF8 ? Item(StringList(String(val.data, String::UTF8)))
: Item(static_cast<int>(val.data.toShort()))
};
}
return {atom->name, Item()};
return {atom->name(), Item()};
}
std::pair<String, Item> ItemFactory::parseUInt(
@ -474,7 +474,7 @@ std::pair<String, Item> ItemFactory::parseUInt(
{
ByteVectorList data = parseData(atom, bytes);
return {
atom->name,
atom->name(),
!data.isEmpty() ? Item(data[0].toUInt()) : Item()
};
}
@ -484,7 +484,7 @@ std::pair<String, Item> ItemFactory::parseLongLong(
{
ByteVectorList data = parseData(atom, bytes);
return {
atom->name,
atom->name(),
!data.isEmpty() ?Item (data[0].toLongLong()) : Item()
};
}
@ -494,7 +494,7 @@ std::pair<String, Item> ItemFactory::parseByte(
{
ByteVectorList data = parseData(atom, bytes);
return {
atom->name,
atom->name(),
!data.isEmpty() ? Item(static_cast<unsigned char>(data[0].at(0))) : Item()
};
}
@ -522,9 +522,9 @@ std::pair<String, Item> ItemFactory::parseIntPair(
if(!data.isEmpty()) {
const int a = data[0].toShort(2U);
const int b = data[0].toShort(4U);
return {atom->name, Item(a, b)};
return {atom->name(), Item(a, b)};
}
return {atom->name, Item()};
return {atom->name(), Item()};
}
std::pair<String, Item> ItemFactory::parseBool(
@ -533,9 +533,9 @@ std::pair<String, Item> ItemFactory::parseBool(
ByteVectorList data = parseData(atom, bytes);
if(!data.isEmpty()) {
bool value = !data[0].isEmpty() && data[0][0] != '\0';
return {atom->name, Item(value)};
return {atom->name(), Item(value)};
}
return {atom->name, Item()};
return {atom->name(), Item()};
}
std::pair<String, Item> ItemFactory::parseText(
@ -547,9 +547,9 @@ std::pair<String, Item> ItemFactory::parseText(
for(const auto &byte : data) {
value.append(String(byte, String::UTF8));
}
return {atom->name, Item(value)};
return {atom->name(), Item(value)};
}
return {atom->name, Item()};
return {atom->name(), Item()};
}
std::pair<String, Item> ItemFactory::parseFreeForm(
@ -591,7 +591,7 @@ std::pair<String, Item> ItemFactory::parseFreeForm(
return {name, item};
}
}
return {atom->name, Item()};
return {atom->name(), Item()};
}
std::pair<String, Item> ItemFactory::parseCovr(
@ -623,7 +623,7 @@ std::pair<String, Item> ItemFactory::parseCovr(
pos += length;
}
return {
atom->name,
atom->name(),
!value.isEmpty() ? Item(value) : Item()
};
}

View File

@ -39,14 +39,14 @@ namespace
{
long long totalLength = 0;
for(const auto &atom : list) {
offset_t length = atom->length;
offset_t length = atom->length();
if(length == 0)
return 0; // for safety, see checkValid() in mp4file.cpp
if(atom->name == "mdat")
if(atom->name() == "mdat")
totalLength += length;
totalLength += calculateMdatLength(atom->children);
totalLength += calculateMdatLength(atom->children());
}
return totalLength;
@ -144,8 +144,8 @@ MP4::Properties::read(File *file, Atoms *atoms)
return;
}
trak = track;
file->seek(hdlr->offset);
data = file->readBlock(hdlr->length);
file->seek(hdlr->offset());
data = file->readBlock(hdlr->length());
if(data.containsAt("soun", 16)) {
break;
}
@ -162,8 +162,8 @@ MP4::Properties::read(File *file, Atoms *atoms)
return;
}
file->seek(mdhd->offset);
data = file->readBlock(mdhd->length);
file->seek(mdhd->offset());
data = file->readBlock(mdhd->length());
const unsigned int version = data.at(8);
long long unit;
@ -187,8 +187,8 @@ MP4::Properties::read(File *file, Atoms *atoms)
if(length == 0) {
// No length found in the media header (mdhd), try the movie header (mvhd)
if(MP4::Atom *mvhd = moov->find("mvhd")) {
file->seek(mvhd->offset);
data = file->readBlock(mvhd->length);
file->seek(mvhd->offset());
data = file->readBlock(mvhd->length());
if(data.size() >= 24 + 4) {
unit = data.toUInt(20U);
length = data.toUInt(24U);
@ -203,8 +203,8 @@ MP4::Properties::read(File *file, Atoms *atoms)
return;
}
file->seek(atom->offset);
data = file->readBlock(atom->length);
file->seek(atom->offset());
data = file->readBlock(atom->length());
if(data.containsAt("mp4a", 20)) {
d->codec = AAC;
d->channels = data.toShort(40U);
@ -228,13 +228,13 @@ MP4::Properties::read(File *file, Atoms *atoms)
}
else {
d->bitrate = static_cast<int>(
(calculateMdatLength(atoms->atoms) * 8) / d->length);
(calculateMdatLength(atoms->atoms()) * 8) / d->length);
}
}
}
}
else if(data.containsAt("alac", 20)) {
if(atom->length == 88 && data.containsAt("alac", 56)) {
if(atom->length() == 88 && data.containsAt("alac", 56)) {
d->codec = ALAC;
d->bitsPerSample = data.at(69);
d->channels = data.at(73);
@ -245,7 +245,7 @@ MP4::Properties::read(File *file, Atoms *atoms)
// There are files which do not contain a nominal bitrate, e.g. those
// generated by refalac64.exe. Calculate the bitrate from the audio
// data size (mdat atoms) and the duration.
d->bitrate = static_cast<int>((calculateMdatLength(atoms->atoms) * 8) / d->length);
d->bitrate = static_cast<int>((calculateMdatLength(atoms->atoms()) * 8) / d->length);
}
}
}

View File

@ -71,9 +71,9 @@ MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms,
return;
}
for(const auto &atom : std::as_const(ilst->children)) {
file->seek(atom->offset + 8);
ByteVector data = d->file->readBlock(atom->length - 8);
for(const auto &atom : ilst->children()) {
file->seek(atom->offset() + 8);
ByteVector data = d->file->readBlock(atom->length() - 8);
const auto &[name, itm] = d->factory->parseItem(atom, data);
if (itm.isValid()) {
addItem(name, itm);
@ -141,19 +141,19 @@ MP4::Tag::updateParents(const AtomList &path, offset_t delta, int ignore)
std::advance(itEnd, 0 - ignore);
for(auto it = path.begin(); it != itEnd; ++it) {
d->file->seek((*it)->offset);
d->file->seek((*it)->offset());
long size = d->file->readBlock(4).toUInt();
// 64-bit
if (size == 1) {
d->file->seek(4, File::Current); // Skip name
long long longSize = d->file->readBlock(8).toLongLong();
// Seek the offset of the 64-bit size
d->file->seek((*it)->offset + 8);
d->file->seek((*it)->offset() + 8);
d->file->writeBlock(ByteVector::fromLongLong(longSize + delta));
}
// 32-bit
else {
d->file->seek((*it)->offset);
d->file->seek((*it)->offset());
d->file->writeBlock(ByteVector::fromUInt(static_cast<unsigned int>(size + delta)));
}
}
@ -166,13 +166,13 @@ MP4::Tag::updateOffsets(offset_t delta, offset_t offset)
if(moov) {
const MP4::AtomList stco = moov->findall("stco", true);
for(const auto &atom : stco) {
if(atom->offset > offset) {
atom->offset += delta;
if(atom->offset() > offset) {
atom->addToOffset(delta);
}
d->file->seek(atom->offset + 12);
ByteVector data = d->file->readBlock(atom->length - 12);
d->file->seek(atom->offset() + 12);
ByteVector data = d->file->readBlock(atom->length() - 12);
unsigned int count = data.toUInt();
d->file->seek(atom->offset + 16);
d->file->seek(atom->offset() + 16);
unsigned int pos = 4;
while(count--) {
auto o = static_cast<offset_t>(data.toUInt(pos));
@ -186,13 +186,13 @@ MP4::Tag::updateOffsets(offset_t delta, offset_t offset)
const MP4::AtomList co64 = moov->findall("co64", true);
for(const auto &atom : co64) {
if(atom->offset > offset) {
atom->offset += delta;
if(atom->offset() > offset) {
atom->addToOffset(delta);
}
d->file->seek(atom->offset + 12);
ByteVector data = d->file->readBlock(atom->length - 12);
d->file->seek(atom->offset() + 12);
ByteVector data = d->file->readBlock(atom->length() - 12);
unsigned int count = data.toUInt();
d->file->seek(atom->offset + 16);
d->file->seek(atom->offset() + 16);
unsigned int pos = 4;
while(count--) {
long long o = data.toLongLong(pos);
@ -209,18 +209,18 @@ MP4::Tag::updateOffsets(offset_t delta, offset_t offset)
if(moof) {
const MP4::AtomList tfhd = moof->findall("tfhd", true);
for(const auto &atom : tfhd) {
if(atom->offset > offset) {
atom->offset += delta;
if(atom->offset() > offset) {
atom->addToOffset(delta);
}
d->file->seek(atom->offset + 9);
ByteVector data = d->file->readBlock(atom->length - 9);
d->file->seek(atom->offset() + 9);
ByteVector data = d->file->readBlock(atom->length() - 9);
const unsigned int flags = data.toUInt(0, 3, true);
if(flags & 1) {
long long o = data.toLongLong(7U);
if(o > offset) {
o += delta;
}
d->file->seek(atom->offset + 16);
d->file->seek(atom->offset() + 16);
d->file->writeBlock(ByteVector::fromLongLong(o));
}
}
@ -241,7 +241,7 @@ MP4::Tag::saveNew(ByteVector data)
data = renderAtom("udta", data);
}
offset_t offset = path.back()->offset + 8;
offset_t offset = path.back()->offset() + 8;
d->file->insert(data, offset, 0);
updateParents(path, data.size());
@ -250,7 +250,7 @@ MP4::Tag::saveNew(ByteVector data)
// Insert the newly created atoms into the tree to keep it up-to-date.
d->file->seek(offset);
path.back()->children.prepend(new Atom(d->file));
path.back()->prependChild(new Atom(d->file));
}
void
@ -259,27 +259,27 @@ MP4::Tag::saveExisting(ByteVector data, const AtomList &path)
auto it = path.end();
MP4::Atom *ilst = *(--it);
offset_t offset = ilst->offset;
offset_t length = ilst->length;
offset_t offset = ilst->offset();
offset_t length = ilst->length();
MP4::Atom *meta = *(--it);
auto index = meta->children.cfind(ilst);
auto index = meta->children().cfind(ilst);
// check if there is an atom before 'ilst', and possibly use it as padding
if(index != meta->children.cbegin()) {
if(index != meta->children().cbegin()) {
auto prevIndex = std::prev(index);
MP4::Atom *prev = *prevIndex;
if(prev->name == "free") {
offset = prev->offset;
length += prev->length;
if(prev->name() == "free") {
offset = prev->offset();
length += prev->length();
}
}
// check if there is an atom after 'ilst', and possibly use it as padding
auto nextIndex = std::next(index);
if(nextIndex != meta->children.cend()) {
if(nextIndex != meta->children().cend()) {
MP4::Atom *next = *nextIndex;
if(next->name == "free") {
length += next->length;
if(next->name() == "free") {
length += next->length();
}
}
@ -304,13 +304,10 @@ MP4::Tag::saveExisting(ByteVector data, const AtomList &path)
else {
// Strip meta if data is empty, only the case when called from strip().
MP4::Atom *udta = *std::prev(it);
AtomList &udtaChildren = udta->children;
auto metaIt = udtaChildren.find(meta);
if(metaIt != udtaChildren.end()) {
offset = meta->offset;
delta = - meta->length;
udtaChildren.erase(metaIt);
d->file->removeBlock(meta->offset, meta->length);
if(udta->removeChild(meta)) {
offset = meta->offset();
delta = - meta->length();
d->file->removeBlock(meta->offset(), meta->length());
delete meta;
if(delta) {

View File

@ -523,6 +523,11 @@ String String::number(int n) // static
return Utils::formatString("%d", n);
}
String String::fromLongLong(long long n) // static
{
return Utils::formatString("%lld", n);
}
wchar_t &String::operator[](int i)
{
detach();

View File

@ -378,6 +378,11 @@ namespace TagLib {
*/
static String number(int n);
/*!
* Converts the base-10 integer \a n to a string.
*/
static String fromLongLong(long long n);
/*!
* Returns a reference to the character at position \a i.
*/

View File

@ -238,8 +238,8 @@ public:
MP4::Atoms a(&f);
MP4::Atom *stco = a.find("moov")->findall("stco", true)[0];
f.seek(stco->offset + 12);
ByteVector data = f.readBlock(stco->length - 12);
f.seek(stco->offset() + 12);
ByteVector data = f.readBlock(stco->length() - 12);
unsigned int count = data.mid(0, 4).toUInt();
int pos = 4;
while (count--) {
@ -257,8 +257,8 @@ public:
MP4::Atoms a(&f);
MP4::Atom *stco = a.find("moov")->findall("stco", true)[0];
f.seek(stco->offset + 12);
ByteVector data = f.readBlock(stco->length - 12);
f.seek(stco->offset() + 12);
ByteVector data = f.readBlock(stco->length() - 12);
unsigned int count = data.mid(0, 4).toUInt();
int pos = 4, i = 0;
while (count--) {
@ -323,8 +323,8 @@ public:
CPPUNIT_ASSERT_EQUAL(true, f.tag()->itemMap()["cpil"].toBool());
MP4::Atoms atoms(&f);
MP4::Atom *moov = atoms.atoms[0];
CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(77), moov->length);
MP4::Atom *moov = atoms.atoms()[0];
CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(77), moov->length());
f.tag()->setItem("pgap", true);
f.save();
@ -335,9 +335,9 @@ public:
CPPUNIT_ASSERT_EQUAL(true, f.tag()->item("pgap").toBool());
MP4::Atoms atoms(&f);
MP4::Atom *moov = atoms.atoms[0];
MP4::Atom *moov = atoms.atoms()[0];
// original size + 'pgap' size + padding
CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(77 + 25 + 974), moov->length);
CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(77 + 25 + 974), moov->length());
}
}