improved media format detection; other minor improvements

This commit is contained in:
Martchus 2015-06-07 00:18:28 +02:00
parent 221a690311
commit c452b009ae
26 changed files with 1583 additions and 353 deletions

View File

@ -32,7 +32,7 @@ AbstractTrack::AbstractTrack(istream &inputStream, ostream &outputStream, uint64
m_writer(BinaryWriter(&outputStream)),
m_startOffset(startOffset),
m_headerValid(false),
m_format(MediaFormat::Unknown),
m_format(),
m_mediaType(MediaType::Unknown),
m_version(0.0),
m_size(0),
@ -79,22 +79,13 @@ AbstractTrack::~AbstractTrack()
/*!
* \brief Returns the format of the track as C-style string if known; otherwise
* returns the format abbreviation or an empty string.
* \remarks
* - The caller must not free the returned string.
* - The string might get invalidated when the track is (re)parsed.
*/
const char *AbstractTrack::formatName() const
{
if(!m_formatName.empty()) {
return m_formatName.c_str();
}
const char *formatName = mediaFormatName(m_format);
if(*formatName == 0) {
if(!m_formatId.empty()) {
return m_formatId.c_str();
} else {
return "unknown";
}
} else {
return formatName;
}
return m_format || m_formatName.empty() ? m_format.name() : m_formatName.c_str();
}
/*!
@ -103,43 +94,8 @@ const char *AbstractTrack::formatName() const
*/
const char *AbstractTrack::formatAbbreviation() const
{
if(!m_formatId.empty()) {
return m_formatId.c_str();
}
switch(m_format) {
case MediaFormat::Pcm:
return "PCM";
case MediaFormat::Mpeg1:
return "MPEG-1";
case MediaFormat::Mpeg2:
return "MPEG-2";
case MediaFormat::MpegL1:
return "MP1";
case MediaFormat::MpegL2:
return "MP2";
case MediaFormat::MpegL3:
return "MP3";
case MediaFormat::Aac:
return "AAC";
case MediaFormat::Png:
return "PNG";
case MediaFormat::Jpeg:
return "JPEG";
case MediaFormat::Mpeg4Avc:
return "AVC";
case MediaFormat::Mpeg4Asp:
return "ASP";
case MediaFormat::Mpeg4:
return "MPEG-4";
case MediaFormat::Gif:
return "GIF";
case MediaFormat::Tiff:
return "TIFF";
case MediaFormat::Ac3:
return "AC3";
default:
return "";
}
const char *abbr = m_format.abbreviation();
return *abbr || m_formatId.empty() ? m_format.abbreviation() : m_formatId.c_str();
}
/*!
@ -148,11 +104,11 @@ const char *AbstractTrack::formatAbbreviation() const
const char *AbstractTrack::mediaTypeName() const
{
switch(m_mediaType) {
case MediaType::Acoustic:
case MediaType::Audio:
return "Audio";
case MediaType::Visual:
case MediaType::Video:
return "Video";
case MediaType::Textual:
case MediaType::Text:
return "Subititle";
case MediaType::Hint:
return "Hint";

View File

@ -4,6 +4,7 @@
#include "statusprovider.h"
#include "size.h"
#include "margin.h"
#include "mediaformat.h"
#include <c++utilities/conversion/types.h>
#include <c++utilities/io/binaryreader.h>
@ -17,7 +18,7 @@
namespace Media {
enum class MediaType;
enum class MediaFormat;
enum class GeneralMediaFormat;
/*!
* \brief Specifies the track type.

View File

@ -4,6 +4,15 @@
namespace Media {
/*!
* \struct Media::SpsInfo
* \brief The SpsInfo struct holds the sequence parameter set.
*/
/*!
* \brief SpsInfo::parse
* \param stream
*/
void SpsInfo::parse(std::istream &stream)
{
static auto highLevelProfileIds = std::unordered_map<unsigned int, bool> {

View File

@ -27,6 +27,72 @@ namespace Media {
template <class ImplementationType>
class GenericFileElement;
/*!
* \class Media::FileElementIterator
* \brief The FileElementIterator class helps iterating through the childs of a FileElement.
*/
template<typename ImplementationType>
class FileElementIterator
{
public:
FileElementIterator(ImplementationType *element = nullptr);
ImplementationType *operator *();
const ImplementationType *operator *() const;
operator bool() const;
FileElementIterator<ImplementationType> &operator ++();
private:
ImplementationType *m_current;
};
/*!
* \brief Constructs a new iterator for the specified \a element.
*/
template<typename ImplementationType>
inline FileElementIterator<ImplementationType>::FileElementIterator(ImplementationType *element) :
m_current(element)
{}
/*!
* \brief Returns a reference to the current element.
*/
template<typename ImplementationType>
inline ImplementationType *FileElementIterator<ImplementationType>::operator *()
{
return m_current;
}
/*!
* \brief Returns a reference the current element (constant).
*/
template<typename ImplementationType>
inline const ImplementationType *FileElementIterator<ImplementationType>::operator *() const
{
return m_current;
}
/*!
* \brief Moves to the next sibling.
*/
template<typename ImplementationType>
inline FileElementIterator<ImplementationType> &FileElementIterator<ImplementationType>::operator ++()
{
m_current->parse(); // ensure the current element has been parsed
m_current = m_current->nextSibling();
return *this;
}
/*!
* \brief Returns whether the iterator points to an element.
*/
template<typename ImplementationType>
inline FileElementIterator<ImplementationType>::operator bool() const
{
return m_current != nullptr;
}
/*!
* \class Media::FileElementTraits
* \brief Defines traits for the specified \a ImplementationType.
@ -66,7 +132,7 @@ public:
/*!
* \brief Specifies the type used to store data sizes.
*/
typedef uint64 dataSizeType;
typedef typename FileElementTraits<ImplementationType>::dataSizeType dataSizeType;
/*!
* \brief Specifies the type of the actual implementation.
@ -106,6 +172,10 @@ public:
implementationType* subelementByPath(std::list<identifierType> &path);
implementationType* childById(const identifierType &id);
implementationType* siblingById(const identifierType &id, bool includeThis = false);
FileElementIterator<implementationType> begin();
FileElementIterator<implementationType> end();
const FileElementIterator<implementationType> begin() const;
const FileElementIterator<implementationType> end() const;
bool isParent() const;
bool isPadding() const;
uint64 firstChildOffset() const;
@ -140,6 +210,7 @@ private:
/*!
* \brief Constructs a new top level file element with the specified \a container at the specified \a startOffset.
* \remarks The available size is obtained using the stream of the \a container.
*/
template <class ImplementationType>
GenericFileElement<ImplementationType>::GenericFileElement(GenericFileElement<ImplementationType>::containerType &container, uint64 startOffset) :
@ -339,6 +410,9 @@ inline uint64 GenericFileElement<ImplementationType>::totalSize() const
/*!
* \brief Returns maximum total size.
*
* This is usually the size of the file for top-level elements and
* the remaining size of the parent for non-top-level elements.
*/
template <class ImplementationType>
inline uint64 GenericFileElement<ImplementationType>::maxTotalSize() const
@ -528,6 +602,42 @@ typename GenericFileElement<ImplementationType>::implementationType *GenericFile
return nullptr;
}
/*!
* \brief Returns an iterator for iterating over the element's childs.
*/
template <class ImplementationType>
FileElementIterator<typename GenericFileElement<ImplementationType>::implementationType> GenericFileElement<ImplementationType>::begin()
{
return FileElementIterator<implementationType>(firstChild());
}
/*!
* \brief Returns an iterator for iterating over the element's childs (constant).
*/
template <class ImplementationType>
const FileElementIterator<typename GenericFileElement<ImplementationType>::implementationType> GenericFileElement<ImplementationType>::begin() const
{
return FileElementIterator<implementationType>(firstChild());
}
/*!
* \brief Returns an invalid iterator.
*/
template <class ImplementationType>
FileElementIterator<typename GenericFileElement<ImplementationType>::implementationType> GenericFileElement<ImplementationType>::end()
{
return FileElementIterator<ImplementationType>();
}
/*!
* \brief Returns an invalid iterator.
*/
template <class ImplementationType>
const FileElementIterator<typename GenericFileElement<ImplementationType>::implementationType> GenericFileElement<ImplementationType>::end() const
{
return FileElementIterator<ImplementationType>();
}
/*!
* \brief Returns an indication whether this instance is a parent element.
*/

View File

@ -34,6 +34,13 @@ EbmlElement::EbmlElement(MatroskaContainer &container, uint64 startOffset) :
GenericFileElement<EbmlElement>(container, startOffset)
{}
/*!
* \brief Constructs a new top level element with the specified \a container at the specified \a startOffset.
*/
EbmlElement::EbmlElement(MatroskaContainer &container, uint64 startOffset, uint64 maxSize) :
GenericFileElement<EbmlElement>(container, startOffset, maxSize)
{}
/*!
* \brief Constructs a new sub level element with the specified \a parent at the specified \a startOffset.
*/
@ -129,7 +136,7 @@ void EbmlElement::internalParse()
if(parent()) {
m_nextSibling.reset(new EbmlElement(*(parent()), startOffset() + totalSize()));
} else {
m_nextSibling = make_unique<EbmlElement>(container(), startOffset() + totalSize());
m_nextSibling.reset(new EbmlElement(container(), startOffset() + totalSize(), maxTotalSize() - totalSize()));
}
} else {
m_nextSibling.reset();

View File

@ -35,6 +35,11 @@ public:
*/
typedef uint32 identifierType;
/*!
* \brief The type used to store element sizes is an unsigned 64-bit integer.
*/
typedef uint64 dataSizeType;
/*!
* \brief The implementation type is EbmlElement.
*/
@ -67,6 +72,7 @@ public:
protected:
EbmlElement(EbmlElement &parent, uint64 startOffset);
EbmlElement(MatroskaContainer &container, uint64 startOffset, uint64 maxSize);
void internalParse();

View File

@ -6,7 +6,10 @@
#include "../mediaformat.h"
#include "../exceptions.h"
#include <c++utilities/conversion/stringconversion.h>
using namespace std;
using namespace ConversionUtilities;
namespace Media {
@ -37,6 +40,144 @@ TrackType MatroskaTrack::type() const
return TrackType::MatroskaTrack;
}
/*!
* \brief Returns the MediaFormat for the specified Matroska codec ID.
*/
MediaFormat MatroskaTrack::codecIdToMediaFormat(const string &codecId)
{
auto parts = splitString<vector<string> >(codecId, "/", EmptyPartsTreat::Keep, 3);
parts.resize(3);
const auto &part1 = parts[0], &part2 = parts[1], &part3 = parts[2];
MediaFormat fmt;
if(part1 == "V_MS" && part2 == "VFW" && part3 == "FOURCC") {
fmt.general = GeneralMediaFormat::MicrosoftVideoCodecManager;
} else if(part1 == "V_UNCOMPRESSED") {
fmt.general = GeneralMediaFormat::UncompressedVideoFrames;
} else if(part1 == "V_MPEG4") {
fmt.general = GeneralMediaFormat::Mpeg4Video;
if(part2 == "ISO") {
if(part3 == "SP") {
fmt.sub = SubFormats::Mpeg4Sp;
} else if(part3 == "ASP") {
fmt.sub = SubFormats::Mpeg4Asp;
} else if(part3 == "AP") {
fmt.sub = SubFormats::Mpeg4Avc;
}
} else if(part2 == "MS" && part3 == "V3") {
fmt.sub = SubFormats::Mpeg4MsV3;
}
} else if(part1 == "V_MPEG1") {
fmt.general = GeneralMediaFormat::Mpeg1Video;
} else if(part1 == "V_MPEG2") {
fmt.general = GeneralMediaFormat::Mpeg2Video;
} else if(part1 == "V_REAL") {
fmt.general = GeneralMediaFormat::RealVideo;
} else if(part1 == "V_QUICKTIME") {
fmt.general = GeneralMediaFormat::QuicktimeVideo;
} else if(part1 == "V_THEORA") {
fmt.general = GeneralMediaFormat::Theora;
} else if(part1 == "V_PRORES") {
fmt.general = GeneralMediaFormat::ProRes;
} else if(part1 == "A_MPEG") {
fmt.general = GeneralMediaFormat::Mpeg1Audio;
if(part2 == "L1") {
fmt.sub = SubFormats::Mpeg1Layer1;
} else if(part2 == "L2") {
fmt.sub = SubFormats::Mpeg1Layer2;
} else if(part2 == "L3") {
fmt.sub = SubFormats::Mpeg1Layer3;
}
} else if(part1 == "A_PCM") {
fmt.general = GeneralMediaFormat::Pcm;
if(part2 == "INT") {
if(part3 == "BIG") {
fmt.sub = SubFormats::PcmIntBe;
} else if(part3 == "LIT") {
fmt.sub = SubFormats::PcmIntLe;
}
} else if (part2 == "FLOAT" && part3 == "IEEE") {
fmt.sub = SubFormats::PcmFloatIeee;
}
} else if(part1 == "A_MPC") {
fmt.general = GeneralMediaFormat::Mpc;
} else if(part1 == "A_AC3") {
fmt.general = GeneralMediaFormat::Ac3;
} else if(part1 == "A_ALAC") {
fmt.general = GeneralMediaFormat::Alac;
} else if(part1 == "A_DTS") {
fmt.general = GeneralMediaFormat::Dts;
if(part2 == "EXPRESS") {
fmt.sub = SubFormats::DtsExpress;
} else if(part2 == "LOSSLESS") {
fmt.sub = SubFormats::DtsLossless;
}
} else if(part1 == "A_VORBIS") {
fmt.general = GeneralMediaFormat::Vorbis;
} else if(part1 == "A_FLAC") {
fmt.general = GeneralMediaFormat::Flac;
} else if(part1 == "A_REAL") {
fmt.general = GeneralMediaFormat::RealAudio;
} else if(part1 == "A_MS" && part2 == "ACM") {
fmt.general = GeneralMediaFormat::MicrosoftAudioCodecManager;
} else if(part1 == "A_AAC") {
fmt.general = GeneralMediaFormat::Aac;
if(part2 == "MPEG2") {
if(part3 == "MAIN") {
fmt.sub = SubFormats::AacMpeg2MainProfile;
} else if(part3 == "LC") {
fmt.sub = SubFormats::AacMpeg2LowComplexityProfile;
} else if(part3 == "SBR") {
fmt.sub = SubFormats::AacMpeg2SpectralBandReplicationProfile;
} else if(part3 == "SSR") {
fmt.sub = SubFormats::AacMpeg2ScalableSamplingRateProfile;
}
} else if(part2 == "MPEG4") {
if(part3 == "MAIN") {
fmt.sub = SubFormats::AacMpeg4MainProfile;
} else if(part3 == "LC") {
fmt.sub = SubFormats::AacMpeg4LowComplexityProfile;
} else if(part3 == "SBR") {
fmt.sub = SubFormats::AacMpeg4SpectralBandReplicationProfile;
} else if(part3 == "SSR") {
fmt.sub = SubFormats::AacMpeg4ScalableSamplingRateProfile;
} else if(part3 == "LTP") {
fmt.sub = SubFormats::AacMpeg4LongTermPredictionProfile;
}
}
} else if(part1 == "A_QUICKTIME") {
fmt.general = GeneralMediaFormat::QuicktimeAudio;
} else if(part1 == "A_TTA1") {
fmt.general = GeneralMediaFormat::Tta;
} else if(part1 == "A_WAVPACK4") {
fmt.general = GeneralMediaFormat::WavPack;
} else if(part1 == "S_TEXT") {
fmt.general = GeneralMediaFormat::TextSubtitle;
if(part2 == "UTF8") {
fmt.sub = SubFormats::TextSubBasicUtf8;
} else if(part2 == "SSA") {
fmt.sub = SubFormats::TextSubSubtitlesFormat;
} else if(part2 == "ASS") {
fmt.sub = SubFormats::TextSubAdvancedSubtitlesFormat;
} else if(part2 == "USF") {
fmt.sub = SubFormats::TextSubUniversalSubtitleFormat;
}
} else if(part1 == "S_IMAGE") {
fmt.general = GeneralMediaFormat::ImageSubtitle;
if(part2 == "BMP") {
fmt.sub = SubFormats::ImgSubBmp;
}
} else if(part1 == "S_VOBSUB") {
fmt.general = GeneralMediaFormat::VobSub;
} else if(part1 == "S_KATE") {
fmt.general = GeneralMediaFormat::OggKate;
} else if(part1 == "B_VOBBTN") {
fmt.general = GeneralMediaFormat::VobBtn;
} else if(part1 == "V_MSWMV") {
fmt.general = GeneralMediaFormat::Vc1;
}
return fmt;
}
void MatroskaTrack::internalParseHeader()
{
const string context("parsing header of Matroska track");
@ -61,13 +202,13 @@ void MatroskaTrack::internalParseHeader()
case MatroskaIds::TrackType:
switch(trackInfoElement->readUInteger()) {
case MatroskaTrackType::Video:
m_mediaType = MediaType::Visual;
m_mediaType = MediaType::Video;
break;
case MatroskaTrackType::Audio:
m_mediaType = MediaType::Acoustic;
m_mediaType = MediaType::Audio;
break;
case MatroskaTrackType::Subtitle:
m_mediaType = MediaType::Textual;
m_mediaType = MediaType::Text;
break;
default:
m_mediaType = MediaType::Unknown;
@ -158,7 +299,10 @@ void MatroskaTrack::internalParseHeader()
m_language = trackInfoElement->readString();
break;
case MatroskaIds::CodecID:
m_formatId = trackInfoElement->readString();
m_format = codecIdToMediaFormat(m_formatId = trackInfoElement->readString());
if(m_formatName.empty()) {
m_formatName = m_format ? string(m_format.name()) : m_formatId;
}
break;
case MatroskaIds::CodecName:
m_formatName = trackInfoElement->readString();
@ -181,7 +325,7 @@ void MatroskaTrack::internalParseHeader()
default: ;
}
switch(m_mediaType) {
case MediaType::Visual:
case MediaType::Video:
if(m_fps == 0 && defaultDuration != 0) {
m_fps = 1000000000.0 / static_cast<double>(defaultDuration);
}

View File

@ -18,6 +18,8 @@ public:
TrackType type() const;
static MediaFormat codecIdToMediaFormat(const std::string &codecId);
protected:
void internalParseHeader();

View File

@ -537,18 +537,11 @@ const char *MediaFileInfo::containerFormatAbbreviation() const
case ContainerFormat::Ogg:
case ContainerFormat::Matroska:
case ContainerFormat::Mp4:
mediaType = hasTracksOfType(MediaType::Visual) ? MediaType::Visual : MediaType::Acoustic;
mediaType = hasTracksOfType(MediaType::Video) ? MediaType::Video : MediaType::Audio;
break;
case ContainerFormat::MpegAudioFrames:
if(m_mpegAudioFrameStream) {
switch(m_mpegAudioFrameStream->format()) {
case MediaFormat::MpegL1:
version = 1;
case MediaFormat::MpegL2:
version = 2;
default:
version = 3;
}
version = m_mpegAudioFrameStream->format().sub;
}
break;
default:
@ -597,17 +590,17 @@ const char *MediaFileInfo::mimeType() const
case ContainerFormat::MpegAudioFrames:
return "audio/mpeg";
case ContainerFormat::Mp4:
if(hasTracksOfType(MediaType::Visual)) {
if(hasTracksOfType(MediaType::Video)) {
return "video/mp4";
}
return "audio/mp4";
case ContainerFormat::Ogg:
if(hasTracksOfType(MediaType::Visual)) {
if(hasTracksOfType(MediaType::Video)) {
return "video/ogg";
}
return "audio/ogg";
case ContainerFormat::Matroska:
if(hasTracksOfType(MediaType::Visual)) {
if(hasTracksOfType(MediaType::Video)) {
return "video/x-matroska";
}
return "audio/x-matroska";
@ -699,7 +692,7 @@ bool MediaFileInfo::hasTracksOfType(MediaType type) const
if(!areTracksParsed()) {
return false;
}
if(type == MediaType::Acoustic && (m_waveAudioStream || m_mpegAudioFrameStream)) {
if(type == MediaType::Audio && (m_waveAudioStream || m_mpegAudioFrameStream)) {
return true;
} else if(m_container) {
for(size_t i = 0, count = m_container->trackCount(); i < count; ++i) {
@ -907,6 +900,7 @@ bool MediaFileInfo::areChaptersSupported() const
}
switch(m_containerFormat) {
case ContainerFormat::Matroska:
case ContainerFormat::Webm:
return true;
default:
return false;
@ -923,6 +917,7 @@ bool MediaFileInfo::areAttachmentsSupported() const
}
switch(m_containerFormat) {
case ContainerFormat::Matroska:
case ContainerFormat::Webm:
return true;
default:
return false;
@ -943,6 +938,7 @@ bool MediaFileInfo::areTracksSupported() const
case ContainerFormat::RiffWave:
case ContainerFormat::Ogg:
case ContainerFormat::Matroska:
case ContainerFormat::Webm:
return true;
default:
return false;
@ -962,6 +958,7 @@ bool MediaFileInfo::areTagsSupported() const
case ContainerFormat::MpegAudioFrames:
case ContainerFormat::Ogg:
case ContainerFormat::Matroska:
case ContainerFormat::Webm:
return true;
default:
return false;

View File

@ -2,74 +2,282 @@
namespace Media {
using namespace SubFormats;
/*!
* \brief Returns the name of the specified media format as C-style string.
* \class Media::MediaFormat
* \brief The MediaFormat class specifies the format of media data.
*/
/*!
* \brief Returns the name of the media format as C-style string.
*
* Returns an empty string if no name is available.
*/
const char *mediaFormatName(MediaFormat mediaFormat)
const char *MediaFormat::name() const
{
switch(mediaFormat) {
case MediaFormat::Pcm:
return "Puls-Code-Modulation";
case MediaFormat::Mpeg1:
return "MPEG-1";
case MediaFormat::Mpeg2:
return "MPEG-2";
case MediaFormat::MpegL1:
return "MPEG-1 Layer 1";
case MediaFormat::MpegL2:
return "MPEG-1 Layer 2";
case MediaFormat::MpegL3:
return "MPEG-1 Layer 3";
case MediaFormat::Aac:
return "Advanced Audio Coding";
case MediaFormat::Vorbis:
return "Vorbis";
case MediaFormat::Png:
return "Portable Network Graphics";
case MediaFormat::Jpeg:
return "JPEG File Interchange Format";
case MediaFormat::Mpeg4Sp:
return "H.264/MPEG-4 Simple profile";
case MediaFormat::Mpeg4Avc:
return "H.264/MPEG-4 Advanced Video Coding";
case MediaFormat::Mpeg4Asp:
return "H.263/MPEG-4 Advanced Simple Profile";
case MediaFormat::Mpeg4:
return "MPEG-4";
case MediaFormat::Gif:
return "Graphics Interchange Format";
case MediaFormat::Tiff:
return "Tagged Image File Format";
case MediaFormat::UncompressedRgb:
return "Uncompressed RGB";
case MediaFormat::AdpcmAcm:
return "Microsoft ADPCM-ACM code 2";
case MediaFormat::ImaadpcmAcm:
return "DVI/Intel IMAADPCM-ACM code 17";
case MediaFormat::Ac3:
return "Dolby Digital (AC-3)";
case MediaFormat::Ac4:
return "Dolby Digital (AC-4)";
case MediaFormat::RealVideo:
return "Real Video";
case MediaFormat::RealAudio:
return "Real Audio";
case MediaFormat::QuicktimeVideo:
return "Quicktime video";
case MediaFormat::QuicktimeAudio:
return "Quicktime audio";
case MediaFormat::Dts:
return "Digital Theatre System";
case MediaFormat::Theora:
return "Theora";
case MediaFormat::ProRes:
return "Apple ProRes";
case MediaFormat::Alac:
return "Apple lossless audio codec";
default:
return "";
switch(general) {
case GeneralMediaFormat::Aac:
switch(sub) {
case AacMpeg2MainProfile: return "Advanced Audio Coding Main Profile";
case AacMpeg2LowComplexityProfile: return "Advanced Audio Coding Low Complexity Profile";
case AacMpeg2SpectralBandReplicationProfile: return "Advanced Audio Coding Low Complexity with Spectral Band Replication Profile";
case AacMpeg2ScalableSamplingRateProfile: return "Advanced Audio Coding Scalable Sampling Rate Profile";
case AacMpeg4MainProfile: return "Advanced Audio Coding Main Profile";
case AacMpeg4LowComplexityProfile: return "Advanced Audio Coding Low Complexity Profile";
case AacMpeg4SpectralBandReplicationProfile: return "Advanced Audio Coding Low Complexity with Spectral Band Replication Profile";
case AacMpeg4ScalableSamplingRateProfile: return "Advanced Audio Coding Scaleable Sampling Rate Profile";
case AacMpeg4LongTermPredictionProfile: return "Advanced Audio Coding Scalable Sampling Rate Profile";
default: return "Advanced Audio Coding";
}
case GeneralMediaFormat::Ac3: return "Dolby Digital";
case GeneralMediaFormat::Ac4: return "AC-4";
case GeneralMediaFormat::AdpcmAcm: return "ADPCM ACM";
case GeneralMediaFormat::AfxStream: return "AFX Stream";
case GeneralMediaFormat::Alac: return "Apple Lossless Audio Codec";
case GeneralMediaFormat::Als: return "ALS";
case GeneralMediaFormat::Bitmap: return "Windows Bitmap";
case GeneralMediaFormat::Dirac: return "Dirac";
case GeneralMediaFormat::Dts: return "DTS";
switch(sub) {
case DtsLossless: return "DTS Lossless";
case DtsExpress: return "DTS Express";
default: return "DTS";
}
case GeneralMediaFormat::DtsHd: return "DTS-HD";
switch(sub) {
case DtsHdHighResolution: return "DTS-HD High Resolution";
case DtsHdMasterAudio: return "DTS-HD Master Audio";
case DtsExpress: return "DTS-HD Express";
default: return "DTS-HD";
}
case GeneralMediaFormat::EAc3: return "Dolby Digital Plus";
case GeneralMediaFormat::Evrc: return "EVRC";
case GeneralMediaFormat::Flac: return "Free Lossless Audio Codec";
case GeneralMediaFormat::FontDataStream: return "Font Data Stream";
case GeneralMediaFormat::Gif: return "GIF";
case GeneralMediaFormat::Gpp2Cmf: return "3GPP2 Compact Multimedia Format (CMF)";
case GeneralMediaFormat::ImaadpcmAcm: return "IMAADPCM ACM";
case GeneralMediaFormat::ImageSubtitle:
switch(sub) {
case SubFormats::ImgSubBmp: return "Bitmap subtitle";
default: return "Image subtitle";
}
case GeneralMediaFormat::InteractionStream: return "Interaction Stream";
case GeneralMediaFormat::Jpeg: return "JPEG";
case GeneralMediaFormat::OggKate: return "Karaoke And Text Encapsulation";
case GeneralMediaFormat::MicrosoftAudioCodecManager: return "Microsoft Audio Codec Manager";
case GeneralMediaFormat::MicrosoftVideoCodecManager: return "Microsoft Video Codec Manager";
case GeneralMediaFormat::Mpeg1Audio:
switch(sub) {
case Mpeg1Layer1: return "MPEG-1 Layer 1";
case Mpeg1Layer2: return "MPEG-1 Layer 2";
case Mpeg1Layer3: return "MPEG-1 Layer 3";
default: return "MPEG-1 Audio";
}
case GeneralMediaFormat::Mpeg1Video: return "MPEG-1 Video";
case GeneralMediaFormat::Mpeg2Audio:
switch(sub) {
case Mpeg1Layer1: return "MPEG-2 Layer 1";
case Mpeg1Layer2: return "MPEG-2 Layer 2";
case Mpeg1Layer3: return "MPEG-2 Layer 3";
default: return "MPEG-2 Audio";
}
case GeneralMediaFormat::Mpeg2Video:
switch(sub) {
case Mpeg2SimpleProfile: return "MPEG-2 Video Simple Profile";
case Mpeg2MainProfile: return "MPEG-2 Video Main Profile";
case Mpeg2SnrProfile: return "MPEG-2 Video SNR Profile";
case Mpeg2SpatialProfile: return "MPEG-2 Video Spatial Profile";
case Mpeg2HighProfile: return "MPEG-2 Video High Profile";
case Mpeg2422Profile: return "MPEG-2 Video 422 Profile";
default: return "MPEG-2 Video";
}
case GeneralMediaFormat::Mpeg4Video:
switch(sub) {
case Mpeg4Sp: return "MPEG-4 Simple Profile";
case Mpeg4Asp: return "MPEG-4 Advanced Simple Profile";
case Mpeg4Avc: return "MPEG-4 Advanced Video Coding";
case Mpeg4AvcParams: return "Parameter for MPEG-4 Advanced Video Coding";
case Mpeg4MsV3: return "MPEG-4 Microsoft V3";
default: return "MPEG-4 Visual";
}
case GeneralMediaFormat::Mpc: return "Musepack SV8";
case GeneralMediaFormat::Pcm:
switch(sub) {
case PcmIntBe: return "Pulse Code Modulation (integer, big endian)";
case PcmIntLe: return "Pulse Code Modulation (integer, little endian)";
case PcmFloatIeee: return "Pulse Code Modulation (float, IEEE)";
default: return "Pulse Code Modulation";
}
case GeneralMediaFormat::Png: return "Portable Network Graphics";
case GeneralMediaFormat::ProRes: return "ProRes";
case GeneralMediaFormat::Qcelp: return "QCELP";
case GeneralMediaFormat::QuicktimeAudio: return "Quicktime Audio";
case GeneralMediaFormat::QuicktimeVideo: return "Quicktime Video";
case GeneralMediaFormat::RealAudio: return "Real Audio";
case GeneralMediaFormat::RealVideo: return "Real Video";
case GeneralMediaFormat::Sa0c: return "SAOC";
case GeneralMediaFormat::Smv: return "SMV";
case GeneralMediaFormat::StreamingTextStream: return "Streaming Text Stream";
case GeneralMediaFormat::SynthesizedTextureStream: return "Synthesized Texture Stream";
case GeneralMediaFormat::Systems: return "Systems";
case GeneralMediaFormat::TextSubtitle:
switch(sub) {
case SubFormats::TextSubBasicUtf8: return "UTF-8 Plain Text subtitles";
case SubFormats::TextSubSubtitlesFormat: return "Subtitles Format";
case SubFormats::TextSubAdvancedSubtitlesFormat: return "Advanced Subtitles Format";
case SubFormats::TextSubUniversalSubtitleFormat: return "Universal Subtitle Format";
default: return "Text subtitle";
}
case GeneralMediaFormat::Theora: return "Theora";
case GeneralMediaFormat::Tiff: return "Tagged Image File Format";
case GeneralMediaFormat::Tta: return "The True Audio";
case GeneralMediaFormat::UncompressedVideoFrames: return "uncompressed video frames";
case GeneralMediaFormat::Vc1: return "Windows Media Video";
case GeneralMediaFormat::VobBtn: return "VobBtn Buttons";
case GeneralMediaFormat::VobSub: return "VobSub";
case GeneralMediaFormat::Vorbis: return "Vorbis";
case GeneralMediaFormat::WavPack: return "WavPack";
default: return "unknown";
}
}
/*!
* \brief Returns the abbreviation of the media format as C-style string.
*
* Returns an empty string if no abbreviation is available.
*/
const char *MediaFormat::abbreviation() const
{
switch(general) {
case GeneralMediaFormat::Aac:
switch(sub) {
case AacMpeg2MainProfile: return "MPEG-2 AAC Main";
case AacMpeg2LowComplexityProfile: return "MPEG-2 AAC-LC";
case AacMpeg2SpectralBandReplicationProfile: return "MPEG-2-SBR";
case AacMpeg2ScalableSamplingRateProfile: return "MPEG-2 AAC-SSR";
case AacMpeg4MainProfile: return "MPEG-4 AAC Main";
case AacMpeg4LowComplexityProfile: return "MPEG-4 AAC-LC";
case AacMpeg4SpectralBandReplicationProfile: return "MPEG-4 AAC-SBR";
case AacMpeg4ScalableSamplingRateProfile: return "MPEG-4 AAC-SSR";
case AacMpeg4LongTermPredictionProfile: return "MPEG-4 AAC-LTP";
default: return "AAC";
}
case GeneralMediaFormat::Ac3: return "AC-3";
case GeneralMediaFormat::Ac4: return "AC-4";
case GeneralMediaFormat::AdpcmAcm: return "ADPCM ACM";
case GeneralMediaFormat::AfxStream: return "AFX";
case GeneralMediaFormat::Alac: return "ALAC";
case GeneralMediaFormat::Als: return "ALS";
case GeneralMediaFormat::Bitmap: return "BMP";
case GeneralMediaFormat::Dirac: return "Dirac";
case GeneralMediaFormat::Dts: return "DTS";
switch(sub) {
case DtsLossless: return "DTS Lossless";
case DtsExpress: return "DTS LBR";
default: return "DTS";
}
case GeneralMediaFormat::DtsHd: return "DTS-HD";
switch(sub) {
case DtsHdHighResolution: return "DTS-HD High Resolution";
case DtsHdMasterAudio: return "DTS-HD Master Audio";
case DtsExpress: return "DTS-HD Express";
default: return "DTS-HD";
}
case GeneralMediaFormat::EAc3: return "E-AC-3";
case GeneralMediaFormat::Evrc: return "EVRC";
case GeneralMediaFormat::Flac: return "FLAC";
case GeneralMediaFormat::FontDataStream: return "FDS";
case GeneralMediaFormat::Gif: return "GIF";
case GeneralMediaFormat::Gpp2Cmf: return "3GPP2 CMF";
case GeneralMediaFormat::ImaadpcmAcm: return "IMAADPCM ACM";
case GeneralMediaFormat::ImageSubtitle:
switch(sub) {
case SubFormats::ImgSubBmp: return "BMP subtitle";
default: return "Image subtitle";
}
case GeneralMediaFormat::InteractionStream: return "Interaction Stream";
case GeneralMediaFormat::Jpeg: return "JPEG";
case GeneralMediaFormat::OggKate: return "OggKate";
case GeneralMediaFormat::MicrosoftAudioCodecManager: return "MS ACM";
case GeneralMediaFormat::MicrosoftVideoCodecManager: return "MS VCM";
case GeneralMediaFormat::Mpeg1Audio:
switch(sub) {
case Mpeg1Layer1: return "MP1";
case Mpeg1Layer2: return "MP2";
case Mpeg1Layer3: return "MP3";
default: return "MPEG-1 Audio";
}
case GeneralMediaFormat::Mpeg1Video: return "MP1";
case GeneralMediaFormat::Mpeg2Audio:
switch(sub) {
case Mpeg1Layer1: return "MP1";
case Mpeg1Layer2: return "MP2";
case Mpeg1Layer3: return "MP3";
default: return "MPEG-2 Audio";
}
case GeneralMediaFormat::Mpeg2Video:
switch(sub) {
case Mpeg2SimpleProfile: return "MPEG-2 SP";
case Mpeg2MainProfile: return "MPEG-2 Main";
case Mpeg2SnrProfile: return "MPEG-2 SNR";
case Mpeg2SpatialProfile: return "MPEG-2 Spatial";
case Mpeg2HighProfile: return "MPEG-2 High";
case Mpeg2422Profile: return "MPEG-2 422";
default: return "MPEG-2 Video";
}
case GeneralMediaFormat::Mpeg4Video:
switch(sub) {
case Mpeg4Sp: return "MPEG-4 SP";
case Mpeg4Asp: return "H.263";
case Mpeg4Avc: return "H.264";
case Mpeg4AvcParams: return "H.264 params";
case Mpeg4MsV3: return "MPEG-4 MS V3";
default: return "MPEG-4 Visual";
}
case GeneralMediaFormat::Mpc: return "MPC";
case GeneralMediaFormat::Pcm:
switch(sub) {
case PcmIntBe: return "PCM (int, BE)";
case PcmIntLe: return "PCM (int, LE)";
case PcmFloatIeee: return "PCM IEEE";
default: return "PCM";
}
case GeneralMediaFormat::Png: return "PNG";
case GeneralMediaFormat::ProRes: return "ProRes";
case GeneralMediaFormat::Qcelp: return "QCELP";
case GeneralMediaFormat::QuicktimeAudio: return "Quicktime Audio";
case GeneralMediaFormat::QuicktimeVideo: return "Quicktime Video";
case GeneralMediaFormat::RealAudio: return "Real Audio";
case GeneralMediaFormat::RealVideo: return "Real Video";
case GeneralMediaFormat::Sa0c: return "SAOC";
case GeneralMediaFormat::Smv: return "SMV";
case GeneralMediaFormat::StreamingTextStream: return "Streaming Text Stream";
case GeneralMediaFormat::SynthesizedTextureStream: return "Synthesized Texture Stream";
case GeneralMediaFormat::Systems:
switch(sub) {
case 2: return "Systems v2";
default: return "Systems";
}
case GeneralMediaFormat::TextSubtitle:
switch(sub) {
case SubFormats::TextSubBasicUtf8: return "UTF-8 Sub";
case SubFormats::TextSubSubtitlesFormat: return "SSA";
case SubFormats::TextSubAdvancedSubtitlesFormat: return "ASS";
case SubFormats::TextSubUniversalSubtitleFormat: return "USF";
default: return "Text subtitle";
}
case GeneralMediaFormat::Theora: return "Theora";
case GeneralMediaFormat::Tiff: return "TIFF";
case GeneralMediaFormat::Tta: return "TTA";
case GeneralMediaFormat::UncompressedVideoFrames: return "uncompressed video frames";
case GeneralMediaFormat::Vc1: return "VC-1";
case GeneralMediaFormat::VobBtn: return "VobBtn";
case GeneralMediaFormat::VobSub: return "VobSub";
case GeneralMediaFormat::Vorbis: return "Vorbis";
case GeneralMediaFormat::WavPack: return "WavPack";
default: return "";
}
}

View File

@ -1,61 +1,185 @@
#ifndef MEDIAFORMAT_H
#define MEDIAFORMAT_H
#include <c++utilities/application/global.h>
#include <utility>
namespace Media {
/*!
* \brief The MediaType enum specifies the type of media data (acoustic, visual, textual, ...).
* \brief The MediaType enum specifies the type of media data (audio, video, text, ...).
*/
enum class MediaType
{
Acoustic,
Visual,
Textual,
Hint,
Unknown
Unknown,
Audio,
Video,
Text,
Hint
};
/*!
* \brief The MediaFormat enum specifies the format of media data (PCM, MPEG-4, PNG, ...).
* \brief The GeneralMediaFormat enum specifies the general format of media data (PCM, MPEG-4, PNG, ...).
*/
enum class MediaFormat
enum class GeneralMediaFormat
{
Pcm,
Mpeg1,
Mpeg2,
MpegL1,
MpegL2,
MpegL3,
Aac,
Alac,
Vorbis,
Png,
Jpeg,
Bitmap,
Mpeg4Sp,
Mpeg4Avc,
Mpeg4Asp,
Mpeg4,
Gif,
Tiff,
UncompressedRgb,
AdpcmAcm,
ImaadpcmAcm,
Ac3,
Ac4,
Dts,
Flac,
RealAudio,
RealVideo,
QuicktimeVideo,
QuicktimeAudio,
Theora,
ProRes,
VobSub,
Unknown
Unknown,
Aac, /**< Advanced Video Coding */
Ac3, /**< Dolby Digital */
Ac4, /**< AC-4 */
AdpcmAcm, /**< ADPCM ACM */
AfxStream, /**< AFX Stream */
Alac, /**< Apple Lossless Audio Codec */
Als, /**< ALS */
Bitmap, /**< Windows Bitmap */
Dirac, /**< Dirac */
Dts, /**< DTS */
DtsHd, /**< DTS-HD */
EAc3, /**< Dolby Digital Plus */
Evrc, /**< EVRC */
Flac, /**< FLAC */
FontDataStream, /**< Font Data Stream */
Gif, /**< GIF */
Gpp2Cmf, /**< 3GPP2 Compact Multimedia Format (CMF) */
ImaadpcmAcm, /**< IMAADPCM ACM */
ImageSubtitle, /**< Image subtitle */
InteractionStream, /**< Interaction Stream */
Jpeg, /**< JPEG */
OggKate, /**< Karaoke And Text Encapsulation */
MicrosoftAudioCodecManager, /**< Microsoft Audio Codec Manager (ACM) */
MicrosoftVideoCodecManager, /**< Microsoft Video Codec Manager (VCM) */
Mpeg1Audio, /**< MPEG-1 Audio */
Mpeg1Video, /**< MPEG-1 Vudio */
Mpeg2Audio, /**< MPEG-2 Audio */
Mpeg2Video, /**< MPEG-2 Vudio */
Mpeg4Video, /**< MPEG-4 */
Mpc, /**< Musepack */
Pcm, /**< Pulse Code Modulation */
Png, /**< PNG */
ProRes, /**< ProRes */
Qcelp, /**< QCELP */
QuicktimeAudio, /**< Quicktime Audio */
QuicktimeVideo, /**< Quicktime Video */
RealAudio, /**< Real Audio */
RealVideo, /**< Real Video */
Sa0c, /**< SAOC */
Smv, /**< SMV */
StreamingTextStream, /**< Streaming Text Stream */
SynthesizedTextureStream, /**< Synthesized Texture Stream */
Systems, /**< Systems */
TextSubtitle, /**< Text subtitle */
Theora, /**< Theora */
Tiff, /**< TIFF */
Tta, /**< The True Audio lessles audio compressor */
UncompressedVideoFrames, /**< uncompressed RGB */
Vc1, /**< VC-1 */
VobBtn, /**< VobBtn */
VobSub, /**< VobSub */
Vorbis, /**< Vorbis */
WavPack /**< WavPack */
};
const char *mediaFormatName(MediaFormat mediaFormat);
/*!
* \brief Encapsulates sub formats.
*
* For instance "Layer 3" is a sub format of MPEG-1 audio.
*/
namespace SubFormats {
enum : unsigned char {
None
};
enum Mpeg1AudioLayer : unsigned char {
Mpeg1Layer1 = 1,
Mpeg1Layer2,
Mpeg1Layer3
};
enum AacProfile : unsigned char {
AacMpeg2MainProfile = 1,
AacMpeg2LowComplexityProfile,
AacMpeg2SpectralBandReplicationProfile,
AacMpeg2ScalableSamplingRateProfile,
AacMpeg4MainProfile,
AacMpeg4LowComplexityProfile,
AacMpeg4SpectralBandReplicationProfile,
AacMpeg4ScalableSamplingRateProfile,
AacMpeg4LongTermPredictionProfile
};
enum Mpeg2VideoProfile : unsigned char {
Mpeg2SimpleProfile = 1,
Mpeg2MainProfile,
Mpeg2SnrProfile,
Mpeg2SpatialProfile,
Mpeg2HighProfile,
Mpeg2422Profile
};
enum Mpeg4VideoProfile : unsigned char {
Mpeg4Sp = 1,
Mpeg4Asp,
Mpeg4Avc,
Mpeg4AvcParams,
Mpeg4MsV3
};
enum DtsSpecifier : unsigned char {
DtsExpress = 1,
DtsLossless,
DtsHdHighResolution,
DtsHdMasterAudio,
};
enum PcmVersion : unsigned char {
PcmIntBe = 1,
PcmIntLe,
PcmFloatIeee
};
enum TextSubtitle : unsigned char {
TextSubBasicUtf8 = 1,
TextSubSubtitlesFormat,
TextSubAdvancedSubtitlesFormat,
TextSubUniversalSubtitleFormat
};
enum ImageSubtitle : unsigned char {
ImgSubBmp = 1
};
}
class LIB_EXPORT MediaFormat
{
public:
MediaFormat(GeneralMediaFormat general = GeneralMediaFormat::Unknown, unsigned char sub = 0);
const char *name() const;
const char *abbreviation() const;
operator bool() const;
GeneralMediaFormat general;
unsigned char sub;
};
/*!
* \brief Constructs a new media format.
*/
inline MediaFormat::MediaFormat(GeneralMediaFormat general, unsigned char sub) :
general(general),
sub(sub)
{}
/*!
* \brief Returns whether the media format is known.
*/
inline MediaFormat::operator bool() const
{
return general != GeneralMediaFormat::Unknown;
}
}

View File

@ -28,6 +28,13 @@ Mp4Atom::Mp4Atom(GenericFileElement::containerType &container, uint64 startOffse
GenericFileElement<Mp4Atom>(container, startOffset)
{}
/*!
* \brief Constructs a new top level atom with the specified \a container at the specified \a startOffset.
*/
Mp4Atom::Mp4Atom(GenericFileElement::containerType &container, uint64 startOffset, uint64 maxSize) :
GenericFileElement<Mp4Atom>(container, startOffset, maxSize)
{}
/*!
* \brief Constructs a new sub level atom with the specified \a parent at the specified \a startOffset.
*/
@ -98,7 +105,7 @@ void Mp4Atom::internalParse()
if(parent()) {
sibling = new Mp4Atom(*(parent()), startOffset() + totalSize());
} else {
sibling = new Mp4Atom(container(), startOffset() + totalSize());
sibling = new Mp4Atom(container(), startOffset() + totalSize(), maxTotalSize() - totalSize());
}
}
m_nextSibling.reset(sibling);

View File

@ -35,6 +35,11 @@ public:
*/
typedef uint32 identifierType;
/*!
* \brief The type used to store element sizes is an unsigned 64-bit integer.
*/
typedef uint64 dataSizeType;
/*!
* \brief The implementation type is Mp4Atom.
*/
@ -56,6 +61,7 @@ public:
static void seekBackAndWriteAtomSize(std::ostream &stream, const std::ostream::pos_type &startOffset, bool denote64BitSize = false);
protected:
Mp4Atom(containerType& container, uint64 startOffset, uint64 maxSize);
Mp4Atom(implementationType &parent, uint64 startOffset);
void internalParse();

View File

@ -1,5 +1,7 @@
#include "mp4ids.h"
#include "../mediaformat.h"
namespace Media {
/*!
@ -35,15 +37,157 @@ namespace Mp4MediaTypeIds {
}
/*!
* \brief Encapsulates all supported MP4 media format IDs.
* \brief Encapsulates all supported MP4 media format IDs (aka "FOURCCs").
* \sa http://wiki.multimedia.cx/?title=QuickTime_container
*/
namespace Mp4FormatIds {
MediaFormat fourccToMediaFormat(uint32 fourccId)
{
switch(fourccId) {
case Mp4FormatIds::Mpeg4Video:
return GeneralMediaFormat::Mpeg4Video;
case Mp4FormatIds::Avc1:
case Mp4FormatIds::Avc2:
case Mp4FormatIds::Avc3:
case Mp4FormatIds::Avc4:
return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg4Avc);
case Mp4FormatIds::H263:
return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg4Asp);
case Mp4FormatIds::Tiff:
return GeneralMediaFormat::Tiff;
case Mp4FormatIds::Raw:
return GeneralMediaFormat::UncompressedVideoFrames;
case Mp4FormatIds::Jpeg:
return GeneralMediaFormat::Jpeg;
case Mp4FormatIds::Gif:
return GeneralMediaFormat::Gif;
case Mp4FormatIds::AdpcmAcm:
return GeneralMediaFormat::AdpcmAcm;
case Mp4FormatIds::ImaadpcmAcm:
return GeneralMediaFormat::ImaadpcmAcm;
case Mp4FormatIds::Mp3CbrOnly:
return MediaFormat(GeneralMediaFormat::Mpeg1Audio, SubFormats::Mpeg1Layer3);
case Mp4FormatIds::Mpeg4Audio:
return GeneralMediaFormat::Aac;
case Mp4FormatIds::Alac:
return GeneralMediaFormat::Alac;
case Mp4FormatIds::Ac3:
return GeneralMediaFormat::Ac3;
case Mp4FormatIds::Ac4:
return GeneralMediaFormat::Ac4;
default:
return GeneralMediaFormat::Unknown;
}
}
}
/*!
* \brief Encapsulates all supported MP4 media format configuration box IDs.
* \brief Encapsulates all supported MP4 media format description extensions.
* \sa https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html
*/
namespace Mp4FormatConfigurationIds {
namespace Mp4FormatExtensionIds {
}
/*!
* \brief Encapsulates all supported MPEG-4 elementary stream object IDs.
*/
namespace Mpeg4ElementaryStreamObjectIds {
/*!
* \brief Returns the Media::MediaFormat denoted by the specified MPEG-4 stream ID.
*/
MediaFormat streamObjectTypeFormat(byte streamObjectTypeId)
{
switch(streamObjectTypeId) {
case SystemsIso144961: return GeneralMediaFormat::Systems;
case SystemsIso144961v2: return MediaFormat(GeneralMediaFormat::Systems, 2);
case InteractionStream: return GeneralMediaFormat::InteractionStream;
case AfxStream: return GeneralMediaFormat::AfxStream;
case FontDataStream: return GeneralMediaFormat::FontDataStream;
case SynthesizedTextureStream: return GeneralMediaFormat::SynthesizedTextureStream;
case StreamingTextStream: return GeneralMediaFormat::StreamingTextStream;
case Mpeg4Visual: return GeneralMediaFormat::Mpeg4Video;
case Avc: return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg4Avc);
case ParameterSetsForAvc: return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg4AvcParams);
case Als: return GeneralMediaFormat::Als;
case Sa0c: return GeneralMediaFormat::Sa0c;
case Aac: return GeneralMediaFormat::Aac;
case Mpeg2VideoSimpleProfile: return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg2SimpleProfile);
case Mpeg2VideoMainProfile: return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg2SnrProfile);
case Mpeg2VideoSnrProfile: return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg2SpatialProfile);
case Mpeg2VideoSpatialProfile: return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg2HighProfile);
case Mpeg2VideoHighProfile: return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg2HighProfile);
case Mpeg2Video422Profile: return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg2SimpleProfile);
case AacMainProfile: return MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg2MainProfile);
case AacLowComplexityProfile: return MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg2LowComplexityProfile);
case AacScaleableSamplingRateProfile: return MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg2ScalableSamplingRateProfile);
case Mpeg2Audio: return GeneralMediaFormat::Mpeg2Audio;
case Mpeg1Video: return GeneralMediaFormat::Mpeg1Video;
case Mpeg1Audio: return GeneralMediaFormat::Mpeg1Audio;
case Jpeg: return GeneralMediaFormat::Jpeg;
case Png: return GeneralMediaFormat::Png;
case Evrc: case PrivateEvrc: return GeneralMediaFormat::Evrc;
case Smv: return GeneralMediaFormat::Smv;
case Gpp2Cmf: return GeneralMediaFormat::Gpp2Cmf;
case Vc1: return GeneralMediaFormat::Vc1;
case Dirac: return GeneralMediaFormat::Dirac;
case Ac3: case PrivateAc3: return GeneralMediaFormat::Ac3;
case EAc3: return GeneralMediaFormat::EAc3;
case Dts: case PrivateDts: return GeneralMediaFormat::Dts;
case DtsHdHighResolution: return MediaFormat(GeneralMediaFormat::DtsHd, SubFormats::DtsHdHighResolution);
case DtsHdMasterAudio: return MediaFormat(GeneralMediaFormat::DtsHd, SubFormats::DtsHdMasterAudio);
case DtsHdExpress: return MediaFormat(GeneralMediaFormat::DtsHd, SubFormats::DtsExpress);
case PrivateOgg: case PrivateOgg2: return GeneralMediaFormat::Vorbis;
case PrivateQcelp: return GeneralMediaFormat::Qcelp;
default: return MediaFormat();
}
}
}
/*!
* \brief Encapsulates all known MPEG-4 descriptor IDs.
*/
namespace Mpeg4DescriptorIds {
}
/*!
* \brief Returns the name of the stream type denoted by the specified MPEG-4 stream type ID.
*/
namespace Mpeg4ElementaryStreamTypeIds {
/*!
* \brief Returns the name of the stream type denoted by the specified MPEG-4 stream type ID.
*/
const char *streamTypeName(byte streamTypeId)
{
switch(streamTypeId) {
case ObjectDescriptor: return "object descriptor";
case ClockReference: return "clock reference";
case SceneDescriptor: return "scene descriptor";
case Visual: return "visual";
case Audio: return "audio";
case Mpeg7: return "MPEG-7";
case Ipmps: return "IMPS";
case ObjectContentInfo: return "object content info";
case MpegJava: return "MPEG Java";
case Interaction: return "interaction";
case Ipmp: return "IPMP";
case FontData: return "font data";
case StreamingText: return "streaming text";
default: "";
}
}
}
/*!
* \brief Encapsulates all supported MPEG-4 audio object format IDs.
* \sa http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio
*/
namespace Mpeg4AudioObjectIds {
}
}

View File

@ -6,6 +6,8 @@
namespace Media
{
class MediaFormat;
namespace Mp4AtomIds {
enum KnownValue : uint32 {
AvcConfiguration = 0x61766343,
@ -66,8 +68,8 @@ enum KnownValue : uint32 {
TrackExtends = 0x74726578,
TrackFragmentRun = 0x7472756E,
UserData = 0x75647461,
DataEntryUrl = 0x75726C20,
DataEntryUrn = 0x75726E20,
DataEntryUrl = 0x75726C20,
DataEntryUrn = 0x75726E20,
VideoMediaHeader = 0x766D6864,
Wide = 0x77696465
};
@ -125,39 +127,228 @@ extern const char *cdec;
namespace Mp4MediaTypeIds {
enum KnownValue : uint32 {
Sound = 0x736f756e,
Video = 0x76696465,
Hint = 0x68696e74,
Meta = 0x6d657461
Sound = 0x736f756e, /**< Sound/Audio */
Video = 0x76696465, /**< Video */
Hint = 0x68696e74, /**< Hint */
Meta = 0x6d657461 /**< Meta */
};
}
namespace Mp4FormatIds {
enum KnownValue : uint32 {
Mpeg4Visual = 0x6d703476,
Avc1 = 0x61766331,
Avc2 = 0x61766332,
Avc3 = 0x61766333,
Avc4 = 0x61766334,
H263 = 0x68323633,
Tiff = 0x74696666,
Jpeg = 0x6a706567,
Raw = 0x72617720,
Gif = 0x67696620,
Mp3 = 0x2e6d7033,
Mpeg4Audio = 0x6d703461,
Alac = 0x616C6163,
Ac3 = 0x61632d33,
Ac4 = 0x61632d34,
AdpcmAcm = 0x6D730002,
ImaadpcmAcm = 0x6D730011,
Mp3CbrOnly = 0x6D730055
Cinepak = 0x63766964, /**< Cinepak */
Mpeg4Video = 0x6d703476, /**< MPEG-4 video */
Graphics = 0x736D6320, /**< Graphics */
Animation = 0x726C6520, /**< Animation */
AppleVideo = 0x72707A61, /**< Apple video */
Png = 0x706E6720, /**< Portable Network Graphics */
Avc1 = 0x61766331, /**< H.264/MPEG-4 AVC video */
Avc2 = 0x61766332, /**< H.264/MPEG-4 AVC video */
Avc3 = 0x61766333, /**< H.264/MPEG-4 AVC video */
Avc4 = 0x61766334, /**< H.264/MPEG-4 AVC video */
H263 = 0x68323633, /**< H.263/MPEG-4 ASP video */
Tiff = 0x74696666, /**< Tagged Image File Format */
Jpeg = 0x6a706567, /**< JPEG */
Raw = 0x72617720, /**< Uncompressed RGB */
Gif = 0x67696620, /**< CompuServe Graphics Interchange Format */
NtscDv25Video = 0x64766320, /**< NTSC DV-25 video */
PalDv25Video = 0x64766370, /**< PAL DV-25 video */
MotionJpegA = 0x6D6A7061, /**< Motion-JPEG (format A) */
MotionJpegB = 0x6D6A7062, /**< Motion-JPEG (format B) */
Mp3 = 0x2e6d7033, /**< MPEG-1 Layer 3 */
Mpeg4Audio = 0x6d703461, /**< MPEG-4 audio */
Mpeg4Stream = 0x6d703473, /**< MPEG-4 stream (other then video/audio) */
Alac = 0x616C6163, /**< Apple Losless Audio Codec */
Ac3 = 0x61632d33, /**< Dolby Digital */
Ac4 = 0x61632d34, /**< ? */
AdpcmAcm = 0x6D730002, /**< ? */
ImaadpcmAcm = 0x6D730011, /**< ? */
Mp3CbrOnly = 0x6D730055 /**< MPEG-1 Layer 3 (constant bitrate only) */
};
MediaFormat fourccToMediaFormat(uint32 fourccId);
}
namespace Mp4FormatExtensionIds {
enum KnownValue : uint32 {
GammaLevel = 0x67616D61, /**< A 32-bit fixed-point number indicating the gamma level at which the image was captured. The decompressor can use this value to gamma-correct at display time. */
FieldHandling = 0x6669656C, /**< Two 8-bit integers that define field handling. */
DefaultQuantizationTable = 0x6D6A7174, /**< The default quantization table for a Motion-JPEG data stream. */
DefaultHuffmanTable = 0x6D6A6874, /**< The default Huffman table for a Motion-JPEG data stream. */
Mpeg4ElementaryStreamDescriptor = 0x65736473, /**< An MPEG-4 elementary stream descriptor atom. This extension is required for MPEG-4 video. */
Mpeg4ElementaryStreamDescriptor2 = 0x6D346473, /**< Alternative if encoded to AVC stanard. */
AvcConfiguration = 0x61766343, /**< An H.264 AVCConfigurationBox. This extension is required for H.264 video as defined in ISO/IEC 14496-15. */
PixelAspectRatio = 0x70617370, /**< Pixel aspect ratio. This extension is mandatory for video formats that use non-square pixels. */
ColorParameters = 0x636F6C72, /**< An image description extension required for all uncompressed Y´CbCr video types. */
CleanAperature = 0x636C6170 /**< Spatial relationship of Y´CbCr components relative to a canonical image center. */
};
}
namespace Mp4FormatConfigurationIds {
enum KnownValue : uint32 {
AvcC = 0x61766343
namespace Mpeg4ElementaryStreamObjectIds {
enum KnownValue : byte {
SystemsIso144961 = 0x01, /**< Systems */
SystemsIso144961v2, /**< Systems (version 2) */
InteractionStream, /**< Interaction Stream */
AfxStream = 0x05, /**< AFX Stream */
FontDataStream, /**< Font Data Stream */
SynthesizedTextureStream, /**< Synthesized Texture Stream */
StreamingTextStream, /**< Streaming Text Stream */
Mpeg4Visual = 0x20, /**< MPEG-4 Visual */
Avc, /**< Advanced Video Coding */
ParameterSetsForAvc, /**< Parameter Sets for Advanced Video Coding */
Als = 0x24, /**< ALS */
Sa0c = 0x2B, /**< SAOC */
Aac = 0x40, /**< Audio ISO/IEC 14496-3 (AAC) */
Mpeg2VideoSimpleProfile = 0x60, /**< MPEG-2 Video Simple Profile */
Mpeg2VideoMainProfile, /**< MPEG-2 Video Main Profile */
Mpeg2VideoSnrProfile, /**< MPEG-2 Video SNR Profile */
Mpeg2VideoSpatialProfile, /**< MPEG-2 Video Spatial Profile */
Mpeg2VideoHighProfile, /**< MPEG-2 Video High Profile */
Mpeg2Video422Profile, /**< MPEG-2 Video 422 Profile */
AacMainProfile, /**< Advanced Audio Coding Main Profile */
AacLowComplexityProfile, /**< Advanced Audio Coding Low Complexity Profile */
AacScaleableSamplingRateProfile, /**< Advanced Audio Coding Scaleable Sampling Rate Profile */
Mpeg2Audio, /**< MPEG-2 Audio */
Mpeg1Video, /**< MPEG-1 Video */
Mpeg1Audio, /**< MPEG-1 Audio */
Jpeg, /**< JPEG */
Png, /**< PNG */
Evrc = 0xA0, /**< EVRC */
Smv, /**< SMV */
Gpp2Cmf, /**< 3GPP2 Compact Multimedia Format (CMF) */
Vc1, /**< VC-1 */
Dirac, /**< Dirac */
Ac3, /**< AC-3 */
EAc3, /**< E-AC-3 */
Dts, /**< DTS */
DtsHdHighResolution, /**< DTS-HD High Resolution */
DtsHdMasterAudio, /**< DTS-HD Master Audio */
DtsHdExpress, /**< DTS-HD Express */
PrivateEvrc = 0xD1, /**< EVRC */
PrivateAc3 = 0xD3, /**< AC-3 */
PrivateDts, /**< DTS */
PrivateOgg = 0xDD, /**< Ogg */
PrivateOgg2, /**< Ogg */
PrivateQcelp = 0xE1 /**< QCELP */
};
MediaFormat streamObjectTypeFormat(byte streamObjectTypeId);
}
namespace Mpeg4ElementaryStreamTypeIds {
enum KnownValue : byte {
ObjectDescriptor = 0x01,
ClockReference,
SceneDescriptor,
Visual,
Audio,
Mpeg7,
Ipmps,
ObjectContentInfo,
MpegJava,
Interaction,
Ipmp,
FontData,
StreamingText
};
const char *streamTypeName(byte streamTypeId);
}
namespace Mpeg4DescriptorIds {
enum KnownValue : byte {
ObjectDescr = 0x01,
InitialObjectDescr,
ElementaryStreamDescr,
DecoderConfigDescr,
DecoderSpecificInfo,
SlConfigDescr,
ContentIdentDescr,
SupplContentIdentDescr,
IpiDescPointer,
IpmpDescPointer,
IpmpDescr,
QoSDescr,
RegistrationDescr,
EsIdInc,
EsIdRef,
Mp4I0d,
Mp40d,
IplDescrPointerRef,
ExtendedProfileLevelDescr,
ProfileLevelIndicationIndexDescr,
ContentClassificationDescr = 0x40,
KeyWordDescr,
RatingDescr,
LanguageDescr,
ShortTextualDescr,
ExpandedTextualDescr,
ContentCreatorNameDescr,
ContentCreationDateDescr,
IcicCreatorDateDescr,
SmpteCameraPositionDescr,
SegmentDescr,
MediaTimeDescr,
IpmpToolsListDescr = 0x60,
IpmpToolTag,
FlexMuxTimingDescr,
FlexMuxCodeTableDescr,
ExtSlConfigDescr,
FlexMuxIdentDescr,
DependencyPointer,
DependencyMaker,
FlexMuxChannelDescr,
UserPrivate = 0xC0
};
}
namespace Mpeg4AudioObjectIds {
enum KnownValue : byte {
Null = 0,
AacMain,
AacLc, /**< low complexity */
AacSsr, /**< scalable sample rate */
AacLtp, /**< long term prediction */
Sbr, /**< spectral band replication */
AacScalable,
TwinVq,
Celp, /**< code excited linear prediction */
Hxvc, /**< harmonic vector excitation coding */
Ttsi = 12, /**< text-to-speech interface */
MainSynthesis,
WavetableSynthesis,
GeneralMidi,
AlgorithmicSynthesisAudioEffects,
ErAacLc, /**< error resillent AAC LC */
ErAacLtp = 19,
ErAacScalable,
ErTwinVq,
ErBsac,
ErAacLd,
ErCelp,
ErHvxc,
ErHiln,
ErParametric,
Ssc,
Ps,
MpegSurround,
EscapeValue,
Layer1,
Layer2,
Layer3,
Dst,
Als, /**< audio lossless */
Sls, /**< scalable lossless */
ErAacEld, /**< enhanced low delay */
SmrSimple, /**< symbolic music representation */
SmrMain,
UsacNoSbr, /**< unified speech and audio coding */
Saoc, /**< spatial audio object coding (no SBR) */
LdMpegSurround,
Usac /**< unified speech and audio coding */
};
}

View File

@ -213,22 +213,17 @@ void Mp4Tag::parse(Mp4Atom &metaAtom)
addNotification(NotificationType::Critical, "Unable to parse child atoms of meta atom (stores hdlr and ilst atoms).", context);
}
if(subAtom) {
Mp4Atom *child = subAtom->firstChild();
Mp4TagField tagField;
while(child) {
for(Mp4Atom *child : *subAtom) {
try {
child->parse();
tagField.invalidateNotifications();
tagField.reparse(*child);
fields().insert(pair<fieldType::identifierType, fieldType>(child->id(), tagField));
addNotifications(context, *child);
addNotifications(context, tagField);
child = child->nextSibling();
} catch(Failure &) {
addNotifications(context, *child);
addNotifications(context, tagField);
break;
}
addNotifications(context, *child);
addNotifications(context, tagField);
}
} else {
addNotification(NotificationType::Warning, "No ilst atom found (stores attached meta information).", context);

View File

@ -2,6 +2,7 @@
#include "mp4container.h"
#include "mp4track.h"
#include "mp4ids.h"
#include "mpeg4descriptor.h"
#include "../exceptions.h"
#include "../mediaformat.h"
@ -21,6 +22,19 @@ namespace Media {
DateTime startDate = DateTime::fromDate(1904, 1, 1);
uint32 sampleRateTable[] = {
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000
};
MediaFormat fmtTable[] = {
GeneralMediaFormat::Unknown,
MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg4MainProfile),
MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg4LowComplexityProfile),
MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg4ScalableSamplingRateProfile),
MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg4LongTermPredictionProfile),
MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg4SpectralBandReplicationProfile)
};
/*!
* \class Media::Mp4Track
* \brief Implementation of Media::AbstractTrack for the MP4 container.
@ -46,6 +60,7 @@ Mp4Track::Mp4Track(Mp4Atom &trakAtom) :
m_stcoAtom(nullptr),
m_stszAtom(nullptr),
m_codecConfigAtom(nullptr),
m_esDescAtom(nullptr),
m_framesPerSample(1),
m_chunkOffsetSize(4),
m_chunkCount(0),
@ -394,37 +409,28 @@ vector<uint64> Mp4Track::readChunkSizes()
/*!
* \brief Reads the AVC configuration for the track.
* \remarks Ensure that the format is MediaFormat::Mpeg4Avc before calling.
* \remarks
* - Returns an empty configuration for non-AVC tracks.
* - Notifications might be added.
*/
AvcConfiguration Mp4Track::readAvcConfiguration()
AvcConfiguration Mp4Track::parseAvcConfiguration()
{
AvcConfiguration config;
try {
auto configSize = m_codecConfigAtom->dataSize();
if(m_codecConfigAtom && configSize >= 5) {
// skip first byte (is always 1)
m_istream->seekg(m_codecConfigAtom->dataOffset() + 1);
// read profile, IDC level, NALU size length
config.profileIdc = m_reader.readByte();
config.profileCompat = m_reader.readByte();
config.levelIdc = m_reader.readByte();
config.naluSizeLength = m_reader.readByte() & 0x03;
// read SPS infos
if((configSize -= 5) >= 3) {
byte entryCount = m_reader.readByte() & 0x0f;
uint16 entrySize;
while(entryCount && configSize) {
if((entrySize = m_reader.readUInt16BE()) <= configSize) {
// TODO: read entry
configSize -= entrySize;
} else {
throw TruncatedDataException();
}
--entryCount;
}
// read PPS infos
if(m_codecConfigAtom) {
try {
auto configSize = m_codecConfigAtom->dataSize();
if(m_codecConfigAtom && configSize >= 5) {
// skip first byte (is always 1)
m_istream->seekg(m_codecConfigAtom->dataOffset() + 1);
// read profile, IDC level, NALU size length
config.profileIdc = m_reader.readByte();
config.profileCompat = m_reader.readByte();
config.levelIdc = m_reader.readByte();
config.naluSizeLength = m_reader.readByte() & 0x03;
// read SPS infos
if((configSize -= 5) >= 3) {
entryCount = m_reader.readByte();
byte entryCount = m_reader.readByte() & 0x0f;
uint16 entrySize;
while(entryCount && configSize) {
if((entrySize = m_reader.readUInt16BE()) <= configSize) {
// TODO: read entry
@ -434,19 +440,107 @@ AvcConfiguration Mp4Track::readAvcConfiguration()
}
--entryCount;
}
// TODO: read trailer
return config;
// read PPS infos
if((configSize -= 5) >= 3) {
entryCount = m_reader.readByte();
while(entryCount && configSize) {
if((entrySize = m_reader.readUInt16BE()) <= configSize) {
// TODO: read entry
configSize -= entrySize;
} else {
throw TruncatedDataException();
}
--entryCount;
}
// TODO: read trailer
return config;
}
}
}
throw TruncatedDataException();
} catch (TruncatedDataException &) {
addNotification(NotificationType::Critical, "AVC configuration is truncated.", "parsing AVC configuration");
}
throw TruncatedDataException();
} catch (TruncatedDataException &) {
addNotification(NotificationType::Critical, "AVC configuration is truncated.", "parsing AVC configuration");
}
return config;
}
/*!
* \brief Reads the MPEG-4 elementary stream descriptor for the track.
* \remarks
* - Notifications might be added.
* \sa mpeg4ElementaryStreamInfo()
*/
void Mp4Track::parseMpeg4ElementaryStreamInfo()
{
static const string context("parsing MPEG-4 elementary stream descriptor");
if(m_esDescAtom) {
if(m_esDescAtom->dataSize() >= 12) {
m_istream->seekg(m_esDescAtom->dataOffset());
// read version/flags
if(m_reader.readUInt32BE() != 0) {
addNotification(NotificationType::Warning, "Unknown version/flags.", context);
}
// read extended descriptor
Mpeg4Descriptor esDesc(m_esDescAtom->container(), m_istream->tellg(), m_esDescAtom->dataSize() - 4);
try {
esDesc.parse();
// check ID
if(esDesc.id() != Mpeg4DescriptorIds::ElementaryStreamDescr) {
addNotification(NotificationType::Critical, "Invalid descriptor found.", context);
throw Failure();
}
// read stream info
m_istream->seekg(esDesc.dataOffset());
m_esInfo = make_unique<Mpeg4ElementaryStreamInfo>();
m_esInfo->id = m_reader.readUInt16BE();
m_esInfo->esDescFlags = m_reader.readByte();
if(m_esInfo->dependencyFlag()) {
m_esInfo->dependsOnId = m_reader.readUInt16BE();
}
if(m_esInfo->urlFlag()) {
m_esInfo->url = m_reader.readString(m_reader.readByte());
}
if(m_esInfo->ocrFlag()) {
m_esInfo->ocrId = m_reader.readUInt16BE();
}
for(Mpeg4Descriptor *esDescChild = esDesc.denoteFirstChild(static_cast<uint64>(m_istream->tellg()) - esDesc.startOffset()); esDescChild; esDescChild = esDescChild->nextSibling()) {
esDescChild->parse();
switch(esDescChild->id()) {
case Mpeg4DescriptorIds::DecoderConfigDescr:
// read decoder config descriptor
m_istream->seekg(esDescChild->dataOffset());
m_esInfo->objectTypeId = m_reader.readByte();
m_esInfo->decCfgDescFlags = m_reader.readByte();
m_esInfo->bufferSize = m_reader.readUInt24BE();
m_esInfo->maxBitrate = m_reader.readUInt32BE();
m_esInfo->averageBitrate = m_reader.readUInt32BE();
for(Mpeg4Descriptor *decCfgDescChild = esDescChild->denoteFirstChild(13); decCfgDescChild; decCfgDescChild = decCfgDescChild->nextSibling()) {
decCfgDescChild->parse();
switch(esDescChild->id()) {
case Mpeg4DescriptorIds::DecoderSpecificInfo:
// read decoder specific info
break;
}
}
break;
case Mpeg4DescriptorIds::SlConfigDescr:
// uninteresting
break;
}
}
} catch (Failure &) {
// notifications will be added in any case
}
addNotifications(esDesc);
} else {
addNotification(NotificationType::Warning, "Elementary stream descriptor atom (esds) is truncated.", context);
}
}
}
/*!
* \brief Updates the chunk offsets of the track. This is necessary when the mdat atom (which contains
* the actual chunk data) is moved.
* \param oldMdatOffsets Specifies a vector holding the old offsets of the "mdat"-atoms.
@ -648,16 +742,16 @@ void Mp4Track::makeMedia()
writer().writeUInt32BE(Mp4AtomIds::HandlerReference);
writer().writeUInt64BE(0); // version, flags, pre defined
switch(m_mediaType) {
case MediaType::Visual:
case MediaType::Video:
outputStream().write("vide", 4);
break;
case MediaType::Acoustic:
case MediaType::Audio:
outputStream().write("soun", 4);
break;
case MediaType::Hint:
outputStream().write("hint", 4);
break;
case MediaType::Textual:
case MediaType::Text:
outputStream().write("meta", 4);
break;
default:
@ -730,6 +824,7 @@ void Mp4Track::makeMediaInfo()
/*!
* \brief Makes the sample table (stbl atom) for the track. The data is written to the assigned output stream
* at the current position.
* \remarks Not fully implemented yet.
*/
void Mp4Track::makeSampleTable()
{
@ -917,13 +1012,13 @@ void Mp4Track::internalParseHeader()
m_istream->seekg(m_hdlrAtom->startOffset() + 16); // seek to beg, skip size, name, version, flags and reserved bytes
string trackTypeStr = reader.readString(4);
if(trackTypeStr == "soun") {
m_mediaType = MediaType::Acoustic;
m_mediaType = MediaType::Audio;
} else if(trackTypeStr == "vide") {
m_mediaType = MediaType::Visual;
m_mediaType = MediaType::Video;
} else if(trackTypeStr == "hint") {
m_mediaType = MediaType::Hint;
} else if(trackTypeStr == "meta") {
m_mediaType = MediaType::Textual;
m_mediaType = MediaType::Text;
} else {
m_mediaType = MediaType::Unknown;
}
@ -941,69 +1036,37 @@ void Mp4Track::internalParseHeader()
if((codecConfigContainerAtom = m_stsdAtom->firstChild())) {
try {
codecConfigContainerAtom->parse();
switch(codecConfigContainerAtom->id()) {
case Mp4FormatIds::Mpeg4Visual:
m_format = MediaFormat::Mpeg4;
break;
case Mp4FormatIds::Avc1:
case Mp4FormatIds::Avc2:
case Mp4FormatIds::Avc3:
case Mp4FormatIds::Avc4:
m_format = MediaFormat::Mpeg4Avc;
m_codecConfigAtom = codecConfigContainerAtom->childById(Mp4AtomIds::AvcConfiguration);
break;
case Mp4FormatIds::H263:
m_format = MediaFormat::Mpeg4Asp;
break;
case Mp4FormatIds::Tiff:
m_format = MediaFormat::Tiff;
break;
case Mp4FormatIds::Raw:
m_format = MediaFormat::UncompressedRgb;
break;
case Mp4FormatIds::Jpeg:
m_format = MediaFormat::Jpeg;
break;
case Mp4FormatIds::Gif:
m_format = MediaFormat::Gif;
break;
case Mp4FormatIds::AdpcmAcm:
m_format = MediaFormat::AdpcmAcm;
break;
case Mp4FormatIds::ImaadpcmAcm:
m_format = MediaFormat::ImaadpcmAcm;
break;
case Mp4FormatIds::Mp3CbrOnly:
m_format = MediaFormat::MpegL3;
break;
case Mp4FormatIds::Mpeg4Audio:
m_format = MediaFormat::Aac;
break;
case Mp4FormatIds::Alac:
m_format = MediaFormat::Alac;
break;
case Mp4FormatIds::Ac3:
m_format = MediaFormat::Ac3;
break;
case Mp4FormatIds::Ac4:
m_format = MediaFormat::Ac4;
break;
default:
// format id is unknown
m_format = MediaFormat::Unknown;
m_formatId = interpretIntegerAsString<uint32>(codecConfigContainerAtom->id());
// parse FOURCC
m_formatId = interpretIntegerAsString<uint32>(codecConfigContainerAtom->id());
m_format = Mp4FormatIds::fourccToMediaFormat(codecConfigContainerAtom->id());
// parse AVC configuration
m_codecConfigAtom = codecConfigContainerAtom->childById(Mp4AtomIds::AvcConfiguration);
// parse MPEG-4 elementary stream descriptor
m_esDescAtom = codecConfigContainerAtom->childById(Mp4FormatExtensionIds::Mpeg4ElementaryStreamDescriptor);
if(!m_esDescAtom) {
m_esDescAtom = codecConfigContainerAtom->childById(Mp4FormatExtensionIds::Mpeg4ElementaryStreamDescriptor2);
}
try {
parseMpeg4ElementaryStreamInfo();
if(m_esInfo) {
auto mediaFormat = Mpeg4ElementaryStreamObjectIds::streamObjectTypeFormat(m_esInfo->objectTypeId);
if(mediaFormat) {
m_format = mediaFormat;
}
}
} catch(Failure &) {
}
// seek to start offset of additional atom and skip reserved bytes and data reference index
m_istream->seekg(codecConfigContainerAtom->startOffset() + 8 + 6 + 2);
switch(m_mediaType) {
case MediaType::Acoustic:
case MediaType::Audio:
m_istream->seekg(8, ios_base::cur); // skip reserved bytes
m_channelCount = reader.readUInt16BE();
m_bitsPerSample = reader.readUInt16BE();
m_istream->seekg(4, ios_base::cur); // skip reserved bytes
m_samplesPerSecond = reader.readUInt32BE() >> 16;
break;
case MediaType::Visual:
case MediaType::Video:
m_istream->seekg(16, ios_base::cur); // skip reserved bytes
m_pixelSize.setWidth(reader.readUInt16BE());
m_pixelSize.setHeight(reader.readUInt16BE());

View File

@ -6,12 +6,79 @@
#include "../abstracttrack.h"
#include <vector>
#include <memory>
namespace Media
{
class Mp4Atom;
class LIB_EXPORT Mpeg4ElementaryStreamInfo
{
public:
Mpeg4ElementaryStreamInfo();
uint16 id;
byte esDescFlags;
uint16 dependsOnId;
std::string url;
uint16 ocrId;
byte objectTypeId;
byte decCfgDescFlags;
uint32 bufferSize;
uint32 maxBitrate;
uint32 averageBitrate;
bool dependencyFlag() const;
bool urlFlag() const;
bool ocrFlag() const;
byte priority() const;
byte streamTypeId() const;
bool upstream() const;
};
inline Mpeg4ElementaryStreamInfo::Mpeg4ElementaryStreamInfo() :
id(0),
esDescFlags(0),
dependsOnId(0),
ocrId(0),
objectTypeId(0),
decCfgDescFlags(0),
bufferSize(0),
maxBitrate(0),
averageBitrate(0)
{}
inline bool Mpeg4ElementaryStreamInfo::dependencyFlag() const
{
return esDescFlags & 0x80;
}
inline bool Mpeg4ElementaryStreamInfo::urlFlag() const
{
return esDescFlags & 0x40;
}
inline bool Mpeg4ElementaryStreamInfo::ocrFlag() const
{
return esDescFlags & 0x20;
}
inline byte Mpeg4ElementaryStreamInfo::priority() const
{
return esDescFlags & 0x1F;
}
inline byte Mpeg4ElementaryStreamInfo::streamTypeId() const
{
return decCfgDescFlags >> 2;
}
inline bool Mpeg4ElementaryStreamInfo::upstream() const
{
return decCfgDescFlags & 0x02;
}
class LIB_EXPORT Mp4Track : public AbstractTrack
{
public:
@ -25,10 +92,13 @@ public:
unsigned int chunkOffsetSize() const;
uint32 chunkCount() const;
uint32 sampleToChunkEntryCount() const;
const Mpeg4ElementaryStreamInfo *mpeg4ElementaryStreamInfo() const;
std::vector<uint64> readChunkOffsets();
std::vector<std::tuple<uint32, uint32, uint32> > readSampleToChunkTable();
std::vector<uint64> readChunkSizes();
AvcConfiguration readAvcConfiguration();
AvcConfiguration parseAvcConfiguration();
bool hasMpeg4ElementaryStreamDesc() const;
void parseMpeg4ElementaryStreamInfo();
void updateChunkOffsets(const std::vector<int64> &oldMdatOffsets, const std::vector<int64> &newMdatOffsets);
void updateChunkOffset(uint32 chunkIndex, uint64 offset);
void makeTrack();
@ -56,11 +126,13 @@ private:
Mp4Atom *m_stcoAtom;
Mp4Atom *m_stszAtom;
Mp4Atom *m_codecConfigAtom;
Mp4Atom *m_esDescAtom;
uint16 m_framesPerSample;
std::vector<uint32> m_sampleSizes;
unsigned int m_chunkOffsetSize;
uint32 m_chunkCount;
uint32 m_sampleToChunkEntryCount;
std::unique_ptr<Mpeg4ElementaryStreamInfo> m_esInfo;
};
/*!
@ -109,6 +181,31 @@ inline uint32 Mp4Track::sampleToChunkEntryCount() const
return m_sampleToChunkEntryCount;
}
/*!
* \brief Returns information about the MPEG-4 elementary stream.
* \remarks
* - The Mp4Track::readMpeg4ElementaryStreamInfo() method must be called before
* to parse the information. This is done when parsing the track.
* - The information is only available, if the track has an MPEG-4 elementary stream
* descriptor atom.
* - The track keeps ownership over the returned object.
* \sa
* - readMpeg4ElementaryStreamInfo()
* - hasMpeg4ElementaryStreamDesc()
*/
inline const Mpeg4ElementaryStreamInfo *Mp4Track::mpeg4ElementaryStreamInfo() const
{
return m_esInfo.get();
}
/*!
* \brief Returns whether the track has an MPEG-4 elementary stream descriptor atom.
*/
inline bool Mp4Track::hasMpeg4ElementaryStreamDesc() const
{
return m_esDescAtom != nullptr;
}
}
#endif // MP4TRACK_H

View File

@ -1,7 +1,88 @@
#include "mpeg4streamdescriptor.h"
#include "mpeg4descriptor.h"
#include "mp4container.h"
Mpeg4StreamDescriptor::Mpeg4StreamDescriptor()
#include "mp4ids.h"
#include <c++utilities/io/binaryreader.h>
#include <c++utilities/conversion/stringconversion.h>
using namespace std;
using namespace ConversionUtilities;
namespace Media {
/*!
* \class Media::Mpeg4Descriptor
* \brief The Mpeg4Descriptor class helps to parse MPEG-4 descriptors.
*/
/*!
* \brief Constructs a new top level descriptor with the specified \a container at the specified \a startOffset
* and with the specified \a maxSize.
*/
Mpeg4Descriptor::Mpeg4Descriptor(containerType &container, uint64 startOffset, uint64 maxSize) :
GenericFileElement<Mpeg4Descriptor>(container, startOffset, maxSize)
{}
/*!
* \brief Constructs a new sub level descriptor with the specified \a parent at the specified \a startOffset.
*/
Mpeg4Descriptor::Mpeg4Descriptor(implementationType &parent, uint64 startOffset) :
GenericFileElement<Mpeg4Descriptor>(parent, startOffset)
{}
/*!
* \brief Returns the parsing context.
*/
string Mpeg4Descriptor::parsingContext() const
{
return "parsing " + idToString() + " descriptor at " + numberToString(startOffset());
}
/*!
* \brief Converts the specified atom \a ID to a printable string.
*/
std::string Mpeg4Descriptor::idToString() const
{
return "0x" + ConversionUtilities::numberToString(id(), 16);
}
/*!
* \brief Parses the MPEG-4 descriptor.
* \remarks Does not detect the first child.
*/
void Mpeg4Descriptor::internalParse()
{
invalidateStatus();
if(maxTotalSize() < 4) {
addNotification(NotificationType::Critical, "Descriptor is smaller then 4 byte and hence invalid. The maximum size within the encloding element is " + numberToString(maxTotalSize()) + ".", "parsing MPEG-4 descriptor");
throw TruncatedDataException();
}
stream().seekg(startOffset());
// read ID
m_idLength = m_sizeLength = 1;
m_id = reader().readByte();
// read data size
byte tmp = reader().readByte() & 0x80;
m_dataSize = tmp & 0x7F;
while(tmp & 0x80) {
m_dataSize = (m_dataSize << 7) | ((tmp = reader().readByte()) & 0x7F);
++m_sizeLength;
}
// check whether the denoted data size exceeds the available data size
if(maxTotalSize() < totalSize()) {
addNotification(NotificationType::Warning, "The descriptor seems to be truncated; unable to parse siblings of that ", parsingContext());
m_dataSize = maxTotalSize(); // using max size instead
}
m_firstChild.reset();
implementationType *sibling = nullptr;
if(totalSize() < maxTotalSize()) {
if(parent()) {
sibling = new implementationType(*(parent()), startOffset() + totalSize());
} else {
sibling = new implementationType(container(), startOffset() + totalSize(), maxTotalSize() - totalSize());
}
}
}
}

View File

@ -1,11 +1,103 @@
#ifndef MPEG4STREAMDESCRIPTOR_H
#define MPEG4STREAMDESCRIPTOR_H
#ifndef MPEG4DESCRIPTOR_H
#define MPEG4DESCRIPTOR_H
#include "../genericfileelement.h"
class Mpeg4StreamDescriptor
#include <c++utilities/misc/memory.h>
namespace Media {
class Mp4Container;
class Mpeg4Descriptor;
/*!
* \brief Defines traits for the GenericFileElement implementation Mpeg4Descriptor.
*/
template <>
class LIB_EXPORT FileElementTraits<Mpeg4Descriptor>
{
public:
Mpeg4StreamDescriptor();
/*!
* \brief The container type used to store such elements is Mp4Container.
*/
typedef Mp4Container containerType;
/*!
* \brief The type used to store atom IDs is an unsigned 32-bit integer.
*/
typedef byte identifierType;
/*!
* \brief The type used to store element sizes is an unsigned 32-bit integer.
*/
typedef uint32 dataSizeType;
/*!
* \brief The implementation type is Mp4Atom.
*/
typedef Mpeg4Descriptor implementationType;
};
#endif // MPEG4STREAMDESCRIPTOR_H
class LIB_EXPORT Mpeg4Descriptor : public GenericFileElement<Mpeg4Descriptor>
{
friend class GenericFileElement<Mpeg4Descriptor>;
public:
Mpeg4Descriptor(containerType& container, uint64 startOffset, uint64 maxSize);
std::string idToString() const;
bool isParent() const;
bool isPadding() const;
uint64 firstChildOffset() const;
implementationType *denoteFirstChild(uint32 offset);
protected:
Mpeg4Descriptor(implementationType &parent, uint64 startOffset);
void internalParse();
private:
std::string parsingContext() const;
};
/*!
* \brief Returns an indication whether the descriptor contains sub descriptors.
*
* \remarks Returns true if a first child has been denoted (via denoteFirstChild()).
*/
inline bool Mpeg4Descriptor::isParent() const
{
return m_firstChild != nullptr;
}
/*!
* \brief Returns always false for MPEG-4 descriptors.
*/
inline bool Mpeg4Descriptor::isPadding() const
{
return false;
}
/*!
* \brief Returns the offset of the first child (relative to the start offset of this descriptor).
*
* \remarks The first child must be denoted (via denoteFirstChild()).
*/
inline uint64 Mpeg4Descriptor::firstChildOffset() const
{
return firstChild() ? firstChild()->startOffset() - startOffset() : 0;
}
/*!
* \brief Denotes the first child to start at the specified \a offset (relative to the start offset of this descriptor).
* \remarks A new first child is constructed. A possibly existing subtree is invalidated.
*/
inline Mpeg4Descriptor::implementationType *Mpeg4Descriptor::denoteFirstChild(uint32 relativeFirstChildOffset)
{
m_firstChild.reset(new implementationType(static_cast<implementationType &>(*this), startOffset() + relativeFirstChildOffset));
return m_firstChild.get();
}
}
#endif // MPEG4DESCRIPTOR_H

View File

@ -24,7 +24,7 @@ namespace Media {
MpegAudioFrameStream::MpegAudioFrameStream(iostream &stream, uint64 startOffset) :
AbstractTrack(stream, startOffset)
{
m_mediaType = MediaType::Acoustic;
m_mediaType = MediaType::Audio;
}
/*!
@ -56,19 +56,7 @@ void MpegAudioFrameStream::internalParseHeader()
MpegAudioFrame frame;
frame.parseHeader(*m_istream);
m_version = frame.mpegVersion();
switch(frame.layer()) {
case 1:
m_format = MediaFormat::MpegL1;
break;
case 2:
m_format = MediaFormat::MpegL2;
break;
case 3:
m_format = MediaFormat::MpegL3;
break;
default:
m_format = MediaFormat::Unknown;
}
m_format = MediaFormat(GeneralMediaFormat::Mpeg1Audio, frame.layer());
m_channelCount = frame.channelMode() == MpegChannelMode::SingleChannel ? 1 : 2;
m_samplesPerSecond = frame.samperate();
if(frame.isXingBytesfieldPresent()) {

View File

@ -61,12 +61,12 @@ void OggStream::internalParseHeader()
if((sig & 0x00ffffffffffff00u) == 0x00766F7262697300u) {
// Vorbis header detected
// set Vorbis as format
switch(m_format) {
case MediaFormat::Unknown:
m_format = MediaFormat::Vorbis;
m_mediaType = MediaType::Acoustic;
switch(m_format.general) {
case GeneralMediaFormat::Unknown:
m_format = GeneralMediaFormat::Vorbis;
m_mediaType = MediaType::Audio;
break;
case MediaFormat::Vorbis:
case GeneralMediaFormat::Vorbis:
break;
default:
addNotification(NotificationType::Warning, "Stream format is inconsistent.", context);

View File

@ -226,14 +226,14 @@ const char *containerFormatAbbreviation(ContainerFormat containerFormat, MediaTy
case ContainerFormat::Lzw: return "lzw";
case ContainerFormat::Mp4:
switch(mediaType) {
case MediaType::Acoustic:
case MediaType::Audio:
return "m4a";
default:
return "mp4";
}
case ContainerFormat::Ogg: return "ogg";
switch(mediaType) {
case MediaType::Visual:
case MediaType::Video:
return "ogv";
default:
return "ogg";
@ -244,7 +244,7 @@ const char *containerFormatAbbreviation(ContainerFormat containerFormat, MediaTy
case ContainerFormat::Rar: return "rar";
case ContainerFormat::Matroska:
switch(mediaType) {
case MediaType::Acoustic:
case MediaType::Audio:
return "mka";
default:
return "mkv";
@ -393,21 +393,21 @@ const char *containerMimeType(ContainerFormat containerFormat, MediaType mediaTy
return "audio/mpeg";
case ContainerFormat::Mp4:
switch(mediaType) {
case MediaType::Acoustic:
case MediaType::Audio:
return "audio/mp4";
default:
return "video/mp4";
}
case ContainerFormat::Ogg:
switch(mediaType) {
case MediaType::Acoustic:
case MediaType::Audio:
return "audio/ogg";
default:
return "video/ogg";
}
case ContainerFormat::Matroska:
switch(mediaType) {
case MediaType::Acoustic:
case MediaType::Audio:
return "audio/x-matroska";
default:
return "video/x-matroska";

2
size.h
View File

@ -94,7 +94,7 @@ inline constexpr bool Size::isNull() const
inline std::string Size::toString() const
{
std::stringstream res;
res << "width: " << m_width << "; height: " << m_height;
res << "width: " << m_width << ", height: " << m_height;
return std::string(res.str());
}

View File

@ -67,7 +67,8 @@ SOURCES += \
abstractattachment.cpp \
matroska/matroskaattachment.cpp \
mediaformat.cpp \
avc/avcconfiguration.cpp
avc/avcconfiguration.cpp \
mp4/mpeg4descriptor.cpp
HEADERS += \
abstractcontainer.h \
@ -132,7 +133,8 @@ HEADERS += \
matroska/matroskaattachment.h \
mediaformat.h \
avc/avcconfiguration.h \
generictagfield.h
generictagfield.h \
mp4/mpeg4descriptor.h
LIBS += -lz

View File

@ -19,7 +19,7 @@ namespace Media {
WaveAudioStream::WaveAudioStream(iostream &stream, uint64 startOffset) :
AbstractTrack(stream, startOffset)
{
m_mediaType = MediaType::Acoustic;
m_mediaType = MediaType::Audio;
}
/*!
@ -46,16 +46,16 @@ void WaveAudioStream::internalParseHeader()
if(restHeaderLen >= 16u) {
switch(m_reader.readUInt16LE()) {
case 0x0001u:
m_format = MediaFormat::Pcm;
m_format = GeneralMediaFormat::Pcm;
break;
case 0x0050u:
m_format = MediaFormat::MpegL2;
m_format = MediaFormat(GeneralMediaFormat::Mpeg1Audio, SubFormats::Mpeg1Layer2);
break;
case 0x0055u:
m_format = MediaFormat::MpegL3;
m_format = MediaFormat(GeneralMediaFormat::Mpeg1Audio, SubFormats::Mpeg1Layer3);
break;
default:
m_format = MediaFormat::Unknown;
m_format = GeneralMediaFormat::Unknown;
}
m_channelCount = m_reader.readUInt16LE();
m_samplesPerSecond = m_reader.readUInt32LE();
@ -64,7 +64,7 @@ void WaveAudioStream::internalParseHeader()
m_bitsPerSample = m_reader.readUInt16LE();
m_bitrate = m_bitsPerSample * m_samplesPerSecond * m_channelCount;
} else {
m_format = MediaFormat::Unknown;
m_format = GeneralMediaFormat::Unknown;
}
if(restHeaderLen > 16u) {
m_istream->seekg(m_dataOffset, ios_base::beg);