sync with en/mplayer.1 rev. 30611
[mplayer/glamo.git] / stream / stream_cue.c
blob6bddab7c9e585b15fc29e50926a14e9c60fd422c
1 /*
2 * VideoCD BinCue
4 * This file is part of MPlayer.
6 * MPlayer is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * MPlayer is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <fcntl.h>
30 #include "config.h"
31 #include "mp_msg.h"
32 #include "help_mp.h"
34 #include "stream.h"
36 #include "help_mp.h"
37 #include "m_option.h"
38 #include "m_struct.h"
39 #include "libavutil/avstring.h"
41 #define byte unsigned char
42 #define SIZERAW 2352
43 #define SIZEISO_MODE1 2048
44 #define SIZEISO_MODE2_RAW 2352
45 #define SIZEISO_MODE2_FORM1 2048
46 #define SIZEISO_MODE2_FORM2 2336
47 #define AUDIO 0
48 #define MODE1 1
49 #define MODE2 2
50 #define MODE1_2352 10
51 #define MODE2_2352 20
52 #define MODE1_2048 30
53 #define MODE2_2336 40
54 #define UNKNOWN -1
56 static struct stream_priv_s {
57 char* filename;
58 } stream_priv_dflts = {
59 NULL
62 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
63 /// URL definition
64 static const m_option_t stream_opts_fields[] = {
65 { "string", ST_OFF(filename), CONF_TYPE_STRING, 0, 0 ,0, NULL},
66 { NULL, NULL, 0, 0, 0, 0, NULL }
68 static const struct m_struct_st stream_opts = {
69 "cue",
70 sizeof(struct stream_priv_s),
71 &stream_priv_dflts,
72 stream_opts_fields
75 static FILE* fd_cue;
76 static int fd_bin = 0;
78 static char bin_filename[256];
80 static char cue_filename[256];
81 static char bincue_path[256];
84 typedef struct track
86 unsigned short mode;
87 unsigned short minute;
88 unsigned short second;
89 unsigned short frame;
91 /* (min*60 + sec) * 75 + fps */
93 unsigned long start_sector;
95 /* = the sizes in bytes off all tracks bevor this one */
96 /* its needed if there are mode1 tracks befor the mpeg tracks */
97 unsigned long start_offset;
99 /* unsigned char num[3]; */
100 } tTrack;
102 /* max 99 tracks on a cd */
103 static tTrack tracks[100];
105 static struct cue_track_pos {
106 int track;
107 unsigned short mode;
108 unsigned short minute;
109 unsigned short second;
110 unsigned short frame;
111 } cue_current_pos;
113 /* number of tracks on the cd */
114 static int nTracks = 0;
116 static int digits2int(char s[2], int errval) {
117 uint8_t a = s[0] - '0';
118 uint8_t b = s[1] - '0';
119 if (a > 9 || b > 9)
120 return errval;
121 return a * 10 + b;
124 /* presumes Line is preloaded with the "current" line of the file */
125 static int cue_getTrackinfo(char *Line, tTrack *track)
127 int already_set = 0;
129 /* Get the 'mode' */
130 if (strncmp(&Line[2], "TRACK ", 6)==0)
132 /* strncpy(track->num, &Line[8], 2); track->num[2] = '\0'; */
134 track->mode = UNKNOWN;
135 if(strncmp(&Line[11], "AUDIO", 5)==0) track->mode = AUDIO;
136 if(strncmp(&Line[11], "MODE1/2352", 10)==0) track->mode = MODE1_2352;
137 if(strncmp(&Line[11], "MODE1/2048", 10)==0) track->mode = MODE1_2048;
138 if(strncmp(&Line[11], "MODE2/2352", 10)==0) track->mode = MODE2_2352;
139 if(strncmp(&Line[11], "MODE2/2336", 10)==0) track->mode = MODE2_2336;
141 else return 1;
143 /* Get the track indexes */
144 while(1) {
145 if(! fgets( Line, 256, fd_cue ) ) { break;}
147 if (strncmp(&Line[2], "TRACK ", 6)==0)
149 /* next track starting */
150 break;
153 /* Track 0 or 1, take the first an get fill the values*/
154 if (strncmp(&Line[4], "INDEX ", 6)==0)
156 /* check stuff here so if the answer is false the else stuff below won't be executed */
157 if ((already_set == 0) && digits2int(Line + 10, 100) <= 1)
159 already_set = 1;
161 track->minute = digits2int(Line + 13, 0);
162 track->second = digits2int(Line + 16, 0);
163 track->frame = digits2int(Line + 19, 0);
166 else if (strncmp(&Line[4], "PREGAP ", 7)==0) { ; /* ignore */ }
167 else if (strncmp(&Line[4], "FLAGS ", 6)==0) { ; /* ignore */ }
168 else mp_msg (MSGT_OPEN,MSGL_INFO,
169 MSGTR_MPDEMUX_CUEREAD_UnexpectedCuefileLine, Line);
171 return 0;
176 /* FIXME: the string operations ( strcpy,strcat ) below depend
177 * on the arrays to have the same size, thus we need to make
178 * sure the sizes are in sync.
180 static int cue_find_bin (char *firstline) {
181 int i,j;
182 char s[256];
183 char t[256];
185 /* get the filename out of that */
186 /* 12345 6 */
187 mp_msg (MSGT_OPEN,MSGL_INFO, "[bincue] cue_find_bin(%s)\n", firstline);
188 if (strncmp(firstline, "FILE \"",6)==0)
190 i = 0;
191 j = 0;
192 while ( firstline[6 + i] != '"')
194 bin_filename[j] = firstline[6 + i];
196 /* if I found a path info, than delete all bevor it */
197 switch (bin_filename[j])
199 case '\\':
200 j = 0;
201 break;
203 case '/':
204 j = 0;
205 break;
207 default:
208 j++;
210 i++;
212 bin_filename[j+1] = '\0';
216 /* now try to open that file, without path */
217 fd_bin = open (bin_filename, O_RDONLY);
218 if (fd_bin == -1)
220 mp_msg(MSGT_OPEN,MSGL_STATUS, MSGTR_MPDEMUX_CUEREAD_BinFilenameTested,
221 bin_filename);
223 /* now try to find it with the path of the cue file */
224 snprintf(s,sizeof( s ),"%s/%s",bincue_path,bin_filename);
225 fd_bin = open (s, O_RDONLY);
226 if (fd_bin == -1)
228 mp_msg(MSGT_OPEN,MSGL_STATUS,
229 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested, s);
230 /* now I would say the whole filename is shit, build our own */
231 strncpy(s, cue_filename, strlen(cue_filename) - 3 );
232 s[strlen(cue_filename) - 3] = '\0';
233 strcat(s, "bin");
234 fd_bin = open (s, O_RDONLY);
235 if (fd_bin == -1)
237 mp_msg(MSGT_OPEN,MSGL_STATUS,
238 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested, s);
240 /* ok try it with path */
241 snprintf(t, sizeof( t ), "%s/%s", bincue_path, s);
242 fd_bin = open (t, O_RDONLY);
243 if (fd_bin == -1)
245 mp_msg(MSGT_OPEN,MSGL_STATUS,
246 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested,t);
247 /* now I would say the whole filename is shit, build our own */
248 strncpy(s, cue_filename, strlen(cue_filename) - 3 );
249 s[strlen(cue_filename) - 3] = '\0';
250 strcat(s, "img");
251 fd_bin = open (s, O_RDONLY);
252 if (fd_bin == -1)
254 mp_msg(MSGT_OPEN,MSGL_STATUS,
255 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested, s);
256 /* ok try it with path */
257 snprintf(t, sizeof( t ), "%s/%s", bincue_path, s);
258 fd_bin = open (t, O_RDONLY);
259 if (fd_bin == -1)
261 mp_msg(MSGT_OPEN,MSGL_STATUS,
262 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested, s);
264 /* I'll give up */
265 mp_msg(MSGT_OPEN,MSGL_ERR,
266 MSGTR_MPDEMUX_CUEREAD_CannotFindBinFile);
267 return -1;
270 } else strcpy(bin_filename, t);
272 } else strcpy(bin_filename, s);
274 } else strcpy(bin_filename, s);
278 mp_msg(MSGT_OPEN,MSGL_INFO,
279 MSGTR_MPDEMUX_CUEREAD_UsingBinFile, bin_filename);
280 return 0;
283 static inline int cue_msf_2_sector(int minute, int second, int frame) {
284 return frame + (second + minute * 60 ) * 75;
287 static inline int cue_get_msf(void) {
288 return cue_msf_2_sector (cue_current_pos.minute,
289 cue_current_pos.second,
290 cue_current_pos.frame);
293 static inline void cue_set_msf(unsigned int sect){
294 cue_current_pos.frame=sect%75;
295 sect=sect/75;
296 cue_current_pos.second=sect%60;
297 sect=sect/60;
298 cue_current_pos.minute=sect;
301 static inline int cue_mode_2_sector_size(int mode)
303 switch (mode)
305 case AUDIO: return AUDIO;
306 case MODE1_2352: return SIZERAW;
307 case MODE1_2048: return SIZEISO_MODE1;
308 case MODE2_2352: return SIZEISO_MODE2_RAW;
309 case MODE2_2336: return SIZEISO_MODE2_FORM2;
311 default:
312 mp_msg(MSGT_OPEN,MSGL_FATAL,
313 MSGTR_MPDEMUX_CUEREAD_UnknownModeForBinfile);
314 abort();
320 static int cue_read_cue (char *in_cue_filename)
322 struct stat filestat;
323 char sLine[256];
324 unsigned int sect;
325 char *s,*t;
326 int i;
328 /* we have no tracks at the beginning */
329 nTracks = 0;
331 fd_bin = 0;
333 /* split the filename into a path and filename part */
334 s = strdup(in_cue_filename);
335 t = strrchr(s, '/');
336 if (t == (char *)NULL)
337 t = ".";
338 else {
339 *t = '\0';
340 t = s;
341 if (*t == '\0')
342 strcpy(t, "/");
345 av_strlcpy(bincue_path,t,sizeof( bincue_path ));
346 mp_msg(MSGT_OPEN,MSGL_V,"dirname: %s, cuepath: %s\n", t, bincue_path);
348 /* no path at all? */
349 if (strcmp(bincue_path, ".") == 0) {
350 mp_msg(MSGT_OPEN,MSGL_V,"bincue_path: %s\n", bincue_path);
351 av_strlcpy(cue_filename,in_cue_filename,sizeof( cue_filename ));
352 } else {
353 av_strlcpy(cue_filename,in_cue_filename + strlen(bincue_path) + 1,
354 sizeof( cue_filename ));
359 /* open the cue file */
360 fd_cue = fopen (in_cue_filename, "r");
361 if (fd_cue == NULL)
363 mp_msg(MSGT_OPEN,MSGL_ERR,
364 MSGTR_MPDEMUX_CUEREAD_CannotOpenCueFile, in_cue_filename);
365 return -1;
368 /* read the first line and hand it to find_bin, which will
369 test more than one possible name of the file */
371 if(! fgets( sLine, 256, fd_cue ) )
373 mp_msg(MSGT_OPEN,MSGL_ERR,
374 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile, in_cue_filename);
375 fclose (fd_cue);
376 return -1;
379 if (cue_find_bin(sLine)) {
380 fclose (fd_cue);
381 return -1;
385 /* now build the track list */
386 /* red the next line and call our track finder */
387 if(! fgets( sLine, 256, fd_cue ) )
389 mp_msg(MSGT_OPEN,MSGL_ERR,
390 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile, in_cue_filename);
391 fclose (fd_cue);
392 return -1;
395 while(!feof(fd_cue))
397 if (cue_getTrackinfo(sLine, &tracks[nTracks++]) != 0)
399 mp_msg(MSGT_OPEN,MSGL_ERR,
400 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile, in_cue_filename);
401 fclose (fd_cue);
402 return -1;
406 /* make a fake track with stands for the Lead out */
407 if (fstat (fd_bin, &filestat) == -1) {
408 mp_msg(MSGT_OPEN,MSGL_ERR,
409 MSGTR_MPDEMUX_CUEREAD_ErrGettingBinFileSize);
410 fclose (fd_cue);
411 return -1;
414 sect = filestat.st_size / 2352;
416 tracks[nTracks].frame = sect%75;
417 sect=sect/75;
418 tracks[nTracks].second = sect%60;
419 sect=sect/60;
420 tracks[nTracks].minute = sect;
423 /* let's calculate the start sectors and offsets */
424 for(i = 0; i <= nTracks; i++)
426 tracks[i].start_sector = cue_msf_2_sector(tracks[i].minute,
427 tracks[nTracks].second,
428 tracks[nTracks].frame);
430 /* if we're the first track we don't need to offset of the one befor */
431 if (i == 0)
433 /* was always 0 on my svcds, but who knows */
434 tracks[0].start_offset = tracks[0].start_sector *
435 cue_mode_2_sector_size(tracks[0].mode);
436 } else
438 tracks[i].start_offset = tracks[i-1].start_offset +
439 (tracks[i].start_sector - tracks[i-1].start_sector) *
440 cue_mode_2_sector_size(tracks[i-1].mode);
444 fclose (fd_cue);
446 return fd_bin;
452 static int cue_read_toc_entry(void) {
454 int track = cue_current_pos.track - 1;
456 /* check if its a valid track, if not return -1 */
457 if (track >= nTracks)
458 return -1;
461 switch (tracks[track].mode)
463 case AUDIO:
464 cue_current_pos.mode = AUDIO;
465 break;
466 case MODE1_2352:
467 cue_current_pos.mode = MODE1;
468 break;
469 case MODE1_2048:
470 cue_current_pos.mode = MODE1;
471 break;
472 default: /* MODE2_2352 and MODE2_2336 */
473 cue_current_pos.mode = MODE2;
475 cue_current_pos.minute = tracks[track].minute;
476 cue_current_pos.second = tracks[track].second;
477 cue_current_pos.frame = tracks[track].frame;
479 return 0;
482 static int cue_vcd_seek_to_track (int track){
483 cue_current_pos.track = track;
485 if (cue_read_toc_entry ())
486 return -1;
488 return VCD_SECTOR_DATA * cue_get_msf();
491 static int cue_vcd_get_track_end (int track){
492 cue_current_pos.frame = tracks[track].frame;
493 cue_current_pos.second = tracks[track].second;
494 cue_current_pos.minute = tracks[track].minute;
496 return VCD_SECTOR_DATA * cue_get_msf();
499 static void cue_vcd_read_toc(void){
500 int i;
501 for (i = 0; i < nTracks; ++i) {
503 mp_msg(MSGT_OPEN,MSGL_INFO,
504 MSGTR_MPDEMUX_CUEREAD_InfoTrackFormat,
505 i+1,
506 tracks[i].mode,
507 tracks[i].minute,
508 tracks[i].second,
509 tracks[i].frame
514 static int cue_vcd_read(stream_t *stream, char *mem, int size) {
515 unsigned long position;
516 int track = cue_current_pos.track - 1;
518 position = tracks[track].start_offset +
519 (cue_msf_2_sector(cue_current_pos.minute,
520 cue_current_pos.second,
521 cue_current_pos.frame) -
522 tracks[track].start_sector)
523 * cue_mode_2_sector_size(tracks[track].mode);
526 if(position >= tracks[track+1].start_offset)
527 return 0;
529 if(lseek(fd_bin, position+VCD_SECTOR_OFFS, SEEK_SET) == -1) {
530 mp_msg(MSGT_OPEN,MSGL_ERR, MSGTR_MPDEMUX_CUEREAD_UnexpectedBinFileEOF);
531 return 0;
534 if(read(fd_bin, mem, VCD_SECTOR_DATA) != VCD_SECTOR_DATA) {
535 mp_msg(MSGT_OPEN,MSGL_ERR, MSGTR_MPDEMUX_CUEREAD_CannotReadNBytesOfPayload, VCD_SECTOR_DATA);
536 return 0;
539 cue_current_pos.frame++;
540 if (cue_current_pos.frame==75){
541 cue_current_pos.frame=0;
542 cue_current_pos.second++;
543 if (cue_current_pos.second==60){
544 cue_current_pos.second=0;
545 cue_current_pos.minute++;
549 return VCD_SECTOR_DATA;
552 static int seek(stream_t *s,off_t newpos) {
553 s->pos=newpos;
554 cue_set_msf(s->pos/VCD_SECTOR_DATA);
555 return 1;
559 static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
560 struct stream_priv_s* p = (struct stream_priv_s*)opts;
561 int ret,ret2,f,track = 0;
562 char *filename = NULL, *colon = NULL;
564 if(mode != STREAM_READ || !p->filename) {
565 m_struct_free(&stream_opts,opts);
566 return STREAM_UNSUPPORTED;
568 filename = strdup(p->filename);
569 if(!filename) {
570 m_struct_free(&stream_opts,opts);
571 return STREAM_UNSUPPORTED;
573 colon = strstr(filename, ":");
574 if(colon) {
575 if(strlen(colon)>1)
576 track = atoi(colon+1);
577 *colon = 0;
579 if(!track)
580 track = 1;
582 f = cue_read_cue(filename);
583 if(f < 0) {
584 m_struct_free(&stream_opts,opts);
585 return STREAM_UNSUPPORTED;
587 cue_vcd_read_toc();
588 ret2=cue_vcd_get_track_end(track);
589 ret=cue_vcd_seek_to_track(track);
590 if(ret<0){
591 mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_ErrTrackSelect " (seek)\n");
592 return STREAM_UNSUPPORTED;
594 mp_msg(MSGT_OPEN,MSGL_INFO,MSGTR_MPDEMUX_CUEREAD_CueStreamInfo_FilenameTrackTracksavail, filename, track, ret, ret2);
596 stream->fd = f;
597 stream->type = STREAMTYPE_VCDBINCUE;
598 stream->sector_size = VCD_SECTOR_DATA;
599 stream->flags = STREAM_READ | MP_STREAM_SEEK_FW;
600 stream->start_pos = ret;
601 stream->end_pos = ret2;
602 stream->fill_buffer = cue_vcd_read;
603 stream->seek = seek;
605 free(filename);
606 m_struct_free(&stream_opts,opts);
607 return STREAM_OK;
610 const stream_info_t stream_info_cue = {
611 "CUE track",
612 "cue",
613 "Albeu",
614 "based on the code from ???",
615 open_s,
616 { "cue", NULL },
617 &stream_opts,
618 1 // Urls are an option string