00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
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
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;
00056 int32_t cache_start_sector;
00057 int32_t cache_read_count;
00058 size_t cache_block_count;
00059 size_t cache_malloc_size;
00060 int cache_valid;
00061 int usage_count;
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;
00068 uint32_t read_ahead_size;
00069 int read_ahead_incr;
00070 int last_sector;
00071 pthread_mutex_t lock;
00072
00073
00074 dvdnav_t *dvd_self;
00075 };
00076
00077
00078
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...)
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)
00093 #else
00094 # define dprintf(fmt, ...)
00095 #endif
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
00141 tmp = self->dvd_self;
00142 pthread_mutex_destroy(&self->lock);
00143 free(self);
00144 free(tmp);
00145 }
00146
00147
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
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
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
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
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
00202
00203
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
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
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
00271
00272
00273
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
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
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
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
00349 dvdnav_read_cache_free(cache);
00350
00351 return DVDNAV_STATUS_OK;
00352 }