3 * by Bertrand Baudet <bertrand_baudet@yahoo.com>
4 * (C) 2002, MPlayer team.
6 * Implementation follow the freedb.howto1.06.txt specification
7 * from http://freedb.freedb.org
9 * discid computation by Jeremy D. Zawodny
10 * Copyright (c) 1998-2000 Jeremy D. Zawodny <Jeremy@Zawodny.com>
11 * Code release under GPL
17 #if defined(HAVE_CDDA) && defined(MPLAYER_NETWORK)
27 #include <sys/ioctl.h>
28 #include <sys/types.h>
31 #if defined(__linux__)
32 #include <linux/cdrom.h>
33 #elif defined(__FreeBSD__) || defined(__bsdi__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
38 #include "../version.h"
42 #define DEFAULT_FREEDB_SERVER "freedb.freedb.org"
43 #define DEFAULT_CACHE_DIR "/.cddb/"
45 stream_t
* open_cdda(char *dev
, char *track
);
47 static cd_toc_t cdtoc
[100];
49 #if defined(__linux__)
51 read_toc(const char *dev
) {
53 struct cdrom_tochdr tochdr
;
54 struct cdrom_tocentry tocentry
;
57 drive
= open(dev
, O_RDONLY
| O_NONBLOCK
);
62 ioctl(drive
, CDROMREADTOCHDR
, &tochdr
);
63 for (i
= tochdr
.cdth_trk0
; i
<= tochdr
.cdth_trk1
; i
++) {
64 tocentry
.cdte_track
= i
;
65 tocentry
.cdte_format
= CDROM_MSF
;
66 ioctl(drive
, CDROMREADTOCENTRY
, &tocentry
);
67 cdtoc
[i
-1].min
= tocentry
.cdte_addr
.msf
.minute
;
68 cdtoc
[i
-1].sec
= tocentry
.cdte_addr
.msf
.second
;
69 cdtoc
[i
-1].frame
= tocentry
.cdte_addr
.msf
.frame
;
70 cdtoc
[i
-1].frame
+= cdtoc
[i
-1].min
*60*75;
71 cdtoc
[i
-1].frame
+= cdtoc
[i
-1].sec
*75;
73 tocentry
.cdte_track
= 0xAA;
74 tocentry
.cdte_format
= CDROM_MSF
;
75 ioctl(drive
, CDROMREADTOCENTRY
, &tocentry
);
76 cdtoc
[tochdr
.cdth_trk1
].min
= tocentry
.cdte_addr
.msf
.minute
;
77 cdtoc
[tochdr
.cdth_trk1
].sec
= tocentry
.cdte_addr
.msf
.second
;
78 cdtoc
[tochdr
.cdth_trk1
].frame
= tocentry
.cdte_addr
.msf
.frame
;
79 cdtoc
[tochdr
.cdth_trk1
].frame
+= cdtoc
[tochdr
.cdth_trk1
].min
*60*75;
80 cdtoc
[tochdr
.cdth_trk1
].frame
+= cdtoc
[tochdr
.cdth_trk1
].sec
*75;
82 return tochdr
.cdth_trk1
;
85 #elif defined(__FreeBSD__) || defined(__bsdi__) || defined(__DragonFly__)
87 read_toc(const char *dev
) {
89 struct ioc_toc_header tochdr
;
90 struct ioc_read_toc_single_entry tocentry
;
93 drive
= open(dev
, O_RDONLY
| O_NONBLOCK
);
98 ioctl(drive
, CDIOREADTOCHEADER
, &tochdr
);
99 for (i
= tochdr
.starting_track
; i
<= tochdr
.ending_track
; i
++) {
101 tocentry
.address_format
= CD_MSF_FORMAT
;
102 ioctl(drive
, CDIOREADTOCENTRY
, &tocentry
);
103 cdtoc
[i
-1].min
= tocentry
.entry
.addr
.msf
.minute
;
104 cdtoc
[i
-1].sec
= tocentry
.entry
.addr
.msf
.second
;
105 cdtoc
[i
-1].frame
= tocentry
.entry
.addr
.msf
.frame
;
106 cdtoc
[i
-1].frame
+= cdtoc
[i
-1].min
*60*75;
107 cdtoc
[i
-1].frame
+= cdtoc
[i
-1].sec
*75;
109 tocentry
.track
= 0xAA;
110 tocentry
.address_format
= CD_MSF_FORMAT
;
111 ioctl(drive
, CDIOREADTOCENTRY
, &tocentry
);
112 cdtoc
[tochdr
.ending_track
].min
= tocentry
.entry
.addr
.msf
.minute
;
113 cdtoc
[tochdr
.ending_track
].sec
= tocentry
.entry
.addr
.msf
.second
;
114 cdtoc
[tochdr
.ending_track
].frame
= tocentry
.entry
.addr
.msf
.frame
;
115 cdtoc
[tochdr
.ending_track
].frame
+= cdtoc
[tochdr
.ending_track
].min
*60*75;
116 cdtoc
[tochdr
.ending_track
].frame
+= cdtoc
[tochdr
.ending_track
].sec
*75;
118 return tochdr
.ending_track
;
121 #elif defined(__NetBSD__) || defined(__OpenBSD__)
123 read_toc(const char *dev
) {
125 struct ioc_toc_header tochdr
;
126 struct ioc_read_toc_entry tocentry
;
128 struct cd_toc_entry toc_buffer
;
130 drive
= open(dev
, O_RDONLY
| O_NONBLOCK
);
135 ioctl(drive
, CDIOREADTOCHEADER
, &tochdr
);
136 for (i
= tochdr
.starting_track
; i
<= tochdr
.ending_track
; i
++) {
137 tocentry
.starting_track
= i
;
138 tocentry
.address_format
= CD_MSF_FORMAT
;
139 tocentry
.data
= &toc_buffer
;
140 tocentry
.data_len
= sizeof(toc_buffer
);
141 ioctl(drive
, CDIOREADTOCENTRYS
, &tocentry
);
142 cdtoc
[i
-1].min
= toc_buffer
.addr
.msf
.minute
;
143 cdtoc
[i
-1].sec
= toc_buffer
.addr
.msf
.second
;
144 cdtoc
[i
-1].frame
= toc_buffer
.addr
.msf
.frame
;
145 cdtoc
[i
-1].frame
+= cdtoc
[i
-1].min
*60*75;
146 cdtoc
[i
-1].frame
+= cdtoc
[i
-1].sec
*75;
148 tocentry
.starting_track
= 0xAA;
149 tocentry
.address_format
= CD_MSF_FORMAT
;
150 ioctl(drive
, CDIOREADTOCENTRYS
, &tocentry
);
151 cdtoc
[tochdr
.ending_track
].min
= toc_buffer
.addr
.msf
.minute
;
152 cdtoc
[tochdr
.ending_track
].sec
= toc_buffer
.addr
.msf
.second
;
153 cdtoc
[tochdr
.ending_track
].frame
= toc_buffer
.addr
.msf
.frame
;
154 cdtoc
[tochdr
.ending_track
].frame
+= cdtoc
[tochdr
.ending_track
].min
*60*75;
155 cdtoc
[tochdr
.ending_track
].frame
+= cdtoc
[tochdr
.ending_track
].sec
*75;
157 return tochdr
.ending_track
;
174 cddb_discid(int tot_trks
) {
175 unsigned int i
, t
= 0, n
= 0;
178 while (i
< (unsigned int)tot_trks
) {
179 n
= n
+ cddb_sum((cdtoc
[i
].min
* 60) + cdtoc
[i
].sec
);
182 t
= ((cdtoc
[tot_trks
].min
* 60) + cdtoc
[tot_trks
].sec
) -
183 ((cdtoc
[0].min
* 60) + cdtoc
[0].sec
);
184 return ((n
% 0xff) << 24 | t
<< 8 | tot_trks
);
190 cddb_http_request(char *command
, int (*reply_parser
)(HTTP_header_t
*,cddb_data_t
*), cddb_data_t
*cddb_data
) {
194 HTTP_header_t
*http_hdr
;
196 if( reply_parser
==NULL
|| command
==NULL
|| cddb_data
==NULL
) return -1;
198 sprintf( request
, "http://%s/~cddb/cddb.cgi?cmd=%s%s&proto=%d", cddb_data
->freedb_server
, command
, cddb_data
->cddb_hello
, cddb_data
->freedb_proto_level
);
199 printf("Request[%s]\n", request
);
201 url
= url_new(request
);
203 printf("Not a valid URL\n");
207 fd
= http_send_request(url
,0);
209 printf("failed to send the http request\n");
213 http_hdr
= http_read_response( fd
);
214 if( http_hdr
==NULL
) {
215 printf("Failed to read the http response\n");
219 http_debug_hdr(http_hdr
);
220 printf("body=[%s]\n", http_hdr
->body
);
222 switch(http_hdr
->status_code
) {
224 ret
= reply_parser(http_hdr
, cddb_data
);
227 printf("Not Found\n");
230 printf("Unknown Error code\n");
233 http_free( http_hdr
);
240 cddb_read_cache(cddb_data_t
*cddb_data
) {
246 if( cddb_data
==NULL
|| cddb_data
->cache_dir
==NULL
) return -1;
248 sprintf( file_name
, "%s%08lx", cddb_data
->cache_dir
, cddb_data
->disc_id
);
250 file_fd
= open(file_name
, O_RDONLY
);
252 printf("No cache found\n");
256 ret
= fstat( file_fd
, &stats
);
261 file_size
= stats
.st_size
;
264 cddb_data
->xmcd_file
= (char*)malloc(file_size
);
265 if( cddb_data
->xmcd_file
==NULL
) {
266 printf("Memory allocation failed\n");
270 cddb_data
->xmcd_file_size
= read(file_fd
, cddb_data
->xmcd_file
, file_size
);
271 if( cddb_data
->xmcd_file_size
!=file_size
) {
272 printf("Not all the xmcd file has been read\n");
283 cddb_write_cache(cddb_data_t
*cddb_data
) {
284 // We have the file, save it for cache.
285 struct stat file_stat
;
290 if( cddb_data
==NULL
|| cddb_data
->cache_dir
==NULL
) return -1;
292 // Check if the CDDB cache dir exist
293 ret
= stat( cddb_data
->cache_dir
, &file_stat
);
295 // Directory not present, create it.
296 ret
= mkdir( cddb_data
->cache_dir
, 0755 );
299 printf("Failed to create directory %s\n", cddb_data
->cache_dir
);
304 sprintf( file_name
, "%s%08lx", cddb_data
->cache_dir
, cddb_data
->disc_id
);
306 file_fd
= creat(file_name
, S_IREAD
|S_IWRITE
);
312 wrote
= write(file_fd
, cddb_data
->xmcd_file
, cddb_data
->xmcd_file_size
);
318 if( (unsigned int)wrote
!=cddb_data
->xmcd_file_size
) {
319 printf("Not all the xmcd file has been written\n");
330 cddb_read_parse(HTTP_header_t
*http_hdr
, cddb_data_t
*cddb_data
) {
331 unsigned long disc_id
;
333 char *ptr
=NULL
, *ptr2
=NULL
;
336 if( http_hdr
==NULL
|| cddb_data
==NULL
) return -1;
338 ret
= sscanf( http_hdr
->body
, "%d ", &status
);
340 printf("Parse error\n");
346 ret
= sscanf( http_hdr
->body
, "%d %s %08lx", &status
, category
, &disc_id
);
348 printf("Parse error\n");
351 // Check if it's a xmcd database file
352 ptr
= strstr(http_hdr
->body
, "# xmcd");
354 printf("Invalid xmcd database file returned\n");
357 // Ok found the beginning of the file
359 ptr2
= strstr(ptr
, "\r\n.\r\n");
361 ptr2
= strstr(ptr
, "\n.\n");
363 printf("Unable to find '.'\n");
364 ptr2
=ptr
+strlen(ptr
); //return -1;
369 if( http_hdr
->body_size
<(unsigned int)(ptr2
-ptr
) ) {
370 printf("Unexpected fix me\n");
373 cddb_data
->xmcd_file
= ptr
;
374 cddb_data
->xmcd_file_size
= ptr2
-ptr
+2;
375 cddb_data
->xmcd_file
[cddb_data
->xmcd_file_size
] = '\0';
376 // Avoid the http_free function to free the xmcd file...save a mempcy...
377 http_hdr
->body
= NULL
;
378 http_hdr
->body_size
= 0;
379 return cddb_write_cache(cddb_data
);
381 printf("Unhandled code\n");
387 cddb_request_titles(cddb_data_t
*cddb_data
) {
389 sprintf( command
, "cddb+read+%s+%08lx", cddb_data
->category
, cddb_data
->disc_id
);
390 return cddb_http_request(command
, cddb_read_parse
, cddb_data
);
394 cddb_parse_matches_list(HTTP_header_t
*http_hdr
, cddb_data_t
*cddb_data
) {
395 char album_title
[100];
399 ptr
= strstr(http_hdr
->body
, "\n");
401 printf("Unable to find end of line\n");
405 // We have a list of exact/inexact matches, so which one do we use?
406 // So let's take the first one.
407 ret
= sscanf(ptr
, "%s %08lx %s", cddb_data
->category
, &(cddb_data
->disc_id
), album_title
);
409 printf("Parse error\n");
412 ptr
= strstr(http_hdr
->body
, album_title
);
416 ptr2
= strstr(ptr
, "\n");
418 len
= (http_hdr
->body_size
)-(ptr
-(http_hdr
->body
));
422 strncpy(album_title
, ptr
, len
);
423 album_title
[len
-2]='\0';
425 printf("Parse OK, found: %s\n", album_title
);
430 cddb_query_parse(HTTP_header_t
*http_hdr
, cddb_data_t
*cddb_data
) {
431 char album_title
[100];
435 ret
= sscanf( http_hdr
->body
, "%d ", &status
);
437 printf("Parse error\n");
444 ret
= sscanf(http_hdr
->body
, "%d %s %08lx %s", &status
, cddb_data
->category
, &(cddb_data
->disc_id
), album_title
);
446 printf("Parse error\n");
449 ptr
= strstr(http_hdr
->body
, album_title
);
453 ptr2
= strstr(ptr
, "\n");
455 len
= (http_hdr
->body_size
)-(ptr
-(http_hdr
->body
));
459 strncpy(album_title
, ptr
, len
);
460 album_title
[len
-2]='\0';
462 printf("Parse OK, found: %s\n", album_title
);
463 return cddb_request_titles(cddb_data
);
466 printf("Album not found\n");
469 // Found exact matches, list follows
470 cddb_parse_matches_list(http_hdr
, cddb_data
);
471 return cddb_request_titles(cddb_data
);
473 body=[210 Found exact matches, list follows (until terminating `.')
474 misc c711930d Santana / Supernatural
475 rock c711930d Santana / Supernatural
476 blues c711930d Santana / Supernatural
480 // Found inexact matches, list follows
481 cddb_parse_matches_list(http_hdr
, cddb_data
);
482 return cddb_request_titles(cddb_data
);
484 printf("Server returns: Command syntax error\n");
487 printf("Unhandled code\n");
493 cddb_proto_level_parse(HTTP_header_t
*http_hdr
, cddb_data_t
*cddb_data
) {
498 ret
= sscanf( http_hdr
->body
, "%d ", &status
);
500 printf("Parse error\n");
506 ptr
= strstr(http_hdr
->body
, "max proto:");
508 printf("Parse error\n");
511 ret
= sscanf(ptr
, "max proto: %d", &max
);
513 printf("Parse error\n");
516 cddb_data
->freedb_proto_level
= max
;
519 printf("Unhandled code\n");
525 cddb_get_proto_level(cddb_data_t
*cddb_data
) {
526 return cddb_http_request("stat", cddb_proto_level_parse
, cddb_data
);
530 cddb_freedb_sites_parse(HTTP_header_t
*http_hdr
, cddb_data_t
*cddb_data
) {
533 ret
= sscanf( http_hdr
->body
, "%d ", &status
);
535 printf("Parse error\n");
541 // TODO: Parse the sites
542 ret
= cddb_data
->anonymous
; // For gcc complaining about unused parameter.
545 printf("No sites information available\n");
548 printf("Unhandled code\n");
554 cddb_get_freedb_sites(cddb_data_t
*cddb_data
) {
555 return cddb_http_request("sites", cddb_freedb_sites_parse
, cddb_data
);
559 cddb_create_hello(cddb_data_t
*cddb_data
) {
563 if( cddb_data
->anonymous
) { // Default is anonymous
564 /* Note from Eduardo PĂ©rez Ureta <eperez@it.uc3m.es> :
565 * We don't send current user/host name in hello to prevent spam.
566 * Software that sends this is considered spyware
567 * that most people don't like.
569 user_name
= "anonymous";
570 strcpy(host_name
, "localhost");
572 if( gethostname(host_name
, 50)<0 ) {
573 strcpy(host_name
, "localhost");
575 user_name
= getenv("LOGNAME");
577 sprintf( cddb_data
->cddb_hello
, "&hello=%s+%s+%s+%s", user_name
, host_name
, "MPlayer", VERSION
);
581 cddb_retrieve(cddb_data_t
*cddb_data
) {
582 char offsets
[1024], command
[1024];
584 unsigned int i
, time_len
;
588 for( i
=0; i
<cddb_data
->tracks
; i
++ ) {
589 ptr
+= sprintf(ptr
, "%d+", cdtoc
[i
].frame
);
590 if (ptr
-offsets
> sizeof offsets
- 40) break;
593 time_len
= (cdtoc
[cddb_data
->tracks
].frame
)/75;
595 cddb_data
->freedb_server
= DEFAULT_FREEDB_SERVER
;
596 cddb_data
->freedb_proto_level
= 1;
597 cddb_data
->xmcd_file
= NULL
;
599 cddb_create_hello(cddb_data
);
600 if( cddb_get_proto_level(cddb_data
)<0 ) {
601 printf("Failed to get the protocol level\n");
605 //cddb_get_freedb_sites(&cddb_data);
607 sprintf(command
, "cddb+query+%08lx+%d+%s%d", cddb_data
->disc_id
, cddb_data
->tracks
, offsets
, time_len
);
608 ret
= cddb_http_request(command
, cddb_query_parse
, cddb_data
);
609 if( ret
<0 ) return -1;
611 if( cddb_data
->cache_dir
!=NULL
) {
612 free(cddb_data
->cache_dir
);
618 cddb_resolve(const char *dev
, char **xmcd_file
) {
619 char cddb_cache_dir
[] = DEFAULT_CACHE_DIR
;
620 char *home_dir
= NULL
;
621 cddb_data_t cddb_data
;
626 printf("Failed to open %s device.\n", dev
);
629 cddb_data
.tracks
= ret
;
630 cddb_data
.disc_id
= cddb_discid(cddb_data
.tracks
);
631 cddb_data
.anonymous
= 1; // Don't send user info by default
633 // Check if there is a CD in the drive
634 // FIXME: That's not really a good way to check
635 if( cddb_data
.disc_id
==0 ) {
636 printf("No CD in the drive\n");
640 home_dir
= getenv("HOME");
641 if( home_dir
==NULL
) {
642 cddb_data
.cache_dir
= NULL
;
644 cddb_data
.cache_dir
= (char*)malloc(strlen(home_dir
)+strlen(cddb_cache_dir
)+1);
645 if( cddb_data
.cache_dir
==NULL
) {
646 printf("Memory allocation failed\n");
649 sprintf(cddb_data
.cache_dir
, "%s%s", home_dir
, cddb_cache_dir
);
652 // Check for a cached file
653 if( cddb_read_cache(&cddb_data
)<0 ) {
655 if( cddb_retrieve(&cddb_data
)<0 ) {
660 if( cddb_data
.xmcd_file
!=NULL
) {
661 // printf("%s\n", cddb_data.xmcd_file );
662 *xmcd_file
= cddb_data
.xmcd_file
;
669 /*******************************************************************************************************************
673 *******************************************************************************************************************/
675 xmcd_parse_dtitle(cd_info_t
*cd_info
, char *line
) {
677 ptr
= strstr(line
, "DTITLE=");
680 album
= strstr(ptr
, "/");
681 if( album
==NULL
) return NULL
;
682 cd_info
->album
= (char*)malloc(strlen(album
+2)+1);
683 if( cd_info
->album
==NULL
) {
686 strcpy( cd_info
->album
, album
+2 );
689 cd_info
->artist
= (char*)malloc(strlen(ptr
)+1);
690 if( cd_info
->artist
==NULL
) {
693 strcpy( cd_info
->artist
, ptr
);
699 xmcd_parse_dgenre(cd_info_t
*cd_info
, char *line
) {
701 ptr
= strstr(line
, "DGENRE=");
704 cd_info
->genre
= (char*)malloc(strlen(ptr
)+1);
705 if( cd_info
->genre
==NULL
) {
708 strcpy( cd_info
->genre
, ptr
);
714 xmcd_parse_ttitle(cd_info_t
*cd_info
, char *line
) {
715 unsigned int track_nb
;
716 unsigned long sec
, off
;
718 ptr
= strstr(line
, "TTITLE");
721 // Here we point to the track number
722 track_nb
= atoi(ptr
);
723 ptr
= strstr(ptr
, "=");
724 if( ptr
==NULL
) return NULL
;
727 sec
= cdtoc
[track_nb
].frame
;
728 off
= cdtoc
[track_nb
+1].frame
-sec
+1;
730 cd_info_add_track( cd_info
, ptr
, track_nb
+1, (unsigned int)(off
/(60*75)), (unsigned int)((off
/75)%60), (unsigned int)(off
%75), sec
, off
);
736 cddb_parse_xmcd(char *xmcd_file
) {
737 cd_info_t
*cd_info
= NULL
;
740 unsigned int audiolen
;
741 if( xmcd_file
==NULL
) return NULL
;
743 cd_info
= cd_info_new();
744 if( cd_info
==NULL
) {
748 length
= strlen(xmcd_file
);
750 while( ptr
!=NULL
&& pos
<length
) {
753 while( ptr2
[0]!='\0' && ptr2
[0]!='\r' && ptr2
[0]!='\n' ) ptr2
++;
754 if( ptr2
[0]=='\0' ) {
760 // Search for the album title
761 if( xmcd_parse_dtitle(cd_info
, ptr
) );
762 // Search for the genre
763 else if( xmcd_parse_dgenre(cd_info
, ptr
) );
764 // Search for a track title
765 else if( xmcd_parse_ttitle(cd_info
, ptr
) ) audiolen
++; // <-- audiolen++ to shut up gcc warning
767 if( ptr2
[1]=='\n' ) ptr2
++;
772 audiolen
= cdtoc
[cd_info
->nb_tracks
].frame
-cdtoc
[0].frame
;
773 cd_info
->min
= (unsigned int)(audiolen
/(60*75));
774 cd_info
->sec
= (unsigned int)((audiolen
/75)%60);
775 cd_info
->msec
= (unsigned int)(audiolen
%75);