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(t&: properties)) { |
65 | QMetaProperty p = m->property(index: pi); |
66 | p.notifySignal().invoke( |
67 | object: q, val0: QGenericArgument(QMetaType::typeName(type: p.userType()), p.read(obj: 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(availability: q->availability()); |
80 | q->availabilityChanged(available: 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 | } |
117 | |
118 | /*! |
119 | Returns the availability of the functionality offered by this object. |
120 | |
121 | In some cases the functionality may not be available (for example, if |
122 | the current operating system or platform does not provide the required |
123 | functionality), or it may be temporarily unavailable (for example, |
124 | audio playback during a phone call or similar). |
125 | */ |
126 | |
127 | QMultimedia::AvailabilityStatus QMediaObject::availability() const |
128 | { |
129 | if (d_func()->service == nullptr) |
130 | return QMultimedia::ServiceMissing; |
131 | |
132 | if (d_func()->availabilityControl) |
133 | return d_func()->availabilityControl->availability(); |
134 | |
135 | return QMultimedia::Available; |
136 | } |
137 | |
138 | /*! |
139 | Returns true if the service is available for use. |
140 | */ |
141 | |
142 | bool QMediaObject::isAvailable() const |
143 | { |
144 | return availability() == QMultimedia::Available; |
145 | } |
146 | |
147 | /*! |
148 | Returns the media service that provides the functionality of this multimedia object. |
149 | */ |
150 | |
151 | QMediaService* QMediaObject::service() const |
152 | { |
153 | return d_func()->service; |
154 | } |
155 | |
156 | int QMediaObject::notifyInterval() const |
157 | { |
158 | return d_func()->notifyTimer->interval(); |
159 | } |
160 | |
161 | void QMediaObject::setNotifyInterval(int milliSeconds) |
162 | { |
163 | Q_D(QMediaObject); |
164 | |
165 | if (d->notifyTimer->interval() != milliSeconds) { |
166 | d->notifyTimer->setInterval(milliSeconds); |
167 | |
168 | emit notifyIntervalChanged(milliSeconds); |
169 | } |
170 | } |
171 | |
172 | /*! |
173 | Bind \a object to this QMediaObject instance. |
174 | |
175 | This method establishes a relationship between this media object and a |
176 | helper object. The nature of the relationship depends on both parties. This |
177 | methods returns true if the helper was successfully bound, false otherwise. |
178 | |
179 | Most subclasses of QMediaObject provide more convenient functions |
180 | that wrap this functionality, so this function rarely needs to be |
181 | called directly. |
182 | |
183 | The object passed must implement the QMediaBindableInterface interface. |
184 | |
185 | \sa QMediaBindableInterface |
186 | */ |
187 | bool QMediaObject::bind(QObject *object) |
188 | { |
189 | QMediaBindableInterface *helper = qobject_cast<QMediaBindableInterface*>(object); |
190 | if (!helper) |
191 | return false; |
192 | |
193 | QMediaObject *currentObject = helper->mediaObject(); |
194 | |
195 | if (currentObject == this) |
196 | return true; |
197 | |
198 | if (currentObject) |
199 | currentObject->unbind(object); |
200 | |
201 | return helper->setMediaObject(this); |
202 | } |
203 | |
204 | /*! |
205 | Detach \a object from the QMediaObject instance. |
206 | |
207 | Unbind the helper object from this media object. A warning |
208 | will be generated if the object was not previously bound to this |
209 | object. |
210 | |
211 | \sa QMediaBindableInterface |
212 | */ |
213 | void QMediaObject::unbind(QObject *object) |
214 | { |
215 | QMediaBindableInterface *helper = qobject_cast<QMediaBindableInterface*>(object); |
216 | |
217 | if (helper && helper->mediaObject() == this) |
218 | helper->setMediaObject(nullptr); |
219 | else |
220 | qWarning() << "QMediaObject: Trying to unbind not connected helper object" ; |
221 | } |
222 | |
223 | /*! |
224 | Constructs a media object which uses the functionality provided by a media \a service. |
225 | |
226 | The \a parent is passed to QObject. |
227 | |
228 | This class is meant as a base class for multimedia objects so this |
229 | constructor is protected. |
230 | */ |
231 | |
232 | QMediaObject::QMediaObject(QObject *parent, QMediaService *service) |
233 | : QObject(*new QMediaObjectPrivate, parent) |
234 | { |
235 | Q_D(QMediaObject); |
236 | |
237 | d->notifyTimer = new QTimer(this); |
238 | d->notifyTimer->setInterval(1000); |
239 | connect(asender: d->notifyTimer, SIGNAL(timeout()), SLOT(_q_notify())); |
240 | |
241 | d->service = service; |
242 | |
243 | setupControls(); |
244 | } |
245 | |
246 | /*! |
247 | \internal |
248 | */ |
249 | |
250 | QMediaObject::QMediaObject(QMediaObjectPrivate &dd, QObject *parent, QMediaService *service) |
251 | : QObject(dd, parent) |
252 | { |
253 | Q_D(QMediaObject); |
254 | |
255 | d->notifyTimer = new QTimer(this); |
256 | d->notifyTimer->setInterval(1000); |
257 | connect(asender: d->notifyTimer, SIGNAL(timeout()), SLOT(_q_notify())); |
258 | |
259 | d->service = service; |
260 | |
261 | setupControls(); |
262 | } |
263 | |
264 | /*! |
265 | Watch the property \a name. The property's notify signal will be emitted |
266 | once every \c notifyInterval milliseconds. |
267 | |
268 | \sa notifyInterval |
269 | */ |
270 | |
271 | void QMediaObject::addPropertyWatch(QByteArray const &name) |
272 | { |
273 | Q_D(QMediaObject); |
274 | |
275 | const QMetaObject* m = metaObject(); |
276 | |
277 | int index = m->indexOfProperty(name: name.constData()); |
278 | |
279 | if (index != -1 && m->property(index).hasNotifySignal()) { |
280 | d->notifyProperties.insert(value: index); |
281 | |
282 | if (!d->notifyTimer->isActive()) |
283 | d->notifyTimer->start(); |
284 | } |
285 | } |
286 | |
287 | /*! |
288 | Remove property \a name from the list of properties whose changes are |
289 | regularly signaled. |
290 | |
291 | \sa notifyInterval |
292 | */ |
293 | |
294 | void QMediaObject::removePropertyWatch(QByteArray const &name) |
295 | { |
296 | Q_D(QMediaObject); |
297 | |
298 | int index = metaObject()->indexOfProperty(name: name.constData()); |
299 | |
300 | if (index != -1) { |
301 | d->notifyProperties.remove(value: index); |
302 | |
303 | if (d->notifyProperties.isEmpty()) |
304 | d->notifyTimer->stop(); |
305 | } |
306 | } |
307 | |
308 | /*! |
309 | \property QMediaObject::notifyInterval |
310 | |
311 | The interval at which notifiable properties will update. |
312 | |
313 | The interval is expressed in milliseconds, the default value is 1000. |
314 | |
315 | \sa addPropertyWatch(), removePropertyWatch() |
316 | */ |
317 | |
318 | /*! |
319 | \fn void QMediaObject::notifyIntervalChanged(int milliseconds) |
320 | |
321 | Signal a change in the notify interval period to \a milliseconds. |
322 | */ |
323 | |
324 | /*! |
325 | Returns true if there is meta-data associated with this media object, else false. |
326 | */ |
327 | |
328 | bool QMediaObject::isMetaDataAvailable() const |
329 | { |
330 | Q_D(const QMediaObject); |
331 | |
332 | return d->metaDataControl |
333 | ? d->metaDataControl->isMetaDataAvailable() |
334 | : false; |
335 | } |
336 | |
337 | /*! |
338 | \fn QMediaObject::metaDataAvailableChanged(bool available) |
339 | |
340 | Signals that the \a available state of a media object's meta-data has changed. |
341 | */ |
342 | |
343 | /*! |
344 | Returns the value associated with a meta-data \a key. |
345 | |
346 | See the list of predefined \l {QMediaMetaData}{meta-data keys}. |
347 | */ |
348 | QVariant QMediaObject::metaData(const QString &key) const |
349 | { |
350 | Q_D(const QMediaObject); |
351 | |
352 | return d->metaDataControl |
353 | ? d->metaDataControl->metaData(key) |
354 | : QVariant(); |
355 | } |
356 | |
357 | /*! |
358 | Returns a list of keys there is meta-data available for. |
359 | */ |
360 | QStringList QMediaObject::availableMetaData() const |
361 | { |
362 | Q_D(const QMediaObject); |
363 | |
364 | return d->metaDataControl |
365 | ? d->metaDataControl->availableMetaData() |
366 | : QStringList(); |
367 | } |
368 | |
369 | /*! |
370 | \fn QMediaObject::metaDataChanged() |
371 | |
372 | Signals that this media object's meta-data has changed. |
373 | |
374 | If multiple meta-data elements are changed, |
375 | metaDataChanged(const QString &key, const QVariant &value) signal is emitted |
376 | for each of them with metaDataChanged() changed emitted once. |
377 | */ |
378 | |
379 | /*! |
380 | \fn QMediaObject::metaDataChanged(const QString &key, const QVariant &value) |
381 | |
382 | Signal the changes of one meta-data element \a value with the given \a key. |
383 | */ |
384 | |
385 | |
386 | void QMediaObject::setupControls() |
387 | { |
388 | Q_D(QMediaObject); |
389 | |
390 | if (d->service != nullptr) { |
391 | d->metaDataControl = qobject_cast<QMetaDataReaderControl*>( |
392 | object: d->service->requestControl(QMetaDataReaderControl_iid)); |
393 | |
394 | if (d->metaDataControl) { |
395 | connect(asender: d->metaDataControl, SIGNAL(metaDataChanged()), SIGNAL(metaDataChanged())); |
396 | connect(asender: d->metaDataControl, |
397 | SIGNAL(metaDataChanged(QString,QVariant)), |
398 | SIGNAL(metaDataChanged(QString,QVariant))); |
399 | connect(asender: d->metaDataControl, |
400 | SIGNAL(metaDataAvailableChanged(bool)), |
401 | SIGNAL(metaDataAvailableChanged(bool))); |
402 | } |
403 | |
404 | d->availabilityControl = d->service->requestControl<QMediaAvailabilityControl*>(); |
405 | if (d->availabilityControl) { |
406 | connect(asender: d->availabilityControl, |
407 | SIGNAL(availabilityChanged(QMultimedia::AvailabilityStatus)), |
408 | SLOT(_q_availabilityChanged())); |
409 | } |
410 | } |
411 | } |
412 | |
413 | /*! |
414 | \fn QMediaObject::availabilityChanged(bool available) |
415 | |
416 | Signal emitted when the availability state has changed to \a available. |
417 | */ |
418 | |
419 | /*! |
420 | \fn QMediaObject::availabilityChanged(QMultimedia::AvailabilityStatus availability) |
421 | |
422 | Signal emitted when the availability of the service has changed to \a availability. |
423 | */ |
424 | |
425 | QT_END_NAMESPACE |
426 | |
427 | #include "moc_qmediaobject.cpp" |
428 | |