annotate data/fileio/FileSource.h @ 1310:aa1b1fc2d018 mp3-gapless

Stop reporting sync errors only when we really are at eof, i.e. after the input callback has been called again (previously we just tested whether we'd buffered up all the input, which of course we do in one go at the start)
author Chris Cannam
date Tue, 29 Nov 2016 16:45:29 +0000
parents 329ddaf7415d
children ad5f892c0c4d
rev   line source
Chris@208 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@208 2
Chris@208 3 /*
Chris@208 4 Sonic Visualiser
Chris@208 5 An audio file viewer and annotation editor.
Chris@208 6 Centre for Digital Music, Queen Mary, University of London.
Chris@208 7 This file copyright 2007 QMUL.
Chris@208 8
Chris@208 9 This program is free software; you can redistribute it and/or
Chris@208 10 modify it under the terms of the GNU General Public License as
Chris@208 11 published by the Free Software Foundation; either version 2 of the
Chris@208 12 License, or (at your option) any later version. See the file
Chris@208 13 COPYING included with this distribution for more information.
Chris@208 14 */
Chris@208 15
Chris@465 16 #ifndef _FILE_SOURCE_H_
Chris@465 17 #define _FILE_SOURCE_H_
Chris@208 18
Chris@208 19 #include <QUrl>
Chris@208 20 #include <QMutex>
Chris@208 21 #include <QString>
Chris@210 22 #include <QTimer>
Chris@762 23 #include <QNetworkReply>
Chris@208 24
Chris@304 25 #include <map>
Chris@304 26
Chris@686 27 #include "base/Debug.h"
Chris@686 28
Chris@208 29 class QFile;
Chris@392 30 class ProgressReporter;
Chris@208 31
Chris@325 32 /**
Chris@325 33 * FileSource is a class used to refer to the contents of a file that
Chris@325 34 * may be either local or at a remote location such as a HTTP URL.
Chris@325 35 *
Chris@325 36 * When a FileSource object is constructed, the file or URL passed to
Chris@325 37 * its constructor is tested for validity, and if it refers to a
Chris@325 38 * remote HTTP or FTP location it is retrieved asynchronously (the Qt
Chris@325 39 * event loop must be running) and cached locally. You can then query
Chris@325 40 * a local path for the file and refer to that as a normal filename.
Chris@325 41 *
Chris@325 42 * Use isAvailable() to test whether the file or remote URL exists,
Chris@325 43 * and isOK() to test for internal validity or transmission errors.
Chris@325 44 * Call waitForStatus() to block until the availability and validity
Chris@325 45 * of the URL have been established so that isAvailable may be called,
Chris@325 46 * and waitForData() to block until the entire file has been cached.
Chris@325 47 *
Chris@325 48 * FileSource handles reference counting for cache files. You can
Chris@325 49 * construct many FileSource objects with the same URL and you will
Chris@325 50 * receive the same cached file for each; it is also reasonable to
Chris@325 51 * pass FileSource objects by value. FileSource only makes sense for
Chris@325 52 * stateless URLs that result in the same data on each request.
Chris@325 53 *
Chris@469 54 * Cached files share a lifetime with their "owning" FileSource
Chris@469 55 * objects; when the last FileSource referring to a given URL is
Chris@469 56 * deleted or goes out of scope, its cached file (if any) is also
Chris@469 57 * removed.
Chris@325 58 */
Chris@317 59 class FileSource : public QObject
Chris@208 60 {
Chris@208 61 Q_OBJECT
Chris@208 62
Chris@208 63 public:
Chris@325 64 /**
Chris@325 65 * Construct a FileSource using the given local file path or URL.
Chris@357 66 * The URL may be raw or encoded.
Chris@357 67 *
Chris@392 68 * If a ProgressReporter is provided, it will be updated with
Chris@392 69 * progress status. Note that the progress() signal will also be
Chris@392 70 * emitted regularly during retrieval, even if no reporter is
Chris@392 71 * supplied here. Caller retains ownership of the reporter object.
Chris@325 72 */
Chris@520 73 FileSource(QString fileOrUrl,
Chris@520 74 ProgressReporter *reporter = 0,
Chris@520 75 QString preferredContentType = "");
Chris@325 76
Chris@325 77 /**
Chris@357 78 * Construct a FileSource using the given remote URL.
Chris@357 79 *
Chris@392 80 * If a ProgressReporter is provided, it will be updated with
Chris@392 81 * progress status. Note that the progress() signal will also be
Chris@392 82 * emitted regularly during retrieval, even if no reporter is
Chris@392 83 * supplied here. Caller retains ownership of the reporter object.
Chris@325 84 */
Chris@469 85 FileSource(QUrl url, ProgressReporter *reporter = 0);
Chris@325 86
Chris@317 87 FileSource(const FileSource &);
Chris@316 88
Chris@317 89 virtual ~FileSource();
Chris@208 90
Chris@325 91 /**
Chris@325 92 * Block on a sub-event-loop until the availability of the file or
Chris@325 93 * remote URL is known.
Chris@325 94 */
Chris@325 95 void waitForStatus();
Chris@325 96
Chris@325 97 /**
Chris@325 98 * Block on a sub-event-loop until the whole of the data has been
Chris@325 99 * retrieved (if it is remote).
Chris@325 100 */
Chris@325 101 void waitForData();
Chris@325 102
Chris@325 103 /**
Chris@981 104 * Return true if the FileSource object is valid and neither error
Chris@981 105 * nor cancellation occurred while retrieving the file or remote
Chris@981 106 * URL. Non-existence of the file or URL is not an error -- call
Chris@981 107 * isAvailable() to test for that.
Chris@325 108 */
Chris@325 109 bool isOK() const;
Chris@325 110
Chris@325 111 /**
Chris@325 112 * Return true if the file or remote URL exists. This may block
Chris@325 113 * on a sub-event-loop (calling waitForStatus) if the status is
Chris@325 114 * not yet available.
Chris@325 115 */
Chris@210 116 bool isAvailable();
Chris@210 117
Chris@325 118 /**
Chris@325 119 * Return true if the entire file has been retrieved and is
Chris@325 120 * available.
Chris@325 121 */
Chris@325 122 bool isDone() const;
Chris@316 123
Chris@325 124 /**
Chris@981 125 * Return true if the operation was cancelled by the user through
Chris@981 126 * the ProgressReporter interface. Note that the cancelled()
Chris@981 127 * signal will have been emitted, and isOK() will also return
Chris@981 128 * false in this case.
Chris@981 129 */
Chris@981 130 bool wasCancelled() const;
Chris@981 131
Chris@981 132 /**
Chris@706 133 * Return true if this FileSource is referring to a QRC resource.
Chris@706 134 */
Chris@706 135 bool isResource() const;
Chris@706 136
Chris@706 137 /**
Chris@325 138 * Return true if this FileSource is referring to a remote URL.
Chris@325 139 */
Chris@325 140 bool isRemote() const;
Chris@325 141
Chris@325 142 /**
Chris@325 143 * Return the location filename or URL as passed to the
Chris@325 144 * constructor.
Chris@325 145 */
Chris@325 146 QString getLocation() const;
Chris@325 147
Chris@325 148 /**
Chris@325 149 * Return the name of the local file this FileSource refers to.
Chris@325 150 * This is the filename that should be used when reading normally
Chris@325 151 * from the FileSource. If the filename passed to the constructor
Chris@325 152 * was a local file, this will return the same filename; otherwise
Chris@325 153 * this will be the name of the temporary cache file owned by the
Chris@325 154 * FileSource.
Chris@325 155 */
Chris@325 156 QString getLocalFilename() const;
Chris@325 157
Chris@325 158 /**
Chris@678 159 * Return the base name, i.e. the final path element (including
Chris@678 160 * extension, if any) of the location.
Chris@678 161 */
Chris@678 162 QString getBasename() const;
Chris@678 163
Chris@678 164 /**
Chris@325 165 * Return the MIME content type of this file, if known.
Chris@325 166 */
Chris@325 167 QString getContentType() const;
Chris@325 168
Chris@325 169 /**
Chris@1098 170 * Return the file extension for this file, if any. The returned
Chris@1098 171 * extension is always lower-case.
Chris@325 172 */
Chris@325 173 QString getExtension() const;
Chris@325 174
Chris@325 175 /**
Chris@325 176 * Return an error message, if isOK() is false.
Chris@325 177 */
Chris@325 178 QString getErrorString() const;
Chris@325 179
Chris@325 180 /**
Chris@325 181 * Specify whether any local, cached file should remain on disc
Chris@325 182 * after this FileSource has been destroyed. The default is false
Chris@469 183 * (cached files share their FileSource owners' lifespans).
Chris@325 184 */
Chris@316 185 void setLeaveLocalFile(bool leave);
Chris@208 186
Chris@325 187 /**
Chris@325 188 * Return true if the given filename or URL refers to a remote
Chris@325 189 * URL.
Chris@325 190 */
Chris@325 191 static bool isRemote(QString fileOrUrl);
Chris@210 192
Chris@325 193 /**
Chris@325 194 * Return true if FileSource can handle the retrieval scheme for
Chris@325 195 * the given URL (or if the URL is for a local file).
Chris@325 196 */
Chris@208 197 static bool canHandleScheme(QUrl url);
Chris@208 198
Chris@1033 199 /**
Chris@1033 200 * Print some stats, if FileSource was compiled with debugging.
Chris@1033 201 * It's safe to leave a call to this function in release code, as
Chris@1033 202 * long as FileSource itself is compiled with release flags.
Chris@1033 203 */
Chris@1033 204 static void debugReport();
Chris@1033 205
Chris@208 206 signals:
Chris@325 207 /**
Chris@325 208 * Emitted during URL retrieval, when the retrieval progress
Chris@325 209 * notches up to a new percentage.
Chris@325 210 */
Chris@208 211 void progress(int percent);
Chris@325 212
Chris@325 213 /**
Chris@325 214 * Emitted when the file's existence has been tested and/or
Chris@325 215 * response header received. Calls to isAvailable() after this
Chris@325 216 * has been emitted will not block.
Chris@325 217 */
Chris@325 218 void statusAvailable();
Chris@325 219
Chris@325 220 /**
Chris@325 221 * Emitted when the entire file data has been retrieved and the
Chris@325 222 * local file is complete (if no error has occurred).
Chris@325 223 */
Chris@208 224 void ready();
Chris@208 225
Chris@208 226 protected slots:
Chris@764 227 void metaDataChanged();
Chris@762 228 void readyRead();
Chris@762 229 void replyFailed(QNetworkReply::NetworkError);
Chris@762 230 void replyFinished();
Chris@762 231 void downloadProgress(qint64 done, qint64 total);
Chris@210 232 void cancelled();
Chris@208 233
Chris@208 234 protected:
Chris@317 235 FileSource &operator=(const FileSource &); // not provided
Chris@316 236
chris@831 237 QString m_rawFileOrUrl;
Chris@304 238 QUrl m_url;
Chris@208 239 QFile *m_localFile;
Chris@762 240 QNetworkReply *m_reply;
Chris@208 241 QString m_localFilename;
Chris@208 242 QString m_errorString;
Chris@315 243 QString m_contentType;
Chris@520 244 QString m_preferredContentType;
Chris@208 245 bool m_ok;
Chris@981 246 bool m_cancelled;
Chris@210 247 int m_lastStatus;
Chris@706 248 bool m_resource;
Chris@316 249 bool m_remote;
Chris@208 250 bool m_done;
Chris@316 251 bool m_leaveLocalFile;
Chris@392 252 ProgressReporter *m_reporter;
Chris@208 253
Chris@304 254 typedef std::map<QUrl, int> RemoteRefCountMap;
Chris@304 255 typedef std::map<QUrl, QString> RemoteLocalMap;
Chris@304 256 static RemoteRefCountMap m_refCountMap;
Chris@304 257 static RemoteLocalMap m_remoteLocalMap;
Chris@304 258 static QMutex m_mapMutex;
Chris@316 259 bool m_refCounted;
Chris@316 260
Chris@357 261 void init();
Chris@762 262 void initRemote();
Chris@304 263
Chris@211 264 void cleanup();
Chris@211 265
Chris@316 266 // Create a local file for m_url. If it already existed, return true.
Chris@316 267 // The local filename is stored in m_localFilename.
Chris@316 268 bool createCacheFile();
Chris@316 269 void deleteCacheFile();
Chris@208 270
Chris@208 271 static QMutex m_fileCreationMutex;
Chris@208 272 static int m_count;
Chris@208 273 };
Chris@208 274
Chris@208 275 #endif