mkvparser: create generic track objects

Formerly, it was only possible to create instances of
the Track subclasses, VideoTrack and AudioTrack.  However,
we now populate WebM files with WebVTT metadata blocks,
so we must allow for a third kind of track object.

We now enable instances of type Track to be created,
by providing a new factory function, Track::Create, and
making all Track methods non-pure and with a generic
implementation.

Change-Id: I7d4c965eb566b9fc2f5ceefe1d43723cf8c1e5f0
This commit is contained in:
Matthew Heaney
2012-10-09 12:08:33 -07:00
parent cb8899a920
commit 2fc496a0d6
2 changed files with 183 additions and 96 deletions

View File

@@ -4760,6 +4760,35 @@ Track::~Track()
delete [] content_encoding_entries_; delete [] content_encoding_entries_;
} }
long Track::Create(
Segment* pSegment,
const Info& info,
long long element_start,
long long element_size,
Track*& pResult)
{
if (pResult)
return -1;
Track* const pTrack = new (std::nothrow) Track(pSegment,
element_start,
element_size);
if (pTrack == NULL)
return -1; //generic error
const int status = info.Copy(pTrack->m_info);
if (status) // error
{
delete pTrack;
return status;
}
pResult = pTrack;
return 0; //success
}
Track::Info::Info(): Track::Info::Info():
nameAsUTF8(NULL), nameAsUTF8(NULL),
codecId(NULL), codecId(NULL),
@@ -5098,6 +5127,31 @@ long Track::GetNext(
return 1; return 1;
} }
bool Track::VetEntry(const BlockEntry* pBlockEntry) const
{
assert(pBlockEntry);
const Block* const pBlock = pBlockEntry->GetBlock();
assert(pBlock);
assert(pBlock->GetTrackNumber() == m_info.number);
// This function is used during a seek to determine whether the
// frame is a valid seek target. This default function simply
// returns true, which means all frames are valid seek targets.
// It gets overridden by the VideoTrack class, because only video
// keyframes can be used as seek target.
return true;
}
long Track::Seek(
long long /* time_ns */ ,
const BlockEntry*& pResult) const
{
// TODO(matthewjheaney): need to implement this?
pResult = NULL;
return -1; // generic error
}
const ContentEncoding* const ContentEncoding*
Track::GetContentEncodingByIndex(unsigned long idx) const { Track::GetContentEncodingByIndex(unsigned long idx) const {
const ptrdiff_t count = const ptrdiff_t count =
@@ -5221,15 +5275,15 @@ VideoTrack::VideoTrack(
long VideoTrack::Parse( long VideoTrack::Parse(
Segment* pSegment, Segment* pSegment,
const Info& i, const Info& info,
long long elem_st, long long element_start,
long long elem_sz, long long element_size,
VideoTrack*& pTrack) VideoTrack*& pResult)
{ {
if (pTrack) if (pResult)
return -1; return -1;
if (i.type != Track::kVideo) if (info.type != Track::kVideo)
return -1; return -1;
long long width = 0; long long width = 0;
@@ -5238,7 +5292,7 @@ long VideoTrack::Parse(
IMkvReader* const pReader = pSegment->m_pReader; IMkvReader* const pReader = pSegment->m_pReader;
const Settings& s = i.settings; const Settings& s = info.settings;
assert(s.start >= 0); assert(s.start >= 0);
assert(s.size >= 0); assert(s.size >= 0);
@@ -5296,36 +5350,35 @@ long VideoTrack::Parse(
assert(pos == stop); assert(pos == stop);
pTrack = new (std::nothrow) VideoTrack(pSegment, elem_st, elem_sz); VideoTrack* const pTrack = new (std::nothrow) VideoTrack(pSegment,
element_start,
element_size);
if (pTrack == NULL) if (pTrack == NULL)
return -1; //generic error return -1; //generic error
const int status = i.Copy(pTrack->m_info); const int status = info.Copy(pTrack->m_info);
if (status) if (status) // error
{
delete pTrack;
return status; return status;
}
pTrack->m_width = width; pTrack->m_width = width;
pTrack->m_height = height; pTrack->m_height = height;
pTrack->m_rate = rate; pTrack->m_rate = rate;
pResult = pTrack;
return 0; //success return 0; //success
} }
bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const
{ {
assert(pBlockEntry); return Track::VetEntry(pBlockEntry) && pBlockEntry->GetBlock()->IsKey();
const Block* const pBlock = pBlockEntry->GetBlock();
assert(pBlock);
assert(pBlock->GetTrackNumber() == m_info.number);
return pBlock->IsKey();
} }
long VideoTrack::Seek( long VideoTrack::Seek(
long long time_ns, long long time_ns,
const BlockEntry*& pResult) const const BlockEntry*& pResult) const
@@ -5459,20 +5512,20 @@ AudioTrack::AudioTrack(
long AudioTrack::Parse( long AudioTrack::Parse(
Segment* pSegment, Segment* pSegment,
const Info& i, const Info& info,
long long elem_st, long long element_start,
long long elem_sz, long long element_size,
AudioTrack*& pTrack) AudioTrack*& pResult)
{ {
if (pTrack) if (pResult)
return -1; return -1;
if (i.type != Track::kAudio) if (info.type != Track::kAudio)
return -1; return -1;
IMkvReader* const pReader = pSegment->m_pReader; IMkvReader* const pReader = pSegment->m_pReader;
const Settings& s = i.settings; const Settings& s = info.settings;
assert(s.start >= 0); assert(s.start >= 0);
assert(s.size >= 0); assert(s.size >= 0);
@@ -5481,7 +5534,7 @@ long AudioTrack::Parse(
const long long stop = pos + s.size; const long long stop = pos + s.size;
double rate = 8000.0; double rate = 8000.0; // MKV default
long long channels = 1; long long channels = 1;
long long bit_depth = 0; long long bit_depth = 0;
@@ -5530,36 +5583,30 @@ long AudioTrack::Parse(
assert(pos == stop); assert(pos == stop);
pTrack = new (std::nothrow) AudioTrack(pSegment, elem_st, elem_sz); AudioTrack* const pTrack = new (std::nothrow) AudioTrack(pSegment,
element_start,
element_size);
if (pTrack == NULL) if (pTrack == NULL)
return -1; //generic error return -1; //generic error
const int status = i.Copy(pTrack->m_info); const int status = info.Copy(pTrack->m_info);
if (status) if (status)
{
delete pTrack;
return status; return status;
}
pTrack->m_rate = rate; pTrack->m_rate = rate;
pTrack->m_channels = channels; pTrack->m_channels = channels;
pTrack->m_bitDepth = bit_depth; pTrack->m_bitDepth = bit_depth;
pResult = pTrack;
return 0; //success return 0; //success
} }
bool AudioTrack::VetEntry(const BlockEntry* pBlockEntry) const
{
assert(pBlockEntry);
const Block* const pBlock = pBlockEntry->GetBlock();
assert(pBlock);
assert(pBlock->GetTrackNumber() == m_info.number);
return true;
}
long AudioTrack::Seek( long AudioTrack::Seek(
long long time_ns, long long time_ns,
const BlockEntry*& pResult) const const BlockEntry*& pResult) const
@@ -5791,11 +5838,11 @@ unsigned long Tracks::GetTracksCount() const
long Tracks::ParseTrackEntry( long Tracks::ParseTrackEntry(
long long track_start, long long track_start,
long long track_size, long long track_size,
long long elem_st, long long element_start,
long long elem_sz, long long element_size,
Track*& pTrack) const Track*& pResult) const
{ {
if (pTrack) if (pResult)
return -1; return -1;
IMkvReader* const pReader = m_pSegment->m_pReader; IMkvReader* const pReader = m_pSegment->m_pReader;
@@ -5803,11 +5850,11 @@ long Tracks::ParseTrackEntry(
long long pos = track_start; long long pos = track_start;
const long long track_stop = track_start + track_size; const long long track_stop = track_start + track_size;
Track::Info i; Track::Info info;
i.type = 0; info.type = 0;
i.number = 0; info.number = 0;
i.uid = 0; info.uid = 0;
Track::Settings v; Track::Settings v;
v.start = -1; v.start = -1;
@@ -5868,7 +5915,7 @@ long Tracks::ParseTrackEntry(
if ((size <= 0) || (size > 8)) if ((size <= 0) || (size > 8))
return E_FILE_FORMAT_INVALID; return E_FILE_FORMAT_INVALID;
i.uid = 0; info.uid = 0;
long long pos_ = start; long long pos_ = start;
const long long pos_end = start + size; const long long pos_end = start + size;
@@ -5882,8 +5929,8 @@ long Tracks::ParseTrackEntry(
if (status) if (status)
return status; return status;
i.uid <<= 8; info.uid <<= 8;
i.uid |= b; info.uid |= b;
++pos_; ++pos_;
} }
@@ -5895,7 +5942,7 @@ long Tracks::ParseTrackEntry(
if ((num <= 0) || (num > 127)) if ((num <= 0) || (num > 127))
return E_FILE_FORMAT_INVALID; return E_FILE_FORMAT_INVALID;
i.number = static_cast<long>(num); info.number = static_cast<long>(num);
} }
else if (id == 0x03) //Track Type else if (id == 0x03) //Track Type
{ {
@@ -5904,7 +5951,7 @@ long Tracks::ParseTrackEntry(
if ((type <= 0) || (type > 254)) if ((type <= 0) || (type > 254))
return E_FILE_FORMAT_INVALID; return E_FILE_FORMAT_INVALID;
i.type = static_cast<long>(type); info.type = static_cast<long>(type);
} }
else if (id == 0x136E) //Track Name else if (id == 0x136E) //Track Name
{ {
@@ -5912,7 +5959,7 @@ long Tracks::ParseTrackEntry(
pReader, pReader,
pos, pos,
size, size,
i.nameAsUTF8); info.nameAsUTF8);
if (status) if (status)
return status; return status;
@@ -5923,7 +5970,7 @@ long Tracks::ParseTrackEntry(
pReader, pReader,
pos, pos,
size, size,
i.codecId); info.codecId);
if (status) if (status)
return status; return status;
@@ -5937,9 +5984,9 @@ long Tracks::ParseTrackEntry(
} }
else if (id == 0x23A2) //Codec Private else if (id == 0x23A2) //Codec Private
{ {
delete[] i.codecPrivate; delete[] info.codecPrivate;
i.codecPrivate = NULL; info.codecPrivate = NULL;
i.codecPrivateSize = 0; info.codecPrivateSize = 0;
if (size <= 0) if (size <= 0)
return E_FILE_FORMAT_INVALID; return E_FILE_FORMAT_INVALID;
@@ -5961,8 +6008,8 @@ long Tracks::ParseTrackEntry(
return status; return status;
} }
i.codecPrivate = buf; info.codecPrivate = buf;
i.codecPrivateSize = buflen; info.codecPrivateSize = buflen;
} }
else if (id == 0x058688) //Codec Name else if (id == 0x058688) //Codec Name
{ {
@@ -5970,7 +6017,7 @@ long Tracks::ParseTrackEntry(
pReader, pReader,
pos, pos,
size, size,
i.codecNameAsUTF8); info.codecNameAsUTF8);
if (status) if (status)
return status; return status;
@@ -5982,29 +6029,18 @@ long Tracks::ParseTrackEntry(
assert(pos == track_stop); assert(pos == track_stop);
if (i.number <= 0) //not specified if (info.number <= 0) //not specified
return E_FILE_FORMAT_INVALID; return E_FILE_FORMAT_INVALID;
if (GetTrackByNumber(i.number)) if (GetTrackByNumber(info.number))
return E_FILE_FORMAT_INVALID; return E_FILE_FORMAT_INVALID;
if (i.type <= 0) //not specified if (info.type <= 0) //not specified
return E_FILE_FORMAT_INVALID; return E_FILE_FORMAT_INVALID;
if ((i.type != Track::kVideo) && (i.type != Track::kAudio)) info.lacing = (lacing > 0) ? true : false;
{
//TODO(matthewjheaney): go ahead and create a "generic" track
//object, so that GetTrackByXXX always returns something, even
//if the object it returns has a type that is not kVideo or kAudio.
return 0; //no error if (info.type == Track::kVideo)
}
i.lacing = (lacing > 0) ? true : false;
long status;
if (i.type == Track::kVideo)
{ {
if (v.start < 0) if (v.start < 0)
return E_FILE_FORMAT_INVALID; return E_FILE_FORMAT_INVALID;
@@ -6012,38 +6048,82 @@ long Tracks::ParseTrackEntry(
if (a.start >= 0) if (a.start >= 0)
return E_FILE_FORMAT_INVALID; return E_FILE_FORMAT_INVALID;
i.settings = v; info.settings = v;
VideoTrack* p = NULL; VideoTrack* pTrack = NULL;
status = VideoTrack::Parse(m_pSegment, i, elem_st, elem_sz, p); const long status = VideoTrack::Parse(m_pSegment,
pTrack = p; info,
element_start,
element_size,
pTrack);
if (status)
return status;
pResult = pTrack;
assert(pResult);
if (e.start >= 0)
pResult->ParseContentEncodingsEntry(e.start, e.size);
} }
else else if (info.type == Track::kAudio)
{ {
assert(i.type == Track::kAudio);
if (a.start < 0) if (a.start < 0)
return E_FILE_FORMAT_INVALID; return E_FILE_FORMAT_INVALID;
if (v.start >= 0) if (v.start >= 0)
return E_FILE_FORMAT_INVALID; return E_FILE_FORMAT_INVALID;
i.settings = a; info.settings = a;
AudioTrack* p = NULL; AudioTrack* pTrack = NULL;
status = AudioTrack::Parse(m_pSegment, i, elem_st, elem_sz, p); const long status = AudioTrack::Parse(m_pSegment,
pTrack = p; info,
} element_start,
element_size,
pTrack);
if (status) if (status)
return status; return status;
assert(pTrack); pResult = pTrack;
assert(pResult);
if (e.start >= 0) if (e.start >= 0)
pTrack->ParseContentEncodingsEntry(e.start, e.size); 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,
element_start,
element_size,
pTrack);
if (status)
return status;
pResult = pTrack;
assert(pResult);
}
return 0; //success return 0; //success
} }

View File

@@ -315,6 +315,14 @@ class Track
Track& operator=(const Track&); Track& operator=(const Track&);
public: public:
class Info;
static long Create(
Segment*,
const Info&,
long long element_start,
long long element_size,
Track*&);
enum Type { kVideo = 1, kAudio = 2 }; enum Type { kVideo = 1, kAudio = 2 };
Segment* const m_pSegment; Segment* const m_pSegment;
@@ -366,8 +374,8 @@ public:
long GetFirst(const BlockEntry*&) const; long GetFirst(const BlockEntry*&) const;
long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const; long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const;
virtual bool VetEntry(const BlockEntry*) const = 0; virtual bool VetEntry(const BlockEntry*) const;
virtual long Seek(long long time_ns, const BlockEntry*&) const = 0; virtual long Seek(long long time_ns, const BlockEntry*&) const;
const ContentEncoding* GetContentEncodingByIndex(unsigned long idx) const; const ContentEncoding* GetContentEncodingByIndex(unsigned long idx) const;
unsigned long GetContentEncodingCount() const; unsigned long GetContentEncodingCount() const;
@@ -452,7 +460,6 @@ public:
double GetSamplingRate() const; double GetSamplingRate() const;
long long GetChannels() const; long long GetChannels() const;
long long GetBitDepth() const; long long GetBitDepth() const;
bool VetEntry(const BlockEntry*) const;
long Seek(long long time_ns, const BlockEntry*&) const; long Seek(long long time_ns, const BlockEntry*&) const;
private: private: