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>
39 #include "libavutil/avstring.h"
41 #define byte unsigned char
43 #define SIZEISO_MODE1 2048
44 #define SIZEISO_MODE2_RAW 2352
45 #define SIZEISO_MODE2_FORM1 2048
46 #define SIZEISO_MODE2_FORM2 2336
56 static struct stream_priv_s
{
58 } stream_priv_dflts
= {
62 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
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
= {
70 sizeof(struct stream_priv_s
),
76 static int fd_bin
= 0;
78 static char bin_filename
[256];
80 static char cue_filename
[256];
81 static char bincue_path
[256];
87 unsigned short minute
;
88 unsigned short second
;
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]; */
102 /* max 99 tracks on a cd */
103 static tTrack tracks
[100];
105 static struct cue_track_pos
{
108 unsigned short minute
;
109 unsigned short second
;
110 unsigned short frame
;
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';
124 /* presumes Line is preloaded with the "current" line of the file */
125 static int cue_getTrackinfo(char *Line
, tTrack
*track
)
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
;
143 /* Get the track indexes */
145 if(! fgets( Line
, 256, fd_cue
) ) { break;}
147 if (strncmp(&Line
[2], "TRACK ", 6)==0)
149 /* next track starting */
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)
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
);
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
) {
185 /* get the filename out of that */
187 mp_msg (MSGT_OPEN
,MSGL_INFO
, "[bincue] cue_find_bin(%s)\n", firstline
);
188 if (strncmp(firstline
, "FILE \"",6)==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
])
212 bin_filename
[j
+1] = '\0';
216 /* now try to open that file, without path */
217 fd_bin
= open (bin_filename
, O_RDONLY
);
220 mp_msg(MSGT_OPEN
,MSGL_STATUS
, MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
,
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
);
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';
234 fd_bin
= open (s
, O_RDONLY
);
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
);
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';
251 fd_bin
= open (s
, O_RDONLY
);
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
);
261 mp_msg(MSGT_OPEN
,MSGL_STATUS
,
262 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
, s
);
265 mp_msg(MSGT_OPEN
,MSGL_ERR
,
266 MSGTR_MPDEMUX_CUEREAD_CannotFindBinFile
);
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
);
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;
296 cue_current_pos
.second
=sect
%60;
298 cue_current_pos
.minute
=sect
;
301 static inline int cue_mode_2_sector_size(int 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
;
312 mp_msg(MSGT_OPEN
,MSGL_FATAL
,
313 MSGTR_MPDEMUX_CUEREAD_UnknownModeForBinfile
);
320 static int cue_read_cue (char *in_cue_filename
)
322 struct stat filestat
;
328 /* we have no tracks at the beginning */
333 /* split the filename into a path and filename part */
334 s
= strdup(in_cue_filename
);
336 if (t
== (char *)NULL
)
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
));
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");
363 mp_msg(MSGT_OPEN
,MSGL_ERR
,
364 MSGTR_MPDEMUX_CUEREAD_CannotOpenCueFile
, in_cue_filename
);
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
);
379 if (cue_find_bin(sLine
)) {
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
);
397 if (cue_getTrackinfo(sLine
, &tracks
[nTracks
++]) != 0)
399 mp_msg(MSGT_OPEN
,MSGL_ERR
,
400 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile
, in_cue_filename
);
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
);
414 sect
= filestat
.st_size
/ 2352;
416 tracks
[nTracks
].frame
= sect
%75;
418 tracks
[nTracks
].second
= 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 */
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
);
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
);
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
)
461 switch (tracks
[track
].mode
)
464 cue_current_pos
.mode
= AUDIO
;
467 cue_current_pos
.mode
= MODE1
;
470 cue_current_pos
.mode
= MODE1
;
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
;
482 static int cue_vcd_seek_to_track (int track
){
483 cue_current_pos
.track
= track
;
485 if (cue_read_toc_entry ())
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){
501 for (i
= 0; i
< nTracks
; ++i
) {
503 mp_msg(MSGT_OPEN
,MSGL_INFO
,
504 MSGTR_MPDEMUX_CUEREAD_InfoTrackFormat
,
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
)
529 if(lseek(fd_bin
, position
+VCD_SECTOR_OFFS
, SEEK_SET
) == -1) {
530 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_MPDEMUX_CUEREAD_UnexpectedBinFileEOF
);
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
);
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
) {
554 cue_set_msf(s
->pos
/VCD_SECTOR_DATA
);
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
);
570 m_struct_free(&stream_opts
,opts
);
571 return STREAM_UNSUPPORTED
;
573 colon
= strstr(filename
, ":");
576 track
= atoi(colon
+1);
582 f
= cue_read_cue(filename
);
584 m_struct_free(&stream_opts
,opts
);
585 return STREAM_UNSUPPORTED
;
588 ret2
=cue_vcd_get_track_end(track
);
589 ret
=cue_vcd_seek_to_track(track
);
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
);
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
;
606 m_struct_free(&stream_opts
,opts
);
610 const stream_info_t stream_info_cue
= {
614 "based on the code from ???",
618 1 // Urls are an option string