Commit a8dd2c5a authored by Urs Fleisch's avatar Urs Fleisch
Browse files

support for FLAC pictures

parent c3ee264d
......@@ -147,11 +147,15 @@ if(WITH_FLAC)
set(_CMAKE_REQUIRED_LIBRARIES_TMP ${CMAKE_REQUIRED_LIBRARIES})
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${FLAC_LIBRARIES} ${FLACPP_LIBRARIES})
CHECK_CXX_SOURCE_COMPILES("#include <FLAC++/metadata.h>\nint main() {\n FLAC::Metadata::VorbisComment vc;\n const ::FLAC__StreamMetadata* fsmd = vc;\n return 0;\n}\n" FLAC_STREAMMETADATA_OPERATOR_FOUND)
CHECK_CXX_SOURCE_COMPILES("#include <FLAC++/metadata.h>\nint main() {\n FLAC::Metadata::Picture pic;\n return 0;\n}\n" FLAC_METADATA_PICTURE_FOUND)
set(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES_TMP})
if(NOT FLAC_STREAMMETADATA_OPERATOR_FOUND)
set(HAVE_NO_FLAC_STREAMMETADATA_OPERATOR 1)
endif(NOT FLAC_STREAMMETADATA_OPERATOR_FOUND)
if(FLAC_METADATA_PICTURE_FOUND)
set(HAVE_FLAC_PICTURE 1)
endif(FLAC_METADATA_PICTURE_FOUND)
else(FLACPP_INCLUDE_DIR AND FLAC_LIBRARY AND FLACPP_LIBRARY)
message(FATAL_ERROR "Could not find FLAC++")
endif(FLACPP_INCLUDE_DIR AND FLAC_LIBRARY AND FLACPP_LIBRARY)
......
......@@ -26,3 +26,4 @@
/* Define if you have FLAC++ installed */
#cmakedefine HAVE_FLAC 1
#cmakedefine HAVE_NO_FLAC_STREAMMETADATA_OPERATOR
#cmakedefine HAVE_FLAC_PICTURE 1
......@@ -320,3 +320,33 @@ EOF
])
FLAC_CHECK_STREAMMETADATA_OPERATOR
fi
# Check if FLAC++ has FLAC::Metadata::Picture
if test "$build_flac" = "yes"; then
AC_DEFUN([FLAC_CHECK_METADATA_TYPE_PICTURE],
[
AC_MSG_CHECKING([for FLAC::Metadata::Picture])
AC_LANG_SAVE
AC_LANG_CPLUSPLUS
cat > conftest.$ac_ext <<EOF
#include <FLAC++/metadata.h>
int main() {
FLAC::Metadata::Picture pic;
return 0;
}
EOF
ac_save_CPPFLAGS=$CPPFLAGS
CPPFLAGS="$all_includes $CPPFLAGS"
if AC_TRY_EVAL(ac_compile); then
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_FLAC_PICTURE,1,[Define to build with FLAC::Metadata::Picture])
else
AC_MSG_RESULT(no)
fi
CPPFLAGS=$ac_save_CPPFLAGS
AC_LANG_RESTORE
])
FLAC_CHECK_METADATA_TYPE_PICTURE
fi
......@@ -5,8 +5,11 @@
#undef CFG_DOCDIR
#undef CFG_TRANSLATIONSDIR
#undef HAVE_ID3LIB
#undef HAVE_NO_ID3LIB_VBR
#undef HAVE_VORBIS
#undef HAVE_FLAC
#undef HAVE_NO_FLAC_STREAMMETADATA_OPERATOR
#undef HAVE_FLAC_PICTURE
#undef HAVE_TAGLIB
#undef HAVE_MP4V2
#undef HAVE_MP4V2_MP4GETMETADATABYINDEX_CHARPP_ARG
......
......@@ -147,6 +147,7 @@ sub generateTs()
my $have_vorbis = 1;
my $have_flac = 1;
my $have_flac_picture = 1;
my $have_id3lib = 1;
my $have_taglib = 1;
my $have_mp4v2 = 1;
......@@ -167,6 +168,9 @@ while (my $opt = shift) {
$have_vorbis = 0;
} elsif ($opt eq "--without-flac") {
$have_flac = 0;
$have_flac_picture = 0;
} elsif ($opt eq "--without-flac-picture") {
$have_flac_picture = 0;
} elsif ($opt eq "--without-id3lib") {
$have_id3lib = 0;
} elsif ($opt eq "--without-taglib") {
......@@ -211,6 +215,7 @@ while (my $opt = shift) {
print " --without-id3lib build without id3lib\n";
print " --without-vorbis build without ogg/vorbis\n";
print " --without-flac build without FLAC\n";
print " --without-flac-picture build without FLAC picture support\n";
print " --enable-gcc-pch enable precompiled headers\n";
print " --enable-debug enables debug symbols\n";
print " --with-qmake=PROGRAM qmake command [qmake]\n";
......@@ -350,6 +355,9 @@ if ($have_flac) {
$config_pri .= "-lFLAC++ -lFLAC ";
$allsys_h .= "#include <FLAC++/metadata.h>\n";
}
if ($have_flac_picture) {
$config_h .= "#define HAVE_FLAC_PICTURE $have_flac_picture\n";
}
if ($have_taglib) {
$config_h .= "#define HAVE_TAGLIB $have_taglib\n";
$config_pri .= "-ltag ";
......
......@@ -317,6 +317,36 @@ EOF
FLAC_CHECK_STREAMMETADATA_OPERATOR
fi
dnl Check if FLAC++ has FLAC::Metadata::Picture
if test "$build_flac" = "yes"; then
AC_DEFUN([FLAC_CHECK_METADATA_TYPE_PICTURE],
[
AC_MSG_CHECKING([for FLAC::Metadata::Picture])
AC_LANG_SAVE
AC_LANG_CPLUSPLUS
cat > conftest.$ac_ext <<EOF
#include <FLAC++/metadata.h>
int main() {
FLAC::Metadata::Picture pic;
return 0;
}
EOF
ac_save_CPPFLAGS=$CPPFLAGS
CPPFLAGS="$all_includes $CPPFLAGS"
if AC_TRY_EVAL(ac_compile); then
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_FLAC_PICTURE,1,[Define to build with FLAC::Metadata::Picture])
else
AC_MSG_RESULT(no)
fi
CPPFLAGS=$ac_save_CPPFLAGS
AC_LANG_RESTORE
])
FLAC_CHECK_METADATA_TYPE_PICTURE
fi
AC_DEFINE_UNQUOTED(VERSION, "${VERSION}")
......
......@@ -30,9 +30,11 @@
#include "standardtags.h"
#include "genres.h"
#include "dirinfo.h"
#include "pictureframe.h"
#include <FLAC++/metadata.h>
#include <qfile.h>
#include <qdir.h>
#include <qimage.h>
#include <sys/stat.h>
#ifdef WIN32
#include <sys/utime.h>
......@@ -67,6 +69,62 @@ FlacFile::~FlacFile()
}
}
#ifdef HAVE_FLAC_PICTURE
/**
* Get the picture block as a picture frame.
*
* @param frame frame to set
* @param pic picture block to get
*/
static void getPicture(Frame& frame, const FLAC::Metadata::Picture* pic)
{
QByteArray ba;
QCM_duplicate(
ba,
reinterpret_cast<const char*>(pic->get_data()),
pic->get_data_length());
PictureFrame::setFields(
frame,
Frame::Field::TE_ISO8859_1, "",
QString::fromAscii(pic->get_mime_type()),
static_cast<PictureFrame::PictureType>(pic->get_type()),
QString::fromUtf8(
reinterpret_cast<const char*>(pic->get_description())),
ba);
frame.setInternalName("Picture");
}
/**
* Set the picture block with the picture frame.
*
* @param frame frame to get
* @param pic picture block to set
*/
static void setPicture(const Frame& frame, FLAC::Metadata::Picture* pic)
{
Frame::Field::TextEncoding enc;
PictureFrame::PictureType pictureType = PictureFrame::PT_CoverFront;
QString imgFormat, mimeType, description;
QByteArray ba;
PictureFrame::getFields(frame, enc, imgFormat, mimeType,
pictureType, description, ba);
QImage image;
if (image.loadFromData(ba)) {
pic->set_width(image.width());
pic->set_height(image.height());
pic->set_depth(image.depth());
pic->set_colors(image.numColors());
}
pic->set_mime_type(mimeType.QCM_toAscii());
pic->set_type(
static_cast<FLAC__StreamMetadata_Picture_Type>(pictureType));
pic->set_description(
reinterpret_cast<const FLAC__byte*>(
static_cast<const char*>(description.QCM_toUtf8())));
pic->set_data(reinterpret_cast<const FLAC__byte*>(ba.data()), ba.size());
}
#endif // HAVE_FLAC_PICTURE
/**
* Read tags from file.
*
......@@ -76,6 +134,10 @@ void FlacFile::readTags(bool force)
{
if (force || !m_fileRead) {
m_comments.clear();
#ifdef HAVE_FLAC_PICTURE
m_pictures.clear();
int pictureNr = 0;
#endif
markTag2Changed(false);
m_fileRead = true;
QCM_QCString fnIn = QFile::encodeName(getDirInfo()->getDirname() + QDir::separator() + currentFilename());
......@@ -127,6 +189,21 @@ void FlacFile::readTags(bool force)
delete proto;
}
}
#ifdef HAVE_FLAC_PICTURE
else if (mdt == FLAC__METADATA_TYPE_PICTURE) {
FLAC::Metadata::Prototype* proto = mdit->get_block();
if (proto) {
FLAC::Metadata::Picture* pic =
dynamic_cast<FLAC::Metadata::Picture*>(proto);
if (pic) {
Frame frame(Frame::FT_Picture, "", "", pictureNr++);
getPicture(frame, pic);
m_pictures.push_back(frame);
}
delete proto;
}
}
#endif
if (!mdit->next()) {
break;
}
......@@ -162,6 +239,11 @@ bool FlacFile::writeTags(bool force, bool* renamed, bool preserve)
if (m_fileRead && (force || isTag2Changed()) && m_chain && m_chain->is_valid()) {
bool commentsSet = false;
#ifdef HAVE_FLAC_PICTURE
bool pictureSet = false;
bool pictureRemoved = false;
PictureList::iterator pictureIt = m_pictures.begin();
#endif
m_chain->sort_padding();
FLAC::Metadata::Iterator* mdit = new FLAC::Metadata::Iterator;
if (mdit) {
......@@ -184,6 +266,41 @@ bool FlacFile::writeTags(bool force, bool* renamed, bool preserve)
}
}
}
#ifdef HAVE_FLAC_PICTURE
else if (mdt == FLAC__METADATA_TYPE_PICTURE) {
if (pictureIt != m_pictures.end()) {
FLAC::Metadata::Prototype* proto = mdit->get_block();
if (proto) {
FLAC::Metadata::Picture* pic =
dynamic_cast<FLAC::Metadata::Picture*>(proto);
if (pic) {
setPicture(*pictureIt++, pic);
pictureSet = true;
}
delete proto;
}
} else {
mdit->delete_block(false);
pictureRemoved = true;
}
} else if (mdt == FLAC__METADATA_TYPE_PADDING) {
if (pictureIt != m_pictures.end()) {
FLAC::Metadata::Picture* pic =
new FLAC::Metadata::Picture;
if (pic) {
setPicture(*pictureIt, pic);
if (mdit->set_block(pic)) {
++pictureIt;
pictureSet = true;
} else {
delete pic;
}
}
} else if (pictureRemoved) {
mdit->delete_block(false);
}
}
#endif
if (!mdit->next()) {
if (!commentsSet) {
FLAC::Metadata::VorbisComment* vc =
......@@ -200,15 +317,38 @@ bool FlacFile::writeTags(bool force, bool* renamed, bool preserve)
}
}
}
#ifdef HAVE_FLAC_PICTURE
while (pictureIt != m_pictures.end()) {
FLAC::Metadata::Picture* pic =
new FLAC::Metadata::Picture;
if (pic) {
setPicture(*pictureIt, pic);
if (mdit->insert_block_after(pic)) {
pictureSet = true;
} else {
delete pic;
}
}
++pictureIt;
}
#endif
break;
}
}
delete mdit;
}
#ifdef HAVE_FLAC_PICTURE
if ((commentsSet || pictureSet) &&
m_chain->write(!pictureRemoved, preserve)) {
markTag2Changed(false);
}
#else
if (commentsSet &&
m_chain->write(true, preserve)) {
markTag2Changed(false);
} else {
}
#endif
else {
return false;
}
}
......@@ -224,6 +364,144 @@ bool FlacFile::writeTags(bool force, bool* renamed, bool preserve)
return true;
}
#ifdef HAVE_FLAC_PICTURE
/**
* Check if file has an ID3v2 tag.
*
* @return true if a V2 tag is available.
* @see isTagInformationRead()
*/
bool FlacFile::hasTagV2() const
{
return OggFile::hasTagV2() || !m_pictures.empty();
}
/**
* Set a frame in the tags 2.
*
* @param frame frame to set
*
* @return true if ok.
*/
bool FlacFile::setFrameV2(const Frame& frame)
{
if (frame.getType() == Frame::FT_Picture) {
int index = frame.getIndex();
if (index != -1 && index < static_cast<int>(m_pictures.size())) {
#if QT_VERSION >= 0x040000
PictureList::iterator it = m_pictures.begin() + index;
#else
PictureList::iterator it = m_pictures.at(index);
#endif
if (it != m_pictures.end()) {
*it = frame;
PictureFrame::setDescription(*it, frame.getValue());
markTag2Changed();
return true;
}
}
}
return OggFile::setFrameV2(frame);
}
/**
* Add a frame in the tags 2.
*
* @param frame frame to add
*
* @return true if ok.
*/
bool FlacFile::addFrameV2(Frame& frame)
{
if (frame.getType() == Frame::FT_Picture) {
if (frame.getFieldList().empty()) {
PictureFrame::setFields(
frame, Frame::Field::TE_ISO8859_1, "JPG", "image/jpeg",
PictureFrame::PT_CoverFront, "", QByteArray());
}
PictureFrame::setDescription(frame, frame.getValue());
frame.setIndex(m_pictures.size());
m_pictures.push_back(frame);
markTag2Changed();
return true;
}
return OggFile::addFrameV2(frame);
}
/**
* Delete a frame in the tags 2.
*
* @param frame frame to delete.
*
* @return true if ok.
*/
bool FlacFile::deleteFrameV2(const Frame& frame)
{
if (frame.getType() == Frame::FT_Picture) {
int index = frame.getIndex();
if (index != -1 && index < static_cast<int>(m_pictures.size())) {
#if QT_VERSION >= 0x040000
m_pictures.removeAt(index);
#else
PictureList::iterator it = m_pictures.at(index);
m_pictures.erase(it);
#endif
markTag2Changed();
return true;
}
}
return OggFile::deleteFrameV2(frame);
}
/**
* Remove ID3v2 frames.
*
* @param flt filter specifying which frames to remove
*/
void FlacFile::deleteFramesV2(const FrameFilter& flt)
{
if (flt.areAllEnabled() || flt.isEnabled(Frame::FT_Picture)) {
m_pictures.clear();
markTag2Changed();
}
OggFile::deleteFramesV2(flt);
}
/**
* Get all frames in tag 2.
*
* @param frames frame collection to set.
*/
void FlacFile::getAllFramesV2(FrameCollection& frames)
{
OggFile::getAllFramesV2(frames);
int i = 0;
for (PictureList::iterator it = m_pictures.begin();
it != m_pictures.end();
++it) {
(*it).setIndex(i++);
frames.insert(*it);
}
}
/**
* Get a list of frame IDs which can be added.
*
* @return list with frame IDs.
*/
QStringList FlacFile::getFrameIds() const
{
QStringList lst(OggFile::getFrameIds());
#if QT_VERSION >= 0x040000
QStringList::iterator it = lst.begin() + Frame::FT_Picture;
#else
QStringList::iterator it = lst.at(Frame::FT_Picture);
#endif
lst.insert(it, QCM_translate(Frame::getNameFromType(Frame::FT_Picture)));
return lst;
}
#endif // HAVE_FLAC_PICTURE
/**
* Set the vorbis comment block with the comments.
*
......
......@@ -37,6 +37,7 @@ namespace FLAC {
class Chain;
class VorbisComment;
class StreamInfo;
class Picture;
};
};
......@@ -119,6 +120,64 @@ public:
*/
virtual QString getFileExtension() const;
#ifdef HAVE_FLAC_PICTURE
/**
* Check if file has an ID3v2 tag.
*
* @return true if a V2 tag is available.
* @see isTagInformationRead()
*/
virtual bool hasTagV2() const;
/**
* Set a frame in the tags 2.
*
* @param frame frame to set
*
* @return true if ok.
*/
virtual bool setFrameV2(const Frame& frame);
/**
* Add a frame in the tags 2.
*
* @param frame frame to add
*
* @return true if ok.
*/
virtual bool addFrameV2(Frame& frame);
/**
* Delete a frame in the tags 2.
*
* @param frame frame to delete.
*
* @return true if ok.
*/
virtual bool deleteFrameV2(const Frame& frame);
/**
* Remove ID3v2 frames.
*
* @param flt filter specifying which frames to remove
*/
virtual void deleteFramesV2(const FrameFilter& flt);
/**
* Get all frames in tag 2.
*
* @param frames frame collection to set.
*/
virtual void getAllFramesV2(FrameCollection& frames);
/**
* Get a list of frame IDs which can be added.
*
* @return list with frame IDs.
*/
virtual QStringList getFrameIds() const;
#endif // HAVE_FLAC_PICTURE
private:
FlacFile(const FlacFile&);
FlacFile& operator=(const FlacFile&);
......@@ -146,6 +205,16 @@ private:
*/
void setVorbisComment(FLAC::Metadata::VorbisComment* vc);
#ifdef HAVE_FLAC_PICTURE
/** Pictures */
#if QT_VERSION >= 0x040000
typedef QList<Frame> PictureList;
#else
typedef QValueList<Frame> PictureList;
#endif
PictureList m_pictures;
#endif // HAVE_FLAC_PICTURE
/** Info about file. */
FileInfo m_fileInfo;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment