16 #include <sys/cdrio.h>
19 #define FIRST_AC3_AID 128
20 #define FIRST_DTS_AID 136
21 #define FIRST_MPG_AID 0
22 #define FIRST_PCM_AID 160
28 /// We keep these 2 for the gui atm, but they will be removed.
30 extern int dvd_chapter
;
31 extern int dvd_last_chapter
;
32 extern char* dvd_device
;
37 #define LIBDVDREAD_VERSION(maj,min,micro) ((maj)*10000 + (min)*100 + (micro))
39 * Try to autodetect the libdvd-0.9.0 library
40 * (0.9.0 removed the <dvdread/dvd_udf.h> header, and moved the two defines
41 * DVD_VIDEO_LB_LEN and MAX_UDF_FILE_NAME_LEN from it to
42 * <dvdread/dvd_reader.h>)
44 #ifndef DVDREAD_VERSION
45 #if defined(DVD_VIDEO_LB_LEN) && defined(MAX_UDF_FILE_NAME_LEN)
46 #define DVDREAD_VERSION LIBDVDREAD_VERSION(0,9,0)
48 #define DVDREAD_VERSION LIBDVDREAD_VERSION(0,8,0)
52 char * dvd_audio_stream_types
[8] = { "ac3","unknown","mpeg1","mpeg2ext","lpcm","unknown","dts" };
53 char * dvd_audio_stream_channels
[6] = { "mono", "stereo", "unknown", "unknown", "5.1/6.1", "5.1" };
57 static struct stream_priv_s
{
59 } stream_priv_dflts
= {
63 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
65 static m_option_t stream_opts_fields
[] = {
66 { "hostname", ST_OFF(title
), CONF_TYPE_INT
, M_OPT_MIN
, 1, 0, NULL
},
67 { NULL
, NULL
, 0, 0, 0, 0, NULL
}
69 static struct m_struct_st stream_opts
= {
71 sizeof(struct stream_priv_s
),
76 int dvd_parse_chapter_range(m_option_t
*conf
, const char *range
) {
80 return M_OPT_MISSING_PARAM
;
84 if(*range
&& isdigit(*range
)) {
85 dvd_chapter
= strtol(range
, &s
, 10);
87 mp_msg(MSGT_OPEN
, MSGL_ERR
, MSGTR_DVDinvalidChapterRange
, range
);
94 mp_msg(MSGT_OPEN
, MSGL_ERR
, MSGTR_DVDinvalidChapterRange
, range
);
101 mp_msg(MSGT_OPEN
, MSGL_ERR
, MSGTR_DVDinvalidChapterRange
, range
);
102 return M_OPT_INVALID
;
104 dvd_last_chapter
= strtol(s
, &t
, 10);
106 mp_msg(MSGT_OPEN
, MSGL_ERR
, MSGTR_DVDinvalidChapterRange
, range
);
107 return M_OPT_INVALID
;
113 int dvd_chapter_from_cell(dvd_priv_t
* dvd
,int title
,int cell
)
119 if(title
< 0 || cell
< 0){
122 /* for most DVD's chapter == cell */
123 /* but there are more complecated cases... */
124 if(chapter
>= dvd
->vmg_file
->tt_srpt
->title
[title
].nr_of_ptts
) {
125 chapter
= dvd
->vmg_file
->tt_srpt
->title
[title
].nr_of_ptts
-1;
127 title
= dvd
->tt_srpt
->title
[title
].vts_ttn
-1;
128 ptt
= dvd
->vts_file
->vts_ptt_srpt
->title
[title
].ptt
;
129 while(chapter
>= 0) {
130 pgc_id
= ptt
[chapter
].pgcn
;
131 pgn
= ptt
[chapter
].pgn
;
132 cur_pgc
= dvd
->vts_file
->vts_pgcit
->pgci_srp
[pgc_id
-1].pgc
;
133 if(cell
>= cur_pgc
->program_map
[pgn
-1]-1) {
138 /* didn't find a chapter ??? */
142 int dvd_aid_from_lang(stream_t
*stream
, unsigned char* lang
) {
143 dvd_priv_t
*d
=stream
->priv
;
146 while(strlen(lang
)>=2) {
147 code
=lang
[1]|(lang
[0]<<8);
148 for(i
=0;i
<d
->nr_of_channels
;i
++) {
149 if(d
->audio_streams
[i
].language
==code
) {
150 mp_msg(MSGT_OPEN
,MSGL_INFO
,MSGTR_DVDaudioChannel
,
151 d
->audio_streams
[i
].id
, lang
[0],lang
[1]);
152 return d
->audio_streams
[i
].id
;
154 //printf("%X != %X (%c%c)\n",code,d->audio_streams[i].language,lang[0],lang[1]);
156 lang
+=2; while (lang
[0]==',' || lang
[0]==' ') ++lang
;
158 mp_msg(MSGT_OPEN
,MSGL_WARN
,MSGTR_DVDnoMatchingAudio
);
163 int dvd_number_of_subs(stream_t
*stream
) {
165 if (!stream
) return -1;
168 return d
->nr_of_subtitles
;
171 int dvd_lang_from_sid(stream_t
*stream
, int id
) {
173 if (!stream
) return 0;
176 if (id
>= d
->nr_of_subtitles
) return 0;
177 return d
->subtitles
[id
].language
;
180 int dvd_sid_from_lang(stream_t
*stream
, unsigned char* lang
) {
181 dvd_priv_t
*d
=stream
->priv
;
183 while(lang
&& strlen(lang
)>=2) {
184 code
=lang
[1]|(lang
[0]<<8);
185 for(i
=0;i
<d
->nr_of_subtitles
;i
++) {
186 if(d
->subtitles
[i
].language
==code
) {
187 mp_msg(MSGT_OPEN
,MSGL_INFO
,MSGTR_DVDsubtitleChannel
, i
, lang
[0],lang
[1]);
192 while (lang
[0]==',' || lang
[0]==' ') ++lang
;
194 mp_msg(MSGT_OPEN
,MSGL_WARN
,MSGTR_DVDnoMatchingSubtitle
);
198 static int dvd_next_cell(dvd_priv_t
*d
) {
199 int next_cell
=d
->cur_cell
;
201 mp_msg(MSGT_DVD
,MSGL_DBG2
, "dvd_next_cell: next1=0x%X \n",next_cell
);
202 if( d
->cur_pgc
->cell_playback
[ next_cell
].block_type
== BLOCK_TYPE_ANGLE_BLOCK
) {
203 while(next_cell
<d
->last_cell
) {
204 if( d
->cur_pgc
->cell_playback
[next_cell
].block_mode
== BLOCK_MODE_LAST_CELL
)
209 mp_msg(MSGT_DVD
,MSGL_DBG2
, "dvd_next_cell: next2=0x%X \n",next_cell
);
212 if(next_cell
>=d
->last_cell
)
214 if(d
->cur_pgc
->cell_playback
[next_cell
].block_type
== BLOCK_TYPE_ANGLE_BLOCK
) {
215 next_cell
+=dvd_angle
;
216 if(next_cell
>=d
->last_cell
)
219 mp_msg(MSGT_DVD
,MSGL_DBG2
, "dvd_next_cell: next3=0x%X \n",next_cell
);
223 int dvd_read_sector(dvd_priv_t
*d
,unsigned char* data
) {
226 if(d
->packs_left
==0) {
228 * If we're not at the end of this cell, we can determine the next
229 * VOBU to display using the VOBU_SRI information section of the
230 * DSI. Using this value correctly follows the current angle,
231 * avoiding the doubled scenes in The Matrix, and makes our life
234 * Otherwise, we set our next address past the end of this cell to
235 * force the code above to go to the next cell in the program.
237 if(d
->dsi_pack
.vobu_sri
.next_vobu
!= SRI_END_OF_CELL
) {
238 d
->cur_pack
= d
->dsi_pack
.dsi_gi
.nv_pck_lbn
+ ( d
->dsi_pack
.vobu_sri
.next_vobu
& 0x7fffffff );
239 mp_msg(MSGT_DVD
,MSGL_DBG2
, "Navi new pos=0x%X \n",d
->cur_pack
);
241 // end of cell! find next cell!
242 mp_msg(MSGT_DVD
,MSGL_V
, "--- END OF CELL !!! ---\n");
243 d
->cur_pack
=d
->cell_last_pack
+1;
248 if(d
->cur_pack
>d
->cell_last_pack
) {
250 int next
=dvd_next_cell(d
);
253 // if( d->cur_pgc->cell_playback[d->cur_cell].block_type
254 // == BLOCK_TYPE_ANGLE_BLOCK ) d->cur_cell+=dvd_angle;
255 d
->cur_pack
= d
->cur_pgc
->cell_playback
[ d
->cur_cell
].first_sector
;
256 d
->cell_last_pack
=d
->cur_pgc
->cell_playback
[ d
->cur_cell
].last_sector
;
257 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
);
262 len
= DVDReadBlocks(d
->title
, d
->cur_pack
, 1, data
);
263 if(!len
) return -1; //error
265 if(data
[38]==0 && data
[39]==0 && data
[40]==1 && data
[41]==0xBF &&
266 data
[1024]==0 && data
[1025]==0 && data
[1026]==1 && data
[1027]==0xBF) {
267 // found a Navi packet!!!
268 #if DVDREAD_VERSION >= LIBDVDREAD_VERSION(0,9,0)
269 navRead_DSI(&d
->dsi_pack
, &(data
[ DSI_START_BYTE
]));
271 navRead_DSI(&d
->dsi_pack
, &(data
[ DSI_START_BYTE
]), sizeof(dsi_t
));
273 if(d
->cur_pack
!= d
->dsi_pack
.dsi_gi
.nv_pck_lbn
) {
274 mp_msg(MSGT_DVD
,MSGL_V
, "Invalid NAVI packet! lba=0x%X navi=0x%X \n",
275 d
->cur_pack
,d
->dsi_pack
.dsi_gi
.nv_pck_lbn
);
278 d
->packs_left
= d
->dsi_pack
.dsi_gi
.vobu_ea
;
279 mp_msg(MSGT_DVD
,MSGL_DBG2
, "Found NAVI packet! lba=0x%X len=%d \n",d
->cur_pack
,d
->packs_left
);
280 //navPrint_DSI(&d->dsi_pack);
281 mp_msg(MSGT_DVD
,MSGL_DBG3
,"\r### CELL %d: Navi: %d/%d IFO: %d/%d \n",d
->cur_cell
,
282 d
->dsi_pack
.dsi_gi
.vobu_c_idn
,d
->dsi_pack
.dsi_gi
.vobu_vob_idn
,
283 d
->cur_pgc
->cell_position
[d
->cur_cell
].cell_nr
,
284 d
->cur_pgc
->cell_position
[d
->cur_cell
].vob_id_nr
);
288 #if defined(__GNUC__) && ( defined(__sparc__) || defined(hpux) )
289 // workaround for a bug in the sparc/hpux version of gcc 2.95.X ... 3.2,
290 // it generates incorrect code for unaligned access to a packed
291 // structure member, resulting in an mplayer crash with a SIGBUS
294 // See also gcc problem report PR c/7847:
295 // http://gcc.gnu.org/cgi-bin/gnatsweb.pl?database=gcc&cmd=view+audit-trail&pr=7847
296 for(i
=0;i
<9;i
++) { // check if all values zero:
297 typeof(d
->dsi_pack
.sml_agli
.data
[i
].address
) tmp_addr
;
298 memcpy(&tmp_addr
,&d
->dsi_pack
.sml_agli
.data
[i
].address
,sizeof(tmp_addr
));
299 if((skip
=tmp_addr
)!=0) break;
302 for(i
=0;i
<9;i
++) // check if all values zero:
303 if((skip
=d
->dsi_pack
.sml_agli
.data
[i
].address
)!=0) break;
306 // sml_agli table has valid data (at least one non-zero):
307 d
->cur_pack
=d
->dsi_pack
.dsi_gi
.nv_pck_lbn
+
308 d
->dsi_pack
.sml_agli
.data
[dvd_angle
].address
;
310 mp_msg(MSGT_DVD
,MSGL_V
, "Angle-seek synced using sml_agli map! new_lba=0x%X \n",d
->cur_pack
);
312 // check if we're in the right cell, jump otherwise:
313 if( (d
->dsi_pack
.dsi_gi
.vobu_c_idn
==d
->cur_pgc
->cell_position
[d
->cur_cell
].cell_nr
) &&
314 (d
->dsi_pack
.dsi_gi
.vobu_vob_idn
==d
->cur_pgc
->cell_position
[d
->cur_cell
].vob_id_nr
) ){
316 mp_msg(MSGT_DVD
,MSGL_V
, "Angle-seek synced by cell/vob IDN search! \n");
318 // wrong angle, skip this vobu:
319 d
->cur_pack
=d
->dsi_pack
.dsi_gi
.nv_pck_lbn
+
320 d
->dsi_pack
.dsi_gi
.vobu_ea
;
321 d
->angle_seek
=2; // DEBUG
331 if(d
->packs_left
>=0) --d
->packs_left
;
334 if(d
->angle_seek
==2) mp_msg(MSGT_DVD
,MSGL_V
, "!!! warning! reading packet while angle_seek !!!\n");
335 goto read_next
; // searching for Navi packet
338 return d
->cur_pack
-1;
341 void dvd_seek(dvd_priv_t
*d
,int pos
) {
345 // check if we stay in current cell (speedup things, and avoid angle skip)
346 if(d
->cur_pack
>d
->cell_last_pack
||
347 d
->cur_pack
<d
->cur_pgc
->cell_playback
[ d
->cur_cell
].first_sector
) {
349 // ok, cell change, find the right cell!
351 if(d
->cur_pgc
->cell_playback
[d
->cur_cell
].block_type
== BLOCK_TYPE_ANGLE_BLOCK
)
352 d
->cur_cell
+=dvd_angle
;
356 d
->cell_last_pack
=d
->cur_pgc
->cell_playback
[ d
->cur_cell
].last_sector
;
357 if(d
->cur_pack
<d
->cur_pgc
->cell_playback
[ d
->cur_cell
].first_sector
) {
358 d
->cur_pack
=d
->cur_pgc
->cell_playback
[ d
->cur_cell
].first_sector
;
361 if(d
->cur_pack
<=d
->cell_last_pack
) break; // ok, we find it! :)
362 next
=dvd_next_cell(d
);
364 //d->cur_pack=d->cell_last_pack+1;
365 break; // we're after the last cell
371 mp_msg(MSGT_DVD
,MSGL_V
, "DVD Seek! lba=0x%X cell=%d packs: 0x%X-0x%X \n",
372 d
->cur_pack
,d
->cur_cell
,d
->cur_pgc
->cell_playback
[ d
->cur_cell
].first_sector
,d
->cell_last_pack
);
374 // if we're in interleaved multi-angle cell, find the right angle chain!
375 // (read Navi block, and use the seamless angle jump table)
379 void dvd_close(dvd_priv_t
*d
) {
380 ifoClose(d
->vts_file
);
381 ifoClose(d
->vmg_file
);
382 DVDCloseFile(d
->title
);
385 dvd_last_chapter
= 0;
390 static int fill_buffer(stream_t
*s
, char *but
, int len
)
393 if(s
->type
== STREAMTYPE_DVD
) {
394 off_t pos
=dvd_read_sector(s
->priv
,s
->buffer
);
396 len
=2048; // full sector
398 } else len
=-1; // error
404 static int seek(stream_t
*s
, off_t newpos
) {
406 s
->pos
=newpos
; // real seek
407 dvd_seek(s
->priv
,s
->pos
/2048);
412 static void stream_dvd_close(stream_t
*s
) {
419 \brief Converts DVD time structure to milliseconds.
420 \param *dev the DVD time structure to convert
421 \return returns the time in milliseconds
423 static int dvdtimetomsec(dvd_time_t
*dt
)
425 static int framerates
[4] = {0, 2500, 0, 2997};
426 int framerate
= framerates
[(dt
->frame_u
& 0xc0) >> 6];
427 int msec
= (((dt
->hour
& 0xf0) >> 3) * 5 + (dt
->hour
& 0x0f)) * 3600000;
428 msec
+= (((dt
->minute
& 0xf0) >> 3) * 5 + (dt
->minute
& 0x0f)) * 60000;
429 msec
+= (((dt
->second
& 0xf0) >> 3) * 5 + (dt
->second
& 0x0f)) * 1000;
431 msec
+= (((dt
->frame_u
& 0x30) >> 3) * 5 + (dt
->frame_u
& 0x0f)) * 100000 / framerate
;
435 static int mp_get_titleset_length(ifo_handle_t
*vts_file
, tt_srpt_t
*tt_srpt
, int title_no
)
437 int vts_ttn
; ///< title number within video title set
438 int pgc_no
; ///< program chain number
439 int msec
; ///< time length in milliseconds
442 if(!vts_file
|| !tt_srpt
)
445 if(vts_file
->vtsi_mat
&& vts_file
->vts_pgcit
)
447 vts_ttn
= tt_srpt
->title
[title_no
].vts_ttn
- 1;
448 pgc_no
= vts_file
->vts_ptt_srpt
->title
[vts_ttn
].ptt
[0].pgcn
- 1;
449 msec
= dvdtimetomsec(&vts_file
->vts_pgcit
->pgci_srp
[pgc_no
].pgc
->playback_time
);
455 static int mp_describe_titleset(dvd_reader_t
*dvd
, tt_srpt_t
*tt_srpt
, int vts_no
)
457 ifo_handle_t
*vts_file
;
458 int title_no
, msec
=0;
460 vts_file
= ifoOpen(dvd
, vts_no
);
464 if(!vts_file
->vtsi_mat
|| !vts_file
->vts_pgcit
)
470 for(title_no
= 0; title_no
< tt_srpt
->nr_of_srpts
; title_no
++)
472 if (tt_srpt
->title
[title_no
].title_set_nr
!= vts_no
)
474 msec
= mp_get_titleset_length(vts_file
, tt_srpt
, title_no
);
475 mp_msg(MSGT_IDENTIFY
, MSGL_V
, "ID_DVD_TITLE_%d_LENGTH=%d.%03d\n", title_no
+ 1, msec
/ 1000, msec
% 1000);
481 static int control(stream_t
*stream
,int cmd
,void* arg
)
485 case STREAM_CTRL_GET_TIME_LENGTH
:
487 dvd_priv_t
*d
= stream
->priv
;
488 *((unsigned int *)arg
) = mp_get_titleset_length(d
->vts_file
, d
->tt_srpt
, d
->cur_title
-1);
492 return STREAM_UNSUPORTED
;
496 static int open_s(stream_t
*stream
,int mode
, void* opts
, int* file_format
) {
497 struct stream_priv_s
* p
= (struct stream_priv_s
*)opts
;
500 filename
= strdup(stream
->url
);
501 mp_msg(MSGT_OPEN
,MSGL_V
,"URL: %s\n", filename
);
502 dvd_title
= p
->title
;
510 ifo_handle_t
*vmg_file
;
512 ifo_handle_t
*vts_file
;
516 if(!dvd_device
) dvd_device
=strdup(DEFAULT_DVD_DEVICE
);
518 /* Dynamic DVD drive selection on Darwin */
519 if(!strcmp(dvd_device
, "/dev/rdiskN")) {
521 char *temp_device
= malloc(strlen(dvd_device
)+1);
523 for (i
= 1; i
< 10; i
++) {
524 sprintf(temp_device
, "/dev/rdisk%d", i
);
525 dvd
= DVDOpen(temp_device
);
527 mp_msg(MSGT_OPEN
,MSGL_ERR
,MSGTR_CantOpenDVD
,temp_device
);
529 #if DVDREAD_VERSION <= LIBDVDREAD_VERSION(0,9,4)
531 if(!UDFFindFile(dvd
,"/",&len
)) {
532 mp_msg(MSGT_OPEN
,MSGL_ERR
,MSGTR_CantOpenDVD
,temp_device
);
544 m_struct_free(&stream_opts
,opts
);
545 return STREAM_UNSUPORTED
;
548 #endif /* SYS_DARWIN */
550 dvd
= DVDOpen(dvd_device
);
552 mp_msg(MSGT_OPEN
,MSGL_ERR
,MSGTR_CantOpenDVD
,dvd_device
);
553 m_struct_free(&stream_opts
,opts
);
554 return STREAM_UNSUPORTED
;
558 mp_msg(MSGT_OPEN
,MSGL_INFO
,MSGTR_DVDwait
);
561 * Load the video manager to find out the information about the titles on
564 vmg_file
= ifoOpen(dvd
, 0);
566 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_DVDnoVMG
);
568 m_struct_free(&stream_opts
,opts
);
569 return STREAM_UNSUPORTED
;
571 tt_srpt
= vmg_file
->tt_srpt
;
572 if (mp_msg_test(MSGT_IDENTIFY
, MSGL_INFO
))
574 int title_no
; ///< title number
575 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_DVD_TITLES=%d\n", tt_srpt
->nr_of_srpts
);
576 for (title_no
= 0; title_no
< tt_srpt
->nr_of_srpts
; title_no
++)
578 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_DVD_TITLE_%d_CHAPTERS=%d\n", title_no
+ 1, tt_srpt
->title
[title_no
].nr_of_ptts
);
579 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_DVD_TITLE_%d_ANGLES=%d\n", title_no
+ 1, tt_srpt
->title
[title_no
].nr_of_angles
);
582 if (mp_msg_test(MSGT_IDENTIFY
, MSGL_V
))
584 unsigned char discid
[16]; ///< disk ID, a 128 bit MD5 sum
585 int vts_no
; ///< video title set number
586 for (vts_no
= 1; vts_no
<= vmg_file
->vts_atrt
->nr_of_vtss
; vts_no
++)
587 mp_describe_titleset(dvd
, tt_srpt
, vts_no
);
588 if (DVDDiscID(dvd
, discid
) >= 0)
592 for (i
= 0; i
< 16; i
++)
593 sprintf(buf
+2*i
, "%02X", discid
[i
]);
594 mp_msg(MSGT_IDENTIFY
, MSGL_V
, "ID_DVD_DISC_ID=%s\n", buf
);
598 * Make sure our title number is valid.
600 mp_msg(MSGT_OPEN
,MSGL_INFO
, MSGTR_DVDnumTitles
, tt_srpt
->nr_of_srpts
);
601 if(dvd_title
< 1 || dvd_title
> tt_srpt
->nr_of_srpts
) {
602 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_DVDinvalidTitle
, dvd_title
);
603 ifoClose( vmg_file
);
605 m_struct_free(&stream_opts
,opts
);
606 return STREAM_UNSUPORTED
;
608 --dvd_title
; // remap 1.. -> 0..
610 * Make sure the chapter number is valid for this title.
612 mp_msg(MSGT_OPEN
,MSGL_INFO
, MSGTR_DVDnumChapters
, tt_srpt
->title
[dvd_title
].nr_of_ptts
);
613 if(dvd_chapter
<1 || dvd_chapter
>tt_srpt
->title
[dvd_title
].nr_of_ptts
) {
614 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_DVDinvalidChapter
, dvd_chapter
);
615 ifoClose( vmg_file
);
617 m_struct_free(&stream_opts
,opts
);
618 return STREAM_UNSUPORTED
;
620 if(dvd_last_chapter
>0) {
621 if(dvd_last_chapter
<dvd_chapter
|| dvd_last_chapter
>tt_srpt
->title
[dvd_title
].nr_of_ptts
) {
622 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_DVDinvalidLastChapter
, dvd_last_chapter
);
623 ifoClose( vmg_file
);
625 m_struct_free(&stream_opts
,opts
);
626 return STREAM_UNSUPORTED
;
629 --dvd_chapter
; // remap 1.. -> 0..
630 /* XXX No need to remap dvd_last_chapter */
632 * Make sure the angle number is valid for this title.
634 mp_msg(MSGT_OPEN
,MSGL_INFO
, MSGTR_DVDnumAngles
, tt_srpt
->title
[dvd_title
].nr_of_angles
);
635 if(dvd_angle
<1 || dvd_angle
>tt_srpt
->title
[dvd_title
].nr_of_angles
) {
636 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_DVDinvalidAngle
, dvd_angle
);
637 ifoClose( vmg_file
);
639 m_struct_free(&stream_opts
,opts
);
640 return STREAM_UNSUPORTED
;
642 --dvd_angle
; // remap 1.. -> 0..
644 ttn
= tt_srpt
->title
[dvd_title
].vts_ttn
- 1;
646 * Load the VTS information for the title set our title is in.
648 vts_file
= ifoOpen( dvd
, tt_srpt
->title
[dvd_title
].title_set_nr
);
650 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_DVDnoIFO
, tt_srpt
->title
[dvd_title
].title_set_nr
);
651 ifoClose( vmg_file
);
653 m_struct_free(&stream_opts
,opts
);
654 return STREAM_UNSUPORTED
;
657 * We've got enough info, time to open the title set data.
659 title
= DVDOpenFile(dvd
, tt_srpt
->title
[dvd_title
].title_set_nr
, DVD_READ_TITLE_VOBS
);
661 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_DVDnoVOBs
, tt_srpt
->title
[dvd_title
].title_set_nr
);
662 ifoClose( vts_file
);
663 ifoClose( vmg_file
);
665 m_struct_free(&stream_opts
,opts
);
666 return STREAM_UNSUPORTED
;
669 mp_msg(MSGT_OPEN
,MSGL_INFO
, MSGTR_DVDopenOk
);
671 d
=malloc(sizeof(dvd_priv_t
)); memset(d
,0,sizeof(dvd_priv_t
));
674 d
->vmg_file
=vmg_file
;
676 d
->vts_file
=vts_file
;
677 d
->cur_title
= dvd_title
+1;
680 * Check number of audio channels and types
684 if(vts_file
->vts_pgcit
) {
688 if(vts_file
->vts_pgcit
->pgci_srp
[ttn
].pgc
->audio_control
[i
].present
) {
690 if(vts_file
->vts_pgcit
->pgci_srp
[ttn
].pgc
->audio_control
[i
] & 0x8000) {
692 audio_attr_t
* audio
= &vts_file
->vtsi_mat
->vts_audio_attr
[i
];
694 char tmp
[] = "unknown";
696 if(audio
->lang_type
== 1) {
697 language
=audio
->lang_code
;
699 tmp
[1]=language
&0xff;
703 d
->audio_streams
[d
->nr_of_channels
].language
=language
;
705 d
->audio_streams
[d
->nr_of_channels
].id
=vts_file
->vts_pgcit
->pgci_srp
[ttn
].pgc
->audio_control
[i
].s_audio
;
707 d
->audio_streams
[d
->nr_of_channels
].id
=vts_file
->vts_pgcit
->pgci_srp
[ttn
].pgc
->audio_control
[i
] >> 8 & 7;
709 switch(audio
->audio_format
) {
711 d
->audio_streams
[d
->nr_of_channels
].id
+=FIRST_AC3_AID
;
714 d
->audio_streams
[d
->nr_of_channels
].id
+=FIRST_DTS_AID
;
716 case 2: // mpeg layer 1/2/3
718 d
->audio_streams
[d
->nr_of_channels
].id
+=FIRST_MPG_AID
;
721 d
->audio_streams
[d
->nr_of_channels
].id
+=FIRST_PCM_AID
;
725 d
->audio_streams
[d
->nr_of_channels
].type
=audio
->audio_format
;
726 // Pontscho: to my mind, tha channels:
729 d
->audio_streams
[d
->nr_of_channels
].channels
=audio
->channels
;
730 mp_msg(MSGT_OPEN
,MSGL_V
,"[open] audio stream: %d audio format: %s (%s) language: %s aid: %d\n",
732 dvd_audio_stream_types
[ audio
->audio_format
],
733 dvd_audio_stream_channels
[ audio
->channels
],
735 d
->audio_streams
[d
->nr_of_channels
].id
737 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_AUDIO_ID=%d\n", d
->audio_streams
[d
->nr_of_channels
].id
);
738 if(language
&& tmp
[0])
739 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_AID_%d_LANG=%s\n", d
->audio_streams
[d
->nr_of_channels
].id
, tmp
);
744 mp_msg(MSGT_OPEN
,MSGL_V
,"[open] number of audio channels on disk: %d.\n",d
->nr_of_channels
);
748 * Check number of subtitles and language
753 d
->nr_of_subtitles
=0;
756 if(vts_file
->vts_pgcit
->pgci_srp
[ttn
].pgc
->subp_control
[i
].present
) {
758 if(vts_file
->vts_pgcit
->pgci_srp
[ttn
].pgc
->subp_control
[i
] & 0x80000000) {
760 subp_attr_t
* subtitle
= &vts_file
->vtsi_mat
->vts_subp_attr
[i
];
761 video_attr_t
*video
= &vts_file
->vtsi_mat
->vts_video_attr
;
763 char tmp
[] = "unknown";
765 if(subtitle
->type
== 1) {
766 language
=subtitle
->lang_code
;
768 tmp
[1]=language
&0xff;
772 d
->subtitles
[ d
->nr_of_subtitles
].language
=language
;
773 d
->subtitles
[ d
->nr_of_subtitles
].id
=d
->nr_of_subtitles
;
774 if(video
->display_aspect_ratio
== 0) /* 4:3 */
776 d
->subtitles
[d
->nr_of_subtitles
].id
= vts_file
->vts_pgcit
->pgci_srp
[ttn
].pgc
->subp_control
[i
].s_4p3
;
778 d
->subtitles
[d
->nr_of_subtitles
].id
= vts_file
->vts_pgcit
->pgci_srp
[ttn
].pgc
->subp_control
[i
] >> 24 & 31;
780 else if(video
->display_aspect_ratio
== 3) /* 16:9 */
782 d
->subtitles
[d
->nr_of_subtitles
].id
= vts_file
->vts_pgcit
->pgci_srp
[ttn
].pgc
->subp_control
[i
].s_lbox
;
784 d
->subtitles
[d
->nr_of_subtitles
].id
= vts_file
->vts_pgcit
->pgci_srp
[ttn
].pgc
->subp_control
[i
] >> 8 & 31;
787 mp_msg(MSGT_OPEN
,MSGL_V
,"[open] subtitle ( sid ): %d language: %s\n", d
->nr_of_subtitles
, tmp
);
788 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_SUBTITLE_ID=%d\n", d
->subtitles
[d
->nr_of_subtitles
].id
);
789 if(language
&& tmp
[0])
790 mp_msg(MSGT_IDENTIFY
, MSGL_INFO
, "ID_SID_%d_LANG=%s\n", d
->nr_of_subtitles
, tmp
);
791 d
->nr_of_subtitles
++;
793 mp_msg(MSGT_OPEN
,MSGL_V
,"[open] number of subtitles on disk: %d\n",d
->nr_of_subtitles
);
797 * Determine which program chain we want to watch. This is based on the
800 pgc_id
= vts_file
->vts_ptt_srpt
->title
[ttn
].ptt
[dvd_chapter
].pgcn
; // local
801 pgn
= vts_file
->vts_ptt_srpt
->title
[ttn
].ptt
[dvd_chapter
].pgn
; // local
802 d
->cur_pgc
= vts_file
->vts_pgcit
->pgci_srp
[pgc_id
-1].pgc
;
803 d
->cur_cell
= d
->cur_pgc
->program_map
[pgn
-1] - 1; // start playback here
804 d
->packs_left
=-1; // for Navi stuff
806 /* XXX dvd_last_chapter is in the range 1..nr_of_ptts */
807 if(dvd_last_chapter
> 0 && dvd_last_chapter
< tt_srpt
->title
[dvd_title
].nr_of_ptts
) {
808 pgn
=vts_file
->vts_ptt_srpt
->title
[ttn
].ptt
[dvd_last_chapter
].pgn
;
809 d
->last_cell
=d
->cur_pgc
->program_map
[pgn
-1] - 1;
811 d
->last_cell
=d
->cur_pgc
->nr_of_cells
;
813 if(d
->cur_pgc
->cell_playback
[d
->cur_cell
].block_type
== BLOCK_TYPE_ANGLE_BLOCK
)
814 d
->cur_cell
+=dvd_angle
;
815 d
->cur_pack
= d
->cur_pgc
->cell_playback
[ d
->cur_cell
].first_sector
;
816 d
->cell_last_pack
=d
->cur_pgc
->cell_playback
[ d
->cur_cell
].last_sector
;
817 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
);
819 // ... (unimplemented)
821 stream
->type
= STREAMTYPE_DVD
;
822 stream
->sector_size
= 2048;
823 stream
->flags
= STREAM_READ
| STREAM_SEEK
;
824 stream
->fill_buffer
= fill_buffer
;
826 stream
->control
= control
;
827 stream
->close
= stream_dvd_close
;
828 stream
->start_pos
= (off_t
)d
->cur_pack
*2048;
829 stream
->end_pos
= (off_t
)(d
->cur_pgc
->cell_playback
[d
->last_cell
-1].last_sector
)*2048;
830 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
);
831 stream
->priv
= (void*)d
;
835 mp_msg(MSGT_DVD
,MSGL_ERR
,MSGTR_NoDVDSupport
);
836 m_struct_free(&stream_opts
,opts
);
837 return STREAM_UNSUPORTED
;
841 stream_info_t stream_info_dvd
= {
849 1 // Urls are an option string