JD2022-TU1/main/extern/libwebm/custom/mkvparser/segment.cpp

540 lines
14 KiB
C++

#include "segment.hpp"
#include "tracks.hpp"
#include "segmentinfo.hpp"
#include "cues.hpp"
#include "seekhead.hpp"
#include "imkvreader.hpp"
#include "ebmlreadhelpers.hpp"
#include "cluster.hpp"
#include <stddef.h>
#include <assert.h>
#include <algorithm>
namespace mkvparser
{
namespace
{
// ClusterVect_t comparison functors
struct ClusterPosLT
{
ClusterPosLT(long long cluster_off)
: m_cluster_off(cluster_off) {}
bool operator()(const Cluster* lhs, const Cluster* rhs) const
{
if (lhs)
return lhs->GetPosition() < m_cluster_off;
if (rhs)
return rhs->GetPosition() < m_cluster_off;
return false;
}
long long m_cluster_off;
};
struct ClusterLT
{
bool operator()(const Cluster* lhs, const Cluster* rhs) const
{
assert(lhs);
assert(rhs);
return lhs->GetPosition() < rhs->GetPosition();
}
};
} // namespace
Segment::Segment(IMkvReader* pReader, const EBMLElement& _ebmlInfo)
: m_pReader(pReader)
, m_segInfo(_ebmlInfo)
, m_firstClusterPos(-1)
, m_pSeekHead(NULL)
, m_pInfo(NULL)
, m_pTracks(NULL)
, m_pCues(NULL)
{
const size_t clusterVectReserveSize = 2048;
m_clusters.reserve(clusterVectReserveSize);
}
Segment::~Segment()
{
m_clusters.clear();
delete m_pTracks;
delete m_pInfo;
delete m_pCues;
delete m_pSeekHead;
}
long long Segment::CreateInstance(
IMkvReader* pReader,
long long pos,
Segment*& pSegment)
{
assert(pReader);
assert(pos >= 0);
pSegment = NULL;
long long total, available;
const long status = pReader->Length(&total, &available);
if (status < 0) //error
return status;
if (available < 0)
return -1;
if ((total >= 0) && (available > total))
return -1;
//I would assume that in practice this loop would execute
//exactly once, but we allow for other elements (e.g. Void)
//to immediately follow the EBML header.
//There is an implied "give up early" by only parsing up
//to the available limit.
for (;;)
{
EBMLElement elemInfo;
long long result = elemInfo.Parse(pReader, pos);
if (result != 0)
return result;
if (elemInfo.GetID() == 0x08538067) //Segment ID
{
pSegment = new Segment(pReader, elemInfo);
if (pSegment == 0)
return -1; //generic error
return 0; //success
}
if ((total >= 0) && ((elemInfo.GetPayloadPos() + elemInfo.GetPayloadSize()) > total))
return E_FILE_FORMAT_INVALID;
if ((elemInfo.GetPayloadPos() + elemInfo.GetPayloadSize()) > available)
return E_BUFFER_NOT_FULL;
pos += elemInfo.GetSize(); // consume element
}
}
long Segment::Load()
{
assert(m_clusters.empty());
assert(!m_clusters.IsClusterLimitOn());
//Outermost (level 0) segment object has been constructed,
//and pos designates start of payload. We need to find the
//inner (level 1) elements.
const long long header_status = ParseHeaders();
if (header_status < 0) //error
return static_cast<long>(header_status);
if (header_status > 0) //underflow
return E_BUFFER_NOT_FULL;
assert(m_pInfo);
assert(m_pTracks);
Cluster* pCurr = NULL;
long status = ParseFirstCluster(pCurr);
while(status == 0 && pCurr)
{
status = ParseNextCluster(pCurr, pCurr);
}
return status;
}
long long Segment::ParseHeaders()
{
// the function must be called only once
assert(m_pSeekHead == NULL);
assert(m_pInfo == NULL);
assert(m_pTracks == NULL);
assert(m_pCues == NULL);
//Outermost (level 0) segment object has been constructed,
//and pos designates start of payload. We need to find the
//inner (level 1) elements.
long long total, available;
const int status = m_pReader->Length(&total, &available);
if (status < 0) //error
return status;
assert((total < 0) || (available <= total));
long long pos = m_segInfo.GetPayloadPos();
const long long segment_stop = m_segInfo.GetEndPos();
assert((segment_stop < 0) || (total < 0) || (segment_stop <= total));
assert((segment_stop < 0) || (pos <= segment_stop));
for (;;)
{
if ((total >= 0) && (pos >= total))
break;
if ((segment_stop >= 0) && (pos >= segment_stop))
break;
EBMLElement elemInfo;
long long result = elemInfo.Parse(m_pReader, pos);
if (result != 0)
return result;
if (elemInfo.GetID() == 0x0F43B675) //Cluster ID, stop parsing
{
m_firstClusterPos = pos;
break;
}
if ((segment_stop >= 0) && (elemInfo.GetEndPos() > segment_stop))
return E_FILE_FORMAT_INVALID;
//We read EBML elements either in total or nothing at all.
if (elemInfo.GetEndPos() > available)
return E_BUFFER_NOT_FULL;
if (elemInfo.GetID() == 0x0549A966) //Segment Info ID
{
if (m_pInfo)
return E_FILE_FORMAT_INVALID;
m_pInfo = new SegmentInfo(this, elemInfo);
if (m_pInfo == NULL)
return -1;
const long status = m_pInfo->Parse();
if (status)
return status;
}
else if (elemInfo.GetID() == 0x0654AE6B) //Tracks ID
{
if (m_pTracks)
return E_FILE_FORMAT_INVALID;
m_pTracks = new Tracks(this, elemInfo);
if (m_pTracks == NULL)
return -1;
const long status = m_pTracks->Parse();
if (status)
return status;
}
else if (elemInfo.GetID() == 0x0C53BB6B) //Cues ID
{
if (m_pCues == NULL)
{
m_pCues = new Cues(this, elemInfo);
if (m_pCues == NULL)
return -1;
}
}
else if (elemInfo.GetID() == 0x014D9B74) //SeekHead ID
{
if (m_pSeekHead == NULL)
{
m_pSeekHead = new SeekHead(this, elemInfo);
if (m_pSeekHead == NULL)
return -1;
const long status = m_pSeekHead->Parse();
if (status)
return status;
}
}
pos += elemInfo.GetSize(); //consume payload
}
assert((segment_stop < 0) || (pos <= segment_stop));
if (m_pInfo == NULL) //TODO: liberalize this behavior
return E_FILE_FORMAT_INVALID;
if (m_pTracks == NULL)
return E_FILE_FORMAT_INVALID;
return 0; //success
}
long Segment::ParseCues(long long off)
{
if (m_pCues)
return 0; //success
if (off < 0)
return -1;
long long total, avail;
const int status = m_pReader->Length(&total, &avail);
if (status < 0) //error
return status;
assert((total < 0) || (avail <= total));
long long pos = m_segInfo.GetPayloadPos() + off;
if ((total < 0) || (pos >= total))
return 1; //don't bother parsing cues
const long long segment_stop = m_segInfo.GetEndPos();
EBMLElement elemInfo;
long long result = elemInfo.Parse(m_pReader, pos);
if (result != 0)
return static_cast<long>(result);
if (elemInfo.GetID() != 0x0C53BB6B) //Cues ID
return E_FILE_FORMAT_INVALID;
const long long element_stop = elemInfo.GetEndPos();
if ((segment_stop >= 0) && (element_stop > segment_stop))
return E_FILE_FORMAT_INVALID;
if ((total >= 0) && (element_stop > total))
return 1; //don't bother parsing anymore
if (element_stop > avail)
return E_BUFFER_NOT_FULL;
m_pCues = new Cues(this, elemInfo);
assert(m_pCues); //TODO
return 0; //success
}
long Segment::ParseFirstCluster(ClusterPtr_t& pFirst)
{
//check if it is already there
const Cluster* first = GetFirstParsedCluster();
if (first && first->GetElementStart() == m_firstClusterPos)
{
pFirst = const_cast<Cluster*>(first);
return 0; // success
}
return DoLoadClusterOrCues(m_firstClusterPos, pFirst);
}
long Segment::ParseNextCluster(const ClusterPtrConst_t& pCurr, ClusterPtr_t& pNext)
{
assert(pCurr);
assert(!m_clusters.empty());
assert(pCurr->GetElementSize() > 0);
long long next_start_pos = pCurr->GetElementStart() + pCurr->GetElementSize();
//is the next already there?
const Cluster* next = GetNextParsedCluster(pCurr);
if (next && next->GetElementStart() == next_start_pos)
{
pNext = const_cast<Cluster*>(next);
return 0; // success
}
return DoLoadClusterOrCues(next_start_pos, pNext);
}
long long Segment::GetDuration() const
{
assert(m_pInfo);
return m_pInfo->GetDuration();
}
unsigned long Segment::GetParsedClustersCount() const
{
return static_cast<unsigned long>(m_clusters.size());
}
const Cluster* Segment::GetFirstParsedCluster() const
{
if (m_clusters.empty())
return NULL;
Cluster* const pCluster = m_clusters[0];
assert(pCluster);
return pCluster;
}
const Cluster* Segment::GetLastParsedCluster() const
{
if (m_clusters.empty())
return NULL;
Cluster* const pCluster = m_clusters.back();
assert(pCluster);
return pCluster;
}
const Cluster* Segment::GetNextParsedCluster(const Cluster* pCurr) const
{
assert(!m_clusters.empty());
assert(pCurr);
Clusters::const_iterator it = std::lower_bound(m_clusters.begin(), m_clusters.end(), pCurr, ClusterLT());
assert(it != m_clusters.end());
assert((*it));
assert((*it) == pCurr);
++it;
if(it != m_clusters.end())
return (*it);
return NULL;
}
const BlockEntry* Segment::GetBlock(const CuePoint& cp, const CuePoint::TrackPosition& tp)
{
Cluster* pCluster;
const long long requested_pos = tp.m_pos;
// find the right cluster
Clusters::iterator it = std::lower_bound(m_clusters.begin(), m_clusters.end(), ClusterPtr_t(0), ClusterPosLT(requested_pos));
if (it != m_clusters.end() && (*it) && (*it)->GetPosition() == requested_pos)
{
// found a match
pCluster = *it;
}
else
{
// load the cluster
const long long abs_requested_pos = requested_pos + m_segInfo.GetPayloadPos();
long status = DoLoadClusterOrCues(abs_requested_pos, pCluster);
if(status != 0)
pCluster = 0;
}
if (pCluster)
return pCluster->GetEntry(cp, tp);
return 0;
}
// private functions
long Segment::DoLoadClusterOrCues(long long pos, ClusterPtr_t& pLoadedCluster)
{
long long total, avail;
long status = m_pReader->Length(&total, &avail);
if (status < 0) //error
return status;
assert((total < 0) || (avail <= total));
const long long segment_stop = m_segInfo.GetEndPos();
for (;;)
{
if ((total >= 0) && (pos >= total))
return 1; //no more clusters
if ((segment_stop >= 0) && (pos >= segment_stop))
return 1; //no more clusters
EBMLElement elemInfo;
long long result = elemInfo.Parse(m_pReader, pos);
if (result != 0)
return static_cast<long>(result);
if (elemInfo.GetPayloadSize() == 0) //weird
{
pos += elemInfo.GetSize();
continue;
}
if ((segment_stop >= 0) && (elemInfo.GetEndPos() > segment_stop))
{
return E_FILE_FORMAT_INVALID;
}
if (elemInfo.GetEndPos() > avail)
return E_BUFFER_NOT_FULL;
if (elemInfo.GetID() == 0x0C53BB6B) //Cues ID
{
if (m_pCues == NULL)
{
m_pCues = new Cues(this, elemInfo);
assert(m_pCues); //TODO
}
pos += elemInfo.GetSize(); //consume payload
continue;
}
// not a cluster element
else if (elemInfo.GetID() != 0x0F43B675) //Cluster ID
{
pos += elemInfo.GetSize(); //consume payload
continue;
}
else
{
//We have a cluster.
long long cluster_off = elemInfo.GetPos() - m_segInfo.GetPayloadPos(); //relative pos
// is the cluster already in the list?
Clusters::iterator it = std::lower_bound(m_clusters.begin(), m_clusters.end(), ClusterPtr_t(0), ClusterPosLT(cluster_off));
if(it != m_clusters.end() && (*it) && (*it)->GetPosition() == cluster_off)
{
// already exists
pos += elemInfo.GetSize(); //consume payload
assert((segment_stop < 0) || (pos <= segment_stop));
pLoadedCluster = *it;
return 0; //success
}
else
{
// new cluster needed
Cluster* const pCluster = Cluster::Create(this, elemInfo);
assert(pCluster);
// iterator points where the cluster should be inserted
m_clusters.insert(it, pCluster);
pos += elemInfo.GetSize(); //consume payload
assert((segment_stop < 0) || (pos <= segment_stop));
pLoadedCluster = pCluster;
return 0; //success
}
}
}
}
} // namespace mkvparser