1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include <QtCore/qmetaobject.h> |
41 | #include <QtCore/qdebug.h> |
42 | |
43 | #include "qmediaobject_p.h" |
44 | |
45 | #include <qmediaservice.h> |
46 | #include <qmetadatareadercontrol.h> |
47 | #include <qmediabindableinterface.h> |
48 | #include <qmediaavailabilitycontrol.h> |
49 | |
50 | QT_BEGIN_NAMESPACE |
51 | |
52 | void QMediaObjectPrivate::_q_notify() |
53 | { |
54 | Q_Q(QMediaObject); |
55 | |
56 | const QMetaObject* m = q->metaObject(); |
57 | |
58 | // QTBUG-57045 |
59 | // we create a copy of notifyProperties container to ensure that if a property is removed |
60 | // from the original container as a result of invoking propertyChanged signal, the iterator |
61 | // won't become invalidated |
62 | QSet<int> properties = notifyProperties; |
63 | |
64 | for (int pi : qAsConst(properties)) { |
65 | QMetaProperty p = m->property(pi); |
66 | p.notifySignal().invoke( |
67 | q, QGenericArgument(QMetaType::typeName(p.userType()), p.read(q).data())); |
68 | } |
69 | } |
70 | |
71 | void QMediaObjectPrivate::_q_availabilityChanged() |
72 | { |
73 | Q_Q(QMediaObject); |
74 | |
75 | // Really this should not always emit, but |
76 | // we can't really tell from here (isAvailable |
77 | // may not have changed, or the mediaobject's overridden |
78 | // availability() may not have changed). |
79 | q->availabilityChanged(q->availability()); |
80 | q->availabilityChanged(q->isAvailable()); |
81 | } |
82 | |
83 | /*! |
84 | \class QMediaObject |
85 | |
86 | \brief The QMediaObject class provides a common base for multimedia objects. |
87 | \inmodule QtMultimedia |
88 | |
89 | \ingroup multimedia |
90 | \ingroup multimedia_core |
91 | |
92 | It provides some basic functionality that is common to other high level classes |
93 | like \l QMediaPlayer, \l QAudioDecoder and \l QCamera, including availability |
94 | and meta-data functionality, as well as functionality to connect media objects |
95 | with support classes like QMediaPlaylist. |
96 | |
97 | The higher level QMediaObject derived classes provide the actual multimedia |
98 | functionality, by internally using a QMediaService. Each media object |
99 | hosts a QMediaService and uses the QMediaControl interfaces implemented by the service to implement its |
100 | API. These controls can be accessed from the media object if necessary, but in general |
101 | the useful functionality can be accessed from the higher level classes. |
102 | |
103 | Most media objects when constructed will request a new |
104 | QMediaService instance, but some like |
105 | QMediaRecorder and QAudioRecorder will share a service with another object. |
106 | |
107 | \sa QMediaService, QMediaControl |
108 | */ |
109 | |
110 | /*! |
111 | Destroys this media object. |
112 | */ |
113 | |
114 | QMediaObject::~QMediaObject() |
115 | { |
116 | delete d_ptr; |
117 | } |
118 | |
119 | /*! |
120 | Returns the availability of the functionality offered by this object. |
121 | |
122 | In some cases the functionality may not be available (for example, if |
123 | the current operating system or platform does not provide the required |
124 | functionality), or it may be temporarily unavailable (for example, |
125 | audio playback during a phone call or similar). |
126 | */ |
127 | |
128 | QMultimedia::AvailabilityStatus QMediaObject::availability() const |
129 | { |
130 | if (d_func()->service == nullptr) |
131 | return QMultimedia::ServiceMissing; |
132 | |
133 | if (d_func()->availabilityControl) |
134 | return d_func()->availabilityControl->availability(); |
135 | |
136 | return QMultimedia::Available; |
137 | } |
138 | |
139 | /*! |
140 | Returns true if the service is available for use. |
141 | */ |
142 | |
143 | bool QMediaObject::isAvailable() const |
144 | { |
145 | return availability() == QMultimedia::Available; |
146 | } |
147 | |
148 | /*! |
149 | Returns the media service that provides the functionality of this multimedia object. |
150 | */ |
151 | |
152 | QMediaService* QMediaObject::service() const |
153 | { |
154 | return d_func()->service; |
155 | } |
156 | |
157 | int QMediaObject::notifyInterval() const |
158 | { |
159 | return d_func()->notifyTimer->interval(); |
160 | } |
161 | |
162 | void QMediaObject::setNotifyInterval(int milliSeconds) |
163 | { |
164 | Q_D(QMediaObject); |
165 | |
166 | if (d->notifyTimer->interval() != milliSeconds) { |
167 | d->notifyTimer->setInterval(milliSeconds); |
168 | |
169 | emit notifyIntervalChanged(milliSeconds); |
170 | } |
171 | } |
172 | |
173 | /*! |
174 | Bind \a object to this QMediaObject instance. |
175 | |
176 | This method establishes a relationship between this media object and a |
177 | helper object. The nature of the relationship depends on both parties. This |
178 | methods returns true if the helper was successfully bound, false otherwise. |
179 | |
180 | Most subclasses of QMediaObject provide more convenient functions |
181 | that wrap this functionality, so this function rarely needs to be |
182 | called directly. |
183 | |
184 | The object passed must implement the QMediaBindableInterface interface. |
185 | |
186 | \sa QMediaBindableInterface |
187 | */ |
188 | bool QMediaObject::bind(QObject *object) |
189 | { |
190 | QMediaBindableInterface *helper = qobject_cast<QMediaBindableInterface*>(object); |
191 | if (!helper) |
192 | return false; |
193 | |
194 | QMediaObject *currentObject = helper->mediaObject(); |
195 | |
196 | if (currentObject == this) |
197 | return true; |
198 | |
199 | if (currentObject) |
200 | currentObject->unbind(object); |
201 | |
202 | return helper->setMediaObject(this); |
203 | } |
204 | |
205 | /*! |
206 | Detach \a object from the QMediaObject instance. |
207 | |
208 | Unbind the helper object from this media object. A warning |
209 | will be generated if the object was not previously bound to this |
210 | object. |
211 | |
212 | \sa QMediaBindableInterface |
213 | */ |
214 | void QMediaObject::unbind(QObject *object) |
215 | { |
216 | QMediaBindableInterface *helper = qobject_cast<QMediaBindableInterface*>(object); |
217 | |
218 | if (helper && helper->mediaObject() == this) |
219 | helper->setMediaObject(nullptr); |
220 | else |
221 | qWarning() << "QMediaObject: Trying to unbind not connected helper object" ; |
222 | } |
223 | |
224 | /*! |
225 | Constructs a media object which uses the functionality provided by a media \a service. |
226 | |
227 | The \a parent is passed to QObject. |
228 | |
229 | This class is meant as a base class for multimedia objects so this |
230 | constructor is protected. |
231 | */ |
232 | |
233 | QMediaObject::QMediaObject(QObject *parent, QMediaService *service): |
234 | QObject(parent), |
235 | d_ptr(new QMediaObjectPrivate) |
236 | |
237 | { |
238 | Q_D(QMediaObject); |
239 | |
240 | d->q_ptr = this; |
241 | |
242 | d->notifyTimer = new QTimer(this); |
243 | d->notifyTimer->setInterval(1000); |
244 | connect(d->notifyTimer, SIGNAL(timeout()), SLOT(_q_notify())); |
245 | |
246 | d->service = service; |
247 | |
248 | setupControls(); |
249 | } |
250 | |
251 | /*! |
252 | \internal |
253 | */ |
254 | |
255 | QMediaObject::QMediaObject(QMediaObjectPrivate &dd, QObject *parent, |
256 | QMediaService *service): |
257 | QObject(parent), |
258 | d_ptr(&dd) |
259 | { |
260 | Q_D(QMediaObject); |
261 | d->q_ptr = this; |
262 | |
263 | d->notifyTimer = new QTimer(this); |
264 | d->notifyTimer->setInterval(1000); |
265 | connect(d->notifyTimer, SIGNAL(timeout()), SLOT(_q_notify())); |
266 | |
267 | d->service = service; |
268 | |
269 | setupControls(); |
270 | } |
271 | |
272 | /*! |
273 | Watch the property \a name. The property's notify signal will be emitted |
274 | once every \c notifyInterval milliseconds. |
275 | |
276 | \sa notifyInterval |
277 | */ |
278 | |
279 | void QMediaObject::addPropertyWatch(QByteArray const &name) |
280 | { |
281 | Q_D(QMediaObject); |
282 | |
283 | const QMetaObject* m = metaObject(); |
284 | |
285 | int index = m->indexOfProperty(name.constData()); |
286 | |
287 | if (index != -1 && m->property(index).hasNotifySignal()) { |
288 | d->notifyProperties.insert(index); |
289 | |
290 | if (!d->notifyTimer->isActive()) |
291 | d->notifyTimer->start(); |
292 | } |
293 | } |
294 | |
295 | /*! |
296 | Remove property \a name from the list of properties whose changes are |
297 | regularly signaled. |
298 | |
299 | \sa notifyInterval |
300 | */ |
301 | |
302 | void QMediaObject::removePropertyWatch(QByteArray const &name) |
303 | { |
304 | Q_D(QMediaObject); |
305 | |
306 | int index = metaObject()->indexOfProperty(name.constData()); |
307 | |
308 | if (index != -1) { |
309 | d->notifyProperties.remove(index); |
310 | |
311 | if (d->notifyProperties.isEmpty()) |
312 | d->notifyTimer->stop(); |
313 | } |
314 | } |
315 | |
316 | /*! |
317 | \property QMediaObject::notifyInterval |
318 | |
319 | The interval at which notifiable properties will update. |
320 | |
321 | The interval is expressed in milliseconds, the default value is 1000. |
322 | |
323 | \sa addPropertyWatch(), removePropertyWatch() |
324 | */ |
325 | |
326 | /*! |
327 | \fn void QMediaObject::notifyIntervalChanged(int milliseconds) |
328 | |
329 | Signal a change in the notify interval period to \a milliseconds. |
330 | */ |
331 | |
332 | /*! |
333 | Returns true if there is meta-data associated with this media object, else false. |
334 | */ |
335 | |
336 | bool QMediaObject::isMetaDataAvailable() const |
337 | { |
338 | Q_D(const QMediaObject); |
339 | |
340 | return d->metaDataControl |
341 | ? d->metaDataControl->isMetaDataAvailable() |
342 | : false; |
343 | } |
344 | |
345 | /*! |
346 | \fn QMediaObject::metaDataAvailableChanged(bool available) |
347 | |
348 | Signals that the \a available state of a media object's meta-data has changed. |
349 | */ |
350 | |
351 | /*! |
352 | Returns the value associated with a meta-data \a key. |
353 | |
354 | See the list of predefined \l {QMediaMetaData}{meta-data keys}. |
355 | */ |
356 | QVariant QMediaObject::metaData(const QString &key) const |
357 | { |
358 | Q_D(const QMediaObject); |
359 | |
360 | return d->metaDataControl |
361 | ? d->metaDataControl->metaData(key) |
362 | : QVariant(); |
363 | } |
364 | |
365 | /*! |
366 | Returns a list of keys there is meta-data available for. |
367 | */ |
368 | QStringList QMediaObject::availableMetaData() const |
369 | { |
370 | Q_D(const QMediaObject); |
371 | |
372 | return d->metaDataControl |
373 | ? d->metaDataControl->availableMetaData() |
374 | : QStringList(); |
375 | } |
376 | |
377 | /*! |
378 | \fn QMediaObject::metaDataChanged() |
379 | |
380 | Signals that this media object's meta-data has changed. |
381 | |
382 | If multiple meta-data elements are changed, |
383 | metaDataChanged(const QString &key, const QVariant &value) signal is emitted |
384 | for each of them with metaDataChanged() changed emitted once. |
385 | */ |
386 | |
387 | /*! |
388 | \fn QMediaObject::metaDataChanged(const QString &key, const QVariant &value) |
389 | |
390 | Signal the changes of one meta-data element \a value with the given \a key. |
391 | */ |
392 | |
393 | |
394 | void QMediaObject::setupControls() |
395 | { |
396 | Q_D(QMediaObject); |
397 | |
398 | if (d->service != nullptr) { |
399 | d->metaDataControl = qobject_cast<QMetaDataReaderControl*>( |
400 | d->service->requestControl(QMetaDataReaderControl_iid)); |
401 | |
402 | if (d->metaDataControl) { |
403 | connect(d->metaDataControl, SIGNAL(metaDataChanged()), SIGNAL(metaDataChanged())); |
404 | connect(d->metaDataControl, |
405 | SIGNAL(metaDataChanged(QString,QVariant)), |
406 | SIGNAL(metaDataChanged(QString,QVariant))); |
407 | connect(d->metaDataControl, |
408 | SIGNAL(metaDataAvailableChanged(bool)), |
409 | SIGNAL(metaDataAvailableChanged(bool))); |
410 | } |
411 | |
412 | d->availabilityControl = d->service->requestControl<QMediaAvailabilityControl*>(); |
413 | if (d->availabilityControl) { |
414 | connect(d->availabilityControl, |
415 | SIGNAL(availabilityChanged(QMultimedia::AvailabilityStatus)), |
416 | SLOT(_q_availabilityChanged())); |
417 | } |
418 | } |
419 | } |
420 | |
421 | /*! |
422 | \fn QMediaObject::availabilityChanged(bool available) |
423 | |
424 | Signal emitted when the availability state has changed to \a available. |
425 | */ |
426 | |
427 | /*! |
428 | \fn QMediaObject::availabilityChanged(QMultimedia::AvailabilityStatus availability) |
429 | |
430 | Signal emitted when the availability of the service has changed to \a availability. |
431 | */ |
432 | |
433 | |
434 | #include "moc_qmediaobject.cpp" |
435 | QT_END_NAMESPACE |
436 | |
437 | |