mirror of
https://github.com/taglib/taglib.git
synced 2025-05-27 13:10:26 -04:00
Use private implementations for MP4::Atom, MP4::Atoms
This commit is contained in:
parent
28baa03b23
commit
8a42e552aa
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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()
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user