aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
diff options
context:
space:
mode:
authorJohn Hurliman2009-09-30 12:18:22 -0700
committerMelanie2009-09-30 19:26:53 +0100
commitf56dc5fcda16d96a309120b2a75a623cf455a8e8 (patch)
treecd86ef9956054404ee784f119593dd9c569d9572 /OpenSim
parentRevert "Attempting to improve the robustness of texture decoding by always ig... (diff)
downloadopensim-SC_OLD-f56dc5fcda16d96a309120b2a75a623cf455a8e8.zip
opensim-SC_OLD-f56dc5fcda16d96a309120b2a75a623cf455a8e8.tar.gz
opensim-SC_OLD-f56dc5fcda16d96a309120b2a75a623cf455a8e8.tar.bz2
opensim-SC_OLD-f56dc5fcda16d96a309120b2a75a623cf455a8e8.tar.xz
Attempting to improve the robustness of texture decoding by always ignoring LayerInfo.End values and creating guessed default layer boundaries on failed decodes Changed a noisy J2K decode log message from Info to Debug Replacing openjpeg-dotnet decoding with managed CSJ2K decoding. Should be much more reliable, faster, and use less memory
* Re-added openjpeg-dotnet files since they are used elsewhere in OpenSim * Updated prebuild.xml with a reference to CSJ2K * Renamed IJ2KDecoder and J2KDecoder member names to follow standard naming conventions * Removed j2kDecodeCache cruft and replaced it with the OpenSim cache system * Rewrote the default layer boundary algorithm to use percentages instead of an exponent * Switched from an infinite in-memory cache to an expiring cache (10 minute timeout) * Slightly quieted logging errors for failed texture decodes
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs2
-rw-r--r--OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs688
-rw-r--r--OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs2
-rw-r--r--OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs5
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneManager.cs2
5 files changed, 165 insertions, 534 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
index b186720..45286be 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
@@ -351,7 +351,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
351 J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]); 351 J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]);
352 } 352 }
353 // Send it off to the jpeg decoder 353 // Send it off to the jpeg decoder
354 m_j2kDecodeModule.decode(m_requestedUUID, Data, J2KDecodedCallback); 354 m_j2kDecodeModule.BeginDecode(m_requestedUUID, Data, J2KDecodedCallback);
355 355
356 } 356 }
357 else 357 else
diff --git a/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs
index 937f76b..49f7f48 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,268 @@ 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 88
124 #region IJ2KDecoder Members 89 #region IJ2KDecoder
125 90
126 91 public void BeginDecode(UUID assetID, byte[] j2kData, DecodedCallback callback)
127 public void decode(UUID AssetId, byte[] assetData, DecodedCallback decodedReturn)
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 int start = layerStarts[i];
212 // Sanity check all of the layers
213 for (int i = 0; i < texture.LayerInfo.Length; i++)
214 {
215 if (texture.LayerInfo[i].End > texture.AssetData.Length)
216 {
217 sane = false;
218 break;
219 }
220 }
221 165
222 if (sane) 166 if (i == 0)
223 { 167 layer.Start = 0;
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 168 else
234 { 169 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 170
241 else 171 if (i == layerStarts.Count - 1)
242 { 172 layer.End = j2kData.Length;
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
266 if (texture.LayerInfo.Length == 0)
267 sane2Heuristics = false;
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 173 else
335 { 174 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); 175
337 } 176 layers[i] = layer;
338 } 177 }
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 } 178 }
353 } 179 }
354 180 catch (Exception ex)
355 } 181 {
356 182 m_log.Warn("[J2KDecoderModule]: CSJ2K threw an exception decoding texture " + assetID + ": " + ex.Message);
357 // Cache Decoded layers 183 }
358 lock (m_cacheddecode)
359 {
360 if (m_cacheddecode.ContainsKey(AssetId))
361 m_cacheddecode.Remove(AssetId);
362 m_cacheddecode.Add(AssetId, layers);
363 184
364 } 185 if (layers == null || layers.Length == 0)
186 {
187 m_log.Warn("[J2KDecoderModule]: Failed to decode layer data for texture " + assetID + ", guessing sane defaults");
188 // Layer decoding completely failed. Guess at sane defaults for the layer boundaries
189 layers = CreateDefaultLayers(j2kData.Length);
190 }
365 191
192 // Cache Decoded layers
193 SaveFileCacheForAsset(assetID, layers);
194 }
195
366 // Notify Interested Parties 196 // Notify Interested Parties
367 lock (m_notifyList) 197 lock (m_notifyList)
368 { 198 {
369 if (m_notifyList.ContainsKey(AssetId)) 199 if (m_notifyList.ContainsKey(assetID))
370 { 200 {
371 foreach (DecodedCallback d in m_notifyList[AssetId]) 201 foreach (DecodedCallback d in m_notifyList[assetID])
372 { 202 {
373 if (d != null) 203 if (d != null)
374 d.DynamicInvoke(AssetId, layers); 204 d.DynamicInvoke(assetID, layers);
375 } 205 }
376 m_notifyList.Remove(AssetId); 206 m_notifyList.Remove(assetID);
377 } 207 }
378 } 208 }
379 } 209 }
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 210
390 System.Threading.Thread.Sleep(600000); 211 private OpenJPEG.J2KLayerInfo[] CreateDefaultLayers(int j2kLength)
391 }
392 }
393
394 private void RedecodeTexture(UUID assetID)
395 { 212 {
396 AssetBase texture = AssetService.Get(assetID.ToString()); 213 OpenJPEG.J2KLayerInfo[] layers = new OpenJPEG.J2KLayerInfo[5];
397 if (texture == null) 214
398 return; 215 for (int i = 0; i < layers.Length; i++)
399 216 layers[i] = new OpenJPEG.J2KLayerInfo();
400 doJ2kDecode(assetID, texture.Data); 217
218 // These default layer sizes are based on a small sampling of real-world texture data
219 // with extra padding thrown in for good measure. This is a worst case fallback plan
220 // and may not gracefully handle all real world data
221 layers[0].Start = 0;
222 layers[1].Start = (int)((float)j2kLength * 0.02f);
223 layers[2].Start = (int)((float)j2kLength * 0.05f);
224 layers[3].Start = (int)((float)j2kLength * 0.20f);
225 layers[4].Start = (int)((float)j2kLength * 0.50f);
226
227 layers[0].End = layers[1].Start - 1;
228 layers[1].End = layers[2].Start - 1;
229 layers[2].End = layers[3].Start - 1;
230 layers[3].End = layers[4].Start - 1;
231 layers[4].End = j2kLength;
232
233 return layers;
401 } 234 }
402 }
403 235
404 public class J2KDecodeFileCache 236 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 { 237 {
419 m_cacheDecodeFolder = pFolder; 238 m_decodedCache.AddOrUpdate(AssetId, Layers, TimeSpan.FromMinutes(10));
420 m_cacheTimeout = timeout;
421 if (!Directory.Exists(pFolder))
422 {
423 Createj2KCacheFolder(pFolder);
424 }
425 }
426 239
427 /// <summary> 240 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 { 241 {
437 FileStream fsCache = 242 AssetBase layerDecodeAsset = new AssetBase();
438 new FileStream(String.Format("{0}/{1}", m_cacheDecodeFolder, FileNameFromAssetId(AssetId)), 243 layerDecodeAsset.ID = "j2kCache_" + AssetId.ToString();
439 FileMode.Create); 244 layerDecodeAsset.Local = true;
440 StreamWriter fsSWCache = new StreamWriter(fsCache); 245 layerDecodeAsset.Name = layerDecodeAsset.ID;
246 layerDecodeAsset.Temporary = true;
247 layerDecodeAsset.Type = (sbyte)AssetType.Notecard;
248
249 #region Serialize Layer Data
250
441 StringBuilder stringResult = new StringBuilder(); 251 StringBuilder stringResult = new StringBuilder();
442 string strEnd = "\n"; 252 string strEnd = "\n";
443 for (int i = 0; i < Layers.Length; i++) 253 for (int i = 0; i < Layers.Length; i++)
444 { 254 {
445 if (i == (Layers.Length - 1)) 255 if (i == Layers.Length - 1)
446 strEnd = ""; 256 strEnd = String.Empty;
447 257
448 stringResult.AppendFormat("{0}|{1}|{2}{3}", Layers[i].Start, Layers[i].End, Layers[i].End - Layers[i].Start, strEnd); 258 stringResult.AppendFormat("{0}|{1}|{2}{3}", Layers[i].Start, Layers[i].End, Layers[i].End - Layers[i].Start, strEnd);
449 } 259 }
450 fsSWCache.Write(stringResult.ToString());
451 fsSWCache.Close();
452 fsSWCache.Dispose();
453 fsCache.Dispose();
454 return true;
455 }
456
457 260
458 return false; 261 layerDecodeAsset.Data = Encoding.UTF8.GetBytes(stringResult.ToString());
459 }
460 262
461 263 #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 264
474 if (!File.Exists(filename)) 265 m_cache.Cache(layerDecodeAsset);
475 return false;
476
477 if (!enabled)
478 {
479 return false;
480 } 266 }
267 }
481 268
482 string readResult = string.Empty; 269 bool TryLoadCacheForAsset(UUID AssetId, out OpenJPEG.J2KLayerInfo[] Layers)
483 270 {
484 try 271 if (m_decodedCache.TryGetValue(AssetId, out Layers))
485 { 272 {
486 FileStream fsCachefile = 273 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 } 274 }
498 catch (IOException ioe) 275 else if (m_cache != null)
499 { 276 {
500 if (ioe is PathTooLongException) 277 string assetName = "j2kCache_" + AssetId.ToString();
501 { 278 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 279
518 } 280 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 { 281 {
534 m_log.Error( 282 #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 283
545 return false; 284 string readResult = Encoding.UTF8.GetString(layerDecodeAsset.Data);
546 } 285 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 286
557 if (lines.Length <= 0) 287 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 { 288 {
576 m_log.WarnFormat("[J2KDecodeCache]: Cache Read failed with ErrorConvert for {0}", AssetId); 289 m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (empty) " + assetName);
577 Layers = new OpenJPEG.J2KLayerInfo[0]; 290 m_cache.Expire(assetName);
578 return false; 291 return false;
579 } 292 }
580 293
581 Layers[i] = new OpenJPEG.J2KLayerInfo(); 294 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 295
601 /// <summary> 296 for (int i = 0; i < lines.Length; i++)
602 /// Routine which converts assetid to file name 297 {
603 /// </summary> 298 string[] elements = lines[i].Split('|');
604 /// <param name="AssetId">asset id of the image</param> 299 if (elements.Length == 3)
605 /// <returns>string filename</returns> 300 {
606 public string FileNameFromAssetId(UUID AssetId) 301 int element1, element2;
607 {
608 return String.Format("j2kCache_{0}.cache", AssetId);
609 }
610 302
611 public UUID AssetIdFromFileName(string fileName) 303 try
612 { 304 {
613 string rawId = fileName.Replace("j2kCache_", "").Replace(".cache", ""); 305 element1 = Convert.ToInt32(elements[0]);
614 UUID asset; 306 element2 = Convert.ToInt32(elements[1]);
615 if (!UUID.TryParse(rawId, out asset)) 307 }
616 return UUID.Zero; 308 catch (FormatException)
309 {
310 m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (format) " + assetName);
311 m_cache.Expire(assetName);
312 return false;
313 }
617 314
618 return asset; 315 Layers[i] = new OpenJPEG.J2KLayerInfo();
619 } 316 Layers[i].Start = element1;
317 Layers[i].End = element2;
318 }
319 else
320 {
321 m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (layout) " + assetName);
322 m_cache.Expire(assetName);
323 return false;
324 }
325 }
620 326
621 /// <summary> 327 #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 328
650 } 329 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 } 330 }
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 } 331 }
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 332
691 foreach (FileInfo f in files) 333 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 } 334 }
703 } 335 }
704} 336}
diff --git a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
index 14eb9a2..9a6c49a 100644
--- a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
@@ -325,7 +325,7 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
325 IJ2KDecoder cacheLayerDecode = scene.RequestModuleInterface<IJ2KDecoder>(); 325 IJ2KDecoder cacheLayerDecode = scene.RequestModuleInterface<IJ2KDecoder>();
326 if (cacheLayerDecode != null) 326 if (cacheLayerDecode != null)
327 { 327 {
328 cacheLayerDecode.syncdecode(asset.FullID, asset.Data); 328 cacheLayerDecode.Decode(asset.FullID, asset.Data);
329 cacheLayerDecode = null; 329 cacheLayerDecode = null;
330 LastAssetID = asset.FullID; 330 LastAssetID = asset.FullID;
331 } 331 }
diff --git a/OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs b/OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs
index b153997..856eb11 100644
--- a/OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs
+++ b/OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs
@@ -30,12 +30,11 @@ using OpenMetaverse.Imaging;
30 30
31namespace OpenSim.Region.Framework.Interfaces 31namespace OpenSim.Region.Framework.Interfaces
32{ 32{
33
34 public delegate void DecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers); 33 public delegate void DecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers);
35 34
36 public interface IJ2KDecoder 35 public interface IJ2KDecoder
37 { 36 {
38 void decode(UUID AssetId, byte[] assetData, DecodedCallback decodedReturn); 37 void BeginDecode(UUID assetID, byte[] j2kData, DecodedCallback callback);
39 void syncdecode(UUID AssetId, byte[] j2kdata); 38 void Decode(UUID assetID, byte[] j2kData);
40 } 39 }
41} 40}
diff --git a/OpenSim/Region/Framework/Scenes/SceneManager.cs b/OpenSim/Region/Framework/Scenes/SceneManager.cs
index 0019b23..091a2d5 100644
--- a/OpenSim/Region/Framework/Scenes/SceneManager.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneManager.cs
@@ -652,7 +652,7 @@ namespace OpenSim.Region.Framework.Scenes
652 AssetBase ab = sn.AssetService.Get(arrassets[i].ToString()); 652 AssetBase ab = sn.AssetService.Get(arrassets[i].ToString());
653 if (ab != null && ab.Data != null) 653 if (ab != null && ab.Data != null)
654 { 654 {
655 j2kdecode.syncdecode(arrassets[i], ab.Data); 655 j2kdecode.Decode(arrassets[i], ab.Data);
656 } 656 }
657 } 657 }
658 ThreadTracker.Remove(thisthread); 658 ThreadTracker.Remove(thisthread);