view read_cache.c @ 40:a049c3753f32 src

Added some packaging patches from Philipp Matthias Hahn <pmhahn@titan.lahn.de> and an initial (non-working) multi-threaded read-ahead cache.
author richwareham
date Sat, 01 Jun 2002 12:00:40 +0000
parents 832ca4921e04
children 50e0855a2017
line wrap: on
line source

/* 
 * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
 * 
 * This file is part of libdvdnav, a DVD navigation library.
 * 
 * libdvdnav is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * libdvdnav is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
 *
 * $Id$
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "dvdnav.h"
#include "read_cache.h"
#include <pthread.h>

/* Read-ahead cache structure. */
#if _MULTITHREAD_

/* For the multithreaded cache, the cache is a ring buffer + writing
 * thread that continuously reads data into the buffer until it is
 * full or the 'upper-bound' has been reached.
 */

#define CACHE_BUFFER_SIZE 1024 /* Cache this number of blocks at a time */

struct read_cache_s {
  pthread_mutex_t cache_lock;
  pthread_t read_thread;
   
  /* Buffer */
  uint8_t   *buffer;
  
  /* Where we are currently 'reading' from (blocks) */
  int32_t    pos;
  /* Location of 'start-point' in buffer */
  int32_t    start;
  /* How much is in buffer? */
  int        size;
  
  /* Bit of strange cross-linking going on here :) -- Gotta love C :) */
  dvdnav_t    *dvd_self;
};

#else
struct read_cache_s {
  /* Read-ahead cache. */
  uint8_t      *cache_buffer;
  int32_t      cache_start_sector; /* -1 means cache invalid */
  size_t       cache_block_count;
  size_t       cache_malloc_size;
  int          cache_valid;

  /* Bit of strange cross-linking going on here :) -- Gotta love C :) */
  dvdnav_t    *dvd_self;
};
#endif

#if _MULTITHREAD_

void * read_cache_read_thread (void * this_gen) {
  int cont = 1;
  int32_t diff, start;
  uint32_t s,c;
  uint8_t *at;
  read_cache_t *self = (read_cache_t*)this_gen;

  while(cont) {
   
    pthread_mutex_lock(&self->cache_lock);
    if((self->size >= 0) && (self->size < CACHE_BUFFER_SIZE/3)) {
      printf("Rad thread -- current state pos=%i, size=%i\n", self->pos, self->size);
      /* Attempt to fill buffer */
      diff = CACHE_BUFFER_SIZE - self->size;
      if(diff > 0) {
	start = (self->start + self->size) % CACHE_BUFFER_SIZE;

	c = 0; s = self->pos + self->size;
	if(start != 0) {
	  s = self->pos + self->size; c = CACHE_BUFFER_SIZE - start;
	  at = self->buffer + (start * DVD_VIDEO_LB_LEN);
	  pthread_mutex_unlock(&self->cache_lock);
	  DVDReadBlocks( self->dvd_self->file, s,c,at);
	  pthread_mutex_lock(&self->cache_lock);
	}
	
	s += c; c = diff - c;
	at = self->buffer;
	pthread_mutex_unlock(&self->cache_lock);
	DVDReadBlocks( self->dvd_self->file, s,c,at);
	pthread_mutex_lock(&self->cache_lock);

	// end = (self->start + self->size + diff) % CACHE_BUFFER_SIZE;
	/*if(end > start) {
	  printf("Reading %i from sector %i\n",diff, self->pos+self->size);
	  pthread_mutex_unlock(&self->cache_lock);
	  DVDReadBlocks( self->dvd_self->file, self->pos + self->size, diff, 
			 self->buffer + (start * DVD_VIDEO_LB_LEN));
	  pthread_mutex_lock(&self->cache_lock);
	} else {*/
	/*
	  if(start != CACHE_BUFFER_SIZE-1) {
	    printf(" -- Reading %i from %i to %i ", CACHE_BUFFER_SIZE - start,
		   self->pos + self->size, start);
	    s = self->pos + self->size; c = CACHE_BUFFER_SIZE - start;
	    at = self->buffer + (start * DVD_VIDEO_LB_LEN);
	    pthread_mutex_unlock(&self->cache_lock);
	    DVDReadBlocks( self->dvd_self->file, s,c,at);
	    pthread_mutex_lock(&self->cache_lock);
	  }
	  if(end != 0) {
	    printf(" -- Reading %i from %i to %i ", diff - CACHE_BUFFER_SIZE + start,
		   self->pos + self->size + CACHE_BUFFER_SIZE - start,
		   0);
	    s = self->pos + self->size + CACHE_BUFFER_SIZE - start;
	    c = diff - CACHE_BUFFER_SIZE + start; at = self->buffer;
	    pthread_mutex_unlock(&self->cache_lock);
	    DVDReadBlocks( self->dvd_self->file, s, c, at);
	    pthread_mutex_lock(&self->cache_lock);
	  }
	  printf("::\n");
	  */
	// }

	self->size += diff;
      }
    }
   
    pthread_mutex_unlock(&self->cache_lock);
    cont = (self->buffer != NULL);
    usleep(100);
  }

  return NULL;
}
					   
