Use MSGT_DECVIDEO in a video decoder.
[mplayer/glamo.git] / stream / stream_cue.c
blobaa6dfd45b98611f2fde17e374dfa2ee9b2573b5e
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 SIZERAW 2352
42 #define SIZEISO_MODE1 2048
43 #define SIZEISO_MODE2_RAW 2352
44 #define SIZEISO_MODE2_FORM1 2048
45 #define SIZEISO_MODE2_FORM2 2336
46 #define AUDIO 0
47 #define MODE1 1
48 #define MODE2 2
49 #define MODE1_2352 10
50 #define MODE2_2352 20
51 #define MODE1_2048 30
52 #define MODE2_2336 40
53 #define UNKNOWN -1
55 static struct stream_priv_s {
56 char* filename;
57 } stream_priv_dflts = {
58 NULL
61 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
62 /// URL definition
63 static const m_option_t stream_opts_fields[] = {
64 { "string", ST_OFF(filename), CONF_TYPE_STRING, 0, 0 ,0, NULL},
65 { NULL, NULL, 0, 0, 0, 0, NULL }
67 static const struct m_struct_st stream_opts = {
68 "cue",
69 sizeof(struct stream_priv_s),
70 &stream_priv_dflts,
71 stream_opts_fields
74 static char cue_filename[256];
75 static char bincue_path[256];
78 typedef struct track
80 unsigned short mode;
81 unsigned short minute;
82 unsigned short second;
83 unsigned short frame;
85 /* (min*60 + sec) * 75 + fps */
87 unsigned long start_sector;
89 /* = the sizes in bytes off all tracks bevor this one */
90 /* its needed if there are mode1 tracks befor the mpeg tracks */
91 unsigned long start_offset;
93 /* unsigned char num[3]; */
94 } tTrack;
96 /* max 99 tracks on a cd */
97 static tTrack tracks[100];
99 static struct cue_track_pos {
100 int track;
101 unsigned short mode;
102 unsigned short minute;
103 unsigned short second;
104 unsigned short frame;
105 } cue_current_pos;
107 /* number of tracks on the cd */
108 static int nTracks = 0;
110 static int digits2int(char s[2], int errval) {
111 uint8_t a = s[0] - '0';
112 uint8_t b = s[1] - '0';
113 if (a > 9 || b > 9)
114 return errval;
115 return a * 10 + b;
118 /* presumes Line is preloaded with the "current" line of the file */
119 static int cue_getTrackinfo(FILE *fd_cue, char *Line, tTrack *track)
121 int already_set = 0;
123 /* Get the 'mode' */
124 if (strncmp(&Line[2], "TRACK ", 6)==0)
126 /* strncpy(track->num, &Line[8], 2); track->num[2] = '\0'; */
128 track->mode = UNKNOWN;
129 if(strncmp(&Line[11], "AUDIO", 5)==0) track->mode = AUDIO;
130 if(strncmp(&Line[11], "MODE1/2352", 10)==0) track->mode = MODE1_2352;
131 if(strncmp(&Line[11], "MODE1/2048", 10)==0) track->mode = MODE1_2048;
132 if(strncmp(&Line[11], "MODE2/2352", 10)==0) track->mode = MODE2_2352;
133 if(strncmp(&Line[11], "MODE2/2336", 10)==0) track->mode = MODE2_2336;
135 else return 1;
137 /* Get the track indexes */
138 while(1) {
139 if(! fgets( Line, 256, fd_cue ) ) { break;}
141 if (strncmp(&Line[2], "TRACK ", 6)==0)
143 /* next track starting */
144 break;
147 /* Track 0 or 1, take the first an get fill the values*/
148 if (strncmp(&Line[4], "INDEX ", 6)==0)
150 /* check stuff here so if the answer is false the else stuff below won't be executed */
151 if ((already_set == 0) && digits2int(Line + 10, 100) <= 1)
153 already_set = 1;
155 track->minute = digits2int(Line + 13, 0);
156 track->second = digits2int(Line + 16, 0);
157 track->frame = digits2int(Line + 19, 0);
160 else if (strncmp(&Line[4], "PREGAP ", 7)==0) { ; /* ignore */ }
161 else if (strncmp(&Line[4], "FLAGS ", 6)==0) { ; /* ignore */ }
162 else mp_msg (MSGT_OPEN,MSGL_INFO,
163 MSGTR_MPDEMUX_CUEREAD_UnexpectedCuefileLine, Line);
165 return 0;
170 /* FIXME: the string operations ( strcpy,strcat ) below depend
171 * on the arrays to have the same size, thus we need to make
172 * sure the sizes are in sync.
174 static int cue_find_bin (char *firstline) {
175 const char *cur_name;
176 int i,j;
177 char bin_filename[256];
178 char s[256];
179 char t[256];
180 int fd_bin;
182 /* get the filename out of that */
183 /* 12345 6 */
184 mp_msg (MSGT_OPEN,MSGL_INFO, "[bincue] cue_find_bin(%s)\n", firstline);
185 if (strncmp(firstline, "FILE \"",6)==0)
187 i = 0;
188 j = 0;
189 while ( firstline[6 + i] != '"')
191 bin_filename[j] = firstline[6 + i];
193 /* if I found a path info, than delete all bevor it */
194 switch (bin_filename[j])
196 case '\\':
197 j = 0;
198 break;
200 case '/':
201 j = 0;
202 break;
204 default:
205 j++;
207 i++;
209 bin_filename[j+1] = '\0';
213 fd_bin = -1;
214 for (i = 0; fd_bin == -1 && i < 6; i++) {
215 switch (i) {
216 case 0:
217 /* now try to open that file, without path */
218 cur_name = bin_filename;
219 break;
220 case 1:
221 /* now try to find it with the path of the cue file */
222 snprintf(s,sizeof( s ),"%s/%s",bincue_path,bin_filename);
223 cur_name = s;
224 break;
225 case 2:
226 /* now I would say the whole filename is shit, build our own */
227 strncpy(s, cue_filename, strlen(cue_filename) - 3 );
228 s[strlen(cue_filename) - 3] = '\0';
229 strcat(s, "bin");
230 cur_name = s;
231 break;
232 case 3:
233 /* ok try it with path */
234 snprintf(t, sizeof( t ), "%s/%s", bincue_path, s);
235 fd_bin = open (t, O_RDONLY);
236 cur_name = t;
237 break;
238 case 4:
239 /* now I would say the whole filename is shit, build our own */
240 strncpy(s, cue_filename, strlen(cue_filename) - 3 );
241 s[strlen(cue_filename) - 3] = '\0';
242 strcat(s, "img");
243 cur_name = s;
244 break;
245 case 5:
246 /* ok try it with path */
247 snprintf(t, sizeof( t ), "%s/%s", bincue_path, s);
248 cur_name = t;
249 break;
251 fd_bin = open(cur_name, O_RDONLY);
252 if (fd_bin == -1) {
253 mp_msg(MSGT_OPEN,MSGL_STATUS, MSGTR_MPDEMUX_CUEREAD_BinFilenameTested,
254 cur_name);
258 if (fd_bin == -1)
260 /* I'll give up */
261 mp_msg(MSGT_OPEN,MSGL_ERR,
262 MSGTR_MPDEMUX_CUEREAD_CannotFindBinFile);
263 return -1;
266 mp_msg(MSGT_OPEN,MSGL_INFO,
267 MSGTR_MPDEMUX_CUEREAD_UsingBinFile, cur_name);
268 return fd_bin;
271 static inline int cue_msf_2_sector(int minute, int second, int frame) {
272 return frame + (second + minute * 60 ) * 75;
275 static inline int cue_get_msf(void) {
276 return cue_msf_2_sector (cue_current_pos.minute,
277 cue_current_pos.second,
278 cue_current_pos.frame);
281 static inline void cue_set_msf(unsigned int sect){
282 cue_current_pos.frame=sect%75;
283 sect=sect/75;
284 cue_current_pos.second=sect%60;
285 sect=sect/60;
286 cue_current_pos.minute=sect;
289 static inline int cue_mode_2_sector_size(int mode)
291 switch (mode)
293 case AUDIO: return AUDIO;
294 case MODE1_2352: return SIZERAW;
295 case MODE1_2048: return SIZEISO_MODE1;
296 case MODE2_2352: return SIZEISO_MODE2_RAW;
297 case MODE2_2336: return SIZEISO_MODE2_FORM2;
299 default:
300 mp_msg(MSGT_OPEN,MSGL_FATAL,
301 MSGTR_MPDEMUX_CUEREAD_UnknownModeForBinfile);
302 abort();
308 static int cue_read_cue (char *in_cue_filename)
310 struct stat filestat;
311 char sLine[256];
312 unsigned int sect;
313 char *s,*t;
314 int i;
315 int fd_bin;
316 FILE *fd_cue;
318 /* we have no tracks at the beginning */
319 nTracks = 0;
321 /* split the filename into a path and filename part */
322 s = strdup(in_cue_filename);
323 t = strrchr(s, '/');
324 if (t == (char *)NULL)
325 t = ".";
326 else {
327 *t = '\0';
328 t = s;
329 if (*t == '\0')
330 strcpy(t, "/");
333 av_strlcpy(bincue_path,t,sizeof( bincue_path ));
334 mp_msg(MSGT_OPEN,MSGL_V,"dirname: %s, cuepath: %s\n", t, bincue_path);
335 free(s);
336 s = t = NULL;
338 /* no path at all? */
339 if (strcmp(bincue_path, ".") == 0) {
340 mp_msg(MSGT_OPEN,MSGL_V,"bincue_path: %s\n", bincue_path);
341 av_strlcpy(cue_filename,in_cue_filename,sizeof( cue_filename ));
342 } else {
343 av_strlcpy(cue_filename,in_cue_filename + strlen(bincue_path) + 1,
344 sizeof( cue_filename ));
349 /* open the cue file */
350 fd_cue = fopen (in_cue_filename, "r");
351 if (fd_cue == NULL)
353 mp_msg(MSGT_OPEN,MSGL_ERR,
354 MSGTR_MPDEMUX_CUEREAD_CannotOpenCueFile, in_cue_filename);
355 return -1;
358 /* read the first line and hand it to find_bin, which will
359 test more than one possible name of the file */
361 if(! fgets( sLine, sizeof(sLine), fd_cue ) )
363 mp_msg(MSGT_OPEN,MSGL_ERR,
364 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile, in_cue_filename);
365 fclose (fd_cue);
366 return -1;
369 fd_bin = cue_find_bin(sLine);
370 if (fd_bin == -1) {
371 fclose (fd_cue);
372 return -1;
376 /* now build the track list */
377 /* red the next line and call our track finder */
378 if(! fgets( sLine, sizeof(sLine), fd_cue ) )
380 mp_msg(MSGT_OPEN,MSGL_ERR,
381 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile, in_cue_filename);
382 fclose (fd_cue);
383 return -1;
386 while(!feof(fd_cue))
388 if (cue_getTrackinfo(fd_cue, sLine, &tracks[nTracks++]) != 0)
390 mp_msg(MSGT_OPEN,MSGL_ERR,
391 MSGTR_MPDEMUX_CUEREAD_ErrReadingFromCueFile, in_cue_filename);
392 fclose (fd_cue);
393 return -1;
397 /* make a fake track with stands for the Lead out */
398 if (fstat (fd_bin, &filestat) == -1) {
399 mp_msg(MSGT_OPEN,MSGL_ERR,
400 MSGTR_MPDEMUX_CUEREAD_ErrGettingBinFileSize);
401 fclose (fd_cue);
402 return -1;
405 sect = filestat.st_size / 2352;
407 tracks[nTracks].frame = sect%75;
408 sect=sect/75;
409 tracks[nTracks].second = sect%60;
410 sect=sect/60;
411 tracks[nTracks].minute = sect;
414 /* let's calculate the start sectors and offsets */
415 for(i = 0; i <= nTracks; i++)
417 tracks[i].start_sector = cue_msf_2_sector(tracks[i].minute,
418 tracks[nTracks].second,
419 tracks[nTracks].frame);
421 /* if we're the first track we don't need to offset of the one befor */
422 if (i == 0)
424 /* was always 0 on my svcds, but who knows */
425 tracks[0].start_offset = tracks[0].start_sector *
426 cue_mode_2_sector_size(tracks[0].mode);
427 } else
429 tracks[i].start_offset = tracks[i-1].start_offset +
430 (tracks[i].start_sector - tracks[i-1].start_sector) *
431 cue_mode_2_sector_size(tracks[i-1].mode);
435 fclose (fd_cue);
437 return fd_bin;
443 static int cue_read_toc_entry(int track) {
444 /* check if its a valid track, if not return -1 */
445 if (track <= 0 || track > nTracks)
446 return -1;
449 cue_current_pos.track = track;
450 track--;
451 switch (tracks[track].mode)
453 case AUDIO:
454 cue_current_pos.mode = AUDIO;
455 break;
456 case MODE1_2352:
457 cue_current_pos.mode = MODE1;
458 break;
459 case MODE1_2048:
460 cue_current_pos.mode = MODE1;
461 break;
462 default: /* MODE2_2352 and MODE2_2336 */
463 cue_current_pos.mode = MODE2;
465 cue_current_pos.minute = tracks[track].minute;
466 cue_current_pos.second = tracks[track].second;
467 cue_current_pos.frame = tracks[track].frame;
469 return 0;
472 static int cue_vcd_get_track_end (int track){
473 int sector = cue_msf_2_sector(tracks[track].minute, tracks[track].second,
474 tracks[track].frame);
476 return VCD_SECTOR_DATA * sector;
479 static int seek(stream_t *s,off_t newpos) {
480 s->pos=newpos;
481 cue_set_msf(s->pos/VCD_SECTOR_DATA);
482 return 1;
485 static int cue_vcd_seek_to_track (stream_t *stream, int track){
486 int pos;
487 if (cue_read_toc_entry (track))
488 return -1;
490 pos = VCD_SECTOR_DATA * cue_get_msf();
491 stream->start_pos = pos;
492 stream->end_pos = cue_vcd_get_track_end(track);
493 seek(stream, pos);
494 return pos;
497 static void cue_vcd_read_toc(void){
498 int i;
499 for (i = 0; i < nTracks; ++i) {
501 mp_msg(MSGT_OPEN,MSGL_INFO,
502 MSGTR_MPDEMUX_CUEREAD_InfoTrackFormat,
503 i+1,
504 tracks[i].mode,
505 tracks[i].minute,
506 tracks[i].second,
507 tracks[i].frame
512 static int cue_vcd_read(stream_t *stream, char *mem, int size) {
513 unsigned long position;
514 int fd_bin = stream->fd;
515 int track = cue_current_pos.track - 1;
517 position = tracks[track].start_offset +
518 (cue_msf_2_sector(cue_current_pos.minute,
519 cue_current_pos.second,
520 cue_current_pos.frame) -
521 tracks[track].start_sector)
522 * cue_mode_2_sector_size(tracks[track].mode);
525 if(position >= tracks[track+1].start_offset)
526 return 0;
528 if(lseek(fd_bin, position+VCD_SECTOR_OFFS, SEEK_SET) == -1) {
529 mp_msg(MSGT_OPEN,MSGL_ERR, MSGTR_MPDEMUX_CUEREAD_UnexpectedBinFileEOF);
530 return 0;
533 if(read(fd_bin, mem, VCD_SECTOR_DATA) != VCD_SECTOR_DATA) {
534 mp_msg(MSGT_OPEN,MSGL_ERR, MSGTR_MPDEMUX_CUEREAD_CannotReadNBytesOfPayload, VCD_SECTOR_DATA);
535 return 0;
538 cue_current_pos.frame++;
539 if (cue_current_pos.frame==75){
540 cue_current_pos.frame=0;
541 cue_current_pos.second++;
542 if (cue_current_pos.second==60){
543 cue_current_pos.second=0;
544 cue_current_pos.minute++;
548 return VCD_SECTOR_DATA;
551 static int control(stream_t *stream, int cmd, void *arg) {
552 switch(cmd) {
553 case STREAM_CTRL_GET_NUM_CHAPTERS:
555 *(unsigned int *)arg = nTracks;
556 return STREAM_OK;
558 case STREAM_CTRL_SEEK_TO_CHAPTER:
560 int r;
561 unsigned int track = *(unsigned int *)arg + 1;
562 r = cue_vcd_seek_to_track(stream, track);
563 if (r >= 0) {
564 return STREAM_OK;
566 break;
568 case STREAM_CTRL_GET_CURRENT_CHAPTER:
570 *(unsigned int *)arg = cue_current_pos.track - 1;
571 return STREAM_OK;
574 return STREAM_UNSUPPORTED;
577 static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
578 struct stream_priv_s* p = (struct stream_priv_s*)opts;
579 int ret,f,track = 0;
580 char *filename = NULL, *colon = NULL;
582 if(mode != STREAM_READ || !p->filename) {
583 m_struct_free(&stream_opts,opts);
584 return STREAM_UNSUPPORTED;
586 filename = strdup(p->filename);
587 if(!filename) {
588 m_struct_free(&stream_opts,opts);
589 return STREAM_UNSUPPORTED;
591 colon = strstr(filename, ":");
592 if(colon) {
593 if(strlen(colon)>1)
594 track = atoi(colon+1);
595 *colon = 0;
597 if(!track)
598 track = 1;
600 f = cue_read_cue(filename);
601 if(f < 0) {
602 m_struct_free(&stream_opts,opts);
603 return STREAM_UNSUPPORTED;
605 cue_vcd_read_toc();
606 ret=cue_vcd_seek_to_track(stream, track);
607 if(ret<0){
608 mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_ErrTrackSelect " (seek)\n");
609 return STREAM_UNSUPPORTED;
611 mp_msg(MSGT_OPEN,MSGL_INFO,MSGTR_MPDEMUX_CUEREAD_CueStreamInfo_FilenameTrackTracksavail,
612 filename, track, ret, (int)stream->end_pos);
614 stream->fd = f;
615 stream->type = STREAMTYPE_VCDBINCUE;
616 stream->sector_size = VCD_SECTOR_DATA;
617 stream->flags = STREAM_READ | MP_STREAM_SEEK_FW;
618 stream->fill_buffer = cue_vcd_read;
619 stream->seek = seek;
620 stream->control = control;
622 free(filename);
623 m_struct_free(&stream_opts,opts);
624 return STREAM_OK;
627 const stream_info_t stream_info_cue = {
628 "CUE track",
629 "cue",
630 "Albeu",
631 "based on the code from ???",
632 open_s,
633 { "cue", NULL },
634 &stream_opts,
635 1 // Urls are an option string