29 using System.Collections.Generic;
32 using System.Reflection;
34 using System.Threading;
39 using OpenMetaverse.Imaging;
41 using OpenSim.Framework;
42 using OpenSim.Region.Framework.Interfaces;
43 using OpenSim.Region.Framework.Scenes;
44 using OpenSim.Services.Interfaces;
50 [Extension(Path =
"/OpenSim/RegionModules", NodeName =
"RegionModule", Id =
"J2KDecoderModule")]
53 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
56 private readonly ExpiringCache<
UUID, OpenJPEG.J2KLayerInfo[]> m_decodedCache =
new ExpiringCache<
UUID,OpenJPEG.J2KLayerInfo[]>();
58 private readonly Dictionary<UUID, List<DecodedCallback>> m_notifyList =
new Dictionary<UUID, List<DecodedCallback>>();
72 private UUID m_CreatorID = UUID.Zero;
73 private Scene m_scene;
75 #region ISharedRegionModule
77 private bool m_useCSJ2K =
true;
79 public string Name {
get {
return "J2KDecoderModule"; } }
87 IConfig startupConfig = source.Configs[
"Startup"];
88 if (startupConfig != null)
90 m_useCSJ2K = startupConfig.GetBoolean(
"UseCSJ2K", m_useCSJ2K);
99 m_CreatorID = scene.RegionInfo.RegionID;
108 if (m_scene == scene)
124 public Type ReplaceableInterface
129 #endregion Region Module interface
135 OpenJPEG.J2KLayerInfo[] result;
138 if (m_decodedCache.TryGetValue(assetID, out result))
144 callback(assetID, result);
155 if (m_notifyList.ContainsKey(assetID))
157 m_notifyList[assetID].Add(callback);
161 List<DecodedCallback> notifylist =
new List<DecodedCallback>();
162 notifylist.Add(callback);
163 m_notifyList.Add(assetID, notifylist);
170 Util.FireAndForget(delegate { Decode(assetID, j2kData); }, null,
"J2KDecoderModule.BeginDecode");
174 public bool Decode(UUID assetID, byte[] j2kData)
176 OpenJPEG.J2KLayerInfo[] layers;
178 return Decode(assetID, j2kData, out layers, out components);
181 public bool Decode(UUID assetID, byte[] j2kData, out OpenJPEG.J2KLayerInfo[] layers, out
int components)
183 return DoJ2KDecode(assetID, j2kData, out layers, out components);
189 return J2kImage.FromBytes(j2kData);
194 if (OpenJPEG.DecodeToImage(j2kData, out mimage, out image))
205 #endregion IJ2KDecoder
215 private bool DoJ2KDecode(UUID assetID, byte[] j2kData, out OpenJPEG.J2KLayerInfo[] layers, out
int components)
220 bool decodedSuccessfully =
true;
228 if (!TryLoadCacheForAsset(assetID, out layers))
234 List<int> layerStarts;
235 using (MemoryStream ms =
new MemoryStream(j2kData))
237 layerStarts = CSJ2K.J2kImage.GetLayerBoundaries(ms);
240 if (layerStarts != null && layerStarts.Count > 0)
242 layers =
new OpenJPEG.J2KLayerInfo[layerStarts.Count];
244 for (
int i = 0; i < layerStarts.Count; i++)
246 OpenJPEG.J2KLayerInfo layer =
new OpenJPEG.J2KLayerInfo();
251 layer.Start = layerStarts[i];
253 if (i == layerStarts.Count - 1)
254 layer.End = j2kData.Length;
256 layer.End = layerStarts[i + 1] - 1;
264 m_log.Warn(
"[J2KDecoderModule]: CSJ2K threw an exception decoding texture " + assetID +
": " + ex.Message);
265 decodedSuccessfully =
false;
270 if (!OpenJPEG.DecodeLayerBoundaries(j2kData, out layers, out components))
272 m_log.Warn(
"[J2KDecoderModule]: OpenJPEG failed to decode texture " + assetID);
273 decodedSuccessfully =
false;
277 if (layers == null || layers.Length == 0)
279 m_log.Warn(
"[J2KDecoderModule]: Failed to decode layer data for texture " + assetID +
", guessing sane defaults");
281 layers = CreateDefaultLayers(j2kData.Length);
282 decodedSuccessfully =
false;
286 SaveFileCacheForAsset(assetID, layers);
292 if (m_notifyList.ContainsKey(assetID))
297 d.DynamicInvoke(assetID, layers);
299 m_notifyList.Remove(assetID);
303 return decodedSuccessfully;
306 private OpenJPEG.J2KLayerInfo[] CreateDefaultLayers(
int j2kLength)
308 OpenJPEG.J2KLayerInfo[] layers =
new OpenJPEG.J2KLayerInfo[5];
310 for (
int i = 0; i < layers.Length; i++)
311 layers[i] =
new OpenJPEG.J2KLayerInfo();
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);
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;
331 private void SaveFileCacheForAsset(UUID AssetId, OpenJPEG.J2KLayerInfo[] Layers)
333 m_decodedCache.AddOrUpdate(AssetId, Layers, TimeSpan.FromMinutes(10));
337 string assetID =
"j2kCache_" + AssetId.ToString();
339 AssetBase layerDecodeAsset =
new AssetBase(assetID, assetID, (sbyte)AssetType.Notecard, m_CreatorID.ToString());
340 layerDecodeAsset.Local =
true;
341 layerDecodeAsset.Temporary =
true;
343 #region Serialize Layer Data
345 StringBuilder stringResult =
new StringBuilder();
346 string strEnd =
"\n";
347 for (
int i = 0; i < Layers.Length; i++)
349 if (i == Layers.Length - 1)
350 strEnd = String.Empty;
352 stringResult.AppendFormat(
"{0}|{1}|{2}{3}", Layers[i].Start, Layers[i].End, Layers[i].End - Layers[i].Start, strEnd);
355 layerDecodeAsset.Data = Util.UTF8.GetBytes(stringResult.ToString());
357 #endregion Serialize Layer Data
359 Cache.Cache(layerDecodeAsset);
363 bool TryLoadCacheForAsset(UUID AssetId, out OpenJPEG.J2KLayerInfo[] Layers)
365 if (m_decodedCache.TryGetValue(AssetId, out Layers))
369 else if (
Cache != null)
371 string assetName =
"j2kCache_" + AssetId.ToString();
372 AssetBase layerDecodeAsset = Cache.Get(assetName);
374 if (layerDecodeAsset != null)
376 #region Deserialize Layer Data
378 string readResult = Util.UTF8.GetString(layerDecodeAsset.Data);
379 string[] lines = readResult.Split(
new char[] {
'\n' }, StringSplitOptions.RemoveEmptyEntries);
381 if (lines.Length == 0)
383 m_log.Warn(
"[J2KDecodeCache]: Expiring corrupted layer data (empty) " + assetName);
384 Cache.Expire(assetName);
388 Layers =
new OpenJPEG.J2KLayerInfo[lines.Length];
390 for (
int i = 0; i < lines.Length; i++)
392 string[] elements = lines[i].Split(
'|');
393 if (elements.Length == 3)
395 int element1, element2;
399 element1 = Convert.ToInt32(elements[0]);
400 element2 = Convert.ToInt32(elements[1]);
402 catch (FormatException)
404 m_log.Warn(
"[J2KDecodeCache]: Expiring corrupted layer data (format) " + assetName);
405 Cache.Expire(assetName);
409 Layers[i] =
new OpenJPEG.J2KLayerInfo();
410 Layers[i].Start = element1;
411 Layers[i].End = element2;
415 m_log.Warn(
"[J2KDecodeCache]: Expiring corrupted layer data (layout) " + assetName);
416 Cache.Expire(assetName);
421 #endregion Deserialize Layer Data
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
bool Decode(UUID assetID, byte[] j2kData, out OpenJPEG.J2KLayerInfo[] layers, out int components)
Provides a synchronous decode so that caller can be assured that this executes before the next line ...
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
void PostInitialise()
This is called exactly once after all the shared region-modules have been instanciated and IRegionMod...
bool Decode(UUID assetID, byte[] j2kData)
Provides a synchronous decode so that caller can be assured that this executes before the next line ...
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
delegate void DecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers)
Asset class. All Assets are reference by this class or a class derived from this class ...
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
void Initialise(IConfigSource source)
This is called to initialize the region module. For shared modules, this is called exactly once...
delegate void J2KDecodeDelegate(UUID assetID)
Interactive OpenSim region server
void BeginDecode(UUID assetID, byte[] j2kData, DecodedCallback callback)
Image DecodeToImage(byte[] j2kData)
Provides a synchronous decode direct to an image object