read_cache_t *dvdnav_read_cache_new(dvdnav_t* dvd_self) {
  read_cache_t *me;

  me = (read_cache_t*)malloc(sizeof(struct read_cache_s));

  if(me) {
    int err;
    
    me->dvd_self = dvd_self;
    me->buffer = (uint8_t*)malloc(CACHE_BUFFER_SIZE * DVD_VIDEO_LB_LEN);
    me->start = 0;
    me->pos = 0;
    me->size = -1;

    /* Initialise the mutex */
    pthread_mutex_init(&me->cache_lock, NULL);

    if ((err = pthread_create (&me->read_thread,
			       NULL, read_cache_read_thread, me)) != 0) {
      fprintf(stderr,"read_cache: can't create new thread (%s)\n",strerror(err));
    }
  }
  
  return me;
}

void dvdnav_read_cache_free(read_cache_t* self) {
  pthread_mutex_lock(&self->cache_lock);
		   
  if(self->buffer) {
    free(self->buffer);
    self->buffer = NULL;
    self->size = -2;
  }

  pthread_mutex_unlock(&self->cache_lock);

  pthread_join(self->read_thread, NULL);
  
  pthread_mutex_destroy(&self->cache_lock);

  free(self);
}

/* This function MUST be called whenever self->file changes. */
void dvdnav_read_cache_clear(read_cache_t *self) {
  if(!self)
   return;
  
  pthread_mutex_lock(&self->cache_lock);
  self->size = 0;
  self->start = 0;
  self->pos = 0;
  pthread_mutex_unlock(&self->cache_lock);
}

/* This function is called just after reading the NAV packet. */
void dvdnav_pre_cache_blocks(read_cache_t *self, int sector, size_t block_count) {
  if(!self)
   return;
  
  if(!self->dvd_self->use_read_ahead) {
    return;
  }
 
  pthread_mutex_lock(&self->cache_lock);
  printf("Requested pre-cache.\n");
  
  /* Are the contents of the buffer in any way relevant? */
  if((sector < self->pos+self->size) && (sector >= self->pos)) {
    int32_t diff = sector - self->pos;

    printf("Contents relevant ... adjusting by %i\n", diff);
    self->size -= diff;
    self->start = (self->start + diff) % CACHE_BUFFER_SIZE;
  } else {
    /* Flush the cache as its not much use */
    pthread_mutex_unlock(&self->cache_lock);
    printf("Contents irrelevent... flushing\n");
    dvdnav_read_cache_clear(self);
    pthread_mutex_lock(&self->cache_lock);
  }
  
  self->pos = sector;

  pthread_mutex_unlock(&self->cache_lock);
}

