Now you can download a copy of these docs so you can use them offline! Download now
AxisCamera.cpp
1 /*----------------------------------------------------------------------------*/
2 /* Copyright (c) FIRST 2008. All Rights Reserved. */
3 /* Open Source Software - may be modified and shared by FRC teams. The code */
4 /* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
5 /*----------------------------------------------------------------------------*/
6 
7 #include "Vision/AxisCamera.h"
8 
9 #include <string.h>
10 #include "NetworkCommunication/UsageReporting.h"
11 #include "Synchronized.h"
12 #include "Vision/PCVideoServer.h"
13 #include "WPIErrors.h"
14 
16 IMAQ_FUNC int Priv_ReadJPEGString_C(Image* _image, const unsigned char* _string, uint32_t _stringLength);
17 
18 // Max packet without jumbo frames is 1500... add 36 because??
19 #define kMaxPacketSize 1536
20 #define kImageBufferAllocationIncrement 1000
21 
22 AxisCamera *AxisCamera::_instance = NULL;
23 
27 AxisCamera::AxisCamera(const char *ipAddress)
28  : AxisCameraParams(ipAddress)
29  , m_cameraSocket(ERROR)
30  , m_protectedImageBuffer(NULL)
31  , m_protectedImageBufferLength(0)
32  , m_protectedImageSize(0)
33  , m_protectedImageSem(NULL)
34  , m_freshImage(false)
35  , m_imageStreamTask("cameraTask", (FUNCPTR)s_ImageStreamTaskFunction)
36  , m_videoServer(NULL)
37 {
38  m_protectedImageSem = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE);
39 
40 #if JAVA_CAMERA_LIB != 1
41  nUsageReporting::report(nUsageReporting::kResourceType_AxisCamera, ipAddress == NULL ? 1 : 2);
42 #endif
43 
44  if (!StatusIsFatal())
45  m_imageStreamTask.Start((int)this);
46 }
47 
52 {
53  delete m_videoServer;
54  m_videoServer = NULL;
55 
56  m_imageStreamTask.Stop();
57  close(m_cameraSocket);
58 
59  SemSet_t::iterator it = m_newImageSemSet.begin();
60  SemSet_t::iterator end = m_newImageSemSet.end();
61  for (;it != end; it++)
62  {
63  semDelete(*it);
64  }
65  m_newImageSemSet.clear();
66 
67  semDelete(m_protectedImageSem);
68 }
69 
75 AxisCamera &AxisCamera::GetInstance(const char *cameraIP)
76 {
77  if (NULL == _instance)
78  {
79  _instance = new AxisCamera(cameraIP);
80 
81  _instance->m_videoServer = new PCVideoServer();
82  }
83 
84  return *_instance;
85 }
86 
91 {
92  delete _instance;
93  _instance = NULL;
94 }
95 
101 {
102  return m_freshImage;
103 }
104 
114 {
115  SEM_ID sem = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY);
116  m_newImageSemSet.insert(sem);
117  return sem;
118 }
119 
126 int AxisCamera::GetImage(Image* imaqImage)
127 {
128  if (m_protectedImageBuffer == NULL)
129  return 0;
130  Synchronized sync(m_protectedImageSem);
131  Priv_ReadJPEGString_C(imaqImage,
132  (unsigned char*)m_protectedImageBuffer, m_protectedImageSize);
133  m_freshImage = false;
134  return 1;
135 }
136 
137 #if JAVA_CAMERA_LIB != 1
138 
144 {
145  return GetImage(image->GetImaqImage());
146 }
147 
155 {
156  HSLImage *image = new HSLImage();
157  GetImage(image);
158  return image;
159 }
160 #endif
161 
172 int AxisCamera::CopyJPEG(char **destImage, int &destImageSize, int &destImageBufferSize)
173 {
174  Synchronized sync(m_protectedImageSem);
175  if (destImage == NULL)
176  wpi_setWPIErrorWithContext(NullParameter, "destImage must not be NULL");
177 
178  if (m_protectedImageBuffer == NULL || m_protectedImageSize <= 0)
179  return 0; // if no source image
180 
181  if (destImageBufferSize < m_protectedImageSize) // if current destination buffer too small
182  {
183  if (*destImage != NULL) delete [] *destImage;
184  destImageBufferSize = m_protectedImageSize + kImageBufferAllocationIncrement;
185  *destImage = new char[destImageBufferSize];
186  if (*destImage == NULL) return 0;
187  }
188  // copy this image into destination buffer
189  if (*destImage == NULL)
190  {
191  wpi_setWPIErrorWithContext(NullParameter, "*destImage must not be NULL");
192  }
193  // TODO: Is this copy realy necessary... perhaps we can simply transmit while holding the protected buffer
194  memcpy(*destImage, m_protectedImageBuffer, m_protectedImageSize);
195  destImageSize = m_protectedImageSize;
196  return 1;
197 }
198 
203 int AxisCamera::s_ImageStreamTaskFunction(AxisCamera *thisPtr)
204 {
205  return thisPtr->ImageStreamTaskFunction();
206 }
207 
213 int AxisCamera::ImageStreamTaskFunction()
214 {
215  // Loop on trying to setup the camera connection. This happens in a background
216  // thread so it shouldn't effect the operation of user programs.
217  while (1)
218  {
219  const char *requestString = "GET /mjpg/video.mjpg HTTP/1.1\n\
220 User-Agent: HTTPStreamClient\n\
221 Connection: Keep-Alive\n\
222 Cache-Control: no-cache\n\
223 Authorization: Basic RlJDOkZSQw==\n\n";
224  semTake(m_socketPossessionSem, WAIT_FOREVER);
225  m_cameraSocket = CreateCameraSocket(requestString);
226  if (m_cameraSocket == ERROR)
227  {
228  // Don't hammer the camera if it isn't ready.
229  semGive(m_socketPossessionSem);
230  taskDelay(1000);
231  }
232  else
233  {
234  ReadImagesFromCamera();
235  }
236  }
237  return 0;
238 }
239 
243 int AxisCamera::ReadImagesFromCamera()
244 {
245  char *imgBuffer = NULL;
246  int imgBufferLength = 0;
247  //Infinite loop, task deletion handled by taskDeleteHook
248  // Socket cleanup handled by destructor
249 
250  // TODO: these recv calls must be non-blocking. Otherwise if the camera
251  // fails during a read, the code hangs and never retries when the camera comes
252  // back up.
253 
254  int counter = 2;
255  while (1)
256  {
257  char initialReadBuffer[kMaxPacketSize] = "";
258  char intermediateBuffer[1];
259  char *trailingPtr = initialReadBuffer;
260  int trailingCounter = 0;
261  while (counter)
262  {
263  // TODO: fix me... this cannot be the most efficient way to approach this, reading one byte at a time.
264  if(recv(m_cameraSocket, intermediateBuffer, 1, 0) == ERROR)
265  {
266  wpi_setErrnoErrorWithContext("Failed to read image header");
267  close (m_cameraSocket);
268  return ERROR;
269  }
270  strncat(initialReadBuffer, intermediateBuffer, 1);
271  // trailingCounter ensures that we start looking for the 4 byte string after
272  // there is at least 4 bytes total. Kind of obscure.
273  // look for 2 blank lines (\r\n)
274  if (NULL != strstr(trailingPtr, "\r\n\r\n"))
275  {
276  --counter;
277  }
278  if (++trailingCounter >= 4)
279  {
280  trailingPtr++;
281  }
282  }
283  counter = 1;
284  char *contentLength = strstr(initialReadBuffer, "Content-Length: ");
285  if (contentLength == NULL)
286  {
287  wpi_setWPIErrorWithContext(IncompatibleMode, "No content-length token found in packet");
288  close(m_cameraSocket);
289  return ERROR;
290  }
291  contentLength = contentLength + 16; // skip past "content length"
292  int readLength = atol(contentLength); // get the image byte count
293 
294  // Make sure buffer is large enough
295  if (imgBufferLength < readLength)
296  {
297  if (imgBuffer) delete[] imgBuffer;
298  imgBufferLength = readLength + kImageBufferAllocationIncrement;
299  imgBuffer = new char[imgBufferLength];
300  if (imgBuffer == NULL)
301  {
302  imgBufferLength = 0;
303  continue;
304  }
305  }
306 
307  // Read the image data for "Content-Length" bytes
308  int bytesRead = 0;
309  int remaining = readLength;
310  while(bytesRead < readLength)
311  {
312  int bytesThisRecv = recv(m_cameraSocket, &imgBuffer[bytesRead], remaining, 0);
313  bytesRead += bytesThisRecv;
314  remaining -= bytesThisRecv;
315  }
316  // Update image
317  UpdatePublicImageFromCamera(imgBuffer, readLength);
318  if (semTake(m_paramChangedSem, NO_WAIT) == OK)
319  {
320  // params need to be updated: close the video stream; release the camera.
321  close(m_cameraSocket);
322  semGive(m_socketPossessionSem);
323  return 0;
324  }
325  }
326 }
327 
333 void AxisCamera::UpdatePublicImageFromCamera(char *imgBuffer, int imgSize)
334 {
335  {
336  Synchronized sync(m_protectedImageSem);
337 
338  // Adjust the buffer size if current destination buffer is too small.
339  if (m_protectedImageBufferLength < imgSize)
340  {
341  if (m_protectedImageBuffer != NULL) delete [] m_protectedImageBuffer;
342  m_protectedImageBufferLength = imgSize + kImageBufferAllocationIncrement;
343  m_protectedImageBuffer = new char[m_protectedImageBufferLength];
344  if (m_protectedImageBuffer == NULL)
345  {
346  m_protectedImageBufferLength = 0;
347  return;
348  }
349  }
350 
351  memcpy(m_protectedImageBuffer, imgBuffer, imgSize);
352  m_protectedImageSize = imgSize;
353  }
354 
355  m_freshImage = true;
356  // Notify everyone who is interested.
357  SemSet_t::iterator it = m_newImageSemSet.begin();
358  SemSet_t::iterator end = m_newImageSemSet.end();
359  for (;it != end; it++)
360  {
361  semGive(*it);
362  }
363 }
364 
368 void AxisCamera::RestartCameraTask()
369 {
370  m_imageStreamTask.Stop();
371  m_imageStreamTask.Start((int)this);
372 }
373 
374 #if JAVA_CAMERA_LIB == 1
375 
376 // C bindings used by Java
377 // These need to stay as is or Java has to change
378 
379 void AxisCameraStart(const char *IPAddress)
380 {
381 #ifdef SVN_REV
382  if (strlen(SVN_REV))
383  {
384  printf("JavaCameraLib was compiled from SVN revision %s\n", SVN_REV);
385  }
386  else
387  {
388  printf("JavaCameraLib was compiled from a location that is not source controlled.\n");
389  }
390 #else
391  printf("JavaCameraLib was compiled without -D'SVN_REV=nnnn'\n");
392 #endif
393  AxisCamera::GetInstance(IPAddress);
394 }
395 
396 int AxisCameraGetImage (Image* image)
397 {
398  return AxisCamera::GetInstance().GetImage(image);
399 }
400 
401 void AxisCameraWriteBrightness(int brightness)
402 {
404 }
405 
406 int AxisCameraGetBrightness()
407 {
409 }
410 
411 void AxisCameraWriteWhiteBalance(AxisCameraParams::WhiteBalance_t whiteBalance)
412 {
414 }
415 
416 AxisCameraParams::WhiteBalance_t AxisCameraGetWhiteBalance()
417 {
419 }
420 
421 void AxisCameraWriteColorLevel(int colorLevel)
422 {
424 }
425 
426 int AxisCameraGetColorLevel()
427 {
429 }
430 
431 void AxisCameraWriteExposureControl(AxisCameraParams::Exposure_t exposure)
432 {
434 }
435 
436 AxisCameraParams::Exposure_t AxisCameraGetExposureControl()
437 {
439 }
440 
441 void AxisCameraWriteExposurePriority(int exposure)
442 {
444 }
445 
446 int AxisCameraGetExposurePriority()
447 {
448  return AxisCamera::GetInstance().GetExposurePriority();
449 }
450 
451 void AxisCameraWriteMaxFPS(int maxFPS)
452 {
454 }
455 
456 int AxisCameraGetMaxFPS()
457 {
459 }
460 
461 void AxisCameraWriteResolution(AxisCameraParams::Resolution_t resolution)
462 {
464 }
465 
466 AxisCameraParams::Resolution_t AxisCameraGetResolution()
467 {
469 }
470 
471 void AxisCameraWriteCompression(int compression)
472 {
474 }
475 
476 int AxisCameraGetCompression()
477 {
479 }
480 
481 void AxisCameraWriteRotation(AxisCameraParams::Rotation_t rotation)
482 {
484 }
485 
486 AxisCameraParams::Rotation_t AxisCameraGetRotation()
487 {
489 }
490 
491 void AxisCameraDeleteInstance()
492 {
494 }
495 
496 int AxisCameraFreshImage()
497 {
499 }
500 
501 #endif // JAVA_CAMERA_LIB == 1
502 
void WriteExposurePriority(int)
HSLImage * GetImage()
Definition: AxisCamera.cpp:154
int CopyJPEG(char **destImage, int &destImageSize, int &destImageBufferSize)
Definition: AxisCamera.cpp:172
int GetImage(Image *imaqImage)
Definition: AxisCamera.cpp:126
bool Start(uint32_t arg0=0, uint32_t arg1=0, uint32_t arg2=0, uint32_t arg3=0, uint32_t arg4=0, uint32_t arg5=0, uint32_t arg6=0, uint32_t arg7=0, uint32_t arg8=0, uint32_t arg9=0)
Definition: Task.cpp:52
Resolution_t GetResolution()
static void DeleteInstance()
Definition: AxisCamera.cpp:90
void WriteExposureControl(Exposure_t)
Exposure_t GetExposureControl()
bool IsFreshImage()
Definition: AxisCamera.cpp:100
bool Stop()
Definition: Task.cpp:81
WhiteBalance_t GetWhiteBalance()
void WriteResolution(Resolution_t)
Rotation_t GetRotation()
Image * GetImaqImage()
Definition: ImageBase.cpp:73
void WriteWhiteBalance(WhiteBalance_t whiteBalance)
virtual ~AxisCamera()
Definition: AxisCamera.cpp:51
static AxisCamera & GetInstance(const char *cameraIP=NULL)
Definition: AxisCamera.cpp:75
SEM_ID GetNewImageSem()
Definition: AxisCamera.cpp:113
void WriteRotation(Rotation_t)

Generated on Sat Apr 26 2014 12:26:45 for WPILibC++ by doxygen 1.8.6