Bug fix : crash with mp4 files
Valentin Rouet
valdu85 at gmail.com
Tue Mar 17 13:27:47 UTC 2009
Here is a patched version of src/meta/File_p.h :
Amarok was crashing when it tried to read tags in mp4 files that had an
empty "composer" or "album" tag.
I added 2 tests :
if ( !mp4tag->itemListMap()["\xA9wrt"].toStringList().isEmpty() )
m_data.composer = strip(
mp4tag->itemListMap()["\xA9wrt"].toStringList().front() );
if ( mp4tag->itemListMap()["disk"].toIntPair().first != NULL)
disc = QString::number(
mp4tag->itemListMap()["disk"].toIntPair().first );
Thanks for your reviews,
Valentin
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.kde.org/pipermail/amarok/attachments/20090317/6bed3a36/attachment.html>
-------------- next part --------------
/*
Copyright (C) 2007 Maximilian Kossick <maximilian.kossick at googlemail.com>
Copyright (C) 2008 Peter ZHOU <peterzhoulei at gmail.com>
Copyright (C) 2008 Seb Ruiz <ruiz at kde.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef AMAROK_META_FILE_P_H
#define AMAROK_META_FILE_P_H
#include "Debug.h"
#include "Meta.h"
#include "MetaUtility.h"
#include "MetaReplayGain.h"
#include <QFile>
#include <QObject>
#include <QPointer>
#include <QSet>
#include <QString>
#include <QTextCodec>
#include <KEncodingProber>
#include <KLocale>
// Taglib Includes
#include <fileref.h>
#include <tag.h>
#include <flacfile.h>
#include <id3v1tag.h>
#include <id3v2tag.h>
#include <mpcfile.h>
#include <mpegfile.h>
#include <oggfile.h>
#include <oggflacfile.h>
#include <tlist.h>
#include <tstring.h>
#include <vorbisfile.h>
#ifdef TAGLIB_EXTRAS_FOUND
#include <mp4file.h>
#endif
namespace MetaFile
{
//d-pointer implementation
struct MetaData
{
MetaData()
: discNumber( 0 )
, trackNumber( 0 )
, length( 0 )
, fileSize( 0 )
, sampleRate( 0 )
, bitRate( 0 )
, year( 0 )
, trackGain( 0.0 )
, trackPeak( 0.0 )
, albumGain( 0.0 )
, albumPeak( 0.0 )
{ }
QString title;
QString artist;
QString album;
QString comment;
QString composer;
QString genre;
int discNumber;
int trackNumber;
int length;
int fileSize;
int sampleRate;
int bitRate;
int year;
qreal trackGain;
qreal trackPeak;
qreal albumGain;
qreal albumPeak;
};
class Track::Private : public QObject
{
public:
Private( Track *t )
: QObject()
, url()
, batchUpdate( false )
, album()
, artist()
, playCount(0)
, track( t )
{}
public:
KUrl url;
bool batchUpdate;
Meta::AlbumPtr album;
Meta::ArtistPtr artist;
Meta::GenrePtr genre;
Meta::ComposerPtr composer;
Meta::YearPtr year;
void readMetaData();
QVariantMap changes;
void writeMetaData() { DEBUG_BLOCK Meta::Field::writeFields( getFileRef(), changes ); changes.clear(); readMetaData(); }
MetaData m_data;
int score;
int rating;
uint lastPlayed;
uint firstPlayed;
int playCount;
private:
TagLib::FileRef getFileRef();
Track *track;
};
TagLib::FileRef
Track::Private::getFileRef()
{
#ifdef COMPLEX_TAGLIB_FILENAME
const wchar_t * encodedName = reinterpret_cast<const wchar_t *>(url.path().utf16());
#else
QByteArray fileName = QFile::encodeName( url.path() );
const char * encodedName = fileName.constData(); // valid as long as fileName exists
#endif
return TagLib::FileRef( encodedName, true, TagLib::AudioProperties::Fast );
}
void Track::Private::readMetaData()
{
#define strip( x ) TStringToQString( x ).trimmed()
debug() << "[BEGIN] Track::Private::readMetaData()";
TagLib::FileRef fileRef = getFileRef();
TagLib::Tag *tag = 0;
if( !fileRef.isNull() )
tag = fileRef.tag();
if( tag )
{
m_data.title = strip( tag->title() );
m_data.artist = strip( tag->artist() );
m_data.album = strip( tag->album() );
m_data.comment = strip( tag->comment() );
m_data.genre = strip( tag->genre() );
m_data.trackNumber = tag->track();
m_data.year = tag->year();
}
if( !fileRef.isNull() )
{
m_data.bitRate = fileRef.audioProperties()->bitrate();
m_data.sampleRate = fileRef.audioProperties()->sampleRate();
m_data.length = fileRef.audioProperties()->length();
Meta::ReplayGainTagMap map = Meta::readReplayGainTags( fileRef );
if ( map.contains( Meta::ReplayGain_Track_Gain ) )
m_data.trackGain = map[Meta::ReplayGain_Track_Gain];
if ( map.contains( Meta::ReplayGain_Track_Peak ) )
m_data.trackPeak = map[Meta::ReplayGain_Track_Peak];
if ( map.contains( Meta::ReplayGain_Album_Gain ) )
m_data.albumGain = map[Meta::ReplayGain_Album_Gain];
else
m_data.albumGain = m_data.trackGain;
if ( map.contains( Meta::ReplayGain_Album_Peak ) )
m_data.albumPeak = map[Meta::ReplayGain_Album_Peak];
else
m_data.albumPeak = m_data.trackPeak;
}
//This is pretty messy...
QString disc;
if( TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File *>( fileRef.file() ) )
{
if( file->ID3v2Tag() )
{
const TagLib::ID3v2::FrameListMap flm = file->ID3v2Tag()->frameListMap();
if( !flm[ "TPOS" ].isEmpty() )
disc = strip( flm[ "TPOS" ].front()->toString() );
if( !flm[ "TCOM" ].isEmpty() )
m_data.composer = strip( flm[ "TCOM" ].front()->toString() );
if( !flm[ "TPE2" ].isEmpty() )
m_data.artist = strip( flm[ "TPE2" ].front()->toString() );
}
else if ( file->ID3v1Tag() )
{
TagLib::String metaData = tag->title() + tag->artist() + tag->album() + tag->comment();
const char* buf = metaData.toCString();
size_t len = strlen( buf );
KEncodingProber prober;
KEncodingProber::ProberState result = prober.feed( buf, len );
QString track_encoding( prober.encodingName() );
if ( result != KEncodingProber::NotMe )
{
//http://doc.trolltech.com/4.4/qtextcodec.html
//http://www.mozilla.org/projects/intl/chardet.html
if ( track_encoding == "x-euc-tw" ) track_encoding = ""; //no match
if ( track_encoding == "HZ-GB2312" ) track_encoding = ""; //no match
if ( track_encoding == "ISO-2022-CN" ) track_encoding = ""; //no match
if ( track_encoding == "ISO-2022-KR" ) track_encoding = ""; //no match
if ( track_encoding == "ISO-2022-JP" ) track_encoding = ""; //no match
if ( track_encoding == "x-mac-cyrillic" ) track_encoding = ""; //no match
if ( track_encoding == "IBM855" ) track_encoding =""; //no match
if ( track_encoding == "IBM866" ) track_encoding = "IBM 866";
if ( track_encoding == "TIS-620" ) track_encoding = ""; //ISO-8859-11, no match
if ( !track_encoding.isEmpty() )
{
//FIXME:about 10% tracks cannot be decoded well. It shows blank for now.
debug () << "Final Codec Name:" << track_encoding.toUtf8() <<endl;
QTextCodec *codec = QTextCodec::codecForName( track_encoding.toUtf8() );
QTextCodec* utf8codec = QTextCodec::codecForName( "UTF-8" );
QTextCodec::setCodecForCStrings( utf8codec );
m_data.title = codec->toUnicode( m_data.title.toLatin1() );
m_data.artist = codec->toUnicode( m_data.artist.toLatin1() );
m_data.album = codec->toUnicode( m_data.album.toLatin1() );
m_data.comment = codec->toUnicode( m_data.comment.toLatin1() );
}
}
}
}
else if( TagLib::Ogg::Vorbis::File *file = dynamic_cast< TagLib::Ogg::Vorbis::File *>( fileRef.file() ) )
{
if( file->tag() )
{
const TagLib::Ogg::FieldListMap flm = file->tag()->fieldListMap();
if( !flm[ "COMPOSER" ].isEmpty() )
m_data.composer = strip( flm[ "COMPOSER" ].front() );
if( !flm[ "DISCNUMBER" ].isEmpty() )
disc = strip( flm[ "DISCNUMBER" ].front() );
}
}
else if( TagLib::FLAC::File *file = dynamic_cast< TagLib::FLAC::File *>( fileRef.file() ) )
{
if( file->xiphComment() )
{
const TagLib::Ogg::FieldListMap flm = file->xiphComment()->fieldListMap();
if( !flm[ "COMPOSER" ].isEmpty() )
m_data.composer = strip( flm[ "COMPOSER" ].front() );
if( !flm[ "DISCNUMBER" ].isEmpty() )
disc = strip( flm[ "DISCNUMBER" ].front() );
}
}
#ifdef TAGLIB_EXTRAS_FOUND
else if( TagLib::MP4::File *file = dynamic_cast<TagLib::MP4::File *>( fileRef.file() ) )
{
TagLib::MP4::Tag *mp4tag = dynamic_cast< TagLib::MP4::Tag *>( file->tag() );
if( mp4tag )
{
if ( !mp4tag->itemListMap()["\xA9wrt"].toStringList().isEmpty() )
m_data.composer = strip( mp4tag->itemListMap()["\xA9wrt"].toStringList().front() );
if ( mp4tag->itemListMap()["disk"].toIntPair().first != NULL)
disc = QString::number( mp4tag->itemListMap()["disk"].toIntPair().first );
}
}
#endif
if( !disc.isEmpty() )
{
int i = disc.indexOf( '/' );
if( i != -1 )
m_data.discNumber = disc.left( i ).toInt();
else
m_data.discNumber = disc.toInt();
}
#undef strip
m_data.fileSize = QFile( url.path() ).size();
debug() << "Read metadata from file for: " + m_data.title;
}
// internal helper classes
class FileArtist : public Meta::Artist
{
public:
FileArtist( MetaFile::Track::Private *dptr )
: Meta::Artist()
, d( dptr )
{}
Meta::TrackList tracks()
{
return Meta::TrackList();
}
Meta::AlbumList albums()
{
return Meta::AlbumList();
}
QString name() const
{
const QString artist = d->m_data.artist;
if( !artist.isEmpty() )
return artist;
return i18nc( "The value is not known", "Unknown" );
}
QString prettyName() const
{
return name();
}
QPointer<MetaFile::Track::Private> const d;
};
class FileAlbum : public Meta::Album
{
public:
FileAlbum( MetaFile::Track::Private *dptr )
: Meta::Album()
, d( dptr )
{}
bool isCompilation() const
{
return false;
}
bool hasAlbumArtist() const
{
return false;
}
Meta::ArtistPtr albumArtist() const
{
return Meta::ArtistPtr();
}
Meta::TrackList tracks()
{
return Meta::TrackList();
}
QString name() const
{
if( d )
{
const QString albumName = d->m_data.album;
if( !albumName.isEmpty() )
return albumName;
else
return i18nc( "The value is not known", "Unknown" );
}
else
return i18nc( "The value is not known", "Unknown" );
}
QString prettyName() const
{
return name();
}
QPixmap image( int size )
{
return Meta::Album::image( size );
}
bool operator==( const Meta::Album &other ) const {
return name() == other.name();
}
QPointer<MetaFile::Track::Private> const d;
};
class FileGenre : public Meta::Genre
{
public:
FileGenre( MetaFile::Track::Private *dptr )
: Meta::Genre()
, d( dptr )
{}
Meta::TrackList tracks()
{
return Meta::TrackList();
}
QString name() const
{
const QString genreName = d->m_data.genre;
if( !genreName.isEmpty() )
return genreName;
return i18nc( "The value is not known", "Unknown" );
}
QString prettyName() const
{
return name();
}
QPointer<MetaFile::Track::Private> const d;
};
class FileComposer : public Meta::Composer
{
public:
FileComposer( MetaFile::Track::Private *dptr )
: Meta::Composer()
, d( dptr )
{}
Meta::TrackList tracks()
{
return Meta::TrackList();
}
QString name() const
{
const QString composer = d->m_data.composer;
if( !composer.isEmpty() )
return composer;
return i18nc( "The value is not known", "Unknown" );
}
QString prettyName() const
{
return name();
}
QPointer<MetaFile::Track::Private> const d;
};
class FileYear : public Meta::Year
{
public:
FileYear( MetaFile::Track::Private *dptr )
: Meta::Year()
, d( dptr )
{}
Meta::TrackList tracks()
{
return Meta::TrackList();
}
QString name() const
{
const QString year = QString::number( d->m_data.year );
if( !year.isEmpty() )
return year;
return i18nc( "The value is not known", "Unknown" );
}
QString prettyName() const
{
return name();
}
QPointer<MetaFile::Track::Private> const d;
};
}
#endif
More information about the Amarok
mailing list