Mercurial > audlegacy-plugins
comparison src/neon/neon.c @ 1730:50d151b259bb
- Add lots of code to support the completely braindead concept that is StreamCast
author | Ralf Ertzinger <ralf@skytale.net> |
---|---|
date | Tue, 18 Sep 2007 18:01:00 +0200 |
parents | eaa8a5747628 |
children | f0b53c4db5ba 8feacd004f50 |
comparison
equal
deleted
inserted
replaced
1729:eaa8a5747628 | 1730:50d151b259bb |
---|---|
60 h->request = NULL; | 60 h->request = NULL; |
61 h->redircount = 0; | 61 h->redircount = 0; |
62 h->pos = 0; | 62 h->pos = 0; |
63 h->content_length = -1; | 63 h->content_length = -1; |
64 h->can_ranges = FALSE; | 64 h->can_ranges = FALSE; |
65 h->icy_metaint = 0; | |
66 h->icy_metaleft = 0; | |
67 h->icy_metadata.stream_name = NULL; | |
68 h->icy_metadata.stream_title = NULL; | |
69 h->icy_metadata.stream_url = NULL; | |
65 h->reader = NULL; | 70 h->reader = NULL; |
66 h->reader_status.mutex = g_mutex_new(); | 71 h->reader_status.mutex = g_mutex_new(); |
67 h->reader_status.cond = g_cond_new(); | 72 h->reader_status.cond = g_cond_new(); |
68 h->reader_status.reading = FALSE; | 73 h->reader_status.reading = FALSE; |
69 h->reader_status.status = NEON_READER_INIT; | 74 h->reader_status.status = NEON_READER_INIT; |
118 static void fini(void) { | 123 static void fini(void) { |
119 | 124 |
120 _ENTER; | 125 _ENTER; |
121 | 126 |
122 ne_sock_exit(); | 127 ne_sock_exit(); |
128 | |
129 _LEAVE; | |
130 } | |
131 | |
132 /* | |
133 * ----- | |
134 */ | |
135 | |
136 static void add_icy(struct icy_metadata* m, gchar* name, gchar* value) { | |
137 | |
138 _ENTER; | |
139 | |
140 if (0 == g_ascii_strncasecmp(name, "StreamTitle", 11)) { | |
141 _DEBUG("Found StreamTitle: %s", value); | |
142 if (NULL != m->stream_title) { | |
143 free(m->stream_title); | |
144 } | |
145 m->stream_title = g_strdup(value); | |
146 } | |
147 | |
148 if (0 == g_ascii_strncasecmp(name, "StreamUrl", 9)) { | |
149 _DEBUG("Found StreamUrl: %s", value); | |
150 if (NULL != m->stream_url) { | |
151 free(m->stream_url); | |
152 } | |
153 m->stream_url = g_strdup(value); | |
154 } | |
155 | |
156 _LEAVE; | |
157 } | |
158 | |
159 /* | |
160 * ----- | |
161 */ | |
162 | |
163 static void parse_icy(struct icy_metadata* m, gchar* metadata, int len) { | |
164 | |
165 gchar* p; | |
166 gchar* tstart; | |
167 gchar* tend; | |
168 gchar name[4096]; | |
169 gchar value[4096]; | |
170 int state; | |
171 int pos; | |
172 | |
173 _ENTER; | |
174 | |
175 p = metadata; | |
176 state = 1; | |
177 pos = 0; | |
178 name[0] = '\0'; | |
179 value[0] = '\0'; | |
180 tstart = metadata; | |
181 tend = metadata; | |
182 while ((pos < len) && (*p != '\0')) { | |
183 switch (state) { | |
184 case 1: | |
185 /* | |
186 * Reading tag name | |
187 */ | |
188 if ('=' == *p) { | |
189 /* | |
190 * End of tag name. | |
191 */ | |
192 *p = '\0'; | |
193 strcpy(name, tstart); | |
194 _DEBUG("Found tag name: %s", name); | |
195 state = 2; | |
196 } else { | |
197 tend = p; | |
198 }; | |
199 break; | |
200 case 2: | |
201 /* | |
202 * Waiting for start of value | |
203 */ | |
204 if ('\'' == *p) { | |
205 /* | |
206 * Leading ' of value | |
207 */ | |
208 tend = tstart = p + 1; | |
209 state = 3; | |
210 value[0] = '\0'; | |
211 } | |
212 break; | |
213 case 3: | |
214 /* | |
215 * Reading value | |
216 */ | |
217 if ('\'' == *p) { | |
218 /* | |
219 * End of value | |
220 */ | |
221 *p = '\0'; | |
222 strcpy(value, tstart); | |
223 _DEBUG("Found tag value: %s", value); | |
224 add_icy(m, name, value); | |
225 state = 4; | |
226 } else { | |
227 tend = p; | |
228 } | |
229 break; | |
230 case 4: | |
231 /* | |
232 * Waiting for next tag start | |
233 */ | |
234 if (';' == *p) { | |
235 /* | |
236 * Next tag name starts after this char | |
237 */ | |
238 tend = tstart = p + 1; | |
239 state = 1; | |
240 name[0] = '\0'; | |
241 value[0] = '\0'; | |
242 } | |
243 break; | |
244 } | |
245 p++; | |
246 pos++; | |
247 } | |
123 | 248 |
124 _LEAVE; | 249 _LEAVE; |
125 } | 250 } |
126 | 251 |
127 /* | 252 /* |
174 if (0 == g_ascii_strncasecmp("content-length", name, 14)) { | 299 if (0 == g_ascii_strncasecmp("content-length", name, 14)) { |
175 /* | 300 /* |
176 * The server sent us the content length. Parse and store. | 301 * The server sent us the content length. Parse and store. |
177 */ | 302 */ |
178 len = strtol(value, &endptr, 10); | 303 len = strtol(value, &endptr, 10); |
179 if ((*value != '\0') && (*endptr == '\0')) { | 304 if ((*value != '\0') && (*endptr == '\0') && (len >= 0)) { |
180 /* | 305 /* |
181 * Valid data. | 306 * Valid data. |
182 */ | 307 */ |
183 _DEBUG("Content length as advertised by server: %d", len); | 308 _DEBUG("Content length as advertised by server: %ld", len); |
184 h->content_length = len; | 309 h->content_length = len; |
310 } else { | |
311 _ERROR("Invalid content length header: %s", value); | |
185 } | 312 } |
313 } | |
314 | |
315 if (0 == g_ascii_strncasecmp("icy-metaint", name, 11)) { | |
316 /* | |
317 * The server sent us a ICY metaint header. Parse and store. | |
318 */ | |
319 len = strtol(value, &endptr, 10); | |
320 if ((*value != '\0') && (*endptr == '\0') && (len > 0)) { | |
321 /* | |
322 * Valid data | |
323 */ | |
324 _DEBUG("ICY MetaInt as advertised by server: %ld", len); | |
325 h->icy_metaint = len; | |
326 h->icy_metaleft = len; | |
327 } else { | |
328 _ERROR("Invalid ICY MetaInt header: %s", value); | |
329 } | |
330 } | |
331 | |
332 if (0 == g_ascii_strncasecmp("icy-name", name, 8)) { | |
333 /* | |
334 * The server sent us a ICY name. Save it for later | |
335 */ | |
336 _DEBUG("ICY stream name: %s", value); | |
337 if (NULL != h->icy_metadata.stream_name) { | |
338 free(h->icy_metadata.stream_name); | |
339 } | |
340 h->icy_metadata.stream_name = g_strdup(value); | |
186 } | 341 } |
187 } | 342 } |
188 | 343 |
189 _LEAVE; | 344 _LEAVE; |
190 } | 345 } |
199 | 354 |
200 _ENTER; | 355 _ENTER; |
201 | 356 |
202 handle->request = ne_request_create(handle->session, "GET", handle->purl->path); | 357 handle->request = ne_request_create(handle->session, "GET", handle->purl->path); |
203 ne_print_request_header(handle->request, "Range", "bytes=%ld-", startbyte); | 358 ne_print_request_header(handle->request, "Range", "bytes=%ld-", startbyte); |
359 ne_print_request_header(handle->request, "Icy-MetaData", "1"); | |
204 | 360 |
205 /* | 361 /* |
206 * Try to connect to the server. | 362 * Try to connect to the server. |
207 */ | 363 */ |
208 _DEBUG("Connecting..."); | 364 _DEBUG("Connecting..."); |
519 | 675 |
520 size_t neon_vfs_fread_impl(gpointer ptr_, size_t size, size_t nmemb, VFSFile* file) { | 676 size_t neon_vfs_fread_impl(gpointer ptr_, size_t size, size_t nmemb, VFSFile* file) { |
521 | 677 |
522 struct neon_handle* h = (struct neon_handle*)file->handle; | 678 struct neon_handle* h = (struct neon_handle*)file->handle; |
523 int belem; | 679 int belem; |
680 int relem; | |
524 int ret; | 681 int ret; |
682 char icy_metadata[4096]; | |
683 unsigned char icy_metalen; | |
525 | 684 |
526 _ENTER; | 685 _ENTER; |
527 | 686 |
528 if (NULL == h->request) { | 687 if (NULL == h->request) { |
529 _ERROR("No request to read from, seek gone wrong?"); | 688 _ERROR("No request to read from, seek gone wrong?"); |
547 g_mutex_lock(h->reader_status.mutex); | 706 g_mutex_lock(h->reader_status.mutex); |
548 if (NEON_READER_RUN == h->reader_status.status) { | 707 if (NEON_READER_RUN == h->reader_status.status) { |
549 g_mutex_unlock(h->reader_status.mutex); | 708 g_mutex_unlock(h->reader_status.mutex); |
550 _ERROR("Buffer underrun, trying rebuffering"); | 709 _ERROR("Buffer underrun, trying rebuffering"); |
551 kill_reader(h); | 710 kill_reader(h); |
711 | |
712 /* | |
713 * We have to check if the reader terminated gracefully | |
714 * again | |
715 */ | |
716 if (NEON_READER_TERM != h->reader_status.status) { | |
717 /* | |
718 * Reader thread did not terminate gracefully. | |
719 */ | |
720 _LEAVE 0; | |
721 } | |
552 } else { | 722 } else { |
553 g_mutex_unlock(h->reader_status.mutex); | 723 g_mutex_unlock(h->reader_status.mutex); |
554 } | 724 } |
555 } | 725 } |
556 | 726 |
632 } | 802 } |
633 | 803 |
634 /* | 804 /* |
635 * Deliver data from the buffer | 805 * Deliver data from the buffer |
636 */ | 806 */ |
637 belem = used_rb(&h->rb) / size; | 807 if (0 == used_rb(&h->rb)) { |
638 | |
639 if (0 == belem) { | |
640 /* | 808 /* |
641 * The buffer is empty, we can deliver no data! | 809 * The buffer is still empty, we can deliver no data! |
642 */ | 810 */ |
643 _ERROR("Buffer still underrun, fatal."); | 811 _ERROR("Buffer still underrun, fatal."); |
644 _LEAVE 0; | 812 _LEAVE 0; |
645 } | 813 } |
646 | 814 |
647 _DEBUG("%d elements of data in the buffer", belem); | 815 if (0 != h->icy_metaint) { |
648 read_rb(&h->rb, ptr_, MIN(belem, nmemb)*size); | 816 _DEBUG("%ld bytes left before next ICY metadata announcement", h->icy_metaleft); |
817 if (0 == h->icy_metaleft) { | |
818 /* | |
819 * The next data in the buffer is a ICY metadata announcement. | |
820 * Get the length byte | |
821 */ | |
822 read_rb(&h->rb, &icy_metalen, 1); | |
823 | |
824 /* | |
825 * We need enough data in the buffer to | |
826 * a) Read the complete ICY metadata block | |
827 * b) deliver at least one byte to the reader | |
828 */ | |
829 _DEBUG("Expecting %d bytes of ICY metadata", (icy_metalen*16)); | |
830 | |
831 if ((free_rb(&h->rb)-(icy_metalen*16)) < size) { | |
832 /* There is not enough data. We do not have much choice at this point, | |
833 * so we'll deliver the metadata as normal data to the reader and | |
834 * hope for the best. | |
835 */ | |
836 _ERROR("Buffer underrun when reading metadata. Expect audio degradation"); | |
837 h->icy_metaleft = h->icy_metaint + (icy_metalen*16); | |
838 } else { | |
839 /* | |
840 * Grab the metadata from the buffer and send it to the parser | |
841 */ | |
842 read_rb(&h->rb, icy_metadata, (icy_metalen*16)); | |
843 parse_icy(&h->icy_metadata, icy_metadata, (icy_metalen*16)); | |
844 h->icy_metaleft = h->icy_metaint; | |
845 } | |
846 } | |
847 | |
848 /* | |
849 * The maximum number of bytes we can deliver is determined | |
850 * by the number of bytes left until the next metadata announcement | |
851 */ | |
852 belem = h->icy_metaleft / size; | |
853 } else { | |
854 belem = used_rb(&h->rb) / size; | |
855 } | |
856 | |
857 relem = MIN(belem, nmemb); | |
858 _DEBUG("%d elements of returnable data in the buffer", belem); | |
859 read_rb(&h->rb, ptr_, relem*size); | |
649 | 860 |
650 /* | 861 /* |
651 * Signal the network thread to continue reading | 862 * Signal the network thread to continue reading |
652 */ | 863 */ |
653 _DEBUG("Waking up reader thread"); | 864 _DEBUG("Waking up reader thread"); |
654 g_mutex_lock(h->reader_status.mutex); | 865 g_mutex_lock(h->reader_status.mutex); |
655 g_cond_signal(h->reader_status.cond); | 866 g_cond_signal(h->reader_status.cond); |
656 g_mutex_unlock(h->reader_status.mutex); | 867 g_mutex_unlock(h->reader_status.mutex); |
657 | 868 |
658 h->pos += (MIN(belem, nmemb)*size); | 869 h->pos += (relem*size); |
659 | 870 h->icy_metaleft -= (relem*size); |
660 _DEBUG("Returning %d elements", MIN(belem, nmemb)); | 871 |
661 | 872 _DEBUG("Returning %d elements", relem); |
662 _LEAVE MIN(belem, nmemb); | 873 |
874 _LEAVE relem; | |
663 } | 875 } |
664 | 876 |
665 | 877 |
666 /* | 878 /* |
667 * ----- | 879 * ----- |
728 | 940 |
729 struct neon_handle* h = (struct neon_handle *)file->handle; | 941 struct neon_handle* h = (struct neon_handle *)file->handle; |
730 | 942 |
731 _ENTER; | 943 _ENTER; |
732 | 944 |
733 _DEBUG("Current file position: %d", h->pos); | 945 _DEBUG("Current file position: %ld", h->pos); |
734 | 946 |
735 _LEAVE h->pos; | 947 _LEAVE h->pos; |
736 } | 948 } |
737 | 949 |
738 /* | 950 /* |
856 | 1068 |
857 /* | 1069 /* |
858 * ----- | 1070 * ----- |
859 */ | 1071 */ |
860 | 1072 |
861 gchar *neon_vfs_metadata_impl(VFSFile* file, const gchar * field) { | 1073 gchar *neon_vfs_metadata_impl(VFSFile* file, const gchar* field) { |
862 | 1074 |
863 _ENTER; | 1075 struct neon_handle* h = (struct neon_handle*)file->handle; |
864 | 1076 |
865 _ERROR("NOT IMPLEMENTED"); | 1077 _ENTER; |
1078 | |
1079 _DEBUG("Field name: %s", field); | |
1080 | |
1081 if (0 == g_ascii_strncasecmp(field, "track-name", 10)) { | |
1082 _LEAVE g_strdup(h->icy_metadata.stream_title); | |
1083 } | |
1084 | |
1085 if (0 == g_ascii_strncasecmp(field, "stream-name", 11)) { | |
1086 _LEAVE g_strdup(h->icy_metadata.stream_name); | |
1087 } | |
866 | 1088 |
867 _LEAVE NULL; | 1089 _LEAVE NULL; |
868 } | 1090 } |
869 | 1091 |
870 /* | 1092 /* |