0.9beta8
- add an additional mutex to protect decode state which could lead to crashes
- fix problem skipping two tracks on windows
- change mp3 dll names on windows to match common sources
Adrian Smith
11 years ago
28 | 28 | extern struct streamstate stream; |
29 | 29 | extern struct outputstate output; |
30 | 30 | |
31 | struct decodestate decode; | |
32 | struct codec *codecs[MAX_CODECS]; | |
33 | static struct codec *codec; | |
34 | static bool running = true; | |
35 | ||
31 | 36 | #define LOCK_S mutex_lock(streambuf->mutex) |
32 | 37 | #define UNLOCK_S mutex_unlock(streambuf->mutex) |
33 | 38 | #define LOCK_O mutex_lock(outputbuf->mutex) |
34 | 39 | #define UNLOCK_O mutex_unlock(outputbuf->mutex) |
35 | ||
36 | struct decodestate decode; | |
37 | struct codec *codecs[MAX_CODECS]; | |
38 | static struct codec *codec; | |
39 | static bool running = true; | |
40 | #define LOCK_D mutex_lock(decode.mutex); | |
41 | #define UNLOCK_D mutex_unlock(decode.mutex); | |
40 | 42 | |
41 | 43 | static void *decode_thread() { |
42 | 44 | |
43 | 45 | while (running) { |
44 | 46 | size_t bytes, space; |
45 | 47 | bool toend; |
46 | decode_state state; | |
48 | bool ran = false; | |
47 | 49 | |
48 | 50 | LOCK_S; |
49 | 51 | bytes = _buf_used(streambuf); |
51 | 53 | UNLOCK_S; |
52 | 54 | LOCK_O; |
53 | 55 | space = _buf_space(outputbuf); |
54 | state = decode.state; | |
55 | 56 | UNLOCK_O; |
56 | 57 | |
57 | if (state == DECODE_RUNNING) { | |
58 | LOCK_D; | |
59 | ||
60 | if (decode.state == DECODE_RUNNING) { | |
58 | 61 | |
59 | 62 | LOG_SDEBUG("streambuf bytes: %u outputbuf space: %u", bytes, space); |
60 | 63 | |
61 | 64 | if (space > codec->min_space && (bytes > codec->min_read_bytes || toend)) { |
62 | 65 | |
63 | state = codec->decode(); | |
66 | decode.state = codec->decode(); | |
64 | 67 | |
65 | if (state != DECODE_RUNNING) { | |
68 | if (decode.state != DECODE_RUNNING) { | |
66 | 69 | |
67 | LOG_INFO("decode %s", state == DECODE_COMPLETE ? "complete" : "error"); | |
70 | LOG_INFO("decode %s", decode.state == DECODE_COMPLETE ? "complete" : "error"); | |
68 | 71 | |
69 | 72 | LOCK_O; |
70 | decode.state = state; | |
71 | 73 | if (output.fade_mode) _checkfade(false); |
72 | 74 | UNLOCK_O; |
73 | 75 | |
74 | 76 | wake_controller(); |
75 | 77 | } |
76 | 78 | |
77 | } else { | |
78 | usleep(100000); | |
79 | ran = true; | |
79 | 80 | } |
81 | } | |
82 | ||
83 | UNLOCK_D; | |
80 | 84 | |
81 | } else { | |
85 | if (!ran) { | |
82 | 86 | usleep(100000); |
83 | 87 | } |
84 | ||
85 | 88 | } |
86 | 89 | |
87 | 90 | return 0; |
108 | 111 | if ( !opt || strstr(opt, "mp3") || strstr(opt, "mad")) codecs[i] = register_mad(); |
109 | 112 | if ((!opt || strstr(opt, "mp3") || strstr(opt, "mpg")) && !codecs[i]) codecs[i] = register_mpg(); |
110 | 113 | |
114 | mutex_create(decode.mutex); | |
115 | ||
111 | 116 | #if LINUX || OSX |
112 | 117 | pthread_attr_t attr; |
113 | 118 | pthread_attr_init(&attr); |
125 | 130 | |
126 | 131 | void decode_close(void) { |
127 | 132 | LOG_INFO("close decode"); |
133 | LOCK_D; | |
128 | 134 | if (codec) { |
129 | 135 | codec->close(); |
136 | codec = NULL; | |
130 | 137 | } |
131 | codec = NULL; | |
132 | 138 | running = false; |
139 | UNLOCK_D; | |
140 | mutex_destroy(decode.mutex); | |
133 | 141 | } |
134 | 142 | |
135 | 143 | void codec_open(u8_t format, u8_t sample_size, u8_t sample_rate, u8_t channels, u8_t endianness) { |
137 | 145 | |
138 | 146 | LOG_INFO("codec open: '%c'", format); |
139 | 147 | |
140 | LOCK_O; | |
148 | LOCK_D; | |
149 | ||
141 | 150 | decode.new_stream = true; |
142 | 151 | decode.state = DECODE_STOPPED; |
143 | UNLOCK_O; | |
144 | 152 | |
145 | 153 | // find the required codec |
146 | 154 | for (i = 0; i < MAX_CODECS; ++i) { |
155 | 163 | codec = codecs[i]; |
156 | 164 | |
157 | 165 | codec->open(sample_size, sample_rate, channels, endianness); |
158 | ||
166 | ||
167 | UNLOCK_D; | |
159 | 168 | return; |
160 | 169 | } |
161 | 170 | } |
162 | 171 | |
172 | UNLOCK_D; | |
173 | ||
163 | 174 | LOG_ERROR("codec not found"); |
164 | 175 | } |
1301 | 1301 | output.latency = latency; |
1302 | 1302 | pa.stream = NULL; |
1303 | 1303 | |
1304 | LOG_INFO("requested latency: %u", output.latency); | |
1305 | ||
1304 | 1306 | if ((err = Pa_Initialize()) != paNoError) { |
1305 | 1307 | LOG_WARN("error initialising port audio: %s", Pa_GetErrorText(err)); |
1306 | 1308 | return; |
34 | 34 | #define LOCAL_PLAYER_PORT 0x0d9b // 3483 |
35 | 35 | #endif |
36 | 36 | |
37 | static sockfd sock; | |
37 | static sockfd sock = -1; | |
38 | 38 | static in_addr_t slimproto_ip; |
39 | 39 | |
40 | 40 | extern struct buffer *streambuf; |
52 | 52 | #define UNLOCK_S mutex_unlock(streambuf->mutex) |
53 | 53 | #define LOCK_O mutex_lock(outputbuf->mutex) |
54 | 54 | #define UNLOCK_O mutex_unlock(outputbuf->mutex) |
55 | #define LOCK_D mutex_lock(decode.mutex) | |
56 | #define UNLOCK_D mutex_unlock(decode.mutex) | |
55 | 57 | |
56 | 58 | static struct { |
57 | 59 | u32_t updated; |
93 | 95 | const char *base_cap = "Model=squeezelite,ModelName=SqueezeLite,AccuratePlayPoints=1,HasDigitalOut=1"; |
94 | 96 | struct HELO_packet pkt; |
95 | 97 | |
98 | memset(&pkt, 0, sizeof(pkt)); | |
96 | 99 | memcpy(&pkt.opcode, "HELO", 4); |
97 | 100 | pkt.length = htonl(sizeof(struct HELO_packet) - 8 + strlen(base_cap) + strlen(fixed_cap) + strlen(var_cap)); |
98 | 101 | pkt.deviceid = 12; // squeezeplay |
255 | 258 | LOCK_O; |
256 | 259 | output.state = jiffies ? OUTPUT_START_AT : OUTPUT_RUNNING; |
257 | 260 | output.start_at = jiffies; |
261 | UNLOCK_O; | |
262 | LOCK_D; | |
258 | 263 | decode.state = DECODE_RUNNING; |
259 | UNLOCK_O; | |
264 | UNLOCK_D; | |
260 | 265 | LOG_INFO("unpause at: %u now: %u", jiffies, gettime_ms()); |
261 | 266 | sendSTAT("STMr", 0); |
262 | 267 | } |
525 | 530 | output.pa_reopen = false; |
526 | 531 | } |
527 | 532 | #endif |
533 | if (output.state == OUTPUT_RUNNING && !sentSTMu && status.output_full == 0 && status.stream_state <= DISCONNECT) { | |
534 | _sendSTMu = true; | |
535 | sentSTMu = true; | |
536 | } | |
537 | if (output.state == OUTPUT_RUNNING && !sentSTMo && status.output_full == 0 && status.stream_state == STREAMING_HTTP) { | |
538 | _sendSTMo = true; | |
539 | sentSTMo = true; | |
540 | } | |
541 | UNLOCK_O; | |
542 | ||
543 | LOCK_D; | |
544 | if (decode.state == DECODE_RUNNING && now - status.last > 1000) { | |
545 | _sendSTMt = true; | |
546 | status.last = now; | |
547 | } | |
528 | 548 | if (decode.state == DECODE_COMPLETE) { |
529 | 549 | _sendSTMd = true; |
530 | 550 | decode.state = DECODE_STOPPED; |
540 | 560 | sentSTMl = true; |
541 | 561 | } else if (autostart == 1) { |
542 | 562 | decode.state = DECODE_RUNNING; |
563 | LOCK_O; | |
543 | 564 | if (output.state == OUTPUT_STOPPED) { |
544 | 565 | output.state = OUTPUT_BUFFER; |
545 | 566 | } |
567 | UNLOCK_O; | |
546 | 568 | } |
547 | 569 | // autostart 2 and 3 require cont to be received first |
548 | 570 | } |
549 | if (output.state == OUTPUT_RUNNING && !sentSTMu && status.output_full == 0 && status.stream_state <= DISCONNECT) { | |
550 | _sendSTMu = true; | |
551 | sentSTMu = true; | |
552 | } | |
553 | if (output.state == OUTPUT_RUNNING && !sentSTMo && status.output_full == 0 && status.stream_state == STREAMING_HTTP) { | |
554 | _sendSTMo = true; | |
555 | sentSTMo = true; | |
556 | } | |
557 | if (decode.state == DECODE_RUNNING && now - status.last > 1000) { | |
558 | _sendSTMt = true; | |
559 | status.last = now; | |
560 | } | |
561 | UNLOCK_O; | |
571 | UNLOCK_D; | |
562 | 572 | |
563 | 573 | // send packets once locks released as packet sending can block |
564 | 574 | if (_sendDSCO) sendDSCO(disconnect); |
17 | 17 | * |
18 | 18 | */ |
19 | 19 | |
20 | #define VERSION "v0.9beta7" | |
20 | #define VERSION "v0.9beta8" | |
21 | 21 | |
22 | 22 | // build detection |
23 | 23 | #if defined(linux) |
81 | 81 | |
82 | 82 | #if WIN |
83 | 83 | #define LIBFLAC "libFLAC.dll" |
84 | #define LIBMAD "libmad.dll" | |
85 | #define LIBMPG "libmpg123.dll" | |
84 | #define LIBMAD "libmad-0.dll" | |
85 | #define LIBMPG "libmpg123-0.dll" | |
86 | 86 | #define LIBVORBIS "libvorbisfile.dll" |
87 | 87 | #define LIBTREMOR "libvorbisidec.dll" |
88 | 88 | #define LIBFAAD "libfaad2.dll" |
338 | 338 | struct decodestate { |
339 | 339 | decode_state state; |
340 | 340 | bool new_stream; |
341 | mutex_type mutex; | |
341 | 342 | }; |
342 | 343 | |
343 | 344 | struct codec { |
98 | 98 | |
99 | 99 | LOCK; |
100 | 100 | |
101 | // check socket has not been closed while in poll | |
102 | if (fd < 0) { | |
103 | UNLOCK; | |
104 | continue; | |
105 | } | |
106 | ||
101 | 107 | if ((pollinfo.revents & POLLOUT) && stream.state == SEND_HEADERS) { |
102 | 108 | send_header(); |
103 | 109 | stream.header_len = 0; |
375 | 381 | |
376 | 382 | void stream_disconnect(void) { |
377 | 383 | LOCK; |
378 | closesocket(fd); | |
379 | fd = -1; | |
384 | if (fd != -1) { | |
385 | closesocket(fd); | |
386 | fd = -1; | |
387 | } | |
380 | 388 | stream.state = STOPPED; |
381 | 389 | UNLOCK; |
382 | 390 | } |