1467
|
1
|
|
2 #include <stdio.h>
|
|
3 #include <stdlib.h>
|
|
4 #include <string.h>
|
|
5 #include <unistd.h>
|
|
6 #include <fcntl.h>
|
|
7
|
|
8 #include "config.h"
|
1567
|
9 #include "mp_msg.h"
|
1584
|
10 #include "help_mp.h"
|
1467
|
11
|
1468
|
12 #ifdef __FreeBSD__
|
|
13 #include <sys/cdrio.h>
|
|
14 #endif
|
|
15
|
1467
|
16 #include "stream.h"
|
1482
|
17 #include "demuxer.h"
|
1467
|
18
|
|
19 #ifdef STREAMING
|
|
20 #include "url.h"
|
|
21 #include "network.h"
|
|
22 static URL_t* url;
|
|
23 #endif
|
|
24
|
1596
|
25 int dvd_title=0;
|
|
26 int dvd_chapter=1;
|
|
27 int dvd_angle=1;
|
|
28
|
|
29 #ifdef USE_DVDREAD
|
|
30
|
|
31 #include <dvdread/dvd_reader.h>
|
|
32 #include <dvdread/ifo_types.h>
|
|
33 #include <dvdread/ifo_read.h>
|
|
34 #include <dvdread/dvd_udf.h>
|
|
35 #include <dvdread/nav_read.h>
|
|
36 //#include <dvdread/nav_print.h>
|
|
37
|
|
38 typedef struct {
|
|
39 dvd_reader_t *dvd;
|
|
40 dvd_file_t *title;
|
|
41 ifo_handle_t *vmg_file;
|
|
42 tt_srpt_t *tt_srpt;
|
|
43 ifo_handle_t *vts_file;
|
|
44 vts_ptt_srpt_t *vts_ptt_srpt;
|
|
45 pgc_t *cur_pgc;
|
|
46 //
|
|
47 int cur_cell;
|
|
48 int cur_pack;
|
|
49 int cell_last_pack;
|
|
50 // Navi:
|
|
51 int packs_left;
|
|
52 dsi_t dsi_pack;
|
|
53 } dvd_priv_t;
|
|
54
|
|
55 #endif
|
|
56
|
1467
|
57 extern int vcd_get_track_end(int fd,int track);
|
|
58
|
|
59 // Open a new stream (stdin/file/vcd/url)
|
|
60
|
|
61 stream_t* open_stream(char* filename,int vcd_track,int* file_format){
|
|
62 stream_t* stream=NULL;
|
|
63 int f=-1;
|
|
64 off_t len;
|
|
65 #ifdef VCD_CACHE
|
|
66 int vcd_cache_size=128;
|
|
67 #endif
|
|
68 #ifdef __FreeBSD__
|
|
69 int bsize = VCD_SECTOR_SIZE;
|
|
70 #endif
|
|
71
|
|
72 //============ Open VideoCD track ==============
|
|
73 if(vcd_track){
|
|
74 int ret,ret2;
|
1596
|
75 if(!filename) filename=DEFAULT_CDROM_DEVICE;
|
1467
|
76 f=open(filename,O_RDONLY);
|
1584
|
77 if(f<0){ mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_CdDevNotfound,filename);return NULL; }
|
1467
|
78 vcd_read_toc(f);
|
|
79 ret2=vcd_get_track_end(f,vcd_track);
|
1584
|
80 if(ret2<0){ mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_ErrTrackSelect " (get)\n");return NULL;}
|
1467
|
81 ret=vcd_seek_to_track(f,vcd_track);
|
1584
|
82 if(ret<0){ mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_ErrTrackSelect " (seek)\n");return NULL;}
|
1467
|
83 // seek_to_byte+=ret;
|
1567
|
84 mp_msg(MSGT_OPEN,MSGL_V,"VCD start byte position: 0x%X end: 0x%X\n",ret,ret2);
|
1467
|
85 #ifdef VCD_CACHE
|
|
86 vcd_cache_init(vcd_cache_size);
|
|
87 #endif
|
|
88 #ifdef __FreeBSD__
|
|
89 if (ioctl (f, CDRIOCSETBLOCKSIZE, &bsize) == -1) {
|
|
90 perror ( "Error in CDRIOCSETBLOCKSIZE");
|
|
91 }
|
|
92 #endif
|
|
93 stream=new_stream(f,STREAMTYPE_VCD);
|
|
94 stream->start_pos=ret;
|
|
95 stream->end_pos=ret2;
|
|
96 return stream;
|
|
97 }
|
|
98
|
1596
|
99 //============ Open DVD title ==============
|
|
100 #ifdef USE_DVDREAD
|
|
101 if(dvd_title){
|
|
102 int ret,ret2;
|
|
103 dvd_priv_t *d;
|
|
104 int ttn,pgc_id,pgn;
|
|
105 dvd_reader_t *dvd;
|
|
106 dvd_file_t *title;
|
|
107 ifo_handle_t *vmg_file;
|
|
108 tt_srpt_t *tt_srpt;
|
|
109 ifo_handle_t *vts_file;
|
|
110 /**
|
|
111 * Open the disc.
|
|
112 */
|
|
113 if(!filename) filename=DEFAULT_DVD_DEVICE;
|
|
114 dvd = DVDOpen(filename);
|
|
115 if( !dvd ) {
|
|
116 mp_msg(MSGT_OPEN,MSGL_ERR, "Couldn't open DVD: %s\n",filename);
|
|
117 return NULL;
|
|
118 }
|
|
119
|
|
120 mp_msg(MSGT_OPEN,MSGL_INFO, "Reading disc structure, please wait...\n");
|
|
121
|
|
122 /**
|
|
123 * Load the video manager to find out the information about the titles on
|
|
124 * this disc.
|
|
125 */
|
|
126 vmg_file = ifoOpen( dvd, 0 );
|
|
127 if( !vmg_file ) {
|
|
128 mp_msg(MSGT_OPEN,MSGL_ERR, "Can't open VMG info!\n");
|
|
129 DVDClose( dvd );
|
|
130 return NULL;
|
|
131 }
|
|
132 tt_srpt = vmg_file->tt_srpt;
|
|
133 /**
|
|
134 * Make sure our title number is valid.
|
|
135 */
|
|
136 mp_msg(MSGT_OPEN,MSGL_INFO, "There are %d titles on this DVD.\n",
|
|
137 tt_srpt->nr_of_srpts );
|
|
138 if( dvd_title < 1 || dvd_title > tt_srpt->nr_of_srpts ) {
|
|
139 mp_msg(MSGT_OPEN,MSGL_ERR, "Invalid DVD title number: %d\n", dvd_title);
|
|
140 ifoClose( vmg_file );
|
|
141 DVDClose( dvd );
|
|
142 return NULL;
|
|
143 }
|
|
144 --dvd_title; // remap 1.. -> 0..
|
|
145 /**
|
|
146 * Make sure the chapter number is valid for this title.
|
|
147 */
|
|
148 mp_msg(MSGT_OPEN,MSGL_INFO, "There are %d chapters in this DVD title.\n",
|
|
149 tt_srpt->title[dvd_title].nr_of_ptts );
|
|
150 if( dvd_chapter<1 || dvd_chapter>tt_srpt->title[dvd_title].nr_of_ptts ) {
|
|
151 mp_msg(MSGT_OPEN,MSGL_ERR, "Invalid DVD chapter number: %d\n", dvd_chapter);
|
|
152 ifoClose( vmg_file );
|
|
153 DVDClose( dvd );
|
|
154 return NULL;
|
|
155 }
|
|
156 --dvd_chapter; // remap 1.. -> 0..
|
|
157 /**
|
|
158 * Make sure the angle number is valid for this title.
|
|
159 */
|
|
160 mp_msg(MSGT_OPEN,MSGL_INFO, "There are %d angles in this DVD title.\n",
|
|
161 tt_srpt->title[dvd_title].nr_of_angles );
|
|
162 if( dvd_angle<1 || dvd_angle>tt_srpt->title[dvd_title].nr_of_angles ) {
|
|
163 mp_msg(MSGT_OPEN,MSGL_ERR, "Invalid DVD angle number: %d\n", dvd_angle);
|
|
164 ifoClose( vmg_file );
|
|
165 DVDClose( dvd );
|
|
166 return NULL;
|
|
167 }
|
|
168 /**
|
|
169 * Load the VTS information for the title set our title is in.
|
|
170 */
|
|
171 vts_file = ifoOpen( dvd, tt_srpt->title[dvd_title].title_set_nr );
|
|
172 if( !vts_file ) {
|
|
173 mp_msg(MSGT_OPEN,MSGL_ERR, "Can't open the IFO file for DVD title %d.\n",
|
|
174 tt_srpt->title[dvd_title].title_set_nr );
|
|
175 ifoClose( vmg_file );
|
|
176 DVDClose( dvd );
|
|
177 return NULL;
|
|
178 }
|
|
179 /**
|
|
180 * We've got enough info, time to open the title set data.
|
|
181 */
|
|
182 title = DVDOpenFile( dvd, tt_srpt->title[dvd_title].title_set_nr,
|
|
183 DVD_READ_TITLE_VOBS );
|
|
184 if( !title ) {
|
|
185 mp_msg(MSGT_OPEN,MSGL_ERR, "Can't open title VOBS (VTS_%02d_1.VOB).\n",
|
|
186 tt_srpt->title[dvd_title].title_set_nr );
|
|
187 ifoClose( vts_file );
|
|
188 ifoClose( vmg_file );
|
|
189 DVDClose( dvd );
|
|
190 return NULL;
|
|
191 }
|
|
192
|
|
193 mp_msg(MSGT_OPEN,MSGL_INFO, "DVD successfully opened!\n");
|
|
194 // store data
|
|
195 d=malloc(sizeof(dvd_priv_t)); memset(d,0,sizeof(dvd_priv_t));
|
|
196 d->dvd=dvd;
|
|
197 d->title=title;
|
|
198 d->vmg_file=vmg_file;
|
|
199 d->tt_srpt=tt_srpt;
|
|
200 d->vts_file=vts_file;
|
|
201
|
|
202 /**
|
|
203 * Determine which program chain we want to watch. This is based on the
|
|
204 * chapter number.
|
|
205 */
|
|
206 ttn = tt_srpt->title[ dvd_title ].vts_ttn; // local
|
|
207 pgc_id = vts_file->vts_ptt_srpt->title[ttn-1].ptt[dvd_chapter].pgcn; // local
|
|
208 pgn = vts_file->vts_ptt_srpt->title[ttn-1].ptt[dvd_chapter].pgn; // local
|
|
209 d->cur_pgc = vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc;
|
|
210 d->cur_cell = d->cur_pgc->program_map[pgn-1] - 1; // start playback here
|
|
211 d->packs_left=-1; // for Navi stuff
|
|
212
|
|
213 if( d->cur_pgc->cell_playback[d->cur_cell].block_type
|
|
214 == BLOCK_TYPE_ANGLE_BLOCK ) d->cur_cell+=dvd_angle;
|
|
215 d->cur_pack = d->cur_pgc->cell_playback[ d->cur_cell ].first_sector;
|
|
216 d->cell_last_pack=d->cur_pgc->cell_playback[ d->cur_cell ].last_sector;
|
|
217 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);
|
|
218
|
|
219 // ... (unimplemented)
|
|
220 // return NULL;
|
|
221 stream=new_stream(-1,STREAMTYPE_DVD);
|
|
222 stream->start_pos=(off_t)d->cur_pack*2048;
|
|
223 //stream->end_pos=0;
|
|
224 stream->priv=(void*)d;
|
|
225 return stream;
|
|
226 }
|
|
227 #endif
|
|
228
|
1467
|
229 //============ Open STDIN ============
|
|
230 if(!strcmp(filename,"-")){
|
|
231 // read from stdin
|
1584
|
232 mp_msg(MSGT_OPEN,MSGL_INFO,MSGTR_ReadSTDIN);
|
1467
|
233 f=0; // 0=stdin
|
|
234 stream=new_stream(f,STREAMTYPE_STREAM);
|
|
235 return stream;
|
|
236 }
|
|
237
|
|
238 #ifdef STREAMING
|
|
239 url = url_new(filename);
|
|
240 if(url) {
|
|
241 (*file_format)=autodetectProtocol( url, &f );
|
|
242 if( (*file_format)==DEMUXER_TYPE_UNKNOWN ) {
|
1584
|
243 mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_UnableOpenURL, filename);
|
1467
|
244 url_free(url);
|
|
245 return NULL;
|
|
246 }
|
|
247 f=streaming_start( &url, f, file_format );
|
1584
|
248 if(f<0){ mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_UnableOpenURL, url->url); return NULL; }
|
|
249 mp_msg(MSGT_OPEN,MSGL_INFO,MSGTR_ConnToServer, url->hostname );
|
1467
|
250 stream=new_stream(f,STREAMTYPE_STREAM);
|
|
251 return NULL;
|
|
252 }
|
|
253 #endif
|
|
254
|
|
255 //============ Open plain FILE ============
|
|
256 f=open(filename,O_RDONLY);
|
1584
|
257 if(f<0){ mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_FileNotFound,filename);return NULL; }
|
1467
|
258 len=lseek(f,0,SEEK_END); lseek(f,0,SEEK_SET);
|
|
259 if (len == -1)
|
|
260 perror("Error: lseek failed to obtain video file size");
|
|
261 else
|
|
262 if(verbose)
|
|
263 #ifdef _LARGEFILE_SOURCE
|
1567
|
264 mp_msg(MSGT_OPEN,MSGL_V,"File size is %lld bytes\n", (long long)len);
|
1467
|
265 #else
|
1567
|
266 mp_msg(MSGT_OPEN,MSGL_V,"File size is %u bytes\n", (unsigned int)len);
|
1467
|
267 #endif
|
|
268 stream=new_stream(f,STREAMTYPE_FILE);
|
|
269 stream->end_pos=len;
|
|
270 return stream;
|
|
271
|
|
272 }
|
1596
|
273
|
|
274
|
|
275 #ifdef USE_DVDREAD
|
|
276
|
|
277 static int dvd_next_cell(dvd_priv_t *d){
|
|
278 int next_cell=d->cur_cell;
|
|
279
|
|
280 if( d->cur_pgc->cell_playback[ next_cell ].block_type
|
|
281 == BLOCK_TYPE_ANGLE_BLOCK ) {
|
|
282 int i;
|
|
283 while(next_cell<d->cur_pgc->nr_of_cells){
|
|
284 if( d->cur_pgc->cell_playback[next_cell].block_mode
|
|
285 == BLOCK_MODE_LAST_CELL ) break;
|
|
286 ++next_cell;
|
|
287 }
|
|
288 }
|
|
289
|
|
290 ++next_cell;
|
|
291 if(next_cell>=d->cur_pgc->nr_of_cells) return -1; // EOF
|
|
292 if( d->cur_pgc->cell_playback[next_cell].block_type == BLOCK_TYPE_ANGLE_BLOCK ){
|
|
293 next_cell+=dvd_angle;
|
|
294 if(next_cell>=d->cur_pgc->nr_of_cells) return -1; // EOF
|
|
295 }
|
|
296 return next_cell;
|
|
297 }
|
|
298
|
|
299 int dvd_read_sector(dvd_priv_t *d,unsigned char* data){
|
|
300 int len;
|
|
301
|
|
302 if(d->packs_left==0){
|
|
303 /**
|
|
304 * If we're not at the end of this cell, we can determine the next
|
|
305 * VOBU to display using the VOBU_SRI information section of the
|
|
306 * DSI. Using this value correctly follows the current angle,
|
|
307 * avoiding the doubled scenes in The Matrix, and makes our life
|
|
308 * really happy.
|
|
309 *
|
|
310 * Otherwise, we set our next address past the end of this cell to
|
|
311 * force the code above to go to the next cell in the program.
|
|
312 */
|
|
313 if( d->dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL ) {
|
|
314 d->cur_pack= d->dsi_pack.dsi_gi.nv_pck_lbn +
|
|
315 ( d->dsi_pack.vobu_sri.next_vobu & 0x7fffffff );
|
|
316 mp_msg(MSGT_DVD,MSGL_DBG2, "Navi new pos=0x%X \n",d->cur_pack);
|
|
317 } else {
|
|
318 // end of cell! find next cell!
|
|
319 mp_msg(MSGT_DVD,MSGL_V, "--- END OF CELL !!! ---\n");
|
|
320 d->cur_pack=d->cell_last_pack+1;
|
|
321 }
|
|
322 }
|
|
323
|
|
324 read_next:
|
|
325
|
|
326 if(d->cur_pack>d->cell_last_pack){
|
|
327 // end of cell!
|
|
328 int next=dvd_next_cell(d);
|
|
329 if(next>=0){
|
|
330 d->cur_cell=next;
|
|
331
|
|
332 if( d->cur_pgc->cell_playback[d->cur_cell].block_type
|
|
333 == BLOCK_TYPE_ANGLE_BLOCK ) d->cur_cell+=dvd_angle;
|
|
334 d->cur_pack = d->cur_pgc->cell_playback[ d->cur_cell ].first_sector;
|
|
335 d->cell_last_pack=d->cur_pgc->cell_playback[ d->cur_cell ].last_sector;
|
|
336 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);
|
|
337
|
|
338 } else return -1; // EOF
|
|
339 }
|
|
340
|
|
341 len = DVDReadBlocks( d->title, d->cur_pack, 1, data );
|
|
342 if(!len) return -1; //error
|
|
343
|
|
344 if(data[38]==0 && data[39]==0 && data[40]==1 && data[41]==0xBF &&
|
|
345 data[1024]==0 && data[1025]==0 && data[1026]==1 && data[1027]==0xBF){
|
|
346 // found a Navi packet!!!
|
|
347 navRead_DSI( &d->dsi_pack, &(data[ DSI_START_BYTE ]), sizeof(dsi_t) );
|
|
348 if(d->cur_pack != d->dsi_pack.dsi_gi.nv_pck_lbn ){
|
|
349 mp_msg(MSGT_DVD,MSGL_V, "Invalid NAVI packet! lba=0x%X navi=0x%X \n",
|
|
350 d->cur_pack,d->dsi_pack.dsi_gi.nv_pck_lbn);
|
|
351 } else {
|
|
352 // process!
|
|
353 d->packs_left = d->dsi_pack.dsi_gi.vobu_ea;
|
|
354 mp_msg(MSGT_DVD,MSGL_DBG2, "Found NAVI packet! lba=0x%X len=%d \n",d->cur_pack,d->packs_left);
|
|
355 }
|
|
356 ++d->cur_pack;
|
|
357 goto read_next;
|
|
358 }
|
|
359
|
|
360 ++d->cur_pack;
|
|
361 if(d->packs_left>=0) --d->packs_left;
|
|
362
|
|
363 return d->cur_pack-1;
|
|
364 }
|
|
365
|
|
366 void dvd_seek(dvd_priv_t *d,int pos){
|
|
367 d->packs_left=-1;
|
|
368 d->cur_pack=pos;
|
|
369
|
|
370 // check if we stay in current cell (speedup things, and avoid angle skip)
|
|
371 if(d->cur_pack>d->cell_last_pack &&
|
|
372 d->cur_pack<d->cur_pgc->cell_playback[ d->cur_cell ].first_sector){
|
|
373
|
|
374 // ok, cell change, find the right cell!
|
|
375 d->cur_cell=0;
|
|
376 if( d->cur_pgc->cell_playback[d->cur_cell].block_type
|
|
377 == BLOCK_TYPE_ANGLE_BLOCK ) d->cur_cell+=dvd_angle;
|
|
378
|
|
379 while(1){
|
|
380 int next;
|
|
381 d->cell_last_pack=d->cur_pgc->cell_playback[ d->cur_cell ].last_sector;
|
|
382 if(d->cur_pack<d->cur_pgc->cell_playback[ d->cur_cell ].first_sector){
|
|
383 d->cur_pack=d->cur_pgc->cell_playback[ d->cur_cell ].first_sector;
|
|
384 break;
|
|
385 }
|
|
386 if(d->cur_pack<=d->cell_last_pack) break; // ok, we find it! :)
|
|
387 next=dvd_next_cell(d);
|
|
388 if(next<0){
|
|
389 // d->cur_pack=d->cell_last_pack+1;
|
|
390 break; // we're after the last cell
|
|
391 }
|
|
392 d->cur_cell=next;
|
|
393 }
|
|
394 }
|
|
395
|
|
396 mp_msg(MSGT_DVD,MSGL_V, "DVD Seek! lba=0x%X cell=%d packs: 0x%X-0x%X \n",
|
|
397 d->cur_pack,d->cur_cell,d->cur_pgc->cell_playback[ d->cur_cell ].first_sector,d->cell_last_pack);
|
|
398
|
|
399 // TODO: if we're in interleaved multi-angle cell, find the right angle chain!
|
|
400 // (read Navi block, and use the seamless angle jump table)
|
|
401
|
|
402 }
|
|
403
|
|
404 #endif
|