J2KImage.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  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. using System;
  28. using System.Collections.Generic;
  29. using OpenMetaverse;
  30. using OpenMetaverse.Imaging;
  31. using OpenSim.Framework;
  32. using OpenSim.Region.Framework.Interfaces;
  33. using OpenSim.Region.Framework.Scenes.Hypergrid;
  34. using OpenSim.Services.Interfaces;
  35. using log4net;
  36. using System.Reflection;
  37. namespace OpenSim.Region.ClientStack.LindenUDP
  38. {
  39. /// <summary>
  40. /// Stores information about a current texture download and a reference to the texture asset
  41. /// </summary>
  42. public class J2KImage
  43. {
  44. private const int IMAGE_PACKET_SIZE = 1000;
  45. private const int FIRST_PACKET_SIZE = 600;
  46. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  47. public uint LastSequence;
  48. public float Priority;
  49. public uint StartPacket;
  50. public sbyte DiscardLevel;
  51. public UUID TextureID;
  52. public IJ2KDecoder J2KDecoder;
  53. public IAssetService AssetService;
  54. public UUID AgentID;
  55. public IHyperAssetService HyperAssets;
  56. public OpenJPEG.J2KLayerInfo[] Layers;
  57. public bool IsDecoded;
  58. public bool HasAsset;
  59. public C5.IPriorityQueueHandle<J2KImage> PriorityQueueHandle;
  60. private uint m_currentPacket;
  61. private bool m_decodeRequested;
  62. private bool m_assetRequested;
  63. private bool m_sentInfo;
  64. private uint m_stopPacket;
  65. private byte[] m_asset;
  66. private LLImageManager m_imageManager;
  67. public J2KImage(LLImageManager imageManager)
  68. {
  69. m_imageManager = imageManager;
  70. }
  71. /// <summary>
  72. /// Sends packets for this texture to a client until packetsToSend is
  73. /// hit or the transfer completes
  74. /// </summary>
  75. /// <param name="client">Reference to the client that the packets are destined for</param>
  76. /// <param name="packetsToSend">Maximum number of packets to send during this call</param>
  77. /// <param name="packetsSent">Number of packets sent during this call</param>
  78. /// <returns>True if the transfer completes at the current discard level, otherwise false</returns>
  79. public bool SendPackets(LLClientView client, int packetsToSend, out int packetsSent)
  80. {
  81. packetsSent = 0;
  82. if (m_currentPacket <= m_stopPacket)
  83. {
  84. bool sendMore = true;
  85. if (!m_sentInfo || (m_currentPacket == 0))
  86. {
  87. sendMore = !SendFirstPacket(client);
  88. m_sentInfo = true;
  89. ++m_currentPacket;
  90. ++packetsSent;
  91. }
  92. if (m_currentPacket < 2)
  93. {
  94. m_currentPacket = 2;
  95. }
  96. while (sendMore && packetsSent < packetsToSend && m_currentPacket <= m_stopPacket)
  97. {
  98. sendMore = SendPacket(client);
  99. ++m_currentPacket;
  100. ++packetsSent;
  101. }
  102. }
  103. return (m_currentPacket > m_stopPacket);
  104. }
  105. public void RunUpdate()
  106. {
  107. //This is where we decide what we need to update
  108. //and assign the real discardLevel and packetNumber
  109. //assuming of course that the connected client might be bonkers
  110. if (!HasAsset)
  111. {
  112. if (!m_assetRequested)
  113. {
  114. m_assetRequested = true;
  115. AssetService.Get(TextureID.ToString(), this, AssetReceived);
  116. }
  117. }
  118. else
  119. {
  120. if (!IsDecoded)
  121. {
  122. //We need to decode the requested image first
  123. if (!m_decodeRequested)
  124. {
  125. //Request decode
  126. m_decodeRequested = true;
  127. // Do we have a jpeg decoder?
  128. if (J2KDecoder != null)
  129. {
  130. if (m_asset == null)
  131. {
  132. J2KDecodedCallback(TextureID, new OpenJPEG.J2KLayerInfo[0]);
  133. }
  134. else
  135. {
  136. // Send it off to the jpeg decoder
  137. J2KDecoder.BeginDecode(TextureID, m_asset, J2KDecodedCallback);
  138. }
  139. }
  140. else
  141. {
  142. J2KDecodedCallback(TextureID, new OpenJPEG.J2KLayerInfo[0]);
  143. }
  144. }
  145. }
  146. else
  147. {
  148. // Check for missing image asset data
  149. if (m_asset == null)
  150. {
  151. m_log.Warn("[J2KIMAGE]: RunUpdate() called with missing asset data (no missing image texture?). Canceling texture transfer");
  152. m_currentPacket = m_stopPacket;
  153. return;
  154. }
  155. if (DiscardLevel >= 0 || m_stopPacket == 0)
  156. {
  157. // This shouldn't happen, but if it does, we really can't proceed
  158. if (Layers == null)
  159. {
  160. m_log.Warn("[J2KIMAGE]: RunUpdate() called with missing Layers. Canceling texture transfer");
  161. m_currentPacket = m_stopPacket;
  162. return;
  163. }
  164. int maxDiscardLevel = Math.Max(0, Layers.Length - 1);
  165. // Treat initial texture downloads with a DiscardLevel of -1 a request for the highest DiscardLevel
  166. if (DiscardLevel < 0 && m_stopPacket == 0)
  167. DiscardLevel = (sbyte)maxDiscardLevel;
  168. // Clamp at the highest discard level
  169. DiscardLevel = (sbyte)Math.Min(DiscardLevel, maxDiscardLevel);
  170. //Calculate the m_stopPacket
  171. if (Layers.Length > 0)
  172. {
  173. m_stopPacket = (uint)GetPacketForBytePosition(Layers[(Layers.Length - 1) - DiscardLevel].End);
  174. //I don't know why, but the viewer seems to expect the final packet if the file
  175. //is just one packet bigger.
  176. if (TexturePacketCount() == m_stopPacket + 1)
  177. {
  178. m_stopPacket = TexturePacketCount();
  179. }
  180. }
  181. else
  182. {
  183. m_stopPacket = TexturePacketCount();
  184. }
  185. m_currentPacket = StartPacket;
  186. }
  187. }
  188. }
  189. }
  190. private bool SendFirstPacket(LLClientView client)
  191. {
  192. if (client == null)
  193. return false;
  194. if (m_asset == null)
  195. {
  196. m_log.Warn("[J2KIMAGE]: Sending ImageNotInDatabase for texture " + TextureID);
  197. client.SendImageNotFound(TextureID);
  198. return true;
  199. }
  200. else if (m_asset.Length <= FIRST_PACKET_SIZE)
  201. {
  202. // We have less then one packet's worth of data
  203. client.SendImageFirstPart(1, TextureID, (uint)m_asset.Length, m_asset, 2);
  204. m_stopPacket = 0;
  205. return true;
  206. }
  207. else
  208. {
  209. // This is going to be a multi-packet texture download
  210. byte[] firstImageData = new byte[FIRST_PACKET_SIZE];
  211. try { Buffer.BlockCopy(m_asset, 0, firstImageData, 0, FIRST_PACKET_SIZE); }
  212. catch (Exception)
  213. {
  214. m_log.ErrorFormat("[J2KIMAGE]: Texture block copy for the first packet failed. textureid={0}, assetlength={1}", TextureID, m_asset.Length);
  215. return true;
  216. }
  217. client.SendImageFirstPart(TexturePacketCount(), TextureID, (uint)m_asset.Length, firstImageData, (byte)ImageCodec.J2C);
  218. }
  219. return false;
  220. }
  221. private bool SendPacket(LLClientView client)
  222. {
  223. if (client == null)
  224. return false;
  225. bool complete = false;
  226. int imagePacketSize = ((int)m_currentPacket == (TexturePacketCount())) ? LastPacketSize() : IMAGE_PACKET_SIZE;
  227. try
  228. {
  229. if ((CurrentBytePosition() + IMAGE_PACKET_SIZE) > m_asset.Length)
  230. {
  231. imagePacketSize = LastPacketSize();
  232. complete = true;
  233. if ((CurrentBytePosition() + imagePacketSize) > m_asset.Length)
  234. {
  235. imagePacketSize = m_asset.Length - CurrentBytePosition();
  236. complete = true;
  237. }
  238. }
  239. // It's concievable that the client might request packet one
  240. // from a one packet image, which is really packet 0,
  241. // which would leave us with a negative imagePacketSize..
  242. if (imagePacketSize > 0)
  243. {
  244. byte[] imageData = new byte[imagePacketSize];
  245. int currentPosition = CurrentBytePosition();
  246. try { Buffer.BlockCopy(m_asset, currentPosition, imageData, 0, imagePacketSize); }
  247. catch (Exception e)
  248. {
  249. m_log.ErrorFormat("[J2KIMAGE]: Texture block copy for the first packet failed. textureid={0}, assetlength={1}, currentposition={2}, imagepacketsize={3}, exception={4}",
  250. TextureID, m_asset.Length, currentPosition, imagePacketSize, e.Message);
  251. return false;
  252. }
  253. //Send the packet
  254. client.SendImageNextPart((ushort)(m_currentPacket - 1), TextureID, imageData);
  255. }
  256. return !complete;
  257. }
  258. catch (Exception)
  259. {
  260. return false;
  261. }
  262. }
  263. private ushort TexturePacketCount()
  264. {
  265. if (!IsDecoded)
  266. return 0;
  267. if (m_asset == null)
  268. return 0;
  269. if (m_asset.Length <= FIRST_PACKET_SIZE)
  270. return 1;
  271. return (ushort)(((m_asset.Length - FIRST_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1);
  272. }
  273. private int GetPacketForBytePosition(int bytePosition)
  274. {
  275. return ((bytePosition - FIRST_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1;
  276. }
  277. private int LastPacketSize()
  278. {
  279. if (m_currentPacket == 1)
  280. return m_asset.Length;
  281. int lastsize = (m_asset.Length - FIRST_PACKET_SIZE) % IMAGE_PACKET_SIZE;
  282. //If the last packet size is zero, it's really cImagePacketSize, it sits on the boundary
  283. if (lastsize == 0)
  284. {
  285. lastsize = IMAGE_PACKET_SIZE;
  286. }
  287. return lastsize;
  288. }
  289. private int CurrentBytePosition()
  290. {
  291. if (m_currentPacket == 0)
  292. return 0;
  293. if (m_currentPacket == 1)
  294. return FIRST_PACKET_SIZE;
  295. int result = FIRST_PACKET_SIZE + ((int)m_currentPacket - 2) * IMAGE_PACKET_SIZE;
  296. if (result < 0)
  297. {
  298. result = FIRST_PACKET_SIZE;
  299. }
  300. return result;
  301. }
  302. private void J2KDecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers)
  303. {
  304. Layers = layers;
  305. IsDecoded = true;
  306. RunUpdate();
  307. }
  308. private void AssetDataCallback(UUID AssetID, AssetBase asset)
  309. {
  310. HasAsset = true;
  311. if (asset == null || asset.Data == null)
  312. {
  313. if (m_imageManager.MissingImage != null)
  314. {
  315. m_asset = m_imageManager.MissingImage.Data;
  316. }
  317. else
  318. {
  319. m_asset = null;
  320. IsDecoded = true;
  321. }
  322. }
  323. else
  324. {
  325. m_asset = asset.Data;
  326. }
  327. RunUpdate();
  328. }
  329. private void AssetReceived(string id, Object sender, AssetBase asset)
  330. {
  331. UUID assetID = UUID.Zero;
  332. if (asset != null)
  333. assetID = asset.FullID;
  334. else if ((HyperAssets != null) && (sender != HyperAssets))
  335. {
  336. // Try the user's inventory, but only if it's different from the regions'
  337. string userAssets = HyperAssets.GetUserAssetServer(AgentID);
  338. if ((userAssets != string.Empty) && (userAssets != HyperAssets.GetSimAssetServer()))
  339. {
  340. m_log.DebugFormat("[J2KIMAGE]: texture {0} not found in local asset storage. Trying user's storage.", id);
  341. AssetService.Get(userAssets + "/" + id, HyperAssets, AssetReceived);
  342. return;
  343. }
  344. }
  345. AssetDataCallback(assetID, asset);
  346. }
  347. }
  348. }