clockwerk-opensim-stable – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 /*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27  
28 using System;
29 using System.Collections.Generic;
30 using System.IO;
31 using System.Reflection;
32 using System.Text;
33 using System.Threading;
34 using log4net;
35 using Mono.Addins;
36 using Nini.Config;
37 using OpenMetaverse;
38 using OpenMetaverse.Imaging;
39 using CSJ2K;
40 using OpenSim.Framework;
41 using OpenSim.Region.Framework.Interfaces;
42 using OpenSim.Region.Framework.Scenes;
43 using OpenSim.Services.Interfaces;
44  
45 namespace OpenSim.Region.CoreModules.Agent.TextureSender
46 {
47 public delegate void J2KDecodeDelegate(UUID assetID);
48  
49 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "J2KDecoderModule")]
50 public class J2KDecoderModule : ISharedRegionModule, IJ2KDecoder
51 {
52 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
53  
54 /// <summary>Temporarily holds deserialized layer data information in memory</summary>
55 private readonly ExpiringCache<UUID, OpenJPEG.J2KLayerInfo[]> m_decodedCache = new ExpiringCache<UUID,OpenJPEG.J2KLayerInfo[]>();
56 /// <summary>List of client methods to notify of results of decode</summary>
57 private readonly Dictionary<UUID, List<DecodedCallback>> m_notifyList = new Dictionary<UUID, List<DecodedCallback>>();
58 /// <summary>Cache that will store decoded JPEG2000 layer boundary data</summary>
59 private IImprovedAssetCache m_cache;
60 private IImprovedAssetCache Cache
61 {
62 get
63 {
64 if (m_cache == null)
65 m_cache = m_scene.RequestModuleInterface<IImprovedAssetCache>();
66  
67 return m_cache;
68 }
69 }
70 /// <summary>Reference to a scene (doesn't matter which one as long as it can load the cache module)</summary>
71 private UUID m_CreatorID = UUID.Zero;
72 private Scene m_scene;
73  
74 #region ISharedRegionModule
75  
76 private bool m_useCSJ2K = true;
77  
78 public string Name { get { return "J2KDecoderModule"; } }
79  
80 public J2KDecoderModule()
81 {
82 }
83  
84 public void Initialise(IConfigSource source)
85 {
86 IConfig startupConfig = source.Configs["Startup"];
87 if (startupConfig != null)
88 {
89 m_useCSJ2K = startupConfig.GetBoolean("UseCSJ2K", m_useCSJ2K);
90 }
91 }
92  
93 public void AddRegion(Scene scene)
94 {
95 if (m_scene == null)
96 {
97 m_scene = scene;
98 m_CreatorID = scene.RegionInfo.RegionID;
99 }
100  
101 scene.RegisterModuleInterface<IJ2KDecoder>(this);
102  
103 }
104  
105 public void RemoveRegion(Scene scene)
106 {
107 if (m_scene == scene)
108 m_scene = null;
109 }
110  
111 public void PostInitialise()
112 {
113 }
114  
115 public void Close()
116 {
117 }
118  
119 public void RegionLoaded(Scene scene)
120 {
121 }
122  
123 public Type ReplaceableInterface
124 {
125 get { return null; }
126 }
127  
128 #endregion Region Module interface
129  
130 #region IJ2KDecoder
131  
132 public void BeginDecode(UUID assetID, byte[] j2kData, DecodedCallback callback)
133 {
134 OpenJPEG.J2KLayerInfo[] result;
135  
136 // If it's cached, return the cached results
137 if (m_decodedCache.TryGetValue(assetID, out result))
138 {
139 // m_log.DebugFormat(
140 // "[J2KDecoderModule]: Returning existing cached {0} layers j2k decode for {1}",
141 // result.Length, assetID);
142  
143 callback(assetID, result);
144 }
145 else
146 {
147 // Not cached, we need to decode it.
148 // Add to notify list and start decoding.
149 // Next request for this asset while it's decoding will only be added to the notify list
150 // once this is decoded, requests will be served from the cache and all clients in the notifylist will be updated
151 bool decode = false;
152 lock (m_notifyList)
153 {
154 if (m_notifyList.ContainsKey(assetID))
155 {
156 m_notifyList[assetID].Add(callback);
157 }
158 else
159 {
160 List<DecodedCallback> notifylist = new List<DecodedCallback>();
161 notifylist.Add(callback);
162 m_notifyList.Add(assetID, notifylist);
163 decode = true;
164 }
165 }
166  
167 // Do Decode!
168 if (decode)
169 Util.FireAndForget(delegate { Decode(assetID, j2kData); });
170 }
171 }
172  
173 public bool Decode(UUID assetID, byte[] j2kData)
174 {
175 OpenJPEG.J2KLayerInfo[] layers;
176 int components;
177 return Decode(assetID, j2kData, out layers, out components);
178 }
179  
180 public bool Decode(UUID assetID, byte[] j2kData, out OpenJPEG.J2KLayerInfo[] layers, out int components)
181 {
182 return DoJ2KDecode(assetID, j2kData, out layers, out components);
183 }
184  
185 #endregion IJ2KDecoder
186  
187 /// <summary>
188 /// Decode Jpeg2000 Asset Data
189 /// </summary>
190 /// <param name="assetID">UUID of Asset</param>
191 /// <param name="j2kData">JPEG2000 data</param>
192 /// <param name="layers">layer data</param>
193 /// <param name="components">number of components</param>
194 /// <returns>true if decode was successful. false otherwise.</returns>
195 private bool DoJ2KDecode(UUID assetID, byte[] j2kData, out OpenJPEG.J2KLayerInfo[] layers, out int components)
196 {
197 // m_log.DebugFormat(
198 // "[J2KDecoderModule]: Doing J2K decoding of {0} bytes for asset {1}", j2kData.Length, assetID);
199  
200 bool decodedSuccessfully = true;
201  
202 //int DecodeTime = 0;
203 //DecodeTime = Environment.TickCount;
204  
205 // We don't get this from CSJ2K. Is it relevant?
206 components = 0;
207  
208 if (!TryLoadCacheForAsset(assetID, out layers))
209 {
210 if (m_useCSJ2K)
211 {
212 try
213 {
214 List<int> layerStarts = CSJ2K.J2kImage.GetLayerBoundaries(new MemoryStream(j2kData));
215  
216 if (layerStarts != null && layerStarts.Count > 0)
217 {
218 layers = new OpenJPEG.J2KLayerInfo[layerStarts.Count];
219  
220 for (int i = 0; i < layerStarts.Count; i++)
221 {
222 OpenJPEG.J2KLayerInfo layer = new OpenJPEG.J2KLayerInfo();
223  
224 if (i == 0)
225 layer.Start = 0;
226 else
227 layer.Start = layerStarts[i];
228  
229 if (i == layerStarts.Count - 1)
230 layer.End = j2kData.Length;
231 else
232 layer.End = layerStarts[i + 1] - 1;
233  
234 layers[i] = layer;
235 }
236 }
237 }
238 catch (Exception ex)
239 {
240 m_log.Warn("[J2KDecoderModule]: CSJ2K threw an exception decoding texture " + assetID + ": " + ex.Message);
241 decodedSuccessfully = false;
242 }
243 }
244 else
245 {
246 if (!OpenJPEG.DecodeLayerBoundaries(j2kData, out layers, out components))
247 {
248 m_log.Warn("[J2KDecoderModule]: OpenJPEG failed to decode texture " + assetID);
249 decodedSuccessfully = false;
250 }
251 }
252  
253 if (layers == null || layers.Length == 0)
254 {
255 m_log.Warn("[J2KDecoderModule]: Failed to decode layer data for texture " + assetID + ", guessing sane defaults");
256 // Layer decoding completely failed. Guess at sane defaults for the layer boundaries
257 layers = CreateDefaultLayers(j2kData.Length);
258 decodedSuccessfully = false;
259 }
260  
261 // Cache Decoded layers
262 SaveFileCacheForAsset(assetID, layers);
263 }
264  
265 // Notify Interested Parties
266 lock (m_notifyList)
267 {
268 if (m_notifyList.ContainsKey(assetID))
269 {
270 foreach (DecodedCallback d in m_notifyList[assetID])
271 {
272 if (d != null)
273 d.DynamicInvoke(assetID, layers);
274 }
275 m_notifyList.Remove(assetID);
276 }
277 }
278  
279 return decodedSuccessfully;
280 }
281  
282 private OpenJPEG.J2KLayerInfo[] CreateDefaultLayers(int j2kLength)
283 {
284 OpenJPEG.J2KLayerInfo[] layers = new OpenJPEG.J2KLayerInfo[5];
285  
286 for (int i = 0; i < layers.Length; i++)
287 layers[i] = new OpenJPEG.J2KLayerInfo();
288  
289 // These default layer sizes are based on a small sampling of real-world texture data
290 // with extra padding thrown in for good measure. This is a worst case fallback plan
291 // and may not gracefully handle all real world data
292 layers[0].Start = 0;
293 layers[1].Start = (int)((float)j2kLength * 0.02f);
294 layers[2].Start = (int)((float)j2kLength * 0.05f);
295 layers[3].Start = (int)((float)j2kLength * 0.20f);
296 layers[4].Start = (int)((float)j2kLength * 0.50f);
297  
298 layers[0].End = layers[1].Start - 1;
299 layers[1].End = layers[2].Start - 1;
300 layers[2].End = layers[3].Start - 1;
301 layers[3].End = layers[4].Start - 1;
302 layers[4].End = j2kLength;
303  
304 return layers;
305 }
306  
307 private void SaveFileCacheForAsset(UUID AssetId, OpenJPEG.J2KLayerInfo[] Layers)
308 {
309 m_decodedCache.AddOrUpdate(AssetId, Layers, TimeSpan.FromMinutes(10));
310  
311 if (Cache != null)
312 {
313 string assetID = "j2kCache_" + AssetId.ToString();
314  
315 AssetBase layerDecodeAsset = new AssetBase(assetID, assetID, (sbyte)AssetType.Notecard, m_CreatorID.ToString());
316 layerDecodeAsset.Local = true;
317 layerDecodeAsset.Temporary = true;
318  
319 #region Serialize Layer Data
320  
321 StringBuilder stringResult = new StringBuilder();
322 string strEnd = "\n";
323 for (int i = 0; i < Layers.Length; i++)
324 {
325 if (i == Layers.Length - 1)
326 strEnd = String.Empty;
327  
328 stringResult.AppendFormat("{0}|{1}|{2}{3}", Layers[i].Start, Layers[i].End, Layers[i].End - Layers[i].Start, strEnd);
329 }
330  
331 layerDecodeAsset.Data = Util.UTF8.GetBytes(stringResult.ToString());
332  
333 #endregion Serialize Layer Data
334  
335 Cache.Cache(layerDecodeAsset);
336 }
337 }
338  
339 bool TryLoadCacheForAsset(UUID AssetId, out OpenJPEG.J2KLayerInfo[] Layers)
340 {
341 if (m_decodedCache.TryGetValue(AssetId, out Layers))
342 {
343 return true;
344 }
345 else if (Cache != null)
346 {
347 string assetName = "j2kCache_" + AssetId.ToString();
348 AssetBase layerDecodeAsset = Cache.Get(assetName);
349  
350 if (layerDecodeAsset != null)
351 {
352 #region Deserialize Layer Data
353  
354 string readResult = Util.UTF8.GetString(layerDecodeAsset.Data);
355 string[] lines = readResult.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
356  
357 if (lines.Length == 0)
358 {
359 m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (empty) " + assetName);
360 Cache.Expire(assetName);
361 return false;
362 }
363  
364 Layers = new OpenJPEG.J2KLayerInfo[lines.Length];
365  
366 for (int i = 0; i < lines.Length; i++)
367 {
368 string[] elements = lines[i].Split('|');
369 if (elements.Length == 3)
370 {
371 int element1, element2;
372  
373 try
374 {
375 element1 = Convert.ToInt32(elements[0]);
376 element2 = Convert.ToInt32(elements[1]);
377 }
378 catch (FormatException)
379 {
380 m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (format) " + assetName);
381 Cache.Expire(assetName);
382 return false;
383 }
384  
385 Layers[i] = new OpenJPEG.J2KLayerInfo();
386 Layers[i].Start = element1;
387 Layers[i].End = element2;
388 }
389 else
390 {
391 m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (layout) " + assetName);
392 Cache.Expire(assetName);
393 return false;
394 }
395 }
396  
397 #endregion Deserialize Layer Data
398  
399 return true;
400 }
401 }
402  
403 return false;
404 }
405 }
406 }