Initial cues work

This commit is contained in:
complexlogic
2025-08-05 06:42:16 -07:00
committed by Urs Fleisch
parent 975eaac3ca
commit f94843614f
4 changed files with 412 additions and 0 deletions

View File

@ -0,0 +1,81 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "ebmlmkcues.h"
#include "ebmluintelement.h"
#include "ebmlstringelement.h"
#include "ebmlutils.h"
#include "matroskafile.h"
#include "matroskacues.h"
#include "tdebug.h"
#include "tutils.h"
using namespace TagLib;
Matroska::Cues* EBML::MkCues::parse()
{
auto cues = new Matroska::Cues();
cues->setOffset(offset);
cues->setSize(getSize());
cues->setID(id);
for (auto cuesChild : elements) {
if (cuesChild->getId() != ElementIDs::MkCuePoint)
continue;
auto cuePointElement = static_cast<MasterElement*>(cuesChild);
auto cuePoint = new Matroska::CuePoint();
for (auto cuePointChild : *cuePointElement) {
Id id = cuePointChild->getId();
if (id == ElementIDs::MkCueTime)
cuePoint->setTime(static_cast<UIntElement*>(cuePointChild)->getValue());
else if (id == ElementIDs::MkCueTrackPositions) {
auto cueTrack = new Matroska::CueTrack();
auto cueTrackElement = static_cast<MasterElement*>(cuePointChild);
for (auto cueTrackChild : *cueTrackElement) {
Id trackId = cueTrackChild->getId();
if (trackId == ElementIDs::MkCueTrack)
cueTrack->setTrackNumber(static_cast<UIntElement*>(cueTrackChild)->getValue());
else if (trackId == ElementIDs::MkCueClusterPosition)
cueTrack->setClusterPosition(static_cast<UIntElement*>(cueTrackChild)->getValue());
else if (trackId == ElementIDs::MkCueRelativePosition)
cueTrack->setRelativePosition(static_cast<UIntElement*>(cueTrackChild)->getValue());
else if (trackId == ElementIDs::MkCueDuration)
cueTrack->setDuration(static_cast<UIntElement*>(cueTrackChild)->getValue());
else if (trackId == ElementIDs::MkCueBlockNumber)
cueTrack->setBlockNumber(static_cast<UIntElement*>(cueTrackChild)->getValue());
else if (trackId == ElementIDs::MkCueCodecState)
cueTrack->setCodecState(static_cast<UIntElement*>(cueTrackChild)->getValue());
else if (trackId == ElementIDs::MkCueReference) {
auto cueReference = static_cast<MasterElement*>(cueTrackChild);
for (auto cueReferenceChild : *cueReference) {
if (cueReferenceChild->getId() != ElementIDs::MkCueReference)
continue;
cueTrack->addReferenceTime(static_cast<UIntElement*>(cueReferenceChild)->getValue());
}
}
}
cuePoint->addCueTrack(cueTrack);
}
}
cues->addCuePoint(cuePoint);
}
return cues;
}

View File

@ -0,0 +1,51 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "ebmlmasterelement.h"
#include "ebmlutils.h"
#include "taglib.h"
#ifndef TAGLIB_EBMLMKCUES_H
#define TAGLIB_EBMLMKCUES_H
#ifndef DO_NOT_DOCUMENT
namespace TagLib {
namespace Matroska {
class Cues;
}
//class Matroska::Tag;
namespace EBML {
class MkCues : public MasterElement
{
public:
MkCues(int sizeLength, offset_t dataSize, offset_t offset)
: MasterElement(ElementIDs::MkCues, sizeLength, dataSize, offset)
{}
MkCues()
: MasterElement(ElementIDs::MkCues, 0, 0, 0)
{}
Matroska::Cues* parse();
};
}
}
#endif
#endif

View File

