241 lines
5.5 KiB
C++
241 lines
5.5 KiB
C++
#include "track.hpp"
|
|
#include "segment.hpp"
|
|
#include "imkvreader.hpp"
|
|
#include "block.hpp"
|
|
#include "ebmlreadhelpers.hpp"
|
|
#include "contentencoding.hpp"
|
|
#include "mkvprofiler.hpp"
|
|
|
|
#include <stddef.h>
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
|
|
namespace mkvparser
|
|
{
|
|
|
|
Track::Info::Info()
|
|
: type(0)
|
|
, number(0)
|
|
, uid(0)
|
|
, nameAsUTF8(NULL)
|
|
, codecId(NULL)
|
|
, codecNameAsUTF8(NULL)
|
|
, lacing(false)
|
|
{
|
|
settings.start = 0;
|
|
settings.size = 0;
|
|
}
|
|
|
|
Track::Info::~Info()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
void Track::Info::Clear()
|
|
{
|
|
mem::Free(nameAsUTF8);
|
|
nameAsUTF8 = NULL;
|
|
|
|
mem::Free(codecId);
|
|
codecId = NULL;
|
|
|
|
codecPrivate.clear();
|
|
|
|
mem::Free(codecNameAsUTF8);
|
|
codecNameAsUTF8 = NULL;
|
|
}
|
|
|
|
int Track::Info::CopyStr(char* Info::*str, Info& dst_) const
|
|
{
|
|
if (str == static_cast<char* Info::*>(NULL))
|
|
return -1;
|
|
|
|
char*& dst = dst_.*str;
|
|
|
|
if (dst) //should be NULL already
|
|
return -1;
|
|
|
|
const char* const src = this->*str;
|
|
|
|
if (src == NULL)
|
|
return 0;
|
|
|
|
const size_t len = std::strlen(src);
|
|
|
|
dst = static_cast<char*>(mem::Allocate(len+1));
|
|
|
|
if (dst == NULL)
|
|
return -1;
|
|
|
|
std::strcpy(dst, src);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Track::Info::Copy(Info& dst) const
|
|
{
|
|
if (&dst == this)
|
|
return 0;
|
|
|
|
dst.type = type;
|
|
dst.number = number;
|
|
dst.uid = uid;
|
|
dst.lacing = lacing;
|
|
dst.settings = settings;
|
|
|
|
//We now copy the string member variables from src to dst.
|
|
//This involves memory allocation so in principle the operation
|
|
//can fail (indeed, that's why we have Info::Copy), so we must
|
|
//report this to the caller. An error return from this function
|
|
//therefore implies that the copy was only partially successful.
|
|
|
|
if (int status = CopyStr(&Info::nameAsUTF8, dst))
|
|
return status;
|
|
|
|
if (int status = CopyStr(&Info::codecId, dst))
|
|
return status;
|
|
|
|
if (int status = CopyStr(&Info::codecNameAsUTF8, dst))
|
|
return status;
|
|
|
|
dst.codecPrivate = codecPrivate;
|
|
|
|
if (dst.codecPrivate.size() != codecPrivate.size())
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
Track::Track(Segment* pSegment, long long element_start, long long element_size)
|
|
: m_pSegment(pSegment)
|
|
, m_element_start(element_start)
|
|
, m_element_size(element_size)
|
|
{
|
|
}
|
|
|
|
Track::~Track()
|
|
{
|
|
m_info.Clear();
|
|
|
|
ContEncodingVect_t::iterator first = content_encoding_entries_.begin();
|
|
ContEncodingVect_t::iterator last = content_encoding_entries_.end();
|
|
|
|
for (; first != last; ++first)
|
|
{
|
|
delete (*first);
|
|
}
|
|
}
|
|
|
|
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 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
|
|
}
|
|
|
|
const unsigned char* Track::GetCodecPrivate(size_t& size) const
|
|
{
|
|
size = m_info.codecPrivate.size();
|
|
if (size > 0)
|
|
return &m_info.codecPrivate[0];
|
|
return 0;
|
|
}
|
|
|
|
bool Track::VetEntry(const BlockEntry* pBlockEntry) const
|
|
{
|
|
assert(pBlockEntry);
|
|
assert(pBlockEntry->GetBlock());
|
|
assert(pBlockEntry->GetBlock()->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;
|
|
}
|
|
|
|
|
|
const ContentEncoding* Track::GetContentEncodingByIndex(unsigned long idx) const
|
|
{
|
|
if (idx >= content_encoding_entries_.size())
|
|
return NULL;
|
|
|
|
return content_encoding_entries_[idx];
|
|
}
|
|
|
|
unsigned long Track::GetContentEncodingCount() const
|
|
{
|
|
return static_cast<unsigned long>(content_encoding_entries_.size());
|
|
}
|
|
|
|
long Track::ParseContentEncodingsEntry(long long start, long long size)
|
|
{
|
|
PROFILER_SCOPED_EVENT("webm::Track::ParseContentEncodingsEntry");
|
|
|
|
IMkvReader* const pReader = m_pSegment->GetReader();
|
|
assert(pReader);
|
|
|
|
long long pos = start;
|
|
const long long stop = start + size;
|
|
|
|
while (pos < stop)
|
|
{
|
|
long long id, size;
|
|
long status = ParseElementHeader(pReader,
|
|
pos,
|
|
stop,
|
|
id,
|
|
size);
|
|
if (status < 0) //error
|
|
return status;
|
|
|
|
//pos now designates start of element
|
|
if (id == 0x2240) // ContentEncoding ID
|
|
{
|
|
ContentEncoding* const content_encoding = new ContentEncoding();
|
|
if (!content_encoding)
|
|
return -1;
|
|
|
|
status = content_encoding->ParseContentEncodingEntry(pos,
|
|
size,
|
|
pReader);
|
|
if (status)
|
|
{
|
|
delete content_encoding;
|
|
return status;
|
|
}
|
|
|
|
content_encoding_entries_.push_back(content_encoding);
|
|
}
|
|
|
|
pos += size; //consume payload
|
|
assert(pos <= stop);
|
|
}
|
|
|
|
assert(pos == stop);
|
|
|
|
return 0;
|
|
}
|
|
|
|
} // namespace mkvparser
|