Mercurial > libdvdnav.hg
diff dvdnav.c @ 114:b6834e6359cf src
big libdvdnav cleanup, quoting the ChangeLog:
* some bugfixes
* code cleanup
* build process polishing
* more sensible event order in get_next_block to ensure useful event delivery
* VOBU level resume
* fixed: seeking in a multiangle feature briefly showed the wrong angle
author | mroi |
---|---|
date | Thu, 20 Feb 2003 15:32:21 +0000 |
parents | e984044bbe7b |
children | b527b7cbfb19 |
line wrap: on
line diff
--- a/dvdnav.c Mon Jan 13 13:33:45 2003 +0000 +++ b/dvdnav.c Thu Feb 20 15:32:21 2003 +0000 @@ -30,7 +30,6 @@ */ #include <pthread.h> -#include <dvdnav.h> #include "dvdnav_internal.h" #include "read_cache.h" @@ -42,119 +41,9 @@ #include "remap.h" -/* - * NOTE: - * All NLCK_*() function are not mutex locked, this made them reusable in - * a locked context. Take care. - * - */ - -/* Current domain (backend to dvdnav_is_domain_() funcs) */ -static int8_t NLCK_dvdnav_is_domain(dvdnav_t *this, domain_t domain) { - dvd_state_t *state; - - if((!this) || (!this->started) || (!this->vm)) - return -1; - - state = &(this->vm->state); - - if(!state) - return -1; - - return (state->domain == domain) ? 1 : 0; -} - -static int8_t _dvdnav_is_domain(dvdnav_t *this, domain_t domain) { - int8_t retval; - - pthread_mutex_lock(&this->vm_lock); - retval = NLCK_dvdnav_is_domain(this, domain); - pthread_mutex_unlock(&this->vm_lock); - - return retval; -} - -static int8_t NCLK_dvdnav_get_audio_logical_stream(dvdnav_t *this, uint8_t audio_num) { - dvd_state_t *state; - int8_t logical = -1; - - if(!NLCK_dvdnav_is_domain(this, VTS_DOMAIN)) - audio_num = 0; - - state = &(this->vm->state); - - if(audio_num < 8) { - if(state->pgc->audio_control[audio_num] & (1 << 15)) { - logical = (state->pgc->audio_control[audio_num] >> 8) & 0x07; - } - } - - return logical; -} +static dvdnav_status_t dvdnav_clear(dvdnav_t * this) { + /* clear everything except file, vm, mutex, readahead */ -static int8_t NCLK_dvdnav_get_spu_logical_stream(dvdnav_t *this, uint8_t subp_num) { - dvd_state_t *state; - ifo_handle_t *vtsi; - - if(!this) - return -1; - - state = &(this->vm->state); - vtsi = this->vm->vtsi; - - if(subp_num >= vtsi->vtsi_mat->nr_of_vts_subp_streams) - return -1; - - return vm_get_subp_stream(this->vm, subp_num, 0); -} - -static int8_t NLCK_dvdnav_get_active_spu_stream(dvdnav_t *this) { - dvd_state_t *state; - int8_t subp_num; - int stream_num; - - state = &(this->vm->state); - subp_num = state->SPST_REG & ~0x40; - stream_num = NCLK_dvdnav_get_spu_logical_stream(this, subp_num); - - if(stream_num == -1) - for(subp_num = 0; subp_num < 32; subp_num++) - if(state->pgc->subp_control[subp_num] & (1 << 31)) { - stream_num = NCLK_dvdnav_get_spu_logical_stream(this, subp_num); - break; - } - - return stream_num; -} - -uint8_t dvdnav_get_video_aspect(dvdnav_t *this) { - uint8_t retval; - - pthread_mutex_lock(&this->vm_lock); - retval = (uint8_t) vm_get_video_aspect(this->vm); - pthread_mutex_unlock(&this->vm_lock); - - return retval; -} - -uint8_t dvdnav_get_video_scale_permission(dvdnav_t *this) { - uint8_t retval; - - pthread_mutex_lock(&this->vm_lock); - retval = (uint8_t) vm_get_video_scale_permission(this->vm); - pthread_mutex_unlock(&this->vm_lock); - - return retval; -} - -dvdnav_status_t dvdnav_clear(dvdnav_t * this) { - if (!this) { - printerr("Passed a NULL pointer"); - return S_ERR; - } - /* clear everything except path, file, vm, mutex, readahead */ - - /* path */ if (this->file) DVDCloseFile(this->file); this->file = NULL; this->open_vtsN = -1; @@ -166,17 +55,15 @@ /* Set initial values of flags */ this->position_current.still = 0; this->skip_still = 0; - this->stop = 0; this->spu_clut_changed = 0; - this->started=0; - /* this->use_read_ahead */ + this->started = 0; dvdnav_read_cache_clear(this->cache); return S_OK; } -dvdnav_status_t dvdnav_open(dvdnav_t** dest, char *path) { +dvdnav_status_t dvdnav_open(dvdnav_t** dest, const char *path) { dvdnav_t *this; struct timeval time; @@ -186,10 +73,9 @@ (*dest) = NULL; this = (dvdnav_t*)malloc(sizeof(dvdnav_t)); if(!this) - return S_ERR; + return S_ERR; memset(this, 0, (sizeof(dvdnav_t) ) ); /* Make sure this structure is clean */ - (*dest) = this; - + pthread_mutex_init(&this->vm_lock, NULL); /* Initialise the error string */ printerr(""); @@ -197,13 +83,13 @@ /* Initialise the VM */ this->vm = vm_new_vm(); if(!this->vm) { - printerr("Error initialising the DVD VM"); + printerr("Error initialising the DVD VM."); pthread_mutex_destroy(&this->vm_lock); free(this); return S_ERR; } - if(vm_reset(this->vm, path) == -1) { - printerr("Error starting the VM / opening the DVD device"); + if(!vm_reset(this->vm, path)) { + printerr("Error starting the VM / opening the DVD device."); pthread_mutex_destroy(&this->vm_lock); vm_free_vm(this->vm); free(this); @@ -213,34 +99,34 @@ /* Set the path. FIXME: Is a deep copy 'right' */ strncpy(this->path, path, MAX_PATH_LEN); - dvdnav_clear(this); - /* Pre-open and close a file so that the CSS-keys are cached. */ this->file = DVDOpenFile(vm_get_dvd_reader(this->vm), 0, DVD_READ_MENU_VOBS); - if (this->file) DVDCloseFile(this->file); - this->file = NULL; /* Start the read-ahead cache. */ this->cache = dvdnav_read_cache_new(this); - /* Seed the random numbers. So that the DVD VM Command rand()i - * gives a different start value each time a DVD is played. - */ - gettimeofday(&time,NULL); + /* Seed the random numbers. So that the DVD VM Command rand() + * gives a different start value each time a DVD is played. */ + gettimeofday(&time, NULL); srand(time.tv_usec); + dvdnav_clear(this); + + (*dest) = this; return S_OK; } dvdnav_status_t dvdnav_close(dvdnav_t *this) { - if(!this) { - printerr("Passed a NULL pointer"); - return S_ERR; - } + #ifdef LOG_DEBUG fprintf(MSG_OUT, "libdvdnav: close:called\n"); #endif + if(!this) { + printerr("Passed a NULL pointer."); + return S_ERR; + } + if (this->file) { DVDCloseFile(this->file); #ifdef LOG_DEBUG @@ -250,24 +136,18 @@ } /* Free the VM */ - if(this->vm) { + if(this->vm) vm_free_vm(this->vm); - } - if (this->file) { - DVDCloseFile(this->file); -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: close2:file closing\n"); -#endif - this->file = NULL; - } + pthread_mutex_destroy(&this->vm_lock); /* We leave the final freeing of the entire structure to the cache, * because we don't know, if there are still buffers out in the wild, * that must return first. */ - if(this->cache) { + if(this->cache) dvdnav_read_cache_free(this->cache); - } else free(this); + else + free(this); return S_OK; } @@ -278,101 +158,83 @@ #ifdef LOG_DEBUG fprintf(MSG_OUT, "libdvdnav: reset:called\n"); #endif + if(!this) { - printerr("Passed a NULL pointer"); + printerr("Passed a NULL pointer."); return S_ERR; } -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: getting lock\n"); -#endif + pthread_mutex_lock(&this->vm_lock); + #ifdef LOG_DEBUG fprintf(MSG_OUT, "libdvdnav: reseting vm\n"); #endif - if(vm_reset(this->vm, NULL) == -1) { - printerr("Error restarting the VM"); + if(!vm_reset(this->vm, NULL)) { + printerr("Error restarting the VM."); pthread_mutex_unlock(&this->vm_lock); return S_ERR; } #ifdef LOG_DEBUG fprintf(MSG_OUT, "libdvdnav: clearing dvdnav\n"); #endif - result=dvdnav_clear(this); -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: unlocking\n"); -#endif + result = dvdnav_clear(this); + pthread_mutex_unlock(&this->vm_lock); return result; } -dvdnav_status_t dvdnav_path(dvdnav_t *this, char** path) { - if(!this || !path || !(*path)) { +dvdnav_status_t dvdnav_path(dvdnav_t *this, const char** path) { + + if(!this || !path) { + printerr("Passed a NULL pointer."); return S_ERR; } - /* FIXME: Is shallow copy 'right'? */ (*path) = this->path; return S_OK; } -char* dvdnav_err_to_string(dvdnav_t *this) { - if(!this) { - /* Shold this be "passed a NULL pointer?" */ +const char* dvdnav_err_to_string(dvdnav_t *this) { + + if(!this) return "Hey! You gave me a NULL pointer you naughty person!"; - } return this->err_str; } -/** +/* * Returns 1 if block contains NAV packet, 0 otherwise. * Precesses said NAV packet if present. * * Most of the code in here is copied from xine's MPEG demuxer * so any bugs which are found in that should be corrected here also. */ -int dvdnav_decode_packet(dvdnav_t *this, uint8_t *p, dsi_t* nav_dsi, pci_t* nav_pci) { - int bMpeg1=0; +static int dvdnav_decode_packet(dvdnav_t *this, uint8_t *p, dsi_t *nav_dsi, pci_t *nav_pci) { + int bMpeg1 = 0; uint32_t nHeaderLen; uint32_t nPacketLen; uint32_t nStreamID; -/* uint8_t *p_start=p; */ - - - if (p==NULL) { - fprintf(MSG_OUT, "libdvdnav: Passed a NULL pointer.\n"); - return 0; - } - - /* dprint("Checking packet...\n"); */ if (p[3] == 0xBA) { /* program stream pack header */ - int nStuffingBytes; - /* xprintf (VERBOSE|DEMUX, "program stream pack header\n"); */ - bMpeg1 = (p[4] & 0x40) == 0; if (bMpeg1) { - p += 12; + p += 12; } else { /* mpeg2 */ nStuffingBytes = p[0xD] & 0x07; p += 14 + nStuffingBytes; } } - if (p[3] == 0xbb) { /* program stream system header */ - int nHeaderLen; - nHeaderLen = (p[4] << 8) | p[5]; p += 6 + nHeaderLen; } /* we should now have a PES packet here */ - if (p[0] || p[1] || (p[2] != 1)) { fprintf(MSG_OUT, "libdvdnav: demux error! %02x %02x %02x (should be 0x000001) \n",p[0],p[1],p[2]); return 0; @@ -385,14 +247,14 @@ p += nHeaderLen; if (nStreamID == 0xbf) { /* Private stream 2 */ -/* - * int i; - * fprintf(MSG_OUT, "libdvdnav: nav packet=%u\n",p-p_start-6); - * for(i=0;i<80;i++) { - * fprintf(MSG_OUT, "%02x ",p[i-6]); - * } - * fprintf(MSG_OUT, "\n"); - */ +#if 0 + int i; + fprintf(MSG_OUT, "libdvdnav: nav packet=%u\n",p-p_start-6); + for(i=0;i<80;i++) + fprintf(MSG_OUT, "%02x ",p[i-6]); + fprintf(MSG_OUT, "\n"); +#endif + if(p[0] == 0x00) { navRead_PCI(nav_pci, p+1); } @@ -403,9 +265,7 @@ if(p[6] == 0x01) { nPacketLen = p[4] << 8 | p[5]; p += 6; - /* dprint("NAV DSI packet\n"); */ navRead_DSI(nav_dsi, p+1); - } return 1; } @@ -415,7 +275,7 @@ /* DSI is used for most angle stuff. * PCI is used for only non-seemless angle stuff */ -int dvdnav_get_vobu(dvdnav_t *self, dsi_t* nav_dsi, pci_t* nav_pci, dvdnav_vobu_t* vobu) { +static int dvdnav_get_vobu(dvdnav_t *this, dsi_t *nav_dsi, pci_t *nav_pci, dvdnav_vobu_t *vobu) { uint32_t next; int angle, num_angle; @@ -434,70 +294,72 @@ * Should really assert if bit 31 != 1 */ - /* Relative offset from vobu_start */ - vobu->vobu_next = ( nav_dsi->vobu_sri.next_vobu & 0x3fffffff ); - - /* Old code -- may still be sueful one day +#if 0 + /* Old code -- may still be useful one day */ if(nav_dsi->vobu_sri.next_vobu != SRI_END_OF_CELL ) { vobu->vobu_next = ( nav_dsi->vobu_sri.next_vobu & 0x3fffffff ); } else { vobu->vobu_next = vobu->vobu_length; - } */ + } +#else + /* Relative offset from vobu_start */ + vobu->vobu_next = ( nav_dsi->vobu_sri.next_vobu & 0x3fffffff ); +#endif - dvdnav_get_angle_info(self, &angle, &num_angle); + vm_get_angle_info(this->vm, &angle, &num_angle); + + /* FIMXE: The angle reset doesn't work for some reason for the moment */ #if 0 - /* FIMXE: The angle reset doesn't work for some reason for the moment */ - if((num_angle < angle) && (angle != 1)) { - printf("OOOOOOO angle ends!\n"); + fprintf(MSG_OUT, "libdvdnav: angle ends!\n"); /* This is to switch back to angle one when we * finish with angles. */ - dvdnav_angle_change(self, 1); + dvdnav_angle_change(this, 1); } #endif if(num_angle != 0) { - next = nav_pci->nsml_agli.nsml_agl_dsta[angle-1]; - if(next != 0) { + if((next = nav_pci->nsml_agli.nsml_agl_dsta[angle-1]) != 0) { if((next & 0x3fffffff) != 0) { - if(next & 0x80000000) { + if(next & 0x80000000) vobu->vobu_next = - (int32_t)(next & 0x3fffffff); - } else { + else vobu->vobu_next = + (int32_t)(next & 0x3fffffff); - } } - - } else if( nav_dsi->sml_agli.data[angle-1].address != 0 ) { - next = nav_dsi->sml_agli.data[angle-1].address; + } else if((next = nav_dsi->sml_agli.data[angle-1].address) != 0) { vobu->vobu_length = nav_dsi->sml_pbi.ilvu_ea; - if((next & 0x80000000) && (next != 0x7fffffff)) { + if((next & 0x80000000) && (next != 0x7fffffff)) vobu->vobu_next = - (int32_t)(next & 0x3fffffff); - } else { + else vobu->vobu_next = + (int32_t)(next & 0x3fffffff); - } } } return 1; } -/* This is the main get_next_block function which actually gets the media stream video and audio etc. - * The use of this function is optional, with the application programmer - * free to implement their own version of this function - * FIXME: Make the function calls from here public API calls. +/* + * These are the main get_next_block function which actually get the media stream video and audio etc. + * + * There are two versions: The second one is using the zero-copy read ahead cache and therefore + * hands out pointers targetting directly into the cache. + * The first one uses a memcopy to fill this cache block into the application provided memory. + * The benefit of this first one is that no special memory management is needed. The application is + * the only one responsible of allocating and freeing the memory associated with the pointer. + * The drawback is the additional memcopy. */ dvdnav_status_t dvdnav_get_next_block(dvdnav_t *this, unsigned char *buf, - int *event, int *len) { + int *event, int *len) { unsigned char *block; dvdnav_status_t status; block = buf; status = dvdnav_get_next_cache_block(this, &block, event, len); - if (block != buf) { + if (status == S_OK && block != buf) { /* we received a block from the cache, copy it, so we can give it back */ memcpy(buf, block, DVD_VIDEO_LB_LEN); dvdnav_free_cache_block(this, block); @@ -506,14 +368,16 @@ } dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *this, unsigned char **buf, - int *event, int *len) { + int *event, int *len) { dvd_state_t *state; int result; + if(!this || !event || !len || !buf || !*buf) { - printerr("Passed a NULL pointer"); + printerr("Passed a NULL pointer."); return S_ERR; } - pthread_mutex_lock(&this->vm_lock); + + pthread_mutex_lock(&this->vm_lock); if(!this->started) { /* Start the VM */ @@ -526,118 +390,64 @@ (*len) = 0; /* Check the STOP flag */ - if(this->stop) { + if(this->vm->stopped) { (*event) = DVDNAV_STOP; - pthread_mutex_unlock(&this->vm_lock); - return S_OK; - } - - /* Check the STILLFRAME flag */ - /* FIXME: Still cell, not still frame */ - if(this->position_current.still != 0) { - dvdnav_still_event_t still_event; - - still_event.length = this->position_current.still; - - (*event) = DVDNAV_STILL_FRAME; - (*len) = sizeof(dvdnav_still_event_t); - memcpy(*buf, &(still_event), sizeof(dvdnav_still_event_t)); - - pthread_mutex_unlock(&this->vm_lock); - return S_OK; - } - - vm_position_get(this->vm,&this->position_next); - /********** - fprintf(MSG_OUT, "libdvdnav: POS-NEXT "); - vm_position_print(this->vm, &this->position_next); - fprintf(MSG_OUT, "libdvdnav: POS-CUR "); - vm_position_print(this->vm, &this->position_current); - **********/ - - if(this->position_current.hop_channel != this->position_next.hop_channel) { - this->position_current.hop_channel = this->position_next.hop_channel; - (*event) = DVDNAV_HOP_CHANNEL; - (*len) = 0; + this->started = 0; pthread_mutex_unlock(&this->vm_lock); return S_OK; } - - - if(this->spu_clut_changed) { - (*event) = DVDNAV_SPU_CLUT_CHANGE; -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: SPU_CLUT_CHANGE\n"); -#endif - (*len) = 16 * sizeof(uint32_t); - memcpy(*buf, &(state->pgc->palette), 16 * sizeof(uint32_t)); - this->spu_clut_changed = 0; -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: SPU_CLUT_CHANGE returning S_OK\n"); -#endif - pthread_mutex_unlock(&this->vm_lock); - return S_OK; - } + vm_position_get(this->vm, &this->position_next); - if(this->position_current.spu_channel != this->position_next.spu_channel) { - dvdnav_spu_stream_change_event_t stream_change; - (*event) = DVDNAV_SPU_STREAM_CHANGE; -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE\n"); +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: POS-NEXT "); + vm_position_print(this->vm, &this->position_next); + fprintf(MSG_OUT, "libdvdnav: POS-CUR "); + vm_position_print(this->vm, &this->position_current); #endif - (*len) = sizeof(dvdnav_spu_stream_change_event_t); - stream_change.physical_wide = vm_get_subp_active_stream(this->vm, 0); - stream_change.physical_letterbox = vm_get_subp_active_stream(this->vm, 1); - stream_change.physical_pan_scan = vm_get_subp_active_stream(this->vm, 2); - memcpy(*buf, &(stream_change), sizeof( dvdnav_spu_stream_change_event_t)); - this->position_current.spu_channel = this->position_next.spu_channel; -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_wide=%d\n",stream_change.physical_wide); - fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_letterbox=%d\n",stream_change.physical_letterbox); - fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_pan_scan=%d\n",stream_change.physical_pan_scan); -#endif - if (stream_change.physical_wide != -1 && - stream_change.physical_letterbox != -1 && - stream_change.physical_pan_scan != -1) { + + /* did we hop? */ + if(this->position_current.hop_channel != this->position_next.hop_channel) { + (*event) = DVDNAV_HOP_CHANNEL; #ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE returning S_OK\n"); -#endif - pthread_mutex_unlock(&this->vm_lock); - return S_OK; - } - } - - if(this->position_current.audio_channel != this->position_next.audio_channel) { - dvdnav_audio_stream_change_event_t stream_change; - (*event) = DVDNAV_AUDIO_STREAM_CHANGE; -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: AUDIO_STREAM_CHANGE\n"); + fprintf(MSG_OUT, "libdvdnav: HOP_CHANNEL\n"); #endif - (*len) = sizeof(dvdnav_audio_stream_change_event_t); - stream_change.physical= vm_get_audio_active_stream( this->vm ); - memcpy(*buf, &(stream_change), sizeof( dvdnav_audio_stream_change_event_t)); - this->position_current.audio_channel = this->position_next.audio_channel; -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: AUDIO_STREAM_CHANGE stream_id=%d returning S_OK\n",stream_change.physical); -#endif - pthread_mutex_unlock(&this->vm_lock); - return S_OK; - } - - /* Check the HIGHLIGHT flag */ - /* FIXME: Use BUTTON instead of HIGHLIGHT. */ - if(this->position_current.button != this->position_next.button) { - dvdnav_highlight_event_t hevent; - - hevent.display = 1; - hevent.buttonN = this->position_next.button; - - this->position_current.button = this->position_next.button; - - (*event) = DVDNAV_HIGHLIGHT; - (*len) = sizeof(hevent); - memcpy(*buf, &(hevent), sizeof(hevent)); + if (this->position_next.hop_channel > HOP_SEEK) { + int num_angles = 0, current; + + /* we seeked -> check for multiple angles */ + vm_get_angle_info(this->vm, ¤t, &num_angles); + if (num_angles > 1) { + int result, block; + /* we have to skip the first VOBU when seeking in a multiangle feature, + * because it might belong to the wrong angle */ + block = this->position_next.cell_start + this->position_next.block; + result = dvdnav_read_cache_block(this->cache, block, 1, buf); + if(result <= 0) { + printerr("Error reading NAV packet."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } + /* Decode nav into pci and dsi. Then get next VOBU info. */ + if(!dvdnav_decode_packet(this, *buf, &this->dsi, &this->pci)) { + printerr("Expected NAV packet but none found."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } + dvdnav_get_vobu(this, &this->dsi, &this->pci, &this->vobu); + /* skip to next, if there is a next */ + if (this->vobu.vobu_next != SRI_END_OF_CELL) { + this->vobu.vobu_start += this->vobu.vobu_next; + this->vobu.vobu_next = 0; + } + /* update VM state */ + this->vm->state.blockN = this->vobu.vobu_start - this->position_next.cell_start; + } + } + this->position_current.hop_channel = this->position_next.hop_channel; + /* Make blockN > vobu_length to do expected_nav */ + this->vobu.vobu_length = 0; + this->vobu.blockN = 1; pthread_mutex_unlock(&this->vm_lock); return S_OK; } @@ -650,7 +460,6 @@ dvdnav_vts_change_event_t vts_event; if(this->file) { - dvdnav_read_cache_clear(this->cache); DVDCloseFile(this->file); this->file = NULL; } @@ -658,22 +467,22 @@ vts_event.old_vtsN = this->open_vtsN; vts_event.old_domain = this->open_domain; - /* Use the current DOMAIN to find whether to open menu or title VOBs */ + /* Use the DOMAIN to find whether to open menu or title VOBs */ switch(this->position_next.domain) { - case FP_DOMAIN: - case VMGM_DOMAIN: + case FP_DOMAIN: + case VMGM_DOMAIN: domain = DVD_READ_MENU_VOBS; vtsN = 0; break; - case VTSM_DOMAIN: + case VTSM_DOMAIN: domain = DVD_READ_MENU_VOBS; vtsN = this->position_next.vts; break; - case VTS_DOMAIN: + case VTS_DOMAIN: domain = DVD_READ_TITLE_VOBS; vtsN = this->position_next.vts; break; - default: + default: printerr("Unknown domain when changing VTS."); pthread_mutex_unlock(&this->vm_lock); return S_ERR; @@ -695,11 +504,12 @@ /* File opened successfully so return a VTS change event */ (*event) = DVDNAV_VTS_CHANGE; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: VTS_CHANGE\n"); +#endif + (*len) = sizeof(vts_event); memcpy(*buf, &(vts_event), sizeof(vts_event)); - (*len) = sizeof(vts_event); - /* On a VTS change, we want to disable any highlights which - * may have been shown (FIXME: is this valid?) */ this->spu_clut_changed = 1; this->position_current.cell = -1; /* Force an update */ this->position_current.spu_channel = -1; /* Force an update */ @@ -708,33 +518,137 @@ pthread_mutex_unlock(&this->vm_lock); return S_OK; } - /* FIXME: Don't really need "cell", we only need vobu_start */ + + /* Check if the cell changed */ if( (this->position_current.cell != this->position_next.cell) || (this->position_current.cell_restart != this->position_next.cell_restart) || - (this->position_current.vobu_start != this->position_next.vobu_start) || - (this->position_current.vobu_next != this->position_next.vobu_next) ) { - this->position_current.cell = this->position_next.cell; + (this->position_current.cell_start != this->position_next.cell_start) ) { + + (*event) = DVDNAV_CELL_CHANGE; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: CELL_CHANGE\n"); +#endif + (*len) = 0; + + this->position_current.cell = this->position_next.cell; this->position_current.cell_restart = this->position_next.cell_restart; - /* vobu_start changes when PGC or PG changes. */ - this->position_current.vobu_start = this->position_next.vobu_start; - this->position_current.vobu_next = this->position_next.vobu_next; - /* FIXME: Need to set vobu_start, vobu_next */ - this->vobu.vobu_start = this->position_next.vobu_start; - /* vobu_next is use for mid cell resumes */ - this->vobu.vobu_next = this->position_next.vobu_next; - this->vobu.vobu_length = 0; - this->vobu.blockN = this->vobu.vobu_length + 1; - /* Make blockN > vobu_lenght to do expected_nav */ - (*event) = DVDNAV_CELL_CHANGE; - (*len) = 0; + this->position_current.cell_start = this->position_next.cell_start; + this->position_current.block = this->position_next.block; + + /* vobu info is used for mid cell resumes */ + this->vobu.vobu_start = this->position_next.cell_start + this->position_next.block; + this->vobu.vobu_next = 0; + /* Make blockN > vobu_length to do expected_nav */ + this->vobu.vobu_length = 0; + this->vobu.blockN = 1; + + /* update the spu palette at least on PGC changes */ + this->spu_clut_changed = 1; + this->position_current.spu_channel = -1; /* Force an update */ + this->position_current.audio_channel = -1; /* Force an update */ + pthread_mutex_unlock(&this->vm_lock); return S_OK; } - - if (this->vobu.blockN > this->vobu.vobu_length) { - /* End of VOBU */ + /* has the CLUT changed? */ + if(this->spu_clut_changed) { + (*event) = DVDNAV_SPU_CLUT_CHANGE; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: SPU_CLUT_CHANGE\n"); +#endif + (*len) = 16 * sizeof(uint32_t); + memcpy(*buf, &(state->pgc->palette), 16 * sizeof(uint32_t)); + this->spu_clut_changed = 0; + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + + /* has the SPU channel changed? */ + if(this->position_current.spu_channel != this->position_next.spu_channel) { + dvdnav_spu_stream_change_event_t stream_change; + + (*event) = DVDNAV_SPU_STREAM_CHANGE; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE\n"); +#endif + (*len) = sizeof(dvdnav_spu_stream_change_event_t); + stream_change.physical_wide = vm_get_subp_active_stream(this->vm, 0); + stream_change.physical_letterbox = vm_get_subp_active_stream(this->vm, 1); + stream_change.physical_pan_scan = vm_get_subp_active_stream(this->vm, 2); + memcpy(*buf, &(stream_change), sizeof(dvdnav_spu_stream_change_event_t)); + this->position_current.spu_channel = this->position_next.spu_channel; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_wide=%d\n",stream_change.physical_wide); + fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_letterbox=%d\n",stream_change.physical_letterbox); + fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_pan_scan=%d\n",stream_change.physical_pan_scan); +#endif + if (stream_change.physical_wide != -1 && + stream_change.physical_letterbox != -1 && + stream_change.physical_pan_scan != -1) { +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE returning S_OK\n"); +#endif + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + } + /* has the audio channel changed? */ + if(this->position_current.audio_channel != this->position_next.audio_channel) { + dvdnav_audio_stream_change_event_t stream_change; + + (*event) = DVDNAV_AUDIO_STREAM_CHANGE; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: AUDIO_STREAM_CHANGE\n"); +#endif + (*len) = sizeof(dvdnav_audio_stream_change_event_t); + stream_change.physical = vm_get_audio_active_stream( this->vm ); + memcpy(*buf, &(stream_change), sizeof( dvdnav_audio_stream_change_event_t)); + this->position_current.audio_channel = this->position_next.audio_channel; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: AUDIO_STREAM_CHANGE stream_id=%d returning S_OK\n",stream_change.physical); +#endif + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + + /* Check the HIGHLIGHT flag */ + if(this->position_current.button != this->position_next.button) { + dvdnav_highlight_event_t hevent; + + (*event) = DVDNAV_HIGHLIGHT; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: HIGHLIGHT\n"); +#endif + (*len) = sizeof(hevent); + hevent.display = 1; + hevent.buttonN = this->position_next.button; + memcpy(*buf, &(hevent), sizeof(hevent)); + this->position_current.button = this->position_next.button; + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + + /* Check the STILLFRAME flag */ + if(this->position_current.still != 0) { + dvdnav_still_event_t still_event; + + (*event) = DVDNAV_STILL_FRAME; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: STILL_FRAME\n"); +#endif + (*len) = sizeof(dvdnav_still_event_t); + still_event.length = this->position_current.still; + memcpy(*buf, &(still_event), sizeof(dvdnav_still_event_t)); + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + + /* Have we reached the end of a VOBU? */ + if (this->vobu.blockN >= this->vobu.vobu_length) { + + /* Have we reached the end of a cell? */ if(this->vobu.vobu_next == SRI_END_OF_CELL) { /* End of Cell from NAV DSI info */ #ifdef LOG_DEBUG @@ -743,58 +657,28 @@ this->position_current.still = this->position_next.still; if( this->position_current.still == 0 || this->skip_still ) { - vm_get_next_cell(this->vm); - vm_position_get(this->vm,&this->position_next); - /* FIXME: Need to set vobu_start, vobu_next */ - this->position_current.still = 0; /* still gets activated at end of cell */ - this->skip_still = 0; - this->position_current.cell = this->position_next.cell; - this->position_current.vobu_start = this->position_next.vobu_start; - this->position_current.vobu_next = this->position_next.vobu_next; - this->vobu.vobu_start = this->position_next.vobu_start; - /* vobu_next is use for mid cell resumes */ - this->vobu.vobu_next = this->position_next.vobu_next; - this->vobu.vobu_length = 0; - this->vobu.blockN = this->vobu.vobu_length + 1; - /* Make blockN > vobu_next to do expected_nav */ - /* update the spu palette on PGC changes */ - this->spu_clut_changed = 1; - this->position_current.spu_channel = -1; /* Force an update */ - this->position_current.audio_channel = -1; /* Force an update */; - (*event) = DVDNAV_CELL_CHANGE; - (*len) = 0; - pthread_mutex_unlock(&this->vm_lock); - return S_OK; - } else { - dvdnav_still_event_t still_event; - still_event.length = this->position_current.still; - (*event) = DVDNAV_STILL_FRAME; - (*len) = sizeof(dvdnav_still_event_t); - memcpy(*buf, &(still_event), sizeof(dvdnav_still_event_t)); - pthread_mutex_unlock(&this->vm_lock); - return S_OK; + /* no active cell still -> get us to the next cell */ + vm_get_next_cell(this->vm); + this->position_current.still = 0; /* still gets activated at end of cell */ + this->skip_still = 0; } + /* handle related state changes in next iteration */ + (*event) = DVDNAV_NOP; + (*len) = 0; + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } - /* Only set still after whole VOBU has been output. */ - /* - if(this->position_next.still != 0) { - this->position_current.still = this->position_next.still; - } - */ - - } - /* Perform the jump if necessary (this is always a + /* Perform remapping jump if necessary (this is always a * VOBU boundary). */ + if (this->vm->map) { + this->vobu.vobu_next = remap_block( this->vm->map, + this->vm->state.domain, this->vm->state.TTN_REG, + this->vm->state.pgN, + this->vobu.vobu_start, this->vobu.vobu_next); + } - if (this->vm->map) { - this->vobu.vobu_next = remap_block( this->vm->map, - this->vm->state.domain, this->vm->state.TTN_REG, - this->vm->state.pgN, - this->vobu.vobu_start, this->vobu.vobu_next); - } - - - /* result = DVDReadBlocks(this->file, this->vobu.vobu_start + this->vobu.vobu_next, 1, buf); */ + /* at the start of the next VOBU -> expecting NAV packet */ result = dvdnav_read_cache_block(this->cache, this->vobu.vobu_start + this->vobu.vobu_next, 1, buf); if(result <= 0) { @@ -802,31 +686,29 @@ pthread_mutex_unlock(&this->vm_lock); return S_ERR; } - /* Decode nav into pci and dsi. */ - /* Then get next VOBU info. */ - if(dvdnav_decode_packet(this, *buf, &this->dsi, &this->pci) == 0) { + /* Decode nav into pci and dsi. Then get next VOBU info. */ + if(!dvdnav_decode_packet(this, *buf, &this->dsi, &this->pci)) { printerr("Expected NAV packet but none found."); pthread_mutex_unlock(&this->vm_lock); return S_ERR; } - dvdnav_get_vobu(this, &this->dsi,&this->pci, &this->vobu); - this->vobu.blockN=1; - /* FIXME: We need to update the vm state->blockN with which VOBU we are in. - * This is so RSM resumes to the VOBU level and not just the CELL level. - * This should be implemented with a new Public API call. + /* We need to update the vm state->blockN with which VOBU we are in. + * This is so RSM resumes to the VOBU level and not just the CELL level. */ - /* We cache one past the end of the VOBU, - * in the hope it might catch the next NAV packet as well. - * This reduces the amount of read commands sent to the DVD device. - * A cache miss will only happen for 3 reasons. - * 1) Seeking - * 2) Menu change - * 3) The next VOBU does not immeadiately follow the current one. E.g. Multi Angles, ILVU. + this->vm->state.blockN = this->vobu.vobu_start - this->position_current.cell_start; + + dvdnav_get_vobu(this, &this->dsi, &this->pci, &this->vobu); + this->vobu.blockN = 0; + /* Give the cache a hint about the size of next VOBU. + * This improves pre-caching, because the VOBU will almost certainly be read entirely. */ dvdnav_pre_cache_blocks(this->cache, this->vobu.vobu_start+1, this->vobu.vobu_length+1); /* Successfully got a NAV packet */ (*event) = DVDNAV_NAV_PACKET; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: NAV_PACKET\n"); +#endif (*len) = 2048; pthread_mutex_unlock(&this->vm_lock); return S_OK; @@ -834,25 +716,62 @@ /* If we've got here, it must just be a normal block. */ if(!this->file) { - printerr("Attempting to read without opening file"); + printerr("Attempting to read without opening file."); pthread_mutex_unlock(&this->vm_lock); return S_ERR; } + this->vobu.blockN++; result = dvdnav_read_cache_block(this->cache, this->vobu.vobu_start + this->vobu.blockN, 1, buf); if(result <= 0) { printerr("Error reading from DVD."); pthread_mutex_unlock(&this->vm_lock); return S_ERR; } - this->vobu.blockN++; + (*event) = DVDNAV_BLOCK_OK; (*len) = 2048; - (*event) = DVDNAV_BLOCK_OK; pthread_mutex_unlock(&this->vm_lock); return S_OK; } +dvdnav_status_t dvdnav_get_title_string(dvdnav_t *this, const char **title_str) { + + if(!this || !title_str) { + printerr("Passed a NULL pointer."); + return S_ERR; + } + + (*title_str) = this->vm->dvd_name; + return S_OK; +} + +uint8_t dvdnav_get_video_aspect(dvdnav_t *this) { + uint8_t retval; + + if(!this) + return -1; + + pthread_mutex_lock(&this->vm_lock); + retval = (uint8_t)vm_get_video_aspect(this->vm); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + +uint8_t dvdnav_get_video_scale_permission(dvdnav_t *this) { + uint8_t retval; + + if(!this) + return -1; + + pthread_mutex_lock(&this->vm_lock); + retval = (uint8_t)vm_get_video_scale_permission(this->vm); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *this, uint8_t stream) { audio_attr_t attr; @@ -869,19 +788,6 @@ return attr.lang_code; } -int8_t dvdnav_get_audio_logical_stream(dvdnav_t *this, uint8_t audio_num) { - int8_t retval; - - if(!this) - return -1; - - pthread_mutex_lock(&this->vm_lock); - retval = NCLK_dvdnav_get_audio_logical_stream(this, audio_num); - pthread_mutex_unlock(&this->vm_lock); - - return retval; -} - uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *this, uint8_t stream) { subp_attr_t attr; @@ -898,16 +804,54 @@ return attr.lang_code; } +int8_t dvdnav_get_audio_logical_stream(dvdnav_t *this, uint8_t audio_num) { + int8_t retval; + + if(!this) + return -1; + + pthread_mutex_lock(&this->vm_lock); + if (!this->vm->state.pgc) { + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + retval = vm_get_audio_stream(this->vm, audio_num); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + int8_t dvdnav_get_spu_logical_stream(dvdnav_t *this, uint8_t subp_num) { int8_t retval; if(!this) return -1; + pthread_mutex_lock(&this->vm_lock); + if (!this->vm->state.pgc) { + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + retval = vm_get_subp_stream(this->vm, subp_num, 0); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + +int8_t dvdnav_get_active_audio_stream(dvdnav_t *this) { + int8_t retval; + + if(!this) + return -1; + pthread_mutex_lock(&this->vm_lock); - retval = NCLK_dvdnav_get_spu_logical_stream(this, subp_num); + if (!this->vm->state.pgc) { + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + retval = vm_get_audio_active_stream(this->vm); pthread_mutex_unlock(&this->vm_lock); - + return retval; } @@ -918,98 +862,110 @@ return -1; pthread_mutex_lock(&this->vm_lock); - retval = NLCK_dvdnav_get_active_spu_stream(this); + if (!this->vm->state.pgc) { + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + retval = vm_get_subp_active_stream(this->vm, 0); pthread_mutex_unlock(&this->vm_lock); return retval; } +static int8_t dvdnav_is_domain(dvdnav_t *this, domain_t domain) { + int8_t retval; + + if (!this || !this->started) + return -1; + + pthread_mutex_lock(&this->vm_lock); + retval = (this->vm->state.domain == domain); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + /* First Play domain. (Menu) */ int8_t dvdnav_is_domain_fp(dvdnav_t *this) { - return _dvdnav_is_domain(this, FP_DOMAIN); + return dvdnav_is_domain(this, FP_DOMAIN); } /* Video management Menu domain. (Menu) */ int8_t dvdnav_is_domain_vmgm(dvdnav_t *this) { - return _dvdnav_is_domain(this, VMGM_DOMAIN); + return dvdnav_is_domain(this, VMGM_DOMAIN); } /* Video Title Menu domain (Menu) */ int8_t dvdnav_is_domain_vtsm(dvdnav_t *this) { - return _dvdnav_is_domain(this, VTSM_DOMAIN); + return dvdnav_is_domain(this, VTSM_DOMAIN); } /* Video Title domain (playing movie). */ int8_t dvdnav_is_domain_vts(dvdnav_t *this) { - return _dvdnav_is_domain(this, VTS_DOMAIN); + return dvdnav_is_domain(this, VTS_DOMAIN); } -/* Generally delegate angle information handling to - * VM */ +/* Generally delegate angle information handling to VM */ dvdnav_status_t dvdnav_angle_change(dvdnav_t *this, int angle) { int num, current; if(!this) { + printerr("Passed a NULL pointer."); return S_ERR; } - if(dvdnav_get_angle_info(this, ¤t, &num) != S_OK) { - printerr("Error getting angle info"); - return S_ERR; - } - + pthread_mutex_lock(&this->vm_lock); + vm_get_angle_info(this->vm, ¤t, &num); /* Set angle SPRM if valid */ if((angle > 0) && (angle <= num)) { this->vm->state.AGL_REG = angle; } else { - printerr("Passed an invalid angle number"); + printerr("Passed an invalid angle number."); + pthread_mutex_unlock(&this->vm_lock); return S_ERR; } - - return S_OK; -} -/* FIXME: change order of current_angle, number_of_angles */ -dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *this, int* current_angle, - int *number_of_angles) { - if(!this || !this->vm) { - return S_ERR; - } - - if(!current_angle || !number_of_angles) { - printerr("Passed a NULL pointer"); - return S_ERR; - } - - vm_get_angle_info(this->vm, number_of_angles, current_angle); + pthread_mutex_unlock(&this->vm_lock); return S_OK; } -dvdnav_status_t dvdnav_get_cell_info(dvdnav_t *this, int* current_angle, - int *number_of_angles) { - if(!this || !this->vm) { +dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *this, int *current_angle, + int *number_of_angles) { + if(!this || !current_angle || !number_of_angles) { + printerr("Passed a NULL pointer."); return S_ERR; } - *current_angle=this->position_next.cell; + + pthread_mutex_lock(&this->vm_lock); + vm_get_angle_info(this->vm, current_angle, number_of_angles); + pthread_mutex_unlock(&this->vm_lock); + return S_OK; -} +} pci_t* dvdnav_get_current_nav_pci(dvdnav_t *this) { - if(!this || !this->vm) return 0; + if(!this) return 0; return &this->pci; } dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *this) { - if(!this || !this->vm) return 0; + if(!this) return 0; return &this->dsi; } uint32_t dvdnav_get_next_still_flag(dvdnav_t *this) { - if(!this || !this->vm) { - return S_ERR; - } + if(!this) return -1; return this->position_next.still; } /* * $Log$ + * Revision 1.40 2003/02/20 15:32:15 mroi + * big libdvdnav cleanup, quoting the ChangeLog: + * * some bugfixes + * * code cleanup + * * build process polishing + * * more sensible event order in get_next_block to ensure useful event delivery + * * VOBU level resume + * * fixed: seeking in a multiangle feature briefly showed the wrong angle + * * Revision 1.39 2002/10/23 11:38:09 mroi * port Stephen's comment fixing to avoid problems when syncing xine-lib's copy of * libdvdnav