improved detection of media formats in Matroska and MP4 files

This commit is contained in:
Martchus 2015-07-07 03:01:48 +02:00
parent 045f2bb14b
commit 8618172f81
8 changed files with 618 additions and 278 deletions

View File

@ -61,14 +61,14 @@ MediaFormat MatroskaTrack::codecIdToMediaFormat(const string &codecId)
fmt.general = GeneralMediaFormat::Mpeg4Video;
if(part2 == "ISO") {
if(part3 == "SP") {
fmt.sub = SubFormats::Mpeg4Sp;
fmt.sub = SubFormats::Mpeg4SimpleProfile1;
} else if(part3 == "ASP") {
fmt.sub = SubFormats::Mpeg4Asp;
fmt.sub = SubFormats::Mpeg4AdvancedSimpleProfile1;
} else if(part3 == "AVC") {
fmt.sub = SubFormats::Mpeg4Avc;
fmt.general = GeneralMediaFormat::Avc;
}
} else if(part2 == "MS" && part3 == "V3") {
fmt.sub = SubFormats::Mpeg4MsV3;
fmt.sub = SubFormats::Mpeg4SimpleProfile1;
}
} else if(part1 == "V_MPEG1") {
fmt.general = GeneralMediaFormat::Mpeg1Video;

View File

@ -41,6 +41,20 @@ const char *MediaFormat::name() const
case GeneralMediaFormat::AfxStream: return "AFX Stream";
case GeneralMediaFormat::Alac: return "Apple Lossless Audio Codec";
case GeneralMediaFormat::Als: return "ALS";
case GeneralMediaFormat::Amr: return "Adaptive Multi-Rate audio codec";
case GeneralMediaFormat::Avc:
switch(sub) {
case AvcBaselineProfile: return "Advanced Video Coding Basline Profile";
case AvcMainProfile: return "Advanced Video Coding Main Profile";
case AvcScalableBaselineProfile: return "Advanced Video Coding Scalable Basline Profile";
case AvcScalableHighProfile: return "Advanced Video Coding Scalable High Profile";
case AvcExtendedProfile: return "Advanced Video Coding Extended Profile";
case AvcHighProfile: return "Advanced Video Coding High Profile";
case AvcHigh10Profile: return "Advanced Video Coding High 10 Profile";
case AvcHigh422Profile: return "Advanced Video Coding High 4:2:2 Profile";
case AvcHigh444Profile: return "Advanced Video Coding High 4:4:4 Profile";
default: return "Advanced Video Coding";
}
case GeneralMediaFormat::Bitmap: return "Windows Bitmap";
case GeneralMediaFormat::Dirac: return "Dirac";
case GeneralMediaFormat::Dts: return "DTS";
@ -62,6 +76,7 @@ const char *MediaFormat::name() const
case GeneralMediaFormat::FontDataStream: return "Font Data Stream";
case GeneralMediaFormat::Gif: return "GIF";
case GeneralMediaFormat::Gpp2Cmf: return "3GPP2 Compact Multimedia Format (CMF)";
case GeneralMediaFormat::Hevc: return "High Efficiency Video Coding";
case GeneralMediaFormat::ImaadpcmAcm: return "IMAADPCM ACM";
case GeneralMediaFormat::ImageSubtitle:
switch(sub) {
@ -73,6 +88,7 @@ const char *MediaFormat::name() const
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::DolbyMlp: return "Dolby TrueHD";
case GeneralMediaFormat::Mpeg1Audio:
switch(sub) {
case Mpeg1Layer1: return "MPEG-1 Layer 1";
@ -100,11 +116,65 @@ const char *MediaFormat::name() const
}
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";
case Mpeg4SimpleProfile1: return "MPEG-4 Simple Profile L1";
case Mpeg4SimpleProfile2: return "MPEG-4 Simple Profile L2";
case Mpeg4SimpleProfile3: return "MPEG-4 Simple Profile L2";
case Mpeg4SimpleProfile0: return "MPEG-4 Simple Profile";
case Mpeg4SimpleScalableProfile0: return "MPEG-4 Simple Scalable Profile";
case Mpeg4SimpleScalableProfile1: return "MPEG-4 Simple Scalable Profile L1";
case Mpeg4SimpleScalableProfile2: return "MPEG-4 Simple Scalable Profile L2";
case Mpeg4CoreProfile1: return "MPEG-4 Core Profile L1";
case Mpeg4CoreProfiel2: return "MPEG-4 Core Profile L2";
case Mpeg4MainProfile2: return "MPEG-4 Main Profile L2";
case Mpeg4MainProfile3: return "MPEG-4 Main Profile L3";
case Mpeg4MainProfile4: return "MPEG-4 Main Profile L4";
case Mpeg4NBitPrifle2: return "MPEG-4 N-Bit Profile L2";
case Mpeg4ScalableTextureProfile1: return "MPEG-4 Scalable Texture Profile L1";
case Mpeg4SimpleFaceAnimationProfile1: return "MPEG-4 Simple Face Animation Profile L1";
case Mpeg4SimpleFaceAnimationProfile2: return "MPEG-4 Simple Face Animation Profile L2";
case Mpeg4SimpleFbaProfile1: return "MPEG-4 Simple FBA Profile L1";
case Mpeg4SimpleFbaProfile2: return "MPEG-4 Simple FBA Profile L2";
case Mpeg4BasicAnimatedTextureProfiel1: return "MPEG-4 Basic Animated Texture Profile L1";
case Mpeg4BasicAnimatedTextureProfiel2: return "MPEG-4 Basic Animated Texture Profile L2";
case Mpeg4AvcProfile: return "MPEG-4 Advanced Audio Coding Profile";
case Mpeg4HybridProfile1: return "MPEG-4 Hybrid Profile L1";
case Mpeg4HybridProfile2: return "MPEG-4 Hybrid Profile L2";
case Mpeg4AdvancedRealTimeSimpleProfile1: return "MPEG-4 Basic Animated Texture Profile L1";
case Mpeg4AdvancedRealTimeSimpleProfile2: return "MPEG-4 Basic Animated Texture Profile L2";
case Mpeg4AdvancedRealTimeSimpleProfile3: return "MPEG-4 Basic Animated Texture Profile L3";
case Mpeg4AdvancedRealTimeSimpleProfile4: return "MPEG-4 Basic Animated Texture Profile L4";
case Mpeg4CoreScalableProfile1: return "MPEG-4 Core Scalable Profile L1";
case Mpeg4CoreScalableProfile2: return "MPEG-4 Core Scalable Profile L2";
case Mpeg4CoreScalableProfile3: return "MPEG-4 Core Scalable Profile L3";
case Mpeg4AdvancedCodingEfficiencyProfile1: return "MPEG-4 Advanced Coding Efficiency Profile L1";
case Mpeg4AdvancedCodingEfficiencyProfile2: return "MPEG-4 Advanced Coding Efficiency Profile L2";
case Mpeg4AdvancedCodingEfficiencyProfile3: return "MPEG-4 Advanced Coding Efficiency Profile L3";
case Mpeg4AdvancedCodingEfficiencyProfile4: return "MPEG-4 Advanced Coding Efficiency Profile L4";
case Mpeg4AdvancedCoreProfile1: return "MPEG-4 Advanced Core Profile L1";
case Mpeg4AdvancedCoreProfile2: return "MPEG-4 Advanced Core Profile L2";
case Mpeg4AdvancedScalableTexture1: return "MPEG-4 Advanced Scalable Texture L1";
case Mpeg4AdvancedScalableTexture2: return "MPEG-4 Advanced Scalable Texture L2";
case Mpeg4SimpleStudioProfile1: return "MPEG-4 Simple Studio Profile L1";
case Mpeg4SimpleStudioProfile2: return "MPEG-4 Simple Studio Profile L2";
case Mpeg4SimpleStudioProfile3: return "MPEG-4 Simple Studio Profile L3";
case Mpeg4SimpleStudioProfile4: return "MPEG-4 Simple Studio Profile L4";
case Mpeg4CoreStudioProfile1: return "MPEG-4 Core Studio Profile L1";
case Mpeg4CoreStudioProfile2: return "MPEG-4 Core Studio Profile L2";
case Mpeg4CoreStudioProfile3: return "MPEG-4 Core Studio Profile L3";
case Mpeg4CoreStudioProfile4: return "MPEG-4 Core Studio Profile L4";
case Mpeg4AdvancedSimpleProfile0: return "MPEG-4 Advanced Simple Profile";
case Mpeg4AdvancedSimpleProfile1: return "MPEG-4 Advanced Simple Profile L1";
case Mpeg4AdvancedSimpleProfile2: return "MPEG-4 Advanced Simple Profile L2";
case Mpeg4AdvancedSimpleProfile3: return "MPEG-4 Advanced Simple Profile L3";
case Mpeg4AdvancedSimpleProfile4: return "MPEG-4 Advanced Simple Profile L4";
case Mpeg4AdvancedSimpleProfile5: return "MPEG-4 Advanced Simple Profile L5";
case Mpeg4AdvancedSimpleProfile3b: return "MPEG-4 Advanced Simple Profile L3b";
case Mpeg4FineGranularityScalableProfile0: return "MPEG-4 Fine Granularity Scalable Profile";
case Mpeg4FineGranularityScalableProfile1: return "MPEG-4 Fine Granularity Scalable Profile L1";
case Mpeg4FineGranularityScalableProfile2: return "MPEG-4 Fine Granularity Scalable Profile L2";
case Mpeg4FineGranularityScalableProfile3: return "MPEG-4 Fine Granularity Scalable Profile L3";
case Mpeg4FineGranularityScalableProfile4: return "MPEG-4 Fine Granularity Scalable Profile L4";
case Mpeg4FineGranularityScalableProfile5: return "MPEG-4 Fine Granularity Scalable Profile L5";
default: return "MPEG-4 Visual";
}
case GeneralMediaFormat::Mpc: return "Musepack SV8";
@ -146,6 +216,7 @@ const char *MediaFormat::name() const
case GeneralMediaFormat::Vorbis: return "Vorbis";
case GeneralMediaFormat::Vp8: return "VP8";
case GeneralMediaFormat::WavPack: return "WavPack";
case GeneralMediaFormat::WindowsMediaAudio: return "Windows Media Audio";
default: return "unknown";
}
}
@ -182,6 +253,8 @@ const char *MediaFormat::abbreviation() const
case GeneralMediaFormat::AfxStream: return "AFX";
case GeneralMediaFormat::Alac: return "ALAC";
case GeneralMediaFormat::Als: return "ALS";
case GeneralMediaFormat::Amr: return "AMR";
case GeneralMediaFormat::Avc: return "H.264";
case GeneralMediaFormat::Bitmap: return "BMP";
case GeneralMediaFormat::Dirac: return "Dirac";
case GeneralMediaFormat::Dts: return "DTS";
@ -203,6 +276,7 @@ const char *MediaFormat::abbreviation() const
case GeneralMediaFormat::FontDataStream: return "FDS";
case GeneralMediaFormat::Gif: return "GIF";
case GeneralMediaFormat::Gpp2Cmf: return "3GPP2 CMF";
case GeneralMediaFormat::Hevc: return "H.265";
case GeneralMediaFormat::ImaadpcmAcm: return "IMAADPCM ACM";
case GeneralMediaFormat::ImageSubtitle:
switch(sub) {
@ -214,6 +288,7 @@ const char *MediaFormat::abbreviation() const
case GeneralMediaFormat::OggKate: return "OggKate";
case GeneralMediaFormat::MicrosoftAudioCodecManager: return "MS ACM";
case GeneralMediaFormat::MicrosoftVideoCodecManager: return "MS VCM";
case GeneralMediaFormat::DolbyMlp: return "Dolby TrueHD";
case GeneralMediaFormat::Mpeg1Audio:
switch(sub) {
case Mpeg1Layer1: return "MP1";
@ -241,11 +316,21 @@ const char *MediaFormat::abbreviation() const
}
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";
case Mpeg4SimpleProfile1:
case Mpeg4SimpleProfile2:
case Mpeg4SimpleProfile3:
case Mpeg4SimpleProfile0:
return "MPEG-4 SP";
case Mpeg4AdvancedSimpleProfile0:
case Mpeg4AdvancedSimpleProfile1:
case Mpeg4AdvancedSimpleProfile2:
case Mpeg4AdvancedSimpleProfile3:
case Mpeg4AdvancedSimpleProfile4:
case Mpeg4AdvancedSimpleProfile5:
case Mpeg4AdvancedSimpleProfile3b:
return "H.263";
case Mpeg4AvcProfile:
return "H.264";
default: return "MPEG-4 Visual";
}
case GeneralMediaFormat::Mpc: return "MPC";
@ -291,6 +376,7 @@ const char *MediaFormat::abbreviation() const
case GeneralMediaFormat::Vorbis: return "Vorbis";
case GeneralMediaFormat::Vp8: return "VP8";
case GeneralMediaFormat::WavPack: return "WavPack";
case GeneralMediaFormat::WindowsMediaAudio: return "WMA";
default: return "";
}
}

