aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs')
-rw-r--r--OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs685
1 files changed, 158 insertions, 527 deletions
diff --git a/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs
index 1fdb003..a0f359b 100644
--- a/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs
+++ b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs
@@ -34,8 +34,8 @@ using System.Threading;
34using log4net; 34using log4net;
35using Nini.Config; 35using Nini.Config;
36using OpenMetaverse; 36using OpenMetaverse;
37using OpenMetaverse.Assets;
38using OpenMetaverse.Imaging; 37using OpenMetaverse.Imaging;
38using CSJ2K;
39using OpenSim.Framework; 39using OpenSim.Framework;
40using OpenSim.Region.Framework.Interfaces; 40using OpenSim.Region.Framework.Interfaces;
41using OpenSim.Region.Framework.Scenes; 41using OpenSim.Region.Framework.Scenes;
@@ -43,31 +43,25 @@ using OpenSim.Services.Interfaces;
43 43
44namespace OpenSim.Region.CoreModules.Agent.TextureSender 44namespace OpenSim.Region.CoreModules.Agent.TextureSender
45{ 45{
46 public delegate void J2KDecodeDelegate(UUID AssetId); 46 public delegate void J2KDecodeDelegate(UUID assetID);
47 47
48 public class J2KDecoderModule : IRegionModule, IJ2KDecoder 48 public class J2KDecoderModule : IRegionModule, IJ2KDecoder
49 { 49 {
50 #region IRegionModule Members 50 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51 51
52 private static readonly ILog m_log 52 /// <summary>Temporarily holds deserialized layer data information in memory</summary>
53 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 53 private readonly ExpiringCache<UUID, OpenJPEG.J2KLayerInfo[]> m_decodedCache = new ExpiringCache<UUID,OpenJPEG.J2KLayerInfo[]>();
54 /// <summary>List of client methods to notify of results of decode</summary>
55 private readonly Dictionary<UUID, List<DecodedCallback>> m_notifyList = new Dictionary<UUID, List<DecodedCallback>>();
56 /// <summary>Cache that will store decoded JPEG2000 layer boundary data</summary>
57 private IImprovedAssetCache m_cache;
58 /// <summary>Reference to a scene (doesn't matter which one as long as it can load the cache module)</summary>
59 private Scene m_scene;
54 60
55 /// <summary> 61 #region IRegionModule
56 /// Cached Decoded Layers
57 /// </summary>
58 private readonly Dictionary<UUID, OpenJPEG.J2KLayerInfo[]> m_cacheddecode = new Dictionary<UUID, OpenJPEG.J2KLayerInfo[]>();
59 private bool OpenJpegFail = false;
60 private string CacheFolder = Util.dataDir() + "/j2kDecodeCache";
61 private int CacheTimeout = 720;
62 private J2KDecodeFileCache fCache = null;
63 private Thread CleanerThread = null;
64 private IAssetService AssetService = null;
65 private Scene m_Scene = null;
66 62
67 /// <summary> 63 public string Name { get { return "J2KDecoderModule"; } }
68 /// List of client methods to notify of results of decode 64 public bool IsSharedModule { get { return true; } }
69 /// </summary>
70 private readonly Dictionary<UUID, List<DecodedCallback>> m_notifyList = new Dictionary<UUID, List<DecodedCallback>>();
71 65
72 public J2KDecoderModule() 66 public J2KDecoderModule()
73 { 67 {
@@ -75,630 +69,267 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender
75 69
76 public void Initialise(Scene scene, IConfigSource source) 70 public void Initialise(Scene scene, IConfigSource source)
77 { 71 {
78 if (m_Scene == null) 72 if (m_scene == null)
79 m_Scene = scene; 73 m_scene = scene;
80
81 IConfig j2kConfig = source.Configs["J2KDecoder"];
82 if (j2kConfig != null)
83 {
84 CacheFolder = j2kConfig.GetString("CacheDir", CacheFolder);
85 CacheTimeout = j2kConfig.GetInt("CacheTimeout", CacheTimeout);
86 }
87
88 if (fCache == null)
89 fCache = new J2KDecodeFileCache(CacheFolder, CacheTimeout);
90 74
91 scene.RegisterModuleInterface<IJ2KDecoder>(this); 75 scene.RegisterModuleInterface<IJ2KDecoder>(this);
92
93 if (CleanerThread == null && CacheTimeout != 0)
94 {
95 CleanerThread = new Thread(CleanCache);
96 CleanerThread.Name = "J2KCleanerThread";
97 CleanerThread.IsBackground = true;
98 CleanerThread.Start();
99 }
100 } 76 }
101 77
102 public void PostInitialise() 78 public void PostInitialise()
103 { 79 {
104 AssetService = m_Scene.AssetService; 80 m_cache = m_scene.RequestModuleInterface<IImprovedAssetCache>();
105 } 81 }
106 82
107 public void Close() 83 public void Close()
108 { 84 {
109
110 }
111
112 public string Name
113 {
114 get { return "J2KDecoderModule"; }
115 }
116
117 public bool IsSharedModule
118 {
119 get { return true; }
120 } 85 }
121 86
122 #endregion 87 #endregion IRegionModule
123
124 #region IJ2KDecoder Members
125 88
89 #region IJ2KDecoder
126 90
127 public void decode(UUID AssetId, byte[] assetData, DecodedCallback decodedReturn) 91 public void BeginDecode(UUID assetID, byte[] j2kData, DecodedCallback callback)
128 { 92 {
129 // Dummy for if decoding fails. 93 OpenJPEG.J2KLayerInfo[] result;
130 OpenJPEG.J2KLayerInfo[] result = new OpenJPEG.J2KLayerInfo[0];
131
132 // Check if it's cached
133 bool cached = false;
134 lock (m_cacheddecode)
135 {
136 if (m_cacheddecode.ContainsKey(AssetId))
137 {
138 cached = true;
139 result = m_cacheddecode[AssetId];
140 }
141 }
142 94
143 // If it's cached, return the cached results 95 // If it's cached, return the cached results
144 if (cached) 96 if (m_decodedCache.TryGetValue(assetID, out result))
145 { 97 {
146 decodedReturn(AssetId, result); 98 callback(assetID, result);
147 } 99 }
148 else 100 else
149 { 101 {
150 // not cached, so we need to decode it 102 // Not cached, we need to decode it.
151 // Add to notify list and start decoding. 103 // Add to notify list and start decoding.
152 // Next request for this asset while it's decoding will only be added to the notify list 104 // Next request for this asset while it's decoding will only be added to the notify list
153 // once this is decoded, requests will be served from the cache and all clients in the notifylist will be updated 105 // once this is decoded, requests will be served from the cache and all clients in the notifylist will be updated
154 bool decode = false; 106 bool decode = false;
155 lock (m_notifyList) 107 lock (m_notifyList)
156 { 108 {
157 if (m_notifyList.ContainsKey(AssetId)) 109 if (m_notifyList.ContainsKey(assetID))
158 { 110 {
159 m_notifyList[AssetId].Add(decodedReturn); 111 m_notifyList[assetID].Add(callback);
160 } 112 }
161 else 113 else
162 { 114 {
163 List<DecodedCallback> notifylist = new List<DecodedCallback>(); 115 List<DecodedCallback> notifylist = new List<DecodedCallback>();
164 notifylist.Add(decodedReturn); 116 notifylist.Add(callback);
165 m_notifyList.Add(AssetId, notifylist); 117 m_notifyList.Add(assetID, notifylist);
166 decode = true; 118 decode = true;
167 } 119 }
168 } 120 }
121
169 // Do Decode! 122 // Do Decode!
170 if (decode) 123 if (decode)
171 { 124 DoJ2KDecode(assetID, j2kData);
172 doJ2kDecode(AssetId, assetData);
173 }
174 } 125 }
175 } 126 }
176 127
177 /// <summary> 128 /// <summary>
178 /// Provides a synchronous decode so that caller can be assured that this executes before the next line 129 /// Provides a synchronous decode so that caller can be assured that this executes before the next line
179 /// </summary> 130 /// </summary>
180 /// <param name="AssetId"></param> 131 /// <param name="assetID"></param>
181 /// <param name="j2kdata"></param> 132 /// <param name="j2kData"></param>
182 public void syncdecode(UUID AssetId, byte[] j2kdata) 133 public void Decode(UUID assetID, byte[] j2kData)
183 { 134 {
184 doJ2kDecode(AssetId, j2kdata); 135 DoJ2KDecode(assetID, j2kData);
185 } 136 }
186 137
187 #endregion 138 #endregion IJ2KDecoder
188 139
189 /// <summary> 140 /// <summary>
190 /// Decode Jpeg2000 Asset Data 141 /// Decode Jpeg2000 Asset Data
191 /// </summary> 142 /// </summary>
192 /// <param name="AssetId">UUID of Asset</param> 143 /// <param name="assetID">UUID of Asset</param>
193 /// <param name="j2kdata">Byte Array Asset Data </param> 144 /// <param name="j2kData">JPEG2000 data</param>
194 private void doJ2kDecode(UUID AssetId, byte[] j2kdata) 145 private void DoJ2KDecode(UUID assetID, byte[] j2kData)
195 { 146 {
196 int DecodeTime = 0; 147 int DecodeTime = 0;
197 DecodeTime = Environment.TickCount; 148 DecodeTime = Environment.TickCount;
198 OpenJPEG.J2KLayerInfo[] layers = new OpenJPEG.J2KLayerInfo[0]; // Dummy result for if it fails. Informs that there's only full quality 149 OpenJPEG.J2KLayerInfo[] layers;
199 150
200 if (!OpenJpegFail) 151 if (!TryLoadCacheForAsset(assetID, out layers))
201 { 152 {
202 if (!fCache.TryLoadCacheForAsset(AssetId, out layers)) 153 try
203 { 154 {
204 try 155 List<int> layerStarts = CSJ2K.J2kImage.GetLayerBoundaries(new MemoryStream(j2kData));
156
157 if (layerStarts != null && layerStarts.Count > 0)
205 { 158 {
159 layers = new OpenJPEG.J2KLayerInfo[layerStarts.Count];
206 160
207 AssetTexture texture = new AssetTexture(AssetId, j2kdata); 161 for (int i = 0; i < layerStarts.Count; i++)
208 if (texture.DecodeLayerBoundaries())
209 { 162 {
210 bool sane = true; 163 OpenJPEG.J2KLayerInfo layer = new OpenJPEG.J2KLayerInfo();
211 164
212 // Sanity check all of the layers 165 if (i == 0)
213 for (int i = 0; i < texture.LayerInfo.Length; i++) 166 layer.Start = 0;
214 {
215 if (texture.LayerInfo[i].End > texture.AssetData.Length)
216 {
217 sane = false;
218 break;
219 }
220 }
221
222 if (sane)
223 {
224 layers = texture.LayerInfo;
225 fCache.SaveFileCacheForAsset(AssetId, layers);
226
227
228 // Write out decode time
229 m_log.InfoFormat("[J2KDecoderModule]: {0} Decode Time: {1}", Environment.TickCount - DecodeTime,
230 AssetId);
231
232 }
233 else 167 else
234 { 168 layer.Start = layerStarts[i];
235 m_log.WarnFormat(
236 "[J2KDecoderModule]: JPEG2000 texture decoding succeeded, but sanity check failed for {0}",
237 AssetId);
238 }
239 }
240
241 else
242 {
243 /*
244 Random rnd = new Random();
245 // scramble ends for test
246 for (int i = 0; i < texture.LayerInfo.Length; i++)
247 {
248 texture.LayerInfo[i].End = rnd.Next(999999);
249 }
250 */
251
252 // Try to do some heuristics error correction! Yeah.
253 bool sane2Heuristics = true;
254
255
256 if (texture.Image == null)
257 sane2Heuristics = false;
258
259 if (texture.LayerInfo == null)
260 sane2Heuristics = false;
261
262 if (sane2Heuristics)
263 {
264
265 169
266 if (texture.LayerInfo.Length == 0) 170 if (i == layerStarts.Count - 1)
267 sane2Heuristics = false; 171 layer.End = j2kData.Length;
268 }
269
270 if (sane2Heuristics)
271 {
272 // Last layer start is less then the end of the file and last layer start is greater then 0
273 if (texture.LayerInfo[texture.LayerInfo.Length - 1].Start < texture.AssetData.Length && texture.LayerInfo[texture.LayerInfo.Length - 1].Start > 0)
274 {
275 }
276 else
277 {
278 sane2Heuristics = false;
279 }
280
281 }
282
283 if (sane2Heuristics)
284 {
285 int start = 0;
286
287 // try to fix it by using consistant data in the start field
288 for (int i = 0; i < texture.LayerInfo.Length; i++)
289 {
290 if (i == 0)
291 start = 0;
292
293 if (i == texture.LayerInfo.Length - 1)
294 texture.LayerInfo[i].End = texture.AssetData.Length;
295 else
296 texture.LayerInfo[i].End = texture.LayerInfo[i + 1].Start - 1;
297
298 // in this case, the end of the next packet is less then the start of the last packet
299 // after we've attempted to fix it which means the start of the last packet is borked
300 // there's no recovery from this
301 if (texture.LayerInfo[i].End < start)
302 {
303 sane2Heuristics = false;
304 break;
305 }
306
307 if (texture.LayerInfo[i].End < 0 || texture.LayerInfo[i].End > texture.AssetData.Length)
308 {
309 sane2Heuristics = false;
310 break;
311 }
312
313 if (texture.LayerInfo[i].Start < 0 || texture.LayerInfo[i].Start > texture.AssetData.Length)
314 {
315 sane2Heuristics = false;
316 break;
317 }
318
319 start = texture.LayerInfo[i].Start;
320 }
321 }
322
323 if (sane2Heuristics)
324 {
325 layers = texture.LayerInfo;
326 fCache.SaveFileCacheForAsset(AssetId, layers);
327
328
329 // Write out decode time
330 m_log.InfoFormat("[J2KDecoderModule]: HEURISTICS SUCCEEDED {0} Decode Time: {1}", Environment.TickCount - DecodeTime,
331 AssetId);
332
333 }
334 else 172 else
335 { 173 layer.End = layerStarts[i + 1] - 1;
336 m_log.WarnFormat("[J2KDecoderModule]: JPEG2000 texture decoding failed for {0}. Is this a texture? is it J2K?", AssetId); 174
337 } 175 layers[i] = layer;
338 } 176 }
339 texture = null; // dereference and dispose of ManagedImage
340 }
341 catch (DllNotFoundException)
342 {
343 m_log.Error(
344 "[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!");
345 OpenJpegFail = true;
346 }
347 catch (Exception ex)
348 {
349 m_log.WarnFormat(
350 "[J2KDecoderModule]: JPEG2000 texture decoding threw an exception for {0}, {1}",
351 AssetId, ex);
352 } 177 }
353 } 178 }
354 179 catch (Exception ex)
355 } 180 {
181 m_log.Warn("[J2KDecoderModule]: CSJ2K threw an exception decoding texture " + assetID + ": " + ex.Message);
182 }
356 183
357 // Cache Decoded layers 184 if (layers == null || layers.Length == 0)
358 lock (m_cacheddecode) 185 {
359 { 186 m_log.Warn("[J2KDecoderModule]: Failed to decode layer data for texture " + assetID + ", guessing sane defaults");
360 if (m_cacheddecode.ContainsKey(AssetId)) 187 // Layer decoding completely failed. Guess at sane defaults for the layer boundaries
361 m_cacheddecode.Remove(AssetId); 188 layers = CreateDefaultLayers(j2kData.Length);
362 m_cacheddecode.Add(AssetId, layers); 189 }
363 190
191 // Cache Decoded layers
192 SaveFileCacheForAsset(assetID, layers);
364 } 193 }
365 194
366 // Notify Interested Parties 195 // Notify Interested Parties
367 lock (m_notifyList) 196 lock (m_notifyList)
368 { 197 {
369 if (m_notifyList.ContainsKey(AssetId)) 198 if (m_notifyList.ContainsKey(assetID))
370 { 199 {
371 foreach (DecodedCallback d in m_notifyList[AssetId]) 200 foreach (DecodedCallback d in m_notifyList[assetID])
372 { 201 {
373 if (d != null) 202 if (d != null)
374 d.DynamicInvoke(AssetId, layers); 203 d.DynamicInvoke(assetID, layers);
375 } 204 }
376 m_notifyList.Remove(AssetId); 205 m_notifyList.Remove(assetID);
377 } 206 }
378 } 207 }
379 } 208 }
380
381 private void CleanCache()
382 {
383 m_log.Info("[J2KDecoderModule]: Cleaner thread started");
384
385 while (true)
386 {
387 if (AssetService != null)
388 fCache.ScanCacheFiles(RedecodeTexture);
389 209
390 System.Threading.Thread.Sleep(600000); 210 private OpenJPEG.J2KLayerInfo[] CreateDefaultLayers(int j2kLength)
391 }
392 }
393
394 private void RedecodeTexture(UUID assetID)
395 { 211 {
396 AssetBase texture = AssetService.Get(assetID.ToString()); 212 OpenJPEG.J2KLayerInfo[] layers = new OpenJPEG.J2KLayerInfo[5];
397 if (texture == null) 213
398 return; 214 for (int i = 0; i < layers.Length; i++)
399 215 layers[i] = new OpenJPEG.J2KLayerInfo();
400 doJ2kDecode(assetID, texture.Data); 216
217 // These default layer sizes are based on a small sampling of real-world texture data
218 // with extra padding thrown in for good measure. This is a worst case fallback plan
219 // and may not gracefully handle all real world data
220 layers[0].Start = 0;
221 layers[1].Start = (int)((float)j2kLength * 0.02f);
222 layers[2].Start = (int)((float)j2kLength * 0.05f);
223 layers[3].Start = (int)((float)j2kLength * 0.20f);
224 layers[4].Start = (int)((float)j2kLength * 0.50f);
225
226 layers[0].End = layers[1].Start - 1;
227 layers[1].End = layers[2].Start - 1;
228 layers[2].End = layers[3].Start - 1;
229 layers[3].End = layers[4].Start - 1;
230 layers[4].End = j2kLength;
231
232 return layers;
401 } 233 }
402 }
403 234
404 public class J2KDecodeFileCache 235 private void SaveFileCacheForAsset(UUID AssetId, OpenJPEG.J2KLayerInfo[] Layers)
405 {
406 private readonly string m_cacheDecodeFolder;
407 private readonly int m_cacheTimeout;
408 private bool enabled = true;
409
410 private static readonly ILog m_log
411 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
412
413 /// <summary>
414 /// Creates a new instance of a file cache
415 /// </summary>
416 /// <param name="pFolder">base folder for the cache. Will be created if it doesn't exist</param>
417 public J2KDecodeFileCache(string pFolder, int timeout)
418 { 236 {
419 m_cacheDecodeFolder = pFolder; 237 m_decodedCache.AddOrUpdate(AssetId, Layers, TimeSpan.FromMinutes(10));
420 m_cacheTimeout = timeout;
421 if (!Directory.Exists(pFolder))
422 {
423 Createj2KCacheFolder(pFolder);
424 }
425 }
426 238
427 /// <summary> 239 if (m_cache != null)
428 /// Save Layers to Disk Cache
429 /// </summary>
430 /// <param name="AssetId">Asset to Save the layers. Used int he file name by default</param>
431 /// <param name="Layers">The Layer Data from OpenJpeg</param>
432 /// <returns></returns>
433 public bool SaveFileCacheForAsset(UUID AssetId, OpenJPEG.J2KLayerInfo[] Layers)
434 {
435 if (Layers.Length > 0 && enabled)
436 { 240 {
437 FileStream fsCache = 241 AssetBase layerDecodeAsset = new AssetBase();
438 new FileStream(String.Format("{0}/{1}", m_cacheDecodeFolder, FileNameFromAssetId(AssetId)), 242 layerDecodeAsset.ID = "j2kCache_" + AssetId.ToString();
439 FileMode.Create); 243 layerDecodeAsset.Local = true;
440 StreamWriter fsSWCache = new StreamWriter(fsCache); 244 layerDecodeAsset.Name = layerDecodeAsset.ID;
245 layerDecodeAsset.Temporary = true;
246 layerDecodeAsset.Type = (sbyte)AssetType.Notecard;
247
248 #region Serialize Layer Data
249
441 StringBuilder stringResult = new StringBuilder(); 250 StringBuilder stringResult = new StringBuilder();
442 string strEnd = "\n"; 251 string strEnd = "\n";
443 for (int i = 0; i < Layers.Length; i++) 252 for (int i = 0; i < Layers.Length; i++)
444 { 253 {
445 if (i == (Layers.Length - 1)) 254 if (i == Layers.Length - 1)
446 strEnd = ""; 255 strEnd = String.Empty;
447 256
448 stringResult.AppendFormat("{0}|{1}|{2}{3}", Layers[i].Start, Layers[i].End, Layers[i].End - Layers[i].Start, strEnd); 257 stringResult.AppendFormat("{0}|{1}|{2}{3}", Layers[i].Start, Layers[i].End, Layers[i].End - Layers[i].Start, strEnd);
449 } 258 }
450 fsSWCache.Write(stringResult.ToString());
451 fsSWCache.Close();
452 fsSWCache.Dispose();
453 fsCache.Dispose();
454 return true;
455 }
456
457 259
458 return false; 260 layerDecodeAsset.Data = Encoding.UTF8.GetBytes(stringResult.ToString());
459 }
460 261
461 262 #endregion Serialize Layer Data
462 /// <summary>
463 /// Loads the Layer data from the disk cache
464 /// Returns true if load succeeded
465 /// </summary>
466 /// <param name="AssetId">AssetId that we're checking the cache for</param>
467 /// <param name="Layers">out layers to save to</param>
468 /// <returns>true if load succeeded</returns>
469 public bool TryLoadCacheForAsset(UUID AssetId, out OpenJPEG.J2KLayerInfo[] Layers)
470 {
471 string filename = String.Format("{0}/{1}", m_cacheDecodeFolder, FileNameFromAssetId(AssetId));
472 Layers = new OpenJPEG.J2KLayerInfo[0];
473 263
474 if (!File.Exists(filename)) 264 m_cache.Cache(layerDecodeAsset);
475 return false;
476
477 if (!enabled)
478 {
479 return false;
480 } 265 }
266 }
481 267
482 string readResult = string.Empty; 268 bool TryLoadCacheForAsset(UUID AssetId, out OpenJPEG.J2KLayerInfo[] Layers)
483 269 {
484 try 270 if (m_decodedCache.TryGetValue(AssetId, out Layers))
485 { 271 {
486 FileStream fsCachefile = 272 return true;
487 new FileStream(filename,
488 FileMode.Open);
489
490 StreamReader sr = new StreamReader(fsCachefile);
491 readResult = sr.ReadToEnd();
492
493 sr.Close();
494 sr.Dispose();
495 fsCachefile.Dispose();
496
497 } 273 }
498 catch (IOException ioe) 274 else if (m_cache != null)
499 { 275 {
500 if (ioe is PathTooLongException) 276 string assetName = "j2kCache_" + AssetId.ToString();
501 { 277 AssetBase layerDecodeAsset = m_cache.Get(assetName);
502 m_log.Error(
503 "[J2KDecodeCache]: Cache Read failed. Path is too long.");
504 }
505 else if (ioe is DirectoryNotFoundException)
506 {
507 m_log.Error(
508 "[J2KDecodeCache]: Cache Read failed. Cache Directory does not exist!");
509 enabled = false;
510 }
511 else
512 {
513 m_log.Error(
514 "[J2KDecodeCache]: Cache Read failed. IO Exception.");
515 }
516 return false;
517 278
518 } 279 if (layerDecodeAsset != null)
519 catch (UnauthorizedAccessException)
520 {
521 m_log.Error(
522 "[J2KDecodeCache]: Cache Read failed. UnauthorizedAccessException Exception. Do you have the proper permissions on this file?");
523 return false;
524 }
525 catch (ArgumentException ae)
526 {
527 if (ae is ArgumentNullException)
528 {
529 m_log.Error(
530 "[J2KDecodeCache]: Cache Read failed. No Filename provided");
531 }
532 else
533 { 280 {
534 m_log.Error( 281 #region Deserialize Layer Data
535 "[J2KDecodeCache]: Cache Read failed. Filname was invalid");
536 }
537 return false;
538 }
539 catch (NotSupportedException)
540 {
541 m_log.Error(
542 "[J2KDecodeCache]: Cache Read failed, not supported. Cache disabled!");
543 enabled = false;
544 282
545 return false; 283 string readResult = Encoding.UTF8.GetString(layerDecodeAsset.Data);
546 } 284 string[] lines = readResult.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
547 catch (Exception e)
548 {
549 m_log.ErrorFormat(
550 "[J2KDecodeCache]: Cache Read failed, unknown exception. Error: {0}",
551 e.ToString());
552 return false;
553 }
554
555 string[] lines = readResult.Split('\n');
556 285
557 if (lines.Length <= 0) 286 if (lines.Length == 0)
558 return false;
559
560 Layers = new OpenJPEG.J2KLayerInfo[lines.Length];
561
562 for (int i = 0; i < lines.Length; i++)
563 {
564 string[] elements = lines[i].Split('|');
565 if (elements.Length == 3)
566 {
567 int element1, element2;
568
569 try
570 {
571 element1 = Convert.ToInt32(elements[0]);
572 element2 = Convert.ToInt32(elements[1]);
573 }
574 catch (FormatException)
575 { 287 {
576 m_log.WarnFormat("[J2KDecodeCache]: Cache Read failed with ErrorConvert for {0}", AssetId); 288 m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (empty) " + assetName);
577 Layers = new OpenJPEG.J2KLayerInfo[0]; 289 m_cache.Expire(assetName);
578 return false; 290 return false;
579 } 291 }
580 292
581 Layers[i] = new OpenJPEG.J2KLayerInfo(); 293 Layers = new OpenJPEG.J2KLayerInfo[lines.Length];
582 Layers[i].Start = element1;
583 Layers[i].End = element2;
584
585 }
586 else
587 {
588 // reading failed
589 m_log.WarnFormat("[J2KDecodeCache]: Cache Read failed for {0}", AssetId);
590 Layers = new OpenJPEG.J2KLayerInfo[0];
591 return false;
592 }
593 }
594
595
596
597
598 return true;
599 }
600 294
601 /// <summary> 295 for (int i = 0; i < lines.Length; i++)
602 /// Routine which converts assetid to file name 296 {
603 /// </summary> 297 string[] elements = lines[i].Split('|');
604 /// <param name="AssetId">asset id of the image</param> 298 if (elements.Length == 3)
605 /// <returns>string filename</returns> 299 {
606 public string FileNameFromAssetId(UUID AssetId) 300 int element1, element2;
607 {
608 return String.Format("j2kCache_{0}.cache", AssetId);
609 }
610 301
611 public UUID AssetIdFromFileName(string fileName) 302 try
612 { 303 {
613 string rawId = fileName.Replace("j2kCache_", "").Replace(".cache", ""); 304 element1 = Convert.ToInt32(elements[0]);
614 UUID asset; 305 element2 = Convert.ToInt32(elements[1]);
615 if (!UUID.TryParse(rawId, out asset)) 306 }
616 return UUID.Zero; 307 catch (FormatException)
308 {
309 m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (format) " + assetName);
310 m_cache.Expire(assetName);
311 return false;
312 }
617 313
618 return asset; 314 Layers[i] = new OpenJPEG.J2KLayerInfo();
619 } 315 Layers[i].Start = element1;
316 Layers[i].End = element2;
317 }
318 else
319 {
320 m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (layout) " + assetName);
321 m_cache.Expire(assetName);
322 return false;
323 }
324 }
620 325
621 /// <summary> 326 #endregion Deserialize Layer Data
622 /// Creates the Cache Folder
623 /// </summary>
624 /// <param name="pFolder">Folder to Create</param>
625 public void Createj2KCacheFolder(string pFolder)
626 {
627 try
628 {
629 Directory.CreateDirectory(pFolder);
630 }
631 catch (IOException ioe)
632 {
633 if (ioe is PathTooLongException)
634 {
635 m_log.Error(
636 "[J2KDecodeCache]: Cache Directory does not exist and create failed because the path to the cache folder is too long. Cache disabled!");
637 }
638 else if (ioe is DirectoryNotFoundException)
639 {
640 m_log.Error(
641 "[J2KDecodeCache]: Cache Directory does not exist and create failed because the supplied base of the directory folder does not exist. Cache disabled!");
642 }
643 else
644 {
645 m_log.Error(
646 "[J2KDecodeCache]: Cache Directory does not exist and create failed because of an IO Exception. Cache disabled!");
647 }
648 enabled = false;
649 327
650 } 328 return true;
651 catch (UnauthorizedAccessException)
652 {
653 m_log.Error(
654 "[J2KDecodeCache]: Cache Directory does not exist and create failed because of an UnauthorizedAccessException Exception. Cache disabled!");
655 enabled = false;
656 }
657 catch (ArgumentException ae)
658 {
659 if (ae is ArgumentNullException)
660 {
661 m_log.Error(
662 "[J2KDecodeCache]: Cache Directory does not exist and create failed because the folder provided is invalid! Cache disabled!");
663 } 329 }
664 else
665 {
666 m_log.Error(
667 "[J2KDecodeCache]: Cache Directory does not exist and create failed because no cache folder was provided! Cache disabled!");
668 }
669 enabled = false;
670 } 330 }
671 catch (NotSupportedException)
672 {
673 m_log.Error(
674 "[J2KDecodeCache]: Cache Directory does not exist and create failed because it's not supported. Cache disabled!");
675 enabled = false;
676 }
677 catch (Exception e)
678 {
679 m_log.ErrorFormat(
680 "[J2KDecodeCache]: Cache Directory does not exist and create failed because of an unknown exception. Cache disabled! Error: {0}",
681 e.ToString());
682 enabled = false;
683 }
684 }
685
686 public void ScanCacheFiles(J2KDecodeDelegate decode)
687 {
688 DirectoryInfo dir = new DirectoryInfo(m_cacheDecodeFolder);
689 FileInfo[] files = dir.GetFiles("j2kCache_*.cache");
690 331
691 foreach (FileInfo f in files) 332 return false;
692 {
693 TimeSpan fileAge = DateTime.Now - f.CreationTime;
694
695 if (m_cacheTimeout != 0 && fileAge >= TimeSpan.FromMinutes(m_cacheTimeout))
696 {
697 File.Delete(f.Name);
698 decode(AssetIdFromFileName(f.Name));
699 System.Threading.Thread.Sleep(5000);
700 }
701 }
702 } 333 }
703 } 334 }
704} 335}