16 #include <sys/cdrio.h>
20 #include <linux/cdrom.h>
22 #include <sys/types.h>
24 #include <sys/ioctl.h>
30 #define FIRST_AC3_AID 128
31 #define FIRST_DTS_AID 136
32 #define FIRST_MPG_AID 0
33 #define FIRST_PCM_AID 160
39 #include "stream_dvd.h"
40 #include "stream_dvd_common.h"
41 #include "libmpdemux/demuxer.h"
42 #include "libavutil/intreadwrite.h"
44 extern int stream_cache_size
;
45 extern char* dvd_device
;
47 int dvd_speed
=0; /* 0 => don't touch speed */
49 static void dvd_set_speed(char *device
, unsigned speed
)
51 #if defined(__linux__) && defined(SG_IO) && defined(GPCMD_SET_STREAMING)
53 unsigned char buffer
[28];
54 unsigned char cmd
[12];
55 struct sg_io_hdr sghdr
;
58 memset(&st
, 0, sizeof(st
));
60 if (stat(device
, &st
) == -1) return;
62 if (!S_ISBLK(st
.st_mode
)) return; /* not a block device */
65 case 0: /* don't touch speed setting */
67 case -1: /* restore default value */
68 if (dvd_speed
== 0) return; /* we haven't touched the speed setting */
69 mp_msg(MSGT_OPEN
, MSGL_INFO
, MSGTR_DVDrestoreSpeed
);
71 default: /* limit to <speed> KB/s */
72 // speed < 100 is multiple of DVD single speed (1350KB/s)
75 mp_msg(MSGT_OPEN
, MSGL_INFO
, MSGTR_DVDlimitSpeed
, speed
);
79 memset(&sghdr
, 0, sizeof(sghdr
));
80 sghdr
.interface_id
= 'S';
82 sghdr
.dxfer_direction
= SG_DXFER_TO_DEV
;
83 sghdr
.dxfer_len
= sizeof(buffer
);
84 sghdr
.dxferp
= buffer
;
85 sghdr
.cmd_len
= sizeof(cmd
);
88 memset(cmd
, 0, sizeof(cmd
));
89 cmd
[0] = GPCMD_SET_STREAMING
;
90 cmd
[10] = sizeof(buffer
);
92 memset(buffer
, 0, sizeof(buffer
));
93 /* first sector 0, last sector 0xffffffff */
94 AV_WB32(buffer
+ 8, 0xffffffff);
96 buffer
[0] = 4; /* restore default */
98 /* <speed> kilobyte */
99 AV_WB32(buffer
+ 12, speed
);
100 AV_WB32(buffer
+ 20, speed
);
103 AV_WB16(buffer
+ 18, 1000);
104 AV_WB16(buffer
+ 26, 1000);
106 fd
= open(device
, O_RDWR
| O_NONBLOCK
);
108 mp_msg(MSGT_OPEN
, MSGL_INFO
, MSGTR_DVDspeedCantOpen
);
112 if (ioctl(fd
, SG_IO
, &sghdr
) < 0)
113 mp_msg(MSGT_OPEN
, MSGL_INFO
, MSGTR_DVDlimitFail
);
115 mp_msg(MSGT_OPEN
, MSGL_INFO
, MSGTR_DVDlimitOk
);
121 #define LIBDVDREAD_VERSION(maj,min,micro) ((maj)*10000 + (min)*100 + (micro))
123 * Try to autodetect the libdvd-0.9.0 library
124 * (0.9.0 removed the <dvdread/dvd_udf.h> header, and moved the two defines
125 * DVD_VIDEO_LB_LEN and MAX_UDF_FILE_NAME_LEN from it to
126 * <dvdread/dvd_reader.h>)
128 #ifndef DVDREAD_VERSION
129 #if defined(DVD_VIDEO_LB_LEN) && defined(MAX_UDF_FILE_NAME_LEN)
130 #define DVDREAD_VERSION LIBDVDREAD_VERSION(0,9,0)
132 #define DVDREAD_VERSION LIBDVDREAD_VERSION(0,8,0)
136 char * dvd_audio_stream_types
[8] = { "ac3","unknown","mpeg1","mpeg2ext","lpcm","unknown","dts" };
137 char * dvd_audio_stream_channels
[6] = { "mono", "stereo", "unknown", "unknown", "5.1/6.1", "5.1" };
140 static struct stream_priv_s
{
142 } stream_priv_dflts
= {
146 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
148 static const m_option_t stream_opts_fields
[] = {
149 { "hostname", ST_OFF(title
), CONF_TYPE_INT
, M_OPT_MIN
, 1, 0, NULL
},
150 { NULL
, NULL
, 0, 0, 0, 0, NULL
}
152 static struct m_struct_st stream_opts
= {
154 sizeof(struct stream_priv_s
),
159 int dvd_parse_chapter_range(const m_option_t
*conf
, const char *range
) {
163 return M_OPT_MISSING_PARAM
;
166 dvd_last_chapter
= 0;
167 if(*range
&& isdigit(*range
)) {
168 dvd_chapter
= strtol(range
, &s
, 10);
170 mp_msg(MSGT_OPEN
, MSGL_ERR
, MSGTR_DVDinvalidChapterRange
, range
);
171 return M_OPT_INVALID
;
177 mp_msg(MSGT_OPEN
, MSGL_ERR
, MSGTR_DVDinvalidChapterRange
, range
);
178 return M_OPT_INVALID
;
184 mp_msg(MSGT_OPEN
, MSGL_ERR
, MSGTR_DVDinvalidChapterRange
, range
);
185 return M_OPT_INVALID
;
187 dvd_last_chapter
= strtol(s
, &t
, 10);
189 mp_msg(MSGT_OPEN
, MSGL_ERR
, MSGTR_DVDinvalidChapterRange
, range
);
190 return M_OPT_INVALID
;
195 int dvd_chapter_from_cell(dvd_priv_t
* dvd
,int title
,int cell
)
201 if(title
< 0 || cell
< 0){
204 /* for most DVD's chapter == cell */
205 /* but there are more complecated cases... */
206 if(chapter
>= dvd
->vmg_file
->tt_srpt
->title
[title
].nr_of_ptts
) {
207 chapter
= dvd
->vmg_file
->tt_srpt
->title
[title
].nr_of_ptts
-1;
209 title
= dvd
->tt_srpt
->title
[title
].vts_ttn
-1;
210 ptt
= dvd
->vts_file
->vts_ptt_srpt
->title
[title
].ptt
;
211 while(chapter
>= 0) {
212 pgc_id
= ptt
[chapter
].pgcn
;
213 pgn
= ptt
[chapter
].pgn
;
214 cur_pgc
= dvd
->vts_file
->vts_pgcit
->pgci_srp
[pgc_id
-1].pgc
;
215 if(cell
>= cur_pgc
->program_map
[pgn
-1]-1) {
220 /* didn't find a chapter ??? */
224 int dvd_lang_from_aid(stream_t
*stream
, int id
) {
227 if (!stream
) return 0;
230 for(i
=0;i
<d
->nr_of_channels
;i
++) {
231 if(d
->audio_streams
[i
].id
==id
)
232 return d
->audio_streams
[i
].language
;
237 int dvd_aid_from_lang(stream_t
*stream
, unsigned char* lang
) {
238 dvd_priv_t
*d
=stream
->priv
;
241 while(strlen(lang
)>=2) {
242 code
=lang
[1]|(lang
[0]<<8);
243 for(i
=0;i
<d
->nr_of_channels
;i
++) {
244 if(d
->audio_streams
[i
].language
==code
) {
245 mp_msg(MSGT_OPEN
,MSGL_INFO
,MSGTR_DVDaudioChannel
,
246 d
->audio_streams
[i
].id
, lang
[0],lang
[1]);
247 return d
->audio_streams
[i
].id
;
249 //printf("%X != %X (%c%c)\n",code,d->audio_streams[i].language,lang[0],lang[1]);
251 lang
+=2; while (lang
[0]==',' || lang
[0]==' ') ++lang
;
253 mp_msg(MSGT_OPEN
,MSGL_WARN
,MSGTR_DVDnoMatchingAudio
);
258 int dvd_number_of_subs(stream_t
*stream
) {
262 if (!stream
) return -1;
265 for (i
= 0; i
< d
->nr_of_subtitles
; i
++)
266 if (d
->subtitles
[i
].id
> maxid
) maxid
= d
->subtitles
[i
].id
;
270 int dvd_lang_from_sid(stream_t
*stream
, int id
) {
273 if (!stream
) return 0;
276 for (i
= 0; i
< d
->nr_of_subtitles
; i
++)
277 if (d
->subtitles
[i
].id
== id
&& d
->subtitles
[i
].language
) return d
->subtitles
[i
].language
;
281 int dvd_sid_from_lang(stream_t
*stream
, unsigned char* lang
) {
282 dvd_priv_t
*d
=stream
->priv
;
284 while(lang
&& strlen(lang
)>=2) {
285 code
=lang
[1]|(lang
[0]<<8);
286 for(i
=0;i
<d
->nr_of_subtitles
;i
++) {
287 if(d
->subtitles
[i
].language
==code
) {
288 mp_msg(MSGT_OPEN
,MSGL_INFO
,MSGTR_DVDsubtitleChannel
, i
, lang
[0],lang
[1]);
289 return d
->subtitles
[i
].id
;
293 while (lang
[0]==',' || lang
[0]==' ') ++lang
;
295 mp_msg(MSGT_OPEN
,MSGL_WARN
,MSGTR_DVDnoMatchingSubtitle
);
299 static int dvd_next_cell(dvd_priv_t
*d
) {
300 int next_cell
=d
->cur_cell
;
302 mp_msg(MSGT_DVD
,MSGL_DBG2
, "dvd_next_cell: next1=0x%X \n",next_cell
);
303 if( d
->cur_pgc
->cell_playback
[ next_cell
].block_type
== BLOCK_TYPE_ANGLE_BLOCK
) {
304 while(next_cell
<d
->last_cell
) {
305 if( d
->cur_pgc
->cell_playback
[next_cell
].block_mode
== BLOCK_MODE_LAST_CELL
)
310 mp_msg(MSGT_DVD
,MSGL_DBG2
, "dvd_next_cell: next2=0x%X \n",next_cell
);
313 if(next_cell
>=d
->last_cell
)
315 if(d
->cur_pgc
->cell_playback
[next_cell
].block_type
== BLOCK_TYPE_ANGLE_BLOCK
) {
316 next_cell
+=dvd_angle
;
317 if(next_cell
>=d
->last_cell
)
320 mp_msg(MSGT_DVD
,MSGL_DBG2
, "dvd_next_cell: next3=0x%X \n",next_cell
);
324 int dvd_read_sector(dvd_priv_t
*d
,unsigned char* data
) {
327 if(d
->packs_left
==0) {
329 * If we're not at the end of this cell, we can determine the next
330 * VOBU to display using the VOBU_SRI information section of the
331 * DSI. Using this value correctly follows the current angle,
332 * avoiding the doubled scenes in The Matrix, and makes our life
335 * Otherwise, we set our next address past the end of this cell to
336 * force the code above to go to the next cell in the program.
338 if(d
->dsi_pack
.vobu_sri
.next_vobu
!= SRI_END_OF_CELL
) {
339 d
->cur_pack
= d
->dsi_pack
.dsi_gi
.nv_pck_lbn
+ ( d
->dsi_pack
.vobu_sri
.next_vobu
& 0x7fffffff );
340 mp_msg(MSGT_DVD
,MSGL_DBG2
, "Navi new pos=0x%X \n",d
->cur_pack
);
342 // end of cell! find next cell!
343 mp_msg(MSGT_DVD
,MSGL_V
, "--- END OF CELL !!! ---\n");
344 d
->cur_pack
=d
->cell_last_pack
+1;
349 if(d
->cur_pack
>d
->cell_last_pack
) {
351 int next
=dvd_next_cell(d
);
354 // if( d->cur_pgc->cell_playback[d->cur_cell].block_type
355 // == BLOCK_TYPE_ANGLE_BLOCK ) d->cur_cell+=dvd_angle;
356 d
->cur_pack
= d
->cur_pgc
->cell_playback
[ d
->cur_cell
].first_sector
;
357 d
->cell_last_pack
=d
->cur_pgc
->cell_playback
[ d
->cur_cell
].last_sector
;
358 mp_msg(MSGT_DVD
,MSGL_V
, "DVD next cell: %d pack: 0x%X-0x%X \n",d
->cur_cell
,d
->cur_pack
,d
->cell_last_pack
);
363 len
= DVDReadBlocks(d
->title
, d
->cur_pack
, 1, data
);
364 if(!len
) return -1; //error
366 if(data
[38]==0 && data
[39]==0 && data
[40]==1 && data
[41]==0xBF &&
367 data
[1024]==0 && data
[1025]==0 && data
[1026]==1 && data
[1027]==0xBF) {
368 // found a Navi packet!!!
369 #if DVDREAD_VERSION >= LIBDVDREAD_VERSION(0,9,0)
370 navRead_DSI(&d
->dsi_pack
, &(data
[ DSI_START_BYTE
]));
372 navRead_DSI(&d
->dsi_pack
, &(data
[ DSI_START_BYTE
]), sizeof(dsi_t
));
374 if(d
->cur_pack
!= d
->dsi_pack
.dsi_gi
.nv_pck_lbn
) {
375 mp_msg(MSGT_DVD
,MSGL_V
, "Invalid NAVI packet! lba=0x%X navi=0x%X \n",
376 d
->cur_pack
,d
->dsi_pack
.dsi_gi
.nv_pck_lbn
);
379 d
->packs_left
= d
->dsi_pack
.dsi_gi
.vobu_ea
;
380 mp_msg(MSGT_DVD
,MSGL_DBG2
, "Found NAVI packet! lba=0x%X len=%d \n",d
->cur_pack
,d
->packs_left
);
381 //navPrint_DSI(&d->dsi_pack);
382 mp_msg(MSGT_DVD
,MSGL_DBG3
,"\r### CELL %d: Navi: %d/%d IFO: %d/%d \n",d
->cur_cell
,
383 d
->dsi_pack
.dsi_gi
.vobu_c_idn
,d
->dsi_pack
.dsi_gi
.vobu_vob_idn
,
384 d
->cur_pgc
->cell_position
[d
->cur_cell
].cell_nr
,
385 d
->cur_pgc
->cell_position
[d
->cur_cell
].vob_id_nr
);
389 #if defined(__GNUC__) && ( defined(__sparc__) || defined(hpux) )
390 // workaround for a bug in the sparc/hpux version of gcc 2.95.X ... 3.2,
391 // it generates incorrect code for unaligned access to a packed
392 // structure member, resulting in an mplayer crash with a SIGBUS
395 // See also gcc problem report PR c/7847:
396 // http://gcc.gnu.org/cgi-bin/gnatsweb.pl?database=gcc&cmd=view+audit-trail&pr=7847
397 for(i
=0;i
<9;i
++) { // check if all values zero:
398 typeof(d
->dsi_pack
.sml_agli
.data
[i
].address
) tmp_addr
;
399 memcpy(&tmp_addr
,&d
->dsi_pack
.sml_agli
.data
[i
].address
,sizeof(tmp_addr
));
400 if((skip
=tmp_addr
)!=0) break;
403 for(i
=0;i
<9;i
++) // check if all values zero:
404 if((skip
=d
->dsi_pack
.sml_agli
.data
[i
].address
)!=0) break;
407 // sml_agli table has valid data (at least one non-zero):
408 d
->cur_pack
=d
->dsi_pack
.dsi_gi
.nv_pck_lbn
+
409 d
->dsi_pack
.sml_agli
.data
[dvd_angle
].address
;
411 mp_msg(MSGT_DVD
,MSGL_V
, "Angle-seek synced using sml_agli map! new_lba=0x%X \n",d
->cur_pack
);
413 // check if we're in the right cell, jump otherwise:
414 if( (d
->dsi_pack
.dsi_gi
.vobu_c_idn
==d
->cur_pgc
->cell_position
[d
->cur_cell
].cell_nr
) &&
415 (d
->dsi_pack
.dsi_gi
.vobu_vob_idn
==d
->cur_pgc
->cell_position
[d
->cur_cell
].vob_id_nr
) ){
417 mp_msg(MSGT_DVD
,MSGL_V
, "Angle-seek synced by cell/vob IDN search! \n");
419 // wrong angle, skip this vobu:
420 d
->cur_pack
=d
->dsi_pack
.dsi_gi
.nv_pck_lbn
+
421 d
->dsi_pack
.dsi_gi
.vobu_ea
;
422 d
->angle_seek
=2; // DEBUG
432 if(d
->packs_left
>=0) --d
->packs_left
;
435 if(d
->angle_seek
==2) mp_msg(MSGT_DVD
,MSGL_V
, "!!! warning! reading packet while angle_seek !!!\n");
436 goto read_next
; // searching for Navi packet
439 return d
->cur_pack
-1;
442 void dvd_seek(dvd_priv_t
*d
,int pos
) {
446 // check if we stay in current cell (speedup things, and avoid angle skip)
447 if(d
->cur_pack
>d
->cell_last_pack
||
448 d
->cur_pack
<d
->cur_pgc
->cell_playback
[ d
->cur_cell
].first_sector
) {
450 // ok, cell change, find the right cell!
452 if(d
->cur_pgc
->cell_playback
[d
->cur_cell
].block_type
== BLOCK_TYPE_ANGLE_BLOCK
)
453 d
->cur_cell
+=dvd_angle
;
457 d
->cell_last_pack
=d
->cur_pgc
->cell_playback
[ d
->cur_cell
].last_sector
;
458 if(d
->cur_pack
<d
->cur_pgc
->cell_playback
[ d
->cur_cell
].first_sector
) {
459 d
->cur_pack
=d
->cur_pgc
->cell_playback
[ d
->cur_cell
].first_sector
;
462 if(d
->cur_pack
<=d
->cell_last_pack
) break; // ok, we find it! :)
463 next
=dvd_next_cell(d
);
465 //d->cur_pack=d->cell_last_pack+1;
466 break; // we're after the last cell
472 mp_msg(MSGT_DVD
,MSGL_V
, "DVD Seek! lba=0x%X cell=%d packs: 0x%X-0x%X \n",
473 d
->cur_pack
,d
->cur_cell
,d
->cur_pgc
->cell_playback
[ d
->cur_cell
].first_sector
,d
->cell_last_pack
);
475 // if we're in interleaved multi-angle cell, find the right angle chain!
476 // (read Navi block, and use the seamless angle jump table)
480 void dvd_close(dvd_priv_t
*d
) {
481 ifoClose(d
->vts_file
);
482 ifoClose(d
->vmg_file
);
483 DVDCloseFile(d
->title
);
486 dvd_last_chapter
= 0;
487 dvd_set_speed(dvd_device
, -1); /* -1 => restore default */
490 static int fill_buffer(stream_t
*s
, char *but
, int len
)
492 if(s
->type
== STREAMTYPE_DVD
) {
493 off_t pos
=dvd_read_sector(s
->priv
,s
->buffer
);
495 len
=2048; // full sector
497 } else len
=-1; // error
502 static int seek(stream_t
*s
, off_t newpos
) {
503 s
->pos
=newpos
; // real seek
504 dvd_seek(s
->priv
,s
->pos
/2048);
508 static void stream_dvd_close(stream_t
*s
) {
512 static int mp_get_titleset_length(ifo_handle_t
*vts_file
, tt_srpt_t
*tt_srpt
, int title_no
)
514 int vts_ttn
; ///< title number within video title set
515 int pgc_no
; ///< program chain number
516 int msec
; ///< time length in milliseconds
519 if(!vts_file
|| !tt_srpt
)
522 if(vts_file
->vtsi_mat
&& vts_file
->vts_pgcit
)
524 vts_ttn
= tt_srpt
->title
[title_no
].vts_ttn
- 1;
525 pgc_no
= vts_file
->vts_ptt_srpt
->title
[vts_ttn
].ptt
[0].pgcn
- 1;
526 msec
= mp_dvdtimetomsec(&vts_file
->vts_pgcit
->pgci_srp
[pgc_no
].pgc
->playback_time
);
532 static int mp_describe_titleset(dvd_reader_t
*dvd
, tt_srpt_t
*tt_srpt
, int vts_no
)
534 ifo_handle_t
*vts_file
;
535 int title_no
, msec
=0;
537 vts_file
= ifoOpen(dvd
, vts_no
);
541 if(!vts_file
->vtsi_mat
|| !vts_file
->vts_pgcit
)
547 for(title_no
= 0; title_no
< tt_srpt
->nr_of_srpts
; title_no
++)
549 if (tt_srpt
->title
[title_no
].title_set_nr
!= vts_no
)
551 msec
= mp_get_titleset_length(vts_file
, tt_srpt
, title_no
);
552 mp_msg(MSGT_IDENTIFY
, MSGL_V
, "ID_DVD_TITLE_%d_LENGTH=%d.%03d\n", title_no
+ 1, msec
/ 1000, msec
% 1000);
558 static int seek_to_chapter(stream_t
*stream
, ifo_handle_t
*vts_file
, tt_srpt_t
*tt_srpt
, int title_no
, int chapter
)
565 if(!vts_file
|| !tt_srpt
)
568 if(chapter
< 0 || chapter
> vts_file
->vts_ptt_srpt
->title
[title_no
].nr_of_ptts
-1) //no such chapter
571 ptt
= vts_file
->vts_ptt_srpt
->title
[title_no
].ptt
[chapter
];
572 pgc
= vts_file
->vts_pgcit
->pgci_srp
[ptt
.pgcn
-1].pgc
;
574 cell
= pgc
->program_map
[ptt
.pgn
- 1] - 1;
575 pos
= (off_t
) pgc
->cell_playback
[cell
].first_sector
* 2048;
576 mp_msg(MSGT_OPEN
,MSGL_V
,"\r\nSTREAM_DVD, seeked to chapter: %d, cell: %u, pos: %"PRIu64
"\n",
577 chapter
, pgc
->cell_playback
[cell
].first_sector
, pos
);
578 stream_seek(stream
, pos
);
583 static void list_chapters(pgc_t
*pgc
)
585 unsigned int i
, cell
;
586 unsigned int t
=0, t2
=0;
588 if(pgc
->nr_of_programs
< 2)
591 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "CHAPTERS: ");
592 for(i
=0; i
<pgc
->nr_of_programs
; i
++)
594 cell
= pgc
->program_map
[i
]; //here the cell is 1-based
596 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "%02d:%02d:%02d,", t2
/3600, (t2
/60)%60, t2
%60);
597 while(i
+1<pgc
->nr_of_programs
&& cell
< pgc
->program_map
[i
+1]) {
598 if(!(pgc
->cell_playback
[cell
-1].block_type
== BLOCK_TYPE_ANGLE_BLOCK
&&
599 pgc
->cell_playback
[cell
-1].block_mode
!= BLOCK_MODE_FIRST_CELL
)
601 t
+= mp_dvdtimetomsec(&pgc
->cell_playback
[cell
-1].playback_time
);
605 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "\n");
608 static double dvd_get_current_time(stream_t
*stream
, int cell
)
611 dvd_priv_t
*d
= stream
->priv
;
614 if(!cell
) cell
=d
->cur_cell
;
615 for(i
=0; i
<d
->cur_cell
; i
++) {
616 if(d
->cur_pgc
->cell_playback
[i
].block_type
== BLOCK_TYPE_ANGLE_BLOCK
&&
617 d
->cur_pgc
->cell_playback
[i
].block_mode
!= BLOCK_MODE_FIRST_CELL
620 tm
+= d
->cell_times_table
[i
];
622 tm
+= mp_dvdtimetomsec(&d
->dsi_pack
.dsi_gi
.c_eltm
);
624 return (double)tm
/1000.0;
627 static int dvd_seek_to_time(stream_t
*stream
, ifo_handle_t
*vts_file
, double sec
)
629 unsigned int i
, j
, k
, timeunit
, ac_time
, tmap_sector
=0, cell_sector
=0, vobu_sector
=0;
633 dvd_priv_t
*d
= stream
->priv
;
634 vts_tmapt_t
*vts_tmapt
= vts_file
->vts_tmapt
;
636 if(!vts_file
->vts_tmapt
|| sec
< 0)
639 duration
= (double) mp_get_titleset_length(d
->vts_file
, d
->tt_srpt
, d
->cur_title
-1) / 1000.0f
;
644 timeunit
= vts_tmapt
->tmap
[i
].tmu
;
645 for(j
= 0; j
< vts_tmapt
->tmap
[i
].nr_of_entries
; j
++) {
646 ac_time
= timeunit
* (j
+ 1);
649 tmap_sector
= vts_tmapt
->tmap
[i
].map_ent
[j
] & 0x7fffffff;
651 //search enclosing cell
652 for(i
=0; i
<d
->cur_pgc
->nr_of_cells
; i
++) {
653 if(tmap_sector
>= d
->cur_pgc
->cell_playback
[i
].first_sector
&& tmap_sector
<= d
->cur_pgc
->cell_playback
[i
].last_sector
) {
654 cell_sector
= d
->cur_pgc
->cell_playback
[i
].first_sector
;
659 pos
= ((off_t
)cell_sector
)<<11;
660 stream_seek(stream
, pos
);
662 stream_skip(stream
, 2048);
663 t
= mp_dvdtimetomsec(&d
->dsi_pack
.dsi_gi
.c_eltm
);
665 tm
= dvd_get_current_time(stream
, 0);
667 pos
= ((off_t
)tmap_sector
)<<11;
668 stream_seek(stream
, pos
);
669 //now get current time in terms of the cell+cell time offset
670 memset(&d
->dsi_pack
.dsi_gi
.c_eltm
, 0, sizeof(dvd_time_t
));
672 if(!stream_skip(stream
, 2048))
674 tm
= dvd_get_current_time(stream
, 0);
676 tmap_sector
= stream
->pos
>> 11;
678 //search closest VOBU sector
679 k
=(vts_file
->vts_vobu_admap
->last_byte
+ 1 - VOBU_ADMAP_SIZE
)/4; //entries in the vobu admap
681 if(vts_file
->vts_vobu_admap
->vobu_start_sectors
[i
] > tmap_sector
)
684 vobu_sector
= vts_file
->vts_vobu_admap
->vobu_start_sectors
[i
-1];
685 pos
= ((off_t
)vobu_sector
) << 11;
686 stream_seek(stream
, pos
);
691 static int control(stream_t
*stream
,int cmd
,void* arg
)
693 dvd_priv_t
*d
= stream
->priv
;
696 case STREAM_CTRL_GET_TIME_LENGTH
:
698 *((double *)arg
) = (double) mp_get_titleset_length(d
->vts_file
, d
->tt_srpt
, d
->cur_title
-1)/1000.0;
701 case STREAM_CTRL_GET_NUM_CHAPTERS
:
703 if(! d
->cur_pgc
->nr_of_programs
) return STREAM_UNSUPPORTED
;
704 *((unsigned int *)arg
) = d
->cur_pgc
->nr_of_programs
;
707 case STREAM_CTRL_SEEK_TO_CHAPTER
:
710 if(stream_cache_size
> 0) return STREAM_UNSUPPORTED
;
711 r
= seek_to_chapter(stream
, d
->vts_file
, d
->tt_srpt
, d
->cur_title
-1, *((unsigned int *)arg
));
712 if(! r
) return STREAM_UNSUPPORTED
;
716 case STREAM_CTRL_GET_CURRENT_CHAPTER
:
718 if(stream_cache_size
> 0) return STREAM_UNSUPPORTED
;
719 *((unsigned int *)arg
) = dvd_chapter_from_cell(d
, d
->cur_title
-1, d
->cur_cell
);
722 case STREAM_CTRL_GET_CURRENT_TIME
:
725 if(stream_cache_size
> 0) return STREAM_UNSUPPORTED
;
726 tm
= dvd_get_current_time(stream
, 0);
728 *((double *)arg
) = tm
;
733 case STREAM_CTRL_SEEK_TO_TIME
:
735 if(stream_cache_size
> 0) return STREAM_UNSUPPORTED
;
736 if(dvd_seek_to_time(stream
, d
->vts_file
, *((double*)arg
)))
740 case STREAM_CTRL_GET_ASPECT_RATIO
:
742 *((double *)arg
) = !d
->vts_file
->vtsi_mat
->vts_video_attr
.display_aspect_ratio
? 4.0/3.0 : 16.0/9.0;
746 return STREAM_UNSUPPORTED
;
750 static int open_s(stream_t
*stream
,int mode
, void* opts
, int* file_format
) {
751 struct stream_priv_s
* p
= (struct stream_priv_s
*)opts
;
754 mp_msg(MSGT_OPEN
,MSGL_V
,"URL: %s\n", stream
->url
);
755 dvd_title
= p
->title
;
762 ifo_handle_t
*vmg_file
;
764 ifo_handle_t
*vts_file
;
769 if(!dvd_device
) dvd_device
=strdup(DEFAULT_DVD_DEVICE
);
770 dvd_set_speed(dvd_device
, dvd_speed
);
771 #if defined(__APPLE__) || defined(__DARWIN__)
772 /* Dynamic DVD drive selection on Darwin */
773 if(!strcmp(dvd_device
, "/dev/rdiskN")) {
775 size_t len
= strlen(dvd_device
)+1;
776 char *temp_device
= malloc(len
);
778 for (i
= 1; i
< 10; i
++) {
779 snprintf(temp_device
, len
, "/dev/rdisk%d", i
);
780 dvd
= DVDOpen(temp_device
);
782 mp_msg(MSGT_OPEN
,MSGL_ERR
,MSGTR_CantOpenDVD
,temp_device
, strerror(errno
));
784 #if DVDREAD_VERSION <= LIBDVDREAD_VERSION(0,9,4)
785 dvd_file_t
*dvdfile
= DVDOpenFile(dvd
,dvd_title
,DVD_READ_INFO_FILE
);
787 mp_msg(MSGT_OPEN
,MSGL_ERR
,MSGTR_CantOpenDVD
,temp_device
, strerror(errno
));
791 DVDCloseFile(dvdfile
);
799 m_struct_free(&stream_opts
,opts
);
800 return STREAM_UNSUPPORTED
;
803 #endif /* defined(__APPLE__) || defined(__DARWIN__) */
805 dvd
= DVDOpen(dvd_device
);
807 mp_msg(MSGT_OPEN
,MSGL_ERR
,MSGTR_CantOpenDVD
,dvd_device
, strerror(errno
));
808 m_struct_free(&stream_opts
,opts
);
809 return STREAM_UNSUPPORTED
;
813 mp_msg(MSGT_OPEN
,MSGL_V
,"Reading disc structure, please wait...\n");
816 * Load the video manager to find out the information about the titles on
819 vmg_file
= ifoOpen(dvd
, 0);
821 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_DVDnoVMG
);
823 m_struct_free(&stream_opts
,opts
);
824 return STREAM_UNSUPPORTED
;
826 tt_srpt
= vmg_file
->tt_srpt
;
827 if (mp_msg_test(MSGT_IDENTIFY
, MSGL_INFO
))
829 int title_no
; ///< title number
830 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_DVD_TITLES=%d\n", tt_srpt
->nr_of_srpts
);
831 for (title_no
= 0; title_no
< tt_srpt
->nr_of_srpts
; title_no
++)
833 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_DVD_TITLE_%d_CHAPTERS=%d\n", title_no
+ 1, tt_srpt
->title
[title_no
].nr_of_ptts
);
834 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_DVD_TITLE_%d_ANGLES=%d\n", title_no
+ 1, tt_srpt
->title
[title_no
].nr_of_angles
);
837 if (mp_msg_test(MSGT_IDENTIFY
, MSGL_V
))
839 unsigned char discid
[16]; ///< disk ID, a 128 bit MD5 sum
840 int vts_no
; ///< video title set number
841 for (vts_no
= 1; vts_no
<= vmg_file
->vts_atrt
->nr_of_vtss
; vts_no
++)
842 mp_describe_titleset(dvd
, tt_srpt
, vts_no
);
843 if (DVDDiscID(dvd
, discid
) >= 0)
846 mp_msg(MSGT_IDENTIFY
, MSGL_V
, "ID_DVD_DISC_ID=");
847 for (i
= 0; i
< 16; i
++)
848 mp_msg(MSGT_IDENTIFY
, MSGL_V
, "%02X", discid
[i
]);
849 mp_msg(MSGT_IDENTIFY
, MSGL_V
, "\n");
853 * Make sure our title number is valid.
855 mp_msg(MSGT_OPEN
,MSGL_STATUS
, MSGTR_DVDnumTitles
, tt_srpt
->nr_of_srpts
);
856 if(dvd_title
< 1 || dvd_title
> tt_srpt
->nr_of_srpts
) {
857 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_DVDinvalidTitle
, dvd_title
);
858 ifoClose( vmg_file
);
860 m_struct_free(&stream_opts
,opts
);
861 return STREAM_UNSUPPORTED
;
863 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_DVD_CURRENT_TITLE=%d\n", dvd_title
);
864 --dvd_title
; // remap 1.. -> 0..
866 * Make sure the chapter number is valid for this title.
868 mp_msg(MSGT_OPEN
,MSGL_STATUS
, MSGTR_DVDnumChapters
, tt_srpt
->title
[dvd_title
].nr_of_ptts
);
869 if(dvd_chapter
<1 || dvd_chapter
>tt_srpt
->title
[dvd_title
].nr_of_ptts
) {
870 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_DVDinvalidChapter
, dvd_chapter
);
873 if(dvd_last_chapter
>0) {
874 if(dvd_last_chapter
<dvd_chapter
|| dvd_last_chapter
>tt_srpt
->title
[dvd_title
].nr_of_ptts
) {
875 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_DVDinvalidLastChapter
, dvd_last_chapter
);
879 --dvd_chapter
; // remap 1.. -> 0..
880 /* XXX No need to remap dvd_last_chapter */
882 * Make sure the angle number is valid for this title.
884 mp_msg(MSGT_OPEN
,MSGL_STATUS
, MSGTR_DVDnumAngles
, tt_srpt
->title
[dvd_title
].nr_of_angles
);
885 if(dvd_angle
<1 || dvd_angle
>tt_srpt
->title
[dvd_title
].nr_of_angles
) {
886 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_DVDinvalidAngle
, dvd_angle
);
889 --dvd_angle
; // remap 1.. -> 0..
891 ttn
= tt_srpt
->title
[dvd_title
].vts_ttn
- 1;
893 * Load the VTS information for the title set our title is in.
895 vts_file
= ifoOpen( dvd
, tt_srpt
->title
[dvd_title
].title_set_nr
);
897 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_DVDnoIFO
, tt_srpt
->title
[dvd_title
].title_set_nr
);
901 * We've got enough info, time to open the title set data.
903 title
= DVDOpenFile(dvd
, tt_srpt
->title
[dvd_title
].title_set_nr
, DVD_READ_TITLE_VOBS
);
905 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_DVDnoVOBs
, tt_srpt
->title
[dvd_title
].title_set_nr
);
906 ifoClose( vts_file
);
910 mp_msg(MSGT_OPEN
,MSGL_V
, "DVD successfully opened.\n");
912 d
=malloc(sizeof(dvd_priv_t
)); memset(d
,0,sizeof(dvd_priv_t
));
915 d
->vmg_file
=vmg_file
;
917 d
->vts_file
=vts_file
;
918 d
->cur_title
= dvd_title
+1;
920 pgc
= vts_file
->vts_pgcit
? vts_file
->vts_pgcit
->pgci_srp
[ttn
].pgc
: NULL
;
922 * Check number of audio channels and types
926 if(vts_file
->vts_pgcit
) {
929 if(pgc
->audio_control
[i
] & 0x8000) {
930 audio_attr_t
* audio
= &vts_file
->vtsi_mat
->vts_audio_attr
[i
];
932 char tmp
[] = "unknown";
933 stream_language_t
*audio_stream
= &d
->audio_streams
[d
->nr_of_channels
];
935 if(audio
->lang_type
== 1) {
936 language
=audio
->lang_code
;
938 tmp
[1]=language
&0xff;
942 audio_stream
->language
=language
;
943 audio_stream
->id
=pgc
->audio_control
[i
] >> 8 & 7;
944 switch(audio
->audio_format
) {
946 audio_stream
->id
+=FIRST_AC3_AID
;
949 audio_stream
->id
+=FIRST_DTS_AID
;
951 case 2: // mpeg layer 1/2/3
953 audio_stream
->id
+=FIRST_MPG_AID
;
956 audio_stream
->id
+=FIRST_PCM_AID
;
960 audio_stream
->type
=audio
->audio_format
;
961 // Pontscho: to my mind, tha channels:
964 audio_stream
->channels
=audio
->channels
;
965 mp_msg(MSGT_OPEN
,MSGL_STATUS
,MSGTR_DVDaudioStreamInfo
,
967 dvd_audio_stream_types
[ audio
->audio_format
],
968 dvd_audio_stream_channels
[ audio
->channels
],
972 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_AUDIO_ID=%d\n", audio_stream
->id
);
973 if(language
&& tmp
[0])
974 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_AID_%d_LANG=%s\n", audio_stream
->id
, tmp
);
979 mp_msg(MSGT_OPEN
,MSGL_STATUS
,MSGTR_DVDnumAudioChannels
,d
->nr_of_channels
);
983 * Check number of subtitles and language
988 d
->nr_of_subtitles
=0;
990 if(pgc
->subp_control
[i
] & 0x80000000) {
991 subp_attr_t
* subtitle
= &vts_file
->vtsi_mat
->vts_subp_attr
[i
];
992 video_attr_t
*video
= &vts_file
->vtsi_mat
->vts_video_attr
;
994 char tmp
[] = "unknown";
995 stream_language_t
*sub_stream
= &d
->subtitles
[d
->nr_of_subtitles
];
997 if(subtitle
->type
== 1) {
998 language
=subtitle
->lang_code
;
1000 tmp
[1]=language
&0xff;
1004 sub_stream
->language
=language
;
1005 sub_stream
->id
=d
->nr_of_subtitles
;
1006 if(video
->display_aspect_ratio
== 0) /* 4:3 */
1007 sub_stream
->id
= pgc
->subp_control
[i
] >> 24 & 31;
1008 else if(video
->display_aspect_ratio
== 3) /* 16:9 */
1009 sub_stream
->id
= pgc
->subp_control
[i
] >> 8 & 31;
1011 mp_msg(MSGT_OPEN
,MSGL_STATUS
,MSGTR_DVDsubtitleLanguage
, sub_stream
->id
, tmp
);
1012 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_SUBTITLE_ID=%d\n", sub_stream
->id
);
1013 if(language
&& tmp
[0])
1014 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_SID_%d_LANG=%s\n", sub_stream
->id
, tmp
);
1015 d
->nr_of_subtitles
++;
1017 mp_msg(MSGT_OPEN
,MSGL_STATUS
,MSGTR_DVDnumSubtitles
,d
->nr_of_subtitles
);
1021 * Determine which program chain we want to watch. This is based on the
1024 pgc_id
= vts_file
->vts_ptt_srpt
->title
[ttn
].ptt
[dvd_chapter
].pgcn
; // local
1025 pgn
= vts_file
->vts_ptt_srpt
->title
[ttn
].ptt
[dvd_chapter
].pgn
; // local
1026 d
->cur_pgc_idx
= pgc_id
-1;
1027 d
->cur_pgc
= vts_file
->vts_pgcit
->pgci_srp
[pgc_id
-1].pgc
;
1028 d
->cur_cell
= d
->cur_pgc
->program_map
[pgn
-1] - 1; // start playback here
1029 d
->packs_left
=-1; // for Navi stuff
1031 /* XXX dvd_last_chapter is in the range 1..nr_of_ptts */
1032 if(dvd_last_chapter
> 0 && dvd_last_chapter
< tt_srpt
->title
[dvd_title
].nr_of_ptts
) {
1033 pgn
=vts_file
->vts_ptt_srpt
->title
[ttn
].ptt
[dvd_last_chapter
].pgn
;
1034 d
->last_cell
=d
->cur_pgc
->program_map
[pgn
-1] - 1;
1036 d
->last_cell
=d
->cur_pgc
->nr_of_cells
;
1038 if(d
->cur_pgc
->cell_playback
[d
->cur_cell
].block_type
== BLOCK_TYPE_ANGLE_BLOCK
)
1039 d
->cur_cell
+=dvd_angle
;
1040 d
->cur_pack
= d
->cur_pgc
->cell_playback
[ d
->cur_cell
].first_sector
;
1041 d
->cell_last_pack
=d
->cur_pgc
->cell_playback
[ d
->cur_cell
].last_sector
;
1042 mp_msg(MSGT_DVD
,MSGL_V
, "DVD start cell: %d pack: 0x%X-0x%X \n",d
->cur_cell
,d
->cur_pack
,d
->cell_last_pack
);
1044 //assign cell_times_table
1045 d
->cell_times_table
= malloc(sizeof(unsigned int) * d
->cur_pgc
->nr_of_cells
);
1046 if(d
->cell_times_table
== NULL
)
1047 return STREAM_UNSUPPORTED
;
1048 for(k
=0; k
<d
->cur_pgc
->nr_of_cells
; k
++)
1049 d
->cell_times_table
[k
] = mp_dvdtimetomsec(&d
->cur_pgc
->cell_playback
[k
].playback_time
);
1050 list_chapters(d
->cur_pgc
);
1052 // ... (unimplemented)
1054 stream
->type
= STREAMTYPE_DVD
;
1055 stream
->sector_size
= 2048;
1056 stream
->flags
= STREAM_READ
| STREAM_SEEK
;
1057 stream
->fill_buffer
= fill_buffer
;
1058 stream
->seek
= seek
;
1059 stream
->control
= control
;
1060 stream
->close
= stream_dvd_close
;
1061 stream
->start_pos
= (off_t
)d
->cur_pack
*2048;
1062 stream
->end_pos
= (off_t
)(d
->cur_pgc
->cell_playback
[d
->last_cell
-1].last_sector
)*2048;
1063 *file_format
= DEMUXER_TYPE_MPEG_PS
;
1064 mp_msg(MSGT_DVD
,MSGL_V
,"DVD start=%d end=%d \n",d
->cur_pack
,d
->cur_pgc
->cell_playback
[d
->last_cell
-1].last_sector
);
1065 stream
->priv
= (void*)d
;
1066 if(stream_cache_size
> 0)
1067 mp_msg(MSGT_DVD
,MSGL_INFO
,"[stream_dvd] Warning! the cache is enabled. Seeking won't work correctly\n");
1073 m_struct_free(&stream_opts
, opts
);
1074 return STREAM_UNSUPPORTED
;
1076 mp_msg(MSGT_DVD
,MSGL_ERR
,MSGTR_NoDVDSupport
);
1077 m_struct_free(&stream_opts
,opts
);
1078 return STREAM_UNSUPPORTED
;
1082 ifo_stream_open (stream_t
*stream
, int mode
, void *opts
, int *file_format
)
1086 struct stream_priv_s
*dvd_priv
;
1088 ext
= strrchr (stream
->url
, '.');
1089 if (!ext
|| strcasecmp (ext
+ 1, "ifo"))
1090 return STREAM_UNSUPPORTED
;
1092 mp_msg(MSGT_DVD
, MSGL_INFO
, ".IFO detected. Redirecting to dvd://\n");
1094 dvd_device
= strdup(dirname (stream
->url
));
1096 filename
= strdup(basename(stream
->url
));
1098 dvd_priv
=calloc(1, sizeof(struct stream_priv_s
));
1099 if(!strncasecmp(filename
,"vts_",4))
1101 if(sscanf(filename
+3, "_%02d_", &dvd_priv
->title
)!=1)
1108 stream
->url
=strdup("dvd://");
1110 return open_s(stream
, mode
, dvd_priv
, file_format
);
1113 const stream_info_t stream_info_dvd
= {
1121 1 // Urls are an option string
1124 const stream_info_t stream_info_ifo
= {
1128 "Mostly used to play DVDs on disk through OSD Menu",
1130 { "file", "", NULL
},