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.
25 #include <sys/types.h>
37 #include "libavutil/avstring.h"
40 #define SIZEISO_MODE1 2048
41 #define SIZEISO_MODE2_RAW 2352
42 #define SIZEISO_MODE2_FORM1 2048
43 #define SIZEISO_MODE2_FORM2 2336
53 static struct stream_priv_s
{
55 } stream_priv_dflts
= {
59 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
61 static const m_option_t stream_opts_fields
[] = {
62 { "string", ST_OFF(filename
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
63 { NULL
, NULL
, 0, 0, 0, 0, NULL
}
65 static const struct m_struct_st stream_opts
= {
67 sizeof(struct stream_priv_s
),
72 static char cue_filename
[256];
73 static char bincue_path
[256];
79 unsigned short minute
;
80 unsigned short second
;
83 /* (min*60 + sec) * 75 + fps */
85 unsigned long start_sector
;
87 /* = the sizes in bytes off all tracks bevor this one */
88 /* its needed if there are mode1 tracks befor the mpeg tracks */
89 unsigned long start_offset
;
91 /* unsigned char num[3]; */
94 /* max 99 tracks on a cd */
95 static tTrack tracks
[100];
97 static struct cue_track_pos
{
100 unsigned short minute
;
101 unsigned short second
;
102 unsigned short frame
;
105 /* number of tracks on the cd */
106 static int nTracks
= 0;
108 static int digits2int(const char s
[2], int errval
) {
109 uint8_t a
= s
[0] - '0';
110 uint8_t b
= s
[1] - '0';
116 /* presumes Line is preloaded with the "current" line of the file */
117 static int cue_getTrackinfo(FILE *fd_cue
, char *Line
, tTrack
*track
)
122 if (strncmp(&Line
[2], "TRACK ", 6)==0)
124 /* strncpy(track->num, &Line[8], 2); track->num[2] = '\0'; */
126 track
->mode
= UNKNOWN
;
127 if(strncmp(&Line
[11], "AUDIO", 5)==0) track
->mode
= AUDIO
;
128 if(strncmp(&Line
[11], "MODE1/2352", 10)==0) track
->mode
= MODE1_2352
;
129 if(strncmp(&Line
[11], "MODE1/2048", 10)==0) track
->mode
= MODE1_2048
;
130 if(strncmp(&Line
[11], "MODE2/2352", 10)==0) track
->mode
= MODE2_2352
;
131 if(strncmp(&Line
[11], "MODE2/2336", 10)==0) track
->mode
= MODE2_2336
;
135 /* Get the track indexes */
137 if(! fgets( Line
, 256, fd_cue
) ) { break;}
139 if (strncmp(&Line
[2], "TRACK ", 6)==0)
141 /* next track starting */
145 /* Track 0 or 1, take the first an get fill the values*/
146 if (strncmp(&Line
[4], "INDEX ", 6)==0)
148 /* check stuff here so if the answer is false the else stuff below won't be executed */
149 if ((already_set
== 0) && digits2int(Line
+ 10, 100) <= 1)
153 track
->minute
= digits2int(Line
+ 13, 0);
154 track
->second
= digits2int(Line
+ 16, 0);
155 track
->frame
= digits2int(Line
+ 19, 0);
158 else if (strncmp(&Line
[4], "PREGAP ", 7)==0) { ; /* ignore */ }
159 else if (strncmp(&Line
[4], "FLAGS ", 6)==0) { ; /* ignore */ }
160 else mp_tmsg (MSGT_OPEN
,MSGL_INFO
,
161 "[bincue] Unexpected cuefile line: %s\n", Line
);
168 /* FIXME: the string operations ( strcpy,strcat ) below depend
169 * on the arrays to have the same size, thus we need to make
170 * sure the sizes are in sync.
172 static int cue_find_bin (const char *firstline
) {
173 struct stat filestat
;
174 const char *cur_name
;
175 char bin_filename
[256];
181 /* get the filename out of that */
183 mp_msg (MSGT_OPEN
,MSGL_INFO
, "[bincue] cue_find_bin(%s)\n", firstline
);
184 if (strncmp(firstline
, "FILE \"",6)==0)
187 while ( *firstline
&& *firstline
!= '"')
189 bin_filename
[i
] = *firstline
++;
191 /* if I found a path info, then delete all before it */
192 switch (bin_filename
[i
])
207 bin_filename
[i
] = '\0';
210 for (i
= 0; fd_bin
== -1 && i
< 6; i
++) {
211 if (i
<=1 && bin_filename
[0] == '\0')
213 if (i
> 1 && strlen(cue_filename
) < 3)
218 /* now try to open that file, without path */
219 cur_name
= bin_filename
;
222 /* now try to find it with the path of the cue file */
223 snprintf(s
,sizeof( s
),"%s/%s",bincue_path
,bin_filename
);
227 /* now I would say the whole filename is shit, build our own */
228 av_strlcpy(s
, cue_filename
, strlen(cue_filename
) - 3 );
233 /* ok try it with path */
234 snprintf(t
, sizeof( t
), "%s/%s", bincue_path
, s
);
238 /* now I would say the whole filename is shit, build our own */
239 av_strlcpy(s
, cue_filename
, strlen(cue_filename
) - 3 );
244 /* ok try it with path */
245 snprintf(t
, sizeof( t
), "%s/%s", bincue_path
, s
);
249 fd_bin
= open(cur_name
, O_RDONLY
);
250 if (fstat(fd_bin
, &filestat
) == -1 || !S_ISREG(filestat
.st_mode
)) {
255 mp_tmsg(MSGT_OPEN
,MSGL_STATUS
, "[bincue] bin filename tested: %s\n",
263 mp_tmsg(MSGT_OPEN
,MSGL_ERR
,
264 "[bincue] Couldn't find the bin file - giving up.\n");
268 mp_tmsg(MSGT_OPEN
,MSGL_INFO
,
269 "[bincue] Using bin file %s.\n", cur_name
);
273 static inline int cue_msf_2_sector(int minute
, int second
, int frame
) {
274 return frame
+ (second
+ minute
* 60 ) * 75;
277 static inline int cue_get_msf(void) {
278 return cue_msf_2_sector (cue_current_pos
.minute
,
279 cue_current_pos
.second
,
280 cue_current_pos
.frame
);
283 static inline void cue_set_msf(unsigned int sect
){
284 cue_current_pos
.frame
=sect
%75;
286 cue_current_pos
.second
=sect
%60;
288 cue_current_pos
.minute
=sect
;
291 static inline int cue_mode_2_sector_size(int mode
)
295 case AUDIO
: return AUDIO
;
296 case MODE1_2352
: return SIZERAW
;
297 case MODE1_2048
: return SIZEISO_MODE1
;
298 case MODE2_2352
: return SIZEISO_MODE2_RAW
;
299 case MODE2_2336
: return SIZEISO_MODE2_FORM2
;
302 mp_tmsg(MSGT_OPEN
,MSGL_FATAL
,
303 "[bincue] unknown mode for binfile. Should not happen. Aborting.\n");
310 static int cue_read_cue (const char *in_cue_filename
)
312 struct stat filestat
;
320 /* we have no tracks at the beginning */
323 /* split the filename into a path and filename part */
324 s
= strdup(in_cue_filename
);
335 av_strlcpy(bincue_path
,t
,sizeof( bincue_path
));
336 mp_msg(MSGT_OPEN
,MSGL_V
,"dirname: %s, cuepath: %s\n", t
, bincue_path
);
340 /* no path at all? */
341 if (strcmp(bincue_path
, ".") == 0) {
342 mp_msg(MSGT_OPEN
,MSGL_V
,"bincue_path: %s\n", bincue_path
);
343 av_strlcpy(cue_filename
,in_cue_filename
,sizeof( cue_filename
));
345 av_strlcpy(cue_filename
,in_cue_filename
+ strlen(bincue_path
) + 1,
346 sizeof( cue_filename
));
351 /* open the cue file */
352 fd_cue
= fopen (in_cue_filename
, "r");
355 mp_tmsg(MSGT_OPEN
,MSGL_ERR
,
356 "[bincue] Cannot open %s.\n", in_cue_filename
);
360 /* read the first line and hand it to find_bin, which will
361 test more than one possible name of the file */
363 if(! fgets( sLine
, sizeof(sLine
), fd_cue
) )
365 mp_tmsg(MSGT_OPEN
,MSGL_ERR
,
366 "[bincue] Error reading from %s\n", in_cue_filename
);
371 fd_bin
= cue_find_bin(sLine
);
378 /* now build the track list */
379 /* red the next line and call our track finder */
380 if(! fgets( sLine
, sizeof(sLine
), fd_cue
) )
382 mp_tmsg(MSGT_OPEN
,MSGL_ERR
,
383 "[bincue] Error reading from %s\n", in_cue_filename
);
390 if (cue_getTrackinfo(fd_cue
, sLine
, &tracks
[nTracks
++]) != 0)
392 mp_tmsg(MSGT_OPEN
,MSGL_ERR
,
393 "[bincue] Error reading from %s\n", in_cue_filename
);
399 /* make a fake track with stands for the Lead out */
400 if (fstat (fd_bin
, &filestat
) == -1) {
401 mp_tmsg(MSGT_OPEN
,MSGL_ERR
,
402 "[bincue] Error getting size of bin file.\n");
407 sect
= filestat
.st_size
/ 2352;
409 tracks
[nTracks
].frame
= sect
%75;
411 tracks
[nTracks
].second
= sect
%60;
413 tracks
[nTracks
].minute
= sect
;
416 /* let's calculate the start sectors and offsets */
417 for(i
= 0; i
<= nTracks
; i
++)
419 tracks
[i
].start_sector
= cue_msf_2_sector(tracks
[i
].minute
,
420 tracks
[nTracks
].second
,
421 tracks
[nTracks
].frame
);
423 /* if we're the first track we don't need to offset of the one befor */
426 /* was always 0 on my svcds, but who knows */
427 tracks
[0].start_offset
= tracks
[0].start_sector
*
428 cue_mode_2_sector_size(tracks
[0].mode
);
431 tracks
[i
].start_offset
= tracks
[i
-1].start_offset
+
432 (tracks
[i
].start_sector
- tracks
[i
-1].start_sector
) *
433 cue_mode_2_sector_size(tracks
[i
-1].mode
);
445 static int cue_read_toc_entry(int track
) {
446 /* check if its a valid track, if not return -1 */
447 if (track
<= 0 || track
> nTracks
)
451 cue_current_pos
.track
= track
;
453 switch (tracks
[track
].mode
)
456 cue_current_pos
.mode
= AUDIO
;
459 cue_current_pos
.mode
= MODE1
;
462 cue_current_pos
.mode
= MODE1
;
464 default: /* MODE2_2352 and MODE2_2336 */
465 cue_current_pos
.mode
= MODE2
;
467 cue_current_pos
.minute
= tracks
[track
].minute
;
468 cue_current_pos
.second
= tracks
[track
].second
;
469 cue_current_pos
.frame
= tracks
[track
].frame
;
474 static int cue_vcd_get_track_end (int track
){
475 int sector
= cue_msf_2_sector(tracks
[track
].minute
, tracks
[track
].second
,
476 tracks
[track
].frame
);
478 return VCD_SECTOR_DATA
* sector
;
481 static int seek(stream_t
*s
,off_t newpos
) {
483 cue_set_msf(s
->pos
/VCD_SECTOR_DATA
);
487 static int cue_vcd_seek_to_track (stream_t
*stream
, int track
){
489 if (cue_read_toc_entry (track
))
492 pos
= VCD_SECTOR_DATA
* cue_get_msf();
493 stream
->start_pos
= pos
;
494 stream
->end_pos
= cue_vcd_get_track_end(track
);
499 static void cue_vcd_read_toc(void){
501 for (i
= 0; i
< nTracks
; ++i
) {
503 mp_tmsg(MSGT_OPEN
,MSGL_INFO
,
504 "track %02d: format=%d %02d:%02d:%02d\n",
514 static int cue_vcd_read(stream_t
*stream
, char *mem
, int size
) {
515 unsigned long position
;
516 int fd_bin
= stream
->fd
;
517 int track
= cue_current_pos
.track
- 1;
519 position
= tracks
[track
].start_offset
+
520 (cue_msf_2_sector(cue_current_pos
.minute
,
521 cue_current_pos
.second
,
522 cue_current_pos
.frame
) -
523 tracks
[track
].start_sector
)
524 * cue_mode_2_sector_size(tracks
[track
].mode
);
527 if(position
>= tracks
[track
+1].start_offset
)
530 if(lseek(fd_bin
, position
+VCD_SECTOR_OFFS
, SEEK_SET
) == -1) {
531 mp_tmsg(MSGT_OPEN
,MSGL_ERR
, "[bincue] unexpected end of bin file\n");
535 if(read(fd_bin
, mem
, VCD_SECTOR_DATA
) != VCD_SECTOR_DATA
) {
536 mp_tmsg(MSGT_OPEN
,MSGL_ERR
, "[bincue] Couldn't read %d bytes of payload.\n", VCD_SECTOR_DATA
);
540 cue_current_pos
.frame
++;
541 if (cue_current_pos
.frame
==75){
542 cue_current_pos
.frame
=0;
543 cue_current_pos
.second
++;
544 if (cue_current_pos
.second
==60){
545 cue_current_pos
.second
=0;
546 cue_current_pos
.minute
++;
550 return VCD_SECTOR_DATA
;
553 static int control(stream_t
*stream
, int cmd
, void *arg
) {
555 case STREAM_CTRL_GET_NUM_CHAPTERS
:
557 *(unsigned int *)arg
= nTracks
;
560 case STREAM_CTRL_SEEK_TO_CHAPTER
:
563 unsigned int track
= *(unsigned int *)arg
+ 1;
564 r
= cue_vcd_seek_to_track(stream
, track
);
570 case STREAM_CTRL_GET_CURRENT_CHAPTER
:
572 *(unsigned int *)arg
= cue_current_pos
.track
- 1;
576 return STREAM_UNSUPPORTED
;
579 static int open_s(stream_t
*stream
,int mode
, void* opts
, int* file_format
) {
580 struct stream_priv_s
* p
= (struct stream_priv_s
*)opts
;
582 char *filename
= NULL
, *colon
= NULL
;
584 if(mode
!= STREAM_READ
|| !p
->filename
) {
585 m_struct_free(&stream_opts
,opts
);
586 return STREAM_UNSUPPORTED
;
588 filename
= strdup(p
->filename
);
590 m_struct_free(&stream_opts
,opts
);
591 return STREAM_UNSUPPORTED
;
593 colon
= strstr(filename
, ":");
596 track
= atoi(colon
+1);
602 f
= cue_read_cue(filename
);
604 m_struct_free(&stream_opts
,opts
);
605 return STREAM_UNSUPPORTED
;
608 ret
=cue_vcd_seek_to_track(stream
, track
);
610 mp_msg(MSGT_OPEN
, MSGL_ERR
, "%s (seek)\n",
611 mp_gtext("Error selecting VCD track."));
612 return STREAM_UNSUPPORTED
;
614 mp_tmsg(MSGT_OPEN
, MSGL_INFO
,
615 "CUE stream_open, filename=%s, track=%d, "
616 "available tracks: %d -> %d\n",
617 filename
, track
, ret
, (int)stream
->end_pos
);
620 stream
->type
= STREAMTYPE_VCDBINCUE
;
621 stream
->sector_size
= VCD_SECTOR_DATA
;
622 stream
->flags
= STREAM_READ
| MP_STREAM_SEEK_FW
;
623 stream
->fill_buffer
= cue_vcd_read
;
625 stream
->control
= control
;
628 m_struct_free(&stream_opts
,opts
);
632 const stream_info_t stream_info_cue
= {
636 "based on the code from ???",
640 1 // Urls are an option string