Tag::Tag(const QString &filename) : m_filename(filename) { TagLib::MPEG::File mpegfile(filename.toLocal8Bit().constData()); TagLib::ID3v2::Tag* id3v2 = mpegfile.ID3v2Tag(); if (id3v2 && !id3v2->isEmpty()) { readRegularTag(id3v2, m_data); int picnum = 0; TagLib::ID3v2::FrameList frames = id3v2->frameListMap()["APIC"]; // attached picture TagLib::ID3v2::FrameList::ConstIterator it = frames.begin(); while (it != frames.end()) { TagLib::ID3v2::AttachedPictureFrame* apic = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(*it); TagLib::ByteVector bytes = apic->picture(); QImage img = QImage::fromData(reinterpret_cast<const uchar*>(bytes.data()), bytes.size()); if (!img.isNull()) { m_data[QLatin1String("picture") + QString::number(picnum++)] = QVariant(img); } ++it; } } else { TagLib::FileRef fileref(filename.toLocal8Bit().constData()); if (fileref.isNull()) return; TagLib::Tag* tag = fileref.tag(); if (!tag || tag->isEmpty()) return; readRegularTag(tag, m_data); } }
bool writeFileFromVector(const char *path, const TagLib::ByteVector &v) { std::ofstream o(path, std::ofstream::binary); if (!o) { return false; } o.write(v.data(), v.size()); return true; }
/* ** Write cover data to file. ** */ bool CCover::WriteCover(const TagLib::ByteVector& data, const std::wstring& target) { bool written = false; FILE* f = _wfopen(target.c_str(), L"wb"); if (f) { written = (fwrite(data.data(), 1, data.size(), f) == data.size()); fclose(f); } return written; }
TagLib::ByteVector CloudStream::readBlock(ulong length) { const uint start = cursor_; const uint end = qMin(cursor_ + length - 1, length_ - 1); if (end < start) { return TagLib::ByteVector(); } if (CheckCache(start, end)) { TagLib::ByteVector cached = GetCached(start, end); cursor_ += cached.size(); return cached; } QNetworkRequest request = QNetworkRequest(url_); if (!auth_.isEmpty()) { request.setRawHeader("Authorization", auth_.toUtf8()); } request.setRawHeader("Range", QString("bytes=%1-%2").arg(start).arg(end).toUtf8()); request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); // The Ubuntu One server applies the byte range to the gzipped data, rather // than the raw data so we must disable compression. if (url_.host() == "files.one.ubuntu.com") { request.setRawHeader("Accept-Encoding", "identity"); } QNetworkReply* reply = network_->get(request); connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(SSLErrors(QList<QSslError>))); ++num_requests_; QEventLoop loop; QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); loop.exec(); reply->deleteLater(); int code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (code >= 400) { qLog(Debug) << "Error retrieving url to tag:" << url_; return TagLib::ByteVector(); } QByteArray data = reply->readAll(); TagLib::ByteVector bytes(data.data(), data.size()); cursor_ += data.size(); FillCache(start, bytes); return bytes; }
QString extractMBIDFromFile(TagLib::MPEG::File *file) { TagLib::ID3v2::Tag *tag = file->ID3v2Tag(); TagLib::ID3v2::FrameList ufid = tag->frameListMap()["UFID"]; if (!ufid.isEmpty()) { for (TagLib::ID3v2::FrameList::Iterator i = ufid.begin(); i != ufid.end(); i++) { TagLib::ID3v2::UniqueFileIdentifierFrame *frame = dynamic_cast<TagLib::ID3v2::UniqueFileIdentifierFrame *>(*i); if (frame && frame->owner() == "http://musicbrainz.org") { TagLib::ByteVector id = frame->identifier(); return QString::fromAscii(id.data(), id.size()); } } } return QString(); }
void CloudStream::FillCache( int start, TagLib::ByteVector data ) { for ( int i = 0; i < data.size(); ++i ) { m_cache.set( start + i, data[i] ); } }
TagLib::ByteVector CloudStream::readBlock(ulong length) { const uint start = cursor_; const uint end = qMin(cursor_ + length - 1, length_ - 1); if (end < start) { return TagLib::ByteVector(); } if (CheckCache(start, end)) { TagLib::ByteVector cached = GetCached(start, end); cursor_ += cached.size(); return cached; } QNetworkRequest request = QNetworkRequest(url_); foreach (const QString& key, headers_) { request.setRawHeader(key.toLatin1(), headers_[key].toUtf8()); }
/* ** Extracts cover art embedded in APE tags. ** */ bool CCover::ExtractAPE(TagLib::APE::Tag* tag, const std::wstring& target) { const TagLib::APE::ItemListMap& listMap = tag->itemListMap(); if (listMap.contains("COVER ART (FRONT)")) { const TagLib::ByteVector nullStringTerminator(1, 0); TagLib::ByteVector item = listMap["COVER ART (FRONT)"].value(); int pos = item.find(nullStringTerminator); // Skip the filename if (++pos > 0) { const TagLib::ByteVector& pic = item.mid(pos); return WriteCover(pic, target); } } return false; }
bool CoverUtils::coverFromXiphComment(TagLib::Ogg::XiphComment *xiphComment, Album *album) { if (!xiphComment) return false; const TagLib::StringList &stringList = xiphComment->fieldListMap()["COVERART"]; if (stringList.isEmpty()) return false; TagLib::ByteVector byteVector = stringList.front().data(TagLib::String::Latin1); QByteArray encodedData; encodedData.setRawData(byteVector.data(), byteVector.size()); QByteArray data = QByteArray::fromBase64(encodedData); QImage image; image.loadFromData(data); if (!isAcceptableImage(image)) return false; qDebug() << "Cover from Xiph!"; return saveImage(image, album); }
/* ** Extracts cover art embedded in APE tags. ** */ bool QCoverArt::ExtractAPE(TagLib::APE::Tag* tag) { const TagLib::APE::ItemListMap& listMap = tag->itemListMap(); if (listMap.contains("COVER ART (FRONT)")) { const TagLib::ByteVector nullStringTerminator(1, 0); TagLib::ByteVector item = listMap["COVER ART (FRONT)"].value(); int pos = item.find(nullStringTerminator); // Skip the filename if (++pos > 0) { const TagLib::ByteVector& pic = item.mid(pos); img.loadFromData((const unsigned char*)pic.data(), pic.size()); return true; } } return false; }
// NOTE: representation is taken to be a binary value with units in the first column, // 1/2 in the second and so on. static qreal readRVA2PeakValue( const TagLib::ByteVector &data, int bits, bool *ok ) { qreal peak = 0.0; // discarding digits at the end reduces precision, but doesn't otherwise change the value if ( bits > 32 ) bits = 32; // the +7 makes sure we round up when we divide by 8 unsigned int bytes = (bits + 7) / 8; // normalize appears not to write a peak at all, and hence sets bits to 0 if ( bits == 0 ) { if ( ok ) *ok = true; } else if ( bits >= 4 && data.size() >= bytes ) // fewer than 4 bits would just be daft { // excessBits is the number of bits we have to discard at the end unsigned int excessBits = (8 * bytes) - bits; // mask has 1s everywhere but the last /excessBits/ bits quint32 mask = 0xffffffff << excessBits; quint32 rawValue = 0; for ( unsigned int i = 0; i < bytes; ++i ) { rawValue <<= 8; rawValue += (unsigned char)data[i]; } rawValue &= mask; peak = rawValue; // amount we need to "shift" the value right to make the first digit the unit column unsigned int rightShift = (8 * bytes) - 1; peak /= (qreal)(1 << rightShift); if ( ok ) *ok = true; } else { if ( ok ) *ok = false; } return peak; }
size_t loadFileIntoVector(const char *path, TagLib::ByteVector &v) { std::ifstream is(path, std::ifstream::binary); if (!is.good()) { return 0; } // get the file size and create an appropriate buffer is.seekg(0, is.end); size_t len = is.tellg(); is.seekg(0, is.beg); char *buf = new char[len]; // read into buffer is.read(buf, len); v.setData(buf, len); is.close(); delete[] buf; return len; }
static Meta::ReplayGainTagMap readID3v2Tags( TagLib::ID3v2::Tag *tag ) { Meta::ReplayGainTagMap map; { // ID3v2.4.0 native replay gain tag support (as written by Quod Libet, for example). TagLib::ID3v2::FrameList frames = tag->frameListMap()["RVA2"]; frames.append(tag->frameListMap()["XRVA"]); if ( !frames.isEmpty() ) { for ( unsigned int i = 0; i < frames.size(); ++i ) { // we have to parse this frame ourselves // ID3v2 frame header is 10 bytes, so skip that TagLib::ByteVector data = frames[i]->render().mid( 10 ); unsigned int offset = 0; QString desc( data.data() ); offset += desc.count() + 1; unsigned int channel = data.mid( offset, 1 ).toUInt( true ); // channel 1 is the main volume - the only one we care about if ( channel == 1 ) { ++offset; qint16 adjustment512 = data.mid( offset, 2 ).toShort( true ); qreal adjustment = ( (qreal)adjustment512 ) / 512.0; offset += 2; unsigned int peakBits = data.mid( offset, 1 ).toUInt( true ); ++offset; bool ok = false; qreal peak = readRVA2PeakValue( data.mid( offset ), peakBits, &ok ); if ( ok ) { if ( desc.toLower() == "album" ) { map[Meta::ReplayGain_Album_Gain] = adjustment; map[Meta::ReplayGain_Album_Peak] = peakToDecibels( peak ); } else if ( desc.toLower() == "track" || !map.contains( Meta::ReplayGain_Track_Gain ) ) { map[Meta::ReplayGain_Track_Gain] = adjustment; map[Meta::ReplayGain_Track_Peak] = peakToDecibels( peak ); } } } } if ( !map.isEmpty() ) return map; } } { // Foobar2000-style ID3v2.3.0 tags TagLib::ID3v2::FrameList frames = tag->frameListMap()["TXXX"]; for ( TagLib::ID3v2::FrameList::Iterator it = frames.begin(); it != frames.end(); ++it ) { TagLib::ID3v2::UserTextIdentificationFrame* frame = dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame*>( *it ); if ( frame && frame->fieldList().size() >= 2 ) { QString desc = TStringToQString( frame->description() ).toLower(); if ( desc == "replaygain_album_gain" ) maybeAddGain( frame->fieldList()[1], Meta::ReplayGain_Album_Gain, &map ); if ( desc == "replaygain_album_peak" ) maybeAddPeak( frame->fieldList()[1], Meta::ReplayGain_Album_Peak, &map ); if ( desc == "replaygain_track_gain" ) maybeAddGain( frame->fieldList()[1], Meta::ReplayGain_Track_Gain, &map ); if ( desc == "replaygain_track_peak" ) maybeAddPeak( frame->fieldList()[1], Meta::ReplayGain_Track_Peak, &map ); } } } return map; }
virtual TagLib::String parse( const TagLib::ByteVector &data ) const { return QStringToTString( m_codec->toUnicode( data.data(), data.size() ) ); }
/*! * \brief Write the albumart image to the file * * \param filename The music file to add the albumart * \param albumart The Album Art image to write * \returns True if successful * * \note We always save the image in JPEG format */ bool MetaIOID3::writeAlbumArt(const QString &filename, const AlbumArtImage *albumart) { if (filename.isEmpty() || !albumart) return false; // load the image into a QByteArray QImage image(albumart->m_filename); QByteArray imageData; QBuffer buffer(&imageData); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "JPEG"); AttachedPictureFrame::Type type = AttachedPictureFrame::Other; switch (albumart->m_imageType) { case IT_FRONTCOVER: type = AttachedPictureFrame::FrontCover; break; case IT_BACKCOVER: type = AttachedPictureFrame::BackCover; break; case IT_CD: type = AttachedPictureFrame::Media; break; case IT_INLAY: type = AttachedPictureFrame::LeafletPage; break; case IT_ARTIST: type = AttachedPictureFrame::Artist; break; default: type = AttachedPictureFrame::Other; break; } if (!OpenFile(filename, true)) return false; TagLib::ID3v2::Tag *tag = GetID3v2Tag(); if (!tag) return false; AttachedPictureFrame *apic = findAPIC(tag, type, QStringToTString(albumart->m_description)); if (!apic) { apic = new AttachedPictureFrame(); tag->addFrame(apic); apic->setType(type); } QString mimetype = "image/jpeg"; TagLib::ByteVector bytevector; bytevector.setData(imageData.data(), imageData.size()); apic->setMimeType(QStringToTString(mimetype)); apic->setPicture(bytevector); apic->setDescription(QStringToTString(albumart->m_description)); if (!SaveFile()) return false; return true; }
TagLib::ByteVector TagLib::DecodeBase64(const TagLib::ByteVector& input) { SFB::CFError error; SFB::SecTransform decoder(SecDecodeTransformCreate(kSecBase64Encoding, &error)); if(!decoder) { LOGGER_WARNING("org.sbooth.AudioEngine", "SecDecodeTransformCreate failed: " << error); return {}; } SFB::CFData sourceData(CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)input.data(), (CFIndex)input.size(), kCFAllocatorNull)); if(!sourceData) return {}; if(!SecTransformSetAttribute(decoder, kSecTransformInputAttributeName, sourceData, &error)) { LOGGER_WARNING("org.sbooth.AudioEngine", "SecTransformSetAttribute failed: " << error); return {}; } SFB::CFData decodedData((CFDataRef)SecTransformExecute(decoder, &error)); if(!decodedData) return {}; return {(const char *)CFDataGetBytePtr((CFDataRef)decodedData), (size_t)CFDataGetLength((CFDataRef)decodedData)}; }
TagLib::ByteVector TagLib::EncodeBase64(const TagLib::ByteVector& input) { ByteVector result; CFErrorRef error; SFB::SecTransform encoder = SecEncodeTransformCreate(kSecBase64Encoding, &error); if(nullptr == encoder) { LOGGER_WARNING("org.sbooth.AudioEngine", "SecEncodeTransformCreate failed: " << error); return TagLib::ByteVector::null; } SFB::CFData sourceData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)input.data(), (CFIndex)input.size(), kCFAllocatorNull); if(!sourceData) return TagLib::ByteVector::null; if(!SecTransformSetAttribute(encoder, kSecTransformInputAttributeName, sourceData, &error)) { LOGGER_WARNING("org.sbooth.AudioEngine", "SecTransformSetAttribute failed: " << error); return TagLib::ByteVector::null; } SFB::CFData encodedData = (CFDataRef)SecTransformExecute(encoder, &error); if(!encodedData) { LOGGER_WARNING("org.sbooth.AudioEngine", "SecTransformExecute failed: " << error); return TagLib::ByteVector::null; } result.setData((const char *)CFDataGetBytePtr((CFDataRef)encodedData), (TagLib::uint)CFDataGetLength((CFDataRef)encodedData)); return result; }
void MediaScannerThread::run() { if (multiPass) { qDebug() << "MediaScannerThread: multi non-single pass selected"; multiPass = false; for (int i=0;i<multiPassFileNames.count();i++) { QString fullPath = multiPassFileNames.at(i); TagLib::FileRef testfile(fullPath.toStdString().c_str()); if (!testfile.isNull()) { ID3Tag *tmpTag = 0; if (!tmpTag) { tmpTag = new ID3Tag(); tmpTag->valid = false; } if (TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File *>( testfile.file())) { if (file->ID3v2Tag(false)) { //Check to see if there are images in the tag #ifdef Q_OS_WIN32 //Currently this only works in the windows version of taglib?? if (!file->ID3v2Tag()->frameListMap()["APIC"].isEmpty()) { TagLib::ID3v2::FrameList frameList = file->ID3v2Tag()->frameListMap()["APIC"]; TagLib::ID3v2::FrameList::Iterator iter; for( iter = frameList.begin(); iter != frameList.end(); ++iter ) { TagLib::ID3v2::AttachedPictureFrame::Type t = (((TagLib::ID3v2::AttachedPictureFrame*)*iter))->type(); if (t == TagLib::ID3v2::AttachedPictureFrame::Type::FrontCover) { TagLib::ByteVector pic = (((TagLib::ID3v2::AttachedPictureFrame*)*iter))->picture(); QImage tmpPicture; tmpPicture.loadFromData((uchar*)pic.data(),pic.size()); tmpTag->frontcover = tmpPicture; } else if (t == TagLib::ID3v2::AttachedPictureFrame::Type::BackCover) { TagLib::ByteVector pic = (((TagLib::ID3v2::AttachedPictureFrame*)*iter))->picture(); QImage tmpPicture; tmpPicture.loadFromData((uchar*)pic.data(),pic.size()); tmpTag->backcover = tmpPicture; } } } #endif //Q_OS_WIN32 TagLib::Tag *tag = testfile.tag(); if (!tmpTag->valid) { tmpTag->artist = tag->artist().toCString(); tmpTag->title = tag->title().toCString(); tmpTag->filename = fullPath; emit tagReceived("",0,tmpTag); } } } else { TagLib::Tag *tag = testfile.tag(); if (!tmpTag->valid) { tmpTag->artist = tag->artist().toCString(); tmpTag->title = tag->title().toCString(); tmpTag->filename = fullPath; emit tagReceived("",0,tmpTag); } } } else { qDebug() << "MediaScannerThread: Invalid File in multipass" << fullPath; int stop = 1; } QThread::msleep(50); } multiPassFileNames.clear(); } else if (singlePass) { qDebug() << "MediaScannerThread: Non-multi single pass selected"; singlePass = false; QString fullPath = singlePassFileName; TagLib::FileRef testfile(fullPath.toStdString().c_str()); if (!testfile.isNull()) { TagLib::Tag *tag = testfile.tag(); //if (tag) //{ ID3Tag *tmpTag = 0; if (!tmpTag) { tmpTag = new ID3Tag(); tmpTag->valid = false; } if (!tmpTag->valid) { tmpTag->artist = tag->artist().toCString(); tmpTag->title = tag->title().toCString(); tmpTag->filename = fullPath; emit tagReceived("",0,tmpTag); } //ID3Tag *tmpTag = new ID3Tag(); } else { qDebug() << "MediaScannerThread: Invalid File in singlepass" << fullPath; } } else { qDebug() << "MediaScannerThread: Non-multi non-single pass selected"; bool firstScan = true; int delay=0; bool anyValid = true; while (anyValid) { anyValid = false; if (firstScan) { delay=0; //firstScan = false; } else { delay=20; } for (int i=0;i<m_playLists->count();i++) { for (int j=0;j<m_playLists->at(i)->size();j++) { QString fullPath = m_playLists->at(i)->getFileNum(j)->fullPath(); // qDebug() << fullPath; TagLib::FileRef testfile(fullPath.toStdString().c_str()); if (!testfile.isNull()) { ID3Tag *tmpTag = m_playLists->at(i)->getFileNum(j)->getId3Tag(); if (!tmpTag) { tmpTag = new ID3Tag(); tmpTag->valid = false; anyValid = true; } if (TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File *>( testfile.file())) { if (file->ID3v2Tag(false)) { //Check to see if there are images in the tag #ifdef Q_OS_WIN32 //Currently this only works in the windows version of taglib?? if (!file->ID3v2Tag()->frameListMap()["APIC"].isEmpty()) { TagLib::ID3v2::FrameList frameList = file->ID3v2Tag()->frameListMap()["APIC"]; TagLib::ID3v2::FrameList::Iterator iter; for( iter = frameList.begin(); iter != frameList.end(); ++iter ) { TagLib::ID3v2::AttachedPictureFrame::Type t = (((TagLib::ID3v2::AttachedPictureFrame*)*iter))->type(); if (t == TagLib::ID3v2::AttachedPictureFrame::Type::FrontCover) { TagLib::ByteVector pic = (((TagLib::ID3v2::AttachedPictureFrame*)*iter))->picture(); QImage tmpPicture; tmpPicture.loadFromData((uchar*)pic.data(),pic.size()); tmpTag->frontcover = tmpPicture; } else if (t == TagLib::ID3v2::AttachedPictureFrame::Type::BackCover) { TagLib::ByteVector pic = (((TagLib::ID3v2::AttachedPictureFrame*)*iter))->picture(); QImage tmpPicture; tmpPicture.loadFromData((uchar*)pic.data(),pic.size()); tmpTag->backcover = tmpPicture; } } } #endif TagLib::Tag *tag = testfile.tag(); if (!tmpTag->valid) { tmpTag->artist = tag->artist().toCString(); tmpTag->title = tag->title().toCString(); tmpTag->filename = fullPath; emit tagReceived(m_playLists->at(i)->title(),j,tmpTag); } } } else { TagLib::Tag *tag = testfile.tag(); if (!tmpTag->valid) { tmpTag->artist = tag->artist().toCString(); tmpTag->title = tag->title().toCString(); tmpTag->valid = true; tmpTag->filename = fullPath; m_playLists->at(i)->setId3(j,tmpTag); anyValid = true; emit tagReceived(m_playLists->at(i)->title(),j,tmpTag); } else { if (tmpTag->filename != fullPath) { tmpTag->artist = tag->artist().toCString(); tmpTag->title = tag->title().toCString(); tmpTag->filename = fullPath; tmpTag->valid = true; m_playLists->at(i)->setId3(j,tmpTag); anyValid = true; emit tagReceived(m_playLists->at(i)->title(),j,tmpTag); } } } //ID3Tag *tmpTag = new ID3Tag(); } else { qDebug() << "MediaScannerThread: Invalid File in standardpass" << fullPath; } QThread::msleep(delay); } } if (firstScan) { firstScan = false; qDebug() << "Initial scan of id3 tags completed. "; if (anyValid) { qDebug() << "Moving to background mode..."; } else { qDebug() << "All files scanned, exiting thread"; } } else { qDebug() << "Secondary id3 scan complete... continuing..."; } } } /* int done = 1; return; QFile tmpFile("music.conf"); if (!tmpFile.open(QIODevice::ReadOnly | QIODevice::Text)) { printf("Error opening configuration file!!!\n"); return; } QString musicDir(tmpFile.readLine()); printf("Music Directory from configuration file: %s\n",musicDir.toStdString().c_str()); tmpFile.close(); musicDir = musicDir.mid(0,musicDir.length()-1); QDir tmpDir(musicDir); tmpDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QDir tmpFiles(musicDir); tmpFiles.setFilter(QDir::Files | QDir::NoDotAndDotDot); QStringList infoList = tmpDir.entryList(); foreach (QString info,infoList) { TagLib::FileRef testfile(info.toStdString().c_str()); TagLib::Tag *tag = testfile.tag(); ID3Tag *tmpTag = new ID3Tag(); tmpTag->artist = tag->artist().to8Bit().c_str(); tmpTag->title = tag->title().to8Bit().c_str(); //emit tagReceived(tmpTag); } bool inLoop = true; //while (inLoop) //{ //QThread:: // QThread::sleep(1); // ID3Tag *tmpTag = new ID3Tag(); //tmpTag->artist = //} */ }
TagLib::ByteVector TagLib::DecodeBase64(const TagLib::ByteVector& input) { #if USE_SECURITY_FRAMEWORK ByteVector result; CFErrorRef error; SecTransformRef decoder = SecDecodeTransformCreate(kSecBase64Encoding, &error); if(nullptr == decoder) { CFShow(error); return TagLib::ByteVector::null; } CFDataRef sourceData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)input.data(), input.size(), kCFAllocatorNull); if(nullptr == sourceData) { CFRelease(decoder), decoder = nullptr; return TagLib::ByteVector::null; } if(!SecTransformSetAttribute(decoder, kSecTransformInputAttributeName, sourceData, &error)) { CFShow(error); CFRelease(sourceData), sourceData = nullptr; CFRelease(decoder), decoder = nullptr; return TagLib::ByteVector::null; } CFTypeRef decodedData = SecTransformExecute(decoder, &error); if(nullptr == decodedData) { CFShow(error); CFRelease(sourceData), sourceData = nullptr; CFRelease(decoder), decoder = nullptr; return TagLib::ByteVector::null; } result.setData((const char *)CFDataGetBytePtr((CFDataRef)decodedData), (TagLib::uint)CFDataGetLength((CFDataRef)decodedData)); CFRelease(decodedData), decodedData = nullptr; CFRelease(sourceData), sourceData = nullptr; CFRelease(decoder), decoder = nullptr; return result; #else ByteVector result; BIO *b64 = BIO_new(BIO_f_base64()); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); BIO *bio = BIO_new_mem_buf(reinterpret_cast<void *>(const_cast<char *>(input.data())), input.size()); bio = BIO_push(b64, bio); char inbuf [512]; int inlen; while(0 < (inlen = BIO_read(bio, inbuf, 512))) result.append(ByteVector(inbuf, inlen)); BIO_free_all(bio); return result; #endif }
TagLib::ByteVector TagLib::EncodeBase64(const TagLib::ByteVector& input) { #if USE_SECURITY_FRAMEWORK ByteVector result; CFErrorRef error; SecTransformRef encoder = SecEncodeTransformCreate(kSecBase64Encoding, &error); if(nullptr == encoder) { CFShow(error); return TagLib::ByteVector::null; } CFDataRef sourceData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)input.data(), input.size(), kCFAllocatorNull); if(nullptr == sourceData) { CFRelease(encoder), encoder = nullptr; return TagLib::ByteVector::null; } if(!SecTransformSetAttribute(encoder, kSecTransformInputAttributeName, sourceData, &error)) { CFShow(error); CFRelease(sourceData), sourceData = nullptr; CFRelease(encoder), encoder = nullptr; return TagLib::ByteVector::null; } CFTypeRef encodedData = SecTransformExecute(encoder, &error); if(nullptr == encodedData) { CFShow(error); CFRelease(sourceData), sourceData = nullptr; CFRelease(encoder), encoder = nullptr; return TagLib::ByteVector::null; } result.setData((const char *)CFDataGetBytePtr((CFDataRef)encodedData), (TagLib::uint)CFDataGetLength((CFDataRef)encodedData)); CFRelease(encodedData), encodedData = nullptr; CFRelease(sourceData), sourceData = nullptr; CFRelease(encoder), encoder = nullptr; return result; #else ByteVector result; BIO *b64 = BIO_new(BIO_f_base64()); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); BIO *bio = BIO_new(BIO_s_mem()); bio = BIO_push(b64, bio); BIO_write(bio, input.data(), input.size()); (void)BIO_flush(bio); void *mem = nullptr; long size = BIO_get_mem_data(bio, &mem); if(0 < size) result.setData(static_cast<const char *>(mem), static_cast<uint>(size)); BIO_free_all(bio); return result; #endif }
void DSFHeader::parse(const TagLib::ByteVector &data) { if (data.size() < DSD_HEADER_SIZE + FMT_HEADER_SIZE) { std::cerr <<"DSFHeader::parse(): header size incorrect" << std::endl; return; } const char *hdr = data.data(); size_t offset = 0; // // ******** DSD chunk header ******** // DSD header chunk should start with "DSD ". // if (hdr[0] != 'D' || hdr[1] != 'S' || hdr[2] != 'D' || hdr[3] != ' ') { std::cerr <<"DSD::Header::parse(): DSD header's first 4 bytes != 'DSD '" << std::endl; return; } offset += 4; // The next 8 bytes contain the size of DSD header // (numerical data is stored in little endian) if (data.toLongLong(offset, false) != DSD_HEADER_SIZE) { std::cerr <<"DSD::Header::parse(): DSD header size is incorrect" << std::endl; return; } offset += LONG_INT_SIZE; // The next 8 bytes contains the file size d->fileSize = bytesToUInt64(&hdr[0], offset); offset += LONG_INT_SIZE; // The next 8 bytes contains the offset to id3v2 tag (0 if not exists) d->ID3v2Offset = bytesToUInt64(&hdr[0], offset); offset += LONG_INT_SIZE; // // ********* FMT chunk ******** // // FMT header chunk should start with "fmt ". // if (hdr[offset] != 'f' || hdr[offset + 1] != 'm' || hdr[offset + 2] != 't' || hdr[offset + 3] != ' ') { std::cerr <<"DSD::Header::parse(): FMT header's first 4 bytes != 'fmt '" << std::endl; return; } offset += 4; // The next 8 bytes contain the size of FMT header, which should be 52 if (data.toLongLong(offset, false) != FMT_HEADER_SIZE) { std::cerr <<"DSD::Header::parse(): FMT header size is incorrect" << std::endl; return; } offset += LONG_INT_SIZE; // Format version // There's only version 1 for now... unsigned int ver = data.toUInt(offset, false); if (ver != 1) { std::cerr <<"DSD::Header::parse(): format version != 1" << std::endl; return; } d->version = static_cast<Version>(ver); offset += INT_SIZE; // Format ID if (data.toUInt(offset, false) != 0) { std::cerr <<"DSD::Header::parse(): format ID != 0" << std::endl; return; } offset += INT_SIZE; // Channel Type unsigned int ct = data.toUInt(offset, false); if (ct < 1 || ct > 7) { std::cerr <<"DSD::Header::parse(): channel type out of range" << std::endl; return; } d->channelType = static_cast<ChannelType>(ct); offset += INT_SIZE; // Channel Num d->channelNum = data.toUInt(offset, false); if (d->channelNum < MinType || d->channelNum > MaxType) { std::cerr <<"DSD::Header::parse(): channel num out of range" << std::endl; return; } offset += INT_SIZE; // Sampling frequency d->sampleRate = data.toUInt(offset, false); if (d->sampleRate != 2822400 && d->sampleRate != 5644800) { std::cerr <<"DSD::Header::parse(): invalid sampling frequency" << std::endl; return; } offset += INT_SIZE; // Bits per sample d->bitsPerSample = data.toUInt(offset, false); if (d->bitsPerSample != 1 && d->bitsPerSample != 8) { std::cerr <<"DSD::Header::parse(): bits per sample invalid" << std::endl; return; } offset += INT_SIZE; // Sample count d->sampleCount = bytesToUInt64(&hdr[0], offset); offset += LONG_INT_SIZE; // Block size per channel if (data.toUInt(offset, false) != 4096) { std::cerr <<"DSD::Header::parse(): block size != 4096" << std::endl; return; } //offset += 4; // Reserved // offset += 4; d->isValid = true; }
bool SetAPETagFromMetadata(const AudioMetadata& metadata, TagLib::APE::Tag *tag, bool setAlbumArt) { if(nullptr == tag) return false; // Standard tags SetAPETag(tag, "ALBUM", metadata.GetAlbumTitle()); SetAPETag(tag, "ARTIST", metadata.GetArtist()); SetAPETag(tag, "ALBUMARTIST", metadata.GetAlbumArtist()); SetAPETag(tag, "COMPOSER", metadata.GetComposer()); SetAPETag(tag, "GENRE", metadata.GetGenre()); SetAPETag(tag, "DATE", metadata.GetReleaseDate()); SetAPETag(tag, "DESCRIPTION", metadata.GetComment()); SetAPETag(tag, "TITLE", metadata.GetTitle()); SetAPETagNumber(tag, "TRACKNUMBER", metadata.GetTrackNumber()); SetAPETagNumber(tag, "TRACKTOTAL", metadata.GetTrackTotal()); SetAPETagBoolean(tag, "COMPILATION", metadata.GetCompilation()); SetAPETagNumber(tag, "DISCNUMBER", metadata.GetDiscNumber()); SetAPETagNumber(tag, "DISCTOTAL", metadata.GetDiscTotal()); SetAPETagNumber(tag, "BPM", metadata.GetBPM()); SetAPETagNumber(tag, "RATING", metadata.GetRating()); SetAPETag(tag, "ISRC", metadata.GetISRC()); SetAPETag(tag, "MCN", metadata.GetMCN()); SetAPETag(tag, "TITLESORT", metadata.GetTitleSortOrder()); SetAPETag(tag, "ALBUMTITLESORT", metadata.GetAlbumTitleSortOrder()); SetAPETag(tag, "ARTISTSORT", metadata.GetArtistSortOrder()); SetAPETag(tag, "ALBUMARTISTSORT", metadata.GetAlbumArtistSortOrder()); SetAPETag(tag, "COMPOSERSORT", metadata.GetComposerSortOrder()); SetAPETag(tag, "GROUPING", metadata.GetGrouping()); // Additional metadata CFDictionaryRef additionalMetadata = metadata.GetAdditionalMetadata(); if(nullptr != additionalMetadata) { CFIndex count = CFDictionaryGetCount(additionalMetadata); const void * keys [count]; const void * values [count]; CFDictionaryGetKeysAndValues(additionalMetadata, reinterpret_cast<const void **>(keys), reinterpret_cast<const void **>(values)); for(CFIndex i = 0; i < count; ++i) { CFIndex keySize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(reinterpret_cast<CFStringRef>(keys[i])), kCFStringEncodingASCII); char key [keySize + 1]; if(!CFStringGetCString(reinterpret_cast<CFStringRef>(keys[i]), key, keySize + 1, kCFStringEncodingASCII)) { LOGGER_ERR("org.sbooth.AudioEngine", "CFStringGetCString failed"); continue; } SetAPETag(tag, key, reinterpret_cast<CFStringRef>(values[i])); } } // ReplayGain info SetAPETagDouble(tag, "REPLAYGAIN_REFERENCE_LOUDNESS", metadata.GetReplayGainReferenceLoudness(), CFSTR("%2.1f dB")); SetAPETagDouble(tag, "REPLAYGAIN_TRACK_GAIN", metadata.GetReplayGainTrackGain(), CFSTR("%+2.2f dB")); SetAPETagDouble(tag, "REPLAYGAIN_TRACK_PEAK", metadata.GetReplayGainTrackPeak(), CFSTR("%1.8f")); SetAPETagDouble(tag, "REPLAYGAIN_ALBUM_GAIN", metadata.GetReplayGainAlbumGain(), CFSTR("%+2.2f dB")); SetAPETagDouble(tag, "REPLAYGAIN_ALBUM_PEAK", metadata.GetReplayGainAlbumPeak(), CFSTR("%1.8f")); // Album art if(setAlbumArt) { tag->removeItem("Cover Art (Front)"); tag->removeItem("Cover Art (Back)"); #if 0 tag->removeItem("METADATA_BLOCK_PICTURE"); #endif for(auto attachedPicture : metadata.GetAttachedPictures()) { // APE can handle front and back covers natively if(AttachedPicture::Type::FrontCover == attachedPicture->GetType() || AttachedPicture::Type::FrontCover == attachedPicture->GetType()) { TagLib::ByteVector data; if(attachedPicture->GetDescription()) data.append(TagLib::StringFromCFString(attachedPicture->GetDescription()).data(TagLib::String::UTF8)); data.append('\0'); data.append(TagLib::ByteVector((const char *)CFDataGetBytePtr(attachedPicture->GetData()), (TagLib::uint)CFDataGetLength(attachedPicture->GetData()))); if(AttachedPicture::Type::FrontCover == attachedPicture->GetType()) tag->setData("Cover Art (Front)", data); else if(AttachedPicture::Type::BackCover == attachedPicture->GetType()) tag->setData("Cover Art (Back)", data); } #if 0 else { CGImageSourceRef imageSource = CGImageSourceCreateWithData(attachedPicture->GetData(), nullptr); if(nullptr == imageSource) return false; TagLib::FLAC::Picture picture; picture.setData(TagLib::ByteVector((const char *)CFDataGetBytePtr(attachedPicture->GetData()), (TagLib::uint)CFDataGetLength(attachedPicture->GetData()))); picture.setType(static_cast<TagLib::FLAC::Picture::Type>(attachedPicture->GetType())); if(attachedPicture->GetDescription()) picture.setDescription(TagLib::StringFromCFString(attachedPicture->GetDescription())); // Convert the image's UTI into a MIME type CFStringRef mimeType = UTTypeCopyPreferredTagWithClass(CGImageSourceGetType(imageSource), kUTTagClassMIMEType); if(mimeType) { picture.setMimeType(TagLib::StringFromCFString(mimeType)); CFRelease(mimeType), mimeType = nullptr; } // Flesh out the height, width, and depth CFDictionaryRef imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nullptr); if(imagePropertiesDictionary) { CFNumberRef imageWidth = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyPixelWidth); CFNumberRef imageHeight = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyPixelHeight); CFNumberRef imageDepth = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyDepth); int height, width, depth; // Ignore numeric conversion errors CFNumberGetValue(imageWidth, kCFNumberIntType, &width); CFNumberGetValue(imageHeight, kCFNumberIntType, &height); CFNumberGetValue(imageDepth, kCFNumberIntType, &depth); picture.setHeight(height); picture.setWidth(width); picture.setColorDepth(depth); CFRelease(imagePropertiesDictionary), imagePropertiesDictionary = nullptr; } TagLib::ByteVector encodedBlock = TagLib::EncodeBase64(picture.render()); tag->addValue("METADATA_BLOCK_PICTURE", TagLib::String(encodedBlock, TagLib::String::UTF8), false); CFRelease(imageSource), imageSource = nullptr; } #endif } } return true; }
TagLib::ByteVector CloudStream::readBlock( ulong length ) { const uint start = m_cursor; const uint end = qMin( m_cursor + length - 1, m_length - 1 ); //tDebug( LOGINFO ) << "#### CloudStream : parsing from " << m_url.toString(); //tDebug( LOGINFO ) << "#### CloudStream : parsing from (encoded) " << m_url.toEncoded().constData(); if ( end < start ) { return TagLib::ByteVector(); } if ( CheckCache( start, end ) ) { TagLib::ByteVector cached = GetCached( start, end ); m_cursor += cached.size(); return cached; } if ( m_num_requests_in_error > MAX_ALLOW_ERROR_QUERY ) { //precache(); return TagLib::ByteVector(); } if ( m_refreshUrlEachTime ) { if( !refreshStreamUrl() ) { tDebug( LOGINFO ) << "#### CloudStream : cannot refresh streamUrl for " << m_filename; } } QNetworkRequest request = QNetworkRequest( m_url ); //setings of specials OAuth (1 or 2) headers foreach ( const QString& headerName, m_headers.keys() ) { request.setRawHeader( headerName.toLocal8Bit(), m_headers[headerName].toString().toLocal8Bit() ); } request.setRawHeader( "Range", QString( "bytes=%1-%2" ).arg( start ).arg( end ).toUtf8() ); request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork ); // The Ubuntu One server applies the byte range to the gzipped data, rather // than the raw data so we must disable compression. if ( m_url.host() == "files.one.ubuntu.com" ) { request.setRawHeader( "Accept-Encoding", "identity" ); } tDebug() << "######## CloudStream : HTTP request : "; tDebug() << "#### CloudStream : url : " << request.url(); m_currentBlocklength = length; m_currentStart = start; m_reply = m_network->get( request ); connect( m_reply, SIGNAL( sslErrors( QList<QSslError> ) ), SLOT( SSLErrors( QList<QSslError> ) ) ); connect( m_reply, SIGNAL( finished() ), this, SLOT( onRequestFinished() ) ); ++m_num_requests; return TagLib::ByteVector(); }