tagparser/matroska/matroskaattachment.cpp

200 lines
8.3 KiB
C++
Raw Normal View History

2015-09-06 19:57:33 +02:00
#include "./matroskaattachment.h"
#include "./ebmlelement.h"
2018-03-07 01:17:50 +01:00
#include "./matroskacontainer.h"
2015-09-06 19:57:33 +02:00
#include "./matroskaid.h"
2015-04-22 19:22:01 +02:00
#include "../mediafileinfo.h"
2015-04-22 19:22:01 +02:00
#include <c++utilities/conversion/binaryconversion.h>
2017-01-27 18:59:22 +01:00
#include <c++utilities/conversion/stringbuilder.h>
2017-02-05 21:02:40 +01:00
#include <memory>
2015-04-22 19:22:01 +02:00
using namespace std;
2019-06-10 22:49:11 +02:00
using namespace CppUtilities;
2015-04-22 19:22:01 +02:00
namespace TagParser {
2015-04-22 19:22:01 +02:00
/*!
* \class TagParser::MatroskaAttachment
* \brief Implementation of TagParser::AbstractAttachment for the Matroska container.
2015-04-22 19:22:01 +02:00
*/
/*!
* \brief Parses attachment from the specified \a attachedFileElement.
*
* \throws Throws std::ios_base::failure when an IO error occurs.
* \throws Throws TagParser::Failure or a derived exception when a parsing
2015-04-22 19:22:01 +02:00
* error occurs.
*/
void MatroskaAttachment::parse(EbmlElement *attachedFileElement, Diagnostics &diag)
2015-04-22 19:22:01 +02:00
{
clear();
static const string context("parsing \"AttachedFile\"-element");
m_attachedFileElement = attachedFileElement;
EbmlElement *subElement = attachedFileElement->firstChild();
2018-03-07 01:17:50 +01:00
while (subElement) {
subElement->parse(diag);
2018-03-07 01:17:50 +01:00
switch (subElement->id()) {
2015-04-22 19:22:01 +02:00
case MatroskaIds::FileDescription:
2018-03-07 01:17:50 +01:00
if (description().empty()) {
2015-04-22 19:22:01 +02:00
setDescription(subElement->readString());
} else {
diag.emplace_back(DiagLevel::Warning, "Multiple \"FileDescription\"-elements found. Surplus elements will be ignored.", context);
2015-04-22 19:22:01 +02:00
}
break;
case MatroskaIds::FileName:
2018-03-07 01:17:50 +01:00
if (name().empty()) {
2015-04-22 19:22:01 +02:00
setName(subElement->readString());
} else {
diag.emplace_back(DiagLevel::Warning, "Multiple \"FileName\"-elements found. Surplus elements will be ignored.", context);
2015-04-22 19:22:01 +02:00
}
break;
case MatroskaIds::FileMimeType:
2018-03-07 01:17:50 +01:00
if (mimeType().empty()) {
2015-04-22 19:22:01 +02:00
setMimeType(subElement->readString());
} else {
diag.emplace_back(DiagLevel::Warning, "Multiple \"FileMimeType\"-elements found. Surplus elements will be ignored.", context);
2015-04-22 19:22:01 +02:00
}
break;
case MatroskaIds::FileData:
2018-03-07 01:17:50 +01:00
if (data()) {
diag.emplace_back(DiagLevel::Warning, "Multiple \"FileData\"-elements found. Surplus elements will be ignored.", context);
2015-04-22 19:22:01 +02:00
} else {
2018-03-07 01:17:50 +01:00
setData(make_unique<StreamDataBlock>(std::bind(&EbmlElement::stream, subElement), subElement->dataOffset(), ios_base::beg,
subElement->startOffset() + subElement->totalSize(), ios_base::beg));
2015-04-22 19:22:01 +02:00
}
break;
case MatroskaIds::FileUID:
2018-03-07 01:17:50 +01:00
if (id()) {
diag.emplace_back(DiagLevel::Warning, "Multiple \"FileUID\"-elements found. Surplus elements will be ignored.", context);
2015-04-22 19:22:01 +02:00
} else {
setId(subElement->readUInteger());
}
break;
case MatroskaIds::FileReferral:
case MatroskaIds::FileUsedStartTime:
case MatroskaIds::FileUsedEndTime:
case EbmlIds::Crc32:
case EbmlIds::Void:
break;
default:
diag.emplace_back(DiagLevel::Warning, "Unknown child element \"" % subElement->idToString() + "\" found.", context);
2015-04-22 19:22:01 +02:00
}
subElement = subElement->nextSibling();
}
}
/*!
* \brief Writes the attachment to the specified \a stream (makes an "AttachedFile"-element).
* \throws Throws std::ios_base::failure when an IO error occurs.
* \throws Throws TagParser::Failure or a derived exception when a making
2015-04-22 19:22:01 +02:00
* error occurs.
* \sa prepareMaking()
*/
void MatroskaAttachment::make(std::ostream &stream, Diagnostics &diag)
2015-04-22 19:22:01 +02:00
{
2018-03-07 01:17:50 +01:00
if (!data() || !data()->size()) {
diag.emplace_back(DiagLevel::Critical, "There is no data assigned.", "making Matroska attachment");
2015-04-22 19:22:01 +02:00
throw InvalidDataException();
}
prepareMaking(diag).make(stream, diag);
2015-04-22 19:22:01 +02:00
}
/*!
* \class TagParser::MatroskaAttachmentMaker
2015-04-22 19:22:01 +02:00
* \brief The MatroskaAttachmentMaker class helps writing Matroska "AttachedFile"-elements which contain an attachment.
*
* An instance can be obtained using the MatroskaAttachment::prepareMaking() method.
*/
/*!
* \brief Prepares making the specified \a attachment.
* \sa See MatroskaAttachment::prepareMaking() for more information.
*/
2018-03-07 01:17:50 +01:00
MatroskaAttachmentMaker::MatroskaAttachmentMaker(MatroskaAttachment &attachment, Diagnostics &diag)
: m_attachment(attachment)
2015-04-22 19:22:01 +02:00
{
2018-03-07 01:17:50 +01:00
m_attachedFileElementSize = 2 + EbmlElement::calculateSizeDenotationLength(attachment.name().size()) + attachment.name().size() + 2
+ EbmlElement::calculateSizeDenotationLength(attachment.mimeType().size()) + attachment.mimeType().size() + 2 + 1
+ EbmlElement::calculateUIntegerLength(attachment.id());
2021-03-20 21:26:25 +01:00
if (auto dataSize = attachment.data() ? attachment.data()->size() : static_cast<std::uint64_t>(0)) {
2015-04-22 19:22:01 +02:00
m_attachedFileElementSize += 2 + EbmlElement::calculateSizeDenotationLength(dataSize) + dataSize;
}
2018-03-07 01:17:50 +01:00
if (!attachment.description().empty()) {
m_attachedFileElementSize
+= 2 + EbmlElement::calculateSizeDenotationLength(attachment.description().size()) + attachment.description().size();
2015-04-22 19:22:01 +02:00
}
2018-03-07 01:17:50 +01:00
if (attachment.attachedFileElement()) {
2015-04-22 19:22:01 +02:00
EbmlElement *child;
2018-03-07 01:17:50 +01:00
for (auto id : initializer_list<EbmlElement::IdentifierType>{
MatroskaIds::FileReferral, MatroskaIds::FileUsedStartTime, MatroskaIds::FileUsedEndTime }) {
if ((child = attachment.attachedFileElement()->childById(id, diag))) {
2015-04-22 19:22:01 +02:00
m_attachedFileElementSize += child->totalSize();
}
}
}
m_totalSize = 2 + EbmlElement::calculateSizeDenotationLength(m_attachedFileElementSize) + m_attachedFileElementSize;
}
/*!
* \brief Saves the attachment (specified when constructing the object) to the
* specified \a stream (makes an "AttachedFile"-element).
* \throws Throws std::ios_base::failure when an IO error occurs.
* \throws Throws Assumes the data is already validated and thus does NOT
* throw TagParser::Failure or a derived exception.
2015-04-22 19:22:01 +02:00
*/
void MatroskaAttachmentMaker::make(ostream &stream, Diagnostics &diag) const
2015-04-22 19:22:01 +02:00
{
char buff[8];
2019-03-13 19:06:42 +01:00
BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::AttachedFile), buff);
2015-04-22 19:22:01 +02:00
stream.write(buff, 2);
2019-03-13 19:06:42 +01:00
std::uint8_t len = EbmlElement::makeSizeDenotation(m_attachedFileElementSize, buff);
2015-04-22 19:22:01 +02:00
stream.write(buff, len);
// make elements
EbmlElement::makeSimpleElement(stream, MatroskaIds::FileName, attachment().name());
2018-03-07 01:17:50 +01:00
if (!attachment().description().empty()) {
2015-04-22 19:22:01 +02:00
EbmlElement::makeSimpleElement(stream, MatroskaIds::FileDescription, attachment().description());
}
EbmlElement::makeSimpleElement(stream, MatroskaIds::FileMimeType, attachment().mimeType());
EbmlElement::makeSimpleElement(stream, MatroskaIds::FileUID, attachment().id());
2018-03-07 01:17:50 +01:00
if (attachment().attachedFileElement()) {
2015-04-22 19:22:01 +02:00
EbmlElement *child;
2018-03-07 01:17:50 +01:00
for (auto id : initializer_list<EbmlElement::IdentifierType>{
MatroskaIds::FileReferral, MatroskaIds::FileUsedStartTime, MatroskaIds::FileUsedEndTime }) {
if ((child = attachment().attachedFileElement()->childById(id, diag))) {
if (child->buffer()) {
child->copyBuffer(stream);
} else {
child->copyEntirely(stream, diag, nullptr);
}
2015-04-22 19:22:01 +02:00
}
}
}
2018-03-07 01:17:50 +01:00
if (attachment().data() && attachment().data()->size()) {
2019-03-13 19:06:42 +01:00
BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::FileData), buff);
2015-04-22 19:22:01 +02:00
stream.write(buff, 2);
2019-03-13 19:06:42 +01:00
len = EbmlElement::makeSizeDenotation(static_cast<std::uint64_t>(attachment().data()->size()), buff);
2015-04-22 19:22:01 +02:00
stream.write(buff, len);
2016-08-04 00:16:19 +02:00
attachment().data()->copyTo(stream);
}
}
void MatroskaAttachmentMaker::bufferCurrentAttachments(Diagnostics &diag)
{
EbmlElement *child;
2018-03-07 01:17:50 +01:00
if (attachment().attachedFileElement()) {
for (auto id : initializer_list<EbmlElement::IdentifierType>{
MatroskaIds::FileReferral, MatroskaIds::FileUsedStartTime, MatroskaIds::FileUsedEndTime }) {
if ((child = attachment().attachedFileElement()->childById(id, diag))) {
child->makeBuffer();
}
}
}
2018-03-07 01:17:50 +01:00
if (attachment().data() && attachment().data()->size() && !attachment().isDataFromFile()) {
attachment().data()->makeBuffer();
2015-04-22 19:22:01 +02:00
}
}
2018-03-07 01:17:50 +01:00
} // namespace TagParser