diff options
Diffstat (limited to 'OpenSim/Region/Environment/Modules/Agent/TextureSender/TextureSender.cs')
-rw-r--r-- | OpenSim/Region/Environment/Modules/Agent/TextureSender/TextureSender.cs | 307 |
1 files changed, 100 insertions, 207 deletions
diff --git a/OpenSim/Region/Environment/Modules/Agent/TextureSender/TextureSender.cs b/OpenSim/Region/Environment/Modules/Agent/TextureSender/TextureSender.cs index 65ca854..cd61798 100644 --- a/OpenSim/Region/Environment/Modules/Agent/TextureSender/TextureSender.cs +++ b/OpenSim/Region/Environment/Modules/Agent/TextureSender/TextureSender.cs | |||
@@ -27,188 +27,106 @@ | |||
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Reflection; | 29 | using System.Reflection; |
30 | using OpenMetaverse.Packets; | ||
30 | using log4net; | 31 | using log4net; |
31 | using OpenSim.Framework; | 32 | using OpenSim.Framework; |
32 | using OpenSim.Region.Environment.Interfaces; | 33 | using OpenSim.Region.Environment.Interfaces; |
33 | 34 | ||
34 | namespace OpenSim.Region.Environment.Modules.Agent.TextureSender | 35 | namespace OpenSim.Region.Environment.Modules.Agent.TextureSender |
35 | { | 36 | { |
36 | public class ImageDownload | 37 | /// <summary> |
38 | /// A TextureSender handles the process of receiving a texture requested by the client from the | ||
39 | /// AssetCache, and then sending that texture back to the client. | ||
40 | /// </summary> | ||
41 | public class TextureSender : ITextureSender | ||
37 | { | 42 | { |
38 | public const int FIRST_IMAGE_PACKET_SIZE = 600; | 43 | private static readonly ILog m_log |
39 | public const int IMAGE_PACKET_SIZE = 1000; | 44 | = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
40 | |||
41 | public OpenMetaverse.AssetTexture Texture; | ||
42 | public int DiscardLevel; | ||
43 | public float Priority; | ||
44 | public int CurrentPacket; | ||
45 | public int StopPacket; | ||
46 | |||
47 | public ImageDownload(OpenMetaverse.AssetTexture texture, int discardLevel, float priority, int packet) | ||
48 | { | ||
49 | Texture = texture; | ||
50 | Update(discardLevel, priority, packet); | ||
51 | } | ||
52 | |||
53 | /// <summary> | ||
54 | /// Updates an image transfer with new information and recalculates | ||
55 | /// offsets | ||
56 | /// </summary> | ||
57 | /// <param name="discardLevel">New requested discard level</param> | ||
58 | /// <param name="priority">New requested priority</param> | ||
59 | /// <param name="packet">New requested packet offset</param> | ||
60 | public void Update(int discardLevel, float priority, int packet) | ||
61 | { | ||
62 | Priority = priority; | ||
63 | DiscardLevel = Clamp(discardLevel, 0, Texture.LayerInfo.Length - 1); | ||
64 | StopPacket = GetPacketForBytePosition(Texture.LayerInfo[(Texture.LayerInfo.Length - 1) - DiscardLevel].End); | ||
65 | CurrentPacket = Clamp(packet, 1, TexturePacketCount()); | ||
66 | } | ||
67 | |||
68 | /// <summary> | ||
69 | /// Returns the total number of packets needed to transfer this texture, | ||
70 | /// including the first packet of size FIRST_IMAGE_PACKET_SIZE | ||
71 | /// </summary> | ||
72 | /// <returns>Total number of packets needed to transfer this texture</returns> | ||
73 | public int TexturePacketCount() | ||
74 | { | ||
75 | return ((Texture.AssetData.Length - FIRST_IMAGE_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1; | ||
76 | } | ||
77 | 45 | ||
78 | /// <summary> | 46 | /// <summary> |
79 | /// Returns the current byte offset for this transfer, calculated from | 47 | /// Records the number of times texture send has been called. |
80 | /// the CurrentPacket | ||
81 | /// </summary> | 48 | /// </summary> |
82 | /// <returns>Current byte offset for this transfer</returns> | 49 | public int counter = 0; |
83 | public int CurrentBytePosition() | ||
84 | { | ||
85 | return FIRST_IMAGE_PACKET_SIZE + (CurrentPacket - 1) * IMAGE_PACKET_SIZE; | ||
86 | } | ||
87 | 50 | ||
88 | /// <summary> | 51 | public bool ImageLoaded = false; |
89 | /// Returns the size, in bytes, of the last packet. This will be somewhere | ||
90 | /// between 1 and IMAGE_PACKET_SIZE bytes | ||
91 | /// </summary> | ||
92 | /// <returns>Size of the last packet in the transfer</returns> | ||
93 | public int LastPacketSize() | ||
94 | { | ||
95 | return Texture.AssetData.Length - (FIRST_IMAGE_PACKET_SIZE + ((TexturePacketCount() - 2) * IMAGE_PACKET_SIZE)); | ||
96 | } | ||
97 | 52 | ||
98 | /// <summary> | 53 | /// <summary> |
99 | /// Find the packet number that contains a given byte position | 54 | /// Holds the texture asset to send. |
100 | /// </summary> | 55 | /// </summary> |
101 | /// <param name="bytePosition">Byte position</param> | 56 | private AssetBase m_asset; |
102 | /// <returns>Packet number that contains the given byte position</returns> | ||
103 | int GetPacketForBytePosition(int bytePosition) | ||
104 | { | ||
105 | return ((bytePosition - FIRST_IMAGE_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE); | ||
106 | } | ||
107 | 57 | ||
108 | /// <summary> | 58 | //public UUID assetID { get { return m_asset.FullID; } } |
109 | /// Clamp a given value between a range | ||
110 | /// </summary> | ||
111 | /// <param name="value">Value to clamp</param> | ||
112 | /// <param name="min">Minimum allowable value</param> | ||
113 | /// <param name="max">Maximum allowable value</param> | ||
114 | /// <returns>A value inclusively between lower and upper</returns> | ||
115 | static int Clamp(int value, int min, int max) | ||
116 | { | ||
117 | // First we check to see if we're greater than the max | ||
118 | value = (value > max) ? max : value; | ||
119 | 59 | ||
120 | // Then we check to see if we're less than the min. | 60 | // private bool m_cancel = false; |
121 | value = (value < min) ? min : value; | ||
122 | 61 | ||
123 | // There's no check to see if min > max. | 62 | // See ITextureSender |
124 | return value; | ||
125 | } | ||
126 | } | ||
127 | 63 | ||
128 | /// <summary> | 64 | // private bool m_sending = false; |
129 | /// A TextureSender handles the process of receiving a texture requested by the client from the | ||
130 | /// AssetCache, and then sending that texture back to the client. | ||
131 | /// </summary> | ||
132 | public class TextureSender : ITextureSender | ||
133 | { | ||
134 | private static readonly ILog m_log | ||
135 | = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
136 | 65 | ||
137 | public bool ImageLoaded = false; | 66 | /// <summary> |
67 | /// This is actually the number of extra packets required to send the texture data! We always assume | ||
68 | /// at least one is required. | ||
69 | /// </summary> | ||
70 | private int NumPackets = 0; | ||
138 | 71 | ||
139 | /// <summary> | 72 | /// <summary> |
140 | /// Holds the texture asset to send. | 73 | /// Holds the packet number to send next. In this case, each packet is 1000 bytes long and starts |
74 | /// at the 600th byte (0th indexed). | ||
141 | /// </summary> | 75 | /// </summary> |
142 | private AssetBase m_asset; | 76 | private int PacketCounter = 0; |
143 | private bool m_cancel = false; | ||
144 | private bool m_sending = false; | ||
145 | private bool sendFirstPacket = false; | ||
146 | private int initialDiscardLevel = 0; | ||
147 | private int initialPacketNum = 0; | ||
148 | private float initialPriority = 0.0f; | ||
149 | 77 | ||
150 | private ImageDownload download; | 78 | private int RequestedDiscardLevel = -1; |
151 | private IClientAPI RequestUser; | 79 | private IClientAPI RequestUser; |
80 | private uint StartPacketNumber = 0; | ||
152 | 81 | ||
153 | public TextureSender(IClientAPI client, int discardLevel, uint packetNumber, float priority) | 82 | public TextureSender(IClientAPI client, int discardLevel, uint packetNumber) |
154 | { | 83 | { |
155 | RequestUser = client; | 84 | RequestUser = client; |
156 | initialDiscardLevel = discardLevel; | 85 | RequestedDiscardLevel = discardLevel; |
157 | initialPacketNum = (int)packetNumber; | 86 | StartPacketNumber = packetNumber; |
158 | initialPriority = priority; | ||
159 | } | 87 | } |
160 | 88 | ||
161 | #region ITextureSender Members | 89 | #region ITextureSender Members |
162 | 90 | ||
163 | public bool Cancel | 91 | public bool Cancel |
164 | { | 92 | { |
165 | get { return m_cancel; } | 93 | get { return false; } |
166 | set { m_cancel = value; } | 94 | set |
95 | { | ||
96 | // m_cancel = value; | ||
97 | } | ||
167 | } | 98 | } |
168 | 99 | ||
169 | public bool Sending | 100 | public bool Sending |
170 | { | 101 | { |
171 | get { return m_sending; } | 102 | get { return false; } |
172 | set { m_sending = value; } | 103 | set |
104 | { | ||
105 | // m_sending = value; | ||
106 | } | ||
173 | } | 107 | } |
174 | 108 | ||
175 | // See ITextureSender | 109 | // See ITextureSender |
176 | public void UpdateRequest(int discardLevel, uint packetNumber) | 110 | public void UpdateRequest(int discardLevel, uint packetNumber) |
177 | { | 111 | { |
178 | if (download == null) | 112 | RequestedDiscardLevel = discardLevel; |
179 | return; | 113 | StartPacketNumber = packetNumber; |
180 | 114 | PacketCounter = (int) StartPacketNumber; | |
181 | lock (download) | ||
182 | { | ||
183 | if (discardLevel < download.DiscardLevel) | ||
184 | m_log.DebugFormat("Image download {0} is changing from DiscardLevel {1} to {2}", | ||
185 | m_asset.FullID, download.DiscardLevel, discardLevel); | ||
186 | |||
187 | if (packetNumber != download.CurrentPacket) | ||
188 | m_log.DebugFormat("Image download {0} is changing from Packet {1} to {2}", | ||
189 | m_asset.FullID, download.CurrentPacket, packetNumber); | ||
190 | |||
191 | download.Update(discardLevel, download.Priority, (int)packetNumber); | ||
192 | |||
193 | sendFirstPacket = true; | ||
194 | } | ||
195 | } | 115 | } |
196 | 116 | ||
197 | // See ITextureSender | 117 | // See ITextureSender |
198 | public bool SendTexturePacket() | 118 | public bool SendTexturePacket() |
199 | { | 119 | { |
200 | if (download != null && !m_cancel && (sendFirstPacket || download.CurrentPacket <= download.StopPacket)) | 120 | //m_log.DebugFormat("[TEXTURE SENDER]: Sending packet for {0}", m_asset.FullID); |
201 | { | 121 | |
202 | SendPacket(); | 122 | SendPacket(); |
203 | return false; | 123 | counter++; |
204 | } | 124 | if ((NumPackets == 0) || (RequestedDiscardLevel == -1) || (PacketCounter > NumPackets) || |
205 | else | 125 | ((RequestedDiscardLevel > 0) && (counter > 50 + (NumPackets / (RequestedDiscardLevel + 1))))) |
206 | { | 126 | { |
207 | m_sending = false; | ||
208 | m_cancel = true; | ||
209 | sendFirstPacket = false; | ||
210 | return true; | 127 | return true; |
211 | } | 128 | } |
129 | return false; | ||
212 | } | 130 | } |
213 | 131 | ||
214 | #endregion | 132 | #endregion |
@@ -222,52 +140,9 @@ namespace OpenSim.Region.Environment.Modules.Agent.TextureSender | |||
222 | public void TextureReceived(AssetBase asset) | 140 | public void TextureReceived(AssetBase asset) |
223 | { | 141 | { |
224 | m_asset = asset; | 142 | m_asset = asset; |
225 | 143 | NumPackets = CalculateNumPackets(asset.Data.Length); | |
226 | try | 144 | PacketCounter = (int) StartPacketNumber; |
227 | { | 145 | ImageLoaded = true; |
228 | OpenMetaverse.AssetTexture texture = new OpenMetaverse.AssetTexture(m_asset.FullID, m_asset.Data); | ||
229 | if (texture.DecodeLayerBoundaries()) | ||
230 | { | ||
231 | bool sane = true; | ||
232 | |||
233 | // Sanity check all of the layers | ||
234 | for (int i = 0; i < texture.LayerInfo.Length; i++) | ||
235 | { | ||
236 | if (texture.LayerInfo[i].End > texture.AssetData.Length) | ||
237 | { | ||
238 | sane = false; | ||
239 | break; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | if (sane) | ||
244 | { | ||
245 | download = new ImageDownload(texture, initialDiscardLevel, initialPriority, initialPacketNum); | ||
246 | ImageLoaded = true; | ||
247 | m_sending = true; | ||
248 | m_cancel = false; | ||
249 | sendFirstPacket = true; | ||
250 | return; | ||
251 | } | ||
252 | else | ||
253 | { | ||
254 | m_log.Error("JPEG2000 texture decoding succeeded, but sanity check failed for " + | ||
255 | m_asset.FullID.ToString()); | ||
256 | } | ||
257 | } | ||
258 | else | ||
259 | { | ||
260 | m_log.Error("JPEG2000 texture decoding failed for " + m_asset.FullID.ToString()); | ||
261 | } | ||
262 | } | ||
263 | catch (Exception ex) | ||
264 | { | ||
265 | m_log.Error("JPEG2000 texture decoding threw an exception for " + m_asset.FullID.ToString(), ex); | ||
266 | } | ||
267 | |||
268 | ImageLoaded = false; | ||
269 | m_sending = false; | ||
270 | m_cancel = true; | ||
271 | } | 146 | } |
272 | 147 | ||
273 | /// <summary> | 148 | /// <summary> |
@@ -275,48 +150,66 @@ namespace OpenSim.Region.Environment.Modules.Agent.TextureSender | |||
275 | /// </summary> | 150 | /// </summary> |
276 | private void SendPacket() | 151 | private void SendPacket() |
277 | { | 152 | { |
278 | lock (download) | 153 | if (PacketCounter <= NumPackets) |
279 | { | 154 | { |
280 | if (sendFirstPacket) | 155 | if (PacketCounter == 0) |
281 | { | 156 | { |
282 | sendFirstPacket = false; | 157 | if (NumPackets == 0) |
283 | |||
284 | if (m_asset.Data.Length <= ImageDownload.FIRST_IMAGE_PACKET_SIZE) | ||
285 | { | 158 | { |
286 | RequestUser.SendImageFirstPart(1, m_asset.FullID, (uint)m_asset.Data.Length, m_asset.Data, 2); | 159 | RequestUser.SendImageFirstPart(1, m_asset.FullID, (uint)m_asset.Data.Length, m_asset.Data, 2); |
287 | return; | 160 | PacketCounter++; |
288 | } | 161 | } |
289 | else | 162 | else |
290 | { | 163 | { |
291 | byte[] firstImageData = new byte[ImageDownload.FIRST_IMAGE_PACKET_SIZE]; | 164 | byte[] ImageData1 = new byte[600]; |
292 | try { Buffer.BlockCopy(m_asset.Data, 0, firstImageData, 0, ImageDownload.FIRST_IMAGE_PACKET_SIZE); } | 165 | Array.Copy(m_asset.Data, 0, ImageData1, 0, 600); |
293 | catch (Exception) | 166 | |
294 | { | 167 | RequestUser.SendImageFirstPart( |
295 | m_log.Error("Texture data copy failed on first packet for " + m_asset.FullID.ToString()); | 168 | (ushort)(NumPackets), m_asset.FullID, (uint)m_asset.Data.Length, ImageData1, 2); |
296 | m_cancel = true; | 169 | PacketCounter++; |
297 | m_sending = false; | ||
298 | return; | ||
299 | } | ||
300 | RequestUser.SendImageFirstPart((ushort)download.TexturePacketCount(), m_asset.FullID, (uint)m_asset.Data.Length, firstImageData, 2); | ||
301 | } | 170 | } |
302 | } | 171 | } |
303 | 172 | else | |
304 | int imagePacketSize = (download.CurrentPacket == download.TexturePacketCount() - 1) ? | ||
305 | download.LastPacketSize() : ImageDownload.IMAGE_PACKET_SIZE; | ||
306 | |||
307 | byte[] imageData = new byte[imagePacketSize]; | ||
308 | try { Buffer.BlockCopy(m_asset.Data, download.CurrentBytePosition(), imageData, 0, imagePacketSize); } | ||
309 | catch (Exception) | ||
310 | { | 173 | { |
311 | m_log.Error("Texture data copy failed for " + m_asset.FullID.ToString()); | 174 | int size = m_asset.Data.Length - 600 - (1000 * (PacketCounter - 1)); |
312 | m_cancel = true; | 175 | if (size > 1000) size = 1000; |
313 | m_sending = false; | 176 | byte[] imageData = new byte[size]; |
314 | return; | 177 | try |
178 | { | ||
179 | Array.Copy(m_asset.Data, 600 + (1000 * (PacketCounter - 1)), imageData, 0, size); | ||
180 | } | ||
181 | catch (ArgumentOutOfRangeException) | ||
182 | { | ||
183 | m_log.Error("[TEXTURE SENDER]: Unable to separate texture into multiple packets: Array bounds failure on asset:" + | ||
184 | m_asset.FullID.ToString()); | ||
185 | return; | ||
186 | } | ||
187 | |||
188 | RequestUser.SendImageNextPart((ushort)PacketCounter, m_asset.FullID, imageData); | ||
189 | PacketCounter++; | ||
315 | } | 190 | } |
191 | } | ||
192 | } | ||
316 | 193 | ||
317 | RequestUser.SendImageNextPart((ushort)download.CurrentPacket, m_asset.FullID, imageData); | 194 | /// <summary> |
318 | ++download.CurrentPacket; | 195 | /// Calculate the number of packets that will be required to send the texture loaded into this sender |
196 | /// This is actually the number of 1000 byte packets not including an initial 600 byte packet... | ||
197 | /// </summary> | ||
198 | /// <param name="length"></param> | ||
199 | /// <returns></returns> | ||
200 | private int CalculateNumPackets(int length) | ||
201 | { | ||
202 | int numPackets = 0; | ||
203 | |||
204 | if (length > 600) | ||
205 | { | ||
206 | //over 600 bytes so split up file | ||
207 | int restData = (length - 600); | ||
208 | int restPackets = ((restData + 999) / 1000); | ||
209 | numPackets = restPackets; | ||
319 | } | 210 | } |
211 | |||
212 | return numPackets; | ||
320 | } | 213 | } |
321 | } | 214 | } |
322 | } | 215 | } |