char* rtspStream::GetSdpLines() { if (fSDPLines != NULL ) return fSDPLines; char* rtpmapLine = "a=rtpmap: 96 H264/90000"; char const* const sdpFmt = "m=video 0 RTP/AVP 96\r\n" "c=IN IP4 0.0.0.0\r\n" "b=AS:%u\r\n" "%s\r\n" "a=control:%s\r\n"; char * tmp = inet_ntoa(sockaddr.sin_addr); unsigned sdpFmtSize = strlen(sdpFmt) + strlen(tmp) + 3 /* max char len */ + 20 /* max int len */ + strlen(rtpmapLine) + strlen(trackId()); char* sdpLines = new char[sdpFmtSize]; sprintf(sdpLines, sdpFmt, estBitrate, // b=AS:<bandwidth> rtpmapLine, // a=rtpmap:... (if present) trackId()); // a=control:<track-id> fSDPLines = strDup(sdpLines); delete[] sdpLines; return fSDPLines; }
char const* PassiveServerMediaSubsession::sdpLines() { if (fSDPLines == NULL ) { // Construct a set of SDP lines that describe this subsession: // Use the components from "rtpSink": Groupsock const& gs = fRTPSink.groupsockBeingUsed(); AddressString groupAddressStr(gs.groupAddress()); unsigned short portNum = ntohs(gs.port().num()); unsigned char ttl = gs.ttl(); unsigned char rtpPayloadType = fRTPSink.rtpPayloadType(); char const* mediaType = fRTPSink.sdpMediaType(); unsigned estBitrate = fRTCPInstance == NULL ? 50 : fRTCPInstance->totSessionBW(); char* rtpmapLine = fRTPSink.rtpmapLine(); char const* rtcpmuxLine = rtcpIsMuxed() ? "a=rtcp-mux\r\n" : ""; char const* rangeLine = rangeSDPLine(); char const* auxSDPLine = fRTPSink.auxSDPLine(); if (auxSDPLine == NULL) auxSDPLine = ""; char const* const sdpFmt = "m=%s %d RTP/AVP %d\r\n" "c=IN IP4 %s/%d\r\n" "b=AS:%u\r\n" "%s" "%s" "%s" "%s" "a=control:%s\r\n"; unsigned sdpFmtSize = strlen(sdpFmt) + strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */ + strlen(groupAddressStr.val()) + 3 /* max char len */ + 20 /* max int len */ + strlen(rtpmapLine) + strlen(rtcpmuxLine) + strlen(rangeLine) + strlen(auxSDPLine) + strlen(trackId()); char* sdpLines = new char[sdpFmtSize]; sprintf(sdpLines, sdpFmt, mediaType, // m= <media> portNum, // m= <port> rtpPayloadType, // m= <fmt list> groupAddressStr.val(), // c= <connection address> ttl, // c= TTL estBitrate, // b=AS:<bandwidth> rtpmapLine, // a=rtpmap:... (if present) rtcpmuxLine, // a=rtcp-mux:... (if present) rangeLine, // a=range:... (if present) auxSDPLine, // optional extra SDP line trackId()); // a=control:<track-id> delete[] (char*)rangeLine; delete[] rtpmapLine; fSDPLines = strDup(sdpLines); delete[] sdpLines; } return fSDPLines; }
void PluginTransfer::listStreams() { if (ResourcesRequest *r = streamsRequest()) { Logger::log("PluginTransfer::listStreams(). ID: " + trackId(), Logger::MediumVerbosity); r->list(Resources::STREAM, trackId()); } else { setErrorString(tr("No streams plugin found for service '%1'").arg(service())); Logger::log("PluginTransfer::listStreams(). Error: " + errorString()); setStatus(Failed); } }
char const* PassiveServerMediaSubsession::sdpLines() { if (fSDPLines == NULL ) { // Construct a set of SDP lines that describe this subsession: // Use the components from "rtpSink": Groupsock const& gs = fRTPSink.groupsockBeingUsed(); struct in_addr const& ipAddress = gs.groupAddress(); unsigned short portNum = ntohs(gs.port().num()); unsigned char ttl = gs.ttl(); unsigned char rtpPayloadType = fRTPSink.rtpPayloadType(); char const* mediaType = fRTPSink.sdpMediaType(); char* rtpmapLine = fRTPSink.rtpmapLine(); char const* rangeLine = rangeSDPLine(); char const* auxSDPLine = fRTPSink.auxSDPLine(); if (auxSDPLine == NULL) auxSDPLine = ""; char* const ipAddressStr = strDup(our_inet_ntoa(ipAddress)); char const* const sdpFmt = "m=%s %d RTP/AVP %d\r\n" "c=IN IP4 %s/%d\r\n" "%s" "%s" "%s" "a=control:%s\r\n"; unsigned sdpFmtSize = strlen(sdpFmt) + strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */ + strlen(ipAddressStr) + 3 /* max char len */ + strlen(rtpmapLine) + strlen(rangeLine) + strlen(auxSDPLine) + strlen(trackId()); char* sdpLines = new char[sdpFmtSize]; sprintf(sdpLines, sdpFmt, mediaType, // m= <media> portNum, // m= <port> rtpPayloadType, // m= <fmt list> ipAddressStr, // c= <connection address> ttl, // c= TTL rtpmapLine, // a=rtpmap:... (if present) rangeLine, // a=range:... (if present) auxSDPLine, // optional extra SDP line trackId()); // a=control:<track-id> delete[] ipAddressStr; delete[] (char*)rangeLine; delete[] rtpmapLine; fSDPLines = strDup(sdpLines); delete[] sdpLines; } return fSDPLines; }
void OnDemandServerMediaSubsession ::setSDPLinesFromRTPSink(RTPSink* rtpSink, FramedSource* inputSource, unsigned estBitrate) { if (rtpSink == NULL) return; liveLogInfo(" OnDemandServerMediaSubsession::setSDPLinesFromRTPSink \n"); char const* mediaType = rtpSink->sdpMediaType(); unsigned char rtpPayloadType = rtpSink->rtpPayloadType(); AddressString ipAddressStr(fServerAddressForSDP); char* rtpmapLine = rtpSink->rtpmapLine(); char const* rtcpmuxLine = fMultiplexRTCPWithRTP ? "a=rtcp-mux\r\n" : ""; char const* rangeLine = rangeSDPLine(); char const* auxSDPLine = getAuxSDPLine(rtpSink, inputSource); if (auxSDPLine == NULL) auxSDPLine = ""; char const* const sdpFmt = "m=%s %u RTP/AVP %d\r\n" "c=IN IP4 %s\r\n" "b=AS:%u\r\n" "%s" "%s" "%s" "%s" "a=control:%s\r\n"; unsigned sdpFmtSize = strlen(sdpFmt) + strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */ + strlen(ipAddressStr.val()) + 20 /* max int len */ + strlen(rtpmapLine) + strlen(rtcpmuxLine) + strlen(rangeLine) + strlen(auxSDPLine) + strlen(trackId()); char* sdpLines = new char[sdpFmtSize]; sprintf(sdpLines, sdpFmt, mediaType, // m= <media> fPortNumForSDP, // m= <port> rtpPayloadType, // m= <fmt list> ipAddressStr.val(), // c= address estBitrate, // b=AS:<bandwidth> rtpmapLine, // a=rtpmap:... (if present) rtcpmuxLine, // a=rtcp-mux:... (if present) rangeLine, // a=range:... (if present) auxSDPLine, // optional extra SDP line trackId()); // a=control:<track-id> delete[] (char*)rangeLine; delete[] rtpmapLine; fSDPLines = strDup(sdpLines); delete[] sdpLines; }
void AutoDJFeature::slotAddRandomTrack(bool) { int failedRetrieveAttempts = 0; // Get access to the auto-DJ playlist PlaylistDAO& playlistDao = m_pTrackCollection->getPlaylistDAO(); if (m_iAutoDJPlaylistId >= 0) { while (failedRetrieveAttempts < kMaxRetrieveAttempts) { // Get the ID of a randomly-selected track. TrackId trackId(m_autoDjCratesDao.getRandomTrackId()); if (trackId.isValid()) { // Get Track Information TrackPointer addedTrack = (m_pTrackCollection->getTrackDAO()).getTrack(trackId); if(addedTrack->exists()) { playlistDao.appendTrackToPlaylist(trackId, m_iAutoDJPlaylistId); m_pAutoDJView->onShow(); return; } else { qDebug() << "Track does not exist:" << addedTrack->getInfo() << addedTrack->getLocation(); } } failedRetrieveAttempts += 1; } // If we couldn't get a track from the crates , get one from the library qDebug () << "Could not load tracks from crates, attempting to load from library."; failedRetrieveAttempts = 0; while ( failedRetrieveAttempts < kMaxRetrieveAttempts ) { TrackId trackId(m_autoDjCratesDao.getRandomTrackIdFromLibrary(m_iAutoDJPlaylistId)); if (trackId.isValid()) { TrackPointer addedTrack = m_pTrackCollection->getTrackDAO().getTrack(trackId); if(addedTrack->exists()) { if(!addedTrack->getPlayCounter().isPlayed()) { playlistDao.appendTrackToPlaylist(trackId, m_iAutoDJPlaylistId); m_pAutoDJView->onShow(); return; } } else { qDebug() << "Track does not exist:" << addedTrack->getInfo() << addedTrack->getLocation(); } } failedRetrieveAttempts += 1; } } // If control reaches here it implies that we couldn't load track qDebug() << "Could not load random track."; }
void CrateFeature::slotTrackSelected(TrackPointer pTrack) { m_pSelectedTrack = pTrack; TrackId trackId(pTrack.isNull() ? TrackId() : pTrack->getId()); m_crateDao.getCratesTrackIsIn(trackId, &m_cratesSelectedTrackIsIn); TreeItem* rootItem = m_childModel.getItem(QModelIndex()); if (rootItem == nullptr) { return; } // Set all crates the track is in bold (or if there is no track selected, // clear all the bolding). int row = 0; for (QList<QPair<int, QString> >::const_iterator it = m_crateList.begin(); it != m_crateList.end(); ++it, ++row) { TreeItem* crate = rootItem->child(row); if (crate == nullptr) { continue; } int crateId = it->first; bool shouldBold = m_cratesSelectedTrackIsIn.contains(crateId); crate->setBold(shouldBold); } m_childModel.triggerRepaint(); }
bool CrateTableModel::addTrack(const QModelIndex& index, QString location) { Q_UNUSED(index); // If a track is dropped but it isn't in the library, then add it because // the user probably dropped a file from outside Mixxx into this playlist. QFileInfo fileInfo(location); if (!fileInfo.exists()) { return false; } TrackDAO& trackDao = m_pTrackCollection->getTrackDAO(); // Adds track, does not insert duplicates, handles unremoving logic. TrackId trackId(trackDao.addTrack(fileInfo, true)); bool success = false; if (trackId.isValid()) { success = m_pTrackCollection->getCrateDAO().addTrackToCrate(trackId, m_iCrateId); } if (success) { // TODO(rryan) just add the track dont select select(); return true; } else { qDebug() << "CrateTableModel::addTrack could not add track" << fileInfo.absoluteFilePath() << "to crate" << m_iCrateId; return false; } }
void OnDemandServerMediaSubsession ::setSDPLinesFromRTPSink(RTPSink* rtpSink, FramedSource* inputSource, unsigned estBitrate) { if (rtpSink == NULL) return; char const* mediaType = rtpSink->sdpMediaType(); unsigned char rtpPayloadType = rtpSink->rtpPayloadType(); struct in_addr serverAddrForSDP; serverAddrForSDP.s_addr = fServerAddressForSDP; char* const ipAddressStr = strDup(our_inet_ntoa(serverAddrForSDP)); char* rtpmapLine = rtpSink->rtpmapLine(); char const* rangeLine = rangeSDPLine(); char const* auxSDPLine = getAuxSDPLine(rtpSink, inputSource); if (auxSDPLine == NULL) auxSDPLine = ""; char const* const sdpFmt = "m=%s %u RTP/AVP %d\r\n" "c=IN IP4 %s\r\n" "b=AS:%u\r\n" "%s" "%s" "%s" "a=control:%s\r\n"; unsigned sdpFmtSize = strlen(sdpFmt) + strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */ + strlen(ipAddressStr) + 20 /* max int len */ + strlen(rtpmapLine) + strlen(rangeLine) + strlen(auxSDPLine) + strlen(trackId()); char* sdpLines = new char[sdpFmtSize]; sprintf(sdpLines, sdpFmt, mediaType, // m= <media> fPortNumForSDP, // m= <port> rtpPayloadType, // m= <fmt list> ipAddressStr, // c= address estBitrate, // b=AS:<bandwidth> rtpmapLine, // a=rtpmap:... (if present) rangeLine, // a=range:... (if present) auxSDPLine, // optional extra SDP line trackId()); // a=control:<track-id> delete[] (char*)rangeLine; delete[] rtpmapLine; delete[] ipAddressStr; fSDPLines = strDup(sdpLines); DEBUG_LOG(INF, "fSDPLines: %s", fSDPLines); delete[] sdpLines; }
char const* H264MediaSubsession::sdpLines() { if (fSDPLines == NULL) { AddressString ipAddressStr(fServerAddressForSDP); const char* rtpmapLine = "a=rtpmap:96 H264/90000\r\n"; char const* rangeLine = rangeSDPLine(); char const* const sdpFmt = "m=%s %u RTP/AVP %d\r\n" "c=IN IP4 %s\r\n" "b=AS:%u\r\n" "%s" "%s" "%s" "a=control:%s\r\n"; unsigned sdpFmtSize = strlen(sdpFmt) + strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */ + strlen(ipAddressStr.val()) + 20 /* max int len */ + strlen(rtpmapLine) + strlen(rangeLine) + strlen(auxSDPLine()) + strlen(trackId()); char* sdpLines = new char[sdpFmtSize]; sprintf(sdpLines, sdpFmt, mediaType, // m= <media> fPortNumForSDP, // m= <port> rtpPayloadType, // m= <fmt list> ipAddressStr.val(), // c= address m_Watcher->GetVideoBitrate(), // b=AS:<bandwidth> rtpmapLine, // a=rtpmap:... (if present) rangeLine, // a=range:... (if present) auxSDPLine(), // optional extra SDP line trackId()); // a=control:<track-id> delete[] (char*)rangeLine; fSDPLines = strDup(sdpLines); delete[] sdpLines; } return fSDPLines; }
bool BaseSqlTableModel::setData( const QModelIndex& index, const QVariant& value, int role) { if (!index.isValid()) return false; int row = index.row(); int column = index.column(); if (sDebug) { qDebug() << this << "setData() column:" << column << "value:" << value << "role:" << role; } // Over-ride sets to TIMESPLAYED and re-direct them to PLAYED if (role == Qt::CheckStateRole) { QString val = value.toInt() > 0 ? QString("true") : QString("false"); if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_TIMESPLAYED)) { QModelIndex playedIndex = index.sibling(index.row(), fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_PLAYED)); return setData(playedIndex, val, Qt::EditRole); } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_BPM)) { QModelIndex bpmLockindex = index.sibling(index.row(), fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_BPM_LOCK)); return setData(bpmLockindex, val, Qt::EditRole); } return false; } if (row < 0 || row >= m_rowInfo.size()) { return false; } const RowInfo& rowInfo = m_rowInfo[row]; TrackId trackId(rowInfo.trackId); // You can't set something in the table columns because we have no way of // persisting it. if (column < m_tableColumns.size()) { return false; } // TODO(rryan) ugly and only works because the mixxx library tables are the // only ones that aren't read-only. This should be moved into BTC. TrackPointer pTrack = m_trackDAO.getTrack(trackId); if (!pTrack) { return false; } setTrackValueForColumn(pTrack, column, value); // Do not save the track here. Changing the track dirties it and the caching // system will automatically save the track once it is unloaded from // memory. rryan 10/2010 //m_trackDAO.saveTrack(pTrack); return true; }
void BasicRTSPOnlySubsession ::setSDPLines() { //TODO: should be more dynamic unsigned estBitrate = 5000; char const* mediaType = "video"; uint8_t rtpPayloadType = 96; AddressString ipAddressStr(fServerAddressForSDP); char* rtpmapLine = strdup("a=rtpmap:96 H264/90000\n"); char const* auxSDPLine = ""; char const* const sdpFmt = "m=%s %u RTP/AVP %u\r\n" "c=IN IP4 %s\r\n" "b=AS:%u\r\n" "%s" "a=control:%s\r\n"; unsigned sdpFmtSize = strlen(sdpFmt) + strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */ + strlen(ipAddressStr.val()) + 20 /* max int len */ + strlen(rtpmapLine) + strlen(trackId()); char* sdpLines = new char[sdpFmtSize]; sprintf(sdpLines, sdpFmt, mediaType, // m= <media> fPortNumForSDP, // m= <port> rtpPayloadType, // m= <fmt list> ipAddressStr.val(), // c= address estBitrate, // b=AS:<bandwidth> rtpmapLine, // a=rtpmap:... (if present) trackId()); // a=control:<track-id> fSDPLines = sdpLines; }
char* rtspStream::generateSDPDescription() { int ipAddressStrSize = strlen(inet_ntoa(sockaddr.sin_addr)); char* sdp = NULL; // for now unsigned sdpLength = 0; char* sdpLines = GetSdpLines(); if (sdpLines == NULL) return NULL; sdpLength += strlen(sdpLines); char const* const sdpPrefixFmt = "v=0\r\n" "o=- %ld%06ld %d IN IP4 %s\r\n" "s=Media Presentation\r\n" "i=%s\r\n" "t=0 0\r\n" "a=type:broadcast\r\n" "a=control:*\r\n" "%s"; sdpLength += strlen(sdpPrefixFmt) + 20 + 6 + 20 + ipAddressStrSize + strlen(sdpLines); sdpLength += 1000; // in case the length of the "subsession->sdpLines()" calls below change sdp = new char[sdpLength]; if (sdp == NULL) return NULL; // Generate the SDP prefix (session-level lines): snprintf(sdp, sdpLength, sdpPrefixFmt, 0, 0, // o= <session id> 1, // o= <version> // (needs to change if params are modified) inet_ntoa(sockaddr.sin_addr), // o= <address> trackId(), sdpLines); // miscellaneous session SDP lines (if any) return sdp; }
bool BaseTrackCache::updateIndexWithQuery(const QString& queryString) { PerformanceTimer timer; timer.start(); if (sDebug) { qDebug() << "updateIndexWithQuery issuing query:" << queryString; } QSqlQuery query(m_database); // This causes a memory savings since QSqlCachedResult (what QtSQLite uses) // won't allocate a giant in-memory table that we won't use at all. query.setForwardOnly(true); // performance improvement? query.prepare(queryString); if (!query.exec()) { LOG_FAILED_QUERY(query); return false; } int numColumns = columnCount(); int idColumn = query.record().indexOf(m_idColumn); while (query.next()) { TrackId trackId(query.value(idColumn)); //m_trackInfo[id] will insert a QVector<QVariant> into the //m_trackInfo HashTable with the key "id" QVector<QVariant>& record = m_trackInfo[trackId]; record.resize(numColumns); for (int i = 0; i < numColumns; ++i) { record[i] = query.value(i); } } qDebug() << this << "updateIndexWithQuery took" << timer.elapsed().debugMillisWithUnit(); return true; }
bool BaseTrackCache::updateIndexWithTrackpointer(TrackPointer pTrack) { if (sDebug) { qDebug() << "updateIndexWithTrackpointer:" << pTrack->getLocation(); } if (!pTrack) { return false; } int numColumns = columnCount(); TrackId trackId(pTrack->getId()); if (trackId.isValid()) { // m_trackInfo[id] will insert a QVector<QVariant> into the // m_trackInfo HashTable with the key "id" QVector<QVariant>& record = m_trackInfo[trackId]; // prealocate memory for all columns at once record.resize(numColumns); for (int i = 0; i < numColumns; ++i) { getTrackValueForColumn(pTrack, i, record[i]); } } return true; }
void Transfer::setTrackId(const QString &i) { if (i != trackId()) { m_trackId = i; emit trackIdChanged(); } }
void BaseSqlTableModel::select() { if (!m_bInitialized) { return; } // We should be able to detect when a select() would be a no-op. The DAO's // do not currently broadcast signals for when common things happen. In the // future, we can turn this check on and avoid a lot of needless // select()'s. rryan 9/2011 // if (!m_bDirty) { // if (sDebug) { // qDebug() << this << "Skipping non-dirty select()"; // } // return; // } if (sDebug) { qDebug() << this << "select()"; } PerformanceTimer time; time.start(); // Prepare query for id and all columns not in m_trackSource QString queryString = QString("SELECT %1 FROM %2 %3") .arg(m_tableColumnsJoined, m_tableName, m_tableOrderBy); if (sDebug) { qDebug() << this << "select() executing:" << queryString; } QSqlQuery query(m_database); // This causes a memory savings since QSqlCachedResult (what QtSQLite uses) // won't allocate a giant in-memory table that we won't use at all. query.setForwardOnly(true); query.prepare(queryString); if (!query.exec()) { LOG_FAILED_QUERY(query); return; } // Remove all the rows from the table. We wait to do this until after the // table query has succeeded. See Bug #1090888. // TODO(rryan) we could edit the table in place instead of clearing it? if (!m_rowInfo.isEmpty()) { beginRemoveRows(QModelIndex(), 0, m_rowInfo.size() - 1); m_rowInfo.clear(); m_trackIdToRows.clear(); endRemoveRows(); } // sqlite does not set size and m_rowInfo was just cleared //if (sDebug) { // qDebug() << "Rows returned" << rows << m_rowInfo.size(); //} QVector<RowInfo> rowInfo; QSet<TrackId> trackIds; while (query.next()) { TrackId trackId(query.value(kIdColumn)); trackIds.insert(trackId); RowInfo thisRowInfo; thisRowInfo.trackId = trackId; // save rows where this currently track id is located thisRowInfo.order = rowInfo.size(); // Get all the table columns and store them in the hash for this // row-info section. thisRowInfo.metadata.reserve(m_tableColumns.size()); for (int i = 0; i < m_tableColumns.size(); ++i) { thisRowInfo.metadata << query.value(i); } rowInfo.push_back(thisRowInfo); } if (sDebug) { qDebug() << "Rows actually received:" << rowInfo.size(); } if (m_trackSource) { m_trackSource->filterAndSort(trackIds, m_currentSearch, m_currentSearchFilter, m_trackSourceOrderBy, m_trackSourceSortColumn, m_trackSourceSortOrder, &m_trackSortOrder); // Re-sort the track IDs since filterAndSort can change their order or mark // them for removal (by setting their row to -1). for (QVector<RowInfo>::iterator it = rowInfo.begin(); it != rowInfo.end(); ++it) { // If the sort is not a track column then we will sort only to // separate removed tracks (order == -1) from present tracks (order == // 0). Otherwise we sort by the order that filterAndSort returned to us. if (m_trackSourceOrderBy.isEmpty()) { it->order = m_trackSortOrder.contains(it->trackId) ? 0 : -1; } else { it->order = m_trackSortOrder.value(it->trackId, -1); } } } // RowInfo::operator< sorts by the order field, except -1 is placed at the // end so we can easily slice off rows that are no longer present. Stable // sort is necessary because the tracks may be in pre-sorted order so we // should not disturb that if we are only removing tracks. qStableSort(rowInfo.begin(), rowInfo.end()); m_trackIdToRows.clear(); for (int i = 0; i < rowInfo.size(); ++i) { const RowInfo& row = rowInfo[i]; if (row.order == -1) { // We've reached the end of valid rows. Resize rowInfo to cut off // this and all further elements. rowInfo.resize(i); break; } QLinkedList<int>& rows = m_trackIdToRows[row.trackId]; rows.push_back(i); } // We're done! Issue the update signals and replace the master maps. if (!rowInfo.isEmpty()) { beginInsertRows(QModelIndex(), 0, rowInfo.size() - 1); m_rowInfo = rowInfo; endInsertRows(); } qDebug() << this << "select() took" << time.elapsed().formatMillisWithUnit() << rowInfo.size(); }
void BaseTrackCache::filterAndSort(const QSet<TrackId>& trackIds, const QString& searchQuery, const QString& extraFilter, const QString& orderByClause, const QList<SortColumn>& sortColumns, const int columnOffset, QHash<TrackId, int>* trackToIndex) { // Skip processing if there are no tracks to filter or sort. if (trackIds.size() == 0) { return; } if (!m_bIndexBuilt) { buildIndex(); } QStringList idStrings; // TODO(rryan) consider making this the data passed in and a separate // QVector for output QSet<TrackId> dirtyTracks; for (const auto& trackId: trackIds) { idStrings << trackId.toString(); if (m_dirtyTracks.contains(trackId)) { dirtyTracks.insert(trackId); } } std::unique_ptr<QueryNode> pQuery(parseQuery( searchQuery, extraFilter, idStrings)); QString filter = pQuery->toSql(); if (!filter.isEmpty()) { filter.prepend("WHERE "); } QString queryString = QString("SELECT %1 FROM %2 %3 %4") .arg(m_idColumn, m_tableName, filter, orderByClause); if (sDebug) { qDebug() << this << "select() executing:" << queryString; } QSqlQuery query(m_database); // This causes a memory savings since QSqlCachedResult (what QtSQLite uses) // won't allocate a giant in-memory table that we won't use at all. query.setForwardOnly(true); query.prepare(queryString); if (!query.exec()) { LOG_FAILED_QUERY(query); } int idColumn = query.record().indexOf(m_idColumn); int rows = query.size(); if (sDebug) { qDebug() << "Rows returned:" << rows; } m_trackOrder.resize(0); // keeps alocated memory trackToIndex->clear(); if (rows > 0) { trackToIndex->reserve(rows); m_trackOrder.reserve(rows); } while (query.next()) { TrackId trackId(query.value(idColumn)); (*trackToIndex)[trackId] = m_trackOrder.size(); m_trackOrder.append(trackId); } // At this point, the original set of tracks have been divided into two // pieces: those that should be in the result set and those that should // not. Unfortunately, due to TrackDAO caching, there may be tracks in // either category that are there incorrectly. We must look at all the dirty // tracks (within the original set, if specified) and evaluate whether they // would match or not match the given filter criteria. Once we correct the // membership of tracks in either set, we must then insertion-sort the // missing tracks into the resulting index list. if (dirtyTracks.size() == 0) { return; } for (TrackId trackId: dirtyTracks) { // Only get the track if it is in the cache. TrackPointer pTrack = lookupCachedTrack(trackId); if (!pTrack) { continue; } // The track should be in the result set if the search is empty or the // track matches the search. bool shouldBeInResultSet = searchQuery.isEmpty() || pQuery->match(pTrack); // If the track is in this result set. bool isInResultSet = trackToIndex->contains(trackId); if (shouldBeInResultSet) { // Track should be in result set... // Remove the track from the results first (we have to do this or it // will sort wrong). if (isInResultSet) { int index = (*trackToIndex)[trackId]; m_trackOrder.remove(index); // Don't update trackToIndex, since we do it below. } // Figure out where it is supposed to sort. The table is sorted by // the sort column, so we can binary search. int insertRow = findSortInsertionPoint( pTrack, sortColumns, columnOffset, m_trackOrder); if (sDebug) { qDebug() << this << "Insertion sort says it should be inserted at:" << insertRow; } // The track should sort at insertRow m_trackOrder.insert(insertRow, trackId); trackToIndex->clear(); // Fix the index. TODO(rryan) find a non-stupid way to do this. for (int i = 0; i < m_trackOrder.size(); ++i) { (*trackToIndex)[m_trackOrder[i]] = i; } } else if (isInResultSet) { // Track should not be in this result set, but it is. We need to // remove it. int index = (*trackToIndex)[trackId]; m_trackOrder.remove(index); trackToIndex->clear(); // Fix the index. TODO(rryan) find a non-stupid way to do this. for (int i = 0; i < m_trackOrder.size(); ++i) { (*trackToIndex)[m_trackOrder[i]] = i; } } } }