rev |
line source |
paulo@0
|
1 /*
|
paulo@0
|
2 * Copyright (C) 2003 by the libdvdnav project
|
paulo@0
|
3 *
|
paulo@0
|
4 * This file is part of libdvdnav, a DVD navigation library.
|
paulo@0
|
5 *
|
paulo@0
|
6 * libdvdnav is free software; you can redistribute it and/or modify
|
paulo@0
|
7 * it under the terms of the GNU General Public License as published by
|
paulo@0
|
8 * the Free Software Foundation; either version 2 of the License, or
|
paulo@0
|
9 * (at your option) any later version.
|
paulo@0
|
10 *
|
paulo@0
|
11 * libdvdnav is distributed in the hope that it will be useful,
|
paulo@0
|
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
paulo@0
|
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
paulo@0
|
14 * GNU General Public License for more details.
|
paulo@0
|
15 *
|
paulo@0
|
16 * You should have received a copy of the GNU General Public License
|
paulo@0
|
17 * along with this program; if not, write to the Free Software
|
paulo@0
|
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
paulo@0
|
19 *
|
paulo@0
|
20 * $Id: menus.c 1135 2008-09-06 21:55:51Z rathann $
|
paulo@0
|
21 *
|
paulo@0
|
22 */
|
paulo@0
|
23
|
paulo@0
|
24 #include <stdio.h>
|
paulo@0
|
25 #include <unistd.h>
|
paulo@0
|
26 #include <inttypes.h>
|
paulo@0
|
27 #include <sys/types.h>
|
paulo@0
|
28 #include <sys/stat.h>
|
paulo@0
|
29 #include <fcntl.h>
|
paulo@0
|
30 #include "dvd_types.h"
|
paulo@0
|
31 #include <dvdread/dvd_reader.h>
|
paulo@0
|
32 #include <dvdread/nav_types.h>
|
paulo@0
|
33 #include <dvdread/ifo_types.h> /* For vm_cmd_t */
|
paulo@0
|
34 #include "dvdnav.h"
|
paulo@0
|
35 #include "dvdnav_events.h"
|
paulo@0
|
36
|
paulo@0
|
37 /* shall we use libdvdnav's read ahead cache? */
|
paulo@0
|
38 #define DVD_READ_CACHE 1
|
paulo@0
|
39
|
paulo@0
|
40 /* which is the default language for menus/audio/subpictures? */
|
paulo@0
|
41 #define DVD_LANGUAGE "en"
|
paulo@0
|
42
|
paulo@0
|
43 #ifdef WIN32
|
paulo@0
|
44 #define S_IRWXG 0
|
paulo@0
|
45 #endif
|
paulo@0
|
46
|
paulo@0
|
47 int main(int argc, char **argv) {
|
paulo@2
|
48 dvdnav_t *dvdnav;
|
paulo@2
|
49 uint8_t mem[DVD_VIDEO_LB_LEN];
|
paulo@2
|
50 int finished = 0;
|
paulo@2
|
51 int output_fd = 0;
|
paulo@2
|
52 int dump = 0, tt_dump = 0, tt_skip = 0;
|
paulo@2
|
53
|
paulo@2
|
54 /* open dvdnav handle */
|
paulo@2
|
55 printf("Opening DVD...\n");
|
paulo@2
|
56 if (dvdnav_open(&dvdnav, "/dev/dvd") != DVDNAV_STATUS_OK) {
|
paulo@2
|
57 printf("Error on dvdnav_open\n");
|
paulo@2
|
58 return 1;
|
paulo@2
|
59 }
|
paulo@2
|
60
|
paulo@2
|
61 /* set read ahead cache usage */
|
paulo@2
|
62 if (dvdnav_set_readahead_flag(dvdnav, DVD_READ_CACHE) != DVDNAV_STATUS_OK) {
|
paulo@2
|
63 printf("Error on dvdnav_set_readahead_flag: %s\n", dvdnav_err_to_string(dvdnav));
|
paulo@2
|
64 return 2;
|
paulo@2
|
65 }
|
paulo@2
|
66
|
paulo@2
|
67 /* set the language */
|
paulo@2
|
68 if (dvdnav_menu_language_select(dvdnav, DVD_LANGUAGE) != DVDNAV_STATUS_OK ||
|
paulo@2
|
69 dvdnav_audio_language_select(dvdnav, DVD_LANGUAGE) != DVDNAV_STATUS_OK ||
|
paulo@2
|
70 dvdnav_spu_language_select(dvdnav, DVD_LANGUAGE) != DVDNAV_STATUS_OK) {
|
paulo@2
|
71 printf("Error on setting languages: %s\n", dvdnav_err_to_string(dvdnav));
|
paulo@2
|
72 return 2;
|
paulo@2
|
73 }
|
paulo@2
|
74
|
paulo@2
|
75 /* set the PGC positioning flag to have position information relatively to the
|
paulo@2
|
76 * whole feature instead of just relatively to the current chapter */
|
paulo@2
|
77 if (dvdnav_set_PGC_positioning_flag(dvdnav, 1) != DVDNAV_STATUS_OK) {
|
paulo@2
|
78 printf("Error on dvdnav_set_PGC_positioning_flag: %s\n", dvdnav_err_to_string(dvdnav));
|
paulo@2
|
79 return 2;
|
paulo@2
|
80 }
|
paulo@2
|
81
|
paulo@6
|
82 /* start at title and part, if specified */
|
paulo@5
|
83 if (argc > 1) {
|
paulo@5
|
84 int start_title = atoi(argv[1]);
|
paulo@6
|
85 int start_part = 1;
|
paulo@6
|
86 if (argc > 2)
|
paulo@6
|
87 start_part = atoi(argv[2]);
|
paulo@6
|
88 dvdnav_part_play(dvdnav, start_title, start_part);
|
paulo@5
|
89 }
|
paulo@2
|
90
|
paulo@2
|
91 /* the read loop which regularly calls dvdnav_get_next_block
|
paulo@2
|
92 * and handles the returned events */
|
paulo@2
|
93 printf("Reading...\n");
|
paulo@2
|
94 while (!finished) {
|
paulo@2
|
95 int result, event, len;
|
paulo@2
|
96 uint8_t *buf = mem;
|
paulo@2
|
97
|
paulo@2
|
98 /* the main reading function */
|
paulo@0
|
99 #if DVD_READ_CACHE
|
paulo@2
|
100 result = dvdnav_get_next_cache_block(dvdnav, &buf, &event, &len);
|
paulo@0
|
101 #else
|
paulo@2
|
102 result = dvdnav_get_next_block(dvdnav, buf, &event, &len);
|
paulo@0
|
103 #endif
|
paulo@0
|
104
|
paulo@2
|
105 if (result == DVDNAV_STATUS_ERR) {
|
paulo@2
|
106 printf("Error getting next block: %s\n", dvdnav_err_to_string(dvdnav));
|
paulo@2
|
107 return 3;
|
paulo@2
|
108 }
|
paulo@2
|
109
|
paulo@2
|
110 switch (event) {
|
paulo@2
|
111 case DVDNAV_BLOCK_OK:
|
paulo@2
|
112 /* We have received a regular block of the currently playing MPEG stream.
|
paulo@2
|
113 * A real player application would now pass this block through demuxing
|
paulo@2
|
114 * and decoding. We simply write it to disc here. */
|
paulo@2
|
115
|
paulo@2
|
116 if (!output_fd && (dump || tt_dump)) {
|
paulo@2
|
117 printf("Opening output...\n");
|
paulo@2
|
118 output_fd = open("libdvdnav.mpg", O_CREAT | O_WRONLY | O_APPEND, S_IRWXU | S_IRWXG);
|
paulo@2
|
119 if (output_fd == -1) {
|
paulo@2
|
120 printf("Error opening output\n");
|
paulo@2
|
121 return 4;
|
paulo@2
|
122 }
|
paulo@2
|
123 }
|
paulo@0
|
124
|
paulo@2
|
125 if (dump || tt_dump)
|
paulo@2
|
126 write(output_fd, buf, len);
|
paulo@0
|
127
|
paulo@2
|
128 break;
|
paulo@2
|
129 case DVDNAV_NOP:
|
paulo@2
|
130 /* Nothing to do here. */
|
paulo@2
|
131 break;
|
paulo@2
|
132 case DVDNAV_STILL_FRAME:
|
paulo@2
|
133 /* We have reached a still frame. A real player application would wait
|
paulo@2
|
134 * the amount of time specified by the still's length while still handling
|
paulo@2
|
135 * user input to make menus and other interactive stills work.
|
paulo@2
|
136 * A length of 0xff means an indefinite still which has to be skipped
|
paulo@2
|
137 * indirectly by some user interaction. */
|
paulo@3
|
138 {
|
paulo@3
|
139 dvdnav_still_event_t *still_event = (dvdnav_still_event_t *)buf;
|
paulo@3
|
140 if (still_event->length < 0xff)
|
paulo@3
|
141 printf("Skipping %d seconds of still frame\n", still_event->length);
|
paulo@3
|
142 else
|
paulo@3
|
143 printf("Skipping indefinite length still frame\n");
|
paulo@3
|
144 dvdnav_still_skip(dvdnav);
|
paulo@3
|
145 }
|
paulo@2
|
146 break;
|
paulo@2
|
147 case DVDNAV_WAIT:
|
paulo@2
|
148 /* We have reached a point in DVD playback, where timing is critical.
|
paulo@2
|
149 * Player application with internal fifos can introduce state
|
paulo@2
|
150 * inconsistencies, because libdvdnav is always the fifo's length
|
paulo@2
|
151 * ahead in the stream compared to what the application sees.
|
paulo@2
|
152 * Such applications should wait until their fifos are empty
|
paulo@2
|
153 * when they receive this type of event. */
|
paulo@2
|
154 printf("Skipping wait condition\n");
|
paulo@2
|
155 dvdnav_wait_skip(dvdnav);
|
paulo@2
|
156 break;
|
paulo@2
|
157 case DVDNAV_SPU_CLUT_CHANGE:
|
paulo@2
|
158 /* Player applications should pass the new colour lookup table to their
|
paulo@2
|
159 * SPU decoder */
|
paulo@2
|
160 break;
|
paulo@2
|
161 case DVDNAV_SPU_STREAM_CHANGE:
|
paulo@2
|
162 /* Player applications should inform their SPU decoder to switch channels */
|
paulo@2
|
163 break;
|
paulo@2
|
164 case DVDNAV_AUDIO_STREAM_CHANGE:
|
paulo@2
|
165 /* Player applications should inform their audio decoder to switch channels */
|
paulo@2
|
166 break;
|
paulo@2
|
167 case DVDNAV_HIGHLIGHT:
|
paulo@2
|
168 /* Player applications should inform their overlay engine to highlight the
|
paulo@2
|
169 * given button */
|
paulo@3
|
170 {
|
paulo@3
|
171 dvdnav_highlight_event_t *highlight_event = (dvdnav_highlight_event_t *)buf;
|
paulo@3
|
172 printf("Selected button %d\n", highlight_event->buttonN);
|
paulo@3
|
173 }
|
paulo@2
|
174 break;
|
paulo@2
|
175 case DVDNAV_VTS_CHANGE:
|
paulo@2
|
176 /* Some status information like video aspect and video scale permissions do
|
paulo@2
|
177 * not change inside a VTS. Therefore this event can be used to query such
|
paulo@2
|
178 * information only when necessary and update the decoding/displaying
|
paulo@2
|
179 * accordingly. */
|
paulo@2
|
180 break;
|
paulo@2
|
181 case DVDNAV_CELL_CHANGE:
|
paulo@2
|
182 /* Some status information like the current Title and Part numbers do not
|
paulo@2
|
183 * change inside a cell. Therefore this event can be used to query such
|
paulo@2
|
184 * information only when necessary and update the decoding/displaying
|
paulo@2
|
185 * accordingly. */
|
paulo@2
|
186 {
|
paulo@2
|
187 int32_t tt = 0, ptt = 0;
|
paulo@7
|
188 uint32_t tpos, tlen;
|
paulo@2
|
189 char input = '\0';
|
paulo@2
|
190
|
paulo@2
|
191 dvdnav_current_title_info(dvdnav, &tt, &ptt);
|
paulo@7
|
192 dvdnav_get_position(dvdnav, &tpos, &tlen);
|
paulo@2
|
193 printf("Cell change: Title %d, Chapter %d\n", tt, ptt);
|
paulo@7
|
194 printf("At position %.0f%% inside the feature (%d / %d) \n", 100 * (double)tpos / (double)tlen, tpos, tlen);
|
paulo@2
|
195
|
paulo@2
|
196 dump = 0;
|
paulo@2
|
197 if (tt_dump && tt != tt_dump)
|
paulo@2
|
198 tt_dump = 0;
|
paulo@2
|
199
|
paulo@2
|
200 if (tt_skip && tt != tt_skip)
|
paulo@2
|
201 tt_skip = 0;
|
paulo@2
|
202
|
paulo@2
|
203 if (output_fd && !tt_dump) {
|
paulo@2
|
204 printf("Closing output...\n");
|
paulo@2
|
205 output_fd = close(output_fd);
|
paulo@2
|
206 }
|
paulo@2
|
207
|
paulo@2
|
208 if (!dump && !tt_dump && !tt_skip) {
|
paulo@2
|
209 fflush(stdin);
|
paulo@2
|
210 while ((input != 'a') && (input != 's') && (input != 'q') && (input != 't') && (input != 'l')) {
|
paulo@2
|
211 printf("(a)ppend cell to output\n(s)kip cell\nappend until end of (t)itle\nskip tit(l)e\n(q)uit\n");
|
paulo@2
|
212 scanf("%c", &input);
|
paulo@2
|
213 }
|
paulo@2
|
214
|
paulo@2
|
215 switch (input) {
|
paulo@2
|
216 case 'a':
|
paulo@2
|
217 dump = 1;
|
paulo@2
|
218 break;
|
paulo@2
|
219 case 't':
|
paulo@2
|
220 tt_dump = tt;
|
paulo@2
|
221 break;
|
paulo@2
|
222 case 'l':
|
paulo@2
|
223 tt_skip = tt;
|
paulo@2
|
224 break;
|
paulo@2
|
225 case 'q':
|
paulo@2
|
226 finished = 1;
|
paulo@2
|
227 }
|
paulo@2
|
228 }
|
paulo@2
|
229 }
|
paulo@2
|
230 break;
|
paulo@2
|
231 case DVDNAV_NAV_PACKET:
|
paulo@2
|
232 /* A NAV packet provides PTS discontinuity information, angle linking information and
|
paulo@2
|
233 * button definitions for DVD menus. Angles are handled completely inside libdvdnav.
|
paulo@2
|
234 * For the menus to work, the NAV packet information has to be passed to the overlay
|
paulo@2
|
235 * engine of the player so that it knows the dimensions of the button areas. */
|
paulo@2
|
236 {
|
paulo@2
|
237 pci_t *pci;
|
paulo@2
|
238
|
paulo@2
|
239 /* Applications with fifos should not use these functions to retrieve NAV packets,
|
paulo@2
|
240 * they should implement their own NAV handling, because the packet you get from these
|
paulo@2
|
241 * functions will already be ahead in the stream which can cause state inconsistencies.
|
paulo@2
|
242 * Applications with fifos should therefore pass the NAV packet through the fifo
|
paulo@2
|
243 * and decoding pipeline just like any other data. */
|
paulo@2
|
244 pci = dvdnav_get_current_nav_pci(dvdnav);
|
paulo@2
|
245 dvdnav_get_current_nav_dsi(dvdnav);
|
paulo@2
|
246
|
paulo@2
|
247 if(pci->hli.hl_gi.btn_ns > 0) {
|
paulo@2
|
248 int button;
|
paulo@2
|
249
|
paulo@2
|
250 printf("Found %i DVD menu buttons...\n", pci->hli.hl_gi.btn_ns);
|
paulo@2
|
251
|
paulo@2
|
252 for (button = 0; button < pci->hli.hl_gi.btn_ns; button++) {
|
paulo@2
|
253 btni_t *btni = &(pci->hli.btnit[button]);
|
paulo@2
|
254 printf("Button %i top-left @ (%i,%i), bottom-right @ (%i,%i)\n",
|
paulo@2
|
255 button + 1, btni->x_start, btni->y_start,
|
paulo@2
|
256 btni->x_end, btni->y_end);
|
paulo@2
|
257 }
|
paulo@2
|
258
|
paulo@2
|
259 button = 0;
|
paulo@2
|
260 while ((button <= 0) || (button > pci->hli.hl_gi.btn_ns)) {
|
paulo@2
|
261 printf("Which button (1 to %i): ", pci->hli.hl_gi.btn_ns);
|
paulo@2
|
262 scanf("%i", &button);
|
paulo@2
|
263 }
|
paulo@2
|
264
|
paulo@2
|
265 printf("Selecting button %i...\n", button);
|
paulo@2
|
266 /* This is the point where applications with fifos have to hand in a NAV packet
|
paulo@2
|
267 * which has traveled through the fifos. See the notes above. */
|
paulo@2
|
268 dvdnav_button_select_and_activate(dvdnav, pci, button);
|
paulo@2
|
269 }
|
paulo@2
|
270 }
|
paulo@2
|
271 break;
|
paulo@2
|
272 case DVDNAV_HOP_CHANNEL:
|
paulo@2
|
273 /* This event is issued whenever a non-seamless operation has been executed.
|
paulo@2
|
274 * Applications with fifos should drop the fifos content to speed up responsiveness. */
|
paulo@2
|
275 break;
|
paulo@2
|
276 case DVDNAV_STOP:
|
paulo@2
|
277 /* Playback should end here. */
|
paulo@2
|
278 finished = 1;
|
paulo@2
|
279 break;
|
paulo@2
|
280 default:
|
paulo@2
|
281 printf("Unknown event (%i)\n", event);
|
paulo@2
|
282 finished = 1;
|
paulo@2
|
283 break;
|
paulo@2
|
284 }
|
paulo@2
|
285 #if DVD_READ_CACHE
|
paulo@2
|
286 dvdnav_free_cache_block(dvdnav, buf);
|
paulo@2
|
287 #endif
|
paulo@0
|
288 }
|
paulo@2
|
289
|
paulo@2
|
290 /* destroy dvdnav handle */
|
paulo@2
|
291 if (dvdnav_close(dvdnav) != DVDNAV_STATUS_OK) {
|
paulo@2
|
292 printf("Error on dvdnav_close: %s\n", dvdnav_err_to_string(dvdnav));
|
paulo@2
|
293 return 5;
|
paulo@1
|
294 }
|
paulo@2
|
295 close(output_fd);
|
paulo@2
|
296
|
paulo@2
|
297 return 0;
|
paulo@0
|
298 }
|