Support for vorbis decode - via libvorbisfile or libvorbisidec (tremor)
Reduce memory footprint slightly for non loaded codecs by reducing use of golobal/static vars
Adrian Smith
11 years ago
0 | 0 | CFLAGS = -Wall -fPIC -O2 |
1 | LDFLAGS = -lasound -lpthread | |
1 | LDFLAGS = -lasound -lpthread -ldl -lrt | |
2 | 2 | |
3 | 3 | all: squeezelite |
4 | 4 | |
5 | squeezelite: main.o slimproto.o utils.o output.o buffer.o stream.o decode.o flac.o pcm.o mad.o | |
6 | $(CC) $(CFLAGS) main.o slimproto.o utils.o output.o buffer.o stream.o decode.o flac.o pcm.o mad.o -o squeezelite $(LDFLAGS) | |
5 | squeezelite: main.o slimproto.o utils.o output.o buffer.o stream.o decode.o flac.o pcm.o mad.o vorbis.o | |
6 | $(CC) $(CFLAGS) main.o slimproto.o utils.o output.o buffer.o stream.o decode.o flac.o pcm.o mad.o vorbis.o $(LDFLAGS) -o squeezelite | |
7 | 7 | |
8 | 8 | main.o: main.c squeezelite.h |
9 | 9 | $(CC) $(CFLAGS) -c main.c -o main.o |
35 | 35 | mad.o: mad.c squeezelite.h |
36 | 36 | $(CC) $(CFLAGS) -c mad.c -o mad.o |
37 | 37 | |
38 | vorbis.o: vorbis.c squeezelite.h | |
39 | $(CC) $(CFLAGS) -c vorbis.c -o vorbis.o | |
40 |
88 | 88 | LOG_INFO("init decode"); |
89 | 89 | |
90 | 90 | // register codecs |
91 | if (!opt || !strcmp(opt, "flac")) codecs[0] = register_flac(); | |
92 | if (!opt || !strcmp(opt, "pcm")) codecs[1] = register_pcm(); | |
93 | if (!opt || !strcmp(opt, "mp3")) codecs[2] = register_mad(); | |
91 | // alc,wma,wmap,wmal,aac,spt,ogg,ogf,flc,aif,pcm,mp3 | |
92 | if (!opt || !strcmp(opt, "ogg")) codecs[0] = register_vorbis(); | |
93 | if (!opt || !strcmp(opt, "flac")) codecs[1] = register_flac(); | |
94 | if (!opt || !strcmp(opt, "pcm")) codecs[2] = register_pcm(); | |
95 | if (!opt || !strcmp(opt, "mp3")) codecs[3] = register_mad(); | |
94 | 96 | |
95 | 97 | pthread_attr_t attr; |
96 | 98 | pthread_attr_init(&attr); |
22 | 22 | #include <FLAC/stream_decoder.h> |
23 | 23 | #include <dlfcn.h> |
24 | 24 | |
25 | #define LIBFLAC "libFLAC.so.8" | |
26 | ||
25 | 27 | // FLAC symbols to be dynamically loaded |
26 | FLAC_API const char **f__StreamDecoderErrorStatusString; | |
27 | FLAC_API const char **f__StreamDecoderStateString; | |
28 | FLAC_API FLAC__StreamDecoder * (* f__stream_decoder_new)(void); | |
29 | FLAC_API FLAC__bool (* f__stream_decoder_reset)(FLAC__StreamDecoder *decoder); | |
30 | FLAC_API void (* f__stream_decoder_delete)(FLAC__StreamDecoder *decoder); | |
31 | FLAC_API FLAC__StreamDecoderInitStatus (* f__stream_decoder_init_stream)( | |
32 | FLAC__StreamDecoder *decoder, | |
33 | FLAC__StreamDecoderReadCallback read_callback, | |
34 | FLAC__StreamDecoderSeekCallback seek_callback, | |
35 | FLAC__StreamDecoderTellCallback tell_callback, | |
36 | FLAC__StreamDecoderLengthCallback length_callback, | |
37 | FLAC__StreamDecoderEofCallback eof_callback, | |
38 | FLAC__StreamDecoderWriteCallback write_callback, | |
39 | FLAC__StreamDecoderMetadataCallback metadata_callback, | |
40 | FLAC__StreamDecoderErrorCallback error_callback, | |
41 | void *client_data | |
42 | ); | |
43 | FLAC_API FLAC__bool (* f__stream_decoder_process_single)(FLAC__StreamDecoder *decoder); | |
44 | FLAC_API FLAC__StreamDecoderState (* f__stream_decoder_get_state)(const FLAC__StreamDecoder *decoder); | |
45 | // end of FLAC symbols | |
28 | struct flac { | |
29 | FLAC__StreamDecoder *decoder; | |
30 | FLAC_API const char **FLAC__StreamDecoderErrorStatusString; | |
31 | FLAC_API const char **FLAC__StreamDecoderStateString; | |
32 | FLAC_API FLAC__StreamDecoder * (* FLAC__stream_decoder_new)(void); | |
33 | FLAC_API FLAC__bool (* FLAC__stream_decoder_reset)(FLAC__StreamDecoder *decoder); | |
34 | FLAC_API void (* FLAC__stream_decoder_delete)(FLAC__StreamDecoder *decoder); | |
35 | FLAC_API FLAC__StreamDecoderInitStatus (* FLAC__stream_decoder_init_stream)( | |
36 | FLAC__StreamDecoder *decoder, | |
37 | FLAC__StreamDecoderReadCallback read_callback, | |
38 | FLAC__StreamDecoderSeekCallback seek_callback, | |
39 | FLAC__StreamDecoderTellCallback tell_callback, | |
40 | FLAC__StreamDecoderLengthCallback length_callback, | |
41 | FLAC__StreamDecoderEofCallback eof_callback, | |
42 | FLAC__StreamDecoderWriteCallback write_callback, | |
43 | FLAC__StreamDecoderMetadataCallback metadata_callback, | |
44 | FLAC__StreamDecoderErrorCallback error_callback, | |
45 | void *client_data | |
46 | ); | |
47 | FLAC_API FLAC__bool (* FLAC__stream_decoder_process_single)(FLAC__StreamDecoder *decoder); | |
48 | FLAC_API FLAC__StreamDecoderState (* FLAC__stream_decoder_get_state)(const FLAC__StreamDecoder *decoder); | |
49 | }; | |
50 | ||
51 | static struct flac *f; | |
46 | 52 | |
47 | 53 | extern log_level loglevel; |
48 | 54 | |
50 | 56 | extern struct buffer *outputbuf; |
51 | 57 | extern struct streamstate stream; |
52 | 58 | extern struct outputstate output; |
53 | ||
54 | struct decodestate decode; | |
59 | extern struct decodestate decode; | |
55 | 60 | |
56 | 61 | #define LOCK_S pthread_mutex_lock(&streambuf->mutex) |
57 | 62 | #define UNLOCK_S pthread_mutex_unlock(&streambuf->mutex) |
59 | 64 | #define UNLOCK_O pthread_mutex_unlock(&outputbuf->mutex) |
60 | 65 | |
61 | 66 | typedef u_int32_t frames_t; |
62 | ||
63 | ||
64 | static FLAC__StreamDecoder *decoder = NULL; | |
65 | 67 | |
66 | 68 | static FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *want, void *client_data) { |
67 | 69 | LOCK_S; |
128 | 130 | } |
129 | 131 | |
130 | 132 | static void error_cb(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { |
131 | LOG_INFO("flac error: %s", f__StreamDecoderErrorStatusString[status]); | |
133 | LOG_INFO("flac error: %s", f->FLAC__StreamDecoderErrorStatusString[status]); | |
132 | 134 | } |
133 | 135 | |
134 | 136 | static void flac_open(u8_t sample_size, u8_t sample_rate, u8_t channels, u8_t endianness) { |
135 | if (decoder) { | |
136 | f__stream_decoder_reset(decoder); | |
137 | if (f->decoder) { | |
138 | f->FLAC__stream_decoder_reset(f->decoder); | |
137 | 139 | } else { |
138 | decoder = f__stream_decoder_new(); | |
139 | } | |
140 | f__stream_decoder_init_stream(decoder, &read_cb, NULL, NULL, NULL, NULL, &write_cb, NULL, &error_cb, NULL); | |
140 | f->decoder = f->FLAC__stream_decoder_new(); | |
141 | } | |
142 | f->FLAC__stream_decoder_init_stream(f->decoder, &read_cb, NULL, NULL, NULL, NULL, &write_cb, NULL, &error_cb, NULL); | |
141 | 143 | } |
142 | 144 | |
143 | 145 | static void flac_close(void) { |
144 | f__stream_decoder_delete(decoder); | |
145 | decoder = NULL; | |
146 | f->FLAC__stream_decoder_delete(f->decoder); | |
147 | f->decoder = NULL; | |
146 | 148 | } |
147 | 149 | |
148 | 150 | static void flac_decode(void) { |
149 | if (!f__stream_decoder_process_single(decoder)) { | |
150 | FLAC__StreamDecoderState state = f__stream_decoder_get_state(decoder); | |
151 | LOG_ERROR("flac error: %s", f__StreamDecoderStateString[state]); | |
151 | if (!f->FLAC__stream_decoder_process_single(f->decoder)) { | |
152 | FLAC__StreamDecoderState state = f->FLAC__stream_decoder_get_state(f->decoder); | |
153 | LOG_ERROR("flac error: %s", f->FLAC__StreamDecoderStateString[state]); | |
152 | 154 | }; |
153 | 155 | } |
154 | 156 | |
155 | 157 | static bool load_flac() { |
156 | void *handle = dlopen("libFLAC.so.8", RTLD_NOW); | |
158 | void *handle = dlopen(LIBFLAC, RTLD_NOW); | |
157 | 159 | if (!handle) { |
158 | 160 | LOG_WARN("dlerror: %s", dlerror()); |
159 | 161 | return false; |
160 | 162 | } |
161 | ||
162 | f__StreamDecoderErrorStatusString = dlsym(handle, "FLAC__StreamDecoderErrorStatusString"); | |
163 | f__StreamDecoderStateString = dlsym(handle, "FLAC__StreamDecoderStateString"); | |
164 | ||
165 | f__stream_decoder_new = dlsym(handle, "FLAC__stream_decoder_new"); | |
166 | f__stream_decoder_reset = dlsym(handle, "FLAC__stream_decoder_reset"); | |
167 | f__stream_decoder_delete = dlsym(handle, "FLAC__stream_decoder_delete"); | |
168 | ||
169 | f__stream_decoder_init_stream = dlsym(handle, "FLAC__stream_decoder_init_stream"); | |
170 | f__stream_decoder_process_single = dlsym(handle, "FLAC__stream_decoder_process_single"); | |
171 | f__stream_decoder_get_state = dlsym(handle, "FLAC__stream_decoder_get_state"); | |
163 | ||
164 | f = malloc(sizeof(struct flac)); | |
165 | ||
166 | f->decoder = NULL; | |
167 | f->FLAC__StreamDecoderErrorStatusString = dlsym(handle, "FLAC__StreamDecoderErrorStatusString"); | |
168 | f->FLAC__StreamDecoderStateString = dlsym(handle, "FLAC__StreamDecoderStateString"); | |
169 | f->FLAC__stream_decoder_new = dlsym(handle, "FLAC__stream_decoder_new"); | |
170 | f->FLAC__stream_decoder_reset = dlsym(handle, "FLAC__stream_decoder_reset"); | |
171 | f->FLAC__stream_decoder_delete = dlsym(handle, "FLAC__stream_decoder_delete"); | |
172 | f->FLAC__stream_decoder_init_stream = dlsym(handle, "FLAC__stream_decoder_init_stream"); | |
173 | f->FLAC__stream_decoder_process_single = dlsym(handle, "FLAC__stream_decoder_process_single"); | |
174 | f->FLAC__stream_decoder_get_state = dlsym(handle, "FLAC__stream_decoder_get_state"); | |
172 | 175 | |
173 | 176 | char *err; |
174 | 177 | if ((err = dlerror()) != NULL) { |
176 | 179 | return false; |
177 | 180 | } |
178 | 181 | |
179 | LOG_INFO("loaded libFLAC"); | |
182 | LOG_INFO("loaded "LIBFLAC); | |
180 | 183 | return true; |
181 | 184 | } |
182 | 185 |
22 | 22 | #include <mad.h> |
23 | 23 | #include <dlfcn.h> |
24 | 24 | |
25 | // mad symbols to be dynamically loaded | |
26 | void (* m_stream_init)(struct mad_stream *); | |
27 | void (* m_frame_init)(struct mad_frame *); | |
28 | void (* m_synth_init)(struct mad_synth *); | |
29 | void (* m_frame_finish)(struct mad_frame *); | |
30 | void (* m_stream_finish)(struct mad_stream *); | |
31 | void (* m_stream_buffer)(struct mad_stream *, unsigned char const *, unsigned long); | |
32 | int (* m_frame_decode)(struct mad_frame *, struct mad_stream *); | |
33 | void (* m_synth_frame)(struct mad_synth *, struct mad_frame const *); | |
34 | char const *(* m_stream_errorstr)(struct mad_stream const *); | |
35 | // end of mad symbols | |
25 | #define READBUF_SIZE 2048 // local buffer used by decoder: FIXME merge with any other decoders needing one? | |
26 | ||
27 | #define LIBMAD "libmad.so.0" | |
28 | ||
29 | struct mad { | |
30 | u8_t *readbuf; | |
31 | unsigned readbuf_len; | |
32 | struct mad_stream stream; | |
33 | struct mad_frame frame; | |
34 | struct mad_synth synth; | |
35 | // mad symbols to be dynamically loaded | |
36 | void (* mad_stream_init)(struct mad_stream *); | |
37 | void (* mad_frame_init)(struct mad_frame *); | |
38 | void (* mad_synth_init)(struct mad_synth *); | |
39 | void (* mad_frame_finish)(struct mad_frame *); | |
40 | void (* mad_stream_finish)(struct mad_stream *); | |
41 | void (* mad_stream_buffer)(struct mad_stream *, unsigned char const *, unsigned long); | |
42 | int (* mad_frame_decode)(struct mad_frame *, struct mad_stream *); | |
43 | void (* mad_synth_frame)(struct mad_synth *, struct mad_frame const *); | |
44 | char const *(* mad_stream_errorstr)(struct mad_stream const *); | |
45 | }; | |
46 | ||
47 | static struct mad *m; | |
36 | 48 | |
37 | 49 | extern log_level loglevel; |
38 | 50 | |
40 | 52 | extern struct buffer *outputbuf; |
41 | 53 | extern struct streamstate stream; |
42 | 54 | extern struct outputstate output; |
43 | ||
44 | struct decodestate decode; | |
55 | extern struct decodestate decode; | |
45 | 56 | |
46 | 57 | #define LOCK_S pthread_mutex_lock(&streambuf->mutex) |
47 | 58 | #define UNLOCK_S pthread_mutex_unlock(&streambuf->mutex) |
48 | 59 | #define LOCK_O pthread_mutex_lock(&outputbuf->mutex) |
49 | 60 | #define UNLOCK_O pthread_mutex_unlock(&outputbuf->mutex) |
50 | ||
51 | #define READBUF_SIZE 2048 | |
52 | ||
53 | static struct mad_stream mad_stream; | |
54 | static struct mad_frame mad_frame; | |
55 | static struct mad_synth mad_synth; | |
56 | static u8_t readbuf[READBUF_SIZE + MAD_BUFFER_GUARD]; | |
57 | static unsigned readbuf_len; | |
58 | 61 | |
59 | 62 | // based on libmad minimad.c scale |
60 | 63 | static inline u32_t scale(mad_fixed_t sample) { |
72 | 75 | LOCK_S; |
73 | 76 | size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); |
74 | 77 | |
75 | if (mad_stream.next_frame && readbuf_len) { | |
76 | readbuf_len -= mad_stream.next_frame - readbuf; | |
77 | memmove(readbuf, mad_stream.next_frame, readbuf_len); | |
78 | } | |
79 | ||
80 | bytes = min(bytes, READBUF_SIZE - readbuf_len); | |
81 | memcpy(readbuf + readbuf_len, streambuf->readp, bytes); | |
82 | readbuf_len += bytes; | |
78 | if (m->stream.next_frame && m->readbuf_len) { | |
79 | m->readbuf_len -= m->stream.next_frame - m->readbuf; | |
80 | memmove(m->readbuf, m->stream.next_frame, m->readbuf_len); | |
81 | } | |
82 | ||
83 | bytes = min(bytes, READBUF_SIZE - m->readbuf_len); | |
84 | memcpy(m->readbuf + m->readbuf_len, streambuf->readp, bytes); | |
85 | m->readbuf_len += bytes; | |
83 | 86 | _buf_inc_readp(streambuf, bytes); |
84 | 87 | |
85 | 88 | if (stream.state <= DISCONNECT && _buf_used(streambuf) == 0) { |
86 | memset(readbuf + readbuf_len, 0, MAD_BUFFER_GUARD); | |
87 | readbuf_len += MAD_BUFFER_GUARD; | |
89 | memset(m->readbuf + m->readbuf_len, 0, MAD_BUFFER_GUARD); | |
90 | m->readbuf_len += MAD_BUFFER_GUARD; | |
88 | 91 | } |
89 | 92 | UNLOCK_S; |
90 | 93 | |
91 | m_stream_buffer(&mad_stream, readbuf, readbuf_len); | |
94 | m->mad_stream_buffer(&m->stream, m->readbuf, m->readbuf_len); | |
92 | 95 | |
93 | 96 | while (true) { |
94 | 97 | |
95 | if (m_frame_decode(&mad_frame, &mad_stream) == -1) { | |
96 | if (mad_stream.error == MAD_ERROR_BUFLEN) { | |
98 | if (m->mad_frame_decode(&m->frame, &m->stream) == -1) { | |
99 | if (m->stream.error == MAD_ERROR_BUFLEN) { | |
97 | 100 | return; |
98 | 101 | } |
99 | if (!MAD_RECOVERABLE(mad_stream.error)) { | |
100 | LOG_WARN("mad_frame_decode error: %s", m_stream_errorstr(&mad_stream)); | |
102 | if (!MAD_RECOVERABLE(m->stream.error)) { | |
103 | LOG_WARN("mad_frame_decode error: %s", m->mad_stream_errorstr(&m->stream)); | |
101 | 104 | LOG_INFO("unrecoverable - stopping decoder"); |
102 | 105 | LOCK_O; |
103 | 106 | decode.state = DECODE_COMPLETE; |
104 | 107 | UNLOCK_O; |
105 | 108 | } else { |
106 | LOG_DEBUG("mad_frame_decode error: %s", m_stream_errorstr(&mad_stream)); | |
109 | LOG_DEBUG("mad_frame_decode error: %s", m->mad_stream_errorstr(&m->stream)); | |
107 | 110 | } |
108 | 111 | return; |
109 | 112 | }; |
110 | 113 | |
111 | m_synth_frame(&mad_synth, &mad_frame); | |
114 | m->mad_synth_frame(&m->synth, &m->frame); | |
112 | 115 | |
113 | 116 | LOCK_O; |
114 | 117 | |
115 | 118 | if (decode.new_stream) { |
116 | 119 | LOG_INFO("setting track_start"); |
117 | output.next_sample_rate = mad_synth.pcm.samplerate; | |
120 | output.next_sample_rate = m->synth.pcm.samplerate; | |
118 | 121 | output.track_start = outputbuf->writep; |
119 | 122 | decode.new_stream = false; |
120 | 123 | } |
121 | 124 | |
122 | if (mad_synth.pcm.length > _buf_space(outputbuf) / BYTES_PER_FRAME) { | |
125 | if (m->synth.pcm.length > _buf_space(outputbuf) / BYTES_PER_FRAME) { | |
123 | 126 | LOG_WARN("too many samples - dropping samples"); |
124 | mad_synth.pcm.length = _buf_space(outputbuf) / BYTES_PER_FRAME; | |
127 | m->synth.pcm.length = _buf_space(outputbuf) / BYTES_PER_FRAME; | |
125 | 128 | } |
126 | 129 | |
127 | size_t frames = mad_synth.pcm.length; | |
128 | s32_t *iptrl = mad_synth.pcm.samples[0]; | |
129 | s32_t *iptrr = mad_synth.pcm.samples[ mad_synth.pcm.channels - 1 ]; | |
130 | size_t frames = m->synth.pcm.length; | |
131 | s32_t *iptrl = m->synth.pcm.samples[0]; | |
132 | s32_t *iptrr = m->synth.pcm.samples[ m->synth.pcm.channels - 1 ]; | |
130 | 133 | |
131 | 134 | while (frames > 0) { |
132 | 135 | size_t f = min(frames, _buf_cont_write(outputbuf) / BYTES_PER_FRAME); |
140 | 143 | _buf_inc_writep(outputbuf, f * BYTES_PER_FRAME); |
141 | 144 | } |
142 | 145 | |
143 | LOG_SDEBUG("write %u frames", mad_synth.pcm.length); | |
146 | LOG_SDEBUG("write %u frames", m->synth.pcm.length); | |
144 | 147 | |
145 | 148 | UNLOCK_O; |
146 | 149 | } |
147 | 150 | } |
148 | 151 | |
149 | 152 | static void mad_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { |
150 | readbuf_len = 0; | |
151 | m_stream_init(&mad_stream); | |
152 | m_frame_init(&mad_frame); | |
153 | m_synth_init(&mad_synth); | |
153 | m->readbuf_len = 0; | |
154 | m->mad_stream_init(&m->stream); | |
155 | m->mad_frame_init(&m->frame); | |
156 | m->mad_synth_init(&m->synth); | |
154 | 157 | } |
155 | 158 | |
156 | 159 | static void mad_close(void) { |
157 | mad_synth_finish(&mad_synth); | |
158 | m_frame_finish(&mad_frame); | |
159 | m_stream_finish(&mad_stream); | |
160 | mad_synth_finish(&m->synth); | |
161 | m->mad_frame_finish(&m->frame); | |
162 | m->mad_stream_finish(&m->stream); | |
160 | 163 | } |
161 | 164 | |
162 | 165 | static bool load_mad() { |
163 | void *handle = dlopen("libmad.so.0", RTLD_NOW); | |
166 | void *handle = dlopen(LIBMAD, RTLD_NOW); | |
164 | 167 | if (!handle) { |
165 | 168 | LOG_WARN("dlerror: %s", dlerror()); |
166 | 169 | return false; |
167 | 170 | } |
168 | 171 | |
169 | m_stream_init = dlsym(handle, "mad_stream_init"); | |
170 | m_frame_init = dlsym(handle, "mad_frame_init"); | |
171 | m_synth_init = dlsym(handle, "mad_synth_init"); | |
172 | m_frame_finish = dlsym(handle, "mad_frame_finish"); | |
173 | m_stream_finish = dlsym(handle, "mad_stream_finish"); | |
174 | m_stream_buffer = dlsym(handle, "mad_stream_buffer"); | |
175 | m_frame_decode = dlsym(handle, "mad_frame_decode"); | |
176 | m_synth_frame = dlsym(handle, "mad_synth_frame"); | |
177 | m_stream_errorstr = dlsym(handle, "mad_stream_errorstr"); | |
172 | m = malloc(sizeof(struct mad)); | |
173 | m->readbuf = malloc(READBUF_SIZE + MAD_BUFFER_GUARD); | |
174 | ||
175 | m->readbuf_len = 0; | |
176 | m->mad_stream_init = dlsym(handle, "mad_stream_init"); | |
177 | m->mad_frame_init = dlsym(handle, "mad_frame_init"); | |
178 | m->mad_synth_init = dlsym(handle, "mad_synth_init"); | |
179 | m->mad_frame_finish = dlsym(handle, "mad_frame_finish"); | |
180 | m->mad_stream_finish = dlsym(handle, "mad_stream_finish"); | |
181 | m->mad_stream_buffer = dlsym(handle, "mad_stream_buffer"); | |
182 | m->mad_frame_decode = dlsym(handle, "mad_frame_decode"); | |
183 | m->mad_synth_frame = dlsym(handle, "mad_synth_frame"); | |
184 | m->mad_stream_errorstr = dlsym(handle, "mad_stream_errorstr"); | |
178 | 185 | |
179 | 186 | char *err; |
180 | 187 | if ((err = dlerror()) != NULL) { |
182 | 189 | return false; |
183 | 190 | } |
184 | 191 | |
185 | LOG_INFO("loaded libmad"); | |
192 | LOG_INFO("loaded "LIBMAD); | |
186 | 193 | return true; |
187 | 194 | } |
188 | 195 |
626 | 626 | loglevel = level; |
627 | 627 | |
628 | 628 | LOG_INFO("init output"); |
629 | ||
630 | output_buf_size = output_buf_size - (output_buf_size % BYTES_PER_FRAME); | |
629 | 631 | LOG_DEBUG("outputbuf size: %u", output_buf_size); |
630 | 632 | |
631 | 633 | buf_init(outputbuf, output_buf_size); |
25 | 25 | extern struct buffer *outputbuf; |
26 | 26 | extern struct streamstate stream; |
27 | 27 | extern struct outputstate output; |
28 | ||
29 | struct decodestate decode; | |
28 | extern struct decodestate decode; | |
30 | 29 | |
31 | 30 | #define LOCK_S pthread_mutex_lock(&streambuf->mutex) |
32 | 31 | #define UNLOCK_S pthread_mutex_unlock(&streambuf->mutex) |
128 | 127 | struct codec *register_pcm(void) { |
129 | 128 | static struct codec ret = { |
130 | 129 | .id = 'p', |
131 | .types = "pcm,aif", | |
130 | .types = "aif,pcm", | |
132 | 131 | .open = pcm_open, |
133 | 132 | .close = pcm_close, |
134 | 133 | .decode= pcm_decode, |
42 | 42 | #define MAX_HEADER 2048 |
43 | 43 | |
44 | 44 | #define STREAM_THREAD_STACK_SIZE (PTHREAD_STACK_MIN * 4) |
45 | #define DECODE_THREAD_STACK_SIZE (PTHREAD_STACK_MIN * 4) | |
45 | #define DECODE_THREAD_STACK_SIZE (PTHREAD_STACK_MIN * 8) | |
46 | 46 | #define OUTPUT_THREAD_STACK_SIZE (PTHREAD_STACK_MIN * 4) |
47 | 47 | |
48 | 48 | #define ALSA_BUFFER_TIME 20000 |
100 | 100 | void buf_destroy(struct buffer *buf); |
101 | 101 | |
102 | 102 | // slimproto.c |
103 | void slimproto(log_level level, const char *addr, u8_t mac[6]); | |
103 | void slimproto(log_level level, const char *addr, u8_t mac[6], const char *name); | |
104 | 104 | void wake_controller(void); |
105 | 105 | |
106 | 106 | // stream.c |
185 | 185 | struct codec *register_flac(void); |
186 | 186 | struct codec *register_pcm(void); |
187 | 187 | struct codec *register_mad(void); |
188 | struct codec *register_vorbis(void); |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezeplay emulator for linux | |
2 | * | |
3 | * (c) Adrian Smith 2012, triode1@btinternet.com | |
4 | * | |
5 | * This program is free software: you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 3 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | * | |
18 | */ | |
19 | ||
20 | #include "squeezelite.h" | |
21 | ||
22 | // automatically select between floating point (preferred) and fixed point libraries: | |
23 | #define LIBVORBIS "libvorbisfile.so.3" | |
24 | #define LIBTREMOR "libvorbisidec.so.1" | |
25 | ||
26 | // NOTE: works with Tremor version here: http://svn.xiph.org/trunk/Tremor, not vorbisidec.1.0.2 currently in ubuntu | |
27 | ||
28 | // we take common definations from <vorbis/vorbisfile.h> even though we can use tremor at run time | |
29 | // tremor's OggVorbis_File struct is normally smaller so this is ok, but padding added to malloc in case it is bigger | |
30 | #define OV_EXCLUDE_STATIC_CALLBACKS | |
31 | ||
32 | #include <vorbis/vorbisfile.h> | |
33 | #include <dlfcn.h> | |
34 | ||
35 | struct vorbis { | |
36 | OggVorbis_File *vf; | |
37 | // vorbis symbols to be dynamically loaded - from either vorbisfile or vorbisidec (tremor) version of library | |
38 | vorbis_info *(* ov_info)(OggVorbis_File *vf, int link); | |
39 | int (* ov_clear)(OggVorbis_File *vf); | |
40 | long (* ov_read)(OggVorbis_File *vf, char *buffer, int length, int bigendianp, int word, int sgned, int *bitstream); | |
41 | long (* ov_read_tremor)(OggVorbis_File *vf, char *buffer, int length, int *bitstream); | |
42 | int (* ov_open_callbacks)(void *datasource, OggVorbis_File *vf, const char *initial, long ibytes, ov_callbacks callbacks); | |
43 | }; | |
44 | ||
45 | static struct vorbis *v; | |
46 | ||
47 | extern log_level loglevel; | |
48 | ||
49 | extern struct buffer *streambuf; | |
50 | extern struct buffer *outputbuf; | |
51 | extern struct streamstate stream; | |
52 | extern struct outputstate output; | |
53 | extern struct decodestate decode; | |
54 | ||
55 | #define LOCK_S pthread_mutex_lock(&streambuf->mutex) | |
56 | #define UNLOCK_S pthread_mutex_unlock(&streambuf->mutex) | |
57 | #define LOCK_O pthread_mutex_lock(&outputbuf->mutex) | |
58 | #define UNLOCK_O pthread_mutex_unlock(&outputbuf->mutex) | |
59 | ||
60 | typedef u_int32_t frames_t; | |
61 | ||
62 | static size_t _read(void *ptr, size_t size, size_t nmemb, void *datasource) { | |
63 | LOCK_S; | |
64 | size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); | |
65 | bytes = min(bytes, size * nmemb); | |
66 | ||
67 | memcpy(ptr, streambuf->readp, bytes); | |
68 | _buf_inc_readp(streambuf, bytes); | |
69 | UNLOCK_S; | |
70 | ||
71 | return bytes / size; | |
72 | } | |
73 | ||
74 | // these are needed for older versions of tremor, later versions and libvorbis allow NULL to be used | |
75 | static int _seek(void *datasource, ogg_int64_t offset, int whence) { return -1; } | |
76 | static int _close(void *datasource) { return 0; } | |
77 | static long _tell(void *datasource) { return 0; } | |
78 | ||
79 | static void vorbis_decode(void) { | |
80 | static int channels; | |
81 | ||
82 | LOCK_O; | |
83 | ||
84 | if (decode.new_stream) { | |
85 | ov_callbacks cbs = { .read_func = _read, .seek_func = NULL, .close_func = NULL, .tell_func = NULL }; | |
86 | if (v->ov_read_tremor) { | |
87 | cbs.seek_func = _seek; cbs.close_func = _close; cbs.tell_func = _tell; | |
88 | } | |
89 | ||
90 | int err; | |
91 | if ((err = v->ov_open_callbacks(streambuf, v->vf, NULL, 0, cbs)) < 0) { | |
92 | LOG_WARN("open_callbacks error: %d", err); | |
93 | decode.state = DECODE_COMPLETE; | |
94 | UNLOCK_O; | |
95 | return; | |
96 | } | |
97 | ||
98 | struct vorbis_info *info = v->ov_info(v->vf, -1); | |
99 | ||
100 | LOG_INFO("setting track_start"); | |
101 | output.next_sample_rate = info->rate; | |
102 | output.track_start = outputbuf->writep; | |
103 | decode.new_stream = false; | |
104 | ||
105 | channels = info->channels; | |
106 | ||
107 | if (channels > 2) { | |
108 | LOG_WARN("too many channels: %d", channels); | |
109 | decode.state = DECODE_COMPLETE; | |
110 | UNLOCK_O; | |
111 | return; | |
112 | } | |
113 | } | |
114 | ||
115 | frames_t frames = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME; | |
116 | int bytes = frames * 2 * channels; // samples returned are 16 bits | |
117 | ||
118 | int stream, n; | |
119 | // write the decoded frames into outputbuf even though they are 16 bits per sample, then unpack them | |
120 | if (v->ov_read) { | |
121 | n = v->ov_read(v->vf, (char *)outputbuf->writep, bytes, 0, 2, 1, &stream); | |
122 | } else { | |
123 | n = v->ov_read_tremor(v->vf, (char *)outputbuf->writep, bytes, &stream); | |
124 | } | |
125 | ||
126 | if (n > 0) { | |
127 | ||
128 | frames = n / 2 / channels; | |
129 | frames_t count = frames * channels; | |
130 | ||
131 | // work backward to unpack samples to 4 bytes per sample | |
132 | s16_t *iptr = (s16_t *)outputbuf->writep + count; | |
133 | s32_t *optr = (s32_t *)outputbuf->writep + frames * 2; | |
134 | ||
135 | if (channels == 2) { | |
136 | while (count--) { | |
137 | *--optr = *--iptr << 16; | |
138 | } | |
139 | } else if (channels == 1) { | |
140 | while (count--) { | |
141 | *--optr = *--iptr << 16; | |
142 | *--optr = *iptr << 16; | |
143 | } | |
144 | } | |
145 | ||
146 | _buf_inc_writep(outputbuf, frames * BYTES_PER_FRAME); | |
147 | ||
148 | LOG_SDEBUG("wrote %u frames", frames); | |
149 | ||
150 | } else if (n == 0) { | |
151 | ||
152 | LOG_INFO("end of stream"); | |
153 | decode.state = DECODE_COMPLETE; | |
154 | ||
155 | } else { | |
156 | ||
157 | LOG_INFO("ov_read error: %d", n); | |
158 | decode.state = DECODE_COMPLETE; | |
159 | } | |
160 | ||
161 | UNLOCK_O; | |
162 | } | |
163 | ||
164 | static void vorbis_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { | |
165 | if (!v->vf) { | |
166 | v->vf = malloc(sizeof(OggVorbis_File) + 128); // add some padding as struct size may be larger | |
167 | } else { | |
168 | v->ov_clear(v->vf); | |
169 | } | |
170 | } | |
171 | ||
172 | static void vorbis_close(void) { | |
173 | v->ov_clear(v->vf); | |
174 | free(v->vf); | |
175 | v->vf = NULL; | |
176 | } | |
177 | ||
178 | static bool load_vorbis() { | |
179 | bool tremor = false; | |
180 | void *handle = dlopen(LIBVORBIS, RTLD_NOW); | |
181 | if (!handle) { | |
182 | handle = dlopen(LIBTREMOR, RTLD_NOW); | |
183 | if (handle) { | |
184 | tremor = true; | |
185 | } else { | |
186 | LOG_WARN("dlerror: %s", dlerror()); | |
187 | return false; | |
188 | } | |
189 | } | |
190 | ||
191 | v = malloc(sizeof(struct vorbis)); | |
192 | v->vf = NULL; | |
193 | v->ov_read = tremor ? NULL : dlsym(handle, "ov_read"); | |
194 | v->ov_read_tremor = tremor ? dlsym(handle, "ov_read") : NULL; | |
195 | v->ov_info = dlsym(handle, "ov_info"); | |
196 | v->ov_clear = dlsym(handle, "ov_clear"); | |
197 | v->ov_open_callbacks = dlsym(handle, "ov_open_callbacks"); | |
198 | ||
199 | char *err; | |
200 | if ((err = dlerror()) != NULL) { | |
201 | LOG_WARN("dlerror: %s", err); | |
202 | return false; | |
203 | } | |
204 | ||
205 | LOG_INFO("loaded %s", tremor ? LIBTREMOR : LIBVORBIS); | |
206 | return true; | |
207 | } | |
208 | ||
209 | struct codec *register_vorbis(void) { | |
210 | static struct codec ret = { | |
211 | .id = 'o', | |
212 | .types = "ogg", | |
213 | .open = vorbis_open, | |
214 | .close = vorbis_close, | |
215 | .decode= vorbis_decode, | |
216 | .min_space = 20480, | |
217 | .min_read_bytes = 2048, | |
218 | }; | |
219 | ||
220 | if (!load_vorbis()) { | |
221 | return NULL; | |
222 | } | |
223 | ||
224 | return &ret; | |
225 | } |