improved handling of unsupported files

additionally:
- added detection for QuickTime files
- minor adjustments
This commit is contained in:
Martchus 2016-03-14 21:56:27 +01:00
parent e0437c0a43
commit ddf9ef02f8
7 changed files with 72 additions and 78 deletions

View File

@ -207,8 +207,9 @@ startParsingSignature:
// continue reading signature // continue reading signature
goto startParsingSignature; goto startParsingSignature;
case ContainerFormat::Mp4: { case ContainerFormat::Mp4:
// EBML/Matroska is handled using Mp4Container instance case ContainerFormat::QuickTime: {
// MP4/QuickTime is handled using Mp4Container instance
m_container = make_unique<Mp4Container>(*this, m_containerOffset); m_container = make_unique<Mp4Container>(*this, m_containerOffset);
NotificationList notifications; NotificationList notifications;
try { try {
@ -512,7 +513,7 @@ bool MediaFileInfo::createAppropriateTags(bool treatUnknownFilesAsMp3Files, TagU
// check if tags need to be created/adjusted/removed // check if tags need to be created/adjusted/removed
bool targetsRequired = !requiredTargets.empty() && (requiredTargets.size() != 1 && requiredTargets.front().isEmpty()); bool targetsRequired = !requiredTargets.empty() && (requiredTargets.size() != 1 && requiredTargets.front().isEmpty());
bool targetsSupported = false; bool targetsSupported = false;
if(m_container) { if(areTagsSupported() && m_container) {
// container object takes care of tag management // container object takes care of tag management
if(targetsRequired) { if(targetsRequired) {
// check whether container supports targets // check whether container supports targets
@ -723,54 +724,17 @@ const char *MediaFileInfo::containerFormatAbbreviation() const
*/ */
const char *MediaFileInfo::mimeType() const const char *MediaFileInfo::mimeType() const
{ {
MediaType mediaType;
switch(m_containerFormat) { switch(m_containerFormat) {
case ContainerFormat::Asf:
return "video/x-ms-asf";
case ContainerFormat::Gif87a:
case ContainerFormat::Gif89a:
return "image/gif";
case ContainerFormat::Jpeg:
return "image/jpeg";
case ContainerFormat::Png:
return "image/png";
case ContainerFormat::MpegAudioFrames:
return "audio/mpeg";
case ContainerFormat::Mp4: case ContainerFormat::Mp4:
if(hasTracksOfType(MediaType::Video)) {
return "video/mp4";
}
return "audio/mp4";
case ContainerFormat::Ogg: case ContainerFormat::Ogg:
if(hasTracksOfType(MediaType::Video)) {
return "video/ogg";
}
return "audio/ogg";
case ContainerFormat::Matroska: case ContainerFormat::Matroska:
if(hasTracksOfType(MediaType::Video)) { mediaType = hasTracksOfType(MediaType::Video) ? MediaType::Video : MediaType::Audio;
return "video/x-matroska"; break;
}
return "audio/x-matroska";
case ContainerFormat::Bzip2:
return "application/x-bzip";
case ContainerFormat::Gzip:
return "application/gzip";
case ContainerFormat::Lha:
return "application/x-lzh-compressed";
case ContainerFormat::Rar:
return "application/x-rar-compressed";
case ContainerFormat::Lzip:
return "application/x-lzip";
case ContainerFormat::Zip:
return "application/zip";
case ContainerFormat::SevenZ:
return "application/x-7z-compressed";
case ContainerFormat::WindowsBitmap:
return "image/bmp";
case ContainerFormat::WindowsIcon:
return "image/vnd.microsoft.icon";
default: default:
return ""; mediaType = MediaType::Unknown;
} }
return Media::containerMimeType(m_containerFormat, mediaType);
} }
/*! /*!
@ -781,7 +745,7 @@ const char *MediaFileInfo::mimeType() const
* *
* \remarks The MediaFileInfo keeps the ownership over the returned * \remarks The MediaFileInfo keeps the ownership over the returned
* pointers. The returned Tracks will be destroyed when the * pointers. The returned Tracks will be destroyed when the
* MediaFileInfo gets invalidated. * MediaFileInfo is invalidated.
* *
* \sa parseTracks() * \sa parseTracks()
*/ */
@ -950,7 +914,7 @@ bool MediaFileInfo::removeAllId3v2Tags()
* \returns Returns the first ID3v2 tag of the current file. * \returns Returns the first ID3v2 tag of the current file.
* *
* \remarks The MediaFileInfo keeps the ownership over the created tag. It will be * \remarks The MediaFileInfo keeps the ownership over the created tag. It will be
* destroyed when the MediaFileInfo gets invalidated. * destroyed when the MediaFileInfo is invalidated.
* *
* \sa applyChanges() * \sa applyChanges()
*/ */
@ -1066,9 +1030,6 @@ bool MediaFileInfo::areTracksSupported() const
*/ */
bool MediaFileInfo::areTagsSupported() const bool MediaFileInfo::areTagsSupported() const
{ {
if(hasAnyTag()) {
return true;
}
switch(m_containerFormat) { switch(m_containerFormat) {
case ContainerFormat::Mp4: case ContainerFormat::Mp4:
case ContainerFormat::MpegAudioFrames: case ContainerFormat::MpegAudioFrames:
@ -1076,9 +1037,12 @@ bool MediaFileInfo::areTagsSupported() const
case ContainerFormat::Matroska: case ContainerFormat::Matroska:
case ContainerFormat::Webm: case ContainerFormat::Webm:
case ContainerFormat::Adts: case ContainerFormat::Adts:
// these container formats are supported
return true; return true;
default: default:
return false; // the container format is unsupported
// -> an ID3 tag might be already present, in this case the tags are considered supported
return !m_container && (hasId3v1Tag() || hasId3v2Tag());
} }
} }
@ -1087,12 +1051,12 @@ bool MediaFileInfo::areTagsSupported() const
* *
* \remarks The MediaFileInfo keeps the ownership over the returned * \remarks The MediaFileInfo keeps the ownership over the returned
* pointer. The returned MP4 tag will be destroyed when the * pointer. The returned MP4 tag will be destroyed when the
* MediaFileInfo gets invalidated. * MediaFileInfo is invalidated.
*/ */
Mp4Tag *MediaFileInfo::mp4Tag() const Mp4Tag *MediaFileInfo::mp4Tag() const
{ {
// simply return the first tag here since MP4 files never contain multiple tags // simply return the first tag here since MP4 files never contain multiple tags
return m_containerFormat == ContainerFormat::Mp4 && m_container && m_container->tagCount() > 0 ? static_cast<Mp4Container *>(m_container.get())->tags().front().get() : nullptr; return (m_containerFormat == ContainerFormat::Mp4 || m_containerFormat == ContainerFormat::QuickTime) && m_container && m_container->tagCount() > 0 ? static_cast<Mp4Container *>(m_container.get())->tags().front().get() : nullptr;
} }
/*! /*!
@ -1100,7 +1064,7 @@ Mp4Tag *MediaFileInfo::mp4Tag() const
* *
* \remarks The MediaFileInfo keeps the ownership over the returned * \remarks The MediaFileInfo keeps the ownership over the returned
* pointers. The returned Matroska tags will be destroyed when the * pointers. The returned Matroska tags will be destroyed when the
* MediaFileInfo gets invalidated. * MediaFileInfo is invalidated.
*/ */
const vector<unique_ptr<MatroskaTag> > &MediaFileInfo::matroskaTags() const const vector<unique_ptr<MatroskaTag> > &MediaFileInfo::matroskaTags() const
{ {
@ -1117,7 +1081,7 @@ const vector<unique_ptr<MatroskaTag> > &MediaFileInfo::matroskaTags() const
* \brief Returns all chapters assigned to the current file. * \brief Returns all chapters assigned to the current file.
* *
* \remarks The MediaFileInfo keeps the ownership over the chapters which will be * \remarks The MediaFileInfo keeps the ownership over the chapters which will be
* destroyed when the MediaFileInfo gets invalidated. * destroyed when the MediaFileInfo is invalidated.
*/ */
vector<AbstractChapter *> MediaFileInfo::chapters() const vector<AbstractChapter *> MediaFileInfo::chapters() const
{ {
@ -1136,7 +1100,7 @@ vector<AbstractChapter *> MediaFileInfo::chapters() const
* \brief Returns all attachments assigned to the current file. * \brief Returns all attachments assigned to the current file.
* *
* \remarks The MediaFileInfo keeps the ownership over the attachments which will be * \remarks The MediaFileInfo keeps the ownership over the attachments which will be
* destroyed when the MediaFileInfo gets invalidated. * destroyed when the MediaFileInfo is invalidated.
*/ */
vector<AbstractAttachment *> MediaFileInfo::attachments() const vector<AbstractAttachment *> MediaFileInfo::attachments() const
{ {
@ -1318,7 +1282,7 @@ void MediaFileInfo::mergeId3v2Tags()
* Previous elements of the vector will not be cleared. * Previous elements of the vector will not be cleared.
* *
* \remarks The MediaFileInfo keeps the ownership over the tags which will be * \remarks The MediaFileInfo keeps the ownership over the tags which will be
* destroyed when the MediaFileInfo gets invalidated. * destroyed when the MediaFileInfo is invalidated.
*/ */
void MediaFileInfo::tags(vector<Tag *> &tags) const void MediaFileInfo::tags(vector<Tag *> &tags) const
{ {
@ -1338,7 +1302,7 @@ void MediaFileInfo::tags(vector<Tag *> &tags) const
* \brief Returns all tags assigned to the current file. * \brief Returns all tags assigned to the current file.
* *
* \remarks The MediaFileInfo keeps the ownership over the tags which will be * \remarks The MediaFileInfo keeps the ownership over the tags which will be
* destroyed when the MediaFileInfo gets invalidated. * destroyed when the MediaFileInfo is invalidated.
*/ */
vector<Tag *> MediaFileInfo::tags() const vector<Tag *> MediaFileInfo::tags() const
{ {
@ -1376,7 +1340,7 @@ void MediaFileInfo::makeMp3File()
stream().seekp(-128, ios_base::end); stream().seekp(-128, ios_base::end);
try { try {
m_id3v1Tag->make(stream()); m_id3v1Tag->make(stream());
} catch(Failure &) { } catch(const Failure &) {
addNotification(NotificationType::Warning, "Unable to write ID3v1 tag.", context); addNotification(NotificationType::Warning, "Unable to write ID3v1 tag.", context);
} }
} else { } else {
@ -1400,7 +1364,7 @@ void MediaFileInfo::makeMp3File()
stream().seekp(0, ios_base::end); stream().seekp(0, ios_base::end);
try { try {
m_id3v1Tag->make(stream()); m_id3v1Tag->make(stream());
} catch(Failure &) { } catch(const Failure &) {
addNotification(NotificationType::Warning, "Unable to write ID3v1 tag.", context); addNotification(NotificationType::Warning, "Unable to write ID3v1 tag.", context);
} }
} else { } else {
@ -1515,7 +1479,7 @@ void MediaFileInfo::makeMp3File()
updateStatus("Writing ID3v1 tag ..."); updateStatus("Writing ID3v1 tag ...");
try { try {
m_id3v1Tag->make(stream()); m_id3v1Tag->make(stream());
} catch(Failure &) { } catch(const Failure &) {
addNotification(NotificationType::Warning, "Unable to write ID3v1 tag.", context); addNotification(NotificationType::Warning, "Unable to write ID3v1 tag.", context);
} }
} }

View File

@ -59,7 +59,7 @@ void Mp4Atom::internalParse()
invalidateStatus(); invalidateStatus();
static const string context("parsing MP4 atom"); static const string context("parsing MP4 atom");
if(maxTotalSize() < minimumElementSize()) { if(maxTotalSize() < minimumElementSize()) {
addNotification(NotificationType::Critical, "Atom is smaller then 8 byte and hence invalid. The maximum size within the parent atom is " + numberToString(maxTotalSize()) + ".", context); addNotification(NotificationType::Critical, "Atom is smaller then 8 byte and hence invalid. The remaining size within the parent atom is " + numberToString(maxTotalSize()) + ".", context);
throw TruncatedDataException(); throw TruncatedDataException();
} }
stream().seekg(startOffset()); stream().seekg(startOffset());

View File

@ -57,7 +57,7 @@ void Mp4Container::internalParseHeader()
m_doctype = reader().readString(4); m_doctype = reader().readString(4);
m_version = reader().readUInt32BE(); m_version = reader().readUInt32BE();
} else { } else {
m_doctype = "mp41"; m_doctype.clear();
m_version = 0; m_version = 0;
} }
} }

View File

@ -180,7 +180,7 @@ void Mp4Tag::parse(Mp4Atom &metaAtom)
Mp4Atom *subAtom = nullptr; Mp4Atom *subAtom = nullptr;
try { try {
metaAtom.childById(Mp4AtomIds::HandlerReference); metaAtom.childById(Mp4AtomIds::HandlerReference);
} catch(Failure &) { } catch(const Failure &) {
addNotification(NotificationType::Critical, "Unable to parse child atoms of meta atom (stores hdlr and ilst atoms).", context); addNotification(NotificationType::Critical, "Unable to parse child atoms of meta atom (stores hdlr and ilst atoms).", context);
} }
if(subAtom) { if(subAtom) {
@ -206,7 +206,7 @@ void Mp4Tag::parse(Mp4Atom &metaAtom)
} }
try { try {
subAtom = metaAtom.childById(Mp4AtomIds::ItunesList); subAtom = metaAtom.childById(Mp4AtomIds::ItunesList);
} catch(Failure &) { } catch(const Failure &) {
addNotification(NotificationType::Critical, "Unable to parse child atoms of meta atom (stores hdlr and ilst atoms).", context); addNotification(NotificationType::Critical, "Unable to parse child atoms of meta atom (stores hdlr and ilst atoms).", context);
} }
if(subAtom) { if(subAtom) {
@ -217,7 +217,7 @@ void Mp4Tag::parse(Mp4Atom &metaAtom)
tagField.invalidateNotifications(); tagField.invalidateNotifications();
tagField.reparse(*child); tagField.reparse(*child);
fields().insert(pair<fieldType::identifierType, fieldType>(child->id(), tagField)); fields().insert(pair<fieldType::identifierType, fieldType>(child->id(), tagField));
} catch(Failure &) { } catch(const Failure &) {
} }
addNotifications(context, *child); addNotifications(context, *child);
addNotifications(context, tagField); addNotifications(context, tagField);

View File

@ -1165,6 +1165,7 @@ void Mp4Track::internalParseHeader()
addNotification(NotificationType::Critical, "\"trak\"-atom is null.", context); addNotification(NotificationType::Critical, "\"trak\"-atom is null.", context);
throw InvalidDataException(); throw InvalidDataException();
} }
// get atoms // get atoms
try { try {
if(!(m_tkhdAtom = m_trakAtom->childById(TrackHeader))) { if(!(m_tkhdAtom = m_trakAtom->childById(TrackHeader))) {
@ -1207,11 +1208,13 @@ void Mp4Track::internalParseHeader()
addNotification(NotificationType::Critical, "No \"stsz\"/\"stz2\"-atom found.", context); addNotification(NotificationType::Critical, "No \"stsz\"/\"stz2\"-atom found.", context);
throw InvalidDataException(); throw InvalidDataException();
} }
} catch(Failure &) { } catch(const Failure &) {
addNotification(NotificationType::Critical, "Unable to parse relevant atoms.", context); addNotification(NotificationType::Critical, "Unable to parse relevant atoms.", context);
throw InvalidDataException(); throw InvalidDataException();
} }
BinaryReader &reader = m_trakAtom->reader(); BinaryReader &reader = m_trakAtom->reader();
// read tkhd atom // read tkhd atom
m_istream->seekg(m_tkhdAtom->startOffset() + 8); // seek to beg, skip size and name m_istream->seekg(m_tkhdAtom->startOffset() + 8); // seek to beg, skip size and name
byte atomVersion = reader.readByte(); // read version byte atomVersion = reader.readByte(); // read version
@ -1236,8 +1239,9 @@ void Mp4Track::internalParseHeader()
m_modificationTime = DateTime(); m_modificationTime = DateTime();
m_id = 0; m_id = 0;
} }
// read mdhd atom // read mdhd atom
m_istream->seekg(m_mdhdAtom->startOffset() + 8); // seek to beg, skip size and name m_istream->seekg(m_mdhdAtom->dataOffset()); // seek to beg, skip size and name
atomVersion = reader.readByte(); // read version atomVersion = reader.readByte(); // read version
m_istream->seekg(3, ios_base::cur); // skip flags m_istream->seekg(3, ios_base::cur); // skip flags
switch(atomVersion) { switch(atomVersion) {
@ -1258,12 +1262,17 @@ void Mp4Track::internalParseHeader()
m_timeScale = 0; m_timeScale = 0;
m_duration = TimeSpan(); m_duration = TimeSpan();
} }
uint16 rawLanguage = reader.readUInt16BE(); uint16 tmp = reader.readUInt16BE();
char buff[3]; if(tmp) {
buff[0] = ((rawLanguage & 0x7C00) >> 0xA) + 0x60; char buff[3];
buff[1] = ((rawLanguage & 0x03E0) >> 0x5) + 0x60; buff[0] = ((tmp & 0x7C00) >> 0xA) + 0x60;
buff[2] = ((rawLanguage & 0x001F) >> 0x0) + 0x60; buff[1] = ((tmp & 0x03E0) >> 0x5) + 0x60;
m_language = string(buff, 3); buff[2] = ((tmp & 0x001F) >> 0x0) + 0x60;
m_language = string(buff, 3);
} else {
m_language.clear();
}
// read hdlr atom // read hdlr atom
// -> seek to begin skipping size, name, version, flags and reserved bytes // -> seek to begin skipping size, name, version, flags and reserved bytes
m_istream->seekg(m_hdlrAtom->dataOffset() + 8); m_istream->seekg(m_hdlrAtom->dataOffset() + 8);
@ -1284,19 +1293,26 @@ void Mp4Track::internalParseHeader()
default: default:
m_mediaType = MediaType::Unknown; m_mediaType = MediaType::Unknown;
} }
// -> name
// name
m_istream->seekg(12, ios_base::cur); // skip reserved bytes m_istream->seekg(12, ios_base::cur); // skip reserved bytes
m_name = reader.readTerminatedString(m_hdlrAtom->totalSize() - 12 - 4 - 12, 0); if((tmp = m_istream->peek()) == m_hdlrAtom->dataSize() - 12 - 4 - 8 - 1) {
// assume size prefixed string (seems to appear in QuickTime files)
m_istream->seekg(1, ios_base::cur);
m_name = reader.readString(tmp);
} else {
// assume null terminated string (appears in MP4 files)
m_name = reader.readTerminatedString(m_hdlrAtom->dataSize() - 12 - 4 - 8, 0);
}
// read stco atom (only chunk count) // read stco atom (only chunk count)
m_chunkOffsetSize = (m_stcoAtom->id() == Mp4AtomIds::ChunkOffset64) ? 8 : 4; m_chunkOffsetSize = (m_stcoAtom->id() == Mp4AtomIds::ChunkOffset64) ? 8 : 4;
m_istream->seekg(m_stcoAtom->dataOffset() + 4); m_istream->seekg(m_stcoAtom->dataOffset() + 4);
m_chunkCount = reader.readUInt32BE(); m_chunkCount = reader.readUInt32BE();
// read stsd atom // read stsd atom
m_istream->seekg(m_stsdAtom->dataOffset() + 4); // seek to beg, skip size, name, version and flags m_istream->seekg(m_stsdAtom->dataOffset() + 4); // seek to beg, skip size, name, version and flags
uint32 entryCount = reader.readUInt32BE(); uint32 entryCount = reader.readUInt32BE();
Mp4Atom *esDescParentAtom = nullptr; Mp4Atom *esDescParentAtom = nullptr;
uint16 tmp;
if(entryCount > 0) { if(entryCount > 0) {
try { try {
for(Mp4Atom *codecConfigContainerAtom = m_stsdAtom->firstChild(); codecConfigContainerAtom; codecConfigContainerAtom = codecConfigContainerAtom->nextSibling()) { for(Mp4Atom *codecConfigContainerAtom = m_stsdAtom->firstChild(); codecConfigContainerAtom; codecConfigContainerAtom = codecConfigContainerAtom->nextSibling()) {
@ -1456,6 +1472,7 @@ void Mp4Track::internalParseHeader()
addNotification(NotificationType::Critical, "Unable to parse child atoms of \"stsd\"-atom.", context); addNotification(NotificationType::Critical, "Unable to parse child atoms of \"stsd\"-atom.", context);
} }
} }
// read stsz atom which holds the sample size table // read stsz atom which holds the sample size table
m_sampleSizes.clear(); m_sampleSizes.clear();
m_size = m_sampleCount = 0; m_size = m_sampleCount = 0;
@ -1527,6 +1544,7 @@ void Mp4Track::internalParseHeader()
} }
} }
} }
// no sample sizes found, search for trun atoms // no sample sizes found, search for trun atoms
uint64 totalDuration = 0; uint64 totalDuration = 0;
for(Mp4Atom *moofAtom = m_trakAtom->container().firstElement()->siblingById(MovieFragment, true); moofAtom; moofAtom = moofAtom->siblingById(MovieFragment, false)) { for(Mp4Atom *moofAtom = m_trakAtom->container().firstElement()->siblingById(MovieFragment, true); moofAtom; moofAtom = moofAtom->siblingById(MovieFragment, false)) {
@ -1654,6 +1672,7 @@ void Mp4Track::internalParseHeader()
} }
} }
} }
// set duration from "trun-information" if the duration has not been determined yet // set duration from "trun-information" if the duration has not been determined yet
if(m_duration.isNull() && totalDuration) { if(m_duration.isNull() && totalDuration) {
uint32 timeScale = m_timeScale; uint32 timeScale = m_timeScale;
@ -1664,10 +1683,12 @@ void Mp4Track::internalParseHeader()
m_duration = TimeSpan::fromSeconds(static_cast<double>(totalDuration) / static_cast<double>(timeScale)); m_duration = TimeSpan::fromSeconds(static_cast<double>(totalDuration) / static_cast<double>(timeScale));
} }
} }
// caluculate average bitrate // caluculate average bitrate
if(m_bitrate < 0.01 && m_bitrate > -0.01) { if(m_bitrate < 0.01 && m_bitrate > -0.01) {
m_bitrate = (static_cast<double>(m_size) * 0.0078125) / m_duration.totalSeconds(); m_bitrate = (static_cast<double>(m_size) * 0.0078125) / m_duration.totalSeconds();
} }
// read stsc atom (only number of entries) // read stsc atom (only number of entries)
m_istream->seekg(m_stscAtom->dataOffset() + 4); m_istream->seekg(m_stscAtom->dataOffset() + 4);
m_sampleToChunkEntryCount = reader.readUInt32BE(); m_sampleToChunkEntryCount = reader.readUInt32BE();