/* This function will do the cache read once implemented */
int dvdnav_read_cache_block( read_cache_t *self, int sector, size_t block_count, uint8_t *buf) {
  int result;
 
  if(!self)
   return 0;

  pthread_mutex_lock(&self->cache_lock);
  if((self->size > 0) && (sector >= self->pos) && (sector + block_count <= self->pos + self->size)) {
    /* Hit */

    //printf("Read from %i -> +%i (buffer pos=%i, size=%i)... ", sector, block_count,
	//   self->pos, self->size);
    
    /* Drop any skipped blocks */
     {
      int diff = sector - self->pos;

      self->pos += diff;
      self->size -= diff;
      self->start = (self->start + diff) % CACHE_BUFFER_SIZE;
     }

    if(self->start + block_count <= CACHE_BUFFER_SIZE) {
      // printf("Single read\n");
      memcpy(buf, self->buffer + ((self->start) * DVD_VIDEO_LB_LEN), block_count * DVD_VIDEO_LB_LEN);
      self->start = (self->start + block_count) % CACHE_BUFFER_SIZE;
      self->pos += block_count;
      self->size --;
      pthread_mutex_unlock(&self->cache_lock);

      return (int)block_count;
    } else {
      int32_t boundary = CACHE_BUFFER_SIZE - self->start - 1;

      printf("************** Multiple read\n");
      memcpy(buf, self->buffer + ((self->start) * DVD_VIDEO_LB_LEN), boundary  * DVD_VIDEO_LB_LEN);
      memcpy(buf + (boundary  * DVD_VIDEO_LB_LEN), self->buffer, (block_count-boundary) * DVD_VIDEO_LB_LEN);
      self->start = (self->start + block_count) % CACHE_BUFFER_SIZE;
      self->pos += block_count;
      self->size --;
      pthread_mutex_unlock(&self->cache_lock);

      return (int)block_count;      
    }
  } else {
    /* Miss */

    fprintf(stderr,"DVD read cache miss! sector=%d\n", sector); 
    result = DVDReadBlocks( self->dvd_self->file, sector, block_count, buf);
    self->pos = sector + block_count;
    usleep(200);
    pthread_mutex_unlock(&self->cache_lock);
    return result;
  }
  
  /* Should never get here */
  return 0;
}

#else

read_cache_t *dvdnav_read_cache_new(dvdnav_t* dvd_self) {
  read_cache_t *me;

  me = (read_cache_t*)malloc(sizeof(struct read_cache_s));

  if(me) {
    me->dvd_self = dvd_self;

    dvdnav_read_cache_clear(me);
    me->cache_buffer = NULL;
  }
  
  /* this->cache_start_sector = -1;
  this->cache_block_count = 0;
  this->cache_valid = 0; */

  return me;
}

void dvdnav_read_cache_free(read_cache_t* self) {
  if(self->cache_buffer) {
    free(self->cache_buffer);
    self->cache_buffer = NULL;
  }

  free(self);
}

/* This function MUST be called whenever self->file changes. */
void dvdnav_read_cache_clear(read_cache_t *self) {
  if(!self)
   return;
  
  self->cache_start_sector = -1;
  self->cache_valid = 0;
}

/* This function is called just after reading the NAV packet. */
void dvdnav_pre_cache_blocks(read_cache_t *self, int sector, size_t block_count) {
  int result;
 
  if(!self)
   return;
  
  if(!self->dvd_self->use_read_ahead) {
    self->cache_valid = 0;
    self->cache_start_sector = -1;
    return;
  }
  
  if (self->cache_buffer) {
    if( block_count > self->cache_malloc_size) {
      self->cache_buffer = realloc(self->cache_buffer, block_count * DVD_VIDEO_LB_LEN);
      self->cache_malloc_size = block_count;
    } 
  } else {
    self->cache_buffer = malloc(block_count * DVD_VIDEO_LB_LEN);
    self->cache_malloc_size = block_count;
  }
  self->cache_start_sector = sector;
  self->cache_block_count = block_count;
  result = DVDReadBlocks( self->dvd_self->file, sector, block_count, self->cache_buffer);
  self->cache_valid = 1;
}

/* This function will do the cache read once implemented */
int dvdnav_read_cache_block( read_cache_t *self, int sector, size_t block_count, uint8_t *buf) {
  int result;
 
  if(!self)
   return 0;

  if(self->cache_valid && self->dvd_self->use_read_ahead) {
    if (self->cache_start_sector != -1 ) {
      if ((sector >= self->cache_start_sector) && 
	  (sector < self->cache_start_sector + self->cache_block_count)) {
	memcpy(buf, self->cache_buffer + ((off_t)((off_t)sector - (off_t)self->cache_start_sector) * DVD_VIDEO_LB_LEN), DVD_VIDEO_LB_LEN);
	return DVD_VIDEO_LB_LEN;
      }
    }
  } else {
    result = DVDReadBlocks( self->dvd_self->file, sector, block_count, buf);
    return result;
  }
  
  fprintf(stderr,"DVD read cache miss! sector=%d, start=%d\n", sector, self->cache_start_sector); 
  result = DVDReadBlocks( self->dvd_self->file, sector, block_count, buf);
  return result;
}


#endif