From 2fc496a0d6df18875cf202634e549a2351f0a4eb Mon Sep 17 00:00:00 2001 From: Matthew Heaney Date: Tue, 9 Oct 2012 12:08:33 -0700 Subject: [PATCH] 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 --- mkvparser.cpp | 266 ++++++++++++++++++++++++++++++++------------------ mkvparser.hpp | 13 ++- 2 files changed, 183 insertions(+), 96 deletions(-) diff --git a/mkvparser.cpp b/mkvparser.cpp index 4e8da47..fcf961c 100644 --- a/mkvparser.cpp +++ b/mkvparser.cpp @@ -4760,6 +4760,35 @@ Track::~Track() 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(): nameAsUTF8(NULL), codecId(NULL), @@ -5098,6 +5127,31 @@ long Track::GetNext( 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* Track::GetContentEncodingByIndex(unsigned long idx) const { const ptrdiff_t count = @@ -5221,15 +5275,15 @@ VideoTrack::VideoTrack( long VideoTrack::Parse( Segment* pSegment, - const Info& i, - long long elem_st, - long long elem_sz, - VideoTrack*& pTrack) + const Info& info, + long long element_start, + long long element_size, + VideoTrack*& pResult) { - if (pTrack) + if (pResult) return -1; - if (i.type != Track::kVideo) + if (info.type != Track::kVideo) return -1; long long width = 0; @@ -5238,7 +5292,7 @@ long VideoTrack::Parse( IMkvReader* const pReader = pSegment->m_pReader; - const Settings& s = i.settings; + const Settings& s = info.settings; assert(s.start >= 0); assert(s.size >= 0); @@ -5296,36 +5350,35 @@ long VideoTrack::Parse( 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) 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; + } pTrack->m_width = width; pTrack->m_height = height; pTrack->m_rate = rate; + pResult = pTrack; return 0; //success } bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const { - assert(pBlockEntry); - - const Block* const pBlock = pBlockEntry->GetBlock(); - assert(pBlock); - assert(pBlock->GetTrackNumber() == m_info.number); - - return pBlock->IsKey(); + return Track::VetEntry(pBlockEntry) && pBlockEntry->GetBlock()->IsKey(); } - long VideoTrack::Seek( long long time_ns, const BlockEntry*& pResult) const @@ -5459,20 +5512,20 @@ AudioTrack::AudioTrack( long AudioTrack::Parse( Segment* pSegment, - const Info& i, - long long elem_st, - long long elem_sz, - AudioTrack*& pTrack) + const Info& info, + long long element_start, + long long element_size, + AudioTrack*& pResult) { - if (pTrack) + if (pResult) return -1; - if (i.type != Track::kAudio) + if (info.type != Track::kAudio) return -1; IMkvReader* const pReader = pSegment->m_pReader; - const Settings& s = i.settings; + const Settings& s = info.settings; assert(s.start >= 0); assert(s.size >= 0); @@ -5481,7 +5534,7 @@ long AudioTrack::Parse( const long long stop = pos + s.size; - double rate = 8000.0; + double rate = 8000.0; // MKV default long long channels = 1; long long bit_depth = 0; @@ -5530,36 +5583,30 @@ long AudioTrack::Parse( 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) return -1; //generic error - const int status = i.Copy(pTrack->m_info); + const int status = info.Copy(pTrack->m_info); if (status) + { + delete pTrack; return status; + } pTrack->m_rate = rate; pTrack->m_channels = channels; pTrack->m_bitDepth = bit_depth; + pResult = pTrack; 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 long time_ns, const BlockEntry*& pResult) const @@ -5791,11 +5838,11 @@ unsigned long Tracks::GetTracksCount() const long Tracks::ParseTrackEntry( long long track_start, long long track_size, - long long elem_st, - long long elem_sz, - Track*& pTrack) const + long long element_start, + long long element_size, + Track*& pResult) const { - if (pTrack) + if (pResult) return -1; IMkvReader* const pReader = m_pSegment->m_pReader; @@ -5803,11 +5850,11 @@ long Tracks::ParseTrackEntry( long long pos = track_start; const long long track_stop = track_start + track_size; - Track::Info i; + Track::Info info; - i.type = 0; - i.number = 0; - i.uid = 0; + info.type = 0; + info.number = 0; + info.uid = 0; Track::Settings v; v.start = -1; @@ -5868,7 +5915,7 @@ long Tracks::ParseTrackEntry( if ((size <= 0) || (size > 8)) return E_FILE_FORMAT_INVALID; - i.uid = 0; + info.uid = 0; long long pos_ = start; const long long pos_end = start + size; @@ -5882,8 +5929,8 @@ long Tracks::ParseTrackEntry( if (status) return status; - i.uid <<= 8; - i.uid |= b; + info.uid <<= 8; + info.uid |= b; ++pos_; } @@ -5895,7 +5942,7 @@ long Tracks::ParseTrackEntry( if ((num <= 0) || (num > 127)) return E_FILE_FORMAT_INVALID; - i.number = static_cast(num); + info.number = static_cast(num); } else if (id == 0x03) //Track Type { @@ -5904,7 +5951,7 @@ long Tracks::ParseTrackEntry( if ((type <= 0) || (type > 254)) return E_FILE_FORMAT_INVALID; - i.type = static_cast(type); + info.type = static_cast(type); } else if (id == 0x136E) //Track Name { @@ -5912,7 +5959,7 @@ long Tracks::ParseTrackEntry( pReader, pos, size, - i.nameAsUTF8); + info.nameAsUTF8); if (status) return status; @@ -5923,7 +5970,7 @@ long Tracks::ParseTrackEntry( pReader, pos, size, - i.codecId); + info.codecId); if (status) return status; @@ -5937,9 +5984,9 @@ long Tracks::ParseTrackEntry( } else if (id == 0x23A2) //Codec Private { - delete[] i.codecPrivate; - i.codecPrivate = NULL; - i.codecPrivateSize = 0; + delete[] info.codecPrivate; + info.codecPrivate = NULL; + info.codecPrivateSize = 0; if (size <= 0) return E_FILE_FORMAT_INVALID; @@ -5961,8 +6008,8 @@ long Tracks::ParseTrackEntry( return status; } - i.codecPrivate = buf; - i.codecPrivateSize = buflen; + info.codecPrivate = buf; + info.codecPrivateSize = buflen; } else if (id == 0x058688) //Codec Name { @@ -5970,7 +6017,7 @@ long Tracks::ParseTrackEntry( pReader, pos, size, - i.codecNameAsUTF8); + info.codecNameAsUTF8); if (status) return status; @@ -5982,29 +6029,18 @@ long Tracks::ParseTrackEntry( assert(pos == track_stop); - if (i.number <= 0) //not specified + if (info.number <= 0) //not specified return E_FILE_FORMAT_INVALID; - if (GetTrackByNumber(i.number)) + if (GetTrackByNumber(info.number)) return E_FILE_FORMAT_INVALID; - if (i.type <= 0) //not specified + if (info.type <= 0) //not specified return E_FILE_FORMAT_INVALID; - if ((i.type != Track::kVideo) && (i.type != Track::kAudio)) - { - //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. + info.lacing = (lacing > 0) ? true : false; - return 0; //no error - } - - i.lacing = (lacing > 0) ? true : false; - - long status; - - if (i.type == Track::kVideo) + if (info.type == Track::kVideo) { if (v.start < 0) return E_FILE_FORMAT_INVALID; @@ -6012,38 +6048,82 @@ long Tracks::ParseTrackEntry( if (a.start >= 0) 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); - pTrack = p; + const long status = VideoTrack::Parse(m_pSegment, + 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) return E_FILE_FORMAT_INVALID; if (v.start >= 0) 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); - pTrack = p; + const long status = AudioTrack::Parse(m_pSegment, + 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 + { + // neither video nor audio - probably metadata - if (status) - return status; + if (a.start >= 0) + return E_FILE_FORMAT_INVALID; - assert(pTrack); + if (v.start >= 0) + return E_FILE_FORMAT_INVALID; - if (e.start >= 0) - pTrack->ParseContentEncodingsEntry(e.start, e.size); + 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 } diff --git a/mkvparser.hpp b/mkvparser.hpp index ecd230b..4247f52 100644 --- a/mkvparser.hpp +++ b/mkvparser.hpp @@ -315,6 +315,14 @@ class Track Track& operator=(const Track&); public: + class Info; + static long Create( + Segment*, + const Info&, + long long element_start, + long long element_size, + Track*&); + enum Type { kVideo = 1, kAudio = 2 }; Segment* const m_pSegment; @@ -366,8 +374,8 @@ public: long GetFirst(const BlockEntry*&) const; long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const; - virtual bool VetEntry(const BlockEntry*) const = 0; - virtual long Seek(long long time_ns, const BlockEntry*&) const = 0; + virtual bool VetEntry(const BlockEntry*) const; + virtual long Seek(long long time_ns, const BlockEntry*&) const; const ContentEncoding* GetContentEncodingByIndex(unsigned long idx) const; unsigned long GetContentEncodingCount() const; @@ -452,7 +460,6 @@ public: double GetSamplingRate() const; long long GetChannels() const; long long GetBitDepth() const; - bool VetEntry(const BlockEntry*) const; long Seek(long long time_ns, const BlockEntry*&) const; private: