aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_flac/Metadata.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Input/in_flac/Metadata.cpp')
-rw-r--r--Src/Plugins/Input/in_flac/Metadata.cpp577
1 files changed, 577 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_flac/Metadata.cpp b/Src/Plugins/Input/in_flac/Metadata.cpp
new file mode 100644
index 00000000..49815c54
--- /dev/null
+++ b/Src/Plugins/Input/in_flac/Metadata.cpp
@@ -0,0 +1,577 @@
+/*
+** Copyright (C) 2007-2011 Nullsoft, Inc.
+**
+** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
+** liable for any damages arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
+** alter it and redistribute it freely, subject to the following restrictions:
+**
+** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
+** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+**
+** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+**
+** 3. This notice may not be removed or altered from any source distribution.
+**
+** Author: Ben Allison benski@winamp.com
+** Created: March 1, 2007
+**
+*/
+#include "Metadata.h"
+#include <FLAC/all.h>
+#include "StreamFileWin32.h" // for FileSize64
+#include "api__in_flv.h"
+#include <strsafe.h>
+
+struct MetadataReader
+{
+ MetadataReader(HANDLE _handle)
+ {
+ handle = _handle;
+ endOfFile=false;
+ }
+ HANDLE handle;
+ bool endOfFile;
+};
+
+static size_t win32_read(void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle)
+{
+ MetadataReader *reader = (MetadataReader *)handle;
+ DWORD bytesRead=0;
+ BOOL result = ReadFile(reader->handle, ptr, size*nmemb, &bytesRead, NULL);
+ if (result == TRUE && bytesRead == 0)
+ reader->endOfFile=true;
+ return bytesRead/size;
+}
+
+static size_t win32_write(const void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle)
+{
+ MetadataReader *reader = (MetadataReader *)handle;
+ DWORD bytesWritten=0;
+ WriteFile(reader->handle, ptr, size*nmemb, &bytesWritten, NULL);
+ return bytesWritten/size;
+}
+
+static __int64 Seek64(HANDLE hf, __int64 distance, DWORD MoveMethod)
+{
+ LARGE_INTEGER li;
+
+ li.QuadPart = distance;
+
+ li.LowPart = SetFilePointer (hf, li.LowPart, &li.HighPart, MoveMethod);
+
+ if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
+ {
+ li.QuadPart = -1;
+ }
+
+ return li.QuadPart;
+}
+
+static int win32_seek(FLAC__IOHandle handle, FLAC__int64 offset, int whence)
+{
+ MetadataReader *reader = (MetadataReader *)handle;
+ if (Seek64(reader->handle, offset, whence) == -1)
+ return -1;
+ else
+ return 0;
+
+}
+
+static FLAC__int64 win32_tell(FLAC__IOHandle handle)
+{
+ MetadataReader *reader = (MetadataReader *)handle;
+ return Seek64(reader->handle, 0, FILE_CURRENT);
+}
+
+static int win32_eof(FLAC__IOHandle handle)
+{
+ MetadataReader *reader = (MetadataReader *)handle;
+ return !!reader->endOfFile;
+}
+
+static int win32_close(FLAC__IOHandle handle)
+{
+ MetadataReader *reader = (MetadataReader *)handle;
+
+ CloseHandle(reader->handle);
+ reader->handle = INVALID_HANDLE_VALUE;
+ return 0;
+}
+
+static FLAC__IOCallbacks unicodeIO =
+{
+ win32_read,
+ win32_write,
+ win32_seek,
+ win32_tell,
+ win32_eof,
+ win32_close,
+};
+
+FLACMetadata::FLACMetadata()
+{
+ chain=FLAC__metadata_chain_new();
+ itr = FLAC__metadata_iterator_new();
+ block=0;
+ streamInfo=0;
+ filesize=0;
+}
+
+FLACMetadata::~FLACMetadata()
+{
+ if (chain)
+ FLAC__metadata_chain_delete(chain);
+ if (itr)
+ FLAC__metadata_iterator_delete(itr);
+}
+
+void FLACMetadata::Reset()
+{
+ if (chain)
+ FLAC__metadata_chain_delete(chain);
+ if (itr)
+ FLAC__metadata_iterator_delete(itr);
+ chain=FLAC__metadata_chain_new();
+ itr = FLAC__metadata_iterator_new();
+ block=0;
+ streamInfo=0;
+ filesize=0;
+}
+
+const FLAC__StreamMetadata_StreamInfo *FLACMetadata::GetStreamInfo()
+{
+ if (streamInfo)
+ return &streamInfo->data.stream_info;
+ else
+ return 0;
+}
+
+bool FLACMetadata::Open(const wchar_t *filename, bool optimize)
+{
+ if (!chain || !itr)
+ return false;
+
+ HANDLE hfile = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
+ MetadataReader reader(hfile);
+ if (reader.handle == INVALID_HANDLE_VALUE)
+ return false;
+ filesize = FileSize64(reader.handle);
+
+ FLAC__bool success = FLAC__metadata_chain_read_with_callbacks(chain, &reader, unicodeIO);
+ CloseHandle(hfile);
+ if (!success)
+ return false;
+
+ if (optimize)
+ {
+ FLAC__metadata_chain_sort_padding(chain);
+ FLAC__metadata_chain_merge_padding(chain);
+ }
+
+ FLAC__metadata_iterator_init(itr, chain);
+ while (1)
+ {
+ FLAC__MetadataType type=FLAC__metadata_iterator_get_block_type(itr);
+ switch (type)
+ {
+ case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+ block = FLAC__metadata_iterator_get_block(itr);
+ break;
+ case FLAC__METADATA_TYPE_STREAMINFO:
+ streamInfo = FLAC__metadata_iterator_get_block(itr);
+ break;
+ }
+ if (FLAC__metadata_iterator_next(itr) == false)
+ break;
+ }
+ return true;
+}
+
+const char *FLACMetadata::GetMetadata(const char *tag)
+{
+ if (!block)
+ return 0;
+
+ int pos = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, tag);
+ if (pos < 0)
+ {
+ // fail
+ }
+ else
+ {
+ const char *entry = (const char *)block->data.vorbis_comment.comments[pos].entry;
+ const char *metadata = strchr(entry, '='); // find the first equal
+ if (metadata)
+ {
+ return metadata+1;
+ }
+ }
+
+ return 0;
+}
+
+void FLACMetadata::SetMetadata(const char *tag, const char *value)
+{
+ if (!block)
+ {
+ FLAC__metadata_iterator_init(itr, chain);
+ do
+ {
+ if (FLAC__METADATA_TYPE_VORBIS_COMMENT == FLAC__metadata_iterator_get_block_type(itr))
+ {
+ block = FLAC__metadata_iterator_get_block(itr);
+ break;
+ }
+ }
+ while (FLAC__metadata_iterator_next(itr) != 0);
+ if (!block)
+ {
+ block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
+ FLAC__metadata_iterator_insert_block_after(itr, block);
+ }
+ }
+
+ if (!block)
+ return;
+
+ FLAC__StreamMetadata_VorbisComment_Entry entry;
+ size_t totalLen = strlen(tag) + 1 /* = */ + strlen(value);
+ entry.entry = (FLAC__byte *)malloc(totalLen + 1);
+ entry.length = totalLen;
+
+ StringCchPrintfA((char *)entry.entry, totalLen+1, "%s=%s", tag, value);
+
+ int pos = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, tag);
+ if (pos < 0)
+ {
+ //new comment
+ FLAC__metadata_object_vorbiscomment_append_comment(block, entry, true);
+ // would love to not copy, but we can't guarantee that FLAC links to the same CRT as us
+ }
+ else
+ {
+ FLAC__metadata_object_vorbiscomment_set_comment(block, pos, entry, true);
+ // would love to not copy, but we can't guarantee that FLAC links to the same CRT as us
+ }
+ free(entry.entry);
+}
+
+void FLACMetadata::RemoveMetadata(const char *tag)
+{
+ if (!block)
+ return;
+
+ FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, tag);
+}
+
+void FLACMetadata::RemoveMetadata(int n)
+{
+ if (!block)
+ return;
+
+ FLAC__metadata_object_vorbiscomment_delete_comment(block, (unsigned int)n);
+}
+
+static FLAC__StreamMetadata *GetOrMakePadding(FLAC__Metadata_Chain *chain, FLAC__Metadata_Iterator *itr)
+{
+ FLAC__metadata_iterator_init(itr, chain);
+ while (1)
+ {
+ if (FLAC__METADATA_TYPE_PADDING == FLAC__metadata_iterator_get_block_type(itr))
+ {
+ FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(itr);
+ return block;
+ }
+
+ if (FLAC__metadata_iterator_next(itr) == false)
+ break;
+ }
+ FLAC__StreamMetadata *padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
+ if (padding)
+ FLAC__metadata_iterator_insert_block_after(itr, padding);
+
+ return padding;
+}
+
+bool FLACMetadata::Save(const wchar_t *filename)
+{
+ if (FLAC__metadata_chain_check_if_tempfile_needed(chain, true))
+ {
+ // since we needed to write a tempfile, let's add some more padding so it doesn't happen again
+ FLAC__metadata_chain_sort_padding(chain);
+
+ FLAC__StreamMetadata *padding = GetOrMakePadding(chain, itr);
+ if (padding && padding->length < 16384)
+ padding->length = 16384; // TODO: configurable padding size
+
+ HANDLE hfile = CreateFileW(filename, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
+ MetadataReader reader(hfile);
+ if (reader.handle == INVALID_HANDLE_VALUE)
+ return false;
+
+ wchar_t tempPath[MAX_PATH-14] = {0}, tempFile[MAX_PATH] = {0};
+ GetTempPathW(MAX_PATH-14, tempPath);
+ GetTempFileNameW(tempPath, L"waf", 0, tempFile);
+
+ HANDLE hTempFile = CreateFileW(tempFile, GENERIC_READ|GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
+ MetadataReader tempReader(hTempFile);
+
+ FLAC__bool res = FLAC__metadata_chain_write_with_callbacks_and_tempfile(chain, false, &reader, unicodeIO, &tempReader, unicodeIO);
+
+ CloseHandle(hfile);
+ CloseHandle(hTempFile);
+ if (!MoveFileW(tempFile, filename))
+ {
+ if (CopyFileW(tempFile, filename, FALSE))
+ {
+ DeleteFileW(tempFile);
+ }
+ else
+ {
+ DeleteFileW(tempFile);
+ return false;
+ }
+ }
+ return !!res;
+ }
+ else
+ {
+ HANDLE hfile = CreateFileW(filename, GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
+ MetadataReader reader(hfile);
+ if (reader.handle == INVALID_HANDLE_VALUE)
+ return false;
+
+ FLAC__bool res = FLAC__metadata_chain_write_with_callbacks(chain, true, &reader, unicodeIO);
+
+ CloseHandle(hfile);
+ return !!res;
+ }
+}
+
+bool FLACMetadata::GetLengthMilliseconds(unsigned __int64 *length)
+{
+ if (!streamInfo)
+ return false;
+ *length = (__int64)((double)(FLAC__int64)streamInfo->data.stream_info.total_samples / (double)streamInfo->data.stream_info.sample_rate * 1000.0 + 0.5);
+ return true;
+}
+
+int FLACMetadata::GetNumMetadataItems()
+{
+ if (block) return block->data.vorbis_comment.num_comments;
+ else return 0;
+}
+
+const char* FLACMetadata::EnumMetadata(int n, char *tag, int taglen)
+{
+ if (tag) tag[0]=0;
+ if (!block) return 0;
+ const char *entry = (const char *)block->data.vorbis_comment.comments[n].entry;
+ const char *metadata = strchr(entry, '='); // find the first equal
+ if (metadata)
+ {
+ if (tag) lstrcpynA(tag,entry,min(metadata-entry+1,taglen));
+ return metadata+1;
+ }
+ else return 0;
+}
+
+void FLACMetadata::SetTag(int pos, const char *tag)
+{
+ char * value = (char*)EnumMetadata(pos,0,0);
+ value = _strdup(value?value:"");
+ FLAC__StreamMetadata_VorbisComment_Entry entry;
+ size_t totalLen = strlen(tag) + 1 /* = */ + strlen(value);
+ entry.entry = (FLAC__byte *)malloc(totalLen + 1);
+ entry.length = totalLen;
+ StringCchPrintfA((char *)entry.entry, totalLen+1, "%s=%s", tag, value);
+ FLAC__metadata_object_vorbiscomment_set_comment(block, pos, entry, true);
+ free(value);
+}
+
+bool FLACMetadata::GetPicture(FLAC__StreamMetadata_Picture_Type type, void **data, size_t *len, wchar_t **mimeType)
+{
+ if (!chain || !itr)
+ return false;
+
+ FLAC__metadata_iterator_init(itr, chain);
+ while (1)
+ {
+ if (FLAC__METADATA_TYPE_PICTURE == FLAC__metadata_iterator_get_block_type(itr))
+ {
+ FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(itr);
+ FLAC__StreamMetadata_Picture &picture = block->data.picture;
+ if (picture.type == type)
+ {
+ *len = picture.data_length;
+ *data = WASABI_API_MEMMGR->sysMalloc(picture.data_length);
+ if (!*data)
+ return false;
+ memcpy(*data, picture.data, picture.data_length);
+
+ char *type = 0;
+ if (picture.mime_type)
+ type = strchr(picture.mime_type, '/');
+
+ if (type && *type)
+ {
+ type++;
+
+ char *type2 = strchr(type, '/');
+ if (type2 && *type2) type2++;
+ else type2 = type;
+
+ int typelen = MultiByteToWideChar(CP_ACP, 0, type2, -1, 0, 0);
+ *mimeType = (wchar_t *)WASABI_API_MEMMGR->sysMalloc(typelen * sizeof(wchar_t));
+ if (*mimeType)
+ MultiByteToWideChar(CP_ACP, 0, type, -1, *mimeType, typelen);
+ }
+ else
+ *mimeType = 0; // unknown!
+
+ return true;
+ }
+ }
+
+ if (FLAC__metadata_iterator_next(itr) == false)
+ break;
+ }
+
+ return false;
+}
+
+bool FLACMetadata::RemovePicture(FLAC__StreamMetadata_Picture_Type type)
+{
+ if (!chain || !itr)
+ return false;
+
+ FLAC__metadata_iterator_init(itr, chain);
+ while (1)
+ {
+ if (FLAC__METADATA_TYPE_PICTURE == FLAC__metadata_iterator_get_block_type(itr))
+ {
+ FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(itr);
+ FLAC__StreamMetadata_Picture &picture = block->data.picture;
+ if (picture.type == type)
+ {
+ FLAC__metadata_iterator_delete_block(itr, false);
+ return true;
+ }
+ }
+
+ if (FLAC__metadata_iterator_next(itr) == false)
+ break;
+ }
+
+ return false;
+}
+
+bool FLACMetadata::SetPicture(FLAC__StreamMetadata_Picture_Type type, void *data, size_t len, const wchar_t *mimeType, int width, int height)
+{
+ if (!chain || !itr)
+ return false;
+
+ FLAC__metadata_iterator_init(itr, chain);
+ while (1)
+ {
+ if (FLAC__METADATA_TYPE_PICTURE == FLAC__metadata_iterator_get_block_type(itr))
+ {
+ FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(itr);
+ FLAC__StreamMetadata_Picture &picture = block->data.picture;
+ if (picture.type == type)
+ {
+ FLAC__metadata_object_picture_set_data(block, (FLAC__byte *)data, len, true);
+ picture.width = width;
+ picture.height = height;
+ picture.depth = 32;
+ picture.colors = 0;
+
+ FLAC__metadata_object_picture_set_description(block, (FLAC__byte *)"", true);// TODO?
+
+ char mime[256] = {0};
+ if (wcsstr(mimeType, L"/") != 0)
+ {
+ StringCchPrintfA(mime, 256, "%S", mimeType);
+ }
+ else
+ {
+ StringCchPrintfA(mime, 256, "image/%S", mimeType);
+ }
+ FLAC__metadata_object_picture_set_mime_type(block, mime, true);
+ return true;
+ }
+ }
+
+ if (FLAC__metadata_iterator_next(itr) == false)
+ break;
+ }
+
+ // not found. let's add it
+ FLAC__StreamMetadata *newBlock = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PICTURE);
+
+ FLAC__metadata_object_picture_set_data(newBlock, (FLAC__byte *)data, len, true);
+ FLAC__StreamMetadata_Picture &picture = newBlock->data.picture;
+ picture.type = type;
+ picture.width = width;
+ picture.height = height;
+ picture.depth = 32;
+ picture.colors = 0;
+
+ FLAC__metadata_object_picture_set_description(newBlock, (FLAC__byte *)"", true);// TODO?
+
+ char mime[256] = {0};
+ StringCchPrintfA(mime, 256, "image/%S", mimeType);
+ FLAC__metadata_object_picture_set_mime_type(newBlock, mime, true);
+
+ FLAC__metadata_iterator_insert_block_after(itr, newBlock);
+ return true;
+}
+
+bool FLACMetadata::GetIndexPicture(int index, FLAC__StreamMetadata_Picture_Type *type, void **data, size_t *len, wchar_t **mimeType)
+{
+ if (!chain || !itr)
+ return false;
+ int i=0;
+ FLAC__metadata_iterator_init(itr, chain);
+ while (1)
+ {
+ if (FLAC__METADATA_TYPE_PICTURE == FLAC__metadata_iterator_get_block_type(itr))
+ {
+ FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(itr);
+ FLAC__StreamMetadata_Picture &picture = block->data.picture;
+ if (i++ == index)
+ {
+ *type = picture.type;
+ *len = picture.data_length;
+ *data = WASABI_API_MEMMGR->sysMalloc(picture.data_length);
+ if (!*data)
+ return false;
+ memcpy(*data, picture.data, picture.data_length);
+
+ char *type = 0;
+ if (picture.mime_type)
+ type = strchr(picture.mime_type, '/');
+
+ if (type && *type)
+ {
+ type++;
+ int typelen = MultiByteToWideChar(CP_ACP, 0, type, -1, 0, 0);
+ *mimeType = (wchar_t *)WASABI_API_MEMMGR->sysMalloc(typelen * sizeof(wchar_t));
+ if (*mimeType)
+ MultiByteToWideChar(CP_ACP, 0, type, -1, *mimeType, typelen);
+ }
+ else
+ *mimeType = 0; // unknown!
+
+ return true;
+ }
+ }
+
+ if (FLAC__metadata_iterator_next(itr) == false)
+ break;
+ }
+
+ return false;
+} \ No newline at end of file