mirror of
https://github.com/taglib/taglib.git
synced 2025-07-20 22:14:22 -04:00
Merge pull request #556 from TsudaKageyu/audioprop-asf
(wishlist) ASF: AudioProperties improvements
This commit is contained in:
@ -25,9 +25,11 @@
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "trefcounter.h"
|
||||
#include <trefcounter.h>
|
||||
|
||||
#include "asfattribute.h"
|
||||
#include "asffile.h"
|
||||
#include "asfutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
@ -181,23 +183,23 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
d->pictureValue = Picture::fromInvalid();
|
||||
// extended content descriptor
|
||||
if(kind == 0) {
|
||||
nameLength = f.readWORD();
|
||||
name = f.readString(nameLength);
|
||||
d->type = ASF::Attribute::AttributeTypes(f.readWORD());
|
||||
size = f.readWORD();
|
||||
nameLength = readWORD(&f);
|
||||
name = readString(&f, nameLength);
|
||||
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
|
||||
size = readWORD(&f);
|
||||
}
|
||||
// metadata & metadata library
|
||||
else {
|
||||
int temp = f.readWORD();
|
||||
int temp = readWORD(&f);
|
||||
// metadata library
|
||||
if(kind == 2) {
|
||||
d->language = temp;
|
||||
}
|
||||
d->stream = f.readWORD();
|
||||
nameLength = f.readWORD();
|
||||
d->type = ASF::Attribute::AttributeTypes(f.readWORD());
|
||||
size = f.readDWORD();
|
||||
name = f.readString(nameLength);
|
||||
d->stream = readWORD(&f);
|
||||
nameLength = readWORD(&f);
|
||||
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
|
||||
size = readDWORD(&f);
|
||||
name = readString(&f, nameLength);
|
||||
}
|
||||
|
||||
if(kind != 2 && size > 65535) {
|
||||
@ -206,28 +208,28 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
|
||||
switch(d->type) {
|
||||
case WordType:
|
||||
d->shortValue = f.readWORD();
|
||||
d->shortValue = readWORD(&f);
|
||||
break;
|
||||
|
||||
case BoolType:
|
||||
if(kind == 0) {
|
||||
d->boolValue = f.readDWORD() == 1;
|
||||
d->boolValue = (readDWORD(&f) == 1);
|
||||
}
|
||||
else {
|
||||
d->boolValue = f.readWORD() == 1;
|
||||
d->boolValue = (readWORD(&f) == 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case DWordType:
|
||||
d->intValue = f.readDWORD();
|
||||
d->intValue = readDWORD(&f);
|
||||
break;
|
||||
|
||||
case QWordType:
|
||||
d->longLongValue = f.readQWORD();
|
||||
d->longLongValue = readQWORD(&f);
|
||||
break;
|
||||
|
||||
case UnicodeType:
|
||||
d->stringValue = f.readString(size);
|
||||
d->stringValue = readString(&f, size);
|
||||
break;
|
||||
|
||||
case BytesType:
|
||||
@ -295,7 +297,7 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
|
||||
break;
|
||||
|
||||
case UnicodeType:
|
||||
data.append(File::renderString(d->stringValue));
|
||||
data.append(renderString(d->stringValue));
|
||||
break;
|
||||
|
||||
case BytesType:
|
||||
@ -309,13 +311,13 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
|
||||
}
|
||||
|
||||
if(kind == 0) {
|
||||
data = File::renderString(name, true) +
|
||||
data = renderString(name, true) +
|
||||
ByteVector::fromShort((int)d->type, false) +
|
||||
ByteVector::fromShort(data.size(), false) +
|
||||
data;
|
||||
}
|
||||
else {
|
||||
ByteVector nameData = File::renderString(name);
|
||||
ByteVector nameData = renderString(name);
|
||||
data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
|
||||
ByteVector::fromShort(d->stream, false) +
|
||||
ByteVector::fromShort(nameData.size(), false) +
|
||||
|
@ -23,19 +23,35 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
// The implementation of this class is based on the document found at:
|
||||
// http://download.microsoft.com/download/8/0/5/80506BEB-C95A-47AE-99CF-0D6D6D028ABA/ASF_Specification.pdf
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tstring.h>
|
||||
|
||||
#include "asffile.h"
|
||||
#include "asftag.h"
|
||||
#include "asfproperties.h"
|
||||
#include "asfutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class ASF::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
class BaseObject;
|
||||
class UnknownObject;
|
||||
class FilePropertiesObject;
|
||||
class StreamPropertiesObject;
|
||||
class ContentDescriptionObject;
|
||||
class ExtendedContentDescriptionObject;
|
||||
class HeaderExtensionObject;
|
||||
class CodecListObject;
|
||||
class MetadataObject;
|
||||
class MetadataLibraryObject;
|
||||
|
||||
FilePrivate():
|
||||
size(0),
|
||||
tag(0),
|
||||
@ -44,192 +60,227 @@ public:
|
||||
extendedContentDescriptionObject(0),
|
||||
headerExtensionObject(0),
|
||||
metadataObject(0),
|
||||
metadataLibraryObject(0) {}
|
||||
metadataLibraryObject(0)
|
||||
{
|
||||
objects.setAutoDelete(true);
|
||||
}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
delete tag;
|
||||
delete properties;
|
||||
}
|
||||
|
||||
unsigned long long size;
|
||||
|
||||
ASF::Tag *tag;
|
||||
ASF::Properties *properties;
|
||||
List<ASF::File::BaseObject *> objects;
|
||||
ASF::File::ContentDescriptionObject *contentDescriptionObject;
|
||||
ASF::File::ExtendedContentDescriptionObject *extendedContentDescriptionObject;
|
||||
ASF::File::HeaderExtensionObject *headerExtensionObject;
|
||||
ASF::File::MetadataObject *metadataObject;
|
||||
ASF::File::MetadataLibraryObject *metadataLibraryObject;
|
||||
|
||||
List<BaseObject *> objects;
|
||||
|
||||
ContentDescriptionObject *contentDescriptionObject;
|
||||
ExtendedContentDescriptionObject *extendedContentDescriptionObject;
|
||||
HeaderExtensionObject *headerExtensionObject;
|
||||
MetadataObject *metadataObject;
|
||||
MetadataLibraryObject *metadataLibraryObject;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
static const ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
|
||||
static const ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16);
|
||||
static const ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16);
|
||||
static const ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
|
||||
static const ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16);
|
||||
static const ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16);
|
||||
static const ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16);
|
||||
static const ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16);
|
||||
static const ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16);
|
||||
static const ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16);
|
||||
static const ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16);
|
||||
const ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
|
||||
const ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16);
|
||||
const ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16);
|
||||
const ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
|
||||
const ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16);
|
||||
const ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16);
|
||||
const ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16);
|
||||
const ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16);
|
||||
const ByteVector codecListGuid("\x40\x52\xd1\x86\x1d\x31\xd0\x11\xa3\xa4\x00\xa0\xc9\x03\x48\xf6", 16);
|
||||
const ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16);
|
||||
const ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16);
|
||||
const ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16);
|
||||
}
|
||||
|
||||
class ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector data;
|
||||
virtual ~BaseObject() {}
|
||||
virtual ByteVector guid() = 0;
|
||||
virtual ByteVector guid() const = 0;
|
||||
virtual void parse(ASF::File *file, unsigned int size);
|
||||
virtual ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
class ASF::File::UnknownObject : public ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
ByteVector myGuid;
|
||||
public:
|
||||
UnknownObject(const ByteVector &guid);
|
||||
ByteVector guid();
|
||||
ByteVector guid() const;
|
||||
};
|
||||
|
||||
class ASF::File::FilePropertiesObject : public ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::FilePropertiesObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid();
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
};
|
||||
|
||||
class ASF::File::StreamPropertiesObject : public ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::StreamPropertiesObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid();
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
};
|
||||
|
||||
class ASF::File::ContentDescriptionObject : public ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::ContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid();
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
class ASF::File::ExtendedContentDescriptionObject : public ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::ExtendedContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid();
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
class ASF::File::MetadataObject : public ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::MetadataObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid();
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
class ASF::File::MetadataLibraryObject : public ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::MetadataLibraryObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid();
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
class ASF::File::HeaderExtensionObject : public ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
List<ASF::File::BaseObject *> objects;
|
||||
~HeaderExtensionObject();
|
||||
ByteVector guid();
|
||||
List<ASF::File::FilePrivate::BaseObject *> objects;
|
||||
HeaderExtensionObject();
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
ASF::File::HeaderExtensionObject::~HeaderExtensionObject()
|
||||
class ASF::File::FilePrivate::CodecListObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
for(unsigned int i = 0; i < objects.size(); i++) {
|
||||
delete objects[i];
|
||||
}
|
||||
}
|
||||
public:
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
|
||||
void ASF::File::BaseObject::parse(ASF::File *file, unsigned int size)
|
||||
private:
|
||||
enum CodecType
|
||||
{
|
||||
Video = 0x0001,
|
||||
Audio = 0x0002,
|
||||
Unknown = 0xFFFF
|
||||
};
|
||||
};
|
||||
|
||||
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size)
|
||||
{
|
||||
data.clear();
|
||||
if (size > 24 && size <= (unsigned int)(file->length()))
|
||||
if(size > 24 && size <= (unsigned int)(file->length()))
|
||||
data = file->readBlock(size - 24);
|
||||
else
|
||||
data = ByteVector::null;
|
||||
}
|
||||
|
||||
ByteVector ASF::File::BaseObject::render(ASF::File * /*file*/)
|
||||
ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/)
|
||||
{
|
||||
return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data;
|
||||
}
|
||||
|
||||
ASF::File::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
|
||||
ASF::File::FilePrivate::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
|
||||
{
|
||||
}
|
||||
|
||||
ByteVector ASF::File::UnknownObject::guid()
|
||||
ByteVector ASF::File::FilePrivate::UnknownObject::guid() const
|
||||
{
|
||||
return myGuid;
|
||||
}
|
||||
|
||||
ByteVector ASF::File::FilePropertiesObject::guid()
|
||||
ByteVector ASF::File::FilePrivate::FilePropertiesObject::guid() const
|
||||
{
|
||||
return filePropertiesGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePropertiesObject::parse(ASF::File *file, uint size)
|
||||
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, uint size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
file->d->properties->setLength(
|
||||
(int)(data.toLongLong(40, false) / 10000000L - data.toLongLong(56, false) / 1000L));
|
||||
if(data.size() < 64) {
|
||||
debug("ASF::File::FilePrivate::FilePropertiesObject::parse() -- data is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
const long long duration = data.toLongLong(40, false);
|
||||
const long long preroll = data.toLongLong(56, false);
|
||||
file->d->properties->setLengthInMilliseconds(static_cast<int>(duration / 10000.0 - preroll + 0.5));
|
||||
}
|
||||
|
||||
ByteVector ASF::File::StreamPropertiesObject::guid()
|
||||
ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const
|
||||
{
|
||||
return streamPropertiesGuid;
|
||||
}
|
||||
|
||||
void ASF::File::StreamPropertiesObject::parse(ASF::File *file, uint size)
|
||||
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, uint size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
file->d->properties->setChannels(data.toShort(56, false));
|
||||
if(data.size() < 70) {
|
||||
debug("ASF::File::FilePrivate::StreamPropertiesObject::parse() -- data is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
file->d->properties->setCodec(data.toUShort(54, false));
|
||||
file->d->properties->setChannels(data.toUShort(56, false));
|
||||
file->d->properties->setSampleRate(data.toUInt(58, false));
|
||||
file->d->properties->setBitrate(data.toUInt(62, false) * 8 / 1000);
|
||||
file->d->properties->setBitrate(static_cast<int>(data.toUInt(62, false) * 8.0 / 1000.0 + 0.5));
|
||||
file->d->properties->setBitsPerSample(data.toUShort(68, false));
|
||||
}
|
||||
|
||||
ByteVector ASF::File::ContentDescriptionObject::guid()
|
||||
ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
|
||||
{
|
||||
return contentDescriptionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::ContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
|
||||
{
|
||||
file->d->contentDescriptionObject = this;
|
||||
int titleLength = file->readWORD();
|
||||
int artistLength = file->readWORD();
|
||||
int copyrightLength = file->readWORD();
|
||||
int commentLength = file->readWORD();
|
||||
int ratingLength = file->readWORD();
|
||||
file->d->tag->setTitle(file->readString(titleLength));
|
||||
file->d->tag->setArtist(file->readString(artistLength));
|
||||
file->d->tag->setCopyright(file->readString(copyrightLength));
|
||||
file->d->tag->setComment(file->readString(commentLength));
|
||||
file->d->tag->setRating(file->readString(ratingLength));
|
||||
const int titleLength = readWORD(file);
|
||||
const int artistLength = readWORD(file);
|
||||
const int copyrightLength = readWORD(file);
|
||||
const int commentLength = readWORD(file);
|
||||
const int ratingLength = readWORD(file);
|
||||
file->d->tag->setTitle(readString(file,titleLength));
|
||||
file->d->tag->setArtist(readString(file,artistLength));
|
||||
file->d->tag->setCopyright(readString(file,copyrightLength));
|
||||
file->d->tag->setComment(readString(file,commentLength));
|
||||
file->d->tag->setRating(readString(file,ratingLength));
|
||||
}
|
||||
|
||||
ByteVector ASF::File::ContentDescriptionObject::render(ASF::File *file)
|
||||
ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *file)
|
||||
{
|
||||
ByteVector v1 = file->renderString(file->d->tag->title());
|
||||
ByteVector v2 = file->renderString(file->d->tag->artist());
|
||||
ByteVector v3 = file->renderString(file->d->tag->copyright());
|
||||
ByteVector v4 = file->renderString(file->d->tag->comment());
|
||||
ByteVector v5 = file->renderString(file->d->tag->rating());
|
||||
const ByteVector v1 = renderString(file->d->tag->title());
|
||||
const ByteVector v2 = renderString(file->d->tag->artist());
|
||||
const ByteVector v3 = renderString(file->d->tag->copyright());
|
||||
const ByteVector v4 = renderString(file->d->tag->comment());
|
||||
const ByteVector v5 = renderString(file->d->tag->rating());
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(v1.size(), false));
|
||||
data.append(ByteVector::fromShort(v2.size(), false));
|
||||
@ -244,15 +295,15 @@ ByteVector ASF::File::ContentDescriptionObject::render(ASF::File *file)
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::ExtendedContentDescriptionObject::guid()
|
||||
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() const
|
||||
{
|
||||
return extendedContentDescriptionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
|
||||
{
|
||||
file->d->extendedContentDescriptionObject = this;
|
||||
int count = file->readWORD();
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
ASF::Attribute attribute;
|
||||
String name = attribute.parse(*file);
|
||||
@ -260,7 +311,7 @@ void ASF::File::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /*
|
||||
}
|
||||
}
|
||||
|
||||
ByteVector ASF::File::ExtendedContentDescriptionObject::render(ASF::File *file)
|
||||
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
@ -268,15 +319,15 @@ ByteVector ASF::File::ExtendedContentDescriptionObject::render(ASF::File *file)
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::MetadataObject::guid()
|
||||
ByteVector ASF::File::FilePrivate::MetadataObject::guid() const
|
||||
{
|
||||
return metadataGuid;
|
||||
}
|
||||
|
||||
void ASF::File::MetadataObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, uint /*size*/)
|
||||
{
|
||||
file->d->metadataObject = this;
|
||||
int count = file->readWORD();
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
ASF::Attribute attribute;
|
||||
String name = attribute.parse(*file, 1);
|
||||
@ -284,7 +335,7 @@ void ASF::File::MetadataObject::parse(ASF::File *file, uint /*size*/)
|
||||
}
|
||||
}
|
||||
|
||||
ByteVector ASF::File::MetadataObject::render(ASF::File *file)
|
||||
ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
@ -292,15 +343,15 @@ ByteVector ASF::File::MetadataObject::render(ASF::File *file)
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::MetadataLibraryObject::guid()
|
||||
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const
|
||||
{
|
||||
return metadataLibraryGuid;
|
||||
}
|
||||
|
||||
void ASF::File::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/)
|
||||
{
|
||||
file->d->metadataLibraryObject = this;
|
||||
int count = file->readWORD();
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
ASF::Attribute attribute;
|
||||
String name = attribute.parse(*file, 2);
|
||||
@ -308,7 +359,7 @@ void ASF::File::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/)
|
||||
}
|
||||
}
|
||||
|
||||
ByteVector ASF::File::MetadataLibraryObject::render(ASF::File *file)
|
||||
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
@ -316,16 +367,21 @@ ByteVector ASF::File::MetadataLibraryObject::render(ASF::File *file)
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::HeaderExtensionObject::guid()
|
||||
ASF::File::FilePrivate::HeaderExtensionObject::HeaderExtensionObject()
|
||||
{
|
||||
objects.setAutoDelete(true);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
|
||||
{
|
||||
return headerExtensionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
|
||||
{
|
||||
file->d->headerExtensionObject = this;
|
||||
file->seek(18, File::Current);
|
||||
long long dataSize = file->readDWORD();
|
||||
long long dataSize = readDWORD(file);
|
||||
long long dataPos = 0;
|
||||
while(dataPos < dataSize) {
|
||||
ByteVector guid = file->readBlock(16);
|
||||
@ -334,7 +390,7 @@ void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
|
||||
break;
|
||||
}
|
||||
bool ok;
|
||||
long long size = file->readQWORD(&ok);
|
||||
long long size = readQWORD(file, &ok);
|
||||
if(!ok) {
|
||||
file->setValid(false);
|
||||
break;
|
||||
@ -355,47 +411,93 @@ void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
|
||||
}
|
||||
}
|
||||
|
||||
ByteVector ASF::File::HeaderExtensionObject::render(ASF::File *file)
|
||||
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
for(unsigned int i = 0; i < objects.size(); i++) {
|
||||
data.append(objects[i]->render(file));
|
||||
for(List<BaseObject *>::ConstIterator it = objects.begin(); it != objects.end(); ++it) {
|
||||
data.append((*it)->render(file));
|
||||
}
|
||||
data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data;
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::FilePrivate::CodecListObject::guid() const
|
||||
{
|
||||
return codecListGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, uint size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
if(data.size() <= 20) {
|
||||
debug("ASF::File::FilePrivate::CodecListObject::parse() -- data is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint pos = 16;
|
||||
|
||||
const int count = data.toUInt(pos, false);
|
||||
pos += 4;
|
||||
|
||||
for(int i = 0; i < count; ++i) {
|
||||
|
||||
if(pos >= data.size())
|
||||
break;
|
||||
|
||||
const CodecType type = static_cast<CodecType>(data.toUShort(pos, false));
|
||||
pos += 2;
|
||||
|
||||
int nameLength = data.toUShort(pos, false);
|
||||
pos += 2;
|
||||
|
||||
const uint namePos = pos;
|
||||
pos += nameLength * 2;
|
||||
|
||||
const int descLength = data.toUShort(pos, false);
|
||||
pos += 2;
|
||||
|
||||
const uint descPos = pos;
|
||||
pos += descLength * 2;
|
||||
|
||||
const int infoLength = data.toUShort(pos, false);
|
||||
pos += 2 + infoLength * 2;
|
||||
|
||||
if(type == CodecListObject::Audio) {
|
||||
// First audio codec found.
|
||||
|
||||
const String name(data.mid(namePos, nameLength * 2), String::UTF16LE);
|
||||
file->d->properties->setCodecName(name.stripWhiteSpace());
|
||||
|
||||
const String desc(data.mid(descPos, descLength * 2), String::UTF16LE);
|
||||
file->d->properties->setCodecDescription(desc.stripWhiteSpace());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle)
|
||||
: TagLib::File(file)
|
||||
ASF::File::File(FileName file, bool, Properties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
d = new FilePrivate;
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
read();
|
||||
}
|
||||
|
||||
ASF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle)
|
||||
: TagLib::File(stream)
|
||||
ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
d = new FilePrivate;
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
read();
|
||||
}
|
||||
|
||||
ASF::File::~File()
|
||||
{
|
||||
for(unsigned int i = 0; i < d->objects.size(); i++) {
|
||||
delete d->objects[i];
|
||||
}
|
||||
if(d->tag) {
|
||||
delete d->tag;
|
||||
}
|
||||
if(d->properties) {
|
||||
delete d->properties;
|
||||
}
|
||||
delete d;
|
||||
}
|
||||
|
||||
@ -424,74 +526,6 @@ ASF::Properties *ASF::File::audioProperties() const
|
||||
return d->properties;
|
||||
}
|
||||
|
||||
void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*propertiesStyle*/)
|
||||
{
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
ByteVector guid = readBlock(16);
|
||||
if(guid != headerGuid) {
|
||||
debug("ASF: Not an ASF file.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->tag = new ASF::Tag();
|
||||
d->properties = new ASF::Properties();
|
||||
|
||||
bool ok;
|
||||
d->size = readQWORD(&ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
int numObjects = readDWORD(&ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
seek(2, Current);
|
||||
|
||||
for(int i = 0; i < numObjects; i++) {
|
||||
ByteVector guid = readBlock(16);
|
||||
if(guid.size() != 16) {
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
long size = (long)readQWORD(&ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
BaseObject *obj;
|
||||
if(guid == filePropertiesGuid) {
|
||||
obj = new FilePropertiesObject();
|
||||
}
|
||||
else if(guid == streamPropertiesGuid) {
|
||||
obj = new StreamPropertiesObject();
|
||||
}
|
||||
else if(guid == contentDescriptionGuid) {
|
||||
obj = new ContentDescriptionObject();
|
||||
}
|
||||
else if(guid == extendedContentDescriptionGuid) {
|
||||
obj = new ExtendedContentDescriptionObject();
|
||||
}
|
||||
else if(guid == headerExtensionGuid) {
|
||||
obj = new HeaderExtensionObject();
|
||||
}
|
||||
else {
|
||||
if(guid == contentEncryptionGuid ||
|
||||
guid == extendedContentEncryptionGuid ||
|
||||
guid == advancedContentEncryptionGuid) {
|
||||
d->properties->setEncrypted(true);
|
||||
}
|
||||
obj = new UnknownObject(guid);
|
||||
}
|
||||
obj->parse(this, size);
|
||||
d->objects.append(obj);
|
||||
}
|
||||
}
|
||||
|
||||
bool ASF::File::save()
|
||||
{
|
||||
if(readOnly()) {
|
||||
@ -505,28 +539,29 @@ bool ASF::File::save()
|
||||
}
|
||||
|
||||
if(!d->contentDescriptionObject) {
|
||||
d->contentDescriptionObject = new ContentDescriptionObject();
|
||||
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
|
||||
d->objects.append(d->contentDescriptionObject);
|
||||
}
|
||||
if(!d->extendedContentDescriptionObject) {
|
||||
d->extendedContentDescriptionObject = new ExtendedContentDescriptionObject();
|
||||
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
|
||||
d->objects.append(d->extendedContentDescriptionObject);
|
||||
}
|
||||
if(!d->headerExtensionObject) {
|
||||
d->headerExtensionObject = new HeaderExtensionObject();
|
||||
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
|
||||
d->objects.append(d->headerExtensionObject);
|
||||
}
|
||||
if(!d->metadataObject) {
|
||||
d->metadataObject = new MetadataObject();
|
||||
d->metadataObject = new FilePrivate::MetadataObject();
|
||||
d->headerExtensionObject->objects.append(d->metadataObject);
|
||||
}
|
||||
if(!d->metadataLibraryObject) {
|
||||
d->metadataLibraryObject = new MetadataLibraryObject();
|
||||
d->metadataLibraryObject = new FilePrivate::MetadataLibraryObject();
|
||||
d->headerExtensionObject->objects.append(d->metadataLibraryObject);
|
||||
}
|
||||
|
||||
ASF::AttributeListMap::ConstIterator it = d->tag->attributeListMap().begin();
|
||||
for(; it != d->tag->attributeListMap().end(); it++) {
|
||||
const AttributeListMap allAttributes = d->tag->attributeListMap();
|
||||
|
||||
for(AttributeListMap::ConstIterator it = allAttributes.begin(); it != allAttributes.end(); ++it) {
|
||||
|
||||
const String &name = it->first;
|
||||
const AttributeList &attributes = it->second;
|
||||
@ -534,9 +569,9 @@ bool ASF::File::save()
|
||||
bool inExtendedContentDescriptionObject = false;
|
||||
bool inMetadataObject = false;
|
||||
|
||||
for(unsigned int j = 0; j < attributes.size(); j++) {
|
||||
for(AttributeList::ConstIterator jt = attributes.begin(); jt != attributes.end(); ++jt) {
|
||||
|
||||
const Attribute &attribute = attributes[j];
|
||||
const Attribute &attribute = *jt;
|
||||
const bool largeValue = (attribute.dataSize() > 65535);
|
||||
const bool guid = (attribute.type() == Attribute::GuidType);
|
||||
|
||||
@ -555,8 +590,8 @@ bool ASF::File::save()
|
||||
}
|
||||
|
||||
ByteVector data;
|
||||
for(unsigned int i = 0; i < d->objects.size(); i++) {
|
||||
data.append(d->objects[i]->render(this));
|
||||
for(List<FilePrivate::BaseObject *>::ConstIterator it = d->objects.begin(); it != d->objects.end(); ++it) {
|
||||
data.append((*it)->render(this));
|
||||
}
|
||||
|
||||
data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data;
|
||||
@ -566,75 +601,76 @@ bool ASF::File::save()
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected members
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int ASF::File::readBYTE(bool *ok)
|
||||
void ASF::File::read()
|
||||
{
|
||||
ByteVector v = readBlock(1);
|
||||
if(v.size() != 1) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v[0];
|
||||
}
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
int ASF::File::readWORD(bool *ok)
|
||||
{
|
||||
ByteVector v = readBlock(2);
|
||||
if(v.size() != 2) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
ByteVector guid = readBlock(16);
|
||||
if(guid != headerGuid) {
|
||||
debug("ASF: Not an ASF file.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toUShort(false);
|
||||
}
|
||||
|
||||
unsigned int ASF::File::readDWORD(bool *ok)
|
||||
{
|
||||
ByteVector v = readBlock(4);
|
||||
if(v.size() != 4) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
d->tag = new ASF::Tag();
|
||||
d->properties = new ASF::Properties();
|
||||
|
||||
bool ok;
|
||||
d->size = readQWORD(this, &ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toUInt(false);
|
||||
}
|
||||
|
||||
long long ASF::File::readQWORD(bool *ok)
|
||||
{
|
||||
ByteVector v = readBlock(8);
|
||||
if(v.size() != 8) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
int numObjects = readDWORD(this, &ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toLongLong(false);
|
||||
}
|
||||
seek(2, Current);
|
||||
|
||||
String ASF::File::readString(int length)
|
||||
{
|
||||
ByteVector data = readBlock(length);
|
||||
unsigned int size = data.size();
|
||||
while (size >= 2) {
|
||||
if(data[size - 1] != '\0' || data[size - 2] != '\0') {
|
||||
for(int i = 0; i < numObjects; i++) {
|
||||
guid = readBlock(16);
|
||||
if(guid.size() != 16) {
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
size -= 2;
|
||||
long size = (long)readQWORD(this, &ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
FilePrivate::BaseObject *obj;
|
||||
if(guid == filePropertiesGuid) {
|
||||
obj = new FilePrivate::FilePropertiesObject();
|
||||
}
|
||||
else if(guid == streamPropertiesGuid) {
|
||||
obj = new FilePrivate::StreamPropertiesObject();
|
||||
}
|
||||
else if(guid == contentDescriptionGuid) {
|
||||
obj = new FilePrivate::ContentDescriptionObject();
|
||||
}
|
||||
else if(guid == extendedContentDescriptionGuid) {
|
||||
obj = new FilePrivate::ExtendedContentDescriptionObject();
|
||||
}
|
||||
else if(guid == headerExtensionGuid) {
|
||||
obj = new FilePrivate::HeaderExtensionObject();
|
||||
}
|
||||
else if(guid == codecListGuid) {
|
||||
obj = new FilePrivate::CodecListObject();
|
||||
}
|
||||
else {
|
||||
if(guid == contentEncryptionGuid ||
|
||||
guid == extendedContentEncryptionGuid ||
|
||||
guid == advancedContentEncryptionGuid) {
|
||||
d->properties->setEncrypted(true);
|
||||
}
|
||||
obj = new FilePrivate::UnknownObject(guid);
|
||||
}
|
||||
obj->parse(this, size);
|
||||
d->objects.append(obj);
|
||||
}
|
||||
if(size != data.size()) {
|
||||
data.resize(size);
|
||||
}
|
||||
return String(data, String::UTF16LE);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::renderString(const String &str, bool includeLength)
|
||||
{
|
||||
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
|
||||
if(includeLength) {
|
||||
data = ByteVector::fromShort(data.size(), false) + data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -116,26 +116,7 @@ namespace TagLib {
|
||||
virtual bool save();
|
||||
|
||||
private:
|
||||
int readBYTE(bool *ok = 0);
|
||||
int readWORD(bool *ok = 0);
|
||||
unsigned int readDWORD(bool *ok = 0);
|
||||
long long readQWORD(bool *ok = 0);
|
||||
static ByteVector renderString(const String &str, bool includeLength = false);
|
||||
String readString(int len);
|
||||
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
|
||||
|
||||
friend class Attribute;
|
||||
friend class Picture;
|
||||
|
||||
class BaseObject;
|
||||
class UnknownObject;
|
||||
class FilePropertiesObject;
|
||||
class StreamPropertiesObject;
|
||||
class ContentDescriptionObject;
|
||||
class ExtendedContentDescriptionObject;
|
||||
class HeaderExtensionObject;
|
||||
class MetadataObject;
|
||||
class MetadataLibraryObject;
|
||||
void read();
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
|
@ -25,10 +25,12 @@
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "trefcounter.h"
|
||||
#include <trefcounter.h>
|
||||
|
||||
#include "asfattribute.h"
|
||||
#include "asffile.h"
|
||||
#include "asfpicture.h"
|
||||
#include "asfutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
@ -134,8 +136,8 @@ ByteVector ASF::Picture::render() const
|
||||
return
|
||||
ByteVector((char)d->type) +
|
||||
ByteVector::fromUInt(d->picture.size(), false) +
|
||||
ASF::File::renderString(d->mimeType) +
|
||||
ASF::File::renderString(d->description) +
|
||||
renderString(d->mimeType) +
|
||||
renderString(d->description) +
|
||||
d->picture;
|
||||
}
|
||||
|
||||
|
@ -205,8 +205,8 @@ namespace TagLib
|
||||
/* THIS IS PRIVATE, DON'T TOUCH IT! */
|
||||
void parse(const ByteVector& );
|
||||
static Picture fromInvalid();
|
||||
friend class Attribute;
|
||||
#endif
|
||||
|
||||
private:
|
||||
class PicturePrivate;
|
||||
PicturePrivate *d;
|
||||
|
@ -32,11 +32,23 @@ using namespace TagLib;
|
||||
class ASF::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate(): length(0), bitrate(0), sampleRate(0), channels(0), encrypted(false) {}
|
||||
PropertiesPrivate() :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
sampleRate(0),
|
||||
channels(0),
|
||||
bitsPerSample(0),
|
||||
codec(ASF::Properties::Unknown),
|
||||
encrypted(false) {}
|
||||
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int bitsPerSample;
|
||||
ASF::Properties::Codec codec;
|
||||
String codecName;
|
||||
String codecDescription;
|
||||
bool encrypted;
|
||||
};
|
||||
|
||||
@ -44,9 +56,10 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ASF::Properties::Properties() : AudioProperties(AudioProperties::Average)
|
||||
ASF::Properties::Properties() :
|
||||
AudioProperties(AudioProperties::Average),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
d = new PropertiesPrivate;
|
||||
}
|
||||
|
||||
ASF::Properties::~Properties()
|
||||
@ -55,6 +68,16 @@ ASF::Properties::~Properties()
|
||||
}
|
||||
|
||||
int ASF::Properties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int ASF::Properties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int ASF::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
@ -74,6 +97,26 @@ int ASF::Properties::channels() const
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int ASF::Properties::bitsPerSample() const
|
||||
{
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
ASF::Properties::Codec ASF::Properties::codec() const
|
||||
{
|
||||
return d->codec;
|
||||
}
|
||||
|
||||
String ASF::Properties::codecName() const
|
||||
{
|
||||
return d->codecName;
|
||||
}
|
||||
|
||||
String ASF::Properties::codecDescription() const
|
||||
{
|
||||
return d->codecDescription;
|
||||
}
|
||||
|
||||
bool ASF::Properties::isEncrypted() const
|
||||
{
|
||||
return d->encrypted;
|
||||
@ -83,28 +126,69 @@ bool ASF::Properties::isEncrypted() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ASF::Properties::setLength(int length)
|
||||
void ASF::Properties::setLength(int /*length*/)
|
||||
{
|
||||
d->length = length;
|
||||
debug("ASF::Properties::setLength() -- This method is deprecated. Do not use.");
|
||||
}
|
||||
|
||||
void ASF::Properties::setBitrate(int length)
|
||||
void ASF::Properties::setLengthInMilliseconds(int value)
|
||||
{
|
||||
d->bitrate = length;
|
||||
d->length = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setSampleRate(int length)
|
||||
void ASF::Properties::setBitrate(int value)
|
||||
{
|
||||
d->sampleRate = length;
|
||||
d->bitrate = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setChannels(int length)
|
||||
void ASF::Properties::setSampleRate(int value)
|
||||
{
|
||||
d->channels = length;
|
||||
d->sampleRate = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setEncrypted(bool encrypted)
|
||||
void ASF::Properties::setChannels(int value)
|
||||
{
|
||||
d->encrypted = encrypted;
|
||||
d->channels = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setBitsPerSample(int value)
|
||||
{
|
||||
d->bitsPerSample = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setCodec(int value)
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
case 0x0160:
|
||||
d->codec = WMA1;
|
||||
break;
|
||||
case 0x0161:
|
||||
d->codec = WMA2;
|
||||
break;
|
||||
case 0x0162:
|
||||
d->codec = WMA9Pro;
|
||||
break;
|
||||
case 0x0163:
|
||||
d->codec = WMA9Lossless;
|
||||
break;
|
||||
default:
|
||||
d->codec = Unknown;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ASF::Properties::setCodecName(const String &value)
|
||||
{
|
||||
d->codecName = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setCodecDescription(const String &value)
|
||||
{
|
||||
d->codecDescription = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setEncrypted(bool value)
|
||||
{
|
||||
d->encrypted = value;
|
||||
}
|
||||
|
@ -40,7 +40,38 @@ namespace TagLib {
|
||||
public:
|
||||
|
||||
/*!
|
||||
* Create an instance of ASF::Properties.
|
||||
* Audio codec types can be used in ASF file.
|
||||
*/
|
||||
enum Codec
|
||||
{
|
||||
/*!
|
||||
* Couldn't detect the codec.
|
||||
*/
|
||||
Unknown = 0,
|
||||
|
||||
/*!
|
||||
* Windows Media Audio 1
|
||||
*/
|
||||
WMA1,
|
||||
|
||||
/*!
|
||||
* Windows Media Audio 2 or above
|
||||
*/
|
||||
WMA2,
|
||||
|
||||
/*!
|
||||
* Windows Media Audio 9 Professional
|
||||
*/
|
||||
WMA9Pro,
|
||||
|
||||
/*!
|
||||
* Windows Media Audio 9 Lossless
|
||||
*/
|
||||
WMA9Lossless,
|
||||
};
|
||||
|
||||
/*!
|
||||
* Creates an instance of ASF::Properties.
|
||||
*/
|
||||
Properties();
|
||||
|
||||
@ -49,18 +80,97 @@ namespace TagLib {
|
||||
*/
|
||||
virtual ~Properties();
|
||||
|
||||
// Reimplementations.
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \note This method is just an alias of lengthInSeconds().
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInSeconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInMilliseconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
*/
|
||||
virtual int bitrate() const;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
virtual int sampleRate() const;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
virtual int channels() const;
|
||||
|
||||
/*!
|
||||
* Returns the number of bits per audio sample.
|
||||
*/
|
||||
int bitsPerSample() const;
|
||||
|
||||
/*!
|
||||
* Returns the codec used in the file.
|
||||
*
|
||||
* \see codecName()
|
||||
* \see codecDescription()
|
||||
*/
|
||||
Codec codec() const;
|
||||
|
||||
/*!
|
||||
* Returns the concrete codec name, for example "Windows Media Audio 9.1"
|
||||
* used in the file if available, otherwise an empty string.
|
||||
*
|
||||
* \see codec()
|
||||
* \see codecDescription()
|
||||
*/
|
||||
String codecName() const;
|
||||
|
||||
/*!
|
||||
* Returns the codec description, typically contains the encoder settings,
|
||||
* for example "VBR Quality 50, 44kHz, stereo 1-pass VBR" if available,
|
||||
* otherwise an empty string.
|
||||
*
|
||||
* \see codec()
|
||||
* \see codecName()
|
||||
*/
|
||||
String codecDescription() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file is encrypted.
|
||||
*/
|
||||
bool isEncrypted() const;
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
// deprecated
|
||||
void setLength(int value);
|
||||
|
||||
void setLengthInMilliseconds(int value);
|
||||
void setBitrate(int value);
|
||||
void setSampleRate(int value);
|
||||
void setChannels(int value);
|
||||
void setBitsPerSample(int value);
|
||||
void setCodec(int value);
|
||||
void setCodecName(const String &value);
|
||||
void setCodecDescription(const String &value);
|
||||
void setEncrypted(bool value);
|
||||
#endif
|
||||
|
||||
|
101
taglib/asf/asfutils.h
Normal file
101
taglib/asf/asfutils.h
Normal file
@ -0,0 +1,101 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2015 by Tsuda Kageyu
|
||||
email : tsuda.kageyu@gmail.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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_ASFUTILS_H
|
||||
#define TAGLIB_ASFUTILS_H
|
||||
|
||||
// THIS FILE IS NOT A PART OF THE TAGLIB API
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header
|
||||
|
||||
namespace TagLib
|
||||
{
|
||||
namespace ASF
|
||||
{
|
||||
|
||||
inline ushort readWORD(File *file, bool *ok = 0)
|
||||
{
|
||||
const ByteVector v = file->readBlock(2);
|
||||
if(v.size() != 2) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toUShort(false);
|
||||
}
|
||||
|
||||
inline uint readDWORD(File *file, bool *ok = 0)
|
||||
{
|
||||
const ByteVector v = file->readBlock(4);
|
||||
if(v.size() != 4) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toUInt(false);
|
||||
}
|
||||
|
||||
inline long long readQWORD(File *file, bool *ok = 0)
|
||||
{
|
||||
const ByteVector v = file->readBlock(8);
|
||||
if(v.size() != 8) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toLongLong(false);
|
||||
}
|
||||
|
||||
inline String readString(File *file, int length)
|
||||
{
|
||||
ByteVector data = file->readBlock(length);
|
||||
unsigned int size = data.size();
|
||||
while (size >= 2) {
|
||||
if(data[size - 1] != '\0' || data[size - 2] != '\0') {
|
||||
break;
|
||||
}
|
||||
size -= 2;
|
||||
}
|
||||
if(size != data.size()) {
|
||||
data.resize(size);
|
||||
}
|
||||
return String(data, String::UTF16LE);
|
||||
}
|
||||
|
||||
inline ByteVector renderString(const String &str, bool includeLength = false)
|
||||
{
|
||||
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
|
||||
if(includeLength) {
|
||||
data = ByteVector::fromShort(data.size(), false) + data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
BIN
tests/data/lossless.wma
Normal file
BIN
tests/data/lossless.wma
Normal file
Binary file not shown.
@ -15,6 +15,7 @@ class TestASF : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(TestASF);
|
||||
CPPUNIT_TEST(testAudioProperties);
|
||||
CPPUNIT_TEST(testLosslessProperties);
|
||||
CPPUNIT_TEST(testRead);
|
||||
CPPUNIT_TEST(testSaveMultipleValues);
|
||||
CPPUNIT_TEST(testSaveStream);
|
||||
@ -31,10 +32,35 @@ public:
|
||||
void testAudioProperties()
|
||||
{
|
||||
ASF::File f(TEST_FILE_PATH_C("silence-1.wma"));
|
||||
CPPUNIT_ASSERT_EQUAL(4, f.audioProperties()->length());
|
||||
CPPUNIT_ASSERT(f.audioProperties());
|
||||
CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->length());
|
||||
CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds());
|
||||
CPPUNIT_ASSERT_EQUAL(3712, f.audioProperties()->lengthInMilliseconds());
|
||||
CPPUNIT_ASSERT_EQUAL(64, f.audioProperties()->bitrate());
|
||||
CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
|
||||
CPPUNIT_ASSERT_EQUAL(48000, f.audioProperties()->sampleRate());
|
||||
CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample());
|
||||
CPPUNIT_ASSERT_EQUAL(ASF::Properties::WMA2, f.audioProperties()->codec());
|
||||
CPPUNIT_ASSERT_EQUAL(String("Windows Media Audio 9.1"), f.audioProperties()->codecName());
|
||||
CPPUNIT_ASSERT_EQUAL(String("64 kbps, 48 kHz, stereo 2-pass CBR"), f.audioProperties()->codecDescription());
|
||||
CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted());
|
||||
}
|
||||
|
||||
void testLosslessProperties()
|
||||
{
|
||||
ASF::File f(TEST_FILE_PATH_C("lossless.wma"));
|
||||
CPPUNIT_ASSERT(f.audioProperties());
|
||||
CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->length());
|
||||
CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds());
|
||||
CPPUNIT_ASSERT_EQUAL(3549, f.audioProperties()->lengthInMilliseconds());
|
||||
CPPUNIT_ASSERT_EQUAL(1152, f.audioProperties()->bitrate());
|
||||
CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
|
||||
CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate());
|
||||
CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample());
|
||||
CPPUNIT_ASSERT_EQUAL(ASF::Properties::WMA9Lossless, f.audioProperties()->codec());
|
||||
CPPUNIT_ASSERT_EQUAL(String("Windows Media Audio 9.2 Lossless"), f.audioProperties()->codecName());
|
||||
CPPUNIT_ASSERT_EQUAL(String("VBR Quality 100, 44 kHz, 2 channel 16 bit 1-pass VBR"), f.audioProperties()->codecDescription());
|
||||
CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted());
|
||||
}
|
||||
|
||||
void testRead()
|
||||
|
Reference in New Issue
Block a user