clockwerk-opensim – 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.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;
235 using (MemoryStream ms = new MemoryStream(j2kData))
236 {
237 layerStarts = CSJ2K.J2kImage.GetLayerBoundaries(ms);
238 }
239  
240 if (layerStarts != null && layerStarts.Count > 0)
241 {
242 layers = new OpenJPEG.J2KLayerInfo[layerStarts.Count];
243  
244 for (int i = 0; i < layerStarts.Count; i++)
245 {
246 OpenJPEG.J2KLayerInfo layer = new OpenJPEG.J2KLayerInfo();
247  
248 if (i == 0)
249 layer.Start = 0;
250 else
251 layer.Start = layerStarts[i];
252  
253 if (i == layerStarts.Count - 1)
254 layer.End = j2kData.Length;
255 else
256 layer.End = layerStarts[i + 1] - 1;
257  
258 layers[i] = layer;
259 }
260 }
261 }
262 catch (Exception ex)
263 {
264 m_log.Warn("[J2KDecoderModule]: CSJ2K threw an exception decoding texture " + assetID + ": " + ex.Message);
265 decodedSuccessfully = false;
266 }
267 }
268 else
269 {
270 if (!OpenJPEG.DecodeLayerBoundaries(j2kData, out layers, out components))
271 {
272 m_log.Warn("[J2KDecoderModule]: OpenJPEG failed to decode texture " + assetID);
273 decodedSuccessfully = false;
274 }
275 }
276  
277 if (layers == null || layers.Length == 0)
278 {
279 m_log.Warn("[J2KDecoderModule]: Failed to decode layer data for texture " + assetID + ", guessing sane defaults");
280 // Layer decoding completely failed. Guess at sane defaults for the layer boundaries
281 layers = CreateDefaultLayers(j2kData.Length);
282 decodedSuccessfully = false;
283 }
284  
285 // Cache Decoded layers
286 SaveFileCacheForAsset(assetID, layers);
287 }
288  
289 // Notify Interested Parties
290 lock (m_notifyList)
291 {
292 if (m_notifyList.ContainsKey(assetID))
293 {
294 foreach (DecodedCallback d in m_notifyList[assetID])
295 {
296 if (d != null)
297 d.DynamicInvoke(assetID, layers);
298 }
299 m_notifyList.Remove(assetID);
300 }
301 }
302  
303 return decodedSuccessfully;
304 }
305  
306 private OpenJPEG.J2KLayerInfo[] CreateDefaultLayers(int j2kLength)
307 {
308 OpenJPEG.J2KLayerInfo[] layers = new OpenJPEG.J2KLayerInfo[5];
309  
310 for (int i = 0; i < layers.Length; i++)
311 layers[i] = new OpenJPEG.J2KLayerInfo();
312  
313 // These default layer sizes are based on a small sampling of real-world texture data
314 // with extra padding thrown in for good measure. This is a worst case fallback plan
315 // and may not gracefully handle all real world data
316 layers[0].Start = 0;
317 layers[1].Start = (int)((float)j2kLength * 0.02f);
318 layers[2].Start = (int)((float)j2kLength * 0.05f);
319 layers[3].Start = (int)((float)j2kLength * 0.20f);
320 layers[4].Start = (int)((float)j2kLength * 0.50f);
321  
322 layers[0].End = layers[1].Start - 1;
323 layers[1].End = layers[2].Start - 1;
324 layers[2].End = layers[3].Start - 1;
325 layers[3].End = layers[4].Start - 1;
326 layers[4].End = j2kLength;
327  
328 return layers;
329 }
330  
331 private void SaveFileCacheForAsset(UUID AssetId, OpenJPEG.J2KLayerInfo[] Layers)
332 {
333 m_decodedCache.AddOrUpdate(AssetId, Layers, TimeSpan.FromMinutes(10));
334  
335 if (Cache != null)
336 {
337 string assetID = "j2kCache_" + AssetId.ToString();
338  
339 AssetBase layerDecodeAsset = new AssetBase(assetID, assetID, (sbyte)AssetType.Notecard, m_CreatorID.ToString());
340 layerDecodeAsset.Local = true;
341 layerDecodeAsset.Temporary = true;
342  
343 #region Serialize Layer Data
344  
345 StringBuilder stringResult = new StringBuilder();
346 string strEnd = "\n";
347 for (int i = 0; i < Layers.Length; i++)
348 {
349 if (i == Layers.Length - 1)
350 strEnd = String.Empty;
351  
352 stringResult.AppendFormat("{0}|{1}|{2}{3}", Layers[i].Start, Layers[i].End, Layers[i].End - Layers[i].Start, strEnd);
353 }
354  
355 layerDecodeAsset.Data = Util.UTF8.GetBytes(stringResult.ToString());
356  
357 #endregion Serialize Layer Data
358  
359 Cache.Cache(layerDecodeAsset);
360 }
361 }
362  
363 bool TryLoadCacheForAsset(UUID AssetId, out OpenJPEG.J2KLayerInfo[] Layers)
364 {
365 if (m_decodedCache.TryGetValue(AssetId, out Layers))
366 {
367 return true;
368 }
369 else if (Cache != null)
370 {
371 string assetName = "j2kCache_" + AssetId.ToString();
372 AssetBase layerDecodeAsset = Cache.Get(assetName);
373  
374 if (layerDecodeAsset != null)
375 {
376 #region Deserialize Layer Data
377  
378 string readResult = Util.UTF8.GetString(layerDecodeAsset.Data);
379 string[] lines = readResult.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
380  
381 if (lines.Length == 0)
382 {
383 m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (empty) " + assetName);
384 Cache.Expire(assetName);
385 return false;
386 }
387  
388 Layers = new OpenJPEG.J2KLayerInfo[lines.Length];
389  
390 for (int i = 0; i < lines.Length; i++)
391 {
392 string[] elements = lines[i].Split('|');
393 if (elements.Length == 3)
394 {
395 int element1, element2;
396  
397 try
398 {
399 element1 = Convert.ToInt32(elements[0]);
400 element2 = Convert.ToInt32(elements[1]);
401 }
402 catch (FormatException)
403 {
404 m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (format) " + assetName);
405 Cache.Expire(assetName);
406 return false;
407 }
408  
409 Layers[i] = new OpenJPEG.J2KLayerInfo();
410 Layers[i].Start = element1;
411 Layers[i].End = element2;
412 }
413 else
414 {
415 m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (layout) " + assetName);
416 Cache.Expire(assetName);
417 return false;
418 }
419 }
420  
421 #endregion Deserialize Layer Data
422  
423 return true;
424 }
425 }
426  
427 return false;
428 }
429 }
430 }