diff options
author | Teravus Ovares | 2009-01-22 01:33:46 +0000 |
---|---|---|
committer | Teravus Ovares | 2009-01-22 01:33:46 +0000 |
commit | c6154c89873da5d9fa017c75cbfad8e5f50da8f0 (patch) | |
tree | b4bebda4fce471815a7880837b475271a3c47ec8 | |
parent | * refactor: Extract caps related code from scene and put into a region module (diff) | |
download | opensim-SC-c6154c89873da5d9fa017c75cbfad8e5f50da8f0.zip opensim-SC-c6154c89873da5d9fa017c75cbfad8e5f50da8f0.tar.gz opensim-SC-c6154c89873da5d9fa017c75cbfad8e5f50da8f0.tar.bz2 opensim-SC-c6154c89873da5d9fa017c75cbfad8e5f50da8f0.tar.xz |
* Add File cache for j2k layer decodes. This will make it so that the server will decode the j2k stream once and cache it to disk so that the cache is saved across sim restarts.
-rw-r--r-- | OpenSim/Region/Environment/Modules/Agent/TextureSender/J2KDecoderModule.cs | 342 |
1 files changed, 304 insertions, 38 deletions
diff --git a/OpenSim/Region/Environment/Modules/Agent/TextureSender/J2KDecoderModule.cs b/OpenSim/Region/Environment/Modules/Agent/TextureSender/J2KDecoderModule.cs index dc46dc6..518766e 100644 --- a/OpenSim/Region/Environment/Modules/Agent/TextureSender/J2KDecoderModule.cs +++ b/OpenSim/Region/Environment/Modules/Agent/TextureSender/J2KDecoderModule.cs | |||
@@ -26,13 +26,16 @@ | |||
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.IO; | ||
29 | using System.Reflection; | 30 | using System.Reflection; |
31 | using System.Text; | ||
30 | using System.Threading; | 32 | using System.Threading; |
31 | using System.Collections.Generic; | 33 | using System.Collections.Generic; |
32 | using log4net; | 34 | using log4net; |
33 | using Nini.Config; | 35 | using Nini.Config; |
34 | using OpenMetaverse; | 36 | using OpenMetaverse; |
35 | using OpenMetaverse.Imaging; | 37 | using OpenMetaverse.Imaging; |
38 | using OpenSim.Framework; | ||
36 | using OpenSim.Region.Environment.Interfaces; | 39 | using OpenSim.Region.Environment.Interfaces; |
37 | using OpenSim.Region.Environment.Scenes; | 40 | using OpenSim.Region.Environment.Scenes; |
38 | 41 | ||
@@ -50,12 +53,19 @@ namespace OpenSim.Region.Environment.Modules.Agent.TextureSender | |||
50 | /// </summary> | 53 | /// </summary> |
51 | private readonly Dictionary<UUID, OpenJPEG.J2KLayerInfo[]> m_cacheddecode = new Dictionary<UUID, OpenJPEG.J2KLayerInfo[]>(); | 54 | private readonly Dictionary<UUID, OpenJPEG.J2KLayerInfo[]> m_cacheddecode = new Dictionary<UUID, OpenJPEG.J2KLayerInfo[]>(); |
52 | private bool OpenJpegFail = false; | 55 | private bool OpenJpegFail = false; |
56 | private readonly string CacheFolder = Util.dataDir() + "/j2kDecodeCache"; | ||
57 | private readonly J2KDecodeFileCache fCache; | ||
53 | 58 | ||
54 | /// <summary> | 59 | /// <summary> |
55 | /// List of client methods to notify of results of decode | 60 | /// List of client methods to notify of results of decode |
56 | /// </summary> | 61 | /// </summary> |
57 | private readonly Dictionary<UUID, List<DecodedCallback>> m_notifyList = new Dictionary<UUID, List<DecodedCallback>>(); | 62 | private readonly Dictionary<UUID, List<DecodedCallback>> m_notifyList = new Dictionary<UUID, List<DecodedCallback>>(); |
58 | 63 | ||
64 | public J2KDecoderModule() | ||
65 | { | ||
66 | fCache = new J2KDecodeFileCache(CacheFolder); | ||
67 | } | ||
68 | |||
59 | public void Initialise(Scene scene, IConfigSource source) | 69 | public void Initialise(Scene scene, IConfigSource source) |
60 | { | 70 | { |
61 | scene.RegisterModuleInterface<IJ2KDecoder>(this); | 71 | scene.RegisterModuleInterface<IJ2KDecoder>(this); |
@@ -151,61 +161,68 @@ namespace OpenSim.Region.Environment.Modules.Agent.TextureSender | |||
151 | 161 | ||
152 | if (!OpenJpegFail) | 162 | if (!OpenJpegFail) |
153 | { | 163 | { |
154 | try | 164 | if (!fCache.TryLoadCacheForAsset(AssetId, out layers)) |
155 | { | 165 | { |
156 | 166 | try | |
157 | AssetTexture texture = new AssetTexture(AssetId, j2kdata); | ||
158 | if (texture.DecodeLayerBoundaries()) | ||
159 | { | 167 | { |
160 | bool sane = true; | ||
161 | 168 | ||
162 | // Sanity check all of the layers | 169 | AssetTexture texture = new AssetTexture(AssetId, j2kdata); |
163 | for (int i = 0; i < texture.LayerInfo.Length; i++) | 170 | if (texture.DecodeLayerBoundaries()) |
164 | { | 171 | { |
165 | if (texture.LayerInfo[i].End > texture.AssetData.Length) | 172 | bool sane = true; |
173 | |||
174 | // Sanity check all of the layers | ||
175 | for (int i = 0; i < texture.LayerInfo.Length; i++) | ||
166 | { | 176 | { |
167 | sane = false; | 177 | if (texture.LayerInfo[i].End > texture.AssetData.Length) |
168 | break; | 178 | { |
179 | sane = false; | ||
180 | break; | ||
181 | } | ||
169 | } | 182 | } |
170 | } | ||
171 | 183 | ||
172 | if (sane) | 184 | if (sane) |
173 | { | 185 | { |
174 | layers = texture.LayerInfo; | 186 | layers = texture.LayerInfo; |
187 | fCache.SaveFileCacheForAsset(AssetId, layers); | ||
188 | |||
189 | |||
190 | // Write out decode time | ||
191 | m_log.InfoFormat("[J2KDecoderModule]: {0} Decode Time: {1}", System.Environment.TickCount - DecodeTime, | ||
192 | AssetId); | ||
193 | |||
194 | } | ||
195 | else | ||
196 | { | ||
197 | m_log.WarnFormat( | ||
198 | "[J2KDecoderModule]: JPEG2000 texture decoding succeeded, but sanity check failed for {0}", | ||
199 | AssetId); | ||
200 | } | ||
175 | } | 201 | } |
202 | |||
176 | else | 203 | else |
177 | { | 204 | { |
178 | m_log.WarnFormat( | 205 | m_log.WarnFormat("[J2KDecoderModule]: JPEG2000 texture decoding failed for {0}", AssetId); |
179 | "[J2KDecoderModule]: JPEG2000 texture decoding succeeded, but sanity check failed for {0}", | ||
180 | AssetId); | ||
181 | } | 206 | } |
207 | texture = null; // dereference and dispose of ManagedImage | ||
182 | } | 208 | } |
183 | 209 | catch (DllNotFoundException) | |
184 | else | ||
185 | { | 210 | { |
186 | m_log.WarnFormat("[J2KDecoderModule]: JPEG2000 texture decoding failed for {0}", AssetId); | 211 | m_log.Error( |
212 | "[J2KDecoderModule]: OpenJpeg is not installed properly. Decoding disabled! This will slow down texture performance! Often times this is because of an old version of GLIBC. You must have version 2.4 or above!"); | ||
213 | OpenJpegFail = true; | ||
214 | } | ||
215 | catch (Exception ex) | ||
216 | { | ||
217 | m_log.WarnFormat( | ||
218 | "[J2KDecoderModule]: JPEG2000 texture decoding threw an exception for {0}, {1}", | ||
219 | AssetId, ex); | ||
187 | } | 220 | } |
188 | texture = null; // dereference and dispose of ManagedImage | ||
189 | } | ||
190 | catch (DllNotFoundException) | ||
191 | { | ||
192 | m_log.Error( | ||
193 | "[J2KDecoderModule]: OpenJpeg is not installed properly. Decoding disabled! This will slow down texture performance! Often times this is because of an old version of GLIBC. You must have version 2.4 or above!"); | ||
194 | OpenJpegFail = true; | ||
195 | } | ||
196 | catch (Exception ex) | ||
197 | { | ||
198 | m_log.WarnFormat("[J2KDecoderModule]: JPEG2000 texture decoding threw an exception for {0}, {1}", | ||
199 | AssetId, ex); | ||
200 | } | 221 | } |
222 | |||
201 | } | 223 | } |
202 | 224 | ||
203 | if (!OpenJpegFail) | 225 | |
204 | { | ||
205 | // Write out decode time | ||
206 | m_log.InfoFormat("[J2KDecoderModule]: {0} Decode Time: {1}", System.Environment.TickCount - DecodeTime, | ||
207 | AssetId); | ||
208 | } | ||
209 | // Cache Decoded layers | 226 | // Cache Decoded layers |
210 | lock (m_cacheddecode) | 227 | lock (m_cacheddecode) |
211 | { | 228 | { |
@@ -213,6 +230,8 @@ namespace OpenSim.Region.Environment.Modules.Agent.TextureSender | |||
213 | 230 | ||
214 | } | 231 | } |
215 | 232 | ||
233 | |||
234 | |||
216 | // Notify Interested Parties | 235 | // Notify Interested Parties |
217 | lock (m_notifyList) | 236 | lock (m_notifyList) |
218 | { | 237 | { |
@@ -228,4 +247,251 @@ namespace OpenSim.Region.Environment.Modules.Agent.TextureSender | |||
228 | } | 247 | } |
229 | } | 248 | } |
230 | } | 249 | } |
250 | |||
251 | public class J2KDecodeFileCache | ||
252 | { | ||
253 | private readonly string m_cacheDecodeFolder; | ||
254 | private bool enabled = true; | ||
255 | |||
256 | private static readonly ILog m_log | ||
257 | = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
258 | |||
259 | public J2KDecodeFileCache(string pFolder) | ||
260 | { | ||
261 | m_cacheDecodeFolder = pFolder; | ||
262 | if (!Directory.Exists(pFolder)) | ||
263 | { | ||
264 | Createj2KCacheFolder(pFolder); | ||
265 | } | ||
266 | |||
267 | } | ||
268 | |||
269 | public bool SaveFileCacheForAsset(UUID AssetId, OpenJPEG.J2KLayerInfo[] Layers) | ||
270 | { | ||
271 | if (Layers.Length > 0 && enabled) | ||
272 | { | ||
273 | FileStream fsCache = | ||
274 | new FileStream(String.Format("{0}/{1}", m_cacheDecodeFolder, FileNameFromAssetId(AssetId)), | ||
275 | FileMode.Create); | ||
276 | StreamWriter fsSWCache = new StreamWriter(fsCache); | ||
277 | StringBuilder stringResult = new StringBuilder(); | ||
278 | string strEnd = "\n"; | ||
279 | for (int i = 0; i < Layers.Length; i++) | ||
280 | { | ||
281 | if (i == (Layers.Length - 1)) | ||
282 | strEnd = ""; | ||
283 | |||
284 | stringResult.AppendFormat("{0}|{1}|{2}{3}", Layers[i].Start, Layers[i].End, Layers[i].Size, strEnd); | ||
285 | } | ||
286 | fsSWCache.Write(stringResult.ToString()); | ||
287 | fsSWCache.Close(); | ||
288 | fsSWCache.Dispose(); | ||
289 | fsCache.Dispose(); | ||
290 | return true; | ||
291 | } | ||
292 | |||
293 | |||
294 | return false; | ||
295 | } | ||
296 | |||
297 | |||
298 | |||
299 | public bool TryLoadCacheForAsset(UUID AssetId, out OpenJPEG.J2KLayerInfo[] Layers) | ||
300 | { | ||
301 | string filename = String.Format("{0}/{1}", m_cacheDecodeFolder, FileNameFromAssetId(AssetId)); | ||
302 | Layers = new OpenJPEG.J2KLayerInfo[0]; | ||
303 | |||
304 | if (!File.Exists(filename)) | ||
305 | return false; | ||
306 | |||
307 | if (!enabled) | ||
308 | { | ||
309 | return false; | ||
310 | } | ||
311 | |||
312 | string readResult = string.Empty; | ||
313 | |||
314 | try | ||
315 | { | ||
316 | FileStream fsCachefile = | ||
317 | new FileStream(filename, | ||
318 | FileMode.Open); | ||
319 | |||
320 | StreamReader sr = new StreamReader(fsCachefile); | ||
321 | readResult = sr.ReadToEnd(); | ||
322 | |||
323 | sr.Close(); | ||
324 | sr.Dispose(); | ||
325 | fsCachefile.Dispose(); | ||
326 | |||
327 | } | ||
328 | catch (IOException ioe) | ||
329 | { | ||
330 | if (ioe is PathTooLongException) | ||
331 | { | ||
332 | m_log.Error( | ||
333 | "[J2KDecodeCache]: Cache Read failed. Path is too long."); | ||
334 | } | ||
335 | else if (ioe is DirectoryNotFoundException) | ||
336 | { | ||
337 | m_log.Error( | ||
338 | "[J2KDecodeCache]: Cache Read failed. Cache Directory does not exist!"); | ||
339 | enabled = false; | ||
340 | } | ||
341 | else | ||
342 | { | ||
343 | m_log.Error( | ||
344 | "[J2KDecodeCache]: Cache Read failed. IO Exception."); | ||
345 | } | ||
346 | return false; | ||
347 | |||
348 | } | ||
349 | catch (UnauthorizedAccessException) | ||
350 | { | ||
351 | m_log.Error( | ||
352 | "[J2KDecodeCache]: Cache Read failed. UnauthorizedAccessException Exception. Do you have the proper permissions on this file?"); | ||
353 | return false; | ||
354 | } | ||
355 | catch (ArgumentException ae) | ||
356 | { | ||
357 | if (ae is ArgumentNullException) | ||
358 | { | ||
359 | m_log.Error( | ||
360 | "[J2KDecodeCache]: Cache Read failed. No Filename provided"); | ||
361 | } | ||
362 | else | ||
363 | { | ||
364 | m_log.Error( | ||
365 | "[J2KDecodeCache]: Cache Read failed. Filname was invalid"); | ||
366 | } | ||
367 | return false; | ||
368 | } | ||
369 | catch (NotSupportedException) | ||
370 | { | ||
371 | m_log.Error( | ||
372 | "[J2KDecodeCache]: Cache Read failed, not supported. Cache disabled!"); | ||
373 | enabled = false; | ||
374 | |||
375 | return false; | ||
376 | } | ||
377 | catch (Exception e) | ||
378 | { | ||
379 | m_log.ErrorFormat( | ||
380 | "[J2KDecodeCache]: Cache Read failed, unknown exception. Error: {0}", | ||
381 | e.ToString()); | ||
382 | return false; | ||
383 | } | ||
384 | |||
385 | string[] lines = readResult.Split('\n'); | ||
386 | |||
387 | if (lines.Length <= 0) | ||
388 | return false; | ||
389 | |||
390 | Layers = new OpenJPEG.J2KLayerInfo[lines.Length]; | ||
391 | |||
392 | for (int i = 0; i < lines.Length; i++) | ||
393 | { | ||
394 | string[] elements = lines[i].Split('|'); | ||
395 | if (elements.Length == 3) | ||
396 | { | ||
397 | int element1, element2; | ||
398 | |||
399 | try | ||
400 | { | ||
401 | element1 = Convert.ToInt32(elements[0]); | ||
402 | element2 = Convert.ToInt32(elements[1]); | ||
403 | } | ||
404 | catch (FormatException) | ||
405 | { | ||
406 | m_log.WarnFormat("[J2KDecodeCache]: Cache Read failed with ErrorConvert for {0}", AssetId); | ||
407 | Layers = new OpenJPEG.J2KLayerInfo[0]; | ||
408 | return false; | ||
409 | } | ||
410 | |||
411 | Layers[i] = new OpenJPEG.J2KLayerInfo(); | ||
412 | Layers[i].Start = element1; | ||
413 | Layers[i].End = element2; | ||
414 | |||
415 | } | ||
416 | else | ||
417 | { | ||
418 | // reading failed | ||
419 | m_log.WarnFormat("[J2KDecodeCache]: Cache Read failed for {0}", AssetId); | ||
420 | Layers = new OpenJPEG.J2KLayerInfo[0]; | ||
421 | return false; | ||
422 | } | ||
423 | } | ||
424 | |||
425 | |||
426 | |||
427 | |||
428 | return true; | ||
429 | } | ||
430 | |||
431 | public string FileNameFromAssetId(UUID AssetId) | ||
432 | { | ||
433 | return String.Format("j2kCache_{0}.cache", AssetId); | ||
434 | } | ||
435 | |||
436 | public void Createj2KCacheFolder(string pFolder) | ||
437 | { | ||
438 | try | ||
439 | { | ||
440 | Directory.CreateDirectory(pFolder); | ||
441 | } | ||
442 | catch (IOException ioe) | ||
443 | { | ||
444 | if (ioe is PathTooLongException) | ||
445 | { | ||
446 | m_log.Error( | ||
447 | "[J2KDecodeCache]: Cache Directory does not exist and create failed because the path to the cache folder is too long. Cache disabled!"); | ||
448 | } | ||
449 | else if (ioe is DirectoryNotFoundException) | ||
450 | { | ||
451 | m_log.Error( | ||
452 | "[J2KDecodeCache]: Cache Directory does not exist and create failed because the supplied base of the directory folder does not exist. Cache disabled!"); | ||
453 | } | ||
454 | else | ||
455 | { | ||
456 | m_log.Error( | ||
457 | "[J2KDecodeCache]: Cache Directory does not exist and create failed because of an IO Exception. Cache disabled!"); | ||
458 | } | ||
459 | enabled = false; | ||
460 | |||
461 | } | ||
462 | catch (UnauthorizedAccessException) | ||
463 | { | ||
464 | m_log.Error( | ||
465 | "[J2KDecodeCache]: Cache Directory does not exist and create failed because of an UnauthorizedAccessException Exception. Cache disabled!"); | ||
466 | enabled = false; | ||
467 | } | ||
468 | catch (ArgumentException ae) | ||
469 | { | ||
470 | if (ae is ArgumentNullException) | ||
471 | { | ||
472 | m_log.Error( | ||
473 | "[J2KDecodeCache]: Cache Directory does not exist and create failed because the folder provided is invalid! Cache disabled!"); | ||
474 | } | ||
475 | else | ||
476 | { | ||
477 | m_log.Error( | ||
478 | "[J2KDecodeCache]: Cache Directory does not exist and create failed because no cache folder was provided! Cache disabled!"); | ||
479 | } | ||
480 | enabled = false; | ||
481 | } | ||
482 | catch (NotSupportedException) | ||
483 | { | ||
484 | m_log.Error( | ||
485 | "[J2KDecodeCache]: Cache Directory does not exist and create failed because it's not supported. Cache disabled!"); | ||
486 | enabled = false; | ||
487 | } | ||
488 | catch (Exception e) | ||
489 | { | ||
490 | m_log.ErrorFormat( | ||
491 | "[J2KDecodeCache]: Cache Directory does not exist and create failed because of an unknown exception. Cache disabled! Error: {0}", | ||
492 | e.ToString()); | ||
493 | enabled = false; | ||
494 | } | ||
495 | } | ||
496 | } | ||
231 | } | 497 | } |