1 //=================== VideoCD BinCue ==========================
21 #include "libavutil/avstring.h"
23 #define byte unsigned char
25 #define SIZEISO_MODE1 2048
26 #define SIZEISO_MODE2_RAW 2352
27 #define SIZEISO_MODE2_FORM1 2048
28 #define SIZEISO_MODE2_FORM2 2336
38 static struct stream_priv_s
{
40 } stream_priv_dflts
= {
44 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
46 static const m_option_t stream_opts_fields
[] = {
47 { "string", ST_OFF(filename
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
48 { NULL
, NULL
, 0, 0, 0, 0, NULL
}
50 static const struct m_struct_st stream_opts
= {
52 sizeof(struct stream_priv_s
),
58 static int fd_bin
= 0;
60 static char bin_filename
[256];
62 static char cue_filename
[256];
63 static char bincue_path
[256];
69 unsigned short minute
;
70 unsigned short second
;
73 /* (min*60 + sec) * 75 + fps */
75 unsigned long start_sector
;
77 /* = the sizes in bytes off all tracks bevor this one */
78 /* its needed if there are mode1 tracks befor the mpeg tracks */
79 unsigned long start_offset
;
81 /* unsigned char num[3]; */
84 /* max 99 tracks on a cd */
85 static tTrack tracks
[100];
87 static struct cue_track_pos
{
90 unsigned short minute
;
91 unsigned short second
;
95 /* number of tracks on the cd */
96 static int nTracks
= 0;
98 static int digits2int(char s
[2], int errval
) {
99 uint8_t a
= s
[0] - '0';
100 uint8_t b
= s
[1] - '0';
106 /* presumes Line is preloaded with the "current" line of the file */
107 static int cue_getTrackinfo(char *Line
, tTrack
*track
)
112 if (strncmp(&Line
[2], "TRACK ", 6)==0)
114 /* strncpy(track->num, &Line[8], 2); track->num[2] = '\0'; */
116 track
->mode
= UNKNOWN
;
117 if(strncmp(&Line
[11], "AUDIO", 5)==0) track
->mode
= AUDIO
;
118 if(strncmp(&Line
[11], "MODE1/2352", 10)==0) track
->mode
= MODE1_2352
;
119 if(strncmp(&Line
[11], "MODE1/2048", 10)==0) track
->mode
= MODE1_2048
;
120 if(strncmp(&Line
[11], "MODE2/2352", 10)==0) track
->mode
= MODE2_2352
;
121 if(strncmp(&Line
[11], "MODE2/2336", 10)==0) track
->mode
= MODE2_2336
;
125 /* Get the track indexes */
127 if(! fgets( Line
, 256, fd_cue
) ) { break;}
129 if (strncmp(&Line
[2], "TRACK ", 6)==0)
131 /* next track starting */
135 /* Track 0 or 1, take the first an get fill the values*/
136 if (strncmp(&Line
[4], "INDEX ", 6)==0)
138 /* check stuff here so if the answer is false the else stuff below won't be executed */
139 if ((already_set
== 0) && digits2int(Line
+ 10, 100) <= 1)
143 track
->minute
= digits2int(Line
+ 13, 0);
144 track
->second
= digits2int(Line
+ 16, 0);
145 track
->frame
= digits2int(Line
+ 19, 0);
148 else if (strncmp(&Line
[4], "PREGAP ", 7)==0) { ; /* ignore */ }
149 else if (strncmp(&Line
[4], "FLAGS ", 6)==0) { ; /* ignore */ }
150 else mp_msg (MSGT_OPEN
,MSGL_INFO
,
151 MSGTR_MPDEMUX_CUEREAD_UnexpectedCuefileLine
, Line
);
158 /* FIXME: the string operations ( strcpy,strcat ) below depend
159 * on the arrays to have the same size, thus we need to make
160 * sure the sizes are in sync.
162 static int cue_find_bin (char *firstline
) {
167 /* get the filename out of that */
169 mp_msg (MSGT_OPEN
,MSGL_INFO
, "[bincue] cue_find_bin(%s)\n", firstline
);
170 if (strncmp(firstline
, "FILE \"",6)==0)
174 while ( firstline
[6 + i
] != '"')
176 bin_filename
[j
] = firstline
[6 + i
];
178 /* if I found a path info, than delete all bevor it */
179 switch (bin_filename
[j
])
194 bin_filename
[j
+1] = '\0';
198 /* now try to open that file, without path */
199 fd_bin
= open (bin_filename
, O_RDONLY
);
202 mp_msg(MSGT_OPEN
,MSGL_STATUS
, MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
,
205 /* now try to find it with the path of the cue file */
206 snprintf(s
,sizeof( s
),"%s/%s",bincue_path
,bin_filename
);
207 fd_bin
= open (s
, O_RDONLY
);
210 mp_msg(MSGT_OPEN
,MSGL_STATUS
,
211 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
, s
);
212 /* now I would say the whole filename is shit, build our own */
213 strncpy(s
, cue_filename
, strlen(cue_filename
) - 3 );
214 s
[strlen(cue_filename
) - 3] = '\0';
216 fd_bin
= open (s
, O_RDONLY
);
219 mp_msg(MSGT_OPEN
,MSGL_STATUS
,
220 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
, s
);
222 /* ok try it with path */
223 snprintf(t
, sizeof( t
), "%s/%s", bincue_path
, s
);
224 fd_bin
= open (t
, O_RDONLY
);
227 mp_msg(MSGT_OPEN
,MSGL_STATUS
,
228 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
,t
);
229 /* now I would say the whole filename is shit, build our own */
230 strncpy(s
, cue_filename
, strlen(cue_filename
) - 3 );
231 s
[strlen(cue_filename
) - 3] = '\0';
233 fd_bin
= open (s
, O_RDONLY
);
236 mp_msg(MSGT_OPEN
,MSGL_STATUS
,
237 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
, s
);
238 /* ok try it with path */
239 snprintf(t
, sizeof( t
), "%s/%s", bincue_path
, s
);
240 fd_bin
= open (t
, O_RDONLY
);
243 mp_msg(MSGT_OPEN
,MSGL_STATUS
,
244 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
, s
);
247 mp_msg(MSGT_OPEN
,MSGL_ERR
,
248 MSGTR_MPDEMUX_CUEREAD_CannotFindBinFile
);
252 } else strcpy(bin_filename
, t
);
254 } else strcpy(bin_filename
, s
);
256 } else strcpy(bin_filename
, s
);
260 mp_msg(MSGT_OPEN
,MSGL_INFO
,
261 MSGTR_MPDEMUX_CUEREAD_UsingBinFile
, bin_filename
);
265 static inline int cue_msf_2_sector(int minute
, int second
, int frame
) {
266 return frame
+ (second
+ minute
* 60 ) * 75;
269 static inline int cue_get_msf(void) {
270 return cue_msf_2_sector (cue_current_pos
.minute
,
271 cue_current_pos
.second
,
272 cue_current_pos
.frame
);
275 static inline void cue_set_msf(unsigned int sect
){
276 cue_current_pos
.frame
=sect
%75;
278 cue_current_pos
.second
=sect
%60;
280 cue_current_pos
.minute
=sect
;
283 static inline int cue_mode_2_sector_size(int mode
)
287 case AUDIO
: return AUDIO
;
288 case MODE1_2352
: return SIZERAW
;
289 case MODE1_2048
: return SIZEISO_MODE1
;
290 case MODE2_2352
: return SIZEISO_MODE2_RAW
;
291 case MODE2_2336
: return SIZEISO_MODE2_FORM2
;
294 mp_msg(MSGT_OPEN
,MSGL_FATAL
,
295 MSGTR_MPDEMUX_CUEREAD_UnknownModeForBinfile
);
302 static int cue_read_cue (char *in_cue_filename
)
304 struct stat filestat
;
310 /* we have no tracks at the beginning */
315 /* split the filename into a path and filename part */
316 s
= strdup(in_cue_filename
);
318 if (t
== (char *)NULL
)
327 av_strlcpy(bincue_path
,t
,sizeof( bincue_path
));
328 mp_msg(MSGT_OPEN
,MSGL_V
,"dirname: %s, cuepath: %s\n", t
, bincue_path
);
330 /* no path at all? */
331 if (strcmp(bincue_path
, ".") == 0) {
332 mp_msg(MSGT_OPEN
,MSGL_V
,"bincue_path: %s\n", bincue_path
);
333 av_strlcpy(cue_filename
,in_cue_filename
,sizeof( cue_filename
));
335 av_strlcpy(cue_filename
,in_cue_filename
+ strlen(bincue_path
) + 1,
336 sizeof( cue_filename
));
341 /* open the cue file */
342 fd_cue
= fopen (in_cue_filename
, "r");
345 mp_msg(MSGT_OPEN
,MSGL_ERR
,
346 MSGTR_MPDEMUX_CUEREAD_CannotOpenCueFile
, in_cue_filename
);
350 /* read the first line and hand it to find_bin, which will
351 test more than one possible name of the file */
353 if(! fgets( sLine
, 256, fd_cue
) )
355 mp_msg(MSGT_OPEN
,MSGL_ERR
,
356 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile
, in_cue_filename
);
361 if (cue_find_bin(sLine
)) {
367 /* now build the track list */
368 /* red the next line and call our track finder */
369 if(! fgets( sLine
, 256, fd_cue
) )
371 mp_msg(MSGT_OPEN
,MSGL_ERR
,
372 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile
, in_cue_filename
);
379 if (cue_getTrackinfo(sLine
, &tracks
[nTracks
++]) != 0)
381 mp_msg(MSGT_OPEN
,MSGL_ERR
,
382 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile
, in_cue_filename
);
388 /* make a fake track with stands for the Lead out */
389 if (fstat (fd_bin
, &filestat
) == -1) {
390 mp_msg(MSGT_OPEN
,MSGL_ERR
,
391 MSGTR_MPDEMUX_CUEREAD_ErrGettingBinFileSize
);
396 sect
= filestat
.st_size
/ 2352;
398 tracks
[nTracks
].frame
= sect
%75;
400 tracks
[nTracks
].second
= sect
%60;
402 tracks
[nTracks
].minute
= sect
;
405 /* let's calculate the start sectors and offsets */
406 for(i
= 0; i
<= nTracks
; i
++)
408 tracks
[i
].start_sector
= cue_msf_2_sector(tracks
[i
].minute
,
409 tracks
[nTracks
].second
,
410 tracks
[nTracks
].frame
);
412 /* if we're the first track we don't need to offset of the one befor */
415 /* was always 0 on my svcds, but who knows */
416 tracks
[0].start_offset
= tracks
[0].start_sector
*
417 cue_mode_2_sector_size(tracks
[0].mode
);
420 tracks
[i
].start_offset
= tracks
[i
-1].start_offset
+
421 (tracks
[i
].start_sector
- tracks
[i
-1].start_sector
) *
422 cue_mode_2_sector_size(tracks
[i
-1].mode
);
434 static int cue_read_toc_entry(void) {
436 int track
= cue_current_pos
.track
- 1;
438 /* check if its a valid track, if not return -1 */
439 if (track
>= nTracks
)
443 switch (tracks
[track
].mode
)
446 cue_current_pos
.mode
= AUDIO
;
449 cue_current_pos
.mode
= MODE1
;
452 cue_current_pos
.mode
= MODE1
;
454 default: /* MODE2_2352 and MODE2_2336 */
455 cue_current_pos
.mode
= MODE2
;
457 cue_current_pos
.minute
= tracks
[track
].minute
;
458 cue_current_pos
.second
= tracks
[track
].second
;
459 cue_current_pos
.frame
= tracks
[track
].frame
;
464 static int cue_vcd_seek_to_track (int track
){
465 cue_current_pos
.track
= track
;
467 if (cue_read_toc_entry ())
470 return VCD_SECTOR_DATA
* cue_get_msf();
473 static int cue_vcd_get_track_end (int track
){
474 cue_current_pos
.frame
= tracks
[track
].frame
;
475 cue_current_pos
.second
= tracks
[track
].second
;
476 cue_current_pos
.minute
= tracks
[track
].minute
;
478 return VCD_SECTOR_DATA
* cue_get_msf();
481 static void cue_vcd_read_toc(void){
483 for (i
= 0; i
< nTracks
; ++i
) {
485 mp_msg(MSGT_OPEN
,MSGL_INFO
,
486 MSGTR_MPDEMUX_CUEREAD_InfoTrackFormat
,
496 static int cue_vcd_read(stream_t
*stream
, char *mem
, int size
) {
497 unsigned long position
;
498 int track
= cue_current_pos
.track
- 1;
500 position
= tracks
[track
].start_offset
+
501 (cue_msf_2_sector(cue_current_pos
.minute
,
502 cue_current_pos
.second
,
503 cue_current_pos
.frame
) -
504 tracks
[track
].start_sector
)
505 * cue_mode_2_sector_size(tracks
[track
].mode
);
508 if(position
>= tracks
[track
+1].start_offset
)
511 if(lseek(fd_bin
, position
+VCD_SECTOR_OFFS
, SEEK_SET
) == -1) {
512 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_MPDEMUX_CUEREAD_UnexpectedBinFileEOF
);
516 if(read(fd_bin
, mem
, VCD_SECTOR_DATA
) != VCD_SECTOR_DATA
) {
517 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_MPDEMUX_CUEREAD_CannotReadNBytesOfPayload
, VCD_SECTOR_DATA
);
521 cue_current_pos
.frame
++;
522 if (cue_current_pos
.frame
==75){
523 cue_current_pos
.frame
=0;
524 cue_current_pos
.second
++;
525 if (cue_current_pos
.second
==60){
526 cue_current_pos
.second
=0;
527 cue_current_pos
.minute
++;
531 return VCD_SECTOR_DATA
;
534 static int seek(stream_t
*s
,off_t newpos
) {
536 cue_set_msf(s
->pos
/VCD_SECTOR_DATA
);
541 static int open_s(stream_t
*stream
,int mode
, void* opts
, int* file_format
) {
542 struct stream_priv_s
* p
= (struct stream_priv_s
*)opts
;
543 int ret
,ret2
,f
,track
= 0;
544 char *filename
= NULL
, *colon
= NULL
;
546 if(mode
!= STREAM_READ
|| !p
->filename
) {
547 m_struct_free(&stream_opts
,opts
);
548 return STREAM_UNSUPPORTED
;
550 filename
= strdup(p
->filename
);
552 m_struct_free(&stream_opts
,opts
);
553 return STREAM_UNSUPPORTED
;
555 colon
= strstr(filename
, ":");
558 track
= atoi(colon
+1);
564 f
= cue_read_cue(filename
);
566 m_struct_free(&stream_opts
,opts
);
567 return STREAM_UNSUPPORTED
;
570 ret2
=cue_vcd_get_track_end(track
);
571 ret
=cue_vcd_seek_to_track(track
);
573 mp_msg(MSGT_OPEN
,MSGL_ERR
,MSGTR_ErrTrackSelect
" (seek)\n");
574 return STREAM_UNSUPPORTED
;
576 mp_msg(MSGT_OPEN
,MSGL_INFO
,MSGTR_MPDEMUX_CUEREAD_CueStreamInfo_FilenameTrackTracksavail
, filename
, track
, ret
, ret2
);
579 stream
->type
= STREAMTYPE_VCDBINCUE
;
580 stream
->sector_size
= VCD_SECTOR_DATA
;
581 stream
->flags
= STREAM_READ
| STREAM_SEEK_FW
;
582 stream
->start_pos
= ret
;
583 stream
->end_pos
= ret2
;
584 stream
->fill_buffer
= cue_vcd_read
;
588 m_struct_free(&stream_opts
,opts
);
592 const stream_info_t stream_info_cue
= {
596 "based on the code from ???",
600 1 // Urls are an option string