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
50QT_BEGIN_NAMESPACE
51
52void 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
71void 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
114QMediaObject::~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
128QMultimedia::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
143bool 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
152QMediaService* QMediaObject::service() const
153{
154 return d_func()->service;
155}
156
157int QMediaObject::notifyInterval() const
158{
159 return d_func()->notifyTimer->interval();
160}
161
162void 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*/
188bool 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*/
214void 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
233QMediaObject::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
255QMediaObject::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
279void 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
302void 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
336bool 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*/
356QVariant 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*/
368QStringList 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
394void 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"
435QT_END_NAMESPACE
436
437