View File

@ -32,6 +32,8 @@ enum class GeneralMediaFormat
AfxStream, /**< AFX Stream */
Alac, /**< Apple Lossless Audio Codec */
Als, /**< ALS */
Amr, /** < AMR */
Avc, /** < Advanced Video Coding */
Bitmap, /**< Windows Bitmap */
Dirac, /**< Dirac */
Dts, /**< DTS */
@ -42,6 +44,7 @@ enum class GeneralMediaFormat
FontDataStream, /**< Font Data Stream */
Gif, /**< GIF */
Gpp2Cmf, /**< 3GPP2 Compact Multimedia Format (CMF) */
Hevc, /**< H.265/High Efficiency Video Coding */
ImaadpcmAcm, /**< IMAADPCM ACM */
ImageSubtitle, /**< Image subtitle */
InteractionStream, /**< Interaction Stream */
@ -49,6 +52,7 @@ enum class GeneralMediaFormat
OggKate, /**< Karaoke And Text Encapsulation */
MicrosoftAudioCodecManager, /**< Microsoft Audio Codec Manager (ACM) */
MicrosoftVideoCodecManager, /**< Microsoft Video Codec Manager (VCM) */
DolbyMlp, /** < Dolby MLP */
Mpeg1Audio, /**< MPEG-1 Audio */
Mpeg1Video, /**< MPEG-1 Vudio */
Mpeg2Audio, /**< MPEG-2 Audio */
@ -79,7 +83,8 @@ enum class GeneralMediaFormat
VobSub, /**< VobSub */
Vorbis, /**< Vorbis */
Vp8, /** < VP8 */
WavPack /**< WavPack */
WavPack, /**< WavPack */
WindowsMediaAudio /**< Windows Media Audio */
};
/*!
@ -126,11 +131,77 @@ enum Mpeg2VideoProfile : unsigned char {
};
enum Mpeg4VideoProfile : unsigned char {
Mpeg4Sp = 1,
Mpeg4Asp,
Mpeg4Avc,
Mpeg4AvcParams,
Mpeg4MsV3
Mpeg4SimpleProfile1 = 0x01,
Mpeg4SimpleProfile2 = 0x02,
Mpeg4SimpleProfile3 = 0x03,
Mpeg4SimpleProfile0 = 0x08,
Mpeg4SimpleScalableProfile0 = 0x10,
Mpeg4SimpleScalableProfile1 = 0x11,
Mpeg4SimpleScalableProfile2 = 0x12,
Mpeg4CoreProfile1 = 0x21,
Mpeg4CoreProfiel2 = 0x22,
Mpeg4MainProfile2 = 0x32,
Mpeg4MainProfile3 = 0x33,
Mpeg4MainProfile4 = 0x34,
Mpeg4NBitPrifle2 = 0x42,
Mpeg4ScalableTextureProfile1 = 0x51,
Mpeg4SimpleFaceAnimationProfile1 = 0x61,
Mpeg4SimpleFaceAnimationProfile2 = 0x62,
Mpeg4SimpleFbaProfile1 = 0x63,
Mpeg4SimpleFbaProfile2 = 0x64,
Mpeg4BasicAnimatedTextureProfiel1 = 0x71,
Mpeg4BasicAnimatedTextureProfiel2 = 0x72,
Mpeg4AvcProfile = 0x7F,
Mpeg4HybridProfile1 = 0x81,
Mpeg4HybridProfile2 = 0x82,
Mpeg4AdvancedRealTimeSimpleProfile1 = 0x91,
Mpeg4AdvancedRealTimeSimpleProfile2 = 0x92,
Mpeg4AdvancedRealTimeSimpleProfile3 = 0x93,
Mpeg4AdvancedRealTimeSimpleProfile4 = 0x94,
Mpeg4CoreScalableProfile1 = 0xA1,
Mpeg4CoreScalableProfile2 = 0xA2,
Mpeg4CoreScalableProfile3 = 0xA3,
Mpeg4AdvancedCodingEfficiencyProfile1 = 0xB1,
Mpeg4AdvancedCodingEfficiencyProfile2 = 0xB2,
Mpeg4AdvancedCodingEfficiencyProfile3 = 0xB3,
Mpeg4AdvancedCodingEfficiencyProfile4 = 0xB4,
Mpeg4AdvancedCoreProfile1 = 0xC1,
Mpeg4AdvancedCoreProfile2 = 0xC2,
Mpeg4AdvancedScalableTexture1 = 0xD1,
Mpeg4AdvancedScalableTexture2 = 0xD2,
Mpeg4SimpleStudioProfile1 = 0xE1,
Mpeg4SimpleStudioProfile2 = 0xE2,
Mpeg4SimpleStudioProfile3 = 0xE3,
Mpeg4SimpleStudioProfile4 = 0xE4,
Mpeg4CoreStudioProfile1 = 0xE5,
Mpeg4CoreStudioProfile2 = 0xE6,
Mpeg4CoreStudioProfile3 = 0xE7,
Mpeg4CoreStudioProfile4 = 0xE8,
Mpeg4AdvancedSimpleProfile0 = 0xF0,
Mpeg4AdvancedSimpleProfile1 = 0xF1,
Mpeg4AdvancedSimpleProfile2 = 0xF2,
Mpeg4AdvancedSimpleProfile3 = 0xF3,
Mpeg4AdvancedSimpleProfile4 = 0xF4,
Mpeg4AdvancedSimpleProfile5 = 0xF5,
Mpeg4AdvancedSimpleProfile3b = 0xF7,
Mpeg4FineGranularityScalableProfile0 = 0xF8,
Mpeg4FineGranularityScalableProfile1 = 0xF9,
Mpeg4FineGranularityScalableProfile2 = 0xFA,
Mpeg4FineGranularityScalableProfile3 = 0xFB,
Mpeg4FineGranularityScalableProfile4 = 0xFC,
Mpeg4FineGranularityScalableProfile5 = 0xFD
};
enum AvcProfile : unsigned char {
AvcBaselineProfile = 0x42,
AvcMainProfile = 0x4D,
AvcScalableBaselineProfile = 0x53,
AvcScalableHighProfile = 0x56,
AvcExtendedProfile = 0x58,
AvcHighProfile = 0x64,
AvcHigh10Profile = 0x6E,
AvcHigh422Profile = 0x7A,
AvcHigh444Profile = 0x90
};
enum DtsSpecifier : unsigned char {
@ -221,3 +292,4 @@ inline MediaFormat::operator bool() const
}
#endif // MEDIAFORMAT_H

View File

@ -151,7 +151,11 @@ bool Mp4Atom::isParent() const
switch(id()) {
case Movie: case Track: case Media: case MediaInformation: case DataInformation:
case SampleTable: case UserData: case Meta: case ItunesList: case MovieFragment:
case TrackFragment: case MovieExtends: case DataReference:
case TrackFragment: case MovieExtends: case DataReference: case Mp4AtomIds::AvcConfiguration:
case FourccIds::Mpeg4Audio: case FourccIds::AmrNarrowband: case FourccIds::Amr:
case FourccIds::Drms: case FourccIds::Alac: case FourccIds::WindowsMediaAudio:
case FourccIds::Ac3: case FourccIds::EAc3: case FourccIds::DolbyMpl:
case FourccIds::Dts: case FourccIds::DtsH: case FourccIds::DtsE:
return true;
default:
if(parent()) {
@ -188,7 +192,8 @@ bool Mp4Atom::isPadding() const
*
* \remarks This information is not read from the atom header. The offsets are known
* for specific atoms.
* \remarks This method returns zero for non-parent atoms which have no childs. *
* \remarks This method returns zero for non-parent atoms which have no childs.
* \remarks Childs with variable offset such as the "esds"-atom must be denoted!
*/
uint64 Mp4Atom::firstChildOffset() const
{
@ -196,15 +201,13 @@ uint64 Mp4Atom::firstChildOffset() const
using namespace FourccIds;
if(isParent()) {
switch(id()) {
case Meta: return headerSize() + 0x4;
case DataReference: return headerSize() + 0x8;
case Meta: return headerSize() + 0x4u;
case DataReference: return headerSize() + 0x8u;
default: return headerSize();
}
} else {
switch(id()) {
case SampleDescription: return headerSize() + 0x08u;
case Avc1: return headerSize() + 0x4eu;
case Mpeg4Audio: return headerSize() + 0x1cu;
default: return 0x00u;
}
}

View File

@ -51,16 +51,21 @@ MediaFormat fourccToMediaFormat(uint32 fourccId)
return GeneralMediaFormat::Mpeg2Video;
case Mpeg4Video:
return GeneralMediaFormat::Mpeg4Video;
case Hevc1: case Hevc2:
return MediaFormat(GeneralMediaFormat::Hevc);
case Avc1: case Avc2: case Avc3: case Avc4: case H264Decoder1: case H264Decoder2:
case H264Decoder3: case H264Decoder4: case H264Decoder5: case H264Decoder6:
return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg4Avc);
case H263: case XvidDecoder1: case XvidDecoder2:
case XvidDecoder3: case XvidDecoder4: case XvidDecoder5:
case Divx5Decoder:
return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg4Asp);
return MediaFormat(GeneralMediaFormat::Avc);
case Divx4Decoder1: case Divx4Decoder2: case Divx4Decoder3:
case Divx4Decoder4: case Divx4Decoder5: case Divx4Decoder6: case Divx4Decoder7:
return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg4Sp);
case H263Quicktime: case H2633GPP: case XvidDecoder1: case XvidDecoder2:
case XvidDecoder3: case XvidDecoder4: case XvidDecoder5: case Divx5Decoder:
return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg4AdvancedSimpleProfile0);
case Divx3Decoder1: case Divx3Decoder2: case Divx3Decoder3: case Divx3Decoder4: case Divx3Decoder5:
case Divx3Decoder6: case Divx3Decoder7: case Divx3Decoder8: case Divx3Decoder9: case Divx3Decoder10:
case Divx3Decoder11: case Divx3Decoder12: case Divx3Decoder13: case Divx3Decoder14: case Divx3Decoder15:
case Divx3Decoder16: case Divx3Decoder17:
return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg4SimpleProfile0);
case Tiff:
return GeneralMediaFormat::Tiff;
case AppleTextAtsuiCodec:
@ -85,6 +90,10 @@ MediaFormat fourccToMediaFormat(uint32 fourccId)
return GeneralMediaFormat::Alac;
case Ac3:
return GeneralMediaFormat::Ac3;
case EAc3:
return GeneralMediaFormat::EAc3;
case DolbyMpl:
return GeneralMediaFormat::DolbyMlp;
case Ac4:
return GeneralMediaFormat::Ac4;
case Rv20: case Rv30: case Rv40:
@ -97,6 +106,15 @@ MediaFormat fourccToMediaFormat(uint32 fourccId)
return MediaFormat(GeneralMediaFormat::Pcm, SubFormats::PcmIntLe);
case FloatingPoint32Bit: case FloatingPoint64Bit:
return MediaFormat(GeneralMediaFormat::Pcm, SubFormats::PcmFloatIeee);
case Amr: case AmrNarrowband:
return MediaFormat(GeneralMediaFormat::Amr);
case Dts: case DtsH:
return MediaFormat(GeneralMediaFormat::Dts);
case DtsE:
return MediaFormat(GeneralMediaFormat::Dts, SubFormats::DtsExpress);
case WindowsMediaAudio: case WindowsMediaAudio7:
case WindowsMediaAudio9Professional: case WindowsMediaAudio9Standard:
return MediaFormat(GeneralMediaFormat::WindowsMediaAudio);
// TODO: map more FOURCCs
default:
return GeneralMediaFormat::Unknown;
@ -131,8 +149,8 @@ MediaFormat streamObjectTypeFormat(byte streamObjectTypeId)
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 Avc: return GeneralMediaFormat::Avc;
case ParameterSetsForAvc: return GeneralMediaFormat::Avc;
case Als: return GeneralMediaFormat::Als;
case Sa0c: return GeneralMediaFormat::Sa0c;
case Aac: return MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg4LowComplexityProfile);
@ -262,4 +280,16 @@ LIB_EXPORT MediaFormat idToMediaFormat(byte mpeg4AudioObjectId, bool sbrPresent,
}
/*!
* \brief Encapsulates MPEG-4 video (14496-2) codes.
*/
namespace Mpeg4VideoCodes {
}
/*!
* \brief Encapsulates MPEG-2 video codes.
*/
namespace Mpeg2VideoCodes {
}
}

