mirror of
https://github.com/taglib/taglib.git
synced 2025-06-04 01:28:21 -04:00
commit
6b56510f99
@ -24,6 +24,8 @@ include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/xm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ebml
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ebml/matroska
|
||||
)
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
@ -131,6 +133,12 @@ set(tag_HDRS
|
||||
s3m/s3mproperties.h
|
||||
xm/xmfile.h
|
||||
xm/xmproperties.h
|
||||
ebml/ebmlfile.h
|
||||
ebml/ebmlelement.h
|
||||
ebml/ebmlconstants.h
|
||||
ebml/matroska/ebmlmatroskafile.h
|
||||
ebml/matroska/ebmlmatroskaconstants.h
|
||||
ebml/matroska/ebmlmatroskaaudio.h
|
||||
)
|
||||
|
||||
set(mpeg_SRCS
|
||||
@ -297,11 +305,22 @@ set(toolkit_SRCS
|
||||
toolkit/unicode.cpp
|
||||
)
|
||||
|
||||
set(ebml_SRCS
|
||||
ebml/ebmlfile.cpp
|
||||
ebml/ebmlelement.cpp
|
||||
)
|
||||
|
||||
set(matroska_SRCS
|
||||
ebml/matroska/ebmlmatroskafile.cpp
|
||||
ebml/matroska/ebmlmatroskaaudio.cpp
|
||||
)
|
||||
|
||||
set(tag_LIB_SRCS
|
||||
${mpeg_SRCS} ${id3v1_SRCS} ${id3v2_SRCS} ${frames_SRCS} ${ogg_SRCS}
|
||||
${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS}
|
||||
${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS}
|
||||
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
|
||||
${ebml_SRCS} ${matroska_SRCS}
|
||||
tag.cpp
|
||||
tagunion.cpp
|
||||
fileref.cpp
|
||||
|
63
taglib/ebml/ebmlconstants.h
Normal file
63
taglib/ebml/ebmlconstants.h
Normal file
@ -0,0 +1,63 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 TAGLIB_EBML_CONSTANTS
|
||||
#define TAGLIB_EBML_CONSTANTS
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <iostream>
|
||||
#include "tdebug.h"
|
||||
#endif
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
//! Shorter representation of the type.
|
||||
typedef unsigned long long int ulli;
|
||||
|
||||
//! The id of an EBML Void element that is just a placeholder.
|
||||
const ulli Void = 0xecL;
|
||||
|
||||
//! The id of an EBML CRC32 element that contains a crc32 value.
|
||||
const ulli CRC32 = 0xc3L;
|
||||
|
||||
//! A namespace containing the ids of the EBML header's elements.
|
||||
namespace Header {
|
||||
const ulli EBML = 0x1a45dfa3L;
|
||||
const ulli EBMLVersion = 0x4286L;
|
||||
const ulli EBMLReadVersion = 0x42f7L;
|
||||
const ulli EBMLMaxIDWidth = 0x42f2L;
|
||||
const ulli EBMLMaxSizeWidth = 0x42f3L;
|
||||
const ulli DocType = 0x4282L;
|
||||
const ulli DocTypeVersion = 0x4287L;
|
||||
const ulli DocTypeReadVersion = 0x4285L;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
496
taglib/ebml/ebmlelement.cpp
Normal file
496
taglib/ebml/ebmlelement.cpp
Normal file
@ -0,0 +1,496 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 "ebmlelement.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class EBML::Element::ElementPrivate
|
||||
{
|
||||
public:
|
||||
// The id of this element.
|
||||
ulli id;
|
||||
|
||||
// The position of the element, where the header begins.
|
||||
offset_t position;
|
||||
|
||||
// The size of the element as read from the header. Note: Actually an ulli but
|
||||
// due to the variable integer size limited, thus offset_t is ok.
|
||||
offset_t size;
|
||||
|
||||
// The position of the element's data.
|
||||
offset_t data;
|
||||
|
||||
// The element's children.
|
||||
List<Element *> children;
|
||||
|
||||
// True: Treated this element as container and read children.
|
||||
bool populated;
|
||||
|
||||
// The parent element. If NULL (0) this is the document root.
|
||||
Element *parent;
|
||||
|
||||
// The file used to read and write.
|
||||
File *document;
|
||||
|
||||
// Destructor: Clean up all children.
|
||||
~ElementPrivate()
|
||||
{
|
||||
for(List<Element *>::Iterator i = children.begin(); i != children.end(); ++i) {
|
||||
delete *i;
|
||||
}
|
||||
}
|
||||
|
||||
// Reads a variable length integer from the file at the given position
|
||||
// and saves its value to result. If cutOne is true the first one of the
|
||||
// binary representation of the result is removed (required for size). If
|
||||
// cutOne is false the one will remain in the result (required for id).
|
||||
// This method returns the position directly after the read integer.
|
||||
offset_t readVInt(offset_t position, ulli *result, bool cutOne = true)
|
||||
{
|
||||
document->seek(position);
|
||||
|
||||
// Determine the length of the integer
|
||||
char firstByte = document->readBlock(1)[0];
|
||||
uint byteSize = 1;
|
||||
for(uint i = 0; i < 8 && ((firstByte << i) & (1 << 7)) == 0; ++i)
|
||||
++byteSize;
|
||||
|
||||
// Load the integer
|
||||
document->seek(position);
|
||||
ByteVector vint = document->readBlock(byteSize);
|
||||
|
||||
// Cut the one if requested
|
||||
if(cutOne)
|
||||
vint[0] = (vint[0] & (~(1 << (8 - byteSize))));
|
||||
|
||||
// Store the result and return the current position
|
||||
if(result)
|
||||
*result = static_cast<ulli>(vint.toInt64());
|
||||
return position + byteSize;
|
||||
}
|
||||
|
||||
// Returns a BytVector containing the given number in the variable integer
|
||||
// format. Truncates numbers > 2^56 (^ means potency in this case).
|
||||
// If addOne is true, the ByteVector will remain the One to determine the
|
||||
// integer's length.
|
||||
// If shortest is true, the ByteVector will be as short as possible (required
|
||||
// for the id)
|
||||
ByteVector createVInt(ulli number, bool addOne = true, bool shortest = true)
|
||||
{
|
||||
ByteVector vint = ByteVector::fromUInt64(static_cast<signed long long>(number));
|
||||
|
||||
// Do we actually need to calculate the length of the variable length
|
||||
// integer? If not, then prepend the 0b0000 0001 if necessary and return the
|
||||
// vint.
|
||||
if(!shortest) {
|
||||
if(addOne)
|
||||
vint[0] = 1;
|
||||
return vint;
|
||||
}
|
||||
|
||||
// Calculate the minimal length of the variable length integer
|
||||
uint byteSize = vint.size();
|
||||
for(uint i = 0; byteSize > 0 && vint[i] == 0; ++i)
|
||||
--byteSize;
|
||||
|
||||
if(!addOne)
|
||||
return ByteVector(vint.data() + vint.size() - byteSize, byteSize);
|
||||
|
||||
ulli firstByte = (1 << (vint.size() - byteSize));
|
||||
// The most significant byte loses #bytSize bits for storing information.
|
||||
// Therefore, we might need to increase byteSize.
|
||||
if(number >= (firstByte << (8 * (byteSize - 1))) && byteSize < vint.size())
|
||||
++byteSize;
|
||||
// Add the one at the correct position
|
||||
uint firstBytePosition = vint.size() - byteSize;
|
||||
vint[firstBytePosition] |= (1 << firstBytePosition);
|
||||
return ByteVector(vint.data() + firstBytePosition, byteSize);
|
||||
}
|
||||
|
||||
// Returns a void element within this element which is at least "least" in
|
||||
// size. Uses best fit method. Returns a null pointer if no suitable element
|
||||
// was found.
|
||||
Element *searchVoid(offset_t least = 0L)
|
||||
{
|
||||
Element *currentBest = 0;
|
||||
for(List<Element *>::Iterator i = children.begin(); i != children.end(); ++i) {
|
||||
if((*i)->d->id == Void &&
|
||||
// We need room for the header if we don't remove the element.
|
||||
((((*i)->d->size + (*i)->d->data - (*i)->d->position) == least || ((*i)->d->size >= least)) &&
|
||||
// best fit
|
||||
(!currentBest || (*i)->d->size < currentBest->d->size))
|
||||
) {
|
||||
currentBest = *i;
|
||||
}
|
||||
}
|
||||
return currentBest;
|
||||
}
|
||||
|
||||
// Replaces this element by a Void element. Returns true on success and false
|
||||
// on error.
|
||||
bool makeVoid()
|
||||
{
|
||||
ulli realSize = size + data - position;
|
||||
ByteVector header(createVInt(Void, false));
|
||||
ulli leftSize = realSize - (header.size() + sizeof(ulli));
|
||||
// Does not make sense to create a Void element
|
||||
if (leftSize > realSize)
|
||||
return false;
|
||||
header.append(createVInt(leftSize, true, false));
|
||||
// Write to file
|
||||
document->seek(position);
|
||||
document->writeBlock(header);
|
||||
// Update data
|
||||
data = position + header.size();
|
||||
size = leftSize;
|
||||
return true;
|
||||
|
||||
// XXX: We actually should merge Voids, if possible.
|
||||
}
|
||||
|
||||
// Reading constructor: Reads all unknown information from the file.
|
||||
ElementPrivate(File *p_document, Element *p_parent = 0, offset_t p_position = 0) :
|
||||
id(0),
|
||||
position(p_position),
|
||||
data(0),
|
||||
populated(false),
|
||||
parent(p_parent),
|
||||
document(p_document)
|
||||
{
|
||||
if(parent) {
|
||||
ulli ssize;
|
||||
data = readVInt(readVInt(position, &id, false), &ssize);
|
||||
size = static_cast<offset_t>(ssize);
|
||||
}
|
||||
else {
|
||||
document->seek(0, File::End);
|
||||
size = document->tell();
|
||||
}
|
||||
}
|
||||
|
||||
// Writing constructor: Takes given information, calculates missing information
|
||||
// and writes everything to the file.
|
||||
// Tries to use void elements if available in the parent.
|
||||
ElementPrivate(ulli p_id, File *p_document, Element *p_parent,
|
||||
offset_t p_position, offset_t p_size) :
|
||||
id(p_id),
|
||||
position(p_position),
|
||||
size(p_size),
|
||||
populated(true), // It is a new element so we know, there are no children.
|
||||
parent(p_parent),
|
||||
document(p_document)
|
||||
{
|
||||
// header
|
||||
ByteVector content(createVInt(id, false).append(createVInt(size, true, false)));
|
||||
data = position + content.size();
|
||||
// space for children
|
||||
content.resize(data - position + size);
|
||||
|
||||
Element *freeSpace;
|
||||
if (!(freeSpace = searchVoid(content.size()))) {
|
||||
// We have to make room
|
||||
document->insert(content, position);
|
||||
// Update parents
|
||||
for(Element *current = parent; current->d->parent; current = current->d->parent) {
|
||||
current->d->size += content.size();
|
||||
// Create new header and write it.
|
||||
ByteVector parentHeader(createVInt(current->d->id, false).append(createVInt(current->d->size, true, false)));
|
||||
uint oldHeaderSize = current->d->data - current->d->position;
|
||||
if(oldHeaderSize < parentHeader.size()) {
|
||||
ByteVector secondHeader(createVInt(current->d->id, false).append(createVInt(current->d->size)));
|
||||
if(oldHeaderSize == secondHeader.size()) {
|
||||
// Write the header where the old one was.
|
||||
document->seek(current->d->position);
|
||||
document->writeBlock(secondHeader);
|
||||
continue; // Very important here!
|
||||
}
|
||||
}
|
||||
// Insert the new header
|
||||
document->insert(parentHeader, current->d->position, oldHeaderSize);
|
||||
current->d->data = current->d->position + parentHeader.size();
|
||||
}
|
||||
}
|
||||
else {
|
||||
document->seek(freeSpace->d->position);
|
||||
if((freeSpace->d->size + freeSpace->d->data - freeSpace->d->position)
|
||||
== static_cast<offset_t>(content.size())) {
|
||||
// Write to file
|
||||
document->writeBlock(content);
|
||||
// Update parent
|
||||
for(List<Element *>::Iterator i = parent->d->children.begin();
|
||||
i != parent->d->children.end(); ++i) {
|
||||
if(freeSpace == *i)
|
||||
parent->d->children.erase(i);
|
||||
}
|
||||
delete freeSpace;
|
||||
}
|
||||
else {
|
||||
ulli newSize = freeSpace->d->size - content.size();
|
||||
ByteVector newVoid(createVInt(Void, false).append(createVInt(newSize, true, false)));
|
||||
|
||||
// Check if the original size of the size field was really 8 byte
|
||||
if (static_cast<offset_t>(newVoid.size()) != (freeSpace->d->data - freeSpace->d->position))
|
||||
newVoid = createVInt(Void, false).append(createVInt(newSize));
|
||||
// Update freeSpace
|
||||
freeSpace->d->size = newSize;
|
||||
freeSpace->d->data = freeSpace->d->position + newVoid.size();
|
||||
// Write to file
|
||||
document->writeBlock(newVoid.resize(newVoid.size() + newSize).append(content));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::Element::~Element()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
EBML::Element::Element(EBML::File *document)
|
||||
: d(new EBML::Element::ElementPrivate(document))
|
||||
{
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::getChild(EBML::ulli id)
|
||||
{
|
||||
populate();
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end();
|
||||
++i) {
|
||||
if ((*i)->d->id == id)
|
||||
return *i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
List<EBML::Element *> EBML::Element::getChildren(EBML::ulli id)
|
||||
{
|
||||
populate();
|
||||
List<Element *> result;
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end();
|
||||
++i) {
|
||||
if ((*i)->d->id == id)
|
||||
result.append(*i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
List<EBML::Element *> EBML::Element::getChildren()
|
||||
{
|
||||
populate();
|
||||
return d->children;
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::getParent()
|
||||
{
|
||||
return d->parent;
|
||||
}
|
||||
|
||||
ByteVector EBML::Element::getAsBinary()
|
||||
{
|
||||
d->document->seek(d->data);
|
||||
return d->document->readBlock(d->size);
|
||||
}
|
||||
|
||||
String EBML::Element::getAsString()
|
||||
{
|
||||
return String(getAsBinary(), String::UTF8);
|
||||
}
|
||||
|
||||
signed long long EBML::Element::getAsInt()
|
||||
{
|
||||
// The debug note about returning 0 because of empty data is irrelevant. The
|
||||
// behavior is as expected.
|
||||
return getAsBinary().toInt64();
|
||||
}
|
||||
|
||||
EBML::ulli EBML::Element::getAsUnsigned()
|
||||
{
|
||||
// The debug note about returning 0 because of empty data is irrelevant. The
|
||||
// behavior is as expected.
|
||||
return static_cast<ulli>(getAsBinary().toInt64());
|
||||
}
|
||||
|
||||
long double EBML::Element::getAsFloat()
|
||||
{
|
||||
// Very dirty implementation!
|
||||
ByteVector bin = getAsBinary();
|
||||
uint size = bin.size();
|
||||
ulli sum = 0.0L;
|
||||
|
||||
// For 0 byte floats and any float that is not defined in the ebml spec.
|
||||
if (size != 4 && size != 8 /*&& size() != 10*/) // XXX: Currently no support for 10 bit floats.
|
||||
return sum;
|
||||
|
||||
// From toNumber; Might not be portable, since it requires IEEE floats.
|
||||
uint last = size - 1;
|
||||
for(uint i = 0; i <= last; i++)
|
||||
sum |= (ulli) uchar(bin[i]) << ((last - i) * 8);
|
||||
|
||||
if (size == 4) {
|
||||
float result = *reinterpret_cast<float *>(&sum);
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
double result = *reinterpret_cast<double *>(&sum);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id)
|
||||
{
|
||||
Element *elem = new Element(
|
||||
new ElementPrivate(id, d->document, this, d->data + d->size, 0)
|
||||
);
|
||||
d->children.append(elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, const ByteVector &binary)
|
||||
{
|
||||
Element *elem = new Element(
|
||||
new ElementPrivate(id, d->document, this, d->data + d->size, binary.size())
|
||||
);
|
||||
d->document->seek(elem->d->data);
|
||||
d->document->writeBlock(binary);
|
||||
d->children.append(elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, const String &string)
|
||||
{
|
||||
return addElement(id, string.data(String::UTF8));
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, signed long long number)
|
||||
{
|
||||
return addElement(id, ByteVector::fromUInt64(number));
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, EBML::ulli number)
|
||||
{
|
||||
return addElement(id, ByteVector::fromUInt64(static_cast<signed long long>(number)));
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, long double number)
|
||||
{
|
||||
// Probably, we will never need this method.
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool EBML::Element::removeChildren(EBML::ulli id, bool useVoid)
|
||||
{
|
||||
bool result = false;
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
|
||||
if((*i)->d->id == id) {
|
||||
removeChild(*i, useVoid);
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EBML::Element::removeChildren(bool useVoid)
|
||||
{
|
||||
// Maybe a better implementation, because we probably create a lot of voids
|
||||
// in a row where a huge Void would be more appropriate.
|
||||
if (d->children.isEmpty())
|
||||
return false;
|
||||
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
|
||||
removeChild(*i, useVoid);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EBML::Element::removeChild(Element *element, bool useVoid)
|
||||
{
|
||||
if (!d->children.contains(element))
|
||||
return false;
|
||||
|
||||
if(!useVoid || !element->d->makeVoid()) {
|
||||
d->document->removeBlock(element->d->position, element->d->size);
|
||||
// Update parents
|
||||
for(Element* current = this; current; current = current->d->parent)
|
||||
current->d->size -= element->d->size;
|
||||
// Update this element
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
|
||||
if(element == *i)
|
||||
d->children.erase(i);
|
||||
delete element;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void EBML::Element::setAsBinary(const ByteVector &binary)
|
||||
{
|
||||
// Maybe: Search for void element after this one
|
||||
d->document->insert(binary, d->data, d->size);
|
||||
}
|
||||
|
||||
void EBML::Element::setAsString(const String &string)
|
||||
{
|
||||
setAsBinary(string.data(String::UTF8));
|
||||
}
|
||||
|
||||
void EBML::Element::setAsInt(signed long long number)
|
||||
{
|
||||
setAsBinary(ByteVector::fromUInt64(number));
|
||||
}
|
||||
|
||||
void EBML::Element::setAsUnsigned(EBML::ulli number)
|
||||
{
|
||||
setAsBinary(ByteVector::fromUInt64(static_cast<signed long long>(number)));
|
||||
}
|
||||
|
||||
void EBML::Element::setAsFloat(long double)
|
||||
{
|
||||
// Probably, we will never need this method.
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EBML::Element::populate()
|
||||
{
|
||||
if(!d->populated) {
|
||||
d->populated = true;
|
||||
offset_t end = d->data + d->size;
|
||||
|
||||
for(offset_t i = d->data; i < end;) {
|
||||
Element *elem = new Element(
|
||||
new ElementPrivate(d->document, this, i)
|
||||
);
|
||||
d->children.append(elem);
|
||||
i = elem->d->data + elem->d->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EBML::Element::Element(EBML::Element::ElementPrivate *pe) : d(pe)
|
||||
{}
|
276
taglib/ebml/ebmlelement.h
Normal file
276
taglib/ebml/ebmlelement.h
Normal file
@ -0,0 +1,276 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 TAGLIB_EBMLELEMENT_H
|
||||
#define TAGLIB_EBMLELEMENT_H
|
||||
|
||||
#include "tlist.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tstring.h"
|
||||
|
||||
#include "ebmlfile.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
|
||||
/*!
|
||||
* Represents an element of the EBML. The only instance of this child, that
|
||||
* is directly used is the root element. Every other element is accessed
|
||||
* via pointers to the elements within the root element.
|
||||
*
|
||||
* Just create one root instance per file to prevent race conditions.
|
||||
*
|
||||
* Changes of the document tree will be directly written back to the file.
|
||||
* Invalid values (exceeding the maximal value defined in the RFC) will be
|
||||
* truncated.
|
||||
*
|
||||
* This class should not be used by library users since the proper file
|
||||
* class should handle the internals.
|
||||
*
|
||||
* NOTE: Currently does not adjust CRC32 values.
|
||||
*/
|
||||
class TAGLIB_EXPORT Element
|
||||
{
|
||||
public:
|
||||
//! Destroys the instance of the element.
|
||||
~Element();
|
||||
|
||||
/*!
|
||||
* Creates an root element using document.
|
||||
*/
|
||||
Element(File *document);
|
||||
|
||||
/*!
|
||||
* Returns the first found child element with the given id. Returns a null
|
||||
* pointer if the child does not exist.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *getChild(const ulli id);
|
||||
|
||||
/*!
|
||||
* Returns a list of all child elements with the given id. Returns an
|
||||
* empty list if no such element exists.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
List<Element *> getChildren(const ulli id);
|
||||
|
||||
/*!
|
||||
* Returns a list of every child elements available. Returns an empty list
|
||||
* if there are no children.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
List<Element *> getChildren();
|
||||
|
||||
/*!
|
||||
* Returns the parent element or null if no such element exists.
|
||||
*/
|
||||
Element *getParent();
|
||||
|
||||
/*!
|
||||
* Returns the raw content of the element.
|
||||
*/
|
||||
ByteVector getAsBinary();
|
||||
|
||||
/*!
|
||||
* Returns the content of this element interpreted as a string.
|
||||
*/
|
||||
String getAsString();
|
||||
|
||||
/*!
|
||||
* Returns the content of this element interpreted as an signed integer.
|
||||
*
|
||||
* Do not call this method if *this element is not an INT element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
signed long long getAsInt();
|
||||
|
||||
/*!
|
||||
* Returns the content of this element interpreted as an unsigned integer.
|
||||
*
|
||||
* Do not call this method if *this element is not an UINT element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
ulli getAsUnsigned();
|
||||
|
||||
/*!
|
||||
* Returns the content of this element interpreted as a floating point
|
||||
* type.
|
||||
*
|
||||
* Do not call this method if *this element is not an FLOAT element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* NOTE: There are 10 byte floats defined, therefore we might need a long
|
||||
* double to store the value.
|
||||
*/
|
||||
long double getAsFloat();
|
||||
|
||||
/*!
|
||||
* Adds an empty element with given id to this element. Returns a pointer
|
||||
* to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given binary, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id, const ByteVector &binary);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given string, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id, const String &string);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given integer, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id, signed long long number);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given unsigned integer, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id, ulli number);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given floating point value, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* This method is not implemented!
|
||||
*/
|
||||
Element *addElement(ulli id, long double number);
|
||||
|
||||
/*!
|
||||
* Removes all children with the given id. Returns false if there was no
|
||||
* such element.
|
||||
* If useVoid is true, the element will be changed to a void element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* Every pointer to a removed element is invalidated.
|
||||
*/
|
||||
bool removeChildren(ulli id, bool useVoid = true);
|
||||
|
||||
/*!
|
||||
* Removes all children. Returns false if this element had no children.
|
||||
* If useVoid ist rue, the element will be changed to a void element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* Every pointer to a removed element is invalidated.
|
||||
*/
|
||||
bool removeChildren(bool useVoid = true);
|
||||
|
||||
/*!
|
||||
* Removes the given element.
|
||||
* If useVoid is true, the element will be changed to a void element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* The pointer to the given element is invalidated.
|
||||
*/
|
||||
bool removeChild(Element *element, bool useVoid = true);
|
||||
|
||||
/*!
|
||||
* Writes the given binary to this element.
|
||||
*/
|
||||
void setAsBinary(const ByteVector &binary);
|
||||
|
||||
/*!
|
||||
* Writes the given string to this element.
|
||||
*/
|
||||
void setAsString(const String &string);
|
||||
|
||||
/*!
|
||||
* Writes the given integer to this element.
|
||||
*/
|
||||
void setAsInt(signed long long number);
|
||||
|
||||
/*!
|
||||
* Writes the given unsigned integer to this element.
|
||||
*/
|
||||
void setAsUnsigned(ulli number);
|
||||
|
||||
/*!
|
||||
* Writes the given floating point variable to this element.
|
||||
*
|
||||
* This method is not implemented!
|
||||
*/
|
||||
void setAsFloat(long double number);
|
||||
|
||||
private:
|
||||
//! Non-copyable
|
||||
Element(const Element &);
|
||||
//! Non-copyable
|
||||
Element &operator=(const File &);
|
||||
|
||||
//! Lazy parsing. This method will be triggered when trying to access
|
||||
//! children.
|
||||
void populate();
|
||||
|
||||
class ElementPrivate;
|
||||
ElementPrivate *d;
|
||||
|
||||
//! Creates a new Element from an ElementPrivate. (The constructor takes
|
||||
//! ownership of the pointer and will delete it when the element is
|
||||
//! destroyed.
|
||||
Element(ElementPrivate *pe);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
102
taglib/ebml/ebmlfile.cpp
Normal file
102
taglib/ebml/ebmlfile.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 "ebmlelement.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class EBML::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(File *document) : root(document)
|
||||
{
|
||||
}
|
||||
|
||||
// Performs a few basic checks and creates the FilePrivate if they were
|
||||
// successful.
|
||||
static FilePrivate *checkAndCreate(File* document)
|
||||
{
|
||||
document->seek(0);
|
||||
ByteVector magical = document->readBlock(4);
|
||||
if(static_cast<ulli>(magical.toInt64()) != Header::EBML)
|
||||
return 0;
|
||||
FilePrivate *d = new FilePrivate(document);
|
||||
Element *head = d->root.getChild(Header::EBML);
|
||||
Element *p;
|
||||
if(!head ||
|
||||
!((p = head->getChild(Header::EBMLVersion)) && p->getAsUnsigned() == 1L) ||
|
||||
!((p = head->getChild(Header::EBMLReadVersion)) && p->getAsUnsigned() == 1L) ||
|
||||
// Actually 4 is the current maximum of the EBML spec, but we support up to 8
|
||||
!((p = head->getChild(Header::EBMLMaxIDWidth)) && p->getAsUnsigned() <= 8) ||
|
||||
!((p = head->getChild(Header::EBMLMaxSizeWidth)) && p->getAsUnsigned() <= 8)
|
||||
) {
|
||||
delete d;
|
||||
return 0;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
Element root;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
EBML::Element *EBML::File::getDocumentRoot()
|
||||
{
|
||||
if(!d && isValid())
|
||||
d = new FilePrivate(this);
|
||||
return &d->root;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::File::File(FileName file) :
|
||||
TagLib::File(file)
|
||||
{
|
||||
if(isOpen()) {
|
||||
d = FilePrivate::checkAndCreate(this);
|
||||
if(!d)
|
||||
setValid(false);
|
||||
}
|
||||
}
|
||||
|
||||
EBML::File::File(IOStream *stream) :
|
||||
TagLib::File(stream)
|
||||
{
|
||||
if(isOpen()) {
|
||||
d = FilePrivate::checkAndCreate(this);
|
||||
if(!d)
|
||||
setValid(false);
|
||||
}
|
||||
}
|
86
taglib/ebml/ebmlfile.h
Normal file
86
taglib/ebml/ebmlfile.h
Normal file
@ -0,0 +1,86 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 TAGLIB_EBMLFILE_H
|
||||
#define TAGLIB_EBMLFILE_H
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tfile.h"
|
||||
|
||||
#include "ebmlconstants.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
//! A namespace for the classes used by EBML-based metadata files
|
||||
namespace EBML {
|
||||
|
||||
class Element;
|
||||
|
||||
/*!
|
||||
* Represents an EBML file. It offers access to the root element which can
|
||||
* be used to obtain the necessary information and to change the file
|
||||
* according to changes.
|
||||
*/
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
{
|
||||
public:
|
||||
//! Destroys the instance of the file.
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the document root element of the EBML file.
|
||||
*/
|
||||
Element *getDocumentRoot();
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Constructs an instance of an EBML file from \a file.
|
||||
*
|
||||
* This constructor is protected since an object should be created
|
||||
* through a specific subclass.
|
||||
*/
|
||||
File(FileName file);
|
||||
|
||||
/*!
|
||||
* Constructs an instance of an EBML file from an IOStream.
|
||||
*
|
||||
* This constructor is protected since an object should be created
|
||||
* through a specific subclass.
|
||||
*/
|
||||
File(IOStream *stream);
|
||||
|
||||
private:
|
||||
//! Non-copyable
|
||||
File(const File&);
|
||||
File &operator=(const File &);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
120
taglib/ebml/matroska/ebmlmatroskaaudio.cpp
Normal file
120
taglib/ebml/matroska/ebmlmatroskaaudio.cpp
Normal file
@ -0,0 +1,120 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 "ebmlmatroskaconstants.h"
|
||||
#include "ebmlmatroskaaudio.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class EBML::Matroska::AudioProperties::AudioPropertiesPrivate
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
AudioPropertiesPrivate(File *p_document) :
|
||||
document(p_document),
|
||||
length(0),
|
||||
bitrate(0),
|
||||
channels(1),
|
||||
samplerate(8000)
|
||||
{
|
||||
Element *elem = document->getDocumentRoot()->getChild(Constants::Segment);
|
||||
Element *info = elem->getChild(Constants::SegmentInfo);
|
||||
Element *value;
|
||||
|
||||
if(info && (value = info->getChild(Constants::Duration))) {
|
||||
length = static_cast<int>(value->getAsFloat());
|
||||
if((value = info->getChild(Constants::TimecodeScale))){
|
||||
length /= (value->getAsUnsigned() * (1 / 1000000000));
|
||||
}
|
||||
}
|
||||
|
||||
info = elem->getChild(Constants::Tracks);
|
||||
if(!info || !(info = info->getChild(Constants::TrackEntry)) ||
|
||||
!(info = info->getChild(Constants::Audio))) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Dirty bitrate:
|
||||
document->seek(0, File::End);
|
||||
bitrate = ((8 * document->tell()) / length) / 1000;
|
||||
|
||||
if((value = info->getChild(Constants::Channels)))
|
||||
channels = value->getAsUnsigned();
|
||||
|
||||
if((value = info->getChild(Constants::SamplingFrequency)))
|
||||
samplerate = static_cast<int>(value->getAsFloat());
|
||||
}
|
||||
|
||||
// The corresponding file
|
||||
File *document;
|
||||
|
||||
// The length of the file
|
||||
int length;
|
||||
|
||||
// The bitrate
|
||||
int bitrate;
|
||||
|
||||
// The amount of channels
|
||||
int channels;
|
||||
|
||||
// The sample rate
|
||||
int samplerate;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::Matroska::AudioProperties::AudioProperties(File *document) :
|
||||
TagLib::AudioProperties(TagLib::AudioProperties::Fast),
|
||||
d(new AudioPropertiesPrivate(document))
|
||||
{
|
||||
}
|
||||
|
||||
EBML::Matroska::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::length() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::sampleRate() const
|
||||
{
|
||||
return d->samplerate;
|
||||
}
|
88
taglib/ebml/matroska/ebmlmatroskaaudio.h
Normal file
88
taglib/ebml/matroska/ebmlmatroskaaudio.h
Normal file
@ -0,0 +1,88 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 TAGLIB_EBMLMATROSKAAUDIO_H
|
||||
#define TAGLIB_EBMLMATROSKAAUDIO_H
|
||||
|
||||
#include "ebmlmatroskafile.h"
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
|
||||
namespace Matroska {
|
||||
|
||||
/*!
|
||||
* This class represents the audio properties of a matroska file.
|
||||
* Currently all information are read from the container format and
|
||||
* could be inexact.
|
||||
*/
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
public:
|
||||
//! Destructor
|
||||
virtual ~AudioProperties();
|
||||
|
||||
/*!
|
||||
* Constructs an instance from a file.
|
||||
*/
|
||||
AudioProperties(File *document);
|
||||
|
||||
/*!
|
||||
* Returns the length of the file.
|
||||
*/
|
||||
virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the bit rate of the file. Since the container format does not
|
||||
* offer a proper value, it ist currently calculated by dividing the
|
||||
* file size by the length.
|
||||
*/
|
||||
virtual int bitrate() const;
|
||||
|
||||
/*!
|
||||
* Returns the amount of channels of the file.
|
||||
*/
|
||||
virtual int channels() const;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate of the file.
|
||||
*/
|
||||
virtual int sampleRate() const;
|
||||
|
||||
private:
|
||||
class AudioPropertiesPrivate;
|
||||
AudioPropertiesPrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
140
taglib/ebml/matroska/ebmlmatroskaconstants.h
Normal file
140
taglib/ebml/matroska/ebmlmatroskaconstants.h
Normal file
@ -0,0 +1,140 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 TAGLIB_EBMLMATROSKACONSTANTS_H
|
||||
#define TAGLIB_EBMLMATROSKACONSTANTS_H
|
||||
|
||||
#include "ebmlconstants.h"
|
||||
#include "tstring.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
|
||||
namespace Matroska {
|
||||
|
||||
namespace Constants {
|
||||
|
||||
//! ID of an Matroska segment.
|
||||
const ulli Segment = 0x18538067L;
|
||||
|
||||
//! ID of the tags element.
|
||||
const ulli Tags = 0x1254c367L;
|
||||
|
||||
//! ID of the tag element.
|
||||
const ulli Tag = 0x7373L;
|
||||
|
||||
//! ID of the targets element.
|
||||
const ulli Targets = 0x63c0L;
|
||||
|
||||
//! ID of the target type value element.
|
||||
const ulli TargetTypeValue = 0x68caL;
|
||||
|
||||
//! ID of the target type element.
|
||||
const ulli TargetType = 0x63caL;
|
||||
|
||||
//! ID of a simple tag element.
|
||||
const ulli SimpleTag = 0x67c8L;
|
||||
|
||||
//! ID of the tag name.
|
||||
const ulli TagName = 0x45a3L;
|
||||
|
||||
//! ID of the tag content.
|
||||
const ulli TagString = 0x4487L;
|
||||
|
||||
//! The DocType of a matroska file.
|
||||
const String DocTypeMatroska = "matroska";
|
||||
|
||||
//! The DocType of a WebM file.
|
||||
const String DocTypeWebM = "webm";
|
||||
|
||||
|
||||
|
||||
//! The TITLE entry
|
||||
const String TITLE = "TITLE";
|
||||
|
||||
//! The ARTIST entry
|
||||
const String ARTIST = "ARTIST";
|
||||
|
||||
//! The COMMENT entry
|
||||
const String COMMENT = "COMMENT";
|
||||
|
||||
//! The GENRE entry
|
||||
const String GENRE = "GENRE";
|
||||
|
||||
//! The DATE_RELEASE entry
|
||||
const String DATE_RELEASE = "DATE_RELEASE";
|
||||
|
||||
//! The PART_NUMBER entry
|
||||
const String PART_NUMBER = "PART_NUMBER";
|
||||
|
||||
//! The TargetTypeValue of the most common grouping level (e.g. album)
|
||||
const ulli MostCommonGroupingValue = 50;
|
||||
|
||||
//! The TargetTypeValue of the most common parts of a group (e.g. track)
|
||||
const ulli MostCommonPartValue = 30;
|
||||
|
||||
//! Name of the TargetType of an album.
|
||||
const String ALBUM = "ALBUM";
|
||||
|
||||
//! Name of the TargetType of a track.
|
||||
const String TRACK = "TRACK";
|
||||
|
||||
|
||||
|
||||
// For AudioProperties
|
||||
|
||||
//! ID of the Info block within the Segment.
|
||||
const ulli SegmentInfo = 0x1549a966L;
|
||||
|
||||
//! ID of the duration element.
|
||||
const ulli Duration = 0x4489L;
|
||||
|
||||
//! ID of TimecodeScale element.
|
||||
const ulli TimecodeScale = 0x2ad7b1L;
|
||||
|
||||
//! ID of the Tracks container
|
||||
const ulli Tracks = 0x1654ae6bL;
|
||||
|
||||
//! ID of a TrackEntry element.
|
||||
const ulli TrackEntry = 0xaeL;
|
||||
|
||||
//! ID of the Audio container.
|
||||
const ulli Audio = 0xe1L;
|
||||
|
||||
//! ID of the SamplingFrequency element.
|
||||
const ulli SamplingFrequency = 0xb5L;
|
||||
|
||||
//! ID of the Channels element.
|
||||
const ulli Channels = 0x9fL;
|
||||
|
||||
//! ID of the BitDepth element.
|
||||
const ulli BitDepth = 0x6264L;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
549
taglib/ebml/matroska/ebmlmatroskafile.cpp
Normal file
549
taglib/ebml/matroska/ebmlmatroskafile.cpp
Normal file
@ -0,0 +1,549 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 "ebmlmatroskaconstants.h"
|
||||
#include "ebmlmatroskaaudio.h"
|
||||
|
||||
#include "tpropertymap.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class EBML::Matroska::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
// Returns true if simpleTag has a TagName and TagString child and writes
|
||||
// their contents into name and value.
|
||||
bool extractContent(Element *simpleTag, String &name, String &value)
|
||||
{
|
||||
Element *n = simpleTag->getChild(Constants::TagName);
|
||||
Element *v = simpleTag->getChild(Constants::TagString);
|
||||
if(!n || !v)
|
||||
return false;
|
||||
|
||||
name = n->getAsString();
|
||||
value = v->getAsString();
|
||||
return true;
|
||||
}
|
||||
|
||||
FilePrivate(File *p_document) : tag(0), document(p_document)
|
||||
{
|
||||
// Just get the first segment, because "Typically a Matroska file is
|
||||
// composed of 1 segment."
|
||||
Element* elem = document->getDocumentRoot()->getChild(Constants::Segment);
|
||||
|
||||
// We take the first tags element (there shouldn't be more), if there is
|
||||
// non such element, consider the file as not compatible.
|
||||
if(!elem || !(elem = elem->getChild(Constants::Tags))) {
|
||||
document->setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Load all Tag entries
|
||||
List<Element *> entries = elem->getChildren(Constants::Tag);
|
||||
for(List<Element *>::Iterator i = entries.begin(); i != entries.end(); ++i) {
|
||||
Element *target = (*i)->getChild(Constants::Targets);
|
||||
ulli ttvalue = 0;
|
||||
if(target && (target = (*i)->getChild(Constants::TargetTypeValue)))
|
||||
ttvalue = target->getAsUnsigned();
|
||||
|
||||
// Load all SimpleTags
|
||||
PropertyMap tagEntries;
|
||||
List<Element *> simpleTags = (*i)->getChildren(Constants::SimpleTag);
|
||||
for(List<Element *>::Iterator j = simpleTags.begin(); j != simpleTags.end();
|
||||
++j) {
|
||||
String name, value;
|
||||
if(!extractContent(*j, name, value))
|
||||
continue;
|
||||
tagEntries.insert(name, StringList(value));
|
||||
}
|
||||
|
||||
tags.append(std::pair<PropertyMap, std::pair<Element *, ulli> >(tagEntries, std::pair<Element *, ulli>(*i, ttvalue)));
|
||||
}
|
||||
}
|
||||
|
||||
// Creates Tag and AudioProperties. Late creation because both require a fully
|
||||
// functional FilePrivate (well AudioProperties doesn't...)
|
||||
void lateCreate()
|
||||
{
|
||||
tag = new Tag(document);
|
||||
audio = new AudioProperties(document);
|
||||
}
|
||||
|
||||
// Checks the EBML header and creates the FilePrivate.
|
||||
static FilePrivate *checkAndCreate(File *document)
|
||||
{
|
||||
Element *elem = document->getDocumentRoot()->getChild(Header::EBML);
|
||||
Element *child = elem->getChild(Header::DocType);
|
||||
if(child) {
|
||||
String dt = child->getAsString();
|
||||
if (dt == Constants::DocTypeMatroska || dt == Constants::DocTypeWebM) {
|
||||
FilePrivate *fp = new FilePrivate(document);
|
||||
return fp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The tags with their Element and TargetTypeValue
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > > tags;
|
||||
|
||||
// The tag
|
||||
Tag *tag;
|
||||
|
||||
// The audio properties
|
||||
AudioProperties *audio;
|
||||
|
||||
// The corresponding file.
|
||||
File *document;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::Matroska::File::~File()
|
||||
{
|
||||
delete d->tag;
|
||||
delete d->audio;
|
||||
delete d;
|
||||
}
|
||||
|
||||
EBML::Matroska::File::File(FileName file) : EBML::File(file), d(0)
|
||||
{
|
||||
if(isValid() && isOpen()) {
|
||||
d = FilePrivate::checkAndCreate(this);
|
||||
if(!d)
|
||||
setValid(false);
|
||||
else
|
||||
d->lateCreate();
|
||||
}
|
||||
}
|
||||
|
||||
EBML::Matroska::File::File(IOStream *stream) : EBML::File(stream), d(0)
|
||||
{
|
||||
if(isValid() && isOpen()) {
|
||||
d = FilePrivate::checkAndCreate(this);
|
||||
if(!d)
|
||||
setValid(false);
|
||||
else
|
||||
d->lateCreate();
|
||||
}
|
||||
}
|
||||
|
||||
Tag *EBML::Matroska::File::tag() const
|
||||
{
|
||||
return d->tag;
|
||||
}
|
||||
|
||||
PropertyMap EBML::Matroska::File::properties() const
|
||||
{
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator best = d->tags.end();
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
|
||||
i != d->tags.end(); ++i) {
|
||||
if(best == d->tags.end() || best->second.second > i->second.second)
|
||||
best = i;
|
||||
}
|
||||
return best->first;
|
||||
}
|
||||
|
||||
PropertyMap EBML::Matroska::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator best = d->tags.end();
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
|
||||
i != d->tags.end(); ++i) {
|
||||
if(best == d->tags.end() || best->second.second > i->second.second)
|
||||
best = i;
|
||||
}
|
||||
|
||||
std::pair<PropertyMap, std::pair<Element *, ulli> > replace(properties, best->second);
|
||||
d->tags.erase(best);
|
||||
d->tags.prepend(replace);
|
||||
|
||||
return PropertyMap();
|
||||
}
|
||||
|
||||
AudioProperties *EBML::Matroska::File::audioProperties() const
|
||||
{
|
||||
return d->audio;
|
||||
}
|
||||
|
||||
bool EBML::Matroska::File::save()
|
||||
{
|
||||
if(readOnly())
|
||||
return false;
|
||||
|
||||
// C++11 features would be nice: for(auto &i : d->tags) { /* ... */ }
|
||||
// Well, here we just iterate over each extracted element.
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
|
||||
i != d->tags.end(); ++i) {
|
||||
|
||||
for(PropertyMap::Iterator j = i->first.begin(); j != i->first.end(); ++j) {
|
||||
|
||||
// No element? Create it!
|
||||
if(!i->second.first) {
|
||||
// Should be save, since we already checked, when creating the object.
|
||||
Element *container = d->document->getDocumentRoot()
|
||||
->getChild(Constants::Segment)->getChild(Constants::Tags);
|
||||
|
||||
// Create Targets container
|
||||
i->second.first = container->addElement(Constants::Tag);
|
||||
Element *target = i->second.first->addElement(Constants::Targets);
|
||||
|
||||
if(i->second.second == Constants::MostCommonPartValue)
|
||||
target->addElement(Constants::TargetType, Constants::TRACK);
|
||||
else if(i->second.second == Constants::MostCommonGroupingValue)
|
||||
target->addElement(Constants::TargetType, Constants::ALBUM);
|
||||
|
||||
target->addElement(Constants::TargetTypeValue, i->second.second);
|
||||
}
|
||||
|
||||
// Find entries
|
||||
List<Element *> simpleTags = i->second.first->getChildren(Constants::SimpleTag);
|
||||
StringList::Iterator str = j->second.begin();
|
||||
List<Element *>::Iterator k = simpleTags.begin();
|
||||
for(; k != simpleTags.end(); ++k) {
|
||||
|
||||
String name, value;
|
||||
if(!d->extractContent(*k, name, value))
|
||||
continue;
|
||||
|
||||
// Write entry from StringList
|
||||
if(name == j->first) {
|
||||
if(str == j->second.end()) {
|
||||
// We have all StringList elements but still found another element
|
||||
// with the same name? Let's delete it!
|
||||
i->second.first->removeChild(*k);
|
||||
}
|
||||
else {
|
||||
if(value != *str) {
|
||||
// extractContent already checked for availability
|
||||
(*k)->getChild(Constants::TagString)->setAsString(*str);
|
||||
}
|
||||
++str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't write the complete StringList, we have to write the rest.
|
||||
for(; str != j->second.end(); ++str) {
|
||||
Element *stag = i->second.first->addElement(Constants::SimpleTag);
|
||||
stag->addElement(Constants::TagName, j->first);
|
||||
stag->addElement(Constants::TagString, *str);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, we have to find elements that are not in the PropertyMap and
|
||||
// remove them.
|
||||
List<Element *> simpleTags = i->second.first->getChildren(Constants::SimpleTag);
|
||||
for(List<Element *>::Iterator j = simpleTags.begin(); j != simpleTags.end(); ++j) {
|
||||
|
||||
String name, value;
|
||||
if(!d->extractContent(*j, name, value))
|
||||
continue;
|
||||
|
||||
if(i->first.find(name) == i->first.end()){
|
||||
i->second.first->removeChild(*j);}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Tag
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class EBML::Matroska::File::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
// Creates a TagPrivate instance
|
||||
TagPrivate(File *p_document) :
|
||||
document(p_document),
|
||||
title(document->d->tags.end()),
|
||||
artist(document->d->tags.end()),
|
||||
album(document->d->tags.end()),
|
||||
comment(document->d->tags.end()),
|
||||
genre(document->d->tags.end()),
|
||||
year(document->d->tags.end()),
|
||||
track(document->d->tags.end())
|
||||
{
|
||||
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
|
||||
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
|
||||
|
||||
// Just save it, if the title is more specific, or there is no title yet.
|
||||
if(i->first.find(Constants::TITLE) != i->first.end() &&
|
||||
(title == document->d->tags.end() ||
|
||||
title->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
title = i;
|
||||
}
|
||||
|
||||
// Same goes for artist.
|
||||
if(i->first.find(Constants::ARTIST) != i->first.end() &&
|
||||
(artist == document->d->tags.end() ||
|
||||
artist->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
artist = i;
|
||||
}
|
||||
|
||||
// Here, we also look for a title (the album title), but since we
|
||||
// specified the granularity, we have to search for it exactly.
|
||||
// Therefore it is possible, that title and album are the same (if only
|
||||
// the title of the album is given).
|
||||
if(i->first.find(Constants::TITLE) != i->first.end() &&
|
||||
i->second.second == Constants::MostCommonGroupingValue) {
|
||||
|
||||
album = i;
|
||||
}
|
||||
|
||||
// Again the same as title and artist.
|
||||
if(i->first.find(Constants::COMMENT) != i->first.end() &&
|
||||
(comment == document->d->tags.end() ||
|
||||
comment->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
comment = i;
|
||||
}
|
||||
|
||||
// Same goes for genre.
|
||||
if(i->first.find(Constants::GENRE) != i->first.end() &&
|
||||
(genre == document->d->tags.end() ||
|
||||
genre->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
genre = i;
|
||||
}
|
||||
|
||||
// And year (in our case: DATE_REALEASE)
|
||||
if(i->first.find(Constants::DATE_RELEASE) != i->first.end() &&
|
||||
(year == document->d->tags.end() ||
|
||||
year->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
year = i;
|
||||
}
|
||||
|
||||
// And track (in our case: PART_NUMBER)
|
||||
if(i->first.find(Constants::PART_NUMBER) != i->first.end() &&
|
||||
(track == document->d->tags.end() ||
|
||||
track->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
track = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Searches for the Tag with given TargetTypeValue (returns the first one)
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator
|
||||
find(ulli ttv)
|
||||
{
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
|
||||
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
|
||||
|
||||
if(i->second.second == ttv)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// Updates the given information
|
||||
void update(
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator t,
|
||||
const String &tagname,
|
||||
const String &s
|
||||
)
|
||||
{
|
||||
t->first.find(tagname)->second.front() = s;
|
||||
}
|
||||
|
||||
// Inserts a tag with given information
|
||||
void insert(const String &tagname, const ulli ttv, const String &s)
|
||||
{
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
|
||||
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
|
||||
|
||||
if(i->second.second == ttv) {
|
||||
i->first.insert(tagname, StringList(s));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found? Create new!
|
||||
PropertyMap pm;
|
||||
pm.insert(tagname, StringList(s));
|
||||
document->d->tags.append(
|
||||
std::pair<PropertyMap, std::pair<Element *, ulli> >(pm,
|
||||
std::pair<Element *, ulli>(0, ttv)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// The PropertyMap from the Matroska::File
|
||||
File *document;
|
||||
|
||||
// Iterators to the tags.
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator title;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator artist;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator album;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator comment;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator genre;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator year;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator track;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::Matroska::File::Tag::~Tag()
|
||||
{
|
||||
delete e;
|
||||
}
|
||||
|
||||
EBML::Matroska::File::Tag::Tag(EBML::Matroska::File *document) :
|
||||
e(new EBML::Matroska::File::Tag::TagPrivate(document))
|
||||
{
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::title() const
|
||||
{
|
||||
if(e->title != e->document->d->tags.end())
|
||||
return e->title->first.find(Constants::TITLE)->second.front();
|
||||
else
|
||||
return String::null;
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::artist() const
|
||||
{
|
||||
if(e->artist != e->document->d->tags.end())
|
||||
return e->artist->first.find(Constants::ARTIST)->second.front();
|
||||
else
|
||||
return String::null;
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::album() const
|
||||
{
|
||||
if(e->album != e->document->d->tags.end())
|
||||
return e->album->first.find(Constants::TITLE)->second.front();
|
||||
else
|
||||
return String::null;
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::comment() const
|
||||
{
|
||||
if(e->comment != e->document->d->tags.end())
|
||||
return e->comment->first.find(Constants::COMMENT)->second.front();
|
||||
else
|
||||
return String::null;
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::genre() const
|
||||
{
|
||||
if(e->genre != e->document->d->tags.end())
|
||||
return e->genre->first.find(Constants::GENRE)->second.front();
|
||||
else
|
||||
return String::null;
|
||||
}
|
||||
|
||||
uint EBML::Matroska::File::Tag::year() const
|
||||
{
|
||||
if(e->year != e->document->d->tags.end())
|
||||
return e->year->first.find(Constants::DATE_RELEASE)->second.front().toInt();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint EBML::Matroska::File::Tag::track() const
|
||||
{
|
||||
if(e->track != e->document->d->tags.end())
|
||||
return e->track->first.find(Constants::PART_NUMBER)->second.front().toInt();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setTitle(const String &s)
|
||||
{
|
||||
if(e->title != e->document->d->tags.end())
|
||||
e->update(e->title, Constants::TITLE, s);
|
||||
else
|
||||
e->insert(Constants::TITLE, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setArtist(const String &s)
|
||||
{
|
||||
if(e->artist != e->document->d->tags.end())
|
||||
e->update(e->artist, Constants::ARTIST, s);
|
||||
else
|
||||
e->insert(Constants::ARTIST, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setAlbum(const String &s)
|
||||
{
|
||||
if(e->album != e->document->d->tags.end())
|
||||
e->update(e->album, Constants::TITLE, s);
|
||||
else
|
||||
e->insert(Constants::TITLE, Constants::MostCommonGroupingValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setComment(const String &s)
|
||||
{
|
||||
if(e->comment != e->document->d->tags.end())
|
||||
e->update(e->comment, Constants::COMMENT, s);
|
||||
else
|
||||
e->insert(Constants::COMMENT, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setGenre(const String &s)
|
||||
{
|
||||
if(e->genre != e->document->d->tags.end())
|
||||
e->update(e->genre, Constants::GENRE, s);
|
||||
else
|
||||
e->insert(Constants::GENRE, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setYear(uint i)
|
||||
{
|
||||
String s = String::number(i);
|
||||
if(e->year != e->document->d->tags.end())
|
||||
e->update(e->year, Constants::DATE_RELEASE, s);
|
||||
else
|
||||
e->insert(Constants::DATE_RELEASE, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setTrack(uint i)
|
||||
{
|
||||
String s = String::number(i);
|
||||
if(e->track != e->document->d->tags.end())
|
||||
e->update(e->track, Constants::PART_NUMBER, s);
|
||||
else
|
||||
e->insert(Constants::PART_NUMBER, Constants::MostCommonPartValue, s);
|
||||
}
|
201
taglib/ebml/matroska/ebmlmatroskafile.h
Normal file
201
taglib/ebml/matroska/ebmlmatroskafile.h
Normal file
@ -0,0 +1,201 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 TAGLIB_EBMLMATROSKAFILE_H
|
||||
#define TAGLIB_EBMLMATROSKAFILE_H
|
||||
|
||||
#include "ebmlelement.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
|
||||
//! Implementation for reading Matroska tags.
|
||||
namespace Matroska {
|
||||
|
||||
/*!
|
||||
* Implements the TagLib::File API and offers access to the tags of the
|
||||
* matroska file.
|
||||
*/
|
||||
class TAGLIB_EXPORT File : public EBML::File
|
||||
{
|
||||
public:
|
||||
//! Destroys the instance of the file.
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Constructs a Matroska File from a file name.
|
||||
*/
|
||||
File(FileName file);
|
||||
|
||||
/*!
|
||||
* Constructs a Matroska File from a stream.
|
||||
*/
|
||||
File(IOStream *stream);
|
||||
|
||||
/*!
|
||||
* Returns the pointer to a tag that allow access on common tags.
|
||||
*/
|
||||
virtual TagLib::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Exports the tags to a PropertyMap. Due to the diversity of the
|
||||
* Matroska format (e.g. multiple media streams in one file, each with
|
||||
* its own tags), only the best fitting tags are taken into account.
|
||||
* There are no unsupported tags.
|
||||
*/
|
||||
virtual PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Sets the tags of the file to those specified in properties. The
|
||||
* returned PropertyMap is always empty.
|
||||
* Note: Only the best fitting tags are taken into account.
|
||||
*/
|
||||
virtual PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to this file's audio properties.
|
||||
*
|
||||
* I'm glad about not having a setAudioProperties method ;)
|
||||
*/
|
||||
virtual AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Saves the file. Returns true on success.
|
||||
*/
|
||||
bool save();
|
||||
|
||||
/*!
|
||||
* Offers access to a few common tag entries.
|
||||
*/
|
||||
class Tag : public TagLib::Tag
|
||||
{
|
||||
public:
|
||||
//! Destroys the tag.
|
||||
~Tag();
|
||||
|
||||
/*!
|
||||
* Creates a new Tag for Matroska files. The given properties are gained
|
||||
* by the Matroska::File.
|
||||
*/
|
||||
Tag(File *document);
|
||||
|
||||
/*!
|
||||
* Returns the track name; if no track name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String title() const;
|
||||
|
||||
/*!
|
||||
* Returns the artist name; if no artist name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String artist() const;
|
||||
|
||||
/*!
|
||||
* Returns the album name; if no album name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String album() const;
|
||||
|
||||
/*!
|
||||
* Returns the track comment; if no comment is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String comment() const;
|
||||
|
||||
/*!
|
||||
* Returns the genre name; if no genre is present in the tag String::null
|
||||
* will be returned.
|
||||
*/
|
||||
virtual String genre() const;
|
||||
|
||||
/*!
|
||||
* Returns the year; if there is no year set, this will return 0.
|
||||
*/
|
||||
virtual uint year() const;
|
||||
|
||||
/*!
|
||||
* Returns the track number; if there is no track number set, this will
|
||||
* return 0.
|
||||
*/
|
||||
virtual uint track() const;
|
||||
|
||||
/*!
|
||||
* Sets the title to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setTitle(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the artist to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setArtist(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the album to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setAlbum(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the comment to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setComment(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the genre to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setGenre(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the year to i. If s is 0 then this value will be cleared.
|
||||
*/
|
||||
virtual void setYear(uint i);
|
||||
|
||||
/*!
|
||||
* Sets the track to i. If s is 0 then this value will be cleared.
|
||||
*/
|
||||
virtual void setTrack(uint i);
|
||||
|
||||
private:
|
||||
class TagPrivate;
|
||||
TagPrivate *e;
|
||||
};
|
||||
|
||||
private:
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user