400 lines
9.8 KiB
C++
400 lines
9.8 KiB
C++
#include "tracks.hpp"
|
|
#include "imkvreader.hpp"
|
|
#include "segment.hpp"
|
|
#include "ebmlreadhelpers.hpp"
|
|
#include "videotrack.hpp"
|
|
#include "audiotrack.hpp"
|
|
#include "mkvprofiler.hpp"
|
|
|
|
#include <stddef.h>
|
|
#include <assert.h>
|
|
|
|
namespace mkvparser
|
|
{
|
|
|
|
Tracks::Tracks(Segment* pSegment, const EBMLElement& elemInfo)
|
|
: m_pSegment(pSegment)
|
|
, m_elemInfo(elemInfo)
|
|
{
|
|
}
|
|
|
|
|
|
Tracks::~Tracks()
|
|
{
|
|
TrackVect_t::iterator first = m_trackEntries.begin();
|
|
TrackVect_t::iterator last = m_trackEntries.end();
|
|
|
|
for (; first != last; ++first)
|
|
{
|
|
delete (*first);
|
|
}
|
|
}
|
|
|
|
|
|
long Tracks::Parse()
|
|
{
|
|
PROFILER_SCOPED_EVENT("webm::Tracks::Parse");
|
|
|
|
assert(m_trackEntries.empty());
|
|
|
|
const long long stop = m_elemInfo.GetEndPos();
|
|
IMkvReader* const pReader = m_pSegment->GetReader();
|
|
|
|
long long pos = m_elemInfo.GetPayloadPos();
|
|
|
|
while (pos < stop)
|
|
{
|
|
EBMLElement elemInfo;
|
|
long long result = elemInfo.Parse(pReader, pos);
|
|
if (result != 0)
|
|
return static_cast<long>(result);
|
|
|
|
if (elemInfo.GetPayloadSize() != 0)
|
|
{
|
|
assert(elemInfo.GetEndPos() <= stop);
|
|
|
|
if (elemInfo.GetID() == 0x2E) //TrackEntry ID
|
|
{
|
|
Track* pTrack = NULL;
|
|
|
|
const long status = ParseTrackEntry(elemInfo, pTrack);
|
|
if (status)
|
|
return status;
|
|
|
|
if (pTrack)
|
|
m_trackEntries.push_back(pTrack);
|
|
}
|
|
}
|
|
|
|
pos += elemInfo.GetSize();
|
|
assert(pos <= stop);
|
|
}
|
|
assert(pos == stop);
|
|
|
|
return 0; //success
|
|
}
|
|
|
|
|
|
long Tracks::ParseTrackEntry(const EBMLElement& elemInfo, Track*& pResult) const
|
|
{
|
|
PROFILER_SCOPED_EVENT("webm::Tracks::ParseTrackEntry");
|
|
|
|
if (pResult)
|
|
return -1;
|
|
|
|
IMkvReader* const pReader = m_pSegment->GetReader();
|
|
|
|
long long pos = elemInfo.GetPayloadPos();
|
|
const long long track_stop = elemInfo.GetEndPos();
|
|
|
|
Track::Info info;
|
|
info.type = 0;
|
|
info.number = 0;
|
|
info.uid = 0;
|
|
|
|
Track::Settings v;
|
|
v.start = -1;
|
|
v.size = -1;
|
|
|
|
Track::Settings a;
|
|
a.start = -1;
|
|
a.size = -1;
|
|
|
|
Track::Settings e; //content_encodings_settings;
|
|
e.start = -1;
|
|
e.size = -1;
|
|
|
|
long long lacing = 1; //default is true
|
|
|
|
while (pos < track_stop)
|
|
{
|
|
EBMLElement trackElemInfo;
|
|
long long result = trackElemInfo.Parse(pReader, pos);
|
|
if (result != 0)
|
|
return static_cast<long>(result);
|
|
|
|
const long long start = trackElemInfo.GetPayloadPos();
|
|
const long long size = trackElemInfo.GetPayloadSize();
|
|
|
|
if (size < 0)
|
|
return E_FILE_FORMAT_INVALID;
|
|
|
|
long long id = trackElemInfo.GetID();
|
|
if (id == 0x60) // VideoSettings ID
|
|
{
|
|
v.start = start;
|
|
v.size = size;
|
|
}
|
|
else if (id == 0x61) // AudioSettings ID
|
|
{
|
|
a.start = start;
|
|
a.size = size;
|
|
}
|
|
else if (id == 0x2D80) // ContentEncodings ID
|
|
{
|
|
e.start = start;
|
|
e.size = size;
|
|
}
|
|
else if (id == 0x33C5) //Track UID
|
|
{
|
|
if (size > 8)
|
|
return E_FILE_FORMAT_INVALID;
|
|
|
|
info.uid = 0;
|
|
|
|
long long pos_ = start;
|
|
const long long pos_end = start + size;
|
|
|
|
while (pos_ != pos_end)
|
|
{
|
|
unsigned char b;
|
|
|
|
const int status = pReader->Read(pos_, 1, &b);
|
|
|
|
if (status)
|
|
return status;
|
|
|
|
info.uid <<= 8;
|
|
info.uid |= b;
|
|
|
|
++pos_;
|
|
}
|
|
}
|
|
else if (id == 0x57) //Track Number
|
|
{
|
|
const long long num = UnserializeUInt(pReader, start, size);
|
|
|
|
if ((num <= 0) || (num > 127))
|
|
return E_FILE_FORMAT_INVALID;
|
|
|
|
info.number = static_cast<long>(num);
|
|
}
|
|
else if (id == 0x03) //Track Type
|
|
{
|
|
const long long type = UnserializeUInt(pReader, start, size);
|
|
|
|
if ((type <= 0) || (type > 254))
|
|
return E_FILE_FORMAT_INVALID;
|
|
|
|
info.type = static_cast<long>(type);
|
|
}
|
|
else if (id == 0x136E) //Track Name
|
|
{
|
|
const long status = UnserializeString(
|
|
pReader,
|
|
start,
|
|
size,
|
|
info.nameAsUTF8);
|
|
|
|
if (status)
|
|
return status;
|
|
}
|
|
else if (id == 0x06) //CodecID
|
|
{
|
|
const long status = UnserializeString(
|
|
pReader,
|
|
start,
|
|
size,
|
|
info.codecId);
|
|
|
|
if (status)
|
|
return status;
|
|
}
|
|
else if (id == 0x1C) //lacing
|
|
{
|
|
lacing = UnserializeUInt(pReader, start, size);
|
|
|
|
if ((lacing < 0) || (lacing > 1))
|
|
return E_FILE_FORMAT_INVALID;
|
|
}
|
|
else if (id == 0x23A2) //Codec Private
|
|
{
|
|
info.codecPrivate.clear();
|
|
|
|
const size_t buflen = static_cast<size_t>(size);
|
|
|
|
if (buflen)
|
|
{
|
|
info.codecPrivate.reserve(buflen);
|
|
|
|
if (info.codecPrivate.capacity() < buflen)
|
|
return -1;
|
|
|
|
info.codecPrivate.resize(buflen);
|
|
const int status = pReader->Read(start, buflen, &info.codecPrivate[0]);
|
|
if (status)
|
|
{
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
else if (id == 0x058688) //Codec Name
|
|
{
|
|
const long status = UnserializeString(
|
|
pReader,
|
|
start,
|
|
size,
|
|
info.codecNameAsUTF8);
|
|
|
|
if (status)
|
|
return status;
|
|
}
|
|
|
|
pos += trackElemInfo.GetSize(); //consume payload
|
|
assert(pos <= track_stop);
|
|
}
|
|
|
|
assert(pos == track_stop);
|
|
|
|
if (info.number <= 0) //not specified
|
|
return E_FILE_FORMAT_INVALID;
|
|
|
|
if (GetTrackByNumber(info.number))
|
|
return E_FILE_FORMAT_INVALID;
|
|
|
|
if (info.type <= 0) //not specified
|
|
return E_FILE_FORMAT_INVALID;
|
|
|
|
info.lacing = (lacing > 0) ? true : false;
|
|
|
|
if (info.type == Track::kVideo)
|
|
{
|
|
if (v.start < 0)
|
|
return E_FILE_FORMAT_INVALID;
|
|
|
|
if (a.start >= 0)
|
|
return E_FILE_FORMAT_INVALID;
|
|
|
|
info.settings = v;
|
|
|
|
VideoTrack* pTrack = NULL;
|
|
|
|
const long status = VideoTrack::Parse(m_pSegment,
|
|
info,
|
|
elemInfo.GetPos(),
|
|
elemInfo.GetSize(),
|
|
pTrack);
|
|
|
|
if (status)
|
|
return status;
|
|
|
|
pResult = pTrack;
|
|
assert(pResult);
|
|
|
|
if (e.start >= 0)
|
|
pResult->ParseContentEncodingsEntry(e.start, e.size);
|
|
}
|
|
else if (info.type == Track::kAudio)
|
|
{
|
|
if (a.start < 0)
|
|
return E_FILE_FORMAT_INVALID;
|
|
|
|
if (v.start >= 0)
|
|
return E_FILE_FORMAT_INVALID;
|
|
|
|
info.settings = a;
|
|
|
|
AudioTrack* pTrack = NULL;
|
|
|
|
const long status = AudioTrack::Parse(m_pSegment,
|
|
info,
|
|
elemInfo.GetPos(),
|
|
elemInfo.GetSize(),
|
|
pTrack);
|
|
|
|
if (status)
|
|
return status;
|
|
|
|
pResult = pTrack;
|
|
assert(pResult);
|
|
|
|
if (e.start >= 0)
|
|
pResult->ParseContentEncodingsEntry(e.start, e.size);
|
|
}
|
|
else
|
|
{
|
|
// neither video nor audio - probably metadata
|
|
|
|
if (a.start >= 0)
|
|
return E_FILE_FORMAT_INVALID;
|
|
|
|
if (v.start >= 0)
|
|
return E_FILE_FORMAT_INVALID;
|
|
|
|
if (e.start >= 0)
|
|
return E_FILE_FORMAT_INVALID;
|
|
|
|
info.settings.start = -1;
|
|
info.settings.size = 0;
|
|
|
|
Track* pTrack = NULL;
|
|
|
|
const long status = Track::Create(m_pSegment,
|
|
info,
|
|
elemInfo.GetPos(),
|
|
elemInfo.GetSize(),
|
|
pTrack);
|
|
|
|
if (status)
|
|
return status;
|
|
|
|
pResult = pTrack;
|
|
assert(pResult);
|
|
}
|
|
|
|
return 0; //success
|
|
}
|
|
|
|
|
|
const Track* Tracks::GetTrackByNumber(long tn) const
|
|
{
|
|
PROFILER_SCOPED_EVENT("webm::Tracks::GetTrackByNumber");
|
|
|
|
if (tn < 0)
|
|
return NULL;
|
|
|
|
TrackVect_t::const_iterator first = m_trackEntries.begin();
|
|
TrackVect_t::const_iterator last = m_trackEntries.end();
|
|
|
|
for (; first != last; ++first)
|
|
{
|
|
Track* const pTrack = *first;
|
|
|
|
if (pTrack == NULL)
|
|
continue;
|
|
|
|
if (tn == pTrack->GetNumber())
|
|
return pTrack;
|
|
}
|
|
|
|
return NULL; //not found
|
|
}
|
|
|
|
|
|
const Track* Tracks::GetTrackByIndex(unsigned long idx) const
|
|
{
|
|
if (idx >= m_trackEntries.size())
|
|
return NULL;
|
|
|
|
return m_trackEntries[idx];
|
|
}
|
|
|
|
const Track* Tracks::GetTrackByType(Track::Type type) const
|
|
{
|
|
TrackVect_t::const_iterator first = m_trackEntries.begin();
|
|
TrackVect_t::const_iterator last = m_trackEntries.end();
|
|
|
|
for (; first != last; ++first)
|
|
{
|
|
Track* const pTrack = *first;
|
|
|
|
if (pTrack == NULL)
|
|
continue;
|
|
|
|
if (type == pTrack->GetType())
|
|
return pTrack;
|
|
}
|
|
return NULL; //not found
|
|
}
|
|
|
|
} // namespace mkvparser
|