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
00028
00029
00030 #include "config.h"
00031
00032 #include "dvdnav_internal.h"
00033 #include "dvdnav.h"
00034 #include "read_cache.h"
00035
00036 #include <time.h>
00037 #ifndef _WIN32
00038 #include <sys/time.h>
00039 #endif
00040
00041 #define READ_CACHE_CHUNKS 10
00042
00043
00044 #define ALIGNMENT 2048
00045
00046 #define READ_AHEAD_SIZE_MIN 4
00047 #define READ_AHEAD_SIZE_MAX 512
00048
00049 typedef struct read_cache_chunk_s {
00050 uint8_t *cache_buffer;
00051 uint8_t *cache_buffer_base;
00052 int32_t cache_start_sector;
00053 int32_t cache_read_count;
00054 size_t cache_block_count;
00055 size_t cache_malloc_size;
00056 int cache_valid;
00057 int usage_count;
00058 } read_cache_chunk_t;
00059
00060 struct read_cache_s {
00061 read_cache_chunk_t chunk[READ_CACHE_CHUNKS];
00062 int current;
00063 int freeing;
00064 uint32_t read_ahead_size;
00065 int read_ahead_incr;
00066 int last_sector;
00067 pthread_mutex_t lock;
00068
00069
00070 dvdnav_t *dvd_self;
00071 };
00072
00073
00074
00075
00076
00077 #ifdef __GNUC__
00078 # if READ_CACHE_TRACE
00079 # define dprintf(fmt, args...) fprintf(MSG_OUT, "libdvdnav: %s: "fmt, __func__ , ## args)
00080 # else
00081 # define dprintf(fmt, args...)
00082 # endif
00083 #else
00084 # if READ_CACHE_TRACE
00085 # define dprintf(fmt, ...) fprintf(MSG_OUT, "libdvdnav: %s: "fmt, __func__ , __VA_ARGS__)
00086 # else
00087 #ifdef _MSC_VER
00088 # define dprintf(fmt, str)
00089 #else
00090 # define dprintf(fmt, ...)
00091 #endif
00092 # endif
00093 #endif
00094
00095
00096 read_cache_t *dvdnav_read_cache_new(dvdnav_t* dvd_self) {
00097 read_cache_t *self;
00098 int i;
00099
00100 self = (read_cache_t *)malloc(sizeof(read_cache_t));
00101
00102 if(self) {
00103 self->current = 0;
00104 self->freeing = 0;
00105 self->dvd_self = dvd_self;
00106 self->last_sector = 0;
00107 self->read_ahead_size = READ_AHEAD_SIZE_MIN;
00108 self->read_ahead_incr = 0;
00109 pthread_mutex_init(&self->lock, NULL);
00110 dvdnav_read_cache_clear(self);
00111 for (i = 0; i < READ_CACHE_CHUNKS; i++) {
00112 self->chunk[i].cache_buffer = NULL;
00113 self->chunk[i].usage_count = 0;
00114 }
00115 }
00116
00117 return self;
00118 }
00119
00120 void dvdnav_read_cache_free(read_cache_t* self) {
00121 dvdnav_t *tmp;
00122 int i;
00123
00124 pthread_mutex_lock(&self->lock);
00125 self->freeing = 1;
00126 for (i = 0; i < READ_CACHE_CHUNKS; i++)
00127 if (self->chunk[i].cache_buffer && self->chunk[i].usage_count == 0) {
00128 free(self->chunk[i].cache_buffer_base);
00129 self->chunk[i].cache_buffer = NULL;
00130 }
00131 pthread_mutex_unlock(&self->lock);
00132
00133 for (i = 0; i < READ_CACHE_CHUNKS; i++)
00134 if (self->chunk[i].cache_buffer) return;
00135
00136
00137 tmp = self->dvd_self;
00138 pthread_mutex_destroy(&self->lock);
00139 free(self);
00140 free(tmp);
00141 }
00142
00143
00144 void dvdnav_read_cache_clear(read_cache_t *self) {
00145 int i;
00146
00147 if(!self)
00148 return;
00149
00150 pthread_mutex_lock(&self->lock);
00151 for (i = 0; i < READ_CACHE_CHUNKS; i++)
00152 self->chunk[i].cache_valid = 0;
00153 pthread_mutex_unlock(&self->lock);
00154 }
00155
00156
00157 void dvdnav_pre_cache_blocks(read_cache_t *self, int sector, size_t block_count) {
00158 int i, use;
00159
00160 if(!self)
00161 return;
00162
00163 if(!self->dvd_self->use_read_ahead)
00164 return;
00165
00166 pthread_mutex_lock(&self->lock);
00167
00168
00169 use = -1;
00170 for (i = 0; i < READ_CACHE_CHUNKS; i++)
00171 if (self->chunk[i].usage_count == 0 && self->chunk[i].cache_buffer &&
00172 self->chunk[i].cache_malloc_size >= block_count &&
00173 (use == -1 || self->chunk[use].cache_malloc_size > self->chunk[i].cache_malloc_size))
00174 use = i;
00175
00176 if (use == -1) {
00177
00178 for (i = 0; i < READ_CACHE_CHUNKS; i++)
00179 if (self->chunk[i].usage_count == 0 && self->chunk[i].cache_buffer &&
00180 (use == -1 || self->chunk[use].cache_malloc_size < self->chunk[i].cache_malloc_size))
00181 use = i;
00182 if (use >= 0) {
00183 self->chunk[use].cache_buffer_base = realloc(self->chunk[use].cache_buffer_base,
00184 block_count * DVD_VIDEO_LB_LEN + ALIGNMENT);
00185 self->chunk[use].cache_buffer =
00186 (uint8_t *)(((uintptr_t)self->chunk[use].cache_buffer_base & ~((uintptr_t)(ALIGNMENT - 1))) + ALIGNMENT);
00187 dprintf("pre_cache DVD read realloc happened\n");
00188 self->chunk[use].cache_malloc_size = block_count;
00189 } else {
00190
00191 for (i = 0; i < READ_CACHE_CHUNKS; i++)
00192 if (!self->chunk[i].cache_buffer) {
00193 use = i;
00194 break;
00195 }
00196 if (use >= 0) {
00197
00198
00199
00200
00201 self->chunk[i].cache_buffer_base =
00202 malloc((block_count > 500 ? block_count : 500) * DVD_VIDEO_LB_LEN + ALIGNMENT);
00203 self->chunk[i].cache_buffer =
00204 (uint8_t *)(((uintptr_t)self->chunk[i].cache_buffer_base & ~((uintptr_t)(ALIGNMENT - 1))) + ALIGNMENT);
00205 self->chunk[i].cache_malloc_size = block_count > 500 ? block_count : 500;
00206 dprintf("pre_cache DVD read malloc %d blocks\n",
00207 (block_count > 500 ? block_count : 500 ));
00208 }
00209 }
00210 }
00211
00212 if (use >= 0) {
00213 self->chunk[use].cache_start_sector = sector;
00214 self->chunk[use].cache_block_count = block_count;
00215 self->chunk[use].cache_read_count = 0;
00216 self->chunk[use].cache_valid = 1;
00217 self->current = use;
00218 } else {
00219 dprintf("pre_caching was impossible, no cache chunk available\n");
00220 }
00221 pthread_mutex_unlock(&self->lock);
00222 }
00223
00224 int dvdnav_read_cache_block(read_cache_t *self, int sector, size_t block_count, uint8_t **buf) {
00225 int i, use;
00226 int start;
00227 int size;
00228 int incr;
00229 uint8_t *read_ahead_buf;
00230 int32_t res;
00231
00232 if(!self)
00233 return 0;
00234
00235 use = -1;
00236
00237 if(self->dvd_self->use_read_ahead) {
00238
00239 read_cache_chunk_t cur = self->chunk[self->current];
00240 if (cur.cache_valid && sector >= cur.cache_start_sector &&
00241 sector <= (cur.cache_start_sector + cur.cache_read_count) &&
00242 sector + block_count <= cur.cache_start_sector + cur.cache_block_count)
00243 use = self->current;
00244 else
00245 for (i = 0; i < READ_CACHE_CHUNKS; i++)
00246 if (self->chunk[i].cache_valid &&
00247 sector >= self->chunk[i].cache_start_sector &&
00248 sector <= (self->chunk[i].cache_start_sector + self->chunk[i].cache_read_count) &&
00249 sector + block_count <= self->chunk[i].cache_start_sector + self->chunk[i].cache_block_count)
00250 use = i;
00251 }
00252
00253 if (use >= 0) {
00254 read_cache_chunk_t *chunk;
00255
00256
00257 if (sector == (self->last_sector + 1)) {
00258 if (self->read_ahead_incr < READ_AHEAD_SIZE_MAX)
00259 self->read_ahead_incr++;
00260 } else {
00261 self->read_ahead_size = READ_AHEAD_SIZE_MIN;
00262 self->read_ahead_incr = 0;
00263 }
00264 self->last_sector = sector;
00265
00266
00267
00268
00269
00270
00271 pthread_mutex_lock(&self->lock);
00272 chunk = &self->chunk[use];
00273 read_ahead_buf = chunk->cache_buffer + chunk->cache_read_count * DVD_VIDEO_LB_LEN;
00274 *buf = chunk->cache_buffer + (sector - chunk->cache_start_sector) * DVD_VIDEO_LB_LEN;
00275 chunk->usage_count++;
00276 pthread_mutex_unlock(&self->lock);
00277
00278 dprintf("libdvdnav: sector=%d, start_sector=%d, last_sector=%d\n", sector, chunk->cache_start_sector, chunk->cache_start_sector + chunk->cache_block_count);
00279
00280
00281 incr = self->read_ahead_incr >> 1;
00282 if ((self->read_ahead_size + incr) > READ_AHEAD_SIZE_MAX) {
00283 self->read_ahead_size = READ_AHEAD_SIZE_MAX;
00284 } else {
00285 self->read_ahead_size += incr;
00286 }
00287
00288
00289 start = chunk->cache_start_sector + chunk->cache_read_count;
00290 if (chunk->cache_read_count + self->read_ahead_size > chunk->cache_block_count) {
00291 size = chunk->cache_block_count - chunk->cache_read_count;
00292 } else {
00293 size = self->read_ahead_size;
00294
00295 if (sector >= chunk->cache_start_sector + chunk->cache_read_count + size)
00296 size = sector - chunk->cache_start_sector - chunk->cache_read_count;
00297 }
00298 dprintf("libdvdnav: read_ahead_size=%d, size=%d\n", self->read_ahead_size, size);
00299
00300 if (size)
00301 chunk->cache_read_count += DVDReadBlocks(self->dvd_self->file,
00302 start,
00303 size,
00304 read_ahead_buf);
00305
00306 res = DVD_VIDEO_LB_LEN * block_count;
00307
00308 } else {
00309
00310 if (self->dvd_self->use_read_ahead)
00311 dprintf("cache miss on sector %d\n", sector);
00312
00313 res = DVDReadBlocks(self->dvd_self->file,
00314 sector,
00315 block_count,
00316 *buf) * DVD_VIDEO_LB_LEN;
00317 }
00318
00319 return res;
00320
00321 }
00322
00323 dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf) {
00324 read_cache_t *cache;
00325 int i;
00326
00327 if (!self)
00328 return DVDNAV_STATUS_ERR;
00329
00330 cache = self->cache;
00331 if (!cache)
00332 return DVDNAV_STATUS_ERR;
00333
00334 pthread_mutex_lock(&cache->lock);
00335 for (i = 0; i < READ_CACHE_CHUNKS; i++) {
00336 if (cache->chunk[i].cache_buffer && buf >= cache->chunk[i].cache_buffer &&
00337 buf < cache->chunk[i].cache_buffer + cache->chunk[i].cache_malloc_size * DVD_VIDEO_LB_LEN) {
00338 cache->chunk[i].usage_count--;
00339 }
00340 }
00341 pthread_mutex_unlock(&cache->lock);
00342
00343 if (cache->freeing)
00344
00345 dvdnav_read_cache_free(cache);
00346
00347 return DVDNAV_STATUS_OK;
00348 }