|
MythTV
0.26-pre
|
00001 /* 00002 * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net> 00003 * 2001-2004 the dvdnav project 00004 * 00005 * This file is part of libdvdnav, a DVD navigation library. 00006 * 00007 * libdvdnav is free software; you can redistribute it and/or modify 00008 * it under the terms of the GNU General Public License as published by 00009 * the Free Software Foundation; either version 2 of the License, or 00010 * (at your option) any later version. 00011 * 00012 * libdvdnav is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU General Public License along 00018 * with libdvdnav; if not, write to the Free Software Foundation, Inc., 00019 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 00020 */ 00021 /* 00022 * There was a multithreaded read ahead cache in here for some time, but 00023 * it had only been used for a short time. If you want to have a look at it, 00024 * search the CVS attic. 00025 */ 00026 00027 #ifdef HAVE_CONFIG_H 00028 #include "config.h" 00029 #endif 00030 00031 #include <inttypes.h> 00032 #include <stdlib.h> 00033 #include <limits.h> 00034 #include <sys/time.h> 00035 #include <time.h> 00036 #include "dvdnav/dvdnav.h" 00037 #include <dvdread/nav_types.h> 00038 #include <dvdread/ifo_types.h> 00039 #include "remap.h" 00040 #include "vm/decoder.h" 00041 #include "vm/vm.h" 00042 #include "dvdnav_internal.h" 00043 #include "read_cache.h" 00044 00045 #define READ_CACHE_CHUNKS 10 00046 00047 /* all cache chunks must be memory aligned to allow use of raw devices */ 00048 #define ALIGNMENT 2048 00049 00050 #define READ_AHEAD_SIZE_MIN 4 00051 #define READ_AHEAD_SIZE_MAX 512 00052 00053 typedef struct read_cache_chunk_s { 00054 uint8_t *cache_buffer; 00055 uint8_t *cache_buffer_base; /* used in malloc and free for alignment */ 00056 int32_t cache_start_sector; /* -1 means cache invalid */ 00057 int32_t cache_read_count; /* this many sectors are already read */ 00058 size_t cache_block_count; /* this many sectors will go in this chunk */ 00059 size_t cache_malloc_size; 00060 int cache_valid; 00061 int usage_count; /* counts how many buffers where issued from this chunk */ 00062 } read_cache_chunk_t; 00063 00064 struct read_cache_s { 00065 read_cache_chunk_t chunk[READ_CACHE_CHUNKS]; 00066 int current; 00067 int freeing; /* is set to one when we are about to dispose the cache */ 00068 uint32_t read_ahead_size; 00069 int read_ahead_incr; 00070 int last_sector; 00071 pthread_mutex_t lock; 00072 00073 /* Bit of strange cross-linking going on here :) -- Gotta love C :) */ 00074 dvdnav_t *dvd_self; 00075 }; 00076 00077 /* 00078 #define READ_CACHE_TRACE 0 00079 */ 00080 00081 #ifdef __GNUC__ 00082 # if READ_CACHE_TRACE 00083 # define dprintf(fmt, args...) fprintf(MSG_OUT, "libdvdnav: %s: "fmt, __func__ , ## args) 00084 # else 00085 # define dprintf(fmt, args...) /* Nowt */ 00086 # endif 00087 #else 00088 # if READ_CACHE_TRACE 00089 # define dprintf(fmt, ...) fprintf(MSG_OUT, "libdvdnav: %s: "fmt, __func__ , __VA_ARGS__) 00090 # else 00091 #ifdef _MSC_VER 00092 # define dprintf(fmt, str) /* Nowt */ 00093 #else 00094 # define dprintf(fmt, ...) /* Nowt */ 00095 #endif /* _MSC_VER */ 00096 # endif 00097 #endif 00098 00099 00100 read_cache_t *dvdnav_read_cache_new(dvdnav_t* dvd_self) { 00101 read_cache_t *self; 00102 int i; 00103 00104 self = (read_cache_t *)malloc(sizeof(read_cache_t)); 00105 00106 if(self) { 00107 self->current = 0; 00108 self->freeing = 0; 00109 self->dvd_self = dvd_self; 00110 self->last_sector = 0; 00111 self->read_ahead_size = READ_AHEAD_SIZE_MIN; 00112 self->read_ahead_incr = 0; 00113 pthread_mutex_init(&self->lock, NULL); 00114 dvdnav_read_cache_clear(self); 00115 for (i = 0; i < READ_CACHE_CHUNKS; i++) { 00116 self->chunk[i].cache_buffer = NULL; 00117 self->chunk[i].usage_count = 0; 00118 } 00119 } 00120 00121 return self; 00122 } 00123 00124 void dvdnav_read_cache_free(read_cache_t* self) { 00125 dvdnav_t *tmp; 00126 int i; 00127 00128 pthread_mutex_lock(&self->lock); 00129 self->freeing = 1; 00130 for (i = 0; i < READ_CACHE_CHUNKS; i++) 00131 if (self->chunk[i].cache_buffer && self->chunk[i].usage_count == 0) { 00132 free(self->chunk[i].cache_buffer_base); 00133 self->chunk[i].cache_buffer = NULL; 00134 } 00135 pthread_mutex_unlock(&self->lock); 00136 00137 for (i = 0; i < READ_CACHE_CHUNKS; i++) 00138 if (self->chunk[i].cache_buffer) return; 00139 00140 /* all buffers returned, free everything */ 00141 tmp = self->dvd_self; 00142 pthread_mutex_destroy(&self->lock); 00143 free(self); 00144 free(tmp); 00145 } 00146 00147 /* This function MUST be called whenever self->file changes. */ 00148 void dvdnav_read_cache_clear(read_cache_t *self) { 00149 int i; 00150 00151 if(!self) 00152 return; 00153 00154 pthread_mutex_lock(&self->lock); 00155 for (i = 0; i < READ_CACHE_CHUNKS; i++) 00156 self->chunk[i].cache_valid = 0; 00157 pthread_mutex_unlock(&self->lock); 00158 } 00159 00160 /* This function is called just after reading the NAV packet. */ 00161 void dvdnav_pre_cache_blocks(read_cache_t *self, int sector, size_t block_count) { 00162 int i, use; 00163 00164 if(!self) 00165 return; 00166 00167 if(!self->dvd_self->use_read_ahead) 00168 return; 00169 00170 pthread_mutex_lock(&self->lock); 00171 00172 /* find a free cache chunk that best fits the required size */ 00173 use = -1; 00174 for (i = 0; i < READ_CACHE_CHUNKS; i++) 00175 if (self->chunk[i].usage_count == 0 && self->chunk[i].cache_buffer && 00176 self->chunk[i].cache_malloc_size >= block_count && 00177 (use == -1 || self->chunk[use].cache_malloc_size > self->chunk[i].cache_malloc_size)) 00178 use = i; 00179 00180 if (use == -1) { 00181 /* we haven't found a cache chunk, so we try to reallocate an existing one */ 00182 for (i = 0; i < READ_CACHE_CHUNKS; i++) 00183 if (self->chunk[i].usage_count == 0 && self->chunk[i].cache_buffer && 00184 (use == -1 || self->chunk[use].cache_malloc_size < self->chunk[i].cache_malloc_size)) 00185 use = i; 00186 if (use >= 0) { 00187 self->chunk[use].cache_buffer_base = realloc(self->chunk[use].cache_buffer_base, 00188 block_count * DVD_VIDEO_LB_LEN + ALIGNMENT); 00189 self->chunk[use].cache_buffer = 00190 (uint8_t *)(((uintptr_t)self->chunk[use].cache_buffer_base & ~((uintptr_t)(ALIGNMENT - 1))) + ALIGNMENT); 00191 dprintf("pre_cache DVD read realloc happened\n"); 00192 self->chunk[use].cache_malloc_size = block_count; 00193 } else { 00194 /* we still haven't found a cache chunk, let's allocate a new one */ 00195 for (i = 0; i < READ_CACHE_CHUNKS; i++) 00196 if (!self->chunk[i].cache_buffer) { 00197 use = i; 00198 break; 00199 } 00200 if (use >= 0) { 00201 /* We start with a sensible figure for the first malloc of 500 blocks. 00202 * Some DVDs I have seen venture to 450 blocks. 00203 * This is so that fewer realloc's happen if at all. 00204 */ 00205 self->chunk[i].cache_buffer_base = 00206 malloc((block_count > 500 ? block_count : 500) * DVD_VIDEO_LB_LEN + ALIGNMENT); 00207 self->chunk[i].cache_buffer = 00208 (uint8_t *)(((uintptr_t)self->chunk[i].cache_buffer_base & ~((uintptr_t)(ALIGNMENT - 1))) + ALIGNMENT); 00209 self->chunk[i].cache_malloc_size = block_count > 500 ? block_count : 500; 00210 dprintf("pre_cache DVD read malloc %d blocks\n", 00211 (block_count > 500 ? block_count : 500 )); 00212 } 00213 } 00214 } 00215 00216 if (use >= 0) { 00217 self->chunk[use].cache_start_sector = sector; 00218 self->chunk[use].cache_block_count = block_count; 00219 self->chunk[use].cache_read_count = 0; 00220 self->chunk[use].cache_valid = 1; 00221 self->current = use; 00222 } else { 00223 dprintf("pre_caching was impossible, no cache chunk available\n"); 00224 } 00225 pthread_mutex_unlock(&self->lock); 00226 } 00227 00228 int dvdnav_read_cache_block(read_cache_t *self, int sector, size_t block_count, uint8_t **buf) { 00229 int i, use; 00230 int start; 00231 int size; 00232 int incr; 00233 uint8_t *read_ahead_buf; 00234 int32_t res; 00235 00236 if(!self) 00237 return 0; 00238 00239 use = -1; 00240 00241 if(self->dvd_self->use_read_ahead) { 00242 /* first check, if sector is in current chunk */ 00243 read_cache_chunk_t cur = self->chunk[self->current]; 00244 if (cur.cache_valid && sector >= cur.cache_start_sector && 00245 sector <= (cur.cache_start_sector + cur.cache_read_count) && 00246 sector + block_count <= cur.cache_start_sector + cur.cache_block_count) 00247 use = self->current; 00248 else 00249 for (i = 0; i < READ_CACHE_CHUNKS; i++) 00250 if (self->chunk[i].cache_valid && 00251 sector >= self->chunk[i].cache_start_sector && 00252 sector <= (self->chunk[i].cache_start_sector + self->chunk[i].cache_read_count) && 00253 sector + block_count <= self->chunk[i].cache_start_sector + self->chunk[i].cache_block_count) 00254 use = i; 00255 } 00256 00257 if (use >= 0) { 00258 read_cache_chunk_t *chunk; 00259 00260 /* Increment read-ahead size if sector follows the last sector */ 00261 if (sector == (self->last_sector + 1)) { 00262 if (self->read_ahead_incr < READ_AHEAD_SIZE_MAX) 00263 self->read_ahead_incr++; 00264 } else { 00265 self->read_ahead_size = READ_AHEAD_SIZE_MIN; 00266 self->read_ahead_incr = 0; 00267 } 00268 self->last_sector = sector; 00269 00270 /* The following resources need to be protected by a mutex : 00271 * self->chunk[*].cache_buffer 00272 * self->chunk[*].cache_malloc_size 00273 * self->chunk[*].usage_count 00274 */ 00275 pthread_mutex_lock(&self->lock); 00276 chunk = &self->chunk[use]; 00277 read_ahead_buf = chunk->cache_buffer + chunk->cache_read_count * DVD_VIDEO_LB_LEN; 00278 *buf = chunk->cache_buffer + (sector - chunk->cache_start_sector) * DVD_VIDEO_LB_LEN; 00279 chunk->usage_count++; 00280 pthread_mutex_unlock(&self->lock); 00281 00282 dprintf("libdvdnav: sector=%d, start_sector=%d, last_sector=%d\n", sector, chunk->cache_start_sector, chunk->cache_start_sector + chunk->cache_block_count); 00283 00284 /* read_ahead_size */ 00285 incr = self->read_ahead_incr >> 1; 00286 if ((self->read_ahead_size + incr) > READ_AHEAD_SIZE_MAX) { 00287 self->read_ahead_size = READ_AHEAD_SIZE_MAX; 00288 } else { 00289 self->read_ahead_size += incr; 00290 } 00291 00292 /* real read size */ 00293 start = chunk->cache_start_sector + chunk->cache_read_count; 00294 if (chunk->cache_read_count + self->read_ahead_size > chunk->cache_block_count) { 00295 size = chunk->cache_block_count - chunk->cache_read_count; 00296 } else { 00297 size = self->read_ahead_size; 00298 /* ensure that the sector we want will be read */ 00299 if (sector >= chunk->cache_start_sector + chunk->cache_read_count + size) 00300 size = sector - chunk->cache_start_sector - chunk->cache_read_count; 00301 } 00302 dprintf("libdvdnav: read_ahead_size=%d, size=%d\n", self->read_ahead_size, size); 00303 00304 if (size) 00305 chunk->cache_read_count += DVDReadBlocks(self->dvd_self->file, 00306 start, 00307 size, 00308 read_ahead_buf); 00309 00310 res = DVD_VIDEO_LB_LEN * block_count; 00311 00312 } else { 00313 00314 if (self->dvd_self->use_read_ahead) 00315 dprintf("cache miss on sector %d\n", sector); 00316 00317 res = DVDReadBlocks(self->dvd_self->file, 00318 sector, 00319 block_count, 00320 *buf) * DVD_VIDEO_LB_LEN; 00321 } 00322 00323 return res; 00324 00325 } 00326 00327 dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf) { 00328 read_cache_t *cache; 00329 int i; 00330 00331 if (!self) 00332 return DVDNAV_STATUS_ERR; 00333 00334 cache = self->cache; 00335 if (!cache) 00336 return DVDNAV_STATUS_ERR; 00337 00338 pthread_mutex_lock(&cache->lock); 00339 for (i = 0; i < READ_CACHE_CHUNKS; i++) { 00340 if (cache->chunk[i].cache_buffer && buf >= cache->chunk[i].cache_buffer && 00341 buf < cache->chunk[i].cache_buffer + cache->chunk[i].cache_malloc_size * DVD_VIDEO_LB_LEN) { 00342 cache->chunk[i].usage_count--; 00343 } 00344 } 00345 pthread_mutex_unlock(&cache->lock); 00346 00347 if (cache->freeing) 00348 /* when we want to dispose the cache, try freeing it now */ 00349 dvdnav_read_cache_free(cache); 00350 00351 return DVDNAV_STATUS_OK; 00352 }
1.7.6.1