Switch to unified view

a/sc2src/sc2mpd.cpp b/sc2src/sc2mpd.cpp
...
...
44
#include "rcvqueue.h"
44
#include "rcvqueue.h"
45
#include "log.h"
45
#include "log.h"
46
#include "conftree.h"
46
#include "conftree.h"
47
#include "chrono.h"
47
#include "chrono.h"
48
#include "watcher.h"
48
#include "watcher.h"
49
#include "songcastreceiver.h"
49
50
50
#ifdef WITH_WAVSC2
51
#ifdef WITH_WAVSC2
51
#include "openaudio.h"
52
#include "openaudio.h"
52
#include "audioreader.h"
53
#include "audioreader.h"
53
#endif
54
#endif
...
...
111
    if (g_sockfd > 0) {
112
    if (g_sockfd > 0) {
112
        shutdown(g_sockfd, SHUT_RD);
113
        shutdown(g_sockfd, SHUT_RD);
113
    }
114
    }
114
}
115
}
115
116
116
using namespace OpenHome;
117
bool needswap(AudioEater::BOrder order)
117
using namespace OpenHome::Net;
118
{
118
using namespace OpenHome::TestFramework;
119
    // Songcast data is always msb-first.  Convert to desired order:
119
using namespace OpenHome::Av;
120
    // depends on what downstream wants, and just as well we do it
120
121
    // here because we copy the buf anyway.
121
class OhmReceiverDriver : public IOhmReceiverDriver, public IOhmMsgProcessor {
122
    bool needswap = false;
122
public:
123
    switch (order) {
123
    OhmReceiverDriver(AudioEater* eater, AudioEater::Context *ctxt);
124
    case AudioEater::BO_MSB:
124
125
        break;
125
    virtual TBool IsConnected();
126
    case AudioEater::BO_LSB:
126
private:
127
        needswap = true;
127
    // IOhmReceiverDriver
128
        break;
128
    virtual void Add(OhmMsg& aMsg);
129
    case AudioEater::BO_HOST:
129
    virtual void Timestamp(OhmMsg& aMsg);
130
#ifdef WORDS_BIGENDIAN
130
    virtual void Started();
131
        needswap = false;
131
    virtual void Connected();
132
#else
132
    virtual void Playing();
133
        needswap = true;
133
    virtual void Disconnected();
134
    virtual void Stopped();
135
136
    // IOhmMsgProcessor
137
    virtual void Process(OhmMsgAudio& aMsg);
138
    virtual void Process(OhmMsgTrack& aMsg);
139
    virtual void Process(OhmMsgMetatext& aMsg);
140
141
private:
142
    // Debug, stats, etc while we get to understand the Songcast streams
143
    class Observer {
144
    public:
145
        TBool iReset;
146
        TUint iCount;
147
        TUint iFrame;
148
        int dumpfd;
149
        Chrono chron;
150
        Observer() : iReset(true), iCount(0), iFrame(0), dumpfd(-1) {
151
#if 0
152
            dumpfd = 
153
                open("/y/av/tmp/sc2dump", O_WRONLY|O_CREAT|O_TRUNC, 0666);
154
            if (dumpfd < 0) {
155
                LOGERR("OhmReceiverDriver::Open dump file failed\n");
156
            }
157
#endif
134
#endif
158
        }
135
        break;
159
160
        void reset() {
161
            iReset = true;
162
        }
163
164
        void process(OhmMsgAudio& aMsg);
165
    };
136
    }
166
    Observer m_obs;
137
    return needswap;
167
    AudioEater *m_eater;
168
    TBool iConnected;
169
};
138
}
170
139
171
OhmReceiverDriver::OhmReceiverDriver(AudioEater *eater, 
172
                                     AudioEater::Context *ctxt)
173
    : m_eater(eater), iConnected(false)
