|
|
|
/***************************************************************************
|
|
|
|
copyright : (C) 2005 by Andy Leadbetter
|
|
|
|
email : andrew.leadbetter@gmail.com
|
|
|
|
|
|
|
|
copyright : (C) 2005 by Martin Aumueller
|
|
|
|
email : aumuell@reserv.at
|
|
|
|
(write support)
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* 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 *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "mp4file.h"
|
|
|
|
|
|
|
|
#include "mp4tag.h"
|
|
|
|
#include <tfile.h>
|
|
|
|
#include <audioproperties.h>
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
#ifdef HAVE_MP4V2_H
|
|
|
|
#define USE_ITMF_TAGS
|
|
|
|
#else
|
|
|
|
#define MP4V2_HAS_WRITE_BUG 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace TagLib {
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// public members
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
MP4::File::File(const char *file,
|
|
|
|
bool readProperties,
|
|
|
|
Properties::ReadStyle propertiesStyle,
|
|
|
|
MP4FileHandle handle) : TagLib::File(file),
|
|
|
|
mp4tag(NULL), properties(NULL)
|
|
|
|
{
|
|
|
|
|
|
|
|
// debug ("MP4::File: create new file object.");
|
|
|
|
//debug ( file );
|
|
|
|
/**
|
|
|
|
* Create the MP4 file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if(handle == MP4_INVALID_FILE_HANDLE)
|
|
|
|
{
|
|
|
|
mp4file = MP4Read(file);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mp4file = handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( isOpen() )
|
|
|
|
{
|
|
|
|
read(readProperties, propertiesStyle );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MP4::File::~File()
|
|
|
|
{
|
|
|
|
MP4Close(mp4file);
|
|
|
|
delete mp4tag;
|
|
|
|
delete properties;
|
|
|
|
}
|
|
|
|
|
|
|
|
TagLib::Tag *MP4::File::tag() const
|
|
|
|
{
|
|
|
|
return mp4tag;
|
|
|
|
}
|
|
|
|
|
|
|
|
TagLib::MP4::Tag *MP4::File::getMP4Tag() const
|
|
|
|
{
|
|
|
|
return mp4tag;
|
|
|
|
}
|
|
|
|
|
|
|
|
MP4::Properties *MP4::File::audioProperties() const
|
|
|
|
{
|
|
|
|
return properties;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MP4::File::save()
|
|
|
|
{
|
|
|
|
MP4Close(mp4file);
|
|
|
|
|
|
|
|
MP4FileHandle handle = MP4Modify(name());
|
|
|
|
if(handle == MP4_INVALID_FILE_HANDLE)
|
|
|
|
{
|
|
|
|
mp4file = MP4Read(name());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef USE_ITMF_TAGS
|
|
|
|
const MP4Tags* filetags = MP4TagsAlloc();
|
|
|
|
MP4TagsFetch(filetags, handle);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef MP4V2_HAS_WRITE_BUG
|
|
|
|
/* according to gtkpod we have to delete all meta data before modifying it,
|
|
|
|
save the stuff we would not touch */
|
|
|
|
|
|
|
|
// need to fetch/rewrite this only if we aren't going to anyway
|
|
|
|
uint8_t compilation = 0;
|
|
|
|
bool has_compilation = mp4tag->compilation() == MP4::Tag::Undefined ? MP4GetMetadataCompilation(handle, &compilation) : false;
|
|
|
|
|
|
|
|
char *tool = NULL;
|
|
|
|
MP4GetMetadataTool(handle, &tool);
|
|
|
|
|
|
|
|
MP4MetadataDelete(handle);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef USE_ITMF_TAGS
|
|
|
|
MP4TagsSetName(filetags, mp4tag->title().isNull() ? "" : mp4tag->title().toCString(true));
|
|
|
|
MP4TagsSetArtist(filetags, mp4tag->artist().isNull() ? "" : mp4tag->artist().toCString(true));
|
|
|
|
MP4TagsSetAlbum(filetags, mp4tag->album().isNull() ? "" : mp4tag->album().toCString(true));
|
|
|
|
MP4TagsSetComments(filetags, mp4tag->comment().isNull() ? "" : mp4tag->comment().toCString(true));
|
|
|
|
MP4TagsSetGenre(filetags, mp4tag->genre().isNull() ? "" : mp4tag->genre().toCString(true));
|
|
|
|
MP4TagsSetComposer(filetags, mp4tag->composer().isNull() ? "" : mp4tag->composer().toCString(true));
|
|
|
|
#else
|
|
|
|
#define setmeta(val, tag) \
|
|
|
|
if(mp4tag->val().isNull()) { \
|
|
|
|
/*MP4DeleteMetadata##tag(handle);*/ \
|
|
|
|
MP4SetMetadata##tag(handle, ""); \
|
|
|
|
} else { \
|
|
|
|
MP4SetMetadata##tag(handle, mp4tag->val().toCString(true)); \
|
|
|
|
}
|
|
|
|
setmeta(title, Name);
|
|
|
|
setmeta(artist, Artist);
|
|
|
|
setmeta(album, Album);
|
|
|
|
setmeta(comment, Comment);
|
|
|
|
setmeta(genre, Genre);
|
|
|
|
setmeta(composer, Writer);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
char buf[100] = "";
|
|
|
|
if(mp4tag->year())
|
|
|
|
snprintf(buf, sizeof(buf), "%u", mp4tag->year());
|
|
|
|
#ifdef USE_ITMF_TAGS
|
|
|
|
MP4TagsSetReleaseDate(filetags, buf);
|
|
|
|
#else
|
|
|
|
MP4SetMetadataYear(handle, buf);
|
|
|
|
#endif
|
|
|
|
u_int16_t t1, t2;
|
|
|
|
|
|
|
|
#ifdef USE_ITMF_TAGS
|
|
|
|
MP4TagTrack track = *filetags->track;
|
|
|
|
track.index = t1;
|
|
|
|
MP4TagsSetTrack(filetags, &track);
|
|
|
|
#else
|
|
|
|
MP4GetMetadataTrack(handle, &t1, &t2);
|
|
|
|
MP4SetMetadataTrack(handle, mp4tag->track(), t2);
|
|
|
|
#endif
|
|
|
|
if(mp4tag->bpm() != 0) {
|
|
|
|
#ifdef USE_ITMF_TAGS
|
|
|
|
u_int16_t tempo = mp4tag->bpm();
|
|
|
|
MP4TagsSetTempo(filetags, &tempo);
|
|
|
|
#else
|
|
|
|
MP4SetMetadataTempo(handle, mp4tag->bpm());
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
if(mp4tag->compilation() != MP4::Tag::Undefined) {
|
|
|
|
#ifdef USE_ITMF_TAGS
|
|
|
|
u_int8_t compilation = mp4tag->compilation();
|
|
|
|
MP4TagsSetCompilation(filetags, &compilation);
|
|
|
|
#else
|
|
|
|
MP4SetMetadataCompilation(handle, mp4tag->compilation());
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef USE_ITMF_TAGS
|
|
|
|
if(mp4tag->cover().size()) {
|
|
|
|
MP4TagArtwork art;
|
|
|
|
art.size = mp4tag->cover().size();
|
|
|
|
art.data = mp4tag->cover().size() ? const_cast<u_int8_t *>( reinterpret_cast<const u_int8_t *>( mp4tag->cover().data() ) ) : 0;
|
|
|
|
art.type = MP4_ART_UNDEFINED; // delegate typing to libmp4v2
|
|
|
|
if(filetags->artworkCount > 0) {
|
|
|
|
MP4TagsSetArtwork(filetags, 0, &art);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
MP4TagsAddArtwork(filetags, &art);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
MP4SetMetadataCoverArt(handle, mp4tag->cover().size() ? const_cast<u_int8_t *>( reinterpret_cast<const u_int8_t *>( mp4tag->cover().data() ) ) : 0, mp4tag->cover().size());
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef MP4V2_HAS_WRITE_BUG
|
|
|
|
// set the saved data again
|
|
|
|
|
|
|
|
if(has_compilation)
|
|
|
|
MP4SetMetadataCompilation(handle, compilation);
|
|
|
|
if(tool)
|
|
|
|
{
|
|
|
|
MP4SetMetadataTool(handle, tool);
|
|
|
|
free(tool);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef USE_ITMF_TAGS
|
|
|
|
MP4TagsStore(filetags, handle);
|
|
|
|
MP4TagsFree(filetags);
|
|
|
|
#endif
|
|
|
|
MP4Close(handle);
|
|
|
|
|
|
|
|
mp4file = MP4Read(name());
|
|
|
|
if(mp4file == MP4_INVALID_FILE_HANDLE)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "reopen failed\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MP4::File::isOpen()
|
|
|
|
{
|
|
|
|
return mp4file != MP4_INVALID_FILE_HANDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// private members
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void MP4::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
|
|
|
|
{
|
|
|
|
properties = new MP4::Properties(propertiesStyle);
|
|
|
|
mp4tag = new MP4::Tag();
|
|
|
|
|
|
|
|
if (mp4file != MP4_INVALID_FILE_HANDLE) {
|
|
|
|
|
|
|
|
if(readProperties)
|
|
|
|
{
|
|
|
|
// Parse bitrate etc.
|
|
|
|
properties->readMP4Properties( mp4file );
|
|
|
|
}
|
|
|
|
|
|
|
|
mp4tag->readTags( mp4file );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|