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 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 /* presumes Line is preloaded with the "current" line of the file */
99 static int cue_getTrackinfo(char *Line
, tTrack
*track
)
108 if (strncmp(&Line
[2], "TRACK ", 6)==0)
110 /* strncpy(track->num, &Line[8], 2); track->num[2] = '\0'; */
112 track
->mode
= UNKNOWN
;
113 if(strncmp(&Line
[11], "AUDIO", 5)==0) track
->mode
= AUDIO
;
114 if(strncmp(&Line
[11], "MODE1/2352", 10)==0) track
->mode
= MODE1_2352
;
115 if(strncmp(&Line
[11], "MODE1/2048", 10)==0) track
->mode
= MODE1_2048
;
116 if(strncmp(&Line
[11], "MODE2/2352", 10)==0) track
->mode
= MODE2_2352
;
117 if(strncmp(&Line
[11], "MODE2/2336", 10)==0) track
->mode
= MODE2_2336
;
121 /* Get the track indexes */
123 if(! fgets( Line
, 256, fd_cue
) ) { break;}
125 if (strncmp(&Line
[2], "TRACK ", 6)==0)
127 /* next track starting */
131 /* Track 0 or 1, take the first an get fill the values*/
132 if (strncmp(&Line
[4], "INDEX ", 6)==0)
134 /* check stuff here so if the answer is false the else stuff below won't be executed */
135 strncpy(inum
, &Line
[10], 2); inum
[2] = '\0';
136 if ((already_set
== 0) &&
137 ((strcmp(inum
, "00")==0) || (strcmp(inum
, "01")==0)))
141 min
= ((Line
[13]-'0')<<4) | (Line
[14]-'0');
142 sec
= ((Line
[16]-'0')<<4) | (Line
[17]-'0');
143 fps
= ((Line
[19]-'0')<<4) | (Line
[20]-'0');
145 track
->minute
= (((min
>>4)*10) + (min
&0xf));
146 track
->second
= (((sec
>>4)*10) + (sec
&0xf));
147 track
->frame
= (((fps
>>4)*10) + (fps
&0xf));
150 else if (strncmp(&Line
[4], "PREGAP ", 7)==0) { ; /* ignore */ }
151 else if (strncmp(&Line
[4], "FLAGS ", 6)==0) { ; /* ignore */ }
152 else mp_msg (MSGT_OPEN
,MSGL_INFO
,
153 MSGTR_MPDEMUX_CUEREAD_UnexpectedCuefileLine
, Line
);
160 /* FIXME: the string operations ( strcpy,strcat ) below depend
161 * on the arrays to have the same size, thus we need to make
162 * sure the sizes are in sync.
164 static int cue_find_bin (char *firstline
) {
169 /* get the filename out of that */
171 mp_msg (MSGT_OPEN
,MSGL_INFO
, "[bincue] cue_find_bin(%s)\n", firstline
);
172 if (strncmp(firstline
, "FILE \"",6)==0)
176 while ( firstline
[6 + i
] != '"')
178 bin_filename
[j
] = firstline
[6 + i
];
180 /* if I found a path info, than delete all bevor it */
181 switch (bin_filename
[j
])
196 bin_filename
[j
+1] = '\0';
200 /* now try to open that file, without path */
201 fd_bin
= open (bin_filename
, O_RDONLY
);
204 mp_msg(MSGT_OPEN
,MSGL_STATUS
, MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
,
207 /* now try to find it with the path of the cue file */
208 snprintf(s
,sizeof( s
),"%s/%s",bincue_path
,bin_filename
);
209 fd_bin
= open (s
, O_RDONLY
);
212 mp_msg(MSGT_OPEN
,MSGL_STATUS
,
213 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
, s
);
214 /* now I would say the whole filename is shit, build our own */
215 strncpy(s
, cue_filename
, strlen(cue_filename
) - 3 );
216 s
[strlen(cue_filename
) - 3] = '\0';
218 fd_bin
= open (s
, O_RDONLY
);
221 mp_msg(MSGT_OPEN
,MSGL_STATUS
,
222 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
, s
);
224 /* ok try it with path */
225 snprintf(t
, sizeof( t
), "%s/%s", bincue_path
, s
);
226 fd_bin
= open (t
, O_RDONLY
);
229 mp_msg(MSGT_OPEN
,MSGL_STATUS
,
230 MSGTR_MPDEMUX_CUEREAD_BinFilenameTested
,t
);
231 /* now I would say the whole filename is shit, build our own */
232 strncpy(s
, cue_filename
, strlen(cue_filename
) - 3 );
233 s
[strlen(cue_filename
) - 3] = '\0';
235 fd_bin
= open (s
, O_RDONLY
);
238 mp_msg(MSGT_OPEN
,MSGL_STATUS
,
239 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
, s
);
249 mp_msg(MSGT_OPEN
,MSGL_ERR
,
250 MSGTR_MPDEMUX_CUEREAD_CannotFindBinFile
);
254 } else strcpy(bin_filename
, t
);
256 } else strcpy(bin_filename
, s
);
258 } else strcpy(bin_filename
, s
);
262 mp_msg(MSGT_OPEN
,MSGL_INFO
,
263 MSGTR_MPDEMUX_CUEREAD_UsingBinFile
, bin_filename
);
267 static inline int cue_msf_2_sector(int minute
, int second
, int frame
) {
268 return frame
+ (second
+ minute
* 60 ) * 75;
271 static inline int cue_get_msf(void) {
272 return cue_msf_2_sector (cue_current_pos
.minute
,
273 cue_current_pos
.second
,
274 cue_current_pos
.frame
);
277 static inline void cue_set_msf(unsigned int sect
){
278 cue_current_pos
.frame
=sect
%75;
280 cue_current_pos
.second
=sect
%60;
282 cue_current_pos
.minute
=sect
;
285 static inline int cue_mode_2_sector_size(int mode
)
289 case AUDIO
: return AUDIO
;
290 case MODE1_2352
: return SIZERAW
;
291 case MODE1_2048
: return SIZEISO_MODE1
;
292 case MODE2_2352
: return SIZEISO_MODE2_RAW
;
293 case MODE2_2336
: return SIZEISO_MODE2_FORM2
;
296 mp_msg(MSGT_OPEN
,MSGL_FATAL
,
297 MSGTR_MPDEMUX_CUEREAD_UnknownModeForBinfile
);
304 static int cue_read_cue (char *in_cue_filename
)
306 struct stat filestat
;
312 /* we have no tracks at the beginning */
317 /* split the filename into a path and filename part */
318 s
= strdup(in_cue_filename
);
320 if (t
== (char *)NULL
)
329 av_strlcpy(bincue_path
,t
,sizeof( bincue_path
));
330 mp_msg(MSGT_OPEN
,MSGL_V
,"dirname: %s, cuepath: %s\n", t
, bincue_path
);
332 /* no path at all? */
333 if (strcmp(bincue_path
, ".") == 0) {
334 mp_msg(MSGT_OPEN
,MSGL_V
,"bincue_path: %s\n", bincue_path
);
335 av_strlcpy(cue_filename
,in_cue_filename
,sizeof( cue_filename
));
337 av_strlcpy(cue_filename
,in_cue_filename
+ strlen(bincue_path
) + 1,
338 sizeof( cue_filename
));
343 /* open the cue file */
344 fd_cue
= fopen (in_cue_filename
, "r");
347 mp_msg(MSGT_OPEN
,MSGL_ERR
,
348 MSGTR_MPDEMUX_CUEREAD_CannotOpenCueFile
, in_cue_filename
);
352 /* read the first line and hand it to find_bin, which will
353 test more than one possible name of the file */
355 if(! fgets( sLine
, 256, fd_cue
) )
357 mp_msg(MSGT_OPEN
,MSGL_ERR
,
358 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile
, in_cue_filename
);
363 if (cue_find_bin(sLine
)) {
369 /* now build the track list */
370 /* red the next line and call our track finder */
371 if(! fgets( sLine
, 256, fd_cue
) )
373 mp_msg(MSGT_OPEN
,MSGL_ERR
,
374 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile
, in_cue_filename
);
381 if (cue_getTrackinfo(sLine
, &tracks
[nTracks
++]) != 0)
383 mp_msg(MSGT_OPEN
,MSGL_ERR
,
384 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile
, in_cue_filename
);
390 /* make a fake track with stands for the Lead out */
391 if (fstat (fd_bin
, &filestat
) == -1) {
392 mp_msg(MSGT_OPEN
,MSGL_ERR
,
393 MSGTR_MPDEMUX_CUEREAD_ErrGettingBinFileSize
);
398 sect
= filestat
.st_size
/ 2352;
400 tracks
[nTracks
].frame
= sect
%75;
402 tracks
[nTracks
].second
= sect
%60;
404 tracks
[nTracks
].minute
= sect
;
407 /* let's calculate the start sectors and offsets */
408 for(i
= 0; i
<= nTracks
; i
++)
410 tracks
[i
].start_sector
= cue_msf_2_sector(tracks
[i
].minute
,
411 tracks
[nTracks
].second
,
412 tracks
[nTracks
].frame
);
414 /* if we're the first track we don't need to offset of the one befor */
417 /* was always 0 on my svcds, but who knows */
418 tracks
[0].start_offset
= tracks
[0].start_sector
*
419 cue_mode_2_sector_size(tracks
[0].mode
);
422 tracks
[i
].start_offset
= tracks
[i
-1].start_offset
+
423 (tracks
[i
].start_sector
- tracks
[i
-1].start_sector
) *
424 cue_mode_2_sector_size(tracks
[i
-1].mode
);
436 static int cue_read_toc_entry(void) {
438 int track
= cue_current_pos
.track
- 1;
440 /* check if its a valid track, if not return -1 */
441 if (track
>= nTracks
)
445 switch (tracks
[track
].mode
)
448 cue_current_pos
.mode
= AUDIO
;
451 cue_current_pos
.mode
= MODE1
;
454 cue_current_pos
.mode
= MODE1
;
456 default: /* MODE2_2352 and MODE2_2336 */
457 cue_current_pos
.mode
= MODE2
;
459 cue_current_pos
.minute
= tracks
[track
].minute
;
460 cue_current_pos
.second
= tracks
[track
].second
;
461 cue_current_pos
.frame
= tracks
[track
].frame
;
466 static int cue_vcd_seek_to_track (int track
){
467 cue_current_pos
.track
= track
;
469 if (cue_read_toc_entry ())
472 return VCD_SECTOR_DATA
* cue_get_msf();
475 static int cue_vcd_get_track_end (int track
){
476 cue_current_pos
.frame
= tracks
[track
].frame
;
477 cue_current_pos
.second
= tracks
[track
].second
;
478 cue_current_pos
.minute
= tracks
[track
].minute
;
480 return VCD_SECTOR_DATA
* cue_get_msf();
483 static void cue_vcd_read_toc(void){
485 for (i
= 0; i
< nTracks
; ++i
) {
487 mp_msg(MSGT_OPEN
,MSGL_INFO
,
488 MSGTR_MPDEMUX_CUEREAD_InfoTrackFormat
,
498 static int cue_vcd_read(stream_t
*stream
, char *mem
, int size
) {
499 unsigned long position
;
500 int track
= cue_current_pos
.track
- 1;
502 position
= tracks
[track
].start_offset
+
503 (cue_msf_2_sector(cue_current_pos
.minute
,
504 cue_current_pos
.second
,
505 cue_current_pos
.frame
) -
506 tracks
[track
].start_sector
)
507 * cue_mode_2_sector_size(tracks
[track
].mode
);
510 if(position
>= tracks
[track
+1].start_offset
)
513 if(lseek(fd_bin
, position
+VCD_SECTOR_OFFS
, SEEK_SET
) == -1) {
514 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_MPDEMUX_CUEREAD_UnexpectedBinFileEOF
);
518 if(read(fd_bin
, mem
, VCD_SECTOR_DATA
) != VCD_SECTOR_DATA
) {
519 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_MPDEMUX_CUEREAD_CannotReadNBytesOfPayload
, VCD_SECTOR_DATA
);
523 cue_current_pos
.frame
++;
524 if (cue_current_pos
.frame
==75){
525 cue_current_pos
.frame
=0;
526 cue_current_pos
.second
++;
527 if (cue_current_pos
.second
==60){
528 cue_current_pos
.second
=0;
529 cue_current_pos
.minute
++;
533 return VCD_SECTOR_DATA
;
536 static int seek(stream_t
*s
,off_t newpos
) {
538 cue_set_msf(s
->pos
/VCD_SECTOR_DATA
);
543 static int open_s(stream_t
*stream
,int mode
, void* opts
, int* file_format
) {
544 struct stream_priv_s
* p
= (struct stream_priv_s
*)opts
;
545 int ret
,ret2
,f
,track
= 0;
546 char *filename
= NULL
, *colon
= NULL
;
548 if(mode
!= STREAM_READ
|| !p
->filename
) {
549 m_struct_free(&stream_opts
,opts
);
550 return STREAM_UNSUPPORTED
;
552 filename
= strdup(p
->filename
);
554 m_struct_free(&stream_opts
,opts
);
555 return STREAM_UNSUPPORTED
;
557 colon
= strstr(filename
, ":");
560 track
= atoi(colon
+1);
566 f
= cue_read_cue(filename
);
568 m_struct_free(&stream_opts
,opts
);
569 return STREAM_UNSUPPORTED
;
572 ret2
=cue_vcd_get_track_end(track
);
573 ret
=cue_vcd_seek_to_track(track
);
575 mp_msg(MSGT_OPEN
,MSGL_ERR
,MSGTR_ErrTrackSelect
" (seek)\n");
576 return STREAM_UNSUPPORTED
;
578 mp_msg(MSGT_OPEN
,MSGL_INFO
,MSGTR_MPDEMUX_CUEREAD_CueStreamInfo_FilenameTrackTracksavail
, filename
, track
, ret
, ret2
);
581 stream
->type
= STREAMTYPE_VCDBINCUE
;
582 stream
->sector_size
= VCD_SECTOR_DATA
;
583 stream
->flags
= STREAM_READ
| STREAM_SEEK_FW
;
584 stream
->start_pos
= ret
;
585 stream
->end_pos
= ret2
;
586 stream
->fill_buffer
= cue_vcd_read
;
590 m_struct_free(&stream_opts
,opts
);
594 const stream_info_t stream_info_cue
= {
598 "based on the code from ???",
602 1 // Urls are an option string