|
a/src/sc2mpd.cpp |
|
b/src/sc2mpd.cpp |
|
... |
|
... |
49 |
#include <sys/stat.h>
|
49 |
#include <sys/stat.h>
|
50 |
#include <fcntl.h>
|
50 |
#include <fcntl.h>
|
51 |
|
51 |
|
52 |
using namespace std;
|
52 |
using namespace std;
|
53 |
|
53 |
|
54 |
WorkQueue<AudioMessage*> audioqueue("audioqueue", 2);
|
54 |
WorkQueue<AudioMessage*> audioqueue("audioqueue", 4);
|
55 |
|
55 |
|
56 |
#ifdef _WIN32
|
56 |
#ifdef _WIN32
|
57 |
|
57 |
|
58 |
#pragma warning(disable:4355) // use of 'this' in ctor lists safe in this case
|
58 |
#pragma warning(disable:4355) // use of 'this' in ctor lists safe in this case
|
59 |
|
59 |
|
|
... |
|
... |
93 |
using namespace OpenHome::Av;
|
93 |
using namespace OpenHome::Av;
|
94 |
|
94 |
|
95 |
|
95 |
|
96 |
class OhmReceiverDriver : public IOhmReceiverDriver, public IOhmMsgProcessor {
|
96 |
class OhmReceiverDriver : public IOhmReceiverDriver, public IOhmMsgProcessor {
|
97 |
public:
|
97 |
public:
|
98 |
OhmReceiverDriver(int port);
|
98 |
OhmReceiverDriver(AudioEater* eater, int port);
|
99 |
|
99 |
|
100 |
private:
|
100 |
private:
|
101 |
// IOhmReceiverDriver
|
101 |
// IOhmReceiverDriver
|
102 |
virtual void Add(OhmMsg& aMsg);
|
102 |
virtual void Add(OhmMsg& aMsg);
|
103 |
virtual void Timestamp(OhmMsg& aMsg);
|
103 |
virtual void Timestamp(OhmMsg& aMsg);
|
|
... |
|
... |
135 |
iReset = true;
|
135 |
iReset = true;
|
136 |
}
|
136 |
}
|
137 |
|
137 |
|
138 |
void process(OhmMsgAudio& aMsg);
|
138 |
void process(OhmMsgAudio& aMsg);
|
139 |
};
|
139 |
};
|
140 |
Observer obs;
|
140 |
Observer m_obs;
|
|
|
141 |
AudioEater *m_eater;
|
141 |
};
|
142 |
};
|
142 |
|
143 |
|
143 |
OhmReceiverDriver::OhmReceiverDriver(int port)
|
144 |
OhmReceiverDriver::OhmReceiverDriver(AudioEater *eater, int port)
|
|
|
145 |
: m_eater(eater)
|
144 |
{
|
146 |
{
|
145 |
AudioEaterContext *ctxt = new AudioEaterContext(port);
|
147 |
AudioEater::Context *ctxt = new AudioEater::Context(&audioqueue, port);
|
146 |
audioqueue.start(1, &audioEater, ctxt);
|
148 |
audioqueue.start(1, m_eater->worker, ctxt);
|
147 |
}
|
149 |
}
|
148 |
|
150 |
|
149 |
void OhmReceiverDriver::Add(OhmMsg& aMsg)
|
151 |
void OhmReceiverDriver::Add(OhmMsg& aMsg)
|
150 |
{
|
152 |
{
|
151 |
aMsg.Process(*this);
|
153 |
aMsg.Process(*this);
|
|
... |
|
... |
161 |
LOGDEB("=== STARTED ====\n");
|
163 |
LOGDEB("=== STARTED ====\n");
|
162 |
}
|
164 |
}
|
163 |
|
165 |
|
164 |
void OhmReceiverDriver::Connected()
|
166 |
void OhmReceiverDriver::Connected()
|
165 |
{
|
167 |
{
|
166 |
obs.reset();
|
168 |
m_obs.reset();
|
167 |
LOGDEB("=== CONNECTED ====\n");
|
169 |
LOGDEB("=== CONNECTED ====\n");
|
168 |
}
|
170 |
}
|
169 |
|
171 |
|
170 |
void OhmReceiverDriver::Playing()
|
172 |
void OhmReceiverDriver::Playing()
|
171 |
{
|
173 |
{
|
|
... |
|
... |
182 |
void OhmReceiverDriver::Stopped()
|
184 |
void OhmReceiverDriver::Stopped()
|
183 |
{
|
185 |
{
|
184 |
LOGDEB("=== STOPPED ====\n");
|
186 |
LOGDEB("=== STOPPED ====\n");
|
185 |
}
|
187 |
}
|
186 |
|
188 |
|
|
|
189 |
// Debug and stats only, not needed for main function
|
187 |
void OhmReceiverDriver::Observer::process(OhmMsgAudio& aMsg)
|
190 |
void OhmReceiverDriver::Observer::process(OhmMsgAudio& aMsg)
|
188 |
{
|
191 |
{
|
189 |
if (++iCount == 400 || aMsg.Halt()) {
|
192 |
if (++iCount == 400 || aMsg.Halt()) {
|
190 |
static unsigned long long last_timestamp;
|
193 |
static unsigned long long last_timestamp;
|
191 |
unsigned long long timestamp = aMsg.MediaTimestamp();
|
194 |
unsigned long long timestamp = aMsg.MediaTimestamp();
|
|
... |
|
... |
252 |
}
|
255 |
}
|
253 |
}
|
256 |
}
|
254 |
|
257 |
|
255 |
void OhmReceiverDriver::Process(OhmMsgAudio& aMsg)
|
258 |
void OhmReceiverDriver::Process(OhmMsgAudio& aMsg)
|
256 |
{
|
259 |
{
|
257 |
|
|
|
258 |
if (aMsg.Audio().Bytes() == 0) {
|
260 |
if (aMsg.Audio().Bytes() == 0) {
|
259 |
LOGDEB("OhmReceiverDriver::Process: empty message\n");
|
261 |
LOGDEB("OhmReceiverDriver::Process: empty message\n");
|
260 |
return;
|
262 |
return;
|
261 |
}
|
263 |
}
|
|
|
264 |
|
|
|
265 |
m_obs.process(aMsg);
|
262 |
|
266 |
|
263 |
unsigned int bytes = aMsg.Audio().Bytes();
|
267 |
unsigned int bytes = aMsg.Audio().Bytes();
|
264 |
char *buf = (char *)malloc(bytes);
|
268 |
char *buf = (char *)malloc(bytes);
|
265 |
if (buf == 0) {
|
269 |
if (buf == 0) {
|
266 |
LOGERR("OhmReceiverDriver::Process: can't allocate " <<
|
270 |
LOGERR("OhmReceiverDriver::Process: can't allocate " <<
|
267 |
bytes << " bytes\n");
|
271 |
bytes << " bytes\n");
|
268 |
return;
|
272 |
return;
|
269 |
}
|
273 |
}
|
270 |
|
274 |
|
|
|
275 |
// Songcast data is always msb-first. Convert to desired order:
|
|
|
276 |
// depends on what downstream wants, and just as well we do it
|
|
|
277 |
// here because we copy the buf anyway.
|
|
|
278 |
bool needswap = false;
|
|
|
279 |
switch (m_eater->input_border) {
|
|
|
280 |
case AudioEater::BO_MSB: break;
|
|
|
281 |
case AudioEater::BO_LSB: needswap = true; break;
|
|
|
282 |
case AudioEater::BO_HOST:
|
271 |
#ifdef WORDS_BIGENDIAN
|
283 |
#ifdef WORDS_BIGENDIAN
|
272 |
memcpy(buf, aMsg.Audio().Ptr(), bytes);
|
284 |
needswap = false;
|
273 |
#else
|
285 |
#else
|
274 |
if (aMsg.BitDepth() == 16) {
|
286 |
needswap = true;
|
275 |
swab(aMsg.Audio().Ptr(), buf, bytes);
|
287 |
#endif
|
276 |
} else if (aMsg.BitDepth() == 24) {
|
288 |
}
|
|
|
289 |
|
|
|
290 |
if (needswap) {
|
277 |
unsigned char *ocp = (unsigned char *)buf;
|
291 |
unsigned char *ocp = (unsigned char *)buf;
|
|
|
292 |
const unsigned char *icp =
|
278 |
const unsigned char *icp = (const unsigned char *)aMsg.Audio().Ptr();
|
293 |
(const unsigned char *)aMsg.Audio().Ptr();
|
279 |
const unsigned char *icp0 = icp;
|
294 |
const unsigned char *icp0 = icp;
|
|
|
295 |
if (aMsg.BitDepth() == 16) {
|
|
|
296 |
swab(aMsg.Audio().Ptr(), buf, bytes);
|
|
|
297 |
} else if (aMsg.BitDepth() == 24) {
|
280 |
while (icp - icp0 <= int(bytes) - 3) {
|
298 |
while (icp - icp0 <= int(bytes) - 3) {
|
281 |
*ocp++ = icp[2];
|
299 |
*ocp++ = icp[2];
|
282 |
*ocp++ = icp[1];
|
300 |
*ocp++ = icp[1];
|
283 |
*ocp++ = *icp;
|
301 |
*ocp++ = *icp;
|
284 |
icp += 3;
|
302 |
icp += 3;
|
285 |
}
|
303 |
}
|
286 |
} else if (aMsg.BitDepth() == 32) {
|
304 |
} else if (aMsg.BitDepth() == 32) {
|
287 |
// Never seen this but whatever...
|
305 |
// Never seen this but whatever...
|
288 |
unsigned char *ocp = (unsigned char *)buf;
|
|
|
289 |
const unsigned char *icp = (const unsigned char *)aMsg.Audio().Ptr();
|
|
|
290 |
const unsigned char *icp0 = icp;
|
|
|
291 |
while (icp - icp0 <= int(bytes) - 4) {
|
306 |
while (icp - icp0 <= int(bytes) - 4) {
|
292 |
*ocp++ = icp[3];
|
307 |
*ocp++ = icp[3];
|
293 |
*ocp++ = icp[2];
|
308 |
*ocp++ = icp[2];
|
294 |
*ocp++ = icp[1];
|
309 |
*ocp++ = icp[1];
|
295 |
*ocp++ = *icp;
|
310 |
*ocp++ = *icp;
|
296 |
icp += 4;
|
311 |
icp += 4;
|
297 |
}
|
312 |
}
|
298 |
}
|
313 |
}
|
299 |
#endif
|
314 |
} else {
|
|
|
315 |
memcpy(buf, aMsg.Audio().Ptr(), bytes);
|
|
|
316 |
}
|
300 |
|
317 |
|
301 |
AudioMessage *ap = new
|
318 |
AudioMessage *ap = new
|
302 |
AudioMessage(aMsg.BitDepth(), aMsg.Channels(), aMsg.Samples(),
|
319 |
AudioMessage(aMsg.BitDepth(), aMsg.Channels(), aMsg.Samples(),
|
303 |
aMsg.SampleRate(), buf);
|
320 |
aMsg.SampleRate(), buf);
|
304 |
|
321 |
|
305 |
// There is nothing special we can do if put fails: no way to
|
322 |
// There is nothing special we can do if put fails: no way to
|
306 |
// return status. Should we just exit ?
|
323 |
// return status. Should we just exit ?
|
307 |
if (!audioqueue.put(ap, true)) {
|
324 |
if (!audioqueue.put(ap, false)) {
|
308 |
}
|
325 |
}
|
309 |
}
|
326 |
}
|
310 |
|
327 |
|
311 |
void OhmReceiverDriver::Process(OhmMsgTrack& aMsg)
|
328 |
void OhmReceiverDriver::Process(OhmMsgTrack& aMsg)
|
312 |
{
|
329 |
{
|
|
... |
|
... |
349 |
|
366 |
|
350 |
OptionString optionConfig("-c", "--config", Brn("/etc/upmpdcli.conf"),
|
367 |
OptionString optionConfig("-c", "--config", Brn("/etc/upmpdcli.conf"),
|
351 |
"[config] upmpdcli configuration file path");
|
368 |
"[config] upmpdcli configuration file path");
|
352 |
parser.AddOption(&optionConfig);
|
369 |
parser.AddOption(&optionConfig);
|
353 |
|
370 |
|
|
|
371 |
OptionBool optionDevice("-d", "--direct-alsa",
|
|
|
372 |
"[stream] Use alsa directly instead of producing "
|
|
|
373 |
"http stream");
|
|
|
374 |
parser.AddOption(&optionDevice);
|
|
|
375 |
|
354 |
if (!parser.Parse(aArgc, aArgv)) {
|
376 |
if (!parser.Parse(aArgc, aArgv)) {
|
355 |
return (1);
|
377 |
return (1);
|
356 |
}
|
378 |
}
|
357 |
|
379 |
|
358 |
InitialisationParams* initParams = InitialisationParams::Create();
|
380 |
InitialisationParams* initParams = InitialisationParams::Create();
|
|
... |
|
... |
398 |
|
420 |
|
399 |
LOGINF("scmpdcli: using subnet " << (subnet & 0xff) << "." <<
|
421 |
LOGINF("scmpdcli: using subnet " << (subnet & 0xff) << "." <<
|
400 |
((subnet >> 8) & 0xff) << "." << ((subnet >> 16) & 0xff) << "." <<
|
422 |
((subnet >> 8) & 0xff) << "." << ((subnet >> 16) & 0xff) << "." <<
|
401 |
((subnet >> 24) & 0xff) << endl);
|
423 |
((subnet >> 24) & 0xff) << endl);
|
402 |
|
424 |
|
403 |
OhmReceiverDriver* driver = new OhmReceiverDriver(port);
|
425 |
OhmReceiverDriver* driver = new OhmReceiverDriver(
|
|
|
426 |
optionDevice.Value() ? &alsaAudioEater : &httpAudioEater, port);
|
404 |
|
427 |
|
405 |
OhmReceiver* receiver = new OhmReceiver(lib->Env(), adapter, ttl, *driver);
|
428 |
OhmReceiver* receiver = new OhmReceiver(lib->Env(), adapter, ttl, *driver);
|
406 |
|
429 |
|
407 |
CpStack* cpStack = lib->StartCp(subnet);
|
430 |
CpStack* cpStack = lib->StartCp(subnet);
|
408 |
cpStack = cpStack; // avoid unused variable warning
|
431 |
cpStack = cpStack; // avoid unused variable warning
|