added detection of ADTS files
This commit is contained in:
parent
15a03a0029
commit
1f4800c9be
|
@ -32,7 +32,8 @@ enum class TrackType
|
||||||
MpegAudioFrameStream, /**< The track is a Media::MpegAudioFrameStream. */
|
MpegAudioFrameStream, /**< The track is a Media::MpegAudioFrameStream. */
|
||||||
Mp4Track, /**< The track is a Media::Mp4Track. */
|
Mp4Track, /**< The track is a Media::Mp4Track. */
|
||||||
WaveAudioStream, /**< The track is a Media::WaveAudioStream. */
|
WaveAudioStream, /**< The track is a Media::WaveAudioStream. */
|
||||||
OggStream /**< The track is a Media::OggStream. */
|
OggStream, /**< The track is a Media::OggStream. */
|
||||||
|
AdtsStream /**< The track is a Media::AdtsStream. */
|
||||||
};
|
};
|
||||||
|
|
||||||
class LIB_EXPORT AbstractTrack : public StatusProvider
|
class LIB_EXPORT AbstractTrack : public StatusProvider
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
#include "adtsstream.h"
|
||||||
|
|
||||||
|
#include "../mp4/mp4ids.h"
|
||||||
|
#include "../exceptions.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace Media {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \class Media::AdtsStream
|
||||||
|
* \brief Implementation of Media::AbstractTrack for ADTS streams.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void AdtsStream::internalParseHeader()
|
||||||
|
{
|
||||||
|
//static const string context("parsing ADTS frame header");
|
||||||
|
if(!m_istream) {
|
||||||
|
throw NoDataFoundException();
|
||||||
|
}
|
||||||
|
// get size
|
||||||
|
m_istream->seekg(-128, ios_base::end);
|
||||||
|
if(m_reader.readUInt24BE() == 0x544147) {
|
||||||
|
m_size = static_cast<uint64>(m_istream->tellg()) - 3u - m_startOffset;
|
||||||
|
} else {
|
||||||
|
m_size = static_cast<uint64>(m_istream->tellg()) + 125u - m_startOffset;
|
||||||
|
}
|
||||||
|
m_istream->seekg(m_startOffset, ios_base::beg);
|
||||||
|
// parse frame header
|
||||||
|
m_firstFrame.parseHeader(m_reader);
|
||||||
|
m_format = Mpeg4AudioObjectIds::idToMediaFormat(m_firstFrame.mpeg4AudioObjectId());
|
||||||
|
m_channelCount = Mpeg4ChannelConfigs::channelCount(m_firstFrame.mpeg4ChannelConfig());
|
||||||
|
byte sampleRateIndex = m_firstFrame.mpeg4SampleRateIndex();
|
||||||
|
m_sampleRate = sampleRateIndex < sizeof(mpeg4SampleRateTable) ? mpeg4SampleRateTable[sampleRateIndex] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Media
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef MEDIA_ADTSSTREAM_H
|
||||||
|
#define MEDIA_ADTSSTREAM_H
|
||||||
|
|
||||||
|
#include "adtsframe.h"
|
||||||
|
#include "../abstracttrack.h"
|
||||||
|
|
||||||
|
namespace Media {
|
||||||
|
|
||||||
|
class LIB_EXPORT AdtsStream : public AbstractTrack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AdtsStream(std::iostream &stream, uint64 startOffset);
|
||||||
|
~AdtsStream();
|
||||||
|
|
||||||
|
TrackType type() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void internalParseHeader();
|
||||||
|
|
||||||
|
private:
|
||||||
|
AdtsFrame m_firstFrame;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Constructs a new track for the \a stream at the specified \a startOffset.
|
||||||
|
*/
|
||||||
|
inline AdtsStream::AdtsStream(std::iostream &stream, uint64 startOffset) :
|
||||||
|
AbstractTrack(stream, startOffset)
|
||||||
|
{
|
||||||
|
m_mediaType = MediaType::Audio;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AdtsStream::~AdtsStream()
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline TrackType AdtsStream::type() const
|
||||||
|
{
|
||||||
|
return TrackType::AdtsStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Media
|
||||||
|
|
||||||
|
#endif // MEDIA_ADTSSTREAM_H
|
|
@ -5,8 +5,8 @@
|
||||||
#include "id3/id3v1tag.h"
|
#include "id3/id3v1tag.h"
|
||||||
#include "id3/id3v2tag.h"
|
#include "id3/id3v2tag.h"
|
||||||
#include "wav/waveaudiostream.h"
|
#include "wav/waveaudiostream.h"
|
||||||
#include "mpegaudio/mpegaudioframe.h"
|
|
||||||
#include "mpegaudio/mpegaudioframestream.h"
|
#include "mpegaudio/mpegaudioframestream.h"
|
||||||
|
#include "adts/adtsstream.h"
|
||||||
#include "mp4/mp4container.h"
|
#include "mp4/mp4container.h"
|
||||||
#include "mp4/mp4atom.h"
|
#include "mp4/mp4atom.h"
|
||||||
#include "mp4/mp4tag.h"
|
#include "mp4/mp4tag.h"
|
||||||
|
@ -131,7 +131,7 @@ startParsingSignature:
|
||||||
stream().seekg(m_containerOffset, ios_base::beg);
|
stream().seekg(m_containerOffset, ios_base::beg);
|
||||||
stream().read(buff, sizeof(buff));
|
stream().read(buff, sizeof(buff));
|
||||||
// skip zero bytes/padding
|
// skip zero bytes/padding
|
||||||
if(buff[0] == 0 && buff[1] == 0 && buff[2] == 0 && buff[3] == 0) {
|
if(!(*reinterpret_cast<uint32 *>(buff))) {
|
||||||
// give up after 0x100 bytes
|
// give up after 0x100 bytes
|
||||||
if(m_paddingSize >= 0x100u) {
|
if(m_paddingSize >= 0x100u) {
|
||||||
m_containerParsed = true;
|
m_containerParsed = true;
|
||||||
|
@ -143,11 +143,10 @@ startParsingSignature:
|
||||||
goto startParsingSignature; // read signature again
|
goto startParsingSignature; // read signature again
|
||||||
}
|
}
|
||||||
if(m_paddingSize) {
|
if(m_paddingSize) {
|
||||||
addNotification(NotificationType::Warning, ConversionUtilities::numberToString(m_paddingSize) + " empty bytes skipped.", context);
|
addNotification(NotificationType::Warning, ConversionUtilities::numberToString(m_paddingSize) + " zero-bytes skipped at the beginning of the file.", context);
|
||||||
}
|
}
|
||||||
// parse signature
|
// parse signature
|
||||||
m_containerFormat = parseSignature(buff, sizeof(buff));
|
switch(m_containerFormat = parseSignature(buff, sizeof(buff))) {
|
||||||
switch(m_containerFormat) {
|
|
||||||
case ContainerFormat::Id2v2Tag:
|
case ContainerFormat::Id2v2Tag:
|
||||||
m_actualId3v2TagOffsets.push_back(m_containerOffset);
|
m_actualId3v2TagOffsets.push_back(m_containerOffset);
|
||||||
if(m_actualId3v2TagOffsets.size() == 2) {
|
if(m_actualId3v2TagOffsets.size() == 2) {
|
||||||
|
@ -233,16 +232,18 @@ void MediaFileInfo::parseTracks()
|
||||||
} else {
|
} else {
|
||||||
switch(m_containerFormat) {
|
switch(m_containerFormat) {
|
||||||
case ContainerFormat::RiffWave:
|
case ContainerFormat::RiffWave:
|
||||||
m_waveAudioStream = make_unique<WaveAudioStream>(stream(), m_containerOffset);
|
m_singleTrack = make_unique<WaveAudioStream>(stream(), m_containerOffset);
|
||||||
m_waveAudioStream->parseHeader();
|
|
||||||
break;
|
break;
|
||||||
case ContainerFormat::MpegAudioFrames:
|
case ContainerFormat::MpegAudioFrames:
|
||||||
m_mpegAudioFrameStream = make_unique<MpegAudioFrameStream>(stream(), m_containerOffset);
|
m_singleTrack = make_unique<MpegAudioFrameStream>(stream(), m_containerOffset);
|
||||||
m_mpegAudioFrameStream->parseHeader();
|
break;
|
||||||
|
case ContainerFormat::Adts:
|
||||||
|
m_singleTrack = make_unique<AdtsStream>(stream(), m_containerOffset);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw NotImplementedException();
|
throw NotImplementedException();
|
||||||
}
|
}
|
||||||
|
m_singleTrack->parseHeader();
|
||||||
}
|
}
|
||||||
} catch (NotImplementedException &) {
|
} catch (NotImplementedException &) {
|
||||||
addNotification(NotificationType::Information, "Parsing tracks is not implemented for the container format of the file.", context);
|
addNotification(NotificationType::Information, "Parsing tracks is not implemented for the container format of the file.", context);
|
||||||
|
@ -339,28 +340,32 @@ void MediaFileInfo::parseTags()
|
||||||
void MediaFileInfo::parseChapters()
|
void MediaFileInfo::parseChapters()
|
||||||
{
|
{
|
||||||
static const string context("parsing chapters");
|
static const string context("parsing chapters");
|
||||||
if(m_container) {
|
try {
|
||||||
try {
|
if(m_container) {
|
||||||
m_container->parseChapters();
|
m_container->parseChapters();
|
||||||
} catch (NotImplementedException &) {
|
} else {
|
||||||
addNotification(NotificationType::Information, "Parsing chapters is not implemented for the container format of the file.", context);
|
throw NotImplementedException();
|
||||||
} catch (Failure &) {
|
|
||||||
addNotification(NotificationType::Critical, "Unable to parse chapters.", context);
|
|
||||||
}
|
}
|
||||||
|
} catch (NotImplementedException &) {
|
||||||
|
addNotification(NotificationType::Information, "Parsing chapters is not implemented for the container format of the file.", context);
|
||||||
|
} catch (Failure &) {
|
||||||
|
addNotification(NotificationType::Critical, "Unable to parse chapters.", context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaFileInfo::parseAttachments()
|
void MediaFileInfo::parseAttachments()
|
||||||
{
|
{
|
||||||
static const string context("parsing attachments");
|
static const string context("parsing attachments");
|
||||||
if(m_container) {
|
try {
|
||||||
try {
|
if(m_container) {
|
||||||
m_container->parseAttachments();
|
m_container->parseAttachments();
|
||||||
} catch (NotImplementedException &) {
|
} else {
|
||||||
addNotification(NotificationType::Information, "Parsing attachments is not implemented for the container format of the file.", context);
|
throw NotImplementedException();
|
||||||
} catch (Failure &) {
|
|
||||||
addNotification(NotificationType::Critical, "Unable to parse attachments.", context);
|
|
||||||
}
|
}
|
||||||
|
} catch (NotImplementedException &) {
|
||||||
|
addNotification(NotificationType::Information, "Parsing attachments is not implemented for the container format of the file.", context);
|
||||||
|
} catch (Failure &) {
|
||||||
|
addNotification(NotificationType::Critical, "Unable to parse attachments.", context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,8 +545,8 @@ const char *MediaFileInfo::containerFormatAbbreviation() const
|
||||||
mediaType = hasTracksOfType(MediaType::Video) ? MediaType::Video : MediaType::Audio;
|
mediaType = hasTracksOfType(MediaType::Video) ? MediaType::Video : MediaType::Audio;
|
||||||
break;
|
break;
|
||||||
case ContainerFormat::MpegAudioFrames:
|
case ContainerFormat::MpegAudioFrames:
|
||||||
if(m_mpegAudioFrameStream) {
|
if(m_singleTrack) {
|
||||||
version = m_mpegAudioFrameStream->format().sub;
|
version = m_singleTrack->format().sub;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -627,29 +632,6 @@ const char *MediaFileInfo::mimeType() const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Returns the number of tracks that could be parsed.
|
|
||||||
*
|
|
||||||
* parseTracks() needs to be called before. Otherwise this
|
|
||||||
* method always returns zero.
|
|
||||||
*
|
|
||||||
* \sa parseTracks()
|
|
||||||
*/
|
|
||||||
size_t MediaFileInfo::trackCount() const
|
|
||||||
{
|
|
||||||
size_t c = 0;
|
|
||||||
if(m_waveAudioStream) {
|
|
||||||
++c;
|
|
||||||
}
|
|
||||||
if(m_mpegAudioFrameStream) {
|
|
||||||
++c;
|
|
||||||
}
|
|
||||||
if(m_container) {
|
|
||||||
c += m_container->trackCount();
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Returns the tracks for the current file.
|
* \brief Returns the tracks for the current file.
|
||||||
*
|
*
|
||||||
|
@ -665,11 +647,8 @@ size_t MediaFileInfo::trackCount() const
|
||||||
vector<AbstractTrack *> MediaFileInfo::tracks() const
|
vector<AbstractTrack *> MediaFileInfo::tracks() const
|
||||||
{
|
{
|
||||||
vector<AbstractTrack *> res;
|
vector<AbstractTrack *> res;
|
||||||
if(m_waveAudioStream) {
|
if(m_singleTrack) {
|
||||||
res.push_back(m_waveAudioStream.get());
|
res.push_back(m_singleTrack.get());
|
||||||
}
|
|
||||||
if(m_mpegAudioFrameStream) {
|
|
||||||
res.push_back(m_mpegAudioFrameStream.get());
|
|
||||||
}
|
}
|
||||||
if(m_container) {
|
if(m_container) {
|
||||||
for(size_t i = 0, count = m_container->trackCount(); i < count; ++i) {
|
for(size_t i = 0, count = m_container->trackCount(); i < count; ++i) {
|
||||||
|
@ -692,7 +671,7 @@ bool MediaFileInfo::hasTracksOfType(MediaType type) const
|
||||||
if(!areTracksParsed()) {
|
if(!areTracksParsed()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(type == MediaType::Audio && (m_waveAudioStream || m_mpegAudioFrameStream)) {
|
if(type == MediaType::Audio && m_singleTrack) {
|
||||||
return true;
|
return true;
|
||||||
} else if(m_container) {
|
} else if(m_container) {
|
||||||
for(size_t i = 0, count = m_container->trackCount(); i < count; ++i) {
|
for(size_t i = 0, count = m_container->trackCount(); i < count; ++i) {
|
||||||
|
@ -1142,8 +1121,7 @@ void MediaFileInfo::clearParsingResults()
|
||||||
m_actualId3v2TagOffsets.clear();
|
m_actualId3v2TagOffsets.clear();
|
||||||
m_actualExistingId3v1Tag = false;
|
m_actualExistingId3v1Tag = false;
|
||||||
m_container.reset();
|
m_container.reset();
|
||||||
m_waveAudioStream.reset();
|
m_singleTrack.reset();
|
||||||
m_mpegAudioFrameStream.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
|
@ -142,8 +142,7 @@ private:
|
||||||
std::unique_ptr<AbstractContainer> m_container;
|
std::unique_ptr<AbstractContainer> m_container;
|
||||||
// fields related to the tracks
|
// fields related to the tracks
|
||||||
bool m_tracksParsed;
|
bool m_tracksParsed;
|
||||||
std::unique_ptr<WaveAudioStream> m_waveAudioStream;
|
std::unique_ptr<AbstractTrack> m_singleTrack;
|
||||||
std::unique_ptr<MpegAudioFrameStream> m_mpegAudioFrameStream;
|
|
||||||
// fields related to the tag
|
// fields related to the tag
|
||||||
bool m_tagParsed;
|
bool m_tagParsed;
|
||||||
std::unique_ptr<Id3v1Tag> m_id3v1Tag;
|
std::unique_ptr<Id3v1Tag> m_id3v1Tag;
|
||||||
|
@ -203,6 +202,19 @@ inline bool MediaFileInfo::areTracksParsed() const
|
||||||
return m_tracksParsed;
|
return m_tracksParsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns the number of tracks that could be parsed.
|
||||||
|
*
|
||||||
|
* parseTracks() needs to be called before. Otherwise this
|
||||||
|
* method always returns zero.
|
||||||
|
*
|
||||||
|
* \sa parseTracks()
|
||||||
|
*/
|
||||||
|
inline size_t MediaFileInfo::trackCount() const
|
||||||
|
{
|
||||||
|
return m_singleTrack ? 1 : 0 + m_container->trackCount();
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Returns whether the chapters have been parsed yet.
|
* \brief Returns whether the chapters have been parsed yet.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -223,7 +223,7 @@ const char *streamTypeName(byte streamTypeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Encapsulates all supported MPEG-4 audio object format IDs.
|
* \brief Encapsulates all supported MPEG-4 audio object type IDs.
|
||||||
* \sa http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio
|
* \sa http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio
|
||||||
*/
|
*/
|
||||||
namespace Mpeg4AudioObjectIds {
|
namespace Mpeg4AudioObjectIds {
|
||||||
|
@ -279,6 +279,61 @@ LIB_EXPORT MediaFormat idToMediaFormat(byte mpeg4AudioObjectId, bool sbrPresent,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 mpeg4SampleRateTable[] = {
|
||||||
|
96000, 88200, 64000, 48000, 44100, 32000,
|
||||||
|
24000, 22050, 16000, 12000, 11025, 8000, 7350
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Mpeg4ChannelConfigs {
|
||||||
|
|
||||||
|
const char *channelConfigString(byte config)
|
||||||
|
{
|
||||||
|
switch(config) {
|
||||||
|
case AotSpecificConfig:
|
||||||
|
return "defined in AOT Specific Config";
|
||||||
|
case FrontCenter:
|
||||||
|
return "1 channel: front-center";
|
||||||
|
case FrontLeftFrontRight:
|
||||||
|
return "2 channels: front-left, front-right";
|
||||||
|
case FrontCenterFrontLeftFrontRight:
|
||||||
|
return "3 channels: front-center, front-left, front-right";
|
||||||
|
case FrontCenterFrontLeftFrontRightBackCenter:
|
||||||
|
return "4 channels: front-center, front-left, front-right, back-center";
|
||||||
|
case FrontCenterFrontLeftFrontRightBackLeftBackRight:
|
||||||
|
return "5 channels: front-center, front-left, front-right, back-left, back-right";
|
||||||
|
case FrontCenterFrontLeftFrontRightBackLeftBackRightLFEChannel:
|
||||||
|
return "6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel";
|
||||||
|
case FrontCenterFrontLeftFrontRightSideLeftSideRightBackLeftBackRightLFEChannel:
|
||||||
|
return "8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte channelCount(byte config)
|
||||||
|
{
|
||||||
|
switch(config) {
|
||||||
|
case FrontCenter:
|
||||||
|
return 1;
|
||||||
|
case FrontLeftFrontRight:
|
||||||
|
return 2;
|
||||||
|
case FrontCenterFrontLeftFrontRight:
|
||||||
|
return 3;
|
||||||
|
case FrontCenterFrontLeftFrontRightBackCenter:
|
||||||
|
return 4;
|
||||||
|
case FrontCenterFrontLeftFrontRightBackLeftBackRight:
|
||||||
|
return 5;
|
||||||
|
case FrontCenterFrontLeftFrontRightBackLeftBackRightLFEChannel:
|
||||||
|
return 6;
|
||||||
|
case FrontCenterFrontLeftFrontRightSideLeftSideRightBackLeftBackRightLFEChannel:
|
||||||
|
return 8;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Encapsulates MPEG-4 video (14496-2) codes.
|
* \brief Encapsulates MPEG-4 video (14496-2) codes.
|
||||||
*/
|
*/
|
||||||
|
|
20
mp4/mp4ids.h
20
mp4/mp4ids.h
|
@ -585,6 +585,26 @@ LIB_EXPORT MediaFormat idToMediaFormat(byte mpeg4AudioObjectId, bool sbrPresent
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern uint32 mpeg4SampleRateTable[13];
|
||||||
|
|
||||||
|
namespace Mpeg4ChannelConfigs {
|
||||||
|
enum Mpeg4ChannelConfig : byte
|
||||||
|
{
|
||||||
|
AotSpecificConfig = 0,
|
||||||
|
FrontCenter,
|
||||||
|
FrontLeftFrontRight,
|
||||||
|
FrontCenterFrontLeftFrontRight,
|
||||||
|
FrontCenterFrontLeftFrontRightBackCenter,
|
||||||
|
FrontCenterFrontLeftFrontRightBackLeftBackRight,
|
||||||
|
FrontCenterFrontLeftFrontRightBackLeftBackRightLFEChannel,
|
||||||
|
FrontCenterFrontLeftFrontRightSideLeftSideRightBackLeftBackRightLFEChannel
|
||||||
|
};
|
||||||
|
|
||||||
|
LIB_EXPORT const char *channelConfigString(byte config);
|
||||||
|
LIB_EXPORT byte channelCount(byte config);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
namespace Mpeg4VideoCodes {
|
namespace Mpeg4VideoCodes {
|
||||||
enum KnownValue : byte {
|
enum KnownValue : byte {
|
||||||
VideoObjectStart = 0x00,
|
VideoObjectStart = 0x00,
|
||||||
|
|
|
@ -26,10 +26,6 @@ namespace Media {
|
||||||
|
|
||||||
DateTime startDate = DateTime::fromDate(1904, 1, 1);
|
DateTime startDate = DateTime::fromDate(1904, 1, 1);
|
||||||
|
|
||||||
uint32 sampleRateTable[] = {
|
|
||||||
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000
|
|
||||||
};
|
|
||||||
|
|
||||||
MediaFormat fmtTable[] = {
|
MediaFormat fmtTable[] = {
|
||||||
GeneralMediaFormat::Unknown,
|
GeneralMediaFormat::Unknown,
|
||||||
MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg4MainProfile),
|
MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg4MainProfile),
|
||||||
|
@ -1369,15 +1365,15 @@ void Mp4Track::internalParseHeader()
|
||||||
m_format += Mpeg4AudioObjectIds::idToMediaFormat(m_esInfo->audioSpecificConfig->audioObjectType, m_esInfo->audioSpecificConfig->sbrPresent, m_esInfo->audioSpecificConfig->psPresent);
|
m_format += Mpeg4AudioObjectIds::idToMediaFormat(m_esInfo->audioSpecificConfig->audioObjectType, m_esInfo->audioSpecificConfig->sbrPresent, m_esInfo->audioSpecificConfig->psPresent);
|
||||||
if(m_esInfo->audioSpecificConfig->sampleFrequencyIndex == 0xF) {
|
if(m_esInfo->audioSpecificConfig->sampleFrequencyIndex == 0xF) {
|
||||||
m_sampleRate = m_esInfo->audioSpecificConfig->sampleFrequency;
|
m_sampleRate = m_esInfo->audioSpecificConfig->sampleFrequency;
|
||||||
} else if(m_esInfo->audioSpecificConfig->sampleFrequencyIndex < sizeof(sampleRateTable)) {
|
} else if(m_esInfo->audioSpecificConfig->sampleFrequencyIndex < sizeof(mpeg4SampleRateTable)) {
|
||||||
m_sampleRate = sampleRateTable[m_esInfo->audioSpecificConfig->sampleFrequencyIndex];
|
m_sampleRate = mpeg4SampleRateTable[m_esInfo->audioSpecificConfig->sampleFrequencyIndex];
|
||||||
} else {
|
} else {
|
||||||
addNotification(NotificationType::Warning, "Audio specific config has invalid sample frequency index.", context);
|
addNotification(NotificationType::Warning, "Audio specific config has invalid sample frequency index.", context);
|
||||||
}
|
}
|
||||||
if(m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex == 0xF) {
|
if(m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex == 0xF) {
|
||||||
m_extensionSampleRate = m_esInfo->audioSpecificConfig->extensionSampleFrequency;
|
m_extensionSampleRate = m_esInfo->audioSpecificConfig->extensionSampleFrequency;
|
||||||
} else if(m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex < sizeof(sampleRateTable)) {
|
} else if(m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex < sizeof(mpeg4SampleRateTable)) {
|
||||||
m_extensionSampleRate = sampleRateTable[m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex];
|
m_extensionSampleRate = mpeg4SampleRateTable[m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex];
|
||||||
} else {
|
} else {
|
||||||
addNotification(NotificationType::Warning, "Audio specific config has invalid extension sample frequency index.", context);
|
addNotification(NotificationType::Warning, "Audio specific config has invalid extension sample frequency index.", context);
|
||||||
}
|
}
|
||||||
|
@ -1397,7 +1393,7 @@ void Mp4Track::internalParseHeader()
|
||||||
MpegAudioFrame frame;
|
MpegAudioFrame frame;
|
||||||
m_istream->seekg(m_stcoAtom->dataOffset() + 8);
|
m_istream->seekg(m_stcoAtom->dataOffset() + 8);
|
||||||
m_istream->seekg(m_chunkOffsetSize == 8 ? reader.readUInt64BE() : reader.readUInt32BE());
|
m_istream->seekg(m_chunkOffsetSize == 8 ? reader.readUInt64BE() : reader.readUInt32BE());
|
||||||
frame.parseHeader(*m_istream);
|
frame.parseHeader(reader);
|
||||||
MpegAudioFrameStream::addInfo(frame, *this);
|
MpegAudioFrameStream::addInfo(frame, *this);
|
||||||
break;
|
break;
|
||||||
} default:
|
} default:
|
||||||
|
|
|
@ -28,15 +28,14 @@ const int MpegAudioFrame::m_bitrateTable[0x2][0x3][0xF] =
|
||||||
const uint32 MpegAudioFrame::m_sync = 0xFFE00000u;
|
const uint32 MpegAudioFrame::m_sync = 0xFFE00000u;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Parses the header read from the specified \a stream at the current position.
|
* \brief Parses the header read using the specified \a reader.
|
||||||
* \throws Throws InvalidDataException if the data read from the stream is
|
* \throws Throws InvalidDataException if the data read from the stream is
|
||||||
* no valid frame header.
|
* no valid frame header.
|
||||||
*/
|
*/
|
||||||
void MpegAudioFrame::parseHeader(istream &stream)
|
void MpegAudioFrame::parseHeader(BinaryReader &reader)
|
||||||
{
|
{
|
||||||
BinaryReader reader(&stream);
|
|
||||||
m_header = reader.readUInt32BE();
|
m_header = reader.readUInt32BE();
|
||||||
stream.seekg(m_xingHeaderOffset - 4, ios_base::cur);
|
reader.stream()->seekg(m_xingHeaderOffset - 4, ios_base::cur);
|
||||||
m_xingHeader = reader.readUInt64BE();
|
m_xingHeader = reader.readUInt64BE();
|
||||||
m_xingHeaderFlags = static_cast<XingHeaderFlags>(m_xingHeader & 0xffffffffuL);
|
m_xingHeaderFlags = static_cast<XingHeaderFlags>(m_xingHeader & 0xffffffffuL);
|
||||||
if(isXingHeaderAvailable()) {
|
if(isXingHeaderAvailable()) {
|
||||||
|
@ -47,7 +46,7 @@ void MpegAudioFrame::parseHeader(istream &stream)
|
||||||
m_xingBytesfield = reader.readUInt32BE();
|
m_xingBytesfield = reader.readUInt32BE();
|
||||||
}
|
}
|
||||||
if(isXingTocFieldPresent()) {
|
if(isXingTocFieldPresent()) {
|
||||||
stream.seekg(64, ios_base::cur);
|
reader.stream()->seekg(64, ios_base::cur);
|
||||||
}
|
}
|
||||||
if(isXingQualityIndicatorFieldPresent()) {
|
if(isXingQualityIndicatorFieldPresent()) {
|
||||||
m_xingQualityIndicator = reader.readUInt32BE();
|
m_xingQualityIndicator = reader.readUInt32BE();
|
||||||
|
|
|
@ -6,6 +6,10 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace IoUtilities {
|
||||||
|
class BinaryReader;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Media
|
namespace Media
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -35,7 +39,7 @@ class LIB_EXPORT MpegAudioFrame
|
||||||
public:
|
public:
|
||||||
MpegAudioFrame();
|
MpegAudioFrame();
|
||||||
|
|
||||||
void parseHeader(std::istream &stream);
|
void parseHeader(IoUtilities::BinaryReader &reader);
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
double mpegVersion() const;
|
double mpegVersion() const;
|
||||||
|
|
|
@ -14,30 +14,9 @@ namespace Media {
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \class Media::MpegAudioFrameStream
|
* \class Media::MpegAudioFrameStream
|
||||||
* \brief Implementation of Media::AbstractTrack for a stream of
|
* \brief Implementation of Media::AbstractTrack MPEG audio streams.
|
||||||
* MPEG audio frames (run-of-the-mill MP3 file).
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Constructs a new track for the \a stream at the specified \a startOffset.
|
|
||||||
*/
|
|
||||||
MpegAudioFrameStream::MpegAudioFrameStream(iostream &stream, uint64 startOffset) :
|
|
||||||
AbstractTrack(stream, startOffset)
|
|
||||||
{
|
|
||||||
m_mediaType = MediaType::Audio;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Destroys the track.
|
|
||||||
*/
|
|
||||||
MpegAudioFrameStream::~MpegAudioFrameStream()
|
|
||||||
{}
|
|
||||||
|
|
||||||
TrackType MpegAudioFrameStream::type() const
|
|
||||||
{
|
|
||||||
return TrackType::MpegAudioFrameStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Adds the information from the specified \a frame to the specified \a track.
|
* \brief Adds the information from the specified \a frame to the specified \a track.
|
||||||
*/
|
*/
|
||||||
|
@ -66,7 +45,7 @@ void MpegAudioFrameStream::internalParseHeader()
|
||||||
// parse frame header
|
// parse frame header
|
||||||
m_frames.emplace_back();
|
m_frames.emplace_back();
|
||||||
MpegAudioFrame &frame = m_frames.back();
|
MpegAudioFrame &frame = m_frames.back();
|
||||||
frame.parseHeader(*m_istream);
|
frame.parseHeader(m_reader);
|
||||||
addInfo(frame, *this);
|
addInfo(frame, *this);
|
||||||
if(frame.isXingBytesfieldPresent()) {
|
if(frame.isXingBytesfieldPresent()) {
|
||||||
uint32 xingSize = frame.xingBytesfield();
|
uint32 xingSize = frame.xingBytesfield();
|
||||||
|
|
|
@ -26,6 +26,23 @@ private:
|
||||||
std::list<MpegAudioFrame> m_frames;
|
std::list<MpegAudioFrame> m_frames;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Constructs a new track for the \a stream at the specified \a startOffset.
|
||||||
|
*/
|
||||||
|
inline MpegAudioFrameStream::MpegAudioFrameStream(std::iostream &stream, uint64 startOffset) :
|
||||||
|
AbstractTrack(stream, startOffset)
|
||||||
|
{
|
||||||
|
m_mediaType = MediaType::Audio;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline MpegAudioFrameStream::~MpegAudioFrameStream()
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline TrackType MpegAudioFrameStream::type() const
|
||||||
|
{
|
||||||
|
return TrackType::MpegAudioFrameStream;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // MPEGAUDIOFRAMESTREAM_H
|
#endif // MPEGAUDIOFRAMESTREAM_H
|
||||||
|
|
|
@ -66,7 +66,7 @@ enum Sig24 : uint32
|
||||||
Bzip2 = 0x425A68u,
|
Bzip2 = 0x425A68u,
|
||||||
Gzip = 0x1F8B08u,
|
Gzip = 0x1F8B08u,
|
||||||
Id3v2 = 0x494433u,
|
Id3v2 = 0x494433u,
|
||||||
Utf8Text = 0xEFBBBFu
|
Utf8Text = 0xEFBBBFu,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -74,6 +74,8 @@ enum Sig24 : uint32
|
||||||
*/
|
*/
|
||||||
enum Sig16 : uint16
|
enum Sig16 : uint16
|
||||||
{
|
{
|
||||||
|
Adts = 0xFFF0u,
|
||||||
|
AdtsMask = 0xFFF6u,
|
||||||
Jpeg = 0xffd8u,
|
Jpeg = 0xffd8u,
|
||||||
Lha = 0x1FA0u,
|
Lha = 0x1FA0u,
|
||||||
Lzw = 0x1F9Du,
|
Lzw = 0x1F9Du,
|
||||||
|
@ -202,6 +204,9 @@ ContainerFormat parseSignature(const char *buffer, int bufferSize)
|
||||||
default:
|
default:
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
if(((sig >> 48) & AdtsMask) == Adts) {
|
||||||
|
return ContainerFormat::Adts;
|
||||||
|
}
|
||||||
return ContainerFormat::Unknown;
|
return ContainerFormat::Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,6 +287,8 @@ const char *containerFormatAbbreviation(ContainerFormat containerFormat, MediaTy
|
||||||
const char *containerFormatName(ContainerFormat containerFormat)
|
const char *containerFormatName(ContainerFormat containerFormat)
|
||||||
{
|
{
|
||||||
switch(containerFormat) {
|
switch(containerFormat) {
|
||||||
|
case ContainerFormat::Adts:
|
||||||
|
return "Audio Data Transport Stream";
|
||||||
case ContainerFormat::Asf:
|
case ContainerFormat::Asf:
|
||||||
return "Advanced Systems Format";
|
return "Advanced Systems Format";
|
||||||
case ContainerFormat::Elf:
|
case ContainerFormat::Elf:
|
||||||
|
|
|
@ -14,6 +14,7 @@ namespace Media {
|
||||||
enum class ContainerFormat
|
enum class ContainerFormat
|
||||||
{
|
{
|
||||||
Unknown, /**< unknown container format */
|
Unknown, /**< unknown container format */
|
||||||
|
Adts, /** < Audio Data Transport Stream */
|
||||||
Asf, /**< Advanced Systems Format */
|
Asf, /**< Advanced Systems Format */
|
||||||
Bzip2, /** bzip2 compressed file */
|
Bzip2, /** bzip2 compressed file */
|
||||||
Elf, /**< Executable and Linkable Format */
|
Elf, /**< Executable and Linkable Format */
|
||||||
|
|
Loading…
Reference in New Issue