View File

@ -146,6 +146,7 @@ enum KnownValue : uint32 {
Alaw21 = 0x616C6177,
AlphaCompositor = 0x626C6E64,
AlphaGain = 0x6761696E,
Amr = 0x73617762,
AmrNarrowband = 0x73616D72,
Animation = 0x726C6520, /**< Animation */
Appl1 = 0x6476690,
@ -252,11 +253,16 @@ enum KnownValue : uint32 {
Divx4Decoder6 = 0x6D347332,
Divx4Decoder7 = 0x6D703473,
Divx5Decoder = 0x44583530,
Drm = 0x64726D73,
Drms = 0x64726D73,
Drmi = 0x64726D69,
Dts = 0x6474736C,
DtsH = 0x64747368,
DtsE = 0x64747365,
Dvca = 0x64766361,
DvcPro501 = 0x64763570,
DvcPro502 = 0x6476356E,
DvcProPal = 0x64767070,
EAc3 = 0x65632D33,
EdgeDetection = 0x65646765,
Emboss = 0x656D6273,
Explode = 0x78706C6F,
@ -271,7 +277,8 @@ enum KnownValue : uint32 {
Glass = 0x676C6173,
GradientWipe = 0x6D617474,
Graphics = 0x736D6320, /**< Graphics */
H263 = 0x68323633, /**< H.263/MPEG-4 ASP video */
H263Quicktime = 0x68323633, /**< H.263/MPEG-4 ASP video (Quicktime) */
H2633GPP = 0x73323633, /**< H.263 (3GPP format) */
H264Decoder1 = 0x44415643,
H264Decoder2 = 0x48323634,
H264Decoder3 = 0x56535348,
@ -279,6 +286,8 @@ enum KnownValue : uint32 {
H264Decoder5 = 0x68323634,
H264Decoder6 = 0x78323634,
Hdv3 = 0x68647633,
Hevc1 = 0x68766331, /**< H.265/High Efficiency Video Coding */
Hevc2 = 0x68657631, /**< H.265/High Efficiency Video Coding */
HslBalance = 0x68736C62,
Ima4 = 0x696D6134,
Ima41 = 0x696D6134,
@ -303,6 +312,7 @@ enum KnownValue : uint32 {
Mace31 = 0x4D414333,
Mace61 = 0x4D414336,
MatrixWipe = 0x736D7034,
DolbyMpl = 0x6D6C7061,
MotionJpegA = 0x6D6A7061, /**< Motion-JPEG (format A) */
MotionJpegB = 0x6D6A7062, /**< Motion-JPEG (format B) */
Mp3 = 0x2e6d7033, /**< MPEG-1 Layer 3 */
@ -366,6 +376,7 @@ enum KnownValue : uint32 {
Ulaw21 = 0x756C6177,
VcmImageCodec = 0x4D6A7067,
Vdva = 0x76647661,
WindowsMediaAudio = 0x6F776D61, /**< ? */
WindowsMediaAudio7 = 0x574D4131,
WindowsMediaAudio9Professional = 0x574D4133,
WindowsMediaAudio9Standard = 0x574D4132,
@ -579,6 +590,39 @@ LIB_EXPORT MediaFormat idToMediaFormat(byte mpeg4AudioObjectId, bool sbrPresent
}
namespace Mpeg4VideoCodes {
enum KnownValue : byte {
VideoObjectStart = 0x00,
VideoObjectLayerStart = 0x20,
VisualObjectSequenceStart = 0xB0,
VisualObjectSequendeEnd = 0xB1,
UserDataStart = 0xB2,
GroupOfVopStart = 0xB3,
VideoSessionError = 0xB4,
VisualObjectStart = 0xB5,
VopStart = 0xB6,
FbaObjectStart = 0xBA,
FbaObjectPlaneStart = 0xBB,
MeshObjectStart = 0xBC,
MeshObjectPlaneStart = 0xBD,
StillTextureObjectStart = 0xBE,
TextureSpatialLayerStart = 0xBF,
TextureSnrLayerStart = 0xC0,
TextureTitleStart = 0xC1,
TextureShapeLayerStart = 0xC2,
StuffingStart = 0xC3
};
}
namespace Mpeg2VideoCodes {
enum KnownValue : byte {
Pic = 0x00,
Seq = 0xB3,
Ext = 0xB5,
Gop = 0xB8
};
}
/*!
* \brief Specifies the tag type.
*/

View File

@ -61,6 +61,9 @@ Mpeg4AudioSpecificConfig::Mpeg4AudioSpecificConfig() :
epConfig(0)
{}
Mpeg4VideoSpecificConfig::Mpeg4VideoSpecificConfig()
{}
/*!
* \class Media::Mp4Track
* \brief Implementation of Media::AbstractTrack for the MP4 container.
@ -160,140 +163,140 @@ vector<uint64> Mp4Track::readChunkOffsets()
}
}
// read sample offsets of fragments
// Mp4Atom *moofAtom = m_trakAtom->container().firstElement()->siblingById(moof, true);
// uint64 totalDuration = 0;
// while(moofAtom) {
// moofAtom->parse();
// Mp4Atom *trafAtom = moofAtom->childById(traf);
// while(trafAtom) {
// trafAtom->parse();
// Mp4Atom *tfhdAtom = trafAtom->childById(tfhd);
// while(tfhdAtom) {
// tfhdAtom->parse();
// uint32 calculatedDataSize = 0;
// if(tfhdAtom->dataSize() < calculatedDataSize) {
// addNotification(NotificationType::Critical, "tfhd atom is truncated.", context);
// } else {
// m_stream->seekg(tfhdAtom->dataOffset() + 1);
// uint32 flags = reader.readUInt24();
// if(m_id == reader.readUInt32()) { // check track ID
// if(flags & 0x000001) { // base-data-offset present
// calculatedDataSize += 8;
// }
// if(flags & 0x000002) { // sample-description-index present
// calculatedDataSize += 4;
// }
// if(flags & 0x000008) { // default-sample-duration present
// calculatedDataSize += 4;
// }
// if(flags & 0x000010) { // default-sample-size present
// calculatedDataSize += 4;
// }
// if(flags & 0x000020) { // default-sample-flags present
// calculatedDataSize += 4;
// }
// //uint64 baseDataOffset = moofAtom->startOffset();
// //uint32 defaultSampleDescriptionIndex = 0;
// uint32 defaultSampleDuration = 0;
// uint32 defaultSampleSize = 0;
// uint32 defaultSampleFlags = 0;
// if(tfhdAtom->dataSize() < calculatedDataSize) {
// addNotification(NotificationType::Critical, "tfhd atom is truncated (presence of fields denoted).", context);
// } else {
// if(flags & 0x000001) { // base-data-offset present
// //baseDataOffset = reader.readUInt64();
// m_stream->seekg(8, ios_base::cur);
// }
// if(flags & 0x000002) { // sample-description-index present
// //defaultSampleDescriptionIndex = reader.readUInt32();
// m_stream->seekg(4, ios_base::cur);
// }
// if(flags & 0x000008) { // default-sample-duration present
// defaultSampleDuration = reader.readUInt32();
// //m_stream->seekg(4, ios_base::cur);
// }
// if(flags & 0x000010) { // default-sample-size present
// defaultSampleSize = reader.readUInt32();
// }
// if(flags & 0x000020) { // default-sample-flags present
// defaultSampleFlags = reader.readUInt32();
// //m_stream->seekg(4, ios_base::cur);
// }
// }
// Mp4Atom *trunAtom = trafAtom->childById(trun);
// while(trunAtom) {
// uint32 calculatedDataSize = 8;
// if(trunAtom->dataSize() < calculatedDataSize) {
// addNotification(NotificationType::Critical, "trun atom is truncated.", context);
// } else {
// m_stream->seekg(trunAtom->dataOffset() + 1);
// uint32 flags = reader.readUInt24();
// uint32 sampleCount = reader.readUInt32();
// m_sampleCount += sampleCount;
// if(flags & 0x000001) { // data offset present
// calculatedDataSize += 4;
// }
// if(flags & 0x000004) { // first-sample-flags present
// calculatedDataSize += 4;
// }
// uint32 entrySize = 0;
// if(flags & 0x000100) { // sample-duration present
// entrySize += 4;
// }
// if(flags & 0x000200) { // sample-size present
// entrySize += 4;
// }
// if(flags & 0x000400) { // sample-flags present
// entrySize += 4;
// }
// if(flags & 0x000800) { // sample-composition-time-offsets present
// entrySize += 4;
// }
// calculatedDataSize += entrySize * sampleCount;
// if(trunAtom->dataSize() < calculatedDataSize) {
// addNotification(NotificationType::Critical, "trun atom is truncated (presence of fields denoted).", context);
// } else {
// if(flags & 0x000001) { // data offset present
// m_stream->seekg(4, ios_base::cur);
// //int32 dataOffset = reader.readInt32();
// }
// if(flags & 0x000004) { // first-sample-flags present
// m_stream->seekg(4, ios_base::cur);
// }
// for(uint32 i = 0; i < sampleCount; ++i) {
// if(flags & 0x000100) { // sample-duration present
// totalDuration += reader.readUInt32();
// } else {
// totalDuration += defaultSampleDuration;
// }
// if(flags & 0x000200) { // sample-size present
// m_sampleSizes.push_back(reader.readUInt32());
// m_size += m_sampleSizes.back();
// } else {
// m_size += defaultSampleSize;
// }
// if(flags & 0x000400) { // sample-flags present
// m_stream->seekg(4, ios_base::cur);
// }
// if(flags & 0x000800) { // sample-composition-time-offsets present
// m_stream->seekg(4, ios_base::cur);
// }
// }
// }
// }
// trunAtom = trunAtom->siblingById(trun, false);
// }
// if(m_sampleSizes.empty() && defaultSampleSize) {
// m_sampleSizes.push_back(defaultSampleSize);
// }
// }
// }
// tfhdAtom = tfhdAtom->siblingById(tfhd, false);
// }
// trafAtom = trafAtom->siblingById(traf, false);
// }
// moofAtom = moofAtom->siblingById(moof, false);
// }
// Mp4Atom *moofAtom = m_trakAtom->container().firstElement()->siblingById(moof, true);
// uint64 totalDuration = 0;
// while(moofAtom) {
// moofAtom->parse();
// Mp4Atom *trafAtom = moofAtom->childById(traf);
// while(trafAtom) {
// trafAtom->parse();
// Mp4Atom *tfhdAtom = trafAtom->childById(tfhd);
// while(tfhdAtom) {
// tfhdAtom->parse();
// uint32 calculatedDataSize = 0;
// if(tfhdAtom->dataSize() < calculatedDataSize) {
// addNotification(NotificationType::Critical, "tfhd atom is truncated.", context);
// } else {
// m_stream->seekg(tfhdAtom->dataOffset() + 1);
// uint32 flags = reader.readUInt24();
// if(m_id == reader.readUInt32()) { // check track ID
// if(flags & 0x000001) { // base-data-offset present
// calculatedDataSize += 8;
// }
// if(flags & 0x000002) { // sample-description-index present
// calculatedDataSize += 4;
// }
// if(flags & 0x000008) { // default-sample-duration present
// calculatedDataSize += 4;
// }
// if(flags & 0x000010) { // default-sample-size present
// calculatedDataSize += 4;
// }
// if(flags & 0x000020) { // default-sample-flags present
// calculatedDataSize += 4;
// }
// //uint64 baseDataOffset = moofAtom->startOffset();
// //uint32 defaultSampleDescriptionIndex = 0;
// uint32 defaultSampleDuration = 0;
// uint32 defaultSampleSize = 0;
// uint32 defaultSampleFlags = 0;
// if(tfhdAtom->dataSize() < calculatedDataSize) {
// addNotification(NotificationType::Critical, "tfhd atom is truncated (presence of fields denoted).", context);
// } else {
// if(flags & 0x000001) { // base-data-offset present
// //baseDataOffset = reader.readUInt64();
// m_stream->seekg(8, ios_base::cur);
// }
// if(flags & 0x000002) { // sample-description-index present
// //defaultSampleDescriptionIndex = reader.readUInt32();
// m_stream->seekg(4, ios_base::cur);
// }
// if(flags & 0x000008) { // default-sample-duration present
// defaultSampleDuration = reader.readUInt32();
// //m_stream->seekg(4, ios_base::cur);
// }
// if(flags & 0x000010) { // default-sample-size present
// defaultSampleSize = reader.readUInt32();
// }
// if(flags & 0x000020) { // default-sample-flags present
// defaultSampleFlags = reader.readUInt32();
// //m_stream->seekg(4, ios_base::cur);
// }
// }
// Mp4Atom *trunAtom = trafAtom->childById(trun);
// while(trunAtom) {
// uint32 calculatedDataSize = 8;
// if(trunAtom->dataSize() < calculatedDataSize) {
// addNotification(NotificationType::Critical, "trun atom is truncated.", context);
// } else {
// m_stream->seekg(trunAtom->dataOffset() + 1);
// uint32 flags = reader.readUInt24();
// uint32 sampleCount = reader.readUInt32();
// m_sampleCount += sampleCount;
// if(flags & 0x000001) { // data offset present
// calculatedDataSize += 4;
// }
// if(flags & 0x000004) { // first-sample-flags present
// calculatedDataSize += 4;
// }
// uint32 entrySize = 0;
// if(flags & 0x000100) { // sample-duration present
// entrySize += 4;
// }
// if(flags & 0x000200) { // sample-size present
// entrySize += 4;
// }
// if(flags & 0x000400) { // sample-flags present
// entrySize += 4;
// }
// if(flags & 0x000800) { // sample-composition-time-offsets present
// entrySize += 4;
// }
// calculatedDataSize += entrySize * sampleCount;
// if(trunAtom->dataSize() < calculatedDataSize) {
// addNotification(NotificationType::Critical, "trun atom is truncated (presence of fields denoted).", context);
// } else {
// if(flags & 0x000001) { // data offset present
// m_stream->seekg(4, ios_base::cur);
// //int32 dataOffset = reader.readInt32();
// }
// if(flags & 0x000004) { // first-sample-flags present
// m_stream->seekg(4, ios_base::cur);
// }
// for(uint32 i = 0; i < sampleCount; ++i) {
// if(flags & 0x000100) { // sample-duration present
// totalDuration += reader.readUInt32();
// } else {
// totalDuration += defaultSampleDuration;
// }
// if(flags & 0x000200) { // sample-size present
// m_sampleSizes.push_back(reader.readUInt32());
// m_size += m_sampleSizes.back();
// } else {
// m_size += defaultSampleSize;
// }
// if(flags & 0x000400) { // sample-flags present
// m_stream->seekg(4, ios_base::cur);
// }
// if(flags & 0x000800) { // sample-composition-time-offsets present
// m_stream->seekg(4, ios_base::cur);
// }
// }
// }
// }
// trunAtom = trunAtom->siblingById(trun, false);
// }
// if(m_sampleSizes.empty() && defaultSampleSize) {
// m_sampleSizes.push_back(defaultSampleSize);
// }
// }
// }
// tfhdAtom = tfhdAtom->siblingById(tfhd, false);
// }
// trafAtom = trafAtom->siblingById(traf, false);
// }
// moofAtom = moofAtom->siblingById(moof, false);
// }
return offsets;
}
@ -511,7 +514,7 @@ std::unique_ptr<Mpeg4ElementaryStreamInfo> Mp4Track::parseMpeg4ElementaryStreamI
// read extended descriptor
Mpeg4Descriptor esDesc(esDescAtom->container(), m_istream->tellg(), esDescAtom->dataSize() - 4);
try {
esDesc.parse();
esDesc.parse();
// check ID
if(esDesc.id() != Mpeg4DescriptorIds::ElementaryStreamDescr) {
addNotification(NotificationType::Critical, "Invalid descriptor found.", context);
@ -551,8 +554,12 @@ std::unique_ptr<Mpeg4ElementaryStreamInfo> Mp4Track::parseMpeg4ElementaryStreamI
case Aac: case Mpeg2AacMainProfile: case Mpeg2AacLowComplexityProfile:
case Mpeg2AacScaleableSamplingRateProfile: case Mpeg2Audio: case Mpeg1Audio:
esInfo->audioSpecificConfig = parseAudioSpecificConfig(decCfgDescChild);
break;
case Mpeg4Visual:
esInfo->videoSpecificConfig = parseVideoSpecificConfig(decCfgDescChild);
break;
default:
; // TODO: covering remaining object types
; // TODO: covering more object types
}
break;
}
@ -573,7 +580,7 @@ std::unique_ptr<Mpeg4ElementaryStreamInfo> Mp4Track::parseMpeg4ElementaryStreamI
}
/*!
* \brief Reads the audio specific configuration for the track.
* \brief Parses the audio specific configuration for the track.
* \remarks
* - Notifications might be added.
* \sa mpeg4ElementaryStreamInfo()
@ -584,7 +591,7 @@ unique_ptr<Mpeg4AudioSpecificConfig> Mp4Track::parseAudioSpecificConfig(Mpeg4Des
using namespace Mpeg4AudioObjectIds;
// read config into buffer and construct BitReader for bitwise reading
m_istream->seekg(decSpecInfoDesc->dataOffset());
cout << "audio cfg @" << decSpecInfoDesc->dataOffset() << endl;
//cout << "audio cfg @" << decSpecInfoDesc->dataOffset() << endl;
auto buff = make_unique<char []>(decSpecInfoDesc->dataSize());
m_istream->read(buff.get(), decSpecInfoDesc->dataSize());
BitReader bitReader(buff.get(), decSpecInfoDesc->dataSize());
@ -670,6 +677,11 @@ unique_ptr<Mpeg4AudioSpecificConfig> Mp4Track::parseAudioSpecificConfig(Mpeg4Des
case ErBsac: case ErAacLd: case ErCelp: case ErHvxc: case ErHiln:
case ErParametric: case ErAacEld:
switch(audioCfg->epConfig = bitReader.readBits<byte>(2)) {
case 2:
break;
case 3:
bitReader.skipBits(1);
break;
default:
throw NotImplementedException(); // TODO
}
@ -690,21 +702,92 @@ unique_ptr<Mpeg4AudioSpecificConfig> Mp4Track::parseAudioSpecificConfig(Mpeg4Des
}
}
}
} else if (syncExtensionType == 0x548) {
audioCfg->psPresent = bitReader.readBits<byte>(1);
}
}
} catch(ios_base::failure &) {
if(m_istream->fail()) {
throw; // IO error caused by input stream
// IO error caused by input stream
throw;
} else {
// IO error caused by bitReader
addNotification(NotificationType::Critical, "Audio specific configuration is truncated.", context);
}
// IO error caused by bitReader
addNotification(NotificationType::Critical, "Audio specific configuration is truncated.", context);
} catch(NotImplementedException &) {
addNotification(NotificationType::Information, "Not implemented for the format of audio track.", context);
}
return audioCfg;
}
/*!
/*!
* \brief Parses the video specific configuration for the track.
* \remarks
* - Notifications might be added.
* \sa mpeg4ElementaryStreamInfo()
*/
std::unique_ptr<Mpeg4VideoSpecificConfig> Mp4Track::parseVideoSpecificConfig(Mpeg4Descriptor *decSpecInfoDesc)
{
static const string context("parsing MPEG-4 video specific config from elementary stream descriptor");
using namespace Mpeg4AudioObjectIds;
auto videoCfg = make_unique<Mpeg4VideoSpecificConfig>();
// seek to start
m_istream->seekg(decSpecInfoDesc->dataOffset());
uint64 bytesRemaining = decSpecInfoDesc->dataSize();
if(bytesRemaining > 3 && (m_reader.readUInt24BE() == 1)) {
bytesRemaining -= 3;
uint32 buff1;
while(bytesRemaining) {
--bytesRemaining;
switch(m_reader.readByte()) { // read start code
case Mpeg4VideoCodes::VisualObjectSequenceStart:
if(bytesRemaining) {
videoCfg->profile = m_reader.readByte();
--bytesRemaining;
}
break;
case Mpeg4VideoCodes::VideoObjectLayerStart:
break;
case Mpeg4VideoCodes::UserDataStart:
buff1 = 0;
while(bytesRemaining >= 3) {
if((buff1 = m_reader.readUInt24BE()) != 1) {
m_istream->seekg(-2, ios_base::cur);
videoCfg->userData.push_back(buff1 >> 16);
--bytesRemaining;
} else {
bytesRemaining -= 3;
break;
}
}
if(buff1 != 1 && bytesRemaining > 0) {
videoCfg->userData += m_reader.readString(bytesRemaining);
bytesRemaining = 0;
}
break;
default:
;
}
// skip stuff we're not interested in to get the start of the
// next video object
while(bytesRemaining >= 3) {
if(m_reader.readUInt24BE() != 1) {
m_istream->seekg(-2, ios_base::cur);
--bytesRemaining;
} else {
bytesRemaining -= 3;
break;
}
}
}
} else {
addNotification(NotificationType::Critical, "\"Visual Object Sequence Header\" not found.", context);
}
return videoCfg;
}
/*!
* \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.
@ -796,8 +879,9 @@ void Mp4Track::updateChunkOffset(uint32 chunkIndex, uint64 offset)
}
/*!
* \brief Makes the track entry (trak atom) for the track. The data is written to the assigned output stream
* \brief Makes the track entry ("trak"-atom) for the track. The data is written to the assigned output stream
* at the current position.
* \remarks Currently the "trak"-atom from the source file is just copied to the output stream.
*/
void Mp4Track::makeTrack()
{
@ -1053,49 +1137,49 @@ void Mp4Track::internalParseHeader()
static const string context("parsing MP4 track");
using namespace Mp4AtomIds;
if(!m_trakAtom) {
addNotification(NotificationType::Critical, "Trak atom is null.", context);
addNotification(NotificationType::Critical, "\"trak\"-atom is null.", context);
throw InvalidDataException();
}
// get atoms
try {
if(!(m_tkhdAtom = m_trakAtom->childById(TrackHeader))) {
addNotification(NotificationType::Critical, "No tkhd atom found.", context);
addNotification(NotificationType::Critical, "No \"tkhd\"-atom found.", context);
throw InvalidDataException();
}
if(!(m_mdiaAtom = m_trakAtom->childById(Media))) {
addNotification(NotificationType::Critical, "No mdia atom found.", context);
addNotification(NotificationType::Critical, "No \"mdia\"-atom found.", context);
throw InvalidDataException();
}
if(!(m_mdhdAtom = m_mdiaAtom->childById(MediaHeader))) {
addNotification(NotificationType::Critical, "No mdhd atom found.", context);
addNotification(NotificationType::Critical, "No \"mdhd\"-atom found.", context);
throw InvalidDataException();
}
if(!(m_hdlrAtom = m_mdiaAtom->childById(HandlerReference))) {
addNotification(NotificationType::Critical, "No hdlr atom found.", context);
addNotification(NotificationType::Critical, "No \"hdlr\"-atom found.", context);
throw InvalidDataException();
}
if(!(m_minfAtom = m_mdiaAtom->childById(MediaInformation))) {
addNotification(NotificationType::Critical, "No minf atom found.", context);
addNotification(NotificationType::Critical, "No \"minf\"-atom found.", context);
throw InvalidDataException();
}
if(!(m_stblAtom = m_minfAtom->childById(SampleTable))) {
addNotification(NotificationType::Critical, "No stbl atom found.", context);
addNotification(NotificationType::Critical, "No \"stbl\"-atom found.", context);
throw InvalidDataException();
}
if(!(m_stsdAtom = m_stblAtom->childById(SampleDescription))) {
addNotification(NotificationType::Critical, "No stsd atom found.", context);
addNotification(NotificationType::Critical, "No \"stsd\"-atom found.", context);
throw InvalidDataException();
}
if(!(m_stcoAtom = m_stblAtom->childById(ChunkOffset)) && !(m_stcoAtom = m_stblAtom->childById(ChunkOffset64))) {
addNotification(NotificationType::Critical, "No stco/co64 atom found.", context);
addNotification(NotificationType::Critical, "No \"stco\"/\"co64\"-atom found.", context);
throw InvalidDataException();
}
if(!(m_stscAtom = m_stblAtom->childById(SampleToChunk))) {
addNotification(NotificationType::Critical, "No stsc atom found.", context);
addNotification(NotificationType::Critical, "No \"stsc\"-atom found.", context);
throw InvalidDataException();
}
if(!(m_stszAtom = m_stblAtom->childById(SampleSize)) && !(m_stszAtom = m_stblAtom->childById(CompactSampleSize))) {
addNotification(NotificationType::Critical, "No stsz/stz2 atom found.", context);
addNotification(NotificationType::Critical, "No \"stsz\"/\"stz2\"-atom found.", context);
throw InvalidDataException();
}
} catch(Failure &) {
@ -1122,7 +1206,7 @@ void Mp4Track::internalParseHeader()
m_id = reader.readUInt32BE();
break;
default:
addNotification(NotificationType::Critical, "Version of tkhd atom not supported. It will be ignored. Track ID, creation time and modification time might not be be determined.", context);
addNotification(NotificationType::Critical, "Version of \"tkhd\"-atom not supported. It will be ignored. Track ID, creation time and modification time might not be be determined.", context);
m_creationTime = DateTime();
m_modificationTime = DateTime();
m_id = 0;
@ -1145,7 +1229,7 @@ void Mp4Track::internalParseHeader()
m_duration = TimeSpan::fromSeconds(static_cast<double>(reader.readUInt64BE()) / static_cast<double>(m_timeScale));
break;
default:
addNotification(NotificationType::Warning, "Version of mdhd atom not supported. It will be ignored. Creation time, modification time, time scale and duration might not be determined.", context);
addNotification(NotificationType::Warning, "Version of \"mdhd\"-atom not supported. It will be ignored. Creation time, modification time, time scale and duration might not be determined.", context);
m_timeScale = 0;
m_duration = TimeSpan();
}
@ -1179,24 +1263,92 @@ void Mp4Track::internalParseHeader()
m_istream->seekg(m_stcoAtom->dataOffset() + 4);
m_chunkCount = reader.readUInt32BE();
// read stsd atom
m_istream->seekg(m_stsdAtom->startOffset() + 12); // 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();
Mp4Atom *codecConfigContainerAtom;
string::size_type firstZeroByte;
Mp4Atom *esDescParentAtom = nullptr;
uint16 tmp;
if(entryCount > 0) {
// read only first entry
if((codecConfigContainerAtom = m_stsdAtom->firstChild())) {
try {
try {
for(Mp4Atom *codecConfigContainerAtom = m_stsdAtom->firstChild(); codecConfigContainerAtom; codecConfigContainerAtom = codecConfigContainerAtom->nextSibling()) {
codecConfigContainerAtom->parse();
// parse FOURCC
m_formatId = interpretIntegerAsString<uint32>(codecConfigContainerAtom->id());
m_format = FourccIds::fourccToMediaFormat(codecConfigContainerAtom->id());
// parse AVC configuration
//codecConfigContainerAtom->childById(Mp4AtomIds::AvcConfiguration);
// parse MPEG-4 elementary stream descriptor
Mp4Atom *esDescAtom = codecConfigContainerAtom->childById(Mp4FormatExtensionIds::Mpeg4ElementaryStreamDescriptor);
// parse codecConfigContainerAtom
m_istream->seekg(codecConfigContainerAtom->dataOffset());
switch(codecConfigContainerAtom->id()) {
case FourccIds::Mpeg4Audio: case FourccIds::AmrNarrowband: case FourccIds::Amr:
case FourccIds::Drms: case FourccIds::Alac: case FourccIds::WindowsMediaAudio:
case FourccIds::Ac3: case FourccIds::EAc3: case FourccIds::DolbyMpl:
case FourccIds::Dts: case FourccIds::DtsH: case FourccIds::DtsE:
m_istream->seekg(6 + 2, ios_base::cur); // skip reserved bytes, data reference index
tmp = reader.readUInt16BE(); // read sound version
m_istream->seekg(6, ios_base::cur);
m_channelCount = reader.readUInt16BE();
m_bitsPerSample = reader.readUInt16BE();
m_istream->seekg(4, ios_base::cur); // skip reserved bytes (again)
if(!m_sampleRate) {
m_sampleRate = reader.readUInt32BE() >> 16;
if(codecConfigContainerAtom->id() != FourccIds::DolbyMpl) {
m_sampleRate >>= 16;
}
} else {
m_istream->seekg(4, ios_base::cur);
}
if(codecConfigContainerAtom->id() != FourccIds::WindowsMediaAudio) {
switch(tmp) {
case 1:
codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28 + 16);
break;
case 2:
codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28 + 32);
break;
default:
codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28);
}
if(!esDescParentAtom) {
esDescParentAtom = codecConfigContainerAtom;
}
}
break;
case FourccIds::Mpeg4Video: case FourccIds::H263Quicktime: case FourccIds::H2633GPP:
case FourccIds::Avc1: case FourccIds::Avc2: case FourccIds::Avc3: case FourccIds::Avc4:
case FourccIds::Drmi: case FourccIds::Hevc1: case FourccIds::Hevc2:
m_istream->seekg(6 + 2 + 16, ios_base::cur); // skip reserved bytes, data reference index, and reserved bytes (again)
m_pixelSize.setWidth(reader.readUInt16BE());
m_pixelSize.setHeight(reader.readUInt16BE());
m_resolution.setWidth(static_cast<uint32>(reader.readFixed16BE()));
m_resolution.setHeight(static_cast<uint32>(reader.readFixed16BE()));
m_istream->seekg(4, ios_base::cur); // skip reserved bytes
m_framesPerSample = reader.readUInt16BE();
tmp = reader.readByte();
m_compressorName = reader.readString(31);
if(tmp == 0) {
m_compressorName.clear();
} else if(tmp < 32) {
m_compressorName.resize(tmp);
}
m_depth = reader.readUInt16BE(); // 24: color without alpha
codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 78);
if(!esDescParentAtom) {
esDescParentAtom = codecConfigContainerAtom;
}
break;
case Mp4AtomIds::PixalAspectRatio:
break; // TODO
case Mp4AtomIds::CleanAperature:
break; // TODO
default:
;
}
}
// parse AVC configuration
//codecConfigContainerAtom->childById(Mp4AtomIds::AvcConfiguration);
// parse MPEG-4 elementary stream descriptor
if(esDescParentAtom) {
Mp4Atom *esDescAtom = esDescParentAtom->childById(Mp4FormatExtensionIds::Mpeg4ElementaryStreamDescriptor);
if(!esDescAtom) {
esDescAtom = codecConfigContainerAtom->childById(Mp4FormatExtensionIds::Mpeg4ElementaryStreamDescriptor2);
esDescAtom = esDescParentAtom->childById(Mp4FormatExtensionIds::Mpeg4ElementaryStreamDescriptor2);
}
if(esDescAtom) {
try {
@ -1222,6 +1374,15 @@ void Mp4Track::internalParseHeader()
addNotification(NotificationType::Warning, "Audio specific config has invalid extension sample frequency index.", context);
}
}
if(m_esInfo->videoSpecificConfig) {
// check the video specific config for useful information
if(m_format.general == GeneralMediaFormat::Mpeg4Video && m_esInfo->videoSpecificConfig->profile) {
m_format.sub = m_esInfo->videoSpecificConfig->profile;
if(!m_esInfo->videoSpecificConfig->userData.empty()) {
m_formatId += " / " + m_esInfo->videoSpecificConfig->userData;
}
}
}
// check the stream data for missing information
switch(m_format.general) {
case GeneralMediaFormat::Mpeg1Audio: case GeneralMediaFormat::Mpeg2Audio: {
@ -1238,73 +1399,14 @@ void Mp4Track::internalParseHeader()
} 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::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
if(!m_sampleRate) {
m_sampleRate = reader.readUInt32BE() >> 16;
} else {
m_istream->seekg(4, ios_base::cur);
}
break;
case MediaType::Video:
m_istream->seekg(16, ios_base::cur); // skip reserved bytes
m_pixelSize.setWidth(reader.readUInt16BE());
m_pixelSize.setHeight(reader.readUInt16BE());
m_resolution.setWidth(reader.readUInt32BE());
m_resolution.setHeight(reader.readUInt32BE());
m_istream->seekg(4, ios_base::cur); // skip reserved bytes
m_framesPerSample = reader.readUInt16BE();
m_compressorName = reader.readString(30);
firstZeroByte = m_compressorName.find('\0');
if(firstZeroByte == 0) {
m_compressorName.clear();
} else if(firstZeroByte != string::npos) {
m_compressorName.resize(firstZeroByte - 1);
}
m_depth = reader.readUInt16BE();
if(m_depth == 0x0018) {
// images are in color with no alpha
} else {
m_depth = 0;
}
codecConfigContainerAtom = codecConfigContainerAtom->nextSibling();
if(codecConfigContainerAtom) {
while(codecConfigContainerAtom) {
codecConfigContainerAtom->parse();
switch(codecConfigContainerAtom->id()) {
case Mp4AtomIds::PixalAspectRatio:
break; // todo
case Mp4AtomIds::CleanAperature:
break; // todo
default:
;
}
codecConfigContainerAtom = codecConfigContainerAtom->nextSibling();
}
codecConfigContainerAtom = codecConfigContainerAtom->siblingById(Mp4AtomIds::Drms, true);
if(codecConfigContainerAtom) {
m_encrypted = true;
}
}
break;
default:
;
}
} catch(Failure &) {
addNotification(NotificationType::Warning, "Unable to parse child atoms of stsd atom correctly.", context);
}
} catch (Failure &) {
addNotification(NotificationType::Critical, "Unable to parse child atoms of \"stsd\"-atom.", context);
}
}
// read stsz atom which holds the sample size table
m_sampleSizes.clear();
m_size = 0;
m_sampleCount = 0;
m_size = m_sampleCount = 0;
uint64 actualSampleSizeTableSize = m_stszAtom->dataSize();
if(actualSampleSizeTableSize < 12) {
addNotification(NotificationType::Critical, "The stsz atom is truncated. There are no sample sizes present. The size of the track can not be determined.", context);
@ -1374,15 +1476,12 @@ void Mp4Track::internalParseHeader()
}
}
// no sample sizes found, search for trun atoms
Mp4Atom *moofAtom = m_trakAtom->container().firstElement()->siblingById(MovieFragment, true);
uint64 totalDuration = 0;
while(moofAtom) {
for(Mp4Atom *moofAtom = m_trakAtom->container().firstElement()->siblingById(MovieFragment, true); moofAtom; moofAtom = moofAtom->siblingById(MovieFragment, false)) {
moofAtom->parse();
Mp4Atom *trafAtom = moofAtom->childById(TrackFragment);
while(trafAtom) {
for(Mp4Atom *trafAtom = moofAtom->childById(TrackFragment); trafAtom; trafAtom = trafAtom->siblingById(TrackFragment, false)) {
trafAtom->parse();
Mp4Atom *tfhdAtom = trafAtom->childById(TrackFragmentHeader);
while(tfhdAtom) {
for(Mp4Atom *tfhdAtom = trafAtom->childById(TrackFragmentHeader); tfhdAtom; tfhdAtom = tfhdAtom->siblingById(TrackFragmentHeader, false)) {
tfhdAtom->parse();
uint32 calculatedDataSize = 0;
if(tfhdAtom->dataSize() < calculatedDataSize) {
@ -1434,8 +1533,7 @@ void Mp4Track::internalParseHeader()
m_istream->seekg(4, ios_base::cur);
}
}
Mp4Atom *trunAtom = trafAtom->childById(TrackFragmentRun);
while(trunAtom) {
for(Mp4Atom *trunAtom = trafAtom->childById(TrackFragmentRun); trunAtom; trunAtom = trunAtom->siblingById(TrackFragmentRun, false)) {
uint32 calculatedDataSize = 8;
if(trunAtom->dataSize() < calculatedDataSize) {
addNotification(NotificationType::Critical, "trun atom is truncated.", context);
@ -1495,18 +1593,14 @@ void Mp4Track::internalParseHeader()
}
}
}
trunAtom = trunAtom->siblingById(TrackFragmentRun, false);
}
if(m_sampleSizes.empty() && defaultSampleSize) {
m_sampleSizes.push_back(defaultSampleSize);
}
}
}
tfhdAtom = tfhdAtom->siblingById(TrackFragmentHeader, false);
}
trafAtom = trafAtom->siblingById(TrackFragment, false);
}
moofAtom = moofAtom->siblingById(MovieFragment, false);
}
// set duration from "trun-information" if the duration has not been determined yet
if(m_duration.isNull() && totalDuration) {
@ -1515,7 +1609,7 @@ void Mp4Track::internalParseHeader()
timeScale = trakAtom().container().timeScale();
}
if(timeScale) {
m_duration = TimeSpan::fromSeconds(static_cast<double>(totalDuration) / static_cast<double>(m_timeScale));
m_duration = TimeSpan::fromSeconds(static_cast<double>(totalDuration) / static_cast<double>(timeScale));
}
}
// caluculate average bitrate

View File

@ -40,6 +40,15 @@ public:
byte epConfig;
};
class LIB_EXPORT Mpeg4VideoSpecificConfig
{
public:
Mpeg4VideoSpecificConfig();
byte profile;
std::string userData;
};
class LIB_EXPORT Mpeg4ElementaryStreamInfo
{
public:
@ -63,6 +72,7 @@ public:
uint32 maxBitrate;
uint32 averageBitrate;
std::unique_ptr<Mpeg4AudioSpecificConfig> audioSpecificConfig;
std::unique_ptr<Mpeg4VideoSpecificConfig> videoSpecificConfig;
};
inline Mpeg4ElementaryStreamInfo::Mpeg4ElementaryStreamInfo() :
@ -126,6 +136,7 @@ public:
AvcConfiguration parseAvcConfiguration(Mp4Atom *avcConfigAtom);
std::unique_ptr<Mpeg4ElementaryStreamInfo> parseMpeg4ElementaryStreamInfo(Mp4Atom *esDescAtom);
std::unique_ptr<Mpeg4AudioSpecificConfig> parseAudioSpecificConfig(Mpeg4Descriptor *decSpecInfoDesc);
std::unique_ptr<Mpeg4VideoSpecificConfig> parseVideoSpecificConfig(Mpeg4Descriptor *decSpecInfoDesc);
// methods to read the "index" (chunk offsets and sizes)
std::vector<uint64> readChunkOffsets();