@ -0,0 +1,161 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "matroskacues.h"
#include "ebmlelement.h"
#include "ebmlmkcues.h"
#include "ebmlmasterelement.h"
#include "ebmluintelement.h"
#include "tlist.h"
#include "tdebug.h"
#include "tfile.h"
using namespace TagLib;
Matroska::Cues::Cues()
: Element(ElementIDs::MkCues)
{
cuePoints.setAutoDelete(true);
}
ByteVector Matroska::Cues::renderInternal()
{
EBML::MkCues cues;
for (auto &cuePoint : cuePoints) {
auto cuePointElement = new EBML::MasterElement(EBML::ElementIDs::MkCuePoint);
auto timestamp = new EBML::UIntElement(EBML::ElementIDs::MkCueTime);
timestamp->setValue(cuePoint->getTime());
cuePointElement->appendElement(timestamp);
auto trackList = cuePoint->cueTrackList();
for (auto &cueTrack : trackList) {
auto cueTrackElement = new EBML::MasterElement(EBML::ElementIDs::MkCueTrackPositions);
// Track number
auto trackNumber = new EBML::UIntElement(EBML::ElementIDs::MkCueTrack);
trackNumber->setValue(cueTrack->getTrackNumber());
cueTrackElement->appendElement(trackNumber);
// Cluster position
auto clusterPosition = new EBML::UIntElement(EBML::ElementIDs::MkCueClusterPosition);
clusterPosition->setValue(cueTrack->getClusterPosition());
cueTrackElement->appendElement(clusterPosition);
// Todo - other elements
// Reference times
auto referenceTimes = cueTrack->referenceTimes();
if (!referenceTimes.isEmpty()) {
auto cueReference = new EBML::MasterElement(EBML::ElementIDs::MkCueReference);
for (auto reference : referenceTimes) {
auto refTime = new EBML::UIntElement(EBML::ElementIDs::MkCueRefTime);
refTime->setValue(reference);
cueReference->appendElement(refTime);
}
cueTrackElement->appendElement(cueReference);
}
cuePointElement->appendElement(cueTrackElement);
}
}
return cues.render();
}
bool Matroska::Cues::render()
{
if (!needsRender)
return true;
setData(cues.render());
needsRender = false;
return true;
}
bool Matroska::Cues::sizeChanged(Element &caller, offset_t delta)
{
offset_t offset = caller.offset();
for (auto cuePoint : cuePoints)
needsRender |= cuePoint->adjustOffset(offset, delta);
return true;
}
bool Matroska::Cues::isValid(TagLib::File &file, offset_t segmentDataOffset) const
{
for (const auto cuePoint : cuePoints) {
if (!cuePoint->isValid(file, segmentDataOffset))
return false;
}
return true;
}
Matroska::CuePoint::CuePoint()
{
cueTracks.setAutoDelete(true);
}
bool Matroska::CuePoint::isValid(TagLib::File &file, offset_t segmentDataOffset) const
{
for (const auto track : cueTracks) {
if (!track->isValid(file, segmentDataOffset))
return false;
}
return true;
}
bool Matroska::CuePoint::adjustOffset(offset_t offset, offset_t delta)
{
bool ret = false;
for (auto cueTrack : cueTracks)
ret |= cueTrack->adjustOffset(offset, delta);
return ret;
}
bool Matroska::CueTrack::isValid(TagLib::File &file, offset_t segmentDataOffset) const
{
if (!trackNumber) {
debug("Cue track number not set");
return false;
}
if (!clusterPosition) {
debug("Cue track cluster position not set");
return false;
}
file.seek(segmentDataOffset + clusterPosition);
if (EBML::Element::readId(file) != EBML::ElementIDs::MkCluster) {
debug("No cluster found at position");
return false;
}
if (codecState) {
file.seek(segmentDataOffset + codecState);
if (EBML::Element::readId(file) != EBML::ElementIDs::MkCodecState) {
debug("No codec state found at position");
return false;
}
}
return true;
}
bool Matroska::CueTrack::adjustOffset(offset_t offset, offset_t delta)
{
return false;
}

View File

@ -0,0 +1,119 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef HAS_MATROSKACUES_H
#define HAS_MATROSKACUES_H
#ifndef DO_NOT_DOCUMENT
#include <memory>
#include <algorithm>
#include <utility>
#include "tlist.h"
#include "matroskaelement.h"
namespace TagLib {
class File;
namespace EBML {
class MkCues;
}
namespace Matroska {
class CuePoint;
class CueTrack;
class Cues : public Element
{
public:
using CuePointList = List<CuePoint*>;
Cues();
~Cues() override = default;
bool isValid(TagLib::File &file, offset_t segmentDataOffset) const;
void addCuePoint(CuePoint *cuePoint) { cuePoints.append(cuePoint); }
CuePointList cuePointList() { return cuePoints; }
bool sizeChanged(Element &caller, offset_t delta) override;
bool render() override;
private:
friend class EBML::MkCues;
ByteVector renderInternal();
bool needsRender = false;
List<CuePoint*> cuePoints;
};
class CuePoint
{
public:
using CueTrackList = List<CueTrack*>;
using Time = unsigned long long;
CuePoint();
~CuePoint() = default;
bool isValid(TagLib::File &file, offset_t segmentDataOffset) const;
void addCueTrack(CueTrack *cueTrack) { cueTracks.append(cueTrack); }
CueTrackList cueTrackList() const { return cueTracks; }
void setTime(Time time) { this->time = time; }
Time getTime() const { return time; }
bool adjustOffset(offset_t offset, offset_t delta);
private:
CueTrackList cueTracks;
Time time = 0;
};
class CueTrack
{
public:
using ReferenceTimeList = List<unsigned long long>;
CueTrack() = default;
~CueTrack() = default;
bool isValid(TagLib::File &file, offset_t segmentDataOffset) const;
void setTrackNumber(unsigned int trackNumber) { this->trackNumber = trackNumber; }
unsigned int getTrackNumber() const { return trackNumber; }
void setClusterPosition(offset_t clusterPosition) { this->clusterPosition = clusterPosition; }
offset_t getClusterPosition() const { return clusterPosition; }
void setRelativePosition(offset_t relativePosition) { this->relativePosition = relativePosition; }
offset_t getRelativePosition() const { return relativePosition; }
void setCodecState(offset_t codecState) { this->codecState = codecState; }
offset_t getCodecState() const { return codecState; }
void setBlockNumber(unsigned int blockNumber) { this->blockNumber = blockNumber; }
unsigned int getBlockNumber() const { return blockNumber; }
void setDuration(unsigned long long duration) { this->duration = duration; }
unsigned long long getDuration() const { return duration; }
void addReferenceTime(unsigned long long refTime) { refTimes.append(refTime); }
ReferenceTimeList referenceTimes() const { return refTimes; }
bool adjustOffset(offset_t offset, offset_t delta);
private:
unsigned int trackNumber = 0;
offset_t clusterPosition = 0;
offset_t relativePosition = 0;
unsigned int blockNumber = 0;
unsigned long long duration = 0;
offset_t codecState = 0;
ReferenceTimeList refTimes;
};
}
}
#endif
#endif