paulo@0: /* paulo@0: * Copyright (C) 2003 by the libdvdnav project paulo@0: * paulo@0: * This file is part of libdvdnav, a DVD navigation library. paulo@0: * paulo@0: * libdvdnav is free software; you can redistribute it and/or modify paulo@0: * it under the terms of the GNU General Public License as published by paulo@0: * the Free Software Foundation; either version 2 of the License, or paulo@0: * (at your option) any later version. paulo@0: * paulo@0: * libdvdnav is distributed in the hope that it will be useful, paulo@0: * but WITHOUT ANY WARRANTY; without even the implied warranty of paulo@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the paulo@0: * GNU General Public License for more details. paulo@0: * paulo@0: * You should have received a copy of the GNU General Public License paulo@0: * along with this program; if not, write to the Free Software paulo@0: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA paulo@0: * paulo@0: * $Id: menus.c 1135 2008-09-06 21:55:51Z rathann $ paulo@0: * paulo@0: */ paulo@0: paulo@0: #include paulo@0: #include paulo@0: #include paulo@0: #include paulo@0: #include paulo@0: #include paulo@0: #include "dvd_types.h" paulo@0: #include paulo@0: #include paulo@0: #include /* For vm_cmd_t */ paulo@0: #include "dvdnav.h" paulo@0: #include "dvdnav_events.h" paulo@0: paulo@0: /* shall we use libdvdnav's read ahead cache? */ paulo@0: #define DVD_READ_CACHE 1 paulo@0: paulo@0: /* which is the default language for menus/audio/subpictures? */ paulo@0: #define DVD_LANGUAGE "en" paulo@0: paulo@0: #ifdef WIN32 paulo@0: #define S_IRWXG 0 paulo@0: #endif paulo@0: paulo@0: int main(int argc, char **argv) { paulo@2: dvdnav_t *dvdnav; paulo@2: uint8_t mem[DVD_VIDEO_LB_LEN]; paulo@2: int finished = 0; paulo@2: int output_fd = 0; paulo@2: int dump = 0, tt_dump = 0, tt_skip = 0; paulo@2: paulo@2: /* open dvdnav handle */ paulo@2: printf("Opening DVD...\n"); paulo@2: if (dvdnav_open(&dvdnav, "/dev/dvd") != DVDNAV_STATUS_OK) { paulo@2: printf("Error on dvdnav_open\n"); paulo@2: return 1; paulo@2: } paulo@2: paulo@2: /* set read ahead cache usage */ paulo@2: if (dvdnav_set_readahead_flag(dvdnav, DVD_READ_CACHE) != DVDNAV_STATUS_OK) { paulo@2: printf("Error on dvdnav_set_readahead_flag: %s\n", dvdnav_err_to_string(dvdnav)); paulo@2: return 2; paulo@2: } paulo@2: paulo@2: /* set the language */ paulo@2: if (dvdnav_menu_language_select(dvdnav, DVD_LANGUAGE) != DVDNAV_STATUS_OK || paulo@2: dvdnav_audio_language_select(dvdnav, DVD_LANGUAGE) != DVDNAV_STATUS_OK || paulo@2: dvdnav_spu_language_select(dvdnav, DVD_LANGUAGE) != DVDNAV_STATUS_OK) { paulo@2: printf("Error on setting languages: %s\n", dvdnav_err_to_string(dvdnav)); paulo@2: return 2; paulo@2: } paulo@2: paulo@2: /* set the PGC positioning flag to have position information relatively to the paulo@2: * whole feature instead of just relatively to the current chapter */ paulo@2: if (dvdnav_set_PGC_positioning_flag(dvdnav, 1) != DVDNAV_STATUS_OK) { paulo@2: printf("Error on dvdnav_set_PGC_positioning_flag: %s\n", dvdnav_err_to_string(dvdnav)); paulo@2: return 2; paulo@2: } paulo@2: paulo@6: /* start at title and part, if specified */ paulo@5: if (argc > 1) { paulo@5: int start_title = atoi(argv[1]); paulo@6: int start_part = 1; paulo@6: if (argc > 2) paulo@6: start_part = atoi(argv[2]); paulo@6: dvdnav_part_play(dvdnav, start_title, start_part); paulo@5: } paulo@2: paulo@2: /* the read loop which regularly calls dvdnav_get_next_block paulo@2: * and handles the returned events */ paulo@2: printf("Reading...\n"); paulo@2: while (!finished) { paulo@2: int result, event, len; paulo@2: uint8_t *buf = mem; paulo@2: paulo@2: /* the main reading function */ paulo@0: #if DVD_READ_CACHE paulo@2: result = dvdnav_get_next_cache_block(dvdnav, &buf, &event, &len); paulo@0: #else paulo@2: result = dvdnav_get_next_block(dvdnav, buf, &event, &len); paulo@0: #endif paulo@0: paulo@2: if (result == DVDNAV_STATUS_ERR) { paulo@2: printf("Error getting next block: %s\n", dvdnav_err_to_string(dvdnav)); paulo@2: return 3; paulo@2: } paulo@2: paulo@2: switch (event) { paulo@2: case DVDNAV_BLOCK_OK: paulo@2: /* We have received a regular block of the currently playing MPEG stream. paulo@2: * A real player application would now pass this block through demuxing paulo@2: * and decoding. We simply write it to disc here. */ paulo@2: paulo@2: if (!output_fd && (dump || tt_dump)) { paulo@2: printf("Opening output...\n"); paulo@2: output_fd = open("libdvdnav.mpg", O_CREAT | O_WRONLY | O_APPEND, S_IRWXU | S_IRWXG); paulo@2: if (output_fd == -1) { paulo@2: printf("Error opening output\n"); paulo@2: return 4; paulo@2: } paulo@2: } paulo@0: paulo@2: if (dump || tt_dump) paulo@2: write(output_fd, buf, len); paulo@0: paulo@2: break; paulo@2: case DVDNAV_NOP: paulo@2: /* Nothing to do here. */ paulo@2: break; paulo@2: case DVDNAV_STILL_FRAME: paulo@2: /* We have reached a still frame. A real player application would wait paulo@2: * the amount of time specified by the still's length while still handling paulo@2: * user input to make menus and other interactive stills work. paulo@2: * A length of 0xff means an indefinite still which has to be skipped paulo@2: * indirectly by some user interaction. */ paulo@3: { paulo@3: dvdnav_still_event_t *still_event = (dvdnav_still_event_t *)buf; paulo@3: if (still_event->length < 0xff) paulo@3: printf("Skipping %d seconds of still frame\n", still_event->length); paulo@3: else paulo@3: printf("Skipping indefinite length still frame\n"); paulo@3: dvdnav_still_skip(dvdnav); paulo@3: } paulo@2: break; paulo@2: case DVDNAV_WAIT: paulo@2: /* We have reached a point in DVD playback, where timing is critical. paulo@2: * Player application with internal fifos can introduce state paulo@2: * inconsistencies, because libdvdnav is always the fifo's length paulo@2: * ahead in the stream compared to what the application sees. paulo@2: * Such applications should wait until their fifos are empty paulo@2: * when they receive this type of event. */ paulo@2: printf("Skipping wait condition\n"); paulo@2: dvdnav_wait_skip(dvdnav); paulo@2: break; paulo@2: case DVDNAV_SPU_CLUT_CHANGE: paulo@2: /* Player applications should pass the new colour lookup table to their paulo@2: * SPU decoder */ paulo@2: break; paulo@2: case DVDNAV_SPU_STREAM_CHANGE: paulo@2: /* Player applications should inform their SPU decoder to switch channels */ paulo@2: break; paulo@2: case DVDNAV_AUDIO_STREAM_CHANGE: paulo@2: /* Player applications should inform their audio decoder to switch channels */ paulo@2: break; paulo@2: case DVDNAV_HIGHLIGHT: paulo@2: /* Player applications should inform their overlay engine to highlight the paulo@2: * given button */ paulo@3: { paulo@3: dvdnav_highlight_event_t *highlight_event = (dvdnav_highlight_event_t *)buf; paulo@3: printf("Selected button %d\n", highlight_event->buttonN); paulo@3: } paulo@2: break; paulo@2: case DVDNAV_VTS_CHANGE: paulo@2: /* Some status information like video aspect and video scale permissions do paulo@2: * not change inside a VTS. Therefore this event can be used to query such paulo@2: * information only when necessary and update the decoding/displaying paulo@2: * accordingly. */ paulo@2: break; paulo@2: case DVDNAV_CELL_CHANGE: paulo@2: /* Some status information like the current Title and Part numbers do not paulo@2: * change inside a cell. Therefore this event can be used to query such paulo@2: * information only when necessary and update the decoding/displaying paulo@2: * accordingly. */ paulo@2: { paulo@2: int32_t tt = 0, ptt = 0; paulo@2: uint32_t pos, len; paulo@2: char input = '\0'; paulo@2: paulo@2: dvdnav_current_title_info(dvdnav, &tt, &ptt); paulo@2: dvdnav_get_position(dvdnav, &pos, &len); paulo@2: printf("Cell change: Title %d, Chapter %d\n", tt, ptt); paulo@2: printf("At position %.0f%% inside the feature\n", 100 * (double)pos / (double)len); paulo@2: paulo@2: dump = 0; paulo@2: if (tt_dump && tt != tt_dump) paulo@2: tt_dump = 0; paulo@2: paulo@2: if (tt_skip && tt != tt_skip) paulo@2: tt_skip = 0; paulo@2: paulo@2: if (output_fd && !tt_dump) { paulo@2: printf("Closing output...\n"); paulo@2: output_fd = close(output_fd); paulo@2: } paulo@2: paulo@2: if (!dump && !tt_dump && !tt_skip) { paulo@2: fflush(stdin); paulo@2: while ((input != 'a') && (input != 's') && (input != 'q') && (input != 't') && (input != 'l')) { paulo@2: 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: scanf("%c", &input); paulo@2: } paulo@2: paulo@2: switch (input) { paulo@2: case 'a': paulo@2: dump = 1; paulo@2: break; paulo@2: case 't': paulo@2: tt_dump = tt; paulo@2: break; paulo@2: case 'l': paulo@2: tt_skip = tt; paulo@2: break; paulo@2: case 'q': paulo@2: finished = 1; paulo@2: } paulo@2: } paulo@2: } paulo@2: break; paulo@2: case DVDNAV_NAV_PACKET: paulo@2: /* A NAV packet provides PTS discontinuity information, angle linking information and paulo@2: * button definitions for DVD menus. Angles are handled completely inside libdvdnav. paulo@2: * For the menus to work, the NAV packet information has to be passed to the overlay paulo@2: * engine of the player so that it knows the dimensions of the button areas. */ paulo@2: { paulo@2: pci_t *pci; paulo@2: paulo@2: /* Applications with fifos should not use these functions to retrieve NAV packets, paulo@2: * they should implement their own NAV handling, because the packet you get from these paulo@2: * functions will already be ahead in the stream which can cause state inconsistencies. paulo@2: * Applications with fifos should therefore pass the NAV packet through the fifo paulo@2: * and decoding pipeline just like any other data. */ paulo@2: pci = dvdnav_get_current_nav_pci(dvdnav); paulo@2: dvdnav_get_current_nav_dsi(dvdnav); paulo@2: paulo@2: if(pci->hli.hl_gi.btn_ns > 0) { paulo@2: int button; paulo@2: paulo@2: printf("Found %i DVD menu buttons...\n", pci->hli.hl_gi.btn_ns); paulo@2: paulo@2: for (button = 0; button < pci->hli.hl_gi.btn_ns; button++) { paulo@2: btni_t *btni = &(pci->hli.btnit[button]); paulo@2: printf("Button %i top-left @ (%i,%i), bottom-right @ (%i,%i)\n", paulo@2: button + 1, btni->x_start, btni->y_start, paulo@2: btni->x_end, btni->y_end); paulo@2: } paulo@2: paulo@2: button = 0; paulo@2: while ((button <= 0) || (button > pci->hli.hl_gi.btn_ns)) { paulo@2: printf("Which button (1 to %i): ", pci->hli.hl_gi.btn_ns); paulo@2: scanf("%i", &button); paulo@2: } paulo@2: paulo@2: printf("Selecting button %i...\n", button); paulo@2: /* This is the point where applications with fifos have to hand in a NAV packet paulo@2: * which has traveled through the fifos. See the notes above. */ paulo@2: dvdnav_button_select_and_activate(dvdnav, pci, button); paulo@2: } paulo@2: } paulo@2: break; paulo@2: case DVDNAV_HOP_CHANNEL: paulo@2: /* This event is issued whenever a non-seamless operation has been executed. paulo@2: * Applications with fifos should drop the fifos content to speed up responsiveness. */ paulo@2: break; paulo@2: case DVDNAV_STOP: paulo@2: /* Playback should end here. */ paulo@2: finished = 1; paulo@2: break; paulo@2: default: paulo@2: printf("Unknown event (%i)\n", event); paulo@2: finished = 1; paulo@2: break; paulo@2: } paulo@2: #if DVD_READ_CACHE paulo@2: dvdnav_free_cache_block(dvdnav, buf); paulo@2: #endif paulo@0: } paulo@2: paulo@2: /* destroy dvdnav handle */ paulo@2: if (dvdnav_close(dvdnav) != DVDNAV_STATUS_OK) { paulo@2: printf("Error on dvdnav_close: %s\n", dvdnav_err_to_string(dvdnav)); paulo@2: return 5; paulo@1: } paulo@2: close(output_fd); paulo@2: paulo@2: return 0; paulo@0: }