Switch to unified view

a/upqo/avtransport_qo.h b/upqo/avtransport_qo.h
1
/* Copyright (C) 2014 J.F.Dockes
1
/* Copyright (C) 2014-2018 J.F.Dockes
2
 *   This program is free software; you can redistribute it and/or modify
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
5
 *   (at your option) any later version.
6
 *
6
 *
...
...
37
class AVTransportQO : public QObject, public UPnPClient::VarEventReporter {
37
class AVTransportQO : public QObject, public UPnPClient::VarEventReporter {
38
    Q_OBJECT;
38
    Q_OBJECT;
39
39
40
public:
40
public:
41
    AVTransportQO(UPnPClient::AVTH avt, QObject *parent = 0)
41
    AVTransportQO(UPnPClient::AVTH avt, QObject *parent = 0)
42
        : QObject(parent), m_srv(avt), m_timer(0), m_errcnt(0),
42
        : QObject(parent), m_srv(avt) {
43
          m_cursecs(-1),
44
          m_sent_end_of_track_sig(false),
45
          m_in_ending(false),
46
          m_tpstate(AVTransport::Unknown) {
47
43
48
        m_timer = new QTimer(this);
44
        m_timer = new QTimer(this);
49
        connect(m_timer, SIGNAL(timeout()), this, SLOT(update()));
45
        connect(m_timer, SIGNAL(timeout()), this, SLOT(update()));
50
        m_timer->start(1000);
46
        m_timer->start(1000);
51
47
...
...
66
    }
62
    }
67
63
68
    const char *tpstatetostr(int tps) {
64
    const char *tpstatetostr(int tps) {
69
        switch (tps) {
65
        switch (tps) {
70
        default:
66
        default:
71
        case UPnPClient::AVTransport::Unknown:
67
        case UPnPClient::AVTransport::Unknown: return "Unknown";
72
            return "Unknown";
73
        case UPnPClient::AVTransport::Stopped:
68
        case UPnPClient::AVTransport::Stopped: return "Stopped";
74
            return "Stopped";
75
        case UPnPClient::AVTransport::Playing:
69
        case UPnPClient::AVTransport::Playing: return "Playing";
76
            return "Playing";
77
        case UPnPClient::AVTransport::Transitioning:
70
        case UPnPClient::AVTransport::Transitioning: return "Transitionning";
78
            return "Transitionning";
79
        case UPnPClient::AVTransport::PausedPlayback:
71
        case UPnPClient::AVTransport::PausedPlayback: return "PausedPlay";
80
            return "PausedPlay";
81
        case UPnPClient::AVTransport::PausedRecording:
72
        case UPnPClient::AVTransport::PausedRecording: return "PausedRecord";
82
            return "PausedRecord";
83
        case UPnPClient::AVTransport::Recording:
73
        case UPnPClient::AVTransport::Recording: return "Recording";
84
            return "Recording";
85
        case UPnPClient::AVTransport::NoMediaPresent:
74
        case UPnPClient::AVTransport::NoMediaPresent: return "No Media";
86
            return "No Media";
87
        }
75
        }
88
    }
76
    }
89
77
90
    virtual void changed(const char *nm, int value) {
78
    virtual void changed(const char *nm, int value) {
91
        if (!strcmp(nm, "CurrentTrackDuration")) {
79
        if (!strcmp(nm, "CurrentTrackDuration")) {
...
...
93
            // apparently don't send it (bubble?). So use the value
81
            // apparently don't send it (bubble?). So use the value
94
            // from GetPositionInfo
82
            // from GetPositionInfo
95
            //qDebug() << "AVT: Changed: " << nm << " (int): " << value;
83
            //qDebug() << "AVT: Changed: " << nm << " (int): " << value;
96
            m_cursecs = value;
84
            m_cursecs = value;
97
        } else if (!strcmp(nm, "TransportState")) {
85
        } else if (!strcmp(nm, "TransportState")) {
98
            //qDebug() << "AVT: Changed: " << nm << " " << tpstatetostr(value);
86
            qDebug() << "AVT: Changed: " << nm << " " << tpstatetostr(value);
99
            m_tpstate = AVTransport::TransportState(value);
87
            m_tpstate = AVTransport::TransportState(value);
100
            emit tpStateChanged(value);
88
            emit tpStateChanged(value);
101
            if (m_in_ending &&
89
            if (m_in_ending &&
102
                (value == UPnPClient::AVTransport::Stopped ||
90
                (value == UPnPClient::AVTransport::Stopped ||
103
                 value == UPnPClient::AVTransport::NoMediaPresent)) {
91
                 value == UPnPClient::AVTransport::NoMediaPresent)) {
104
                m_in_ending = false;
92
                m_in_ending = false;
105
                qDebug() << "AVT EVT: emitting stoppedAtEOT";
93
                qDebug() << "AVT: changed: emitting stoppedAtEOT";
106
                emit stoppedAtEOT();
94
                emit stoppedAtEOT();
107
            }
95
            }
108
        } else if (!strcmp(nm, "CurrentTransportActions")) {
96
        } else if (!strcmp(nm, "CurrentTransportActions")) {
109
            //qDebug() << "AVT: Changed: " << nm << " (int): " << value;
97
            //qDebug() << "AVT: changed: " << nm << " (int): " << value;
110
            emit tpActionsChanged(value);
98
            emit tpActionsChanged(value);
111
        }
99
        }
112
    }
100
    }
113
101
114
    // Note about uri change detection:
102
    // Note about uri change detection:
...
...
129
        if (!strcmp(nm, "AVTransportURI")) {
117
        if (!strcmp(nm, "AVTransportURI")) {
130
            if (m_cururi.compare(value) &&
118
            if (m_cururi.compare(value) &&
131
                    (m_tpstate == UPnPClient::AVTransport::Playing ||
119
                    (m_tpstate == UPnPClient::AVTransport::Playing ||
132
                     m_tpstate == UPnPClient::AVTransport::Transitioning ||
120
                     m_tpstate == UPnPClient::AVTransport::Transitioning ||
133
                     m_tpstate == UPnPClient::AVTransport::PausedPlayback)) {
121
                     m_tpstate == UPnPClient::AVTransport::PausedPlayback)) {
134
                //qDebug() << "AVT: ext track change: cur [" << m_cururi.c_str()
122
                qDebug() << "AVT: ext track change: cur [" << m_cururi.c_str()
135
                //         << "] new [" << value << "]";
123
                         << "] new [" << value << "]";
136
                setcururi(value);
124
                setcururi(value);
137
                emit newTrackPlaying(QString::fromUtf8(value));
125
                emit newTrackPlaying(QString::fromUtf8(value));
138
            }
126
            }
139
        }
127
        }
140
    }
128
    }
141
129
142
    virtual void changed(const char *nm, UPnPClient::UPnPDirObject meta) {
130
    virtual void changed(const char *nm, UPnPClient::UPnPDirObject meta) {
143
        if (!strcmp(nm, "AVTransportURIMetaData")) {
131
        if (!strcmp(nm, "AVTransportURIMetaData")) {
144
            qDebug() << "AVT: Changed: " << nm << " (dirc): " <<
132
            //qDebug() << "AVT: Changed: " << nm << " (dirc): " <<
145
                     meta.dump().c_str();
133
            //       meta.dump().c_str();
146
            // Don't use this if no resources are set. XBMC/Kodi does
134
            // Don't use this if no resources are set. XBMC/Kodi does
147
            // this for some reason. Else we'd end-up with
135
            // this for some reason. Else we'd end-up with
148
            // resource-less unplayable entries in the
136
            // resource-less unplayable entries in the
149
            // playlist. Scheduling a state update is not useful
137
            // playlist. Scheduling a state update is not useful
150
            // either because the data will have the same
138
            // either because the data will have the same
...
...
160
    }
148
    }
161
149
162
public slots:
150
public slots:
163
151
164
    virtual void play() {
152
    virtual void play() {
153
        qDebug() << "AVT: play";
165
        m_srv->play();
154
        m_srv->play();
166
    }
155
    }
167
    virtual void stop() {
156
    virtual void stop() {
157
        qDebug() << "AVT: stop";
168
        setcururi("");
158
        setcururi("");
169
        m_in_ending = false;
159
        m_in_ending = false;
170
        m_srv->stop();
160
        m_srv->stop();
171
    }
161
    }
172
    virtual void pause() {
162
    virtual void pause() {
163
        qDebug() << "AVT: pause";
173
        m_srv->pause();
164
        m_srv->pause();
174
    }
165
    }
175
166
176
    virtual void changeTrack(const std::string& uri, const AVTMetadata* md) {
167
    virtual void changeTrack(const std::string& uri, const AVTMetadata* md) {
177
        qDebug() << "AVT:changeTrack: " << uri.c_str();
168
        qDebug() << "AVT: changeTrack: " << uri.c_str();
178
        m_srv->setAVTransportURI(uri, md->getDidl());
169
        m_srv->setAVTransportURI(uri, md->getDidl());
179
        // Don't do this: wait for the renderer data, else we risk
170
        // Don't do this: wait for the renderer data, else we risk
180
        // flickering if an event reports the old track.
171
        // flickering if an event reports the old track.
181
        // setcururi(uri);
172
        // setcururi(uri);
182
    }
173
    }
183
174
184
    virtual void prepareNextTrack(const std::string& uri,
175
    virtual void prepareNextTrack(const std::string& uri,
185
                                  const AVTMetadata* md) {
176
                                  const AVTMetadata* md) {
186
        qDebug() << "AVT:prepareNextTrack: " << uri.c_str();
177
        qDebug() << "AVT: prepareNextTrack: " << uri.c_str();
187
        m_srv->setNextAVTransportURI(uri, md->getDidl());
178
        m_srv->setNextAVTransportURI(uri, md->getDidl());
188
    }
179
    }
189
180
190
    // Seek to point. Parameter in seconds
181
    // Seek to point. Parameter in seconds
191
    virtual void seek(int secs) {
182
    virtual void seek(int secs) {
192
        qDebug() << "AVT: seek to " << secs << " S. m_cursecs " << m_cursecs;
183
        qDebug() << "AVT: seek to " << secs << " S. m_cursecs " << m_cursecs;
193
        m_srv->seek(UPnPClient::AVTransport::SEEK_REL_TIME, secs);
184
        m_srv->seek(UPnPClient::AVTransport::SEEK_REL_TIME, secs);
194
    }
185
    }
195
186
196
    // Retrieve the current track length in seconds. This is useful
187
    // Retrieve the current track length in seconds. This is useful if
197
    // if the available metadata does not have a duration (esp. happens with video)
188
    // the available metadata does not have a duration (esp. happens
189
    // with video)
198
    virtual int trackSecs() {
190
    virtual int trackSecs() {
199
        return m_cursecs;
191
        return m_cursecs;
200
    }
192
    }
201
    virtual void fetchState() {
193
    virtual void fetchState() {
202
        update(true);
194
        update(true);
...
...
205
    // Called by timer every sec
197
    // Called by timer every sec
206
    virtual void update(bool force = false) {
198
    virtual void update(bool force = false) {
207
        UPnPClient::AVTransport::PositionInfo info;
199
        UPnPClient::AVTransport::PositionInfo info;
208
        int error;
200
        int error;
209
        if ((error = m_srv->getPositionInfo(info)) != 0) {
201
        if ((error = m_srv->getPositionInfo(info)) != 0) {
210
            qDebug() << "getPositionInfo failed with error " << error;
202
            qDebug() << "AVT: getPositionInfo failed with error " << error;
211
            if (m_errcnt++ > 4) {
203
            if (m_errcnt++ > 4) {
212
                emit connectionLost();
204
                emit connectionLost();
213
            }
205
            }
214
            return;
206
            return;
215
        }
207
        }
216
        m_errcnt = 0;
208
        m_errcnt = 0;
217
        m_cursecs = info.trackduration;
209
        m_cursecs = info.trackduration;
218
210
219
        UPnPClient::AVTransport::TransportInfo tinfo;
211
        UPnPClient::AVTransport::TransportInfo tinfo;
220
        if ((error = m_srv->getTransportInfo(tinfo)) != 0) {
212
        if ((error = m_srv->getTransportInfo(tinfo)) != 0) {
221
            qDebug() << "getTransportInfo failed with error " << error;
213
            qDebug() << "AVT: getTransportInfo failed with error " << error;
222
            return;
214
            return;
223
        }
215
        }
224
216
225
        //qDebug() << "AVT: update: posinfo: reltime " << info.reltime <<
217
        //qDebug() << "AVT: update: posinfo: reltime " << info.reltime <<
226
        //    " tdur " << info.trackduration << " meta " <<
218
        //    " tdur " << info.trackduration << " meta " <<
...
...
229
        if (m_tpstate == UPnPClient::AVTransport::Playing) {
221
        if (m_tpstate == UPnPClient::AVTransport::Playing) {
230
            // Time-related stuff
222
            // Time-related stuff
231
            emit secsInSongChanged(info.reltime);
223
            emit secsInSongChanged(info.reltime);
232
            if (m_cursecs > 0) {
224
            if (m_cursecs > 0) {
233
                if (info.reltime > m_cursecs - 10) {
225
                if (info.reltime > m_cursecs - 10) {
234
                    if (!m_sent_end_of_track_sig) {
235
                        qDebug() << "AVT: Emitting endOfTrackIsNear()";
236
                        emit endOfTrackIsNear();
237
                        m_sent_end_of_track_sig = true;
238
                    }
239
                    m_in_ending = true;
226
                    m_in_ending = true;
240
                } else if (info.reltime > 0 && info.reltime < 5) {
227
                } else if (info.reltime > 0 && info.reltime < 5) {
241
                    // This is for the case where we are playing 2
228
                    // This is for the case where we are playing 2
242
                    // consecutive identical URIs: heuristic try to detect
229
                    // consecutive identical URIs: heuristic try to detect
243
                    // the change
230
                    // the change
...
...
285
        }
272
        }
286
    }
273
    }
287
274
288
signals:
275
signals:
289
    void secsInSongChanged(quint32);
276
    void secsInSongChanged(quint32);
290
    void endOfTrackIsNear();
291
    void newTrackPlaying(QString);
277
    void newTrackPlaying(QString);
292
    void tpStateChanged(int);
278
    void tpStateChanged(int);
293
    void tpActionsChanged(int);
279
    void tpActionsChanged(int);
294
    void stoppedAtEOT();
280
    void stoppedAtEOT();
295
    void currentMetadata(UPnPClient::UPnPDirObject);
281
    void currentMetadata(UPnPClient::UPnPDirObject);
296
    void connectionLost();
282
    void connectionLost();
297
283
298
private:
284
private:
299
    UPnPClient::AVTH m_srv;
285
    UPnPClient::AVTH m_srv;
300
    QTimer *m_timer;
286
    QTimer *m_timer{nullptr};
301
    int m_errcnt;
287
    int m_errcnt{0};
302
    int m_cursecs;
288
    int m_cursecs{-1};
303
    bool m_sent_end_of_track_sig;
304
    bool m_in_ending;
289
    bool m_in_ending{false};
305
    std::string m_cururi;
290
    std::string m_cururi;
306
    AVTransport::TransportState m_tpstate;
291
    AVTransport::TransportState m_tpstate{AVTransport::Unknown};
307
292
308
    void setcururi(const std::string& uri) {
293
    void setcururi(const std::string& uri) {
309
        qDebug() << "setcururi: " << uri.c_str();
294
        qDebug() << "AVT: setcururi: " << uri.c_str();
310
        m_cururi = uri;
295
        m_cururi = uri;
311
        m_sent_end_of_track_sig = false;
312
        if (uri != "") {
296
        if (uri != "") {
313
            // Don't reset m_in_ending if uri is null: we often get an
297
            // Don't reset m_in_ending if uri is null: we often get an
314
            // uri change to "" before we get the transport state
298
            // uri change to "" before we get the transport state
315
            // change event. Resetting m_in_ending would prevent the
299
            // change event. Resetting m_in_ending would prevent the
316
            // emission of the stoppedAtEOT signal
300
            // emission of the stoppedAtEOT signal