/***************************************************************************** * Close: *****************************************************************************/ static void Close( vlc_object_t *p_this ) { demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys = p_demux->p_sys; int i; for( i = 0; i < PS_TK_COUNT; i++ ) { ps_track_t *tk = &p_sys->tk[i]; if( tk->b_seen ) { es_format_Clean( &tk->fmt ); if( tk->es ) es_out_Del( p_demux->out, tk->es ); } } /* Free the array of titles */ for( int i = 0; i < p_sys->i_titles; i++ ) vlc_input_title_Delete( p_sys->titles[i] ); TAB_CLEAN( p_sys->i_titles, p_sys->titles ); /* Close libdvdread */ if( p_sys->p_title ) DVDCloseFile( p_sys->p_title ); if( p_sys->p_vts_file ) ifoClose( p_sys->p_vts_file ); if( p_sys->p_vmg_file ) ifoClose( p_sys->p_vmg_file ); DVDClose( p_sys->p_dvdread ); free( p_sys ); }
static int mp_describe_titleset(stream_t *stream, dvd_reader_t *dvd, tt_srpt_t *tt_srpt, int vts_no) { ifo_handle_t *vts_file; int title_no, msec=0; vts_file = ifoOpen(dvd, vts_no); if(!vts_file) return 0; if(!vts_file->vtsi_mat || !vts_file->vts_pgcit) { ifoClose(vts_file); return 0; } for(title_no = 0; title_no < tt_srpt->nr_of_srpts; title_no++) { if (tt_srpt->title[title_no].title_set_nr != vts_no) continue; msec = mp_get_titleset_length(vts_file, tt_srpt, title_no); MP_SMODE(stream, "ID_DVD_TITLE_%d_LENGTH=%d.%03d\n", title_no, msec / 1000, msec % 1000); } ifoClose(vts_file); return 1; }
static void stream_dvd_close(stream_t *s) { dvd_priv_t *d = s->priv; ifoClose(d->vts_file); ifoClose(d->vmg_file); DVDCloseFile(d->title); DVDClose(d->dvd); dvd_set_speed(s,dvd_device_current, -1); /* -1 => restore default */ }
static void dvd_close(dvd_priv_t *d) { ifoClose(d->vts_file); ifoClose(d->vmg_file); DVDCloseFile(d->title); DVDClose(d->dvd); dvd_set_speed(dvd_device_current, -1); /* -1 => restore default */ }
int main (int argc, const char *argv[]) { dvd_reader_t *dvd; ifo_handle_t *ifohandle; tt_srpt_t *tt_srpt; int number, i; if( argc != 2 ) { fprintf( stderr, "Usage: %s <dvd path>\n", argv[ 0 ] ); return -1; } dvd = DVDOpen(argv[1]); if( !dvd ) return 0; ifohandle = ifoOpen(dvd, 0); number = ifohandle->vmgi_mat->vmg_nr_of_title_sets; tt_srpt = ifohandle->tt_srpt; for( i = 0; i < tt_srpt->nr_of_srpts; ++i ) { ifo_handle_t *ifo2; ifo2 = ifoOpen(dvd, tt_srpt->title[ i ].title_set_nr); if(!ifo2) return 0; printf("%02x:%02x:%02x\n", ifo2->vts_pgcit->pgci_srp->pgc->playback_time.hour, ifo2->vts_pgcit->pgci_srp->pgc->playback_time.minute, ifo2->vts_pgcit->pgci_srp->pgc->playback_time.second); ifoClose(ifo2); } ifoClose(ifohandle); DVDClose(dvd); }
/***************************************************************************** * Close: *****************************************************************************/ static void Close( vlc_object_t *p_this ) { demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys = p_demux->p_sys; int i; for( i = 0; i < PS_TK_COUNT; i++ ) { ps_track_t *tk = &p_sys->tk[i]; if( tk->b_seen ) { es_format_Clean( &tk->fmt ); if( tk->es ) es_out_Del( p_demux->out, tk->es ); } } /* Close libdvdread */ if( p_sys->p_title ) DVDCloseFile( p_sys->p_title ); if( p_sys->p_vts_file ) ifoClose( p_sys->p_vts_file ); if( p_sys->p_vmg_file ) ifoClose( p_sys->p_vmg_file ); DVDClose( p_sys->p_dvdread ); free( p_sys ); }
/* ---------------------------------------------------------------------------------*/ void TrackClose( DVD_TRACK_INFO* stTrack ) { if( stTrack->file ) DVDCloseFile( stTrack->file ); if( stTrack->ifo_file ) ifoClose( stTrack->ifo_file ); if( stTrack->dvd ) DVDClose( stTrack->dvd ); if( stTrack->sBuf ) free( stTrack->sBuf ); stTrack->file= NULL; stTrack->dvd= NULL; stTrack->ifo_file= NULL; stTrack->sBuf= NULL; stTrack->iVobuNo= -1; }
int DVDDiscID( dvd_reader_t *dvd, unsigned char *discid ) { struct md5_s ctx; int title; int title_sets; int nr_of_files = 0; ifo_handle_t *vmg_ifo; /* Check arguments. */ if( dvd == NULL || discid == NULL ) return 0; vmg_ifo = ifoOpen( dvd, 0 ); if( !vmg_ifo ) { fprintf( stderr, "libdvdread: DVDDiscId, failed to " "open VMG IFO!\n" ); return -1; } title_sets = vmg_ifo->vmgi_mat->vmg_nr_of_title_sets + 1; ifoClose( vmg_ifo ); if( title_sets > 10 ) title_sets = 10; /* Go through the first IFO:s, in order, up until the tenth, * and md5sum them, i.e VIDEO_TS.IFO and VTS_0?_0.IFO */ InitMD5( &ctx ); for( title = 0; title < title_sets; title++ ) { dvd_file_t *dvd_file = DVDOpenFile( dvd, title, DVD_READ_INFO_FILE ); if( dvd_file != NULL ) { ssize_t bytes_read; ssize_t file_size = dvd_file->filesize * DVD_VIDEO_LB_LEN; char *buffer_base = malloc( file_size + 2048 ); if( buffer_base == NULL ) { DVDCloseFile( dvd_file ); fprintf( stderr, "libdvdread: DVDDiscId, failed to " "allocate memory for file read!\n" ); return -1; } char *buffer = (char *)(((uintptr_t)buffer_base & ~((uintptr_t)2047)) + 2048); bytes_read = DVDReadBytes( dvd_file, buffer, file_size ); if( bytes_read != file_size ) { fprintf( stderr, "libdvdread: DVDDiscId read returned %zd bytes" ", wanted %zd\n", bytes_read, file_size ); DVDCloseFile( dvd_file ); free( buffer_base ); return -1; } AddMD5( &ctx, buffer, file_size ); DVDCloseFile( dvd_file ); free( buffer_base ); nr_of_files++; } } EndMD5( &ctx ); memcpy( discid, ctx.buf, 16 ); if(!nr_of_files) return -1; return 0; }
int main( int argc, char **argv ) { dvd_reader_t *dvd; ifo_handle_t *ifo_file; tt_srpt_t *tt_srpt; int i, j; /** * Usage. */ if( argc != 2 ) { fprintf( stderr, "Usage: %s <dvd path>\n", argv[ 0 ] ); return -1; } dvd = DVDOpen( argv[ 1 ] ); if( !dvd ) { fprintf( stderr, "Couldn't open DVD: %s\n", argv[ 1 ] ); return -1; } ifo_file = ifoOpen( dvd, 0 ); if( !ifo_file ) { fprintf( stderr, "Can't open VMG info.\n" ); DVDClose( dvd ); return -1; } tt_srpt = ifo_file->tt_srpt; //printf( "There are %d titles.\n", tt_srpt->nr_of_srpts ); // let's see if we can make this spit out JSON... printf("[\n"); for( i = 0; i < tt_srpt->nr_of_srpts; ++i ) { ifo_handle_t *vts_file; vts_ptt_srpt_t *vts_ptt_srpt; pgc_t *cur_pgc; int vtsnum, ttnnum, pgcnum, chapts; vtsnum = tt_srpt->title[ i ].title_set_nr; ttnnum = tt_srpt->title[ i ].vts_ttn; chapts = tt_srpt->title[ i ].nr_of_ptts; printf("{\"title\":\"%d\",\n", i + 1); printf("\"vts\":\"%d\",\n", vtsnum); printf("\"ttn\":\"%d\",\n", ttnnum); printf("\"chapters\":[\n"); //printf( "\nTitle %d:\n", i + 1 ); //printf( "\tIn VTS: %d [TTN %d]\n", vtsnum, ttnnum ); //printf( "\n" ); //printf( "\tTitle has %d chapters and %d angles\n", chapts, // tt_srpt->title[ i ].nr_of_angles ); vts_file = ifoOpen( dvd, vtsnum ); if( !vts_file ) { fprintf( stderr, "Can't open info file for title %d.\n", vtsnum ); DVDClose( dvd ); return -1; } vts_ptt_srpt = vts_file->vts_ptt_srpt; for( j = 0; j < chapts; ++j ) { int start_cell; int pgn; pgcnum = vts_ptt_srpt->title[ ttnnum - 1 ].ptt[ j ].pgcn; pgn = vts_ptt_srpt->title[ ttnnum - 1 ].ptt[ j ].pgn; cur_pgc = vts_file->vts_pgcit->pgci_srp[ pgcnum - 1 ].pgc; start_cell = cur_pgc->program_map[ pgn - 1 ] - 1; printf("{\"chapter\":\"%d\"}", j + 1); if(j < chapts - 1){ printf(","); } printf("\n"); /* printf( "\tChapter %3d [PGC %2d, PG %2d] starts at Cell %2d [sector %x-%x]\n", j, pgcnum, pgn, start_cell, cur_pgc->cell_playback[ start_cell ].first_sector, cur_pgc->cell_playback[ start_cell ].last_sector ); */ } printf("]}"); if(i < tt_srpt->nr_of_srpts - 1){ printf(","); } printf("\n"); ifoClose( vts_file ); } printf("]"); ifoClose( ifo_file ); DVDClose( dvd ); DVDFinish(); return 0; }
int main(int argc, char **argv) { if(argc == 2 || argc > 3 || (argc > 1 && strncmp(&argv[0][0], "-", 1) == 0)) { printf("dvd_drive_status [dvd_device dvd_raw_device]\n"); printf("Default devices: %s %s\n", DEFAULT_DVD_DEVICE, DEFAULT_DVD_RAW_DEVICE); return 1; } const char *device_filename = NULL; const char *raw_device_filename = NULL; if(argc > 2) { device_filename = argv[1]; raw_device_filename = argv[2]; } else { device_filename = DEFAULT_DVD_DEVICE; raw_device_filename = DEFAULT_DVD_RAW_DEVICE; } int fd = -1; fd = open(raw_device_filename, O_RDONLY); if(fd < 0) { if(errno == EACCES) fprintf(stderr, "could not open device %s permission denied\n", raw_device_filename); else fprintf(stderr, "could not open device %s (errno: %i)\n", raw_device_filename, errno); return 1; } close(fd); fd = open(device_filename, O_RDONLY); if(fd < 0) { #ifdef __NetBSD__ if(errno == ENODEV) #else if(errno == EIO) #endif { printf("drive is closed with no disc or drive is open\n"); return 2; } else { fprintf(stderr, "could not detect drive status (errno: %i)\n", errno); return 1; } } /** * Look to see if the disc in the drive is a DVD or not. * libdvdread will open any disc just fine, so scan it to see if there * is a VMG IFO as well as an additional check. */ dvd_reader_t *dvdread_dvd = NULL; // FreeBSD and NetBSD will throw syslog messages if there's a disc in the drive that's // not a DVD. dvdread_dvd = DVDOpen(device_filename); if(dvdread_dvd) { ifo_handle_t *vmg_ifo = NULL; // OpenBSD will throw a syslog error here if the tray is open vmg_ifo = ifoOpen(dvdread_dvd, 0); if(vmg_ifo == NULL) { ifoClose(vmg_ifo); DVDClose(dvdread_dvd); close(fd); #ifdef __OpenBSD__ printf("drive is closed with no disc or non-DVD or drive is open\n"); return 2; #else printf("drive is closed with a disc that is not a DVD\n"); return 4; #endif } else { printf("drive is closed with a DVD\n"); DVDClose(dvdread_dvd); close(fd); return 3; } } close(fd); return 0; }
int main(int argc, char **argv) { /** * Parse options */ bool verbose = false; bool debug = false; bool opt_track_number = false; bool opt_chapter_number = false; bool opt_filename = false; bool valid_preset = false; uint16_t arg_track_number = 0; int long_index = 0; int opt = 0; opterr = 1; uint8_t arg_first_chapter = 1; uint8_t arg_last_chapter = 99; char *token = NULL; char *token_filename = NULL; char tmp_filename[5] = {'\0'}; char dvd_mpv_args[13] = {'\0'}; char dvd_mpv_first_chapter[5] = {'\0'}; char dvd_mpv_last_chapter[5] = {'\0'}; mpv_handle *dvd_mpv = NULL; mpv_event *dvd_mpv_event = NULL; struct mpv_event_log_message *dvd_mpv_log_message = NULL; // Video Title Set struct dvd_vts dvd_vts[99]; const char str_options[] = "Ac:dehl:o:p:t:Vvz"; struct option long_options[] = { { "chapters", required_argument, 0, 'c' }, { "track", required_argument, 0, 't' }, { "alang", required_argument, 0, 'l' }, { "aid", required_argument, 0, 'A' }, { "deinterlace", no_argument, 0, 'd' }, { "detelecine", no_argument, 0, 'e' }, { "preset", required_argument, 0, 'p' }, { "output", required_argument, 0, 'o' }, { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'V' }, { "verbose", no_argument, 0, 'v' }, { "debug", no_argument, 0, 'z' }, { 0, 0, 0, 0 } }; struct dvd_trip dvd_trip; dvd_trip.track = 1; dvd_trip.first_chapter = 1; dvd_trip.last_chapter = 99; memset(dvd_mpv_first_chapter, '\0', sizeof(dvd_mpv_first_chapter)); memset(dvd_mpv_last_chapter, '\0', sizeof(dvd_mpv_last_chapter)); memset(dvd_trip.filename, '\0', sizeof(dvd_trip.filename)); memset(dvd_trip.container, '\0', sizeof(dvd_trip.container)); strcpy(dvd_trip.container, "mkv"); memset(dvd_trip.preset, '\0', sizeof(dvd_trip.preset)); strcpy(dvd_trip.preset, "medium"); memset(dvd_trip.vcodec, '\0', sizeof(dvd_trip.vcodec)); memset(dvd_trip.vcodec_preset, '\0', sizeof(dvd_trip.vcodec_preset)); memset(dvd_trip.vcodec_opts, '\0', sizeof(dvd_trip.vcodec_opts)); memset(dvd_trip.vcodec_log_level, '\0', sizeof(dvd_trip.vcodec_log_level)); memset(dvd_trip.color_opts, '\0', sizeof(dvd_trip.color_opts)); memset(dvd_trip.acodec, '\0', sizeof(dvd_trip.acodec)); memset(dvd_trip.acodec_opts, '\0', sizeof(dvd_trip.acodec_opts)); memset(dvd_trip.audio_lang, '\0', sizeof(dvd_trip.audio_lang)); memset(dvd_trip.audio_aid, '\0', sizeof(dvd_trip.audio_aid)); memset(dvd_trip.vf_opts, '\0', sizeof(dvd_trip.vf_opts)); dvd_trip.crf = 28; memset(dvd_trip.fps, '\0', sizeof(dvd_trip.fps)); dvd_trip.deinterlace = false; dvd_trip.detelecine = false; dvd_trip.pass = 1; while((opt = getopt_long(argc, argv, str_options, long_options, &long_index )) != -1) { switch(opt) { case 'A': strncpy(dvd_trip.audio_aid, optarg, 3); break; case 'c': opt_chapter_number = true; token = strtok(optarg, "-"); { if(strlen(token) > 2) { fprintf(stderr, "Chapter range must be between 1 and 99\n"); return 1; } arg_first_chapter = (uint8_t)strtoumax(token, NULL, 0); } token = strtok(NULL, "-"); if(token != NULL) { if(strlen(token) > 2) { fprintf(stderr, "Chapter range must be between 1 and 99\n"); return 1; } arg_last_chapter = (uint8_t)strtoumax(token, NULL, 0); } if(arg_first_chapter == 0) arg_first_chapter = 1; if(arg_last_chapter < arg_first_chapter) arg_last_chapter = arg_first_chapter; break; case 'd': dvd_trip.deinterlace = true; break; case 'e': dvd_trip.detelecine = true; break; case 'h': print_usage(DVD_INFO_PROGRAM); return 0; case 'l': strncpy(dvd_trip.audio_lang, optarg, 2); break; case 'p': if(strncmp(optarg, "low", 3) == 0) { strcpy(dvd_trip.preset, "low"); } else if(strncmp(optarg, "medium", 6) == 0) { strcpy(dvd_trip.preset, "medium"); } else if(strncmp(optarg, "high", 4) == 0) { strcpy(dvd_trip.preset, "high"); } else if(strncmp(optarg, "insane", 6) == 0) { strcpy(dvd_trip.preset, "insane"); } else { printf("dvd_trip [error]: valid presets - low medium high insane\n"); return 1; } break; case 'o': opt_filename = true; strncpy(dvd_trip.filename, optarg, PATH_MAX - 1); token_filename = strtok(optarg, "."); // Choose preset from file extension while(token_filename != NULL) { snprintf(tmp_filename, 5, "%s", token_filename); token_filename = strtok(NULL, "."); if(token_filename == NULL && strlen(tmp_filename) == 3 && strncmp(tmp_filename, "mkv", 3) == 0) { strncpy(dvd_trip.container, "mkv", 4); valid_preset = true; } else if(token_filename == NULL && strlen(tmp_filename) == 3 && strncmp(tmp_filename, "mp4", 3) == 0) { strncpy(dvd_trip.container, "mp4", 4); valid_preset = true; } else if(token_filename == NULL && strlen(tmp_filename) == 4 && strncmp(tmp_filename, "webm", 4) == 0) { strncpy(dvd_trip.container, "webm", 5); valid_preset = true; } } if(!valid_preset) { printf("dvd_trip [error]: output filename extension must be one of: .mkv, .mp4, .webm\n"); return 1; } break; case 't': opt_track_number = true; arg_track_number = (uint16_t)strtoumax(optarg, NULL, 0); break; case 'V': print_version("dvd_trip"); return 0; case 'v': verbose = true; break; case 'z': verbose = true; debug = true; break; // ignore unknown arguments case '?': print_usage("dvd_trip"); return 1; // let getopt_long set the variable case 0: default: break; } } const char *device_filename = DEFAULT_DVD_DEVICE; if (argv[optind]) device_filename = argv[optind]; if(access(device_filename, F_OK) != 0) { fprintf(stderr, "cannot access %s\n", device_filename); return 1; } // Check to see if device can be opened int dvd_fd = 0; dvd_fd = dvd_device_open(device_filename); if(dvd_fd < 0) { fprintf(stderr, "dvd_trip: error opening %s\n", device_filename); return 1; } dvd_device_close(dvd_fd); #ifdef __linux__ // Poll drive status if it is hardware if(dvd_device_is_hardware(device_filename)) { // Wait for the drive to become ready if(!dvd_drive_has_media(device_filename)) { fprintf(stderr, "drive status: "); dvd_drive_display_status(device_filename); return 1; } } #endif dvd_reader_t *dvdread_dvd = NULL; dvdread_dvd = DVDOpen(device_filename); if(!dvdread_dvd) { fprintf(stderr, "* dvdread could not open %s\n", device_filename); return 1; } ifo_handle_t *vmg_ifo = NULL; vmg_ifo = ifoOpen(dvdread_dvd, 0); if(vmg_ifo == NULL) { fprintf(stderr, "* Could not open IFO zero\n"); DVDClose(dvdread_dvd); return 1; } // DVD struct dvd_info dvd_info; memset(dvd_info.dvdread_id, '\0', sizeof(dvd_info.dvdread_id)); dvd_info.video_title_sets = dvd_video_title_sets(vmg_ifo); dvd_info.side = 1; memset(dvd_info.title, '\0', sizeof(dvd_info.title)); memset(dvd_info.provider_id, '\0', sizeof(dvd_info.provider_id)); memset(dvd_info.vmg_id, '\0', sizeof(dvd_info.vmg_id)); dvd_info.tracks = dvd_tracks(vmg_ifo); dvd_info.longest_track = 1; dvd_title(dvd_info.title, device_filename); printf("Disc title: %s\n", dvd_info.title); uint16_t num_ifos = 1; num_ifos = vmg_ifo->vts_atrt->nr_of_vtss; if(num_ifos < 1) { fprintf(stderr, "* DVD has no title IFOs?!\n"); fprintf(stderr, "* Most likely a bug in libdvdread or a bad master or problems reading the disc\n"); ifoClose(vmg_ifo); DVDClose(dvdread_dvd); return 1; } // Track struct dvd_track dvd_track; memset(&dvd_track, 0, sizeof(dvd_track)); struct dvd_track dvd_tracks[DVD_MAX_TRACKS]; memset(&dvd_tracks, 0, sizeof(dvd_track) * dvd_info.tracks); // Cells struct dvd_cell dvd_cell; dvd_cell.cell = 1; memset(dvd_cell.length, '\0', sizeof(dvd_cell.length)); snprintf(dvd_cell.length, DVD_CELL_LENGTH + 1, "00:00:00.000"); dvd_cell.msecs = 0; // Open first IFO uint16_t vts = 1; ifo_handle_t *vts_ifo = NULL; vts_ifo = ifoOpen(dvdread_dvd, vts); if(vts_ifo == NULL) { fprintf(stderr, "* Could not open VTS_IFO for track %u\n", 1); return 1; } ifoClose(vts_ifo); vts_ifo = NULL; // Create an array of all the IFOs ifo_handle_t *vts_ifos[DVD_MAX_VTS_IFOS]; vts_ifos[0] = NULL; for(vts = 1; vts < dvd_info.video_title_sets + 1; vts++) { dvd_vts[vts].vts = vts; dvd_vts[vts].valid = false; dvd_vts[vts].blocks = 0; dvd_vts[vts].filesize = 0; dvd_vts[vts].vobs = 0; dvd_vts[vts].tracks = 0; dvd_vts[vts].valid_tracks = 0; dvd_vts[vts].invalid_tracks = 0; vts_ifos[vts] = ifoOpen(dvdread_dvd, vts); if(vts_ifos[vts] == NULL) { dvd_vts[vts].valid = false; vts_ifos[vts] = NULL; } else if(!ifo_is_vts(vts_ifos[vts])) { dvd_vts[vts].valid = false; ifoClose(vts_ifos[vts]); vts_ifos[vts] = NULL; } else { dvd_vts[vts].valid = true; } } // Exit if track number requested does not exist if(opt_track_number && (arg_track_number > dvd_info.tracks)) { fprintf(stderr, "dvd_trip: Invalid track number %d\n", arg_track_number); fprintf(stderr, "dvd_trip: Valid track numbers: 1 to %u\n", dvd_info.tracks); ifoClose(vmg_ifo); DVDClose(dvdread_dvd); return 1; } else if(opt_track_number) { dvd_trip.track = arg_track_number; } uint16_t ix = 0; uint16_t track = 1; uint32_t longest_msecs = 0; for(ix = 0, track = 1; ix < dvd_info.tracks; ix++, track++) { vts = dvd_vts_ifo_number(vmg_ifo, ix + 1); vts_ifo = vts_ifos[vts]; dvd_track_info(&dvd_tracks[ix], track, vmg_ifo, vts_ifo); dvd_tracks[ix].valid = true; if(dvd_tracks[ix].msecs > longest_msecs) { dvd_info.longest_track = track; longest_msecs = dvd_tracks[ix].msecs; } } // Set the track number to rip if none is passed as an argument if(!opt_track_number) dvd_trip.track = dvd_info.longest_track; dvd_track = dvd_tracks[dvd_trip.track - 1]; // Set the proper chapter range if(opt_chapter_number) { if(arg_first_chapter > dvd_track.chapters) { dvd_trip.first_chapter = dvd_track.chapters; fprintf(stderr, "Resetting first chapter to %u\n", dvd_trip.first_chapter); } else dvd_trip.first_chapter = arg_first_chapter; if(arg_last_chapter > dvd_track.chapters) { dvd_trip.last_chapter = dvd_track.chapters; fprintf(stderr, "Resetting last chapter to %u\n", dvd_trip.last_chapter); } else dvd_trip.last_chapter = arg_last_chapter; } else { dvd_trip.first_chapter = 1; dvd_trip.last_chapter = dvd_track.chapters; } /** * File descriptors and filenames */ dvd_file_t *dvdread_vts_file = NULL; vts = dvd_vts_ifo_number(vmg_ifo, dvd_trip.track); vts_ifo = vts_ifos[vts]; // Open the VTS VOB dvdread_vts_file = DVDOpenFile(dvdread_dvd, vts, DVD_READ_TITLE_VOBS); printf("Track: %02u, Length: %s, Chapters: %02u, Cells: %02u, Audio streams: %02u, Subpictures: %02u, Filesize: %lu, Blocks: %lu\n", dvd_track.track, dvd_track.length, dvd_track.chapters, dvd_track.cells, dvd_track.audio_tracks, dvd_track.subtitles, dvd_track.filesize, dvd_track.blocks); // Check for track issues dvd_track.valid = true; if(dvd_vts[vts].valid == false) { dvd_track.valid = false; } if(dvd_track.msecs == 0) { printf(" Error: track has zero length\n"); dvd_track.valid = false; } if(dvd_track.chapters == 0) { printf(" Error: track has zero chapters\n"); dvd_track.valid = false; } if(dvd_track.cells == 0) { printf(" Error: track has zero cells\n"); dvd_track.valid = false; } if(dvd_track.valid == false) { printf("Track has been marked as invalid, quitting\n"); DVDCloseFile(dvdread_vts_file); if(vts_ifo) ifoClose(vts_ifo); if(vmg_ifo) ifoClose(vmg_ifo); if(dvdread_dvd) DVDClose(dvdread_dvd); return 1; } // MPV zero-indexes tracks sprintf(dvd_mpv_args, "dvdread://%u", dvd_trip.track - 1); const char *dvd_mpv_commands[] = { "loadfile", dvd_mpv_args, NULL }; // DVD playback using libmpv dvd_mpv = mpv_create(); // Terminal output mpv_set_option_string(dvd_mpv, "terminal", "yes"); if(!debug) mpv_set_option_string(dvd_mpv, "term-osd-bar", "yes"); if (debug) { mpv_request_log_messages(dvd_mpv, "debug"); strcpy(dvd_trip.vcodec_log_level, "full"); } else if(verbose) { mpv_request_log_messages(dvd_mpv, "v"); strcpy(dvd_trip.vcodec_log_level, "info"); } else { mpv_request_log_messages(dvd_mpv, "info"); strcpy(dvd_trip.vcodec_log_level, "info"); } /** Video **/ // Set output frames per second and color spaces based on source (NTSC or PAL) if(dvd_track_pal_video(vts_ifo)) { strcpy(dvd_trip.fps, "25"); strcpy(dvd_trip.color_opts, "color_primaries=bt470bg,color_trc=gamma28,colorspace=bt470bg"); } else { strcpy(dvd_trip.fps, "30000/1001"); strcpy(dvd_trip.color_opts, "color_primaries=smpte170m,color_trc=smpte170m,colorspace=smpte170m"); } /** Containers and Presets **/ // Set preset defaults if(strncmp(dvd_trip.container, "mkv", 3) == 0) { if(!opt_filename) strcpy(dvd_trip.filename, "trip_encode.mkv"); strcpy(dvd_trip.vcodec, "libx265"); strcpy(dvd_trip.vcodec_preset, "medium"); strcpy(dvd_trip.acodec, "libfdk_aac"); if(strncmp(dvd_trip.preset, "low", 3) == 0) { strcpy(dvd_trip.vcodec_preset, "fast"); dvd_trip.crf = 28; } else if(strncmp(dvd_trip.preset, "medium", 6) == 0) { strcpy(dvd_trip.vcodec_preset, "medium"); dvd_trip.crf = 24; } else if(strncmp(dvd_trip.preset, "high", 4) == 0) { strcpy(dvd_trip.vcodec_preset, "slow"); strcpy(dvd_trip.acodec_opts, "b=192k"); dvd_trip.crf = 20; } else if(strncmp(dvd_trip.preset, "insane", 6) == 0) { strcpy(dvd_trip.vcodec_preset, "slower"); strcpy(dvd_trip.acodec_opts, "b=256k"); dvd_trip.crf = 14; } sprintf(dvd_trip.vcodec_opts, "%s,preset=%s,crf=%u,x265-params=log-level=%s", dvd_trip.color_opts, dvd_trip.vcodec_preset, dvd_trip.crf, dvd_trip.vcodec_log_level); } if(strncmp(dvd_trip.container, "mp4", 3) == 0) { if(!opt_filename) strcpy(dvd_trip.filename, "trip_encode.mp4"); strcpy(dvd_trip.vcodec, "libx264"); strcpy(dvd_trip.vcodec_preset, "medium"); strcpy(dvd_trip.acodec, "libfdk_aac"); strcpy(dvd_trip.acodec_opts, ""); if(strncmp(dvd_trip.preset, "low", 3) == 0) { strcpy(dvd_trip.vcodec_preset, "fast"); dvd_trip.crf = 28; } else if(strncmp(dvd_trip.preset, "medium", 6) == 0) { strcpy(dvd_trip.vcodec_preset, "medium"); dvd_trip.crf = 22; } else if(strncmp(dvd_trip.preset, "high", 4) == 0) { strcpy(dvd_trip.vcodec_preset, "slow"); strcpy(dvd_trip.acodec_opts, "b=192k"); dvd_trip.crf = 20; } else if(strncmp(dvd_trip.preset, "insane", 6) == 0) { strcpy(dvd_trip.vcodec_preset, "slower"); strcpy(dvd_trip.acodec_opts, "b=256k"); dvd_trip.crf = 16; } // x264 doesn't allow passing log level (that I can see) sprintf(dvd_trip.vcodec_opts, "%s,preset=%s,crf=%u", dvd_trip.color_opts, dvd_trip.vcodec_preset, dvd_trip.crf); } if(strncmp(dvd_trip.container, "webm", 4) == 0) { if(!opt_filename) strcpy(dvd_trip.filename, "trip_encode.webm"); strcpy(dvd_trip.vcodec, "libvpx-vp9"); strcpy(dvd_trip.acodec, "libopus"); strcpy(dvd_trip.acodec_opts, "application=audio"); if(strncmp(dvd_trip.preset, "low", 3) == 0) { dvd_trip.crf = 34; sprintf(dvd_trip.vcodec_opts, "%s,b=0,crf=%u,keyint_min=0,g=360", dvd_trip.color_opts, dvd_trip.crf); strcpy(dvd_trip.acodec_opts, "application=audio,b=96000"); } if(strncmp(dvd_trip.preset, "medium", 6) == 0) { dvd_trip.crf = 32; sprintf(dvd_trip.vcodec_opts, "%s,b=0,crf=%u,keyint_min=0,g=360", dvd_trip.color_opts, dvd_trip.crf); strcpy(dvd_trip.acodec_opts, "application=audio,b=144000"); } if(strncmp(dvd_trip.preset, "high", 4) == 0) { dvd_trip.crf = 22; sprintf(dvd_trip.vcodec_opts, "%s,b=0,crf=%u,keyint_min=0,g=360", dvd_trip.color_opts, dvd_trip.crf); strcpy(dvd_trip.acodec_opts, "application=audio,b=192000"); } if(strncmp(dvd_trip.preset, "insane", 6) == 0) { dvd_trip.crf = 16; sprintf(dvd_trip.vcodec_opts, "%s,b=0,crf=%u,keyint_min=0,g=360", dvd_trip.color_opts, dvd_trip.crf); strcpy(dvd_trip.acodec_opts, "application=audio,b=256000"); } } mpv_set_option_string(dvd_mpv, "o", dvd_trip.filename); mpv_set_option_string(dvd_mpv, "ovc", dvd_trip.vcodec); mpv_set_option_string(dvd_mpv, "ovcopts", dvd_trip.vcodec_opts); mpv_set_option_string(dvd_mpv, "oac", dvd_trip.acodec); if(strlen(dvd_trip.acodec_opts) > 0) mpv_set_option_string(dvd_mpv, "oacopts", dvd_trip.acodec_opts); mpv_set_option_string(dvd_mpv, "dvd-device", device_filename); mpv_set_option_string(dvd_mpv, "track-auto-selection", "yes"); mpv_set_option_string(dvd_mpv, "input-default-bindings", "yes"); mpv_set_option_string(dvd_mpv, "input-vo-keyboard", "yes"); mpv_set_option_string(dvd_mpv, "resume-playback", "no"); // MPV's chapter range starts at the first one, and ends at the last one plus one // fex: to play chapter 1 only, mpv --start '#1' --end '#2' sprintf(dvd_mpv_first_chapter, "#%u", dvd_trip.first_chapter); sprintf(dvd_mpv_last_chapter, "#%u", dvd_trip.last_chapter + 1); mpv_set_option_string(dvd_mpv, "start", dvd_mpv_first_chapter); mpv_set_option_string(dvd_mpv, "end", dvd_mpv_last_chapter); if(strlen(dvd_trip.audio_aid) > 0) mpv_set_option_string(dvd_mpv, "aid", dvd_trip.audio_aid); else if(strlen(dvd_trip.audio_lang) > 0) mpv_set_option_string(dvd_mpv, "alang", dvd_trip.audio_lang); /** Video Filters **/ if(mpv_client_api_version() <= MPV_MAKE_VERSION(1, 25)) { // Syntax up to 0.27.2 mpv_set_option_string(dvd_mpv, "ofps", dvd_trip.fps); if(dvd_trip.detelecine && dvd_trip.deinterlace) sprintf(dvd_trip.vf_opts, "lavfi=yadif,lavfi=pullup,lavfi=dejudder"); else if(dvd_trip.deinterlace) sprintf(dvd_trip.vf_opts, "lavfi=yadif"); else if(dvd_trip.detelecine) sprintf(dvd_trip.vf_opts, "lavfi=pullup,lavfi=dejudder"); } else { // Syntax starting in 0.29.1 if(dvd_trip.detelecine && dvd_trip.deinterlace) sprintf(dvd_trip.vf_opts, "lavfi-yadif,lavfi-pullup,lavfi-dejudder,fps=%s", dvd_trip.fps); else if(dvd_trip.deinterlace) sprintf(dvd_trip.vf_opts, "lavfi-yadif,fps=%s", dvd_trip.fps); else if(dvd_trip.detelecine) sprintf(dvd_trip.vf_opts, "lavfi-pullup,lavfi-dejudder,fps=%s", dvd_trip.fps); else sprintf(dvd_trip.vf_opts, "fps=%s", dvd_trip.fps); } mpv_set_option_string(dvd_mpv, "vf", dvd_trip.vf_opts); if(dvd_trip.pass == 1) { fprintf(stderr, "dvd_trip [info]: dvd track %u\n", dvd_trip.track); fprintf(stderr, "dvd_trip [info]: chapters %u to %u\n", dvd_trip.first_chapter, dvd_trip.last_chapter); fprintf(stderr, "dvd_trip [info]: saving to %s\n", dvd_trip.filename); fprintf(stderr, "dvd_trip [info]: vcodec %s\n", dvd_trip.vcodec); fprintf(stderr, "dvd_trip [info]: acodec %s\n", dvd_trip.acodec); fprintf(stderr, "dvd_trip [info]: ovcopts %s\n", dvd_trip.vcodec_opts); fprintf(stderr, "dvd_trip [info]: oacopts %s\n", dvd_trip.acodec_opts); if(strlen(dvd_trip.vf_opts)) fprintf(stderr, "dvd_trip [info]: vf %s\n", dvd_trip.vf_opts); fprintf(stderr, "dvd_trip [info]: output fps %s\n", dvd_trip.fps); if(dvd_trip.deinterlace) fprintf(stderr, "dvd_trip [info]: deinterlacing video\n"); if(dvd_trip.detelecine) fprintf(stderr, "dvd_trip [info]: detelecining video\n"); } mpv_initialize(dvd_mpv); mpv_command(dvd_mpv, dvd_mpv_commands); while(true) { dvd_mpv_event = mpv_wait_event(dvd_mpv, -1); if(dvd_mpv_event->event_id == MPV_EVENT_SHUTDOWN || dvd_mpv_event->event_id == MPV_EVENT_END_FILE) break; // Logging output if((verbose || debug) && dvd_mpv_event->event_id == MPV_EVENT_LOG_MESSAGE) { dvd_mpv_log_message = (struct mpv_event_log_message *)dvd_mpv_event->data; printf("mpv [%s]: %s", dvd_mpv_log_message->level, dvd_mpv_log_message->text); } } mpv_terminate_destroy(dvd_mpv); DVDCloseFile(dvdread_vts_file); if(vts_ifo) ifoClose(vts_ifo); if(vmg_ifo) ifoClose(vmg_ifo); if(dvdread_dvd) DVDClose(dvdread_dvd); return 0; }
title_set_info_t * DVDGetFileSet(char *dvd) { /* * TODO Fix close of files if * we error out * We also assume that all * DVD files are of valid * size i.e. file%2048 == 0 */ /* title interation */ int title_sets; int titles; int counter; int i; /* DVD file structures */ dvd_reader_t * _dvd = NULL; ifo_handle_t * vmg_ifo = NULL; ifo_handle_t * vts_ifo = NULL; dvd_file_t * vmg_vob_file = NULL; dvd_file_t * vmg_ifo_file = NULL; dvd_file_t * vts_ifo_file = NULL; dvd_file_t * vts_menu_file = NULL; dvd_file_t * vts_title_file = NULL; /* The sizes it self of each file */ int ifo; int bup; int menu_vob; int title_vob; /* Arrays keeping the title - filset relationship */ int * sector; int * title; int * title_sets_array; int * sector_sets_array; /* DVD Video files */ struct stat fileinfo; char temppoint[PATH_MAX + 1]; /* The Title Set Info struct*/ title_set_info_t * title_set_info; /* Temporary mount point - to be used later */ char mountpoint[PATH_MAX + 1]; strncpy(mountpoint, dvd, sizeof (mountpoint)); mountpoint[sizeof (mountpoint)-1] = '\0'; _dvd = DVDOpen(dvd); if (!_dvd) { #ifdef USE_LIBSCHILY errmsgno(EX_BAD, "Can't open device '%s'\n", dvd); #else fprintf(stderr, "Can't open device\n"); #endif return (0); } vmg_ifo = ifoOpen(_dvd, 0); if (!vmg_ifo) { #ifdef USE_LIBSCHILY errmsgno(EX_BAD, "Can't open VMG info for '%s'.\n", dvd); #else fprintf(stderr, "Can't open VMG info.\n"); #endif return (0); } /* Check mount point */ snprintf(temppoint, sizeof (temppoint), "%s/VIDEO_TS/VIDEO_TS.IFO", mountpoint); if (stat(temppoint, &fileinfo) < 0) { /* If we can't stat the file, give up */ #ifdef USE_LIBSCHILY errmsg("Can't stat %s\n", temppoint); #else fprintf(stderr, "Can't stat %s\n", temppoint); perror(""); #endif return (0); } title_sets = vmg_ifo->vmgi_mat->vmg_nr_of_title_sets; titles = vmg_ifo->tt_srpt->nr_of_srpts; sector = e_malloc(titles * sizeof (int)); memset(sector, 0, titles * sizeof (int)); title = e_malloc(titles * sizeof (int)); title_sets_array = e_malloc(title_sets * sizeof (int)); sector_sets_array = e_malloc(title_sets * sizeof (int)); title_set_info = (title_set_info_t *)e_malloc(sizeof (title_set_info_t)); title_set_info->title_set = (title_set_t *)e_malloc((title_sets + 1) * sizeof (title_set_t)); title_set_info->num_titles = title_sets; /* Fill and sort the arrays for titles*/ if (titles >= 1) { for (counter = 0; counter < titles; counter++) { sector[counter] = vmg_ifo->tt_srpt->title[counter].title_set_sector; title[counter] = counter + 1; } } /* Yes, we should probably do a better sort than B - but what the heck*/ bsort(sector, title, titles); /* * Since title sets and titles are not the same we will need to sort * out "bogus" titles */ uniq(sector, title, title_sets_array, sector_sets_array, titles); /* Open VIDEO_TS.VOB is present */ vmg_vob_file = DVDOpenFile(_dvd, 0, DVD_READ_MENU_VOBS); /* Check VIDEO_TS title set */ vmg_ifo_file = DVDOpenFile(_dvd, 0, DVD_READ_INFO_FILE); if ((vmg_vob_file == 0) && vmg_ifo->vmgi_mat->vmg_last_sector + 1 < 2 * DVDFileSize(vmg_ifo_file)) { #ifdef USE_LIBSCHILY errmsgno(EX_BAD, "IFO is not of correct size aborting\n"); #else fprintf(stderr, "IFO is not of correct size aborting\n"); #endif DVDFreeFileSetArrays(sector, title, title_sets_array, sector_sets_array); DVDFreeFileSet(title_set_info); return (0); } else if ((vmg_vob_file != 0) && (vmg_ifo->vmgi_mat->vmg_last_sector + 1 < 2 * DVDFileSize(vmg_ifo_file) + DVDFileSize(vmg_vob_file))) { #ifdef USE_LIBSCHILY errmsgno(EX_BAD, "Either VIDEO_TS.IFO or VIDEO_TS.VOB is not of correct size"); #else fprintf(stderr, "Either VIDEO_TS.IFO or VIDEO_TS.VOB is not of correct size"); #endif DVDFreeFileSetArrays(sector, title, title_sets_array, sector_sets_array); DVDFreeFileSet(title_set_info); return (0); } /* Find the actuall right size of VIDEO_TS.IFO */ if (vmg_vob_file == 0) { if (vmg_ifo->vmgi_mat->vmg_last_sector + 1 > 2 * DVDFileSize(vmg_ifo_file)) { ifo = vmg_ifo->vmgi_mat->vmg_last_sector - DVDFileSize(vmg_ifo_file) + 1; } else { ifo = vmg_ifo->vmgi_mat->vmgi_last_sector + 1; } } else { if (vmg_ifo->vmgi_mat->vmgi_last_sector + 1 < vmg_ifo->vmgi_mat->vmgm_vobs) { ifo = vmg_ifo->vmgi_mat->vmgm_vobs; } else { ifo = vmg_ifo->vmgi_mat->vmgi_last_sector + 1; } } title_set_info->title_set[0].size_ifo = ifo * 2048; title_set_info->title_set[0].realsize_ifo = fileinfo.st_size; title_set_info->title_set[0].pad_ifo = ifo - DVDFileSize(vmg_ifo_file); /* Find the actuall right size of VIDEO_TS.VOB */ if (vmg_vob_file != 0) { if (ifo + DVDFileSize(vmg_ifo_file) + DVDFileSize(vmg_vob_file) - 1 < vmg_ifo->vmgi_mat->vmg_last_sector) { menu_vob = vmg_ifo->vmgi_mat->vmg_last_sector - ifo - DVDFileSize(vmg_ifo_file) + 1; } else { menu_vob = vmg_ifo->vmgi_mat->vmg_last_sector - ifo - DVDFileSize(vmg_ifo_file) + 1; } snprintf(temppoint, sizeof (temppoint), "%s/VIDEO_TS/VIDEO_TS.VOB", mountpoint); if (stat(temppoint, &fileinfo) < 0) { #ifdef USE_LIBSCHILY errmsg("calc: Can't stat %s\n", temppoint); #else fprintf(stderr, "calc: Can't stat %s\n", temppoint); perror(""); #endif DVDFreeFileSetArrays(sector, title, title_sets_array, sector_sets_array); DVDFreeFileSet(title_set_info); return (0); } title_set_info->title_set[0].realsize_menu = fileinfo.st_size; title_set_info->title_set[0].pad_menu = menu_vob - DVDFileSize(vmg_vob_file); title_set_info->title_set[0].size_menu = menu_vob * 2048; DVDCloseFile(vmg_vob_file); } else { title_set_info->title_set[0].size_menu = 0; title_set_info->title_set[0].realsize_menu = 0; title_set_info->title_set[0].pad_menu = 0; menu_vob = 0; } /* Finding the actuall right size of VIDEO_TS.BUP */ if (title_sets >= 1) { bup = sector_sets_array[0] - menu_vob - ifo; } else { /* Just in case we burn a DVD-Video without any title_sets */ bup = vmg_ifo->vmgi_mat->vmg_last_sector + 1 - menu_vob - ifo; } /* Never trust the BUP file - use a copy of the IFO */ snprintf(temppoint, sizeof (temppoint), "%s/VIDEO_TS/VIDEO_TS.IFO", mountpoint); if (stat(temppoint, &fileinfo) < 0) { #ifdef USE_LIBSCHILY errmsg("calc: Can't stat %s\n", temppoint); #else fprintf(stderr, "calc: Can't stat %s\n", temppoint); perror(""); #endif DVDFreeFileSetArrays(sector, title, title_sets_array, sector_sets_array); DVDFreeFileSet(title_set_info); return (0); } title_set_info->title_set[0].realsize_bup = fileinfo.st_size; title_set_info->title_set[0].size_bup = bup * 2048; title_set_info->title_set[0].pad_bup = bup - DVDFileSize(vmg_ifo_file); /* Take care of the titles which we don't have in VMG */ title_set_info->title_set[0].number_of_vob_files = 0; title_set_info->title_set[0].realsize_vob[0] = 0; title_set_info->title_set[0].pad_title = 0; DVDCloseFile(vmg_ifo_file); if (title_sets >= 1) { for (counter = 0; counter < title_sets; counter++) { vts_ifo = ifoOpen(_dvd, counter + 1); if (!vts_ifo) { #ifdef USE_LIBSCHILY errmsgno(EX_BAD, "Can't open VTS info.\n"); #else fprintf(stderr, "Can't open VTS info.\n"); #endif DVDFreeFileSetArrays(sector, title, title_sets_array, sector_sets_array); DVDFreeFileSet(title_set_info); return (0); } snprintf(temppoint, sizeof (temppoint), "%s/VIDEO_TS/VTS_%02i_0.IFO", mountpoint, counter + 1); if (stat(temppoint, &fileinfo) < 0) { #ifdef USE_LIBSCHILY errmsg("calc: Can't stat %s\n", temppoint); #else fprintf(stderr, "calc: Can't stat %s\n", temppoint); perror(""); #endif DVDFreeFileSetArrays(sector, title, title_sets_array, sector_sets_array); DVDFreeFileSet(title_set_info); return (0); } /* Test if VTS_XX_0.VOB is present */ vts_menu_file = DVDOpenFile(_dvd, counter + 1, DVD_READ_MENU_VOBS); /* Test if VTS_XX_X.VOB are present */ vts_title_file = DVDOpenFile(_dvd, counter + 1, DVD_READ_TITLE_VOBS); /* Check VIDEO_TS.IFO */ vts_ifo_file = DVDOpenFile(_dvd, counter + 1, DVD_READ_INFO_FILE); /* * Checking that title will fit in the * space given by the ifo file */ if (vts_ifo->vtsi_mat->vts_last_sector + 1 < 2 * DVDFileSize(vts_ifo_file)) { #ifdef USE_LIBSCHILY errmsgno(EX_BAD, "IFO is not of correct size aborting.\n"); #else fprintf(stderr, "IFO is not of correct size aborting\n"); #endif DVDFreeFileSetArrays(sector, title, title_sets_array, sector_sets_array); DVDFreeFileSet(title_set_info); return (0); } else if ((vts_title_file != 0) && (vts_menu_file != 0) && (vts_ifo->vtsi_mat->vts_last_sector + 1 < 2 * DVDFileSize(vts_ifo_file) + DVDFileSize(vts_title_file) + DVDFileSize(vts_menu_file))) { #ifdef USE_LIBSCHILY errmsgno(EX_BAD, "Either VIDEO_TS.IFO or VIDEO_TS.VOB is not of correct size.\n"); #else fprintf(stderr, "Either VIDEO_TS.IFO or VIDEO_TS.VOB is not of correct size"); #endif DVDFreeFileSetArrays(sector, title, title_sets_array, sector_sets_array); DVDFreeFileSet(title_set_info); return (0); } else if ((vts_title_file != 0) && (vts_menu_file == 0) && (vts_ifo->vtsi_mat->vts_last_sector + 1 < 2 * DVDFileSize(vts_ifo_file) + DVDFileSize(vts_title_file))) { #ifdef USE_LIBSCHILY errmsgno(EX_BAD, "Either VIDEO_TS.IFO or VIDEO_TS.VOB is not of correct size.\n"); #else fprintf(stderr, "Either VIDEO_TS.IFO or VIDEO_TS.VOB is not of correct size"); #endif DVDFreeFileSetArrays(sector, title, title_sets_array, sector_sets_array); DVDFreeFileSet(title_set_info); return (0); } else if ((vts_menu_file != 0) && (vts_title_file == 0) && (vts_ifo->vtsi_mat->vts_last_sector + 1 < 2 * DVDFileSize(vts_ifo_file) + DVDFileSize(vts_menu_file))) { #ifdef USE_LIBSCHILY errmsgno(EX_BAD, "Either VIDEO_TS.IFO or VIDEO_TS.VOB is not of correct size.\n"); #else fprintf(stderr, "Either VIDEO_TS.IFO or VIDEO_TS.VOB is not of correct size"); #endif DVDFreeFileSetArrays(sector, title, title_sets_array, sector_sets_array); DVDFreeFileSet(title_set_info); return (0); } /* Find the actuall right size of VTS_XX_0.IFO */ if ((vts_title_file == 0) && (vts_menu_file == 0)) { if (vts_ifo->vtsi_mat->vts_last_sector + 1 > 2 * DVDFileSize(vts_ifo_file)) { ifo = vts_ifo->vtsi_mat->vts_last_sector - DVDFileSize(vts_ifo_file) + 1; } else { ifo = vts_ifo->vtsi_mat->vts_last_sector - DVDFileSize(vts_ifo_file) + 1; } } else if (vts_title_file == 0) { if (vts_ifo->vtsi_mat->vtsi_last_sector + 1 < vts_ifo->vtsi_mat->vtstt_vobs) { ifo = vmg_ifo->vtsi_mat->vtstt_vobs; } else { ifo = vmg_ifo->vtsi_mat->vtstt_vobs; } } else { if (vts_ifo->vtsi_mat->vtsi_last_sector + 1 < vts_ifo->vtsi_mat->vtsm_vobs) { ifo = vts_ifo->vtsi_mat->vtsm_vobs; } else { ifo = vts_ifo->vtsi_mat->vtsi_last_sector + 1; } } title_set_info->title_set[counter + 1].size_ifo = ifo * 2048; title_set_info->title_set[counter + 1].realsize_ifo = fileinfo.st_size; title_set_info->title_set[counter + 1].pad_ifo = ifo - DVDFileSize(vts_ifo_file); /* Find the actuall right size of VTS_XX_0.VOB */ if (vts_menu_file != 0) { if (vts_ifo->vtsi_mat->vtsm_vobs == 0) { /* * Apparently start sector 0 means that * VTS_XX_0.VOB is empty after all... */ menu_vob = 0; if (DVDFileSize(vts_menu_file) != 0) { /* * Paranoia: we most likely never * come here... */ #ifdef USE_LIBSCHILY errmsgno(EX_BAD, "%s/VIDEO_TS/VTS_%02i_0.IFO appears to be corrupted.\n", mountpoint, counter+1); #else fprintf(stderr, "%s/VIDEO_TS/VTS_%02i_0.IFO appears to be corrupted.\n", mountpoint, counter+1); #endif return (0); } } else if ((vts_title_file != 0) && (vts_ifo->vtsi_mat->vtstt_vobs - vts_ifo->vtsi_mat->vtsm_vobs > DVDFileSize(vts_menu_file))) { menu_vob = vts_ifo->vtsi_mat->vtstt_vobs - vts_ifo->vtsi_mat->vtsm_vobs; } else if ((vts_title_file == 0) && (vts_ifo->vtsi_mat->vtsm_vobs + DVDFileSize(vts_menu_file) + DVDFileSize(vts_ifo_file) - 1 < vts_ifo->vtsi_mat->vts_last_sector)) { menu_vob = vts_ifo->vtsi_mat->vts_last_sector - DVDFileSize(vts_ifo_file) - vts_ifo->vtsi_mat->vtsm_vobs + 1; } else { menu_vob = vts_ifo->vtsi_mat->vtstt_vobs - vts_ifo->vtsi_mat->vtsm_vobs; } snprintf(temppoint, sizeof (temppoint), "%s/VIDEO_TS/VTS_%02i_0.VOB", mountpoint, counter + 1); if (stat(temppoint, &fileinfo) < 0) { #ifdef USE_LIBSCHILY errmsg("calc: Can't stat %s\n", temppoint); #else fprintf(stderr, "calc: Can't stat %s\n", temppoint); perror(""); #endif DVDFreeFileSetArrays(sector, title, title_sets_array, sector_sets_array); DVDFreeFileSet(title_set_info); return (0); } title_set_info->title_set[counter + 1].realsize_menu = fileinfo.st_size; title_set_info->title_set[counter + 1].size_menu = menu_vob * 2048; title_set_info->title_set[counter + 1].pad_menu = menu_vob - DVDFileSize(vts_menu_file); } else { title_set_info->title_set[counter + 1].size_menu = 0; title_set_info->title_set[counter + 1].realsize_menu = 0; title_set_info->title_set[counter + 1].pad_menu = 0; menu_vob = 0; } /* Find the actuall total size of VTS_XX_[1 to 9].VOB */ if (vts_title_file != 0) { if (ifo + menu_vob + DVDFileSize(vts_ifo_file) - 1 < vts_ifo->vtsi_mat->vts_last_sector) { title_vob = vts_ifo->vtsi_mat->vts_last_sector + 1 - ifo - menu_vob - DVDFileSize(vts_ifo_file); } else { title_vob = vts_ifo->vtsi_mat->vts_last_sector + 1 - ifo - menu_vob - DVDFileSize(vts_ifo_file); } /* * Find out how many vob files * and the size of them */ for (i = 0; i < 9; ++i) { snprintf(temppoint, sizeof (temppoint), "%s/VIDEO_TS/VTS_%02i_%i.VOB", mountpoint, counter + 1, i + 1); if (stat(temppoint, &fileinfo) < 0) { break; } title_set_info->title_set[counter + 1].realsize_vob[i] = fileinfo.st_size; } title_set_info->title_set[counter + 1].number_of_vob_files = i; title_set_info->title_set[counter + 1].size_title = title_vob * 2048; title_set_info->title_set[counter + 1].pad_title = title_vob - DVDFileSize(vts_title_file); } else { title_set_info->title_set[counter + 1].number_of_vob_files = 0; title_set_info->title_set[counter + 1].realsize_vob[0] = 0; title_set_info->title_set[counter + 1].size_title = 0; title_set_info->title_set[counter + 1].pad_title = 0; title_vob = 0; } /* Find the actuall total size of VTS_XX_0.BUP */ if (title_sets - 1 > counter) { bup = sector_sets_array[counter+1] - sector_sets_array[counter] - title_vob - menu_vob - ifo; } else { bup = vts_ifo->vtsi_mat->vts_last_sector + 1 - title_vob - menu_vob - ifo; } /* Never trust the BUP use a copy of the IFO */ snprintf(temppoint, sizeof (temppoint), "%s/VIDEO_TS/VTS_%02i_0.IFO", mountpoint, counter + 1); if (stat(temppoint, &fileinfo) < 0) { #ifdef USE_LIBSCHILY errmsg("calc: Can't stat %s\n", temppoint); #else fprintf(stderr, "calc: Can't stat %s\n", temppoint); perror(""); #endif DVDFreeFileSetArrays(sector, title, title_sets_array, sector_sets_array); DVDFreeFileSet(title_set_info); return (0); } title_set_info->title_set[counter + 1].size_bup = bup * 2048; title_set_info->title_set[counter + 1].realsize_bup = fileinfo.st_size; title_set_info->title_set[counter + 1].pad_bup = bup - DVDFileSize(vts_ifo_file); /* Closing files */ if (vts_menu_file != 0) { DVDCloseFile(vts_menu_file); } if (vts_title_file != 0) { DVDCloseFile(vts_title_file); } if (vts_ifo_file != 0) { DVDCloseFile(vts_ifo_file); } ifoClose(vts_ifo); } } DVDFreeFileSetArrays(sector, title, title_sets_array, sector_sets_array); /* Close the VMG ifo file we got all the info we need */ ifoClose(vmg_ifo); /* Close the DVD */ DVDClose(_dvd); /* Return the actuall info*/ return (title_set_info); }
int main(int argc, char** argv) { // a lot of this was modified from dvd_reader.c // which is part of the transcode package (copyright 2001-2003 Thomas Oestreich, 2003-2004 T. Bitterberg, 2004-2008 Transcode Team) dvd_reader_t* dvd; ifo_handle_t* ifoHandle; ifo_handle_t* titleIfoHandle; tt_srpt_t* tt_srpt; pgc_t* cur_pgc; dvd_time_t* time; int numtitles, title, pgc_id, ttn, playtime, i; if(argc < 3) { printf("Usage: %s [DVD DEVICE] [TITLE NUMBER]\n\tEg. %s /dev/dvd 1\t// get duration for title 1\n", argv[0], argv[0]); return 1; } dvd = DVDOpen(argv[1]); if(!dvd) { printf("Error, can't open dvd!\n\n"); return 2; } ifoHandle = ifoOpen(dvd, 0); if(!ifoHandle) { printf("Error, can't open vmg!\n\n"); return 3; } tt_srpt = ifoHandle->tt_srpt; numtitles = tt_srpt->nr_of_srpts; int retval = sscanf(argv[2], "%d", &title); title--; if(title < 0 || title >= numtitles) { printf("Invalid title specified"); ifoClose(ifoHandle); return 4; } titleIfoHandle = ifoOpen(dvd, tt_srpt->title[title].title_set_nr); if(!titleIfoHandle) { printf("Can't open title info!"); ifoClose(ifoHandle); return 5; } ttn = tt_srpt->title[title].vts_ttn; pgc_id = titleIfoHandle->vts_ptt_srpt->title[ ttn - 1 ].ptt[0].pgcn; cur_pgc = titleIfoHandle->vts_pgcit->pgci_srp[ pgc_id - 1 ].pgc; time = &(cur_pgc->playback_time); i=time->hour>>4; playtime = (i*10 + time->hour-(i<<4))*60*60; i=(time->minute>>4); playtime +=(i*10 + time->minute-(i<<4))*60; i=(time->second>>4); playtime +=i*10 + time->second-(i<<4); playtime++; printf("%d\n", playtime); ifoClose(titleIfoHandle); ifoClose(ifoHandle); return 0; }
static ifo_handle_t * ifoReadVTSI(int file, ifo_handle_t *ifofile) { off_t offset; UInt32_t sector; vtsi_mat_t * vtsi_mat; /* Make the VMG part NULL */ ifofile->vmgi_mat = NULL; ifofile->tt_srpt = NULL; vtsi_mat = (vtsi_mat_t *)e_malloc(sizeof (vtsi_mat_t)); if (!vtsi_mat) { /* fprintf(stderr, "Memmory allocation error\n");*/ free(ifofile); return (0); } ifofile->vtsi_mat = vtsi_mat; /* Last sector of VTS i.e. last sector of BUP */ offset = 12; if (lseek(file, offset, SEEK_SET) != offset) { #ifdef USE_LIBSCHILY errmsg(MSGESEEK); #else printf(stderr, MSGESEEK); #endif ifoClose(ifofile); return (0); } if (read(file, §or, sizeof (sector)) != sizeof (sector)) { #ifdef USE_LIBSCHILY errmsg(MSGEREAD); #else printf(stderr, MSGEREAD); #endif ifoClose(ifofile); return (0); } B2N_32(sector); vtsi_mat->vts_last_sector = sector; /* Last sector of IFO */ offset = 28; if (lseek(file, offset, SEEK_SET) != offset) { #ifdef USE_LIBSCHILY errmsg(MSGESEEK); #else printf(stderr, MSGESEEK); #endif ifoClose(ifofile); return (0); } if (read(file, §or, sizeof (sector)) != sizeof (sector)) { #ifdef USE_LIBSCHILY errmsg(MSGEREAD); #else printf(stderr, MSGEREAD); #endif ifoClose(ifofile); return (0); } B2N_32(sector); vtsi_mat->vtsi_last_sector = sector; /* Star sector of VTS Menu VOB */ offset = 192; if (lseek(file, offset, SEEK_SET) != offset) { #ifdef USE_LIBSCHILY errmsg(MSGESEEK); #else printf(stderr, MSGESEEK); #endif ifoClose(ifofile); return (0); } if (read(file, §or, sizeof (sector)) != sizeof (sector)) { #ifdef USE_LIBSCHILY errmsg(MSGEREAD); #else printf(stderr, MSGEREAD); #endif ifoClose(ifofile); return (0); } B2N_32(sector); vtsi_mat->vtsm_vobs = sector; /* Start sector of VTS Title VOB */ offset = 196; if (lseek(file, offset, SEEK_SET) != offset) { #ifdef USE_LIBSCHILY errmsg(MSGESEEK); #else printf(stderr, MSGESEEK); #endif ifoClose(ifofile); return (0); } if (read(file, §or, sizeof (sector)) != sizeof (sector)) { #ifdef USE_LIBSCHILY errmsg(MSGEREAD); #else printf(stderr, MSGEREAD); #endif ifoClose(ifofile); return (0); } B2N_32(sector); vtsi_mat->vtstt_vobs = sector; return (ifofile); }
static ifo_handle_t * ifoReadVGMI(int file, ifo_handle_t *ifofile) { off_t offset; Uint counter; UInt32_t sector; UInt16_t titles; vmgi_mat_t *vmgi_mat; tt_srpt_t *tt_srpt; /* Make the VTS part null */ ifofile->vtsi_mat = NULL; vmgi_mat = (vmgi_mat_t *)e_malloc(sizeof (vmgi_mat_t)); if (!vmgi_mat) { /* fprintf(stderr, "Memmory allocation error\n");*/ free(ifofile); return (0); } ifofile->vmgi_mat = vmgi_mat; /* Last sector of VMG i.e. last sector of BUP */ offset = 12; if (lseek(file, offset, SEEK_SET) != offset) { #ifdef USE_LIBSCHILY errmsg(MSGESEEK); #else printf(stderr, MSGESEEK); #endif ifoClose(ifofile); return (0); } if (read(file, §or, sizeof (sector)) != sizeof (sector)) { #ifdef USE_LIBSCHILY errmsg(MSGEREAD); #else printf(stderr, MSGEREAD); #endif ifoClose(ifofile); return (0); } B2N_32(sector); vmgi_mat->vmg_last_sector = sector; /* Last sector of IFO */ offset = 28; if (lseek(file, offset, SEEK_SET) != offset) { #ifdef USE_LIBSCHILY errmsg(MSGESEEK); #else printf(stderr, MSGESEEK); #endif ifoClose(ifofile); return (0); } if (read(file, §or, sizeof (sector)) != sizeof (sector)) { #ifdef USE_LIBSCHILY errmsg(MSGEREAD); #else printf(stderr, MSGEREAD); #endif ifoClose(ifofile); return (0); } B2N_32(sector); vmgi_mat->vmgi_last_sector = sector; /* Number of VTS i.e. title sets */ offset = 62; if (lseek(file, offset, SEEK_SET) != offset) { #ifdef USE_LIBSCHILY errmsg(MSGESEEK); #else printf(stderr, MSGESEEK); #endif ifoClose(ifofile); return (0); } if (read(file, &titles, sizeof (titles)) != sizeof (titles)) { #ifdef USE_LIBSCHILY errmsg(MSGEREAD); #else printf(stderr, MSGEREAD); #endif ifoClose(ifofile); return (0); } B2N_16(titles); vmgi_mat->vmg_nr_of_title_sets = titles; /* Star sector of VMG Menu VOB */ offset = 192; if (lseek(file, offset, SEEK_SET) != offset) { #ifdef USE_LIBSCHILY errmsg(MSGESEEK); #else printf(stderr, MSGESEEK); #endif ifoClose(ifofile); return (0); } if (read(file, §or, sizeof (sector)) != sizeof (sector)) { #ifdef USE_LIBSCHILY errmsg(MSGEREAD); #else printf(stderr, MSGEREAD); #endif ifoClose(ifofile); return (0); } B2N_32(sector); vmgi_mat->vmgm_vobs = sector; /* Sector offset to TT_SRPT */ offset = 196; if (lseek(file, offset, SEEK_SET) != offset) { #ifdef USE_LIBSCHILY errmsg(MSGESEEK); #else printf(stderr, MSGESEEK); #endif ifoClose(ifofile); return (0); } if (read(file, §or, sizeof (sector)) != sizeof (sector)) { #ifdef USE_LIBSCHILY errmsg(MSGEREAD); #else printf(stderr, MSGEREAD); #endif ifoClose(ifofile); return (0); } B2N_32(sector); vmgi_mat->tt_srpt = sector; tt_srpt = (tt_srpt_t *)e_malloc(sizeof (tt_srpt_t)); if (!tt_srpt) { /* fprintf(stderr, "Memmory allocation error\n");*/ ifoClose(ifofile); return (0); } ifofile->tt_srpt = tt_srpt; /* Number of titles in TT_SRPT */ offset = 2048 * vmgi_mat->tt_srpt; if (lseek(file, offset, SEEK_SET) != offset) { #ifdef USE_LIBSCHILY errmsg(MSGESEEK); #else printf(stderr, MSGESEEK); #endif return (0); } if (read(file, &titles, sizeof (titles)) != sizeof (titles)) { #ifdef USE_LIBSCHILY errmsg(MSGEREAD); #else printf(stderr, MSGEREAD); #endif return (0); } B2N_16(titles); tt_srpt->nr_of_srpts = titles; tt_srpt->title = (title_info_t *)e_malloc(sizeof (title_info_t) * tt_srpt->nr_of_srpts); if (!tt_srpt->title) { /* fprintf(stderr, "Memmory allocation error\n");*/ ifoClose(ifofile); return (0); } /* Start sector of each title in TT_SRPT */ for (counter = 0; counter < tt_srpt->nr_of_srpts; counter++) { offset = (2048 * vmgi_mat->tt_srpt) + 8 + (counter * 12) + 8; if (lseek(file, offset, SEEK_SET) != offset) { #ifdef USE_LIBSCHILY errmsg(MSGESEEK); #else printf(stderr, MSGESEEK); #endif ifoClose(ifofile); return (0); } if (read(file, §or, sizeof (sector)) != sizeof (sector)) { #ifdef USE_LIBSCHILY errmsg(MSGEREAD); #else printf(stderr, MSGEREAD); #endif ifoClose(ifofile); return (0); } B2N_32(sector); tt_srpt->title[counter].title_set_sector = sector; } return (ifofile); }
static int open_s(stream_t *stream) { int k; dvd_priv_t *d = stream->priv; d->dvd_angle = stream->opts->dvd_angle; MP_VERBOSE(stream, "URL: %s\n", stream->url); d->dvd_title = d->cfg_title + 1; if(1){ //int ret,ret2; int ttn,pgc_id,pgn; dvd_reader_t *dvd; dvd_file_t *title; ifo_handle_t *vmg_file; tt_srpt_t *tt_srpt; ifo_handle_t *vts_file; pgc_t *pgc; /** * Open the disc. */ if(d->cfg_device && d->cfg_device[0]) d->dvd_device_current = d->cfg_device; else if(stream->opts->dvd_device && stream->opts->dvd_device[0]) d->dvd_device_current = talloc_strdup(stream, stream->opts->dvd_device); else d->dvd_device_current = DEFAULT_DVD_DEVICE; d->dvd_speed = stream->opts->dvd_speed; dvd_set_speed(stream,d->dvd_device_current, d->dvd_speed); #if defined(__APPLE__) || defined(__DARWIN__) /* Dynamic DVD drive selection on Darwin */ if(!strcmp(d->dvd_device_current, "/dev/rdiskN")) { int i; size_t len = strlen(d->dvd_device_current)+1; char *temp_device = malloc(len); for (i = 1; i < 10; i++) { snprintf(temp_device, len, "/dev/rdisk%d", i); dvd = DVDOpen(temp_device); if(!dvd) { MP_ERR(stream, "Couldn't open DVD device: %s (%s)\n",temp_device, strerror(errno)); } else { #if DVDREAD_VERSION <= LIBDVDREAD_VERSION(0,9,4) dvd_file_t *dvdfile = DVDOpenFile(dvd,d->dvd_title,DVD_READ_INFO_FILE); if(!dvdfile) { MP_ERR(stream, "Couldn't open DVD device: %s (%s)\n",temp_device, strerror(errno)); DVDClose(dvd); continue; } DVDCloseFile(dvdfile); #endif break; } } free(temp_device); if(!dvd) { return STREAM_UNSUPPORTED; } } else #endif /* defined(__APPLE__) || defined(__DARWIN__) */ { dvd = DVDOpen(d->dvd_device_current); if(!dvd) { MP_ERR(stream, "Couldn't open DVD device: %s (%s)\n",d->dvd_device_current, strerror(errno)); return STREAM_UNSUPPORTED; } } MP_VERBOSE(stream, "Reading disc structure, please wait...\n"); /** * Load the video manager to find out the information about the titles on * this disc. */ vmg_file = ifoOpen(dvd, 0); if(!vmg_file) { MP_ERR(stream, "Can't open VMG info!\n"); DVDClose( dvd ); return STREAM_UNSUPPORTED; } tt_srpt = vmg_file->tt_srpt; /** * Make sure our title number is valid. */ MP_INFO(stream, "There are %d titles on this DVD.\n", tt_srpt->nr_of_srpts ); if(d->dvd_title < 1 || d->dvd_title > tt_srpt->nr_of_srpts) { MP_ERR(stream, "Invalid DVD title number: %d\n", d->dvd_title); ifoClose( vmg_file ); DVDClose( dvd ); return STREAM_UNSUPPORTED; } --(d->dvd_title); // remap 1.. -> 0.. /** * Make sure the angle number is valid for this title. */ MP_INFO(stream, "There are %d angles in this DVD title.\n", tt_srpt->title[d->dvd_title].nr_of_angles); if(d->dvd_angle<1 || d->dvd_angle>tt_srpt->title[d->dvd_title].nr_of_angles) { MP_ERR(stream, "Invalid DVD angle number: %d\n", d->dvd_angle); goto fail; } ttn = tt_srpt->title[d->dvd_title].vts_ttn - 1; /** * Load the VTS information for the title set our title is in. */ vts_file = ifoOpen( dvd, tt_srpt->title[d->dvd_title].title_set_nr ); if(!vts_file) { MP_ERR(stream, "Cannot open the IFO file for DVD title %d.\n", tt_srpt->title[d->dvd_title].title_set_nr ); goto fail; } /** * We've got enough info, time to open the title set data. */ title = DVDOpenFile(dvd, tt_srpt->title[d->dvd_title].title_set_nr, DVD_READ_TITLE_VOBS); if(!title) { MP_ERR(stream, "Cannot open title VOBS (VTS_%02d_1.VOB).\n", tt_srpt->title[d->dvd_title].title_set_nr); ifoClose( vts_file ); goto fail; } MP_VERBOSE(stream, "DVD successfully opened.\n"); // store data d->dvd=dvd; d->title=title; d->vmg_file=vmg_file; d->tt_srpt=tt_srpt; d->vts_file=vts_file; d->cur_title = d->dvd_title; pgc = vts_file->vts_pgcit ? vts_file->vts_pgcit->pgci_srp[ttn].pgc : NULL; /** * Check number of audio channels and types */ { d->nr_of_channels=0; if(vts_file->vts_pgcit) { int i; for(i=0;i<8;i++) if(pgc->audio_control[i] & 0x8000) { audio_attr_t * audio = &vts_file->vtsi_mat->vts_audio_attr[i]; int language = 0; char tmp[] = "unknown"; stream_language_t *audio_stream = &d->audio_streams[d->nr_of_channels]; if(audio->lang_type == 1) { language=audio->lang_code; tmp[0]=language>>8; tmp[1]=language&0xff; tmp[2]=0; } audio_stream->language=language; audio_stream->id=pgc->audio_control[i] >> 8 & 7; switch(audio->audio_format) { case 0: // ac3 audio_stream->id+=FIRST_AC3_AID; break; case 6: // dts audio_stream->id+=FIRST_DTS_AID; break; case 2: // mpeg layer 1/2/3 case 3: // mpeg2 ext audio_stream->id+=FIRST_MPG_AID; break; case 4: // lpcm audio_stream->id+=FIRST_PCM_AID; break; } audio_stream->type=audio->audio_format; // Pontscho: to my mind, tha channels: // 1 - stereo // 5 - 5.1 audio_stream->channels=audio->channels; MP_INFO(stream, "audio stream: %d format: %s (%s) language: %s aid: %d.\n", d->nr_of_channels, dvd_audio_stream_types[ audio->audio_format ], dvd_audio_stream_channels[ audio->channels ], tmp, audio_stream->id ); d->nr_of_channels++; }
static int open_s(stream_t *stream,int mode, void* opts, int* file_format) { struct stream_priv_s* p = (struct stream_priv_s*)opts; int k; mp_msg(MSGT_OPEN,MSGL_V,"URL: %s\n", stream->url); dvd_title = p->title; if(1){ //int ret,ret2; dvd_priv_t *d; int ttn,pgc_id,pgn; dvd_reader_t *dvd; dvd_file_t *title; ifo_handle_t *vmg_file; tt_srpt_t *tt_srpt; ifo_handle_t *vts_file; pgc_t *pgc; /** * Open the disc. */ if(p->device) dvd_device_current = p->device; else if(dvd_device) dvd_device_current = dvd_device; else dvd_device_current = DEFAULT_DVD_DEVICE; dvd_set_speed(dvd_device_current, dvd_speed); #if defined(__APPLE__) || defined(__DARWIN__) /* Dynamic DVD drive selection on Darwin */ if(!strcmp(dvd_device_current, "/dev/rdiskN")) { int i; size_t len = strlen(dvd_device_current)+1; char *temp_device = malloc(len); for (i = 1; i < 10; i++) { snprintf(temp_device, len, "/dev/rdisk%d", i); dvd = DVDOpen(temp_device); if(!dvd) { mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_CantOpenDVD,temp_device, strerror(errno)); } else { #if DVDREAD_VERSION <= LIBDVDREAD_VERSION(0,9,4) dvd_file_t *dvdfile = DVDOpenFile(dvd,dvd_title,DVD_READ_INFO_FILE); if(!dvdfile) { mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_CantOpenDVD,temp_device, strerror(errno)); DVDClose(dvd); continue; } DVDCloseFile(dvdfile); #endif break; } } free(temp_device); if(!dvd) { m_struct_free(&stream_opts,opts); return STREAM_UNSUPPORTED; } } else #endif /* defined(__APPLE__) || defined(__DARWIN__) */ { dvd = DVDOpen(dvd_device_current); if(!dvd) { mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_CantOpenDVD,dvd_device_current, strerror(errno)); m_struct_free(&stream_opts,opts); return STREAM_UNSUPPORTED; } } mp_msg(MSGT_OPEN,MSGL_V,"Reading disc structure, please wait...\n"); /** * Load the video manager to find out the information about the titles on * this disc. */ vmg_file = ifoOpen(dvd, 0); if(!vmg_file) { mp_msg(MSGT_OPEN,MSGL_ERR, MSGTR_DVDnoVMG); DVDClose( dvd ); m_struct_free(&stream_opts,opts); return STREAM_UNSUPPORTED; } tt_srpt = vmg_file->tt_srpt; if (mp_msg_test(MSGT_IDENTIFY, MSGL_INFO)) { int title_no; ///< title number mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_DVD_TITLES=%d\n", tt_srpt->nr_of_srpts); for (title_no = 0; title_no < tt_srpt->nr_of_srpts; title_no++) { mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_DVD_TITLE_%d_CHAPTERS=%d\n", title_no + 1, tt_srpt->title[title_no].nr_of_ptts); mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_DVD_TITLE_%d_ANGLES=%d\n", title_no + 1, tt_srpt->title[title_no].nr_of_angles); } } if (mp_msg_test(MSGT_IDENTIFY, MSGL_V)) { char volid[32]; unsigned char discid [16]; ///< disk ID, a 128 bit MD5 sum int vts_no; ///< video title set number for (vts_no = 1; vts_no <= vmg_file->vts_atrt->nr_of_vtss; vts_no++) mp_describe_titleset(dvd, tt_srpt, vts_no); if (DVDDiscID(dvd, discid) >= 0) { int i; mp_msg(MSGT_IDENTIFY, MSGL_V, "ID_DVD_DISC_ID="); for (i = 0; i < 16; i ++) mp_msg(MSGT_IDENTIFY, MSGL_V, "%02X", discid[i]); mp_msg(MSGT_IDENTIFY, MSGL_V, "\n"); } if (DVDUDFVolumeInfo(dvd, volid, sizeof(volid), NULL, 0) >= 0 || DVDISOVolumeInfo(dvd, volid, sizeof(volid), NULL, 0) >= 0) mp_msg(MSGT_IDENTIFY, MSGL_V, "ID_DVD_VOLUME_ID=%s\n", volid); } /** * Make sure our title number is valid. */ mp_msg(MSGT_OPEN,MSGL_STATUS, MSGTR_DVDnumTitles, tt_srpt->nr_of_srpts ); if(dvd_title < 1 || dvd_title > tt_srpt->nr_of_srpts) { mp_msg(MSGT_OPEN,MSGL_ERR, MSGTR_DVDinvalidTitle, dvd_title); ifoClose( vmg_file ); DVDClose( dvd ); m_struct_free(&stream_opts,opts); return STREAM_UNSUPPORTED; } mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_DVD_CURRENT_TITLE=%d\n", dvd_title); --dvd_title; // remap 1.. -> 0.. /** * Make sure the angle number is valid for this title. */ mp_msg(MSGT_OPEN,MSGL_STATUS, MSGTR_DVDnumAngles, tt_srpt->title[dvd_title].nr_of_angles); if(dvd_angle<1 || dvd_angle>tt_srpt->title[dvd_title].nr_of_angles) { mp_msg(MSGT_OPEN,MSGL_ERR, MSGTR_DVDinvalidAngle, dvd_angle); goto fail; } --dvd_angle; // remap 1.. -> 0.. ttn = tt_srpt->title[dvd_title].vts_ttn - 1; /** * Load the VTS information for the title set our title is in. */ vts_file = ifoOpen( dvd, tt_srpt->title[dvd_title].title_set_nr ); if(!vts_file) { mp_msg(MSGT_OPEN,MSGL_ERR, MSGTR_DVDnoIFO, tt_srpt->title[dvd_title].title_set_nr ); goto fail; } /** * We've got enough info, time to open the title set data. */ title = DVDOpenFile(dvd, tt_srpt->title[dvd_title].title_set_nr, DVD_READ_TITLE_VOBS); if(!title) { mp_msg(MSGT_OPEN,MSGL_ERR, MSGTR_DVDnoVOBs, tt_srpt->title[dvd_title].title_set_nr); ifoClose( vts_file ); goto fail; } mp_msg(MSGT_OPEN,MSGL_V, "DVD successfully opened.\n"); // store data d=malloc(sizeof(dvd_priv_t)); memset(d,0,sizeof(dvd_priv_t)); d->dvd=dvd; d->title=title; d->vmg_file=vmg_file; d->tt_srpt=tt_srpt; d->vts_file=vts_file; d->cur_title = dvd_title+1; pgc = vts_file->vts_pgcit ? vts_file->vts_pgcit->pgci_srp[ttn].pgc : NULL; /** * Check number of audio channels and types */ { d->nr_of_channels=0; if(vts_file->vts_pgcit) { int i; for(i=0;i<8;i++) if(pgc->audio_control[i] & 0x8000) { audio_attr_t * audio = &vts_file->vtsi_mat->vts_audio_attr[i]; int language = 0; char tmp[] = "unknown"; stream_language_t *audio_stream = &d->audio_streams[d->nr_of_channels]; if(audio->lang_type == 1) { language=audio->lang_code; tmp[0]=language>>8; tmp[1]=language&0xff; tmp[2]=0; } audio_stream->language=language; audio_stream->id=pgc->audio_control[i] >> 8 & 7; switch(audio->audio_format) { case 0: // ac3 audio_stream->id+=FIRST_AC3_AID; break; case 6: // dts audio_stream->id+=FIRST_DTS_AID; break; case 2: // mpeg layer 1/2/3 case 3: // mpeg2 ext audio_stream->id+=FIRST_MPG_AID; break; case 4: // lpcm audio_stream->id+=FIRST_PCM_AID; break; } audio_stream->type=audio->audio_format; // Pontscho: to my mind, tha channels: // 1 - stereo // 5 - 5.1 audio_stream->channels=audio->channels; mp_msg(MSGT_OPEN,MSGL_STATUS,MSGTR_DVDaudioStreamInfo, d->nr_of_channels, dvd_audio_stream_types[ audio->audio_format ], dvd_audio_stream_channels[ audio->channels ], tmp, audio_stream->id ); mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_ID=%d\n", audio_stream->id); if(language && tmp[0]) mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AID_%d_LANG=%s\n", audio_stream->id, tmp); d->nr_of_channels++; }
int main(int argc, char **argv) { /** * Parse options */ bool verbose = false; bool debug = false; bool opt_track_number = false; bool opt_chapter_number = false; bool opt_widescreen = false; bool opt_pan_scan = false; bool opt_no_video = false; bool opt_no_audio = false; uint16_t arg_track_number = 0; uint8_t arg_first_chapter = 1; uint8_t arg_last_chapter = 99; struct dvd_player dvd_player; struct dvd_playback dvd_playback; char dvd_mpv_args[13] = {'\0'}; mpv_handle *dvd_mpv = NULL; mpv_event *dvd_mpv_event = NULL; struct mpv_event_log_message *dvd_mpv_log_message = NULL; const char *home_dir = getenv("HOME"); const char *lang = getenv("LANG"); // Video Title Set struct dvd_vts dvd_vts[99]; // DVD player default options snprintf(dvd_player.config_dir, 20, "/.config/dvd_player"); memset(dvd_player.mpv_config_dir, '\0', sizeof(dvd_player.mpv_config_dir)); if(home_dir != NULL) snprintf(dvd_player.mpv_config_dir, PATH_MAX - 1, "%s%s", home_dir, dvd_player.config_dir); // DVD playback default options dvd_playback.track = 1; dvd_playback.first_chapter = 1; dvd_playback.last_chapter = 99; dvd_playback.fullscreen = false; dvd_playback.deinterlace = false; dvd_playback.subtitles = false; snprintf(dvd_playback.mpv_chapters_range, 8, "%u-%u", 1, 99); memset(dvd_playback.audio_lang, '\0', sizeof(dvd_playback.audio_lang)); if(strlen(lang) >= 2) snprintf(dvd_playback.audio_lang, 3, "%s", strndup(lang, 2)); memset(dvd_playback.audio_aid, '\0', sizeof(dvd_playback.audio_aid)); memset(dvd_playback.subtitles_lang, '\0', sizeof(dvd_playback.subtitles_lang)); if(strlen(lang) >= 2) snprintf(dvd_playback.subtitles_lang, 3, "%s", strndup(lang, 2)); memset(dvd_playback.subtitles_sid, '\0', sizeof(dvd_playback.subtitles_sid)); const char str_options[] = "Aa:c:dfhpSs:t:Vvwz"; struct option long_options[] = { { "track", required_argument, 0, 't' }, { "chapters", required_argument, 0, 'c' }, { "fullscreen", no_argument, 0, 'f' }, { "deinterlace", no_argument, 0, 'd' }, { "alang", required_argument, 0, 'a' }, { "slang", required_argument, 0, 's' }, { "aid", required_argument, 0, 'A' }, { "sid", required_argument, 0, 'S' }, { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'V' }, { "widescreen", no_argument, 0, 'w' }, { "pan-scan", no_argument, 0, 'p' }, { "verbose", no_argument, 0, 'v' }, { "debug", no_argument, 0, 'z' }, { 0, 0, 0, 0 } }; int long_index = 0; int opt = 0; opterr = 1; char *token = NULL; while((opt = getopt_long(argc, argv, str_options, long_options, &long_index )) != -1) { switch(opt) { case 'A': strncpy(dvd_playback.audio_aid, optarg, 3); break; case 'a': strncpy(dvd_playback.audio_lang, optarg, 2); break; case 'c': opt_chapter_number = true; token = strtok(optarg, "-"); { if(strlen(token) > 2) { fprintf(stderr, "Chapter range must be between 1 and 99\n"); return 1; } arg_first_chapter = (uint8_t)strtoumax(token, NULL, 0); } token = strtok(NULL, "-"); if(token != NULL) { if(strlen(token) > 2) { fprintf(stderr, "Chapter range must be between 1 and 99\n"); return 1; } arg_last_chapter = (uint8_t)strtoumax(token, NULL, 0); } if(arg_first_chapter == 0) arg_first_chapter = 1; if(arg_last_chapter < arg_first_chapter) arg_last_chapter = arg_first_chapter; if(arg_first_chapter > arg_last_chapter) arg_first_chapter = arg_last_chapter; break; case 'd': dvd_playback.deinterlace = true; break; case 'f': dvd_playback.fullscreen = true; break; case 'h': print_usage(DVD_INFO_PROGRAM); return 0; case 'p': opt_pan_scan = true; break; case 's': strncpy(dvd_playback.subtitles_lang, optarg, 2); dvd_playback.subtitles = true; break; case 'S': strncpy(dvd_playback.subtitles_sid, optarg, 3); dvd_playback.subtitles = true; break; case 't': opt_track_number = true; arg_track_number = (uint16_t)strtoumax(optarg, NULL, 0); break; case 'V': print_version("dvd_player"); return 0; case 'v': verbose = true; break; case 'w': opt_widescreen = true; break; case 'z': verbose = true; debug = true; break; // ignore unknown arguments case '?': print_usage("dvd_player"); return 1; // let getopt_long set the variable case 0: default: break; } } if(opt_pan_scan && opt_widescreen) opt_pan_scan = false; const char *device_filename = DEFAULT_DVD_DEVICE; if (argv[optind]) device_filename = argv[optind]; if(access(device_filename, F_OK) != 0) { fprintf(stderr, "cannot access %s\n", device_filename); return 1; } // Check to see if device can be opened int dvd_fd = 0; dvd_fd = dvd_device_open(device_filename); if(dvd_fd < 0) { fprintf(stderr, "dvd_player: error opening %s\n", device_filename); return 1; } dvd_device_close(dvd_fd); #ifdef __linux__ // Poll drive status if it is hardware if(dvd_device_is_hardware(device_filename)) { // Wait for the drive to become ready if(!dvd_drive_has_media(device_filename)) { fprintf(stderr, "drive status: "); dvd_drive_display_status(device_filename); return 1; } } #endif dvd_reader_t *dvdread_dvd = NULL; dvdread_dvd = DVDOpen(device_filename); if(!dvdread_dvd) { fprintf(stderr, "* dvdread could not open %s\n", device_filename); return 1; } ifo_handle_t *vmg_ifo = NULL; vmg_ifo = ifoOpen(dvdread_dvd, 0); if(vmg_ifo == NULL) { fprintf(stderr, "* Could not open IFO zero\n"); DVDClose(dvdread_dvd); return 1; } // DVD struct dvd_info dvd_info; memset(dvd_info.dvdread_id, '\0', sizeof(dvd_info.dvdread_id)); dvd_info.video_title_sets = dvd_video_title_sets(vmg_ifo); dvd_info.side = 1; memset(dvd_info.title, '\0', sizeof(dvd_info.title)); memset(dvd_info.provider_id, '\0', sizeof(dvd_info.provider_id)); memset(dvd_info.vmg_id, '\0', sizeof(dvd_info.vmg_id)); dvd_info.tracks = dvd_tracks(vmg_ifo); dvd_info.longest_track = 1; dvd_title(dvd_info.title, device_filename); printf("Disc title: %s\n", dvd_info.title); uint16_t num_ifos = 1; num_ifos = vmg_ifo->vts_atrt->nr_of_vtss; if(num_ifos < 1) { fprintf(stderr, "* DVD has no title IFOs?!\n"); fprintf(stderr, "* Most likely a bug in libdvdread or a bad master or problems reading the disc\n"); ifoClose(vmg_ifo); DVDClose(dvdread_dvd); return 1; } // Track struct dvd_track dvd_track; memset(&dvd_track, 0, sizeof(dvd_track)); struct dvd_track dvd_tracks[DVD_MAX_TRACKS]; memset(&dvd_tracks, 0, sizeof(dvd_track) * dvd_info.tracks); // Open first IFO uint16_t vts = 1; ifo_handle_t *vts_ifo = NULL; vts_ifo = ifoOpen(dvdread_dvd, vts); if(vts_ifo == NULL) { fprintf(stderr, "* Could not open VTS_IFO for track %u\n", 1); return 1; } ifoClose(vts_ifo); vts_ifo = NULL; // Create an array of all the IFOs ifo_handle_t *vts_ifos[DVD_MAX_VTS_IFOS]; vts_ifos[0] = NULL; for(vts = 1; vts < dvd_info.video_title_sets + 1; vts++) { dvd_vts[vts].vts = vts; dvd_vts[vts].valid = false; dvd_vts[vts].blocks = 0; dvd_vts[vts].filesize = 0; dvd_vts[vts].vobs = 0; dvd_vts[vts].tracks = 0; dvd_vts[vts].valid_tracks = 0; dvd_vts[vts].invalid_tracks = 0; vts_ifos[vts] = ifoOpen(dvdread_dvd, vts); if(vts_ifos[vts] == NULL) { dvd_vts[vts].valid = false; vts_ifos[vts] = NULL; } else if(!ifo_is_vts(vts_ifos[vts])) { dvd_vts[vts].valid = false; ifoClose(vts_ifos[vts]); vts_ifos[vts] = NULL; } else { dvd_vts[vts].valid = true; } } // Exit if track number requested does not exist if(opt_track_number && (arg_track_number > dvd_info.tracks)) { fprintf(stderr, "dvd_player: Invalid track number %d\n", arg_track_number); fprintf(stderr, "dvd_player: Valid track numbers: 1 to %u\n", dvd_info.tracks); ifoClose(vmg_ifo); DVDClose(dvdread_dvd); return 1; } else if(opt_track_number) { dvd_playback.track = arg_track_number; } uint16_t ix = 0; uint16_t track = 1; uint32_t longest_msecs = 0; uint16_t longest_widescreen_track = 0; uint32_t longest_widescreen_msecs = 0; uint16_t longest_pan_scan_track = 0; uint32_t longest_pan_scan_msecs = 0; for(ix = 0, track = 1; ix < dvd_info.tracks; ix++, track++) { vts = dvd_vts_ifo_number(vmg_ifo, ix + 1); vts_ifo = vts_ifos[vts]; dvd_track_info(&dvd_tracks[ix], track, vmg_ifo, vts_ifo); if(dvd_tracks[ix].msecs > longest_msecs) { dvd_info.longest_track = track; longest_msecs = dvd_tracks[ix].msecs; } if(dvd_track_aspect_ratio_16x9(vts_ifo) && dvd_tracks[ix].msecs > longest_widescreen_msecs) { longest_widescreen_msecs = dvd_tracks[ix].msecs; longest_widescreen_track = track; } else if(dvd_track_aspect_ratio_4x3(vts_ifo) && dvd_tracks[ix].msecs > longest_pan_scan_msecs) { longest_pan_scan_msecs = dvd_tracks[ix].msecs; longest_pan_scan_track = track; } } if(opt_widescreen && longest_widescreen_track != 0) dvd_info.longest_track = longest_widescreen_track; else if(opt_pan_scan && longest_pan_scan_track != 0) dvd_info.longest_track = longest_pan_scan_track; // TODO if there is another track that is active, within ~1 seconds of longest track, and // the first is pan & scan, and the second is widescreen, switch to the widescreen one. // A more intelligent search might also see if the second one has audio tracks. Need to // find a reference DVD. // Set the track number to play if none is passed as an argument if(!opt_track_number) dvd_playback.track = dvd_info.longest_track; dvd_track = dvd_tracks[dvd_playback.track - 1]; // Set the proper chapter range if(opt_chapter_number) { if(arg_first_chapter > dvd_track.chapters) { dvd_playback.first_chapter = dvd_track.chapters; } else dvd_playback.first_chapter = arg_first_chapter; if(arg_last_chapter > dvd_track.chapters) { dvd_playback.last_chapter = dvd_track.chapters; } else dvd_playback.last_chapter = arg_last_chapter; } else { dvd_playback.first_chapter = 1; dvd_playback.last_chapter = dvd_track.chapters; } /** * File descriptors and filenames */ dvd_file_t *dvdread_vts_file = NULL; vts = dvd_vts_ifo_number(vmg_ifo, dvd_playback.track); vts_ifo = vts_ifos[vts]; // Open the VTS VOB dvdread_vts_file = DVDOpenFile(dvdread_dvd, vts, DVD_READ_TITLE_VOBS); printf("Track: %02u, Length: %s, Chapters: %02u, Cells: %02u, Audio streams: %02u, Subpictures: %02u, Filesize: %lu, Blocks: %lu\n", dvd_track.track, dvd_track.length, dvd_track.chapters, dvd_track.cells, dvd_track.audio_tracks, dvd_track.subtitles, dvd_track.filesize, dvd_track.blocks); // Check for track issues dvd_track.valid = true; if(dvd_vts[vts].valid == false) { dvd_track.valid = false; } if(dvd_track.msecs == 0) { printf(" Error: track has zero length\n"); dvd_track.valid = false; } if(dvd_track.chapters == 0) { printf(" Error: track has zero chapters\n"); dvd_track.valid = false; } if(dvd_track.cells == 0) { printf(" Error: track has zero cells\n"); dvd_track.valid = false; } if(dvd_track.valid == false) { printf("Track has been marked as invalid, quitting\n"); DVDCloseFile(dvdread_vts_file); if(vts_ifo) ifoClose(vts_ifo); if(vmg_ifo) ifoClose(vmg_ifo); if(dvdread_dvd) DVDClose(dvdread_dvd); return 1; } // DVD playback using libmpv dvd_mpv = mpv_create(); // Terminal output mpv_set_option_string(dvd_mpv, "terminal", "yes"); mpv_set_option_string(dvd_mpv, "term-osd-bar", "yes"); if(debug) { mpv_request_log_messages(dvd_mpv, "debug"); } else if(verbose) { mpv_request_log_messages(dvd_mpv, "v"); } else { mpv_request_log_messages(dvd_mpv, "none"); // Skip "[ffmpeg/audio] ac3: frame sync error" which are normal when seeking on DVDs mpv_set_option_string(dvd_mpv, "msg-level", "ffmpeg/audio=none"); } // mpv zero-indexes tracks snprintf(dvd_mpv_args, 13, "dvdread://%u", dvd_playback.track - 1); // MPV uses zero-indexing for tracks, dvd_info uses one instead const char *dvd_mpv_commands[] = { "loadfile", dvd_mpv_args, NULL }; // Load user's mpv configuration in ~/.config/dvd_player/mpv.conf (and friends) if(strlen(dvd_player.mpv_config_dir) > 0) { mpv_set_option_string(dvd_mpv, "config-dir", dvd_player.mpv_config_dir); mpv_set_option_string(dvd_mpv, "config", "yes"); } // When choosing a chapter range, mpv will add 1 to the last one requested snprintf(dvd_playback.mpv_chapters_range, 8, "%u-%u", dvd_playback.first_chapter, dvd_playback.last_chapter + 1); // Playback options and default configuration mpv_set_option_string(dvd_mpv, "dvd-device", device_filename); if(strlen(dvd_info.title) > 0) mpv_set_option_string(dvd_mpv, "title", dvd_info.title); else mpv_set_option_string(dvd_mpv, "title", "dvd_player"); mpv_set_option_string(dvd_mpv, "chapter", dvd_playback.mpv_chapters_range); mpv_set_option_string(dvd_mpv, "input-default-bindings", "yes"); mpv_set_option_string(dvd_mpv, "input-vo-keyboard", "yes"); if(strlen(dvd_playback.audio_aid) > 0) mpv_set_option_string(dvd_mpv, "aid", dvd_playback.audio_aid); else if(strlen(dvd_playback.audio_lang) > 0) mpv_set_option_string(dvd_mpv, "alang", dvd_playback.audio_lang); if(dvd_playback.subtitles && strlen(dvd_playback.subtitles_sid) > 0) mpv_set_option_string(dvd_mpv, "sid", dvd_playback.subtitles_sid); else if(dvd_playback.subtitles && strlen(dvd_playback.subtitles_lang) > 0) mpv_set_option_string(dvd_mpv, "slang", dvd_playback.subtitles_lang); if(dvd_playback.fullscreen) mpv_set_option_string(dvd_mpv, "fullscreen", NULL); if(dvd_playback.deinterlace) mpv_set_option_string(dvd_mpv, "deinterlace", "yes"); if(opt_no_video) mpv_set_option_string(dvd_mpv, "video", "no"); if(opt_no_audio) mpv_set_option_string(dvd_mpv, "audio", "no"); // start mpv mpv_initialize(dvd_mpv); mpv_command(dvd_mpv, dvd_mpv_commands); while(true) { dvd_mpv_event = mpv_wait_event(dvd_mpv, -1); // Goodbye :) if(dvd_mpv_event->event_id == MPV_EVENT_SHUTDOWN || dvd_mpv_event->event_id == MPV_EVENT_END_FILE) break; if(debug && dvd_mpv_event->event_id != MPV_EVENT_LOG_MESSAGE) printf("dvd_player [mpv_event_name]: %s\n", mpv_event_name(dvd_mpv_event->event_id)); // Logging output if((verbose || debug) && dvd_mpv_event->event_id == MPV_EVENT_LOG_MESSAGE) { dvd_mpv_log_message = (struct mpv_event_log_message *)dvd_mpv_event->data; printf("mpv [%s]: %s", dvd_mpv_log_message->level, dvd_mpv_log_message->text); } } mpv_terminate_destroy(dvd_mpv); DVDCloseFile(dvdread_vts_file); if(vts_ifo) ifoClose(vts_ifo); if(vmg_ifo) ifoClose(vmg_ifo); if(dvdread_dvd) DVDClose(dvdread_dvd); return 0; }