View File

@ -47,6 +47,7 @@ enum Sig32 : uint32
Mp4 = 0x66747970u, Mp4 = 0x66747970u,
Ogg = 0x4F676753u, Ogg = 0x4F676753u,
PhotoshopDocument = 0x38425053u, PhotoshopDocument = 0x38425053u,
QuickTime = 0x6D6F6F76u,
Riff = 0x52494646u, Riff = 0x52494646u,
RiffWave =0x57415645u, RiffWave =0x57415645u,
TiffBigEndian = 0x4D4D002Au, TiffBigEndian = 0x4D4D002Au,
@ -124,6 +125,8 @@ ContainerFormat parseSignature(const char *buffer, int bufferSize)
switch(sig & 0x00000000FFFFFFFF) { // check 32-bit signatures @ bit 31 switch(sig & 0x00000000FFFFFFFF) { // check 32-bit signatures @ bit 31
case Mp4: case Mp4:
return ContainerFormat::Mp4; return ContainerFormat::Mp4;
case QuickTime:
return ContainerFormat::QuickTime;
default: default:
; ;
} }
@ -285,6 +288,7 @@ const char *containerFormatAbbreviation(ContainerFormat containerFormat, MediaTy
case ContainerFormat::Bzip2: return "bz"; case ContainerFormat::Bzip2: return "bz";
case ContainerFormat::Gzip: return "gz"; case ContainerFormat::Gzip: return "gz";
case ContainerFormat::Lzip: return "lz"; case ContainerFormat::Lzip: return "lz";
case ContainerFormat::QuickTime: return "mov";
case ContainerFormat::Zip: return "zip"; case ContainerFormat::Zip: return "zip";
case ContainerFormat::SevenZ: return "7z"; case ContainerFormat::SevenZ: return "7z";
default: return ""; default: return "";
@ -367,6 +371,8 @@ const char *containerFormatName(ContainerFormat containerFormat)
return "lzip compressed file"; return "lzip compressed file";
case ContainerFormat::SevenZ: case ContainerFormat::SevenZ:
return "7z archive"; return "7z archive";
case ContainerFormat::QuickTime:
return "Quick Time";
case ContainerFormat::Zip: case ContainerFormat::Zip:
return "ZIP archive"; return "ZIP archive";
default: default:
@ -445,6 +451,8 @@ const char *containerMimeType(ContainerFormat containerFormat, MediaType mediaTy
return "application/x-rar-compressed"; return "application/x-rar-compressed";
case ContainerFormat::Lzip: case ContainerFormat::Lzip:
return "application/x-lzip"; return "application/x-lzip";
case ContainerFormat::QuickTime:
return "video/quicktime";
case ContainerFormat::Zip: case ContainerFormat::Zip:
return "application/zip"; return "application/zip";
case ContainerFormat::SevenZ: case ContainerFormat::SevenZ:

View File

@ -50,6 +50,7 @@ enum class ContainerFormat
WindowsIcon, /**< Microsoft Windows Icon */ WindowsIcon, /**< Microsoft Windows Icon */
SevenZ, /**< 7z archive */ SevenZ, /**< 7z archive */
Lzip, /**< lz compressed file */ Lzip, /**< lz compressed file */
QuickTime, /**< QuickTime container */
Zip /**< ZIP archive */ Zip /**< ZIP archive */
}; };