6 #include "../exceptions.h" 8 #include <c++utilities/conversion/stringbuilder.h> 9 #include <c++utilities/io/binaryreader.h> 10 #include <c++utilities/io/binarywriter.h> 18 using namespace ConversionUtilities;
30 Mp4TagField::Mp4TagField()
31 : m_parsedRawDataType(RawDataType::
Reserved)
32 , m_countryIndicator(0)
42 , m_parsedRawDataType(RawDataType::
Reserved)
43 , m_countryIndicator(0)
77 using namespace Mp4AtomIds;
78 using namespace Mp4TagAtomIds;
79 string context(
"parsing MP4 tag field");
80 ilstChild.
parse(diag);
82 context =
"parsing MP4 tag field " + ilstChild.
idToString();
83 iostream &stream = ilstChild.
stream();
84 BinaryReader &reader = ilstChild.
container().reader();
85 int dataAtomFound = 0, meanAtomFound = 0, nameAtomFound = 0;
86 for (
Mp4Atom *dataAtom = ilstChild.
firstChild(); dataAtom; dataAtom = dataAtom->nextSibling()) {
88 dataAtom->parse(diag);
90 if (dataAtom->dataSize() < 8) {
91 diag.emplace_back(
DiagLevel::Warning,
"Truncated child atom \"data\" in tag atom (ilst child) found. (will be ignored)", context);
94 if (++dataAtomFound > 1) {
95 if (dataAtomFound == 2) {
97 DiagLevel::Warning,
"Multiple \"data\" child atom in tag atom (ilst child) found. (will be ignored)", context);
101 stream.seekg(static_cast<streamoff>(dataAtom->dataOffset()));
102 if (reader.readByte() != 0) {
104 "The version indicator byte is not zero, the tag atom might be unsupported and hence not be parsed correctly.", context);
106 setTypeInfo(m_parsedRawDataType = reader.readUInt24BE());
110 diag.emplace_back(
DiagLevel::Warning,
"Unexpected data type indicator found.", context);
115 m_countryIndicator = reader.readUInt16BE();
116 m_langIndicator = reader.readUInt16BE();
117 switch (m_parsedRawDataType) {
120 stream.seekg(static_cast<streamoff>(dataAtom->dataOffset() + 8));
128 switch (m_parsedRawDataType) {
143 const auto coverSize = static_cast<streamoff>(dataAtom->dataSize() - 8);
144 auto coverData = make_unique<char[]>(static_cast<size_t>(coverSize));
145 stream.read(coverData.get(), coverSize);
151 if (dataAtom->dataSize() > (8 + 4)) {
152 diag.emplace_back(
DiagLevel::Warning,
"Data atom stores integer of invalid size. Trying to read data anyways.", context);
154 if (dataAtom->dataSize() >= (8 + 4)) {
155 number = reader.readInt32BE();
156 }
else if (dataAtom->dataSize() == (8 + 2)) {
157 number = reader.readInt16BE();
158 }
else if (dataAtom->dataSize() == (8 + 1)) {
159 number = reader.readChar();
161 switch (ilstChild.
id()) {
172 if (dataAtom->dataSize() > (8 + 4)) {
173 diag.emplace_back(
DiagLevel::Warning,
"Data atom stores integer of invalid size. Trying to read data anyways.", context);
175 if (dataAtom->dataSize() >= (8 + 4)) {
176 number = static_cast<int>(reader.readUInt32BE());
177 }
else if (dataAtom->dataSize() == (8 + 2)) {
178 number = static_cast<int>(reader.readUInt16BE());
179 }
else if (dataAtom->dataSize() == (8 + 1)) {
180 number = static_cast<int>(reader.readByte());
182 switch (ilstChild.
id()) {
192 switch (ilstChild.
id()) {
196 if (dataAtom->dataSize() < (8 + 6)) {
197 diag.emplace_back(
DiagLevel::Warning,
"Track/disk position is truncated. Trying to read data anyways.", context);
199 uint16 pos = 0, total = 0;
200 if (dataAtom->dataSize() >= (8 + 4)) {
201 stream.seekg(2, ios_base::cur);
202 pos = reader.readUInt16BE();
204 if (dataAtom->dataSize() >= (8 + 6)) {
205 total = reader.readUInt16BE();
211 if (dataAtom->dataSize() < (8 + 2)) {
218 const auto dataSize = static_cast<streamsize>(dataAtom->dataSize() - 8);
219 auto data = make_unique<char[]>(static_cast<size_t>(dataSize));
220 stream.read(data.get(), dataSize);
229 if (dataAtom->dataSize() < 8) {
230 diag.emplace_back(
DiagLevel::Warning,
"Truncated child atom \"mean\" in tag atom (ilst child) found. (will be ignored)", context);
233 if (++meanAtomFound > 1) {
234 if (meanAtomFound == 2) {
236 DiagLevel::Warning,
"Tag atom contains more than one mean atom. The addiational mean atoms will be ignored.", context);
240 stream.seekg(static_cast<streamoff>(dataAtom->dataOffset() + 4));
241 m_mean = reader.readString(dataAtom->dataSize() - 4);
243 if (dataAtom->dataSize() < 4) {
244 diag.emplace_back(
DiagLevel::Warning,
"Truncated child atom \"name\" in tag atom (ilst child) found. (will be ignored)", context);
247 if (++nameAtomFound > 1) {
248 if (nameAtomFound == 2) {
250 DiagLevel::Warning,
"Tag atom contains more than one name atom. The addiational name atoms will be ignored.", context);
254 stream.seekg(static_cast<streamoff>(dataAtom->dataOffset() + 4));
255 m_name = reader.readString(dataAtom->dataSize() - 4);
258 "Unkown child atom \"" % dataAtom->idToString() +
"\" in tag atom (ilst child) found. (will be ignored)", context);
261 diag.emplace_back(
DiagLevel::Warning,
"Unable to parse all childs atom in tag atom (ilst child) found. (will be ignored)", context);
264 if (
value().isEmpty()) {
301 using namespace Mp4TagAtomIds;
302 std::vector<uint32> res;
360 using namespace Mp4TagAtomIds;
384 switch (
value().dataEncoding()) {
401 if (mimeType ==
"image/jpg" || mimeType ==
"image/jpeg") {
403 }
else if (mimeType ==
"image/png") {
405 }
else if (mimeType ==
"image/bmp") {
413 switch (
value().dataEncoding()) {
429 void Mp4TagField::reset()
434 m_countryIndicator = 0;
449 Mp4TagFieldMaker::Mp4TagFieldMaker(
Mp4TagField &field, Diagnostics &diag)
451 , m_convertedData(stringstream::in | stringstream::out | stringstream::binary)
452 , m_writer(&m_convertedData)
456 diag.emplace_back(
DiagLevel::Warning,
"Invalid tag atom id.",
"making MP4 tag field");
457 throw InvalidDataException();
460 if (m_field.value().isEmpty() && (!m_field.mean().empty() || !m_field.name().empty())) {
462 throw InvalidDataException();
467 m_rawDataType = m_field.appropriateRawDataType();
468 }
catch (
const Failure &) {
474 DiagLevel::Warning,
"It was not possible to find an appropriate raw data type id. JPEG image will be assumed.", context);
478 diag.emplace_back(
DiagLevel::Warning,
"It was not possible to find an appropriate raw data type id. UTF-8 will be assumed.", context);
483 if (!m_field.value().isEmpty()) {
484 m_convertedData.exceptions(std::stringstream::failbit | std::stringstream::badbit);
485 switch (m_rawDataType) {
488 m_writer.writeString(m_field.value().toString());
491 int number = m_field.value().toInteger();
492 if (number <= numeric_limits<int16>::max() && number >= numeric_limits<int16>::min()) {
493 m_writer.writeInt16BE(static_cast<int16>(number));
495 m_writer.writeInt32BE(number);
500 int number = m_field.value().toInteger();
501 if (number <= numeric_limits<uint16>::max() && number >= numeric_limits<uint16>::min()) {
502 m_writer.writeUInt16BE(static_cast<uint16>(number));
503 }
else if (number > 0) {
504 m_writer.writeUInt32BE(static_cast<uint32>(number));
506 throw ConversionException(
507 "Negative integer can not be assigned to the field with the ID \"" % interpretIntegerAsString<uint32>(m_field.id()) +
"\".");
516 switch (m_field.id()) {
522 m_writer.writeInt32BE(pos.position());
523 if (pos.total() <= numeric_limits<int16>::max()) {
524 m_writer.writeInt16BE(static_cast<int16>(pos.total()));
526 throw ConversionException(
527 "Integer can not be assigned to the field with the id \"" % interpretIntegerAsString<uint32>(m_field.id())
528 +
"\" because it is to big.");
530 m_writer.writeUInt16BE(0);
534 m_writer.writeUInt16BE(static_cast<uint16>(m_field.value().toStandardGenreIndex()));
540 }
catch (ConversionException &ex) {
542 if (char_traits<char>::length(ex.what())) {
545 diag.emplace_back(
DiagLevel::Critical,
"The assigned tag value can not be converted to be written appropriately.", context);
547 throw InvalidDataException();
552 = m_field.value().isEmpty() ? 0 : (m_convertedData.tellp() ? static_cast<size_t>(m_convertedData.tellp()) : m_field.value().dataSize());
554 + (m_field.name().empty() ? 0 : (12 + m_field.name().length())) + (m_field.mean().empty() ? 0 : (12 + m_field.mean().length()))
555 + (m_dataSize ? (16 + m_dataSize) : 0);
556 if (m_totalSize > numeric_limits<uint32>::max()) {
557 diag.emplace_back(
DiagLevel::Critical,
"Making a such big MP4 tag field is not supported.", context);
558 throw NotImplementedException();
571 m_writer.setStream(&stream);
573 m_writer.writeUInt32BE(static_cast<uint32>(m_totalSize));
575 m_writer.writeUInt32BE(m_field.
id());
576 if (!m_field.
mean().empty()) {
578 m_writer.writeUInt32BE(static_cast<uint32>(12 + m_field.
mean().size()));
580 m_writer.writeUInt32BE(0);
581 m_writer.writeString(m_field.
mean());
583 if (!m_field.
name().empty()) {
585 m_writer.writeUInt32BE(static_cast<uint32>(12 + m_field.
name().length()));
587 m_writer.writeUInt32BE(0);
588 m_writer.writeString(m_field.
name());
591 m_writer.writeUInt32BE(static_cast<uint32>(16 + m_dataSize));
593 m_writer.writeByte(0);
594 m_writer.writeUInt24BE(m_rawDataType);
597 if (m_convertedData.tellp()) {
599 stream << m_convertedData.rdbuf();
Mp4TagField()
Constructs a new Mp4TagField.
void setTypeInfo(const TypeInfoType &typeInfo)
Sets the type info of the current TagField.
const std::string & name() const
Returns the "name" for "extended" fields.
uint16 languageIndicator() const
Returns the language indicator.
The Mp4TagField class is used by Mp4Tag to store the fields.
void setMimeType(const std::string &mimeType)
Sets the MIME type.
ImplementationType * firstChild()
Returns the first child of the element.
static std::string fieldIdToString(IdentifierType id)
Returns the string representation for the specified id.
uint16 countryIndicator() const
Returns the country indicator.
The Mp4TagFieldMaker class helps making tag fields.
The Mp4Atom class helps to parse MP4 files.
bool isEmpty() const
Returns an indication whether an value is assigned.
const std::string & mimeType() const
Returns the MIME type.
std::size_t dataSize() const
Returns the size of the assigned value in bytes.
Contains utility classes helping to read and write streams.
void parse(Diagnostics &diag)
Parses the header information of the element which is read from the related stream at the start offse...
ContainerType & container()
Returns the related container.
void assignPosition(PositionInSet value)
Assigns the given PositionInSet value.
void make(std::ostream &stream)
Saves the field (specified when constructing the object) to the specified stream.
void assignStandardGenreIndex(int index)
Assigns the given standard genre index to be assigned.
Mp4TagFieldMaker prepareMaking(Diagnostics &diag)
Prepares making.
const TypeInfoType & typeInfo() const
Returns the type info of the current TagField.
The TagField class is used by FieldMapBasedTag to store the fields.
char * dataPointer()
Returns a pointer to the raw data assigned to the current instance.
const IdentifierType & id() const
Returns the id of the current TagField.
uint32 appropriateRawDataType() const
Returns an appropriate raw data type.
void assignInteger(int value)
Assigns the given integer value.
void assignText(const char *text, std::size_t textSize, TagTextEncoding textEncoding=TagTextEncoding::Latin1, TagTextEncoding convertTo=TagTextEncoding::Unspecified)
Assigns a copy of the given text.
void reparse(Mp4Atom &ilstChild, Diagnostics &diag)
Parses field information from the specified Mp4Atom.
void assignData(const char *data, std::size_t length, TagDataType type=TagDataType::Binary, TagTextEncoding encoding=TagTextEncoding::Latin1)
std::iostream & stream()
Returns the related stream.
std::vector< uint32 > expectedRawDataTypes() const
Returns the expected raw data types for the ID of the field.
The TagValue class wraps values of different types.
bool isTypeInfoAssigned() const
Returns an indication whether a type info is assigned.
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
const IdentifierType & id() const
Returns the element ID.
Contains all classes and functions of the TagInfo library.
std::string idToString() const
Converts the specified atom ID to a printable string.
void setId(const IdentifierType &id)
Sets the id of the current Tag Field.
The Diagnostics class is a container for DiagMessage.
void make(std::ostream &stream, Diagnostics &diag)
Saves the field to the specified stream.
TagValue & value()
Returns the value of the current TagField.
const std::string & mean() const
Returns the "mean" for "extended" fields.