mirror of
https://github.com/taglib/taglib.git
synced 2025-05-27 21:20:26 -04:00
C bindings: taglib_file_new_iostream() to access file from memory (#987)
A ByteVectorStream can be created/freed from C using taglib_memory_iostream_new()/taglib_iostream_free().
This commit is contained in:
parent
135c0eb647
commit
a7c0b53f7a
@ -31,6 +31,8 @@
|
||||
#endif
|
||||
#include "tstringlist.h"
|
||||
#include "tbytevectorlist.h"
|
||||
#include "tbytevectorstream.h"
|
||||
#include "tiostream.h"
|
||||
#include "tfile.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "fileref.h"
|
||||
@ -91,6 +93,21 @@ void taglib_free(void* pointer)
|
||||
free(pointer);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TagLib::IOStream wrapper
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TagLib_IOStream *taglib_memory_iostream_new(const char *data, unsigned int size)
|
||||
{
|
||||
return reinterpret_cast<TagLib_IOStream *>(
|
||||
new ByteVectorStream(ByteVector(data, size)));
|
||||
}
|
||||
|
||||
void taglib_iostream_free(TagLib_IOStream *stream)
|
||||
{
|
||||
delete reinterpret_cast<IOStream *>(stream);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TagLib::FileRef wrapper
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -140,6 +157,12 @@ TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
|
||||
return file ? reinterpret_cast<TagLib_File *>(new FileRef(file)) : NULL;
|
||||
}
|
||||
|
||||
TagLib_File *taglib_file_new_iostream(TagLib_IOStream *stream)
|
||||
{
|
||||
return reinterpret_cast<TagLib_File *>(
|
||||
new FileRef(reinterpret_cast<IOStream *>(stream)));
|
||||
}
|
||||
|
||||
void taglib_file_free(TagLib_File *file)
|
||||
{
|
||||
delete reinterpret_cast<FileRef *>(file);
|
||||
|
@ -68,6 +68,7 @@ extern "C" {
|
||||
typedef struct { int dummy; } TagLib_File;
|
||||
typedef struct { int dummy; } TagLib_Tag;
|
||||
typedef struct { int dummy; } TagLib_AudioProperties;
|
||||
typedef struct { int dummy; } TagLib_IOStream;
|
||||
|
||||
/*!
|
||||
* By default all strings coming into or out of TagLib's C API are in UTF8.
|
||||
@ -89,6 +90,22 @@ TAGLIB_C_EXPORT void taglib_set_string_management_enabled(BOOL management);
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_free(void* pointer);
|
||||
|
||||
/*******************************************************************************
|
||||
* Stream API
|
||||
******************************************************************************/
|
||||
|
||||
/*!
|
||||
* Creates a byte vector stream from \a size bytes of \a data.
|
||||
* Such a stream can be used with taglib_file_new_iostream() to create a file
|
||||
* from memory.
|
||||
*/
|
||||
TAGLIB_C_EXPORT TagLib_IOStream *taglib_memory_iostream_new(const char *data, unsigned int size);
|
||||
|
||||
/*!
|
||||
* Frees and closes the stream.
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_iostream_free(TagLib_IOStream *stream);
|
||||
|
||||
/*******************************************************************************
|
||||
* File API
|
||||
******************************************************************************/
|
||||
@ -121,6 +138,16 @@ TAGLIB_C_EXPORT TagLib_File *taglib_file_new(const char *filename);
|
||||
*/
|
||||
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type);
|
||||
|
||||
/*!
|
||||
* Creates a TagLib file from a \a stream.
|
||||
* A byte vector stream can be used to read a file from memory and write to
|
||||
* memory, e.g. when fetching network data.
|
||||
* The stream has to be created using taglib_memory_iostream_new() and shall be
|
||||
* freed using taglib_iostream_free() \e after this file is freed with
|
||||
* taglib_file_free().
|
||||
*/
|
||||
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_iostream(TagLib_IOStream *stream);
|
||||
|
||||
/*!
|
||||
* Frees and closes the file.
|
||||
*/
|
||||
|
@ -30,15 +30,54 @@
|
||||
#include "tag_c.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tstring.h"
|
||||
#include "plainfile.h"
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include "utils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace {
|
||||
|
||||
void propertiesToMap(
|
||||
TagLib_File *file,
|
||||
std::unordered_map<std::string, std::list<std::string>> &propertyMap)
|
||||
{
|
||||
if(char **keys = taglib_property_keys(file)) {
|
||||
char **keyPtr = keys;
|
||||
while(*keyPtr) {
|
||||
char **values = taglib_property_get(file, *keyPtr);
|
||||
char **valuePtr = values;
|
||||
std::list<std::string> valueList;
|
||||
while(*valuePtr) {
|
||||
valueList.push_back(*valuePtr++);
|
||||
}
|
||||
taglib_property_free(values);
|
||||
propertyMap[*keyPtr++] = valueList;
|
||||
}
|
||||
taglib_property_free(keys);
|
||||
}
|
||||
}
|
||||
|
||||
void complexPropertyKeysToList(
|
||||
TagLib_File *file,
|
||||
std::list<std::string> &keyList)
|
||||
{
|
||||
if(char **complexKeys = taglib_complex_property_keys(file)) {
|
||||
char **complexKeyPtr = complexKeys;
|
||||
while(*complexKeyPtr) {
|
||||
keyList.push_back(*complexKeyPtr++);
|
||||
}
|
||||
taglib_complex_property_free_keys(complexKeys);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class TestTagC : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(TestTagC);
|
||||
CPPUNIT_TEST(testMp3);
|
||||
CPPUNIT_TEST(testStream);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@ -89,21 +128,8 @@ public:
|
||||
CPPUNIT_ASSERT_EQUAL(2U, taglib_tag_track(tag));
|
||||
CPPUNIT_ASSERT_EQUAL(2023U, taglib_tag_year(tag));
|
||||
|
||||
char **keys = taglib_property_keys(file);
|
||||
CPPUNIT_ASSERT(keys);
|
||||
std::unordered_map<std::string, std::list<std::string>> propertyMap;
|
||||
char **keyPtr = keys;
|
||||
while(*keyPtr) {
|
||||
char **values = taglib_property_get(file, *keyPtr);
|
||||
char **valuePtr = values;
|
||||
std::list<std::string> valueList;
|
||||
while(*valuePtr) {
|
||||
valueList.push_back(*valuePtr++);
|
||||
}
|
||||
taglib_property_free(values);
|
||||
propertyMap[*keyPtr++] = valueList;
|
||||
}
|
||||
taglib_property_free(keys);
|
||||
propertiesToMap(file, propertyMap);
|
||||
const std::unordered_map<std::string, std::list<std::string>> expected {
|
||||
{"TRACKNUMBER"s, {"2"s}},
|
||||
{"TITLE"s, {"Title"s}},
|
||||
@ -117,14 +143,8 @@ public:
|
||||
};
|
||||
CPPUNIT_ASSERT(expected == propertyMap);
|
||||
|
||||
char **complexKeys = taglib_complex_property_keys(file);
|
||||
CPPUNIT_ASSERT(complexKeys);
|
||||
std::list<std::string> keyList;
|
||||
char **complexKeyPtr = complexKeys;
|
||||
while(*complexKeyPtr) {
|
||||
keyList.push_back(*complexKeyPtr++);
|
||||
}
|
||||
taglib_complex_property_free_keys(complexKeys);
|
||||
complexPropertyKeysToList(file, keyList);
|
||||
CPPUNIT_ASSERT(std::list{"PICTURE"s} == keyList);
|
||||
|
||||
TagLib_Complex_Property_Attribute*** properties =
|
||||
@ -143,6 +163,75 @@ public:
|
||||
|
||||
taglib_tag_free_strings();
|
||||
}
|
||||
|
||||
void testStream()
|
||||
{
|
||||
// Only fetch the beginning of a FLAC file
|
||||
const ByteVector data = PlainFile(TEST_FILE_PATH_C("silence-44-s.flac"))
|
||||
.readBlock(4200);
|
||||
|
||||
{
|
||||
TagLib_IOStream *stream = taglib_memory_iostream_new(data.data(), data.size());
|
||||
TagLib_File *file = taglib_file_new_iostream(stream);
|
||||
CPPUNIT_ASSERT(taglib_file_is_valid(file));
|
||||
const TagLib_AudioProperties *audioProperties = taglib_file_audioproperties(file);
|
||||
CPPUNIT_ASSERT_EQUAL(2, taglib_audioproperties_channels(audioProperties));
|
||||
CPPUNIT_ASSERT_EQUAL(3, taglib_audioproperties_length(audioProperties));
|
||||
CPPUNIT_ASSERT_EQUAL(44100, taglib_audioproperties_samplerate(audioProperties));
|
||||
|
||||
TagLib_Tag *tag = taglib_file_tag(file);
|
||||
CPPUNIT_ASSERT_EQUAL("Quod Libet Test Data"s, std::string(taglib_tag_album(tag)));
|
||||
CPPUNIT_ASSERT_EQUAL("piman jzig"s, std::string(taglib_tag_artist(tag)));
|
||||
CPPUNIT_ASSERT_EQUAL("Silence"s, std::string(taglib_tag_genre(tag)));
|
||||
CPPUNIT_ASSERT_EQUAL(""s, std::string(taglib_tag_comment(tag)));
|
||||
CPPUNIT_ASSERT_EQUAL("Silence"s, std::string(taglib_tag_title(tag)));
|
||||
CPPUNIT_ASSERT_EQUAL(2U, taglib_tag_track(tag));
|
||||
CPPUNIT_ASSERT_EQUAL(2004U, taglib_tag_year(tag));
|
||||
|
||||
std::unordered_map<std::string, std::list<std::string>> propertyMap;
|
||||
propertiesToMap(file, propertyMap);
|
||||
const std::unordered_map<std::string, std::list<std::string>> expected {
|
||||
{"TRACKNUMBER"s, {"02/10"s}},
|
||||
{"TITLE"s, {"Silence"s}},
|
||||
{"GENRE"s, {"Silence"s}},
|
||||
{"DATE"s, {"2004"s}},
|
||||
{"ARTIST"s, {"piman"s, "jzig"s}},
|
||||
{"ALBUM"s, {"Quod Libet Test Data"s}}
|
||||
};
|
||||
CPPUNIT_ASSERT(expected == propertyMap);
|
||||
|
||||
std::list<std::string> keyList;
|
||||
complexPropertyKeysToList(file, keyList);
|
||||
CPPUNIT_ASSERT(std::list{"PICTURE"s} == keyList);
|
||||
|
||||
TagLib_Complex_Property_Attribute*** properties =
|
||||
taglib_complex_property_get(file, "PICTURE");
|
||||
TagLib_Complex_Property_Picture_Data picture;
|
||||
taglib_picture_from_complex_property(properties, &picture);
|
||||
ByteVector expectedData(
|
||||
"\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR"
|
||||
"\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90\x77\x53"
|
||||
"\xde\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b"
|
||||
"\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xd6"
|
||||
"\x0b\x1c\x0a\x36\x06\x08\x44\x3d\x32\x00\x00\x00\x1dtEXt"
|
||||
"Comment\0Created"
|
||||
" with The GIMP\xef\x64"
|
||||
"\x25\x6e\x00\x00\x00\x0cIDAT\x08\xd7\x63\xf8\xff\xff"
|
||||
"\x3f\x00\x05\xfe\x02\xfe\xdc\xcc\x59\xe7\x00\x00\x00\x00IEND"
|
||||
"\xae\x42\x60\x82", 150);
|
||||
CPPUNIT_ASSERT_EQUAL("image/png"s, std::string(picture.mimeType));
|
||||
CPPUNIT_ASSERT_EQUAL("A pixel."s, std::string(picture.description));
|
||||
CPPUNIT_ASSERT_EQUAL("Front Cover"s, std::string(picture.pictureType));
|
||||
CPPUNIT_ASSERT_EQUAL(expectedData, ByteVector(picture.data, 150));
|
||||
CPPUNIT_ASSERT_EQUAL(150U, picture.size);
|
||||
taglib_complex_property_free(properties);
|
||||
|
||||
taglib_file_free(file);
|
||||
taglib_iostream_free(stream);
|
||||
}
|
||||
|
||||
taglib_tag_free_strings();
|
||||
}
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestTagC);
|
||||
|
Loading…
Reference in New Issue
Block a user