opensim – Blame information for rev 1

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