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