174
{
175
    audioqueue.start(1, m_eater->worker, ctxt);
176
}
177
178
void OhmReceiverDriver::Add(OhmMsg& aMsg)
179
{
180
    aMsg.Process(*this);
181
    aMsg.RemoveRef();
182
}
183
184
void OhmReceiverDriver::Timestamp(OhmMsg& /*aMsg*/)
185
{
186
}
187
188
void OhmReceiverDriver::Started()
189
{
190
    LOGDEB("=== STARTED ====\n");
191
}
192
193
void OhmReceiverDriver::Connected()
194
{
195
    m_obs.reset();
196
    printf("CONNECTED\n");
197
    fflush(stdout);
198
    LOGDEB("=== CONNECTED ====\n");
199
    iConnected = true;
200
}
201
202
void OhmReceiverDriver::Playing()
203
{
204
    LOGDEB("=== PLAYING ====\n");
205
    iConnected = true;
206
    if (m_eater->playing)
207
        m_eater->playing();
208
}
209
210
void OhmReceiverDriver::Disconnected()
211
{
212
    LOGDEB("=== DISCONNECTED ====\n");
213
    iConnected = false;
214
}
215
216
void OhmReceiverDriver::Stopped()
217
{
218
    LOGDEB("=== STOPPED ====\n");
219
    if (m_eater->stopped)
220
        m_eater->stopped();
221
}
222
223
TBool OhmReceiverDriver::IsConnected()
224
{
225
    return iConnected;
226
}
227
228
// Debug and stats only, not needed for main function
229
void OhmReceiverDriver::Observer::process(OhmMsgAudio& aMsg)
230
{
231
    if (++iCount == 400 || aMsg.Halt()) {
232
        LOGDEB("OhmRcvDrv::Process:audio: samplerate " << aMsg.SampleRate() <<
233
               " bitdepth " << aMsg.BitDepth() << " channels " <<
234
               aMsg.Channels() << " samples " << aMsg.Samples() << 
235
               " Halted ? " << aMsg.Halt() << endl);
236
237
#if 0
238
        static unsigned long long last_timestamp;
239
        unsigned long long timestamp = aMsg.MediaTimestamp();
240
        if (last_timestamp) {
241
            long long intervalus = 
242
                ((timestamp - last_timestamp) * 1000000) / (256*48000);
243
            long long atsus = 
244
                ((timestamp) * 1000000) / (256*48000);
245
246
            // Not too sure what this did. amicros not in chrono.cpp any more
247
            long long absus = chron.amicros() - 1430477861905884LL;
248
            LOGDEB("Computed-uS: " << intervalus  << 
249
                   " Elapsed-uS: " << chron.urestart() << 
250
                   " Timestamp-uS: " << atsus <<
251
                   " Abs-uS: " << absus << 
252
                   " Diff-mS " << (absus - atsus) / 1000 <<
253
                   endl);
254
        }
255
        last_timestamp = timestamp;
256
#endif
257
258
        if (!aMsg.Halt()) {
259
            unsigned int bytes = 
260
                aMsg.Samples() * (aMsg.BitDepth() / 8) * aMsg.Channels();
261
262
            if (bytes != aMsg.Audio().Bytes()) {
263
                LOGERR("OhmRcvDrv::Process:audio: computed bytes " << bytes << 
264
                       " !=  buffer's " << aMsg.Audio().Bytes() << endl);
265
                bytes = aMsg.Audio().Bytes();
266
            }
267
            const unsigned char *icp = 
268
                (const unsigned char *)aMsg.Audio().Ptr();
269
            bool silence = true;
270
            for (unsigned int i = 0; i < bytes; i++) {
271
                if (icp[i]) {
272
                    silence = false;
273
                    break;
274
                }
275
            }
276
            if (silence) {
277
                LOGDEB("OhmRcvDrv::Process:audio: silence buffer" << endl);
278
            }
279
            if (dumpfd >= 0) {
280
                if (write(dumpfd, icp, bytes) != int(bytes)) {
281
                    ;
282
                }
283
            }
284
        }
285
286
        iCount = 0;
287
    }
288
289
    if (iReset) {
290
        iFrame = aMsg.Frame();
291
        iReset = false;
292
    } else {
293
        if (aMsg.Frame() != iFrame + 1) {
294
            LOGINF("Missed frames between " << iFrame << " and " << 
295
                   aMsg.Frame() << endl);
296
        }
297
        iFrame = aMsg.Frame();
298
    }
299
}
300
301
void copyswap(unsigned char *dest, const unsigned char *src, 
140
void copyswap(unsigned char *dest, const unsigned char *src,
302
              unsigned int bytes, unsigned int bits)
141
              unsigned int bytes, unsigned int bits)
303
{
142
{
304
    unsigned char *ocp = dest;
143
    unsigned char *ocp = dest;
305
    const unsigned char *icp = src;
144
    const unsigned char *icp = src;
306
    if (bits == 16) {
145
    if (bits == 16) {
...
...
322
            icp += 4;
161
            icp += 4;
323
        }
162
        }
324
    }
163
    }
325
}
164
}
326
165
327
void OhmReceiverDriver::Process(OhmMsgAudio& aMsg)
166
using namespace OpenHome;
328
{
167
using namespace OpenHome::Net;
329
    unsigned int bytes = 0;
168
using namespace OpenHome::TestFramework;
330
    unsigned int allocbytes = 0;
169
using namespace OpenHome::Av;
331
    char *buf = NULL;
332
    bool needswap = false;
333
334
    if (aMsg.Audio().Bytes() == 0) {
335
        if (aMsg.Halt()) {
336
            LOGDEB("OhmReceiverDriver::Process: empty message with halt flag "
337
                   "set\n");
338
            goto put_audio_message;
339
        } else {
340
            LOGDEB("OhmReceiverDriver::Process: ignoring empty message\n");
341
            return;
342
        }
343
    }
344
345
    m_obs.process(aMsg);
346
    if (aMsg.Halt()) {
347
        LOGDEB("OhmReceiverDriver::Process: halt flag set in message\n");
348
    }
349
350
    bytes = aMsg.Audio().Bytes();
351
    // We allocate a bit more space to avoir reallocations in the resampler
352
    allocbytes = bytes + 100;
353
    buf = (char *)malloc(allocbytes);
354
    if (buf == 0) {
355
        LOGERR("OhmReceiverDriver::Process: can't allocate " << 
356
               bytes << " bytes\n");
357
        return;
358
    }
359
360
    // Songcast data is always msb-first.  Convert to desired order:
361
    // depends on what downstream wants, and just as well we do it
362
    // here because we copy the buf anyway.
363
    switch (m_eater->input_border) {
364
    case AudioEater::BO_MSB: 
365
        break;
366
    case AudioEater::BO_LSB: 
367
        needswap = true; 
368
        break;
369
    case AudioEater::BO_HOST:
370
#ifdef WORDS_BIGENDIAN
371
        needswap = false;
372
#else
373
        needswap = true;
374
#endif
375
        break;
376
    }
377
378
    if (needswap) {
379
        copyswap((unsigned char *)buf, aMsg.Audio().Ptr(), bytes, aMsg.BitDepth());
380
    } else {
381
        memcpy(buf, aMsg.Audio().Ptr(), bytes);
382
    }
383
384
put_audio_message:
385
    AudioMessage *ap = new 
386
        AudioMessage(aMsg.BitDepth(), aMsg.Channels(), aMsg.Samples(),
387
                     aMsg.SampleRate(), aMsg.Halt(), buf, allocbytes);
388
389
    // There is nothing special we can do if put fails: no way to
390
    // return status. Should we just exit ?
391
    if (!audioqueue.put(ap, false)) {
392
        LOGERR("sc2mpd: queue dead: exiting\n");
393
        exit(1);
394
    }
395
}
396
397
void OhmReceiverDriver::Process(OhmMsgTrack& aMsg)
398
{
399
    Brhz uri(aMsg.Uri());
400
    Brhz metadata(aMsg.Metadata());
401
    LOGDEB("OhmRcvDrv::Process:trk: TRACK SEQ " << aMsg.Sequence() <<
402
           " URI " << uri.CString() <<
403
           " METADATA " << metadata.CString() << endl);
404
}
405
406
void OhmReceiverDriver::Process(OhmMsgMetatext& aMsg)
407
{
408
    Brhz metatext(aMsg.Metatext());
409
    LOGDEB("OhmRcvDrv::Process:meta: METATEXT SEQUENCE " <<  aMsg.Sequence() <<
410
           " METATEXT " << metatext.CString() << endl);
411
}
412
170
413
#ifdef WITH_WAVSC2
171
#ifdef WITH_WAVSC2
414
static int playWav(const string& wavfile, AudioEater *eater,
172
static int playWav(const string& wavfile, AudioEater *eater,
415
                   AudioEater::Context *ctxt)
173
                   AudioEater::Context *ctxt)
416
{
174
{
...
...
487
}
245
}
488
#endif
246
#endif
489
247
490
#define SOCK_PATH "/tmp/sc2mpd.sock"
248
#define SOCK_PATH "/tmp/sc2mpd.sock"
491
#define BUF_SIZE 16
249
#define BUF_SIZE 16
492
void HandleUserCmd(OhmReceiver* receiver, OhmReceiverDriver* driver,
250
void HandleUserCmd(OhmReceiver* receiver, SongcastReceiver* driver,
493
                   AudioEater* audio, const Brx& aUri)
251
                   AudioEater* audio, const Brx& aUri)
494
{
252
{
495
    struct sockaddr_un server_addr, client_addr;
253
    struct sockaddr_un server_addr, client_addr;
496
    socklen_t len;
254
    socklen_t len;
497
    ssize_t num_bytes;
255
    ssize_t num_bytes;
...
...
678
           ((subnet >> 24) & 0xff) << endl);
436
           ((subnet >> 24) & 0xff) << endl);
679
437
680
    AudioEater* eater = optionDevice.Value() ?
438
    AudioEater* eater = optionDevice.Value() ?
681
                          &alsaAudioEater : &httpAudioEater;
439
                          &alsaAudioEater : &httpAudioEater;
682
440
683
    OhmReceiverDriver* driver = new OhmReceiverDriver(eater, ctxt);
441
    SongcastReceiver* driver = new SongcastReceiver(eater, ctxt);
684
442
685
    OhmReceiver* receiver = new OhmReceiver(lib->Env(), adapter, ttl, *driver);
443
    OhmReceiver* receiver = new OhmReceiver(lib->Env(), adapter, ttl, *driver);
686
444
687
    CpStack* cpStack = lib->StartCp(subnet);
445
    CpStack* cpStack = lib->StartCp(subnet);
688
    cpStack = cpStack; // avoid unused variable warning
446
    cpStack = cpStack; // avoid unused variable warning
...
...
722
            sleep(1000);
480
            sleep(1000);
723
        }
481
        }
724
    }
482
    }
725
483
726
    delete(receiver);
484
    delete(receiver);
727
485
    delete driver;
728
    delete lib;
486
    delete lib;
729
487
730
    if (optionInteract.Value())
488
    if (optionInteract.Value())
731
        printf("\n");
489
        printf("\n");
732
490