Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Native Development

Reply
New Contributor
EverySecond
Posts: 7
Registered: ‎12-02-2012
My Device: Playbook
Accepted Solution

playbook ndk - camera_take_photo returning with error 17 - file exists

Dear Developer mates,

 

I'm using C++ NDK for blackberry playbook (testing with playbook hardware) and a problem came into my way. I can take pictures with camera_take_photo function. I'm trying to make capture faster so I'm setting the wait parameter to false. In this case camera_take_photo will not block the execution of my application.

 

The problem is that if the last call for camera_take_photo's image_callback function has not completed yet and I call camera_take_photo again I'll get error as result. Error code is: 17 which is not related to camera_error_t. I had checked in errno.h and it is:

 

My question is that what kind of file exists? As I know camera_take_photo is not saving any file (only when I implement saving picture).

 

Error 17 in errno.h:

#define EEXIST          17  /* File exists  */

My implementation:

void CameraModule::takePhoto() {
camera_error_t result1 = camera_take_photo(this->m_CameraHandle, NULL, NULL, NULL, CameraModule::photoImageCallback, NULL, false);
}

void CameraModule::photoImageCallback(camera_handle_t cameraHandle1, camera_buffer_t * cameraBuffer1, void * params) {
// JPeG is ready and I can save it
// But this is an empty function for testing
sleep (3000); // do nothing for 3 seconds
}

Call:

// Test 1:
takePhoto(); // OK takePhoto(); // Error 17 from camera_take_photo, but I'm not writing any file
// Test 2: takePhoto(); // OK sleep(5000); // Wait some seconds takePhoto(); // OK

Flow chart for representing error:

 

Reference for camera_take_photo usage:

camera_error_t
camera_take_photo(camera_handle_t handle,
                  void (*shutter_callback)(camera_handle_t, void*),
                  void (*raw_callback)(camera_handle_t, camera_buffer_t*,
                                       void*),
                  void (*postview_callback)(camera_handle_t,
                                            camera_buffer_t*, void*),
                  void (*image_callback)(camera_handle_t, camera_buffer_t*,
                                         void*),
                  void *arg,
                  bool wait);

Your help is appreciated in advance.

Thank you.

BlackBerry Development Advisor
smcveigh
Posts: 668
Registered: ‎11-29-2011
My Device: developer

Re: playbook ndk - camera_take_photo returning with error 17 - file exists

Hi, good question.

First off, I should explain what the "bool wait" argument does in camera_take_photo().

It blocks the calling thread until all registered callbacks complete.  Eg. if you call camera_take_photo() with a shutter_callback and an image_callback declared, then the function will remain blocked until both of those callbck functions have returned.

It was implemented this was as a convenience so that the calling code would know when the drivers are ready to capture the next photo.

Listening for the CAMERA_STATUS_CAPTURECOMPLETE status event is another way to know when the picture-taking code is ready for the next capture.

 

By setting wait=false, you are simply bypassing this logic and are responsible for tracking your own callback completion.

 

Unfortunately, you cannot have 2 picture-taking operations in progress at the same time.  This is due to a mix of hardware requirements, the camapi library design, and memory limitations. 

 

The shot-to-shot time is somewhere on the order of 600ms or so I believe, but may vary depending on the platform you are testing on.  If you let me know what your specific use case is, perhaps I can suggest an alternate implementation idea for you.

 

Cheers,

Sean

New Contributor
EverySecond
Posts: 7
Registered: ‎12-02-2012
My Device: Playbook

Re: playbook ndk - camera_take_photo returning with error 17 - file exists

Thank you Sean.

 

I'm trying to implement "burst" function for Playbook. As I know there is no burst option for Playbook cameras (no CAMERA_FEATURE_BURST option), so it is only calling camera_take_photo sequentially. In my case the shot-to-shot takes ~1sec to complete (but I'll disable UI until taking picture and I might be faster). Saving image depends on compressed jpeg size (~300ms).

 

The current idea that I'm trying to implement: setting camera_take_photo to wait=true. In image_callback I created a copy of camera_buffer_t and pushing the data to a separate thread for saving.


Current image callback function:

void CameraModule::photoImageCallback(camera_handle_t cameraHandle1, camera_buffer_t * cameraBuffer1, void * params) {

// Duplicate camera buffer double start1 = LogModule::GetExternalClock(); camera_buffer_t * cameraBufferOut = new camera_buffer_t; memcpy(cameraBufferOut, cameraBuffer1, sizeof(camera_buffer_t)); if (cameraBuffer1->frametype == CAMERA_FRAMETYPE_JPEG) { cameraBufferOut->framebuf = new uint8_t[cameraBuffer1->framedesc.jpeg.bufsize]; memcpy(cameraBufferOut->framebuf, cameraBuffer1->framebuf, cameraBuffer1->framedesc.jpeg.bufsize);

// Pushing to a separate thread
CameraModule::m_TakePhotoHelper->addPicture(pair<camera_buffer_t *, camera_handle_t>(cameraBufferOut, cameraHandle1)); }
// Completed without waiting to save
}

Separate thread function for saving picture:

void CameraTakePhotoHelper::process() {
  while (true) {
    if (this->m_PicturesQueue.size() != 0) {
      camera_buffer_t * cameraBuffer1 = this->m_PicturesQueue[0].first;
      camera_handle_t cameraHandle1 = this->m_PicturesQueue[0].second;
      this->m_PicturesQueue.erase(this->m_PicturesQueue.begin());

      camera_error_t result1 = CAMERA_EOK;
      int filePointer1;
      char fileName1[CAMERA_ROLL_NAMELEN];

      result1 = camera_roll_open_photo(cameraHandle1, &filePointer1, fileName1, CAMERA_ROLL_NAMELEN, CAMERA_ROLL_PHOTO_FMT_JPG);
      if (result1 != CAMERA_EOK) {
        // Always OK, this is only a worst case scenario
        return;
      }

      // Saving
      // ...

      camera_roll_close_photo(filePointer1);

// Deleting camera_buffer_t
// ... } msleep(20); } }

In this case only one camera_take_photo function is running and saving image is done in the background. It is relatively faster. But if I call camera_roll_open_photo in this separate thread while next camera_take_photo is called I got the same error code (17 from camera_take_photo).

 

I'm reimplementing camera roll right now to determine the next filename I could use and seems everything is fine.

 

In the future the best solution will be if I can compress the raw image on a separate thread and not blocking camera_take_photo execution.

 

If you have any suggestion or concerns with this workaround, please feel free to share with me.

 

Thanks,

Gabor

BlackBerry Development Advisor
smcveigh
Posts: 668
Registered: ‎11-29-2011
My Device: developer

Re: playbook ndk - camera_take_photo returning with error 17 - file exists

Hi,

Your approach using wait=true sounds good.  Your plan to copy the buffers into a save queue to be serviced on the other thread also sounds like what I would do.

 

Your callback and processing code is not thread-safe.  The callback could be modifying the queue while your processing thread is inspecting it.  You should be mutex guarding your image queue, and you should probably use a condvar wait/signal instead of that msleep(), but perhaps you will optimize later.

 

You should not be seeing EEXIST (17) from camera_roll_open_photo().  That is weird. Or are you saying that camera_take_photo() is giving you grief?  Oh... wait... are you invoking camera_take_photo() from your picture-saving thread?  You have to be certain that the previous call with wait=true has unblocked and returned before you call camera_take_photo() again.  If you are calling camera_take_photo() from different threads, then you need some additional synchronization on your end.  Simply wrapping the call with a mutex would be enough to ensure that only one thread is calling it at a given time.

 

Cheers,

Sean

 

 

 

BlackBerry Development Advisor
smcveigh
Posts: 668
Registered: ‎11-29-2011
My Device: developer

Re: playbook ndk - camera_take_photo returning with error 17 - file exists

[ Edited ]

The solution I would suggest looks something like the following... excuse the pseudo-code...

void* burst_thread() {
    while(!stop_bursting) {
        camera_take_photo(....., still_callback, ..., true);
    }
    stop_saving = true;
    pthread_cond_signal(&queueCond);  // wake queue thread so it can exit
}

void still_callback(...) {
    to_save = new(...);
    memcpy(to_save, buffer, size);
    pthread_mutex_lock(&queueMutex);
    add_to_save_queue(to_save);
    pthread_cond_signal(&queueCond);   // wake queue thread
    pthread_mutex_unlock(&queueMutex);
}

void* save_thread(...) {
pthread_mutex_lock(&queueMutex); while(!stop_saving) {
if (queue_is_empty()) { pthread_wait(&queueCond, &queueMutex);
} if (!queue_is_empty()) { x = dequeue_buffer(); pthread_mutex_unlock(&queueMutex); // unlock while busy save_buffer(x);
delete x;
pthread_mutex_lock(&queueMutex);
} }
pthread_mutex_unlock(&queueMutex);
// cleanup queue
while(!queue_is_empty()) {
clean up dangling buffers? or save them to disk. whatever. just make sure not to leak memory.
} } void start_bursting() { stop_bursting = stop_saving = false; pthread_create(save_thread); pthread_create(burst_thread); } void stop_bursting() { stop_bursting = true; pthread_join(burst_thread_tid); pthread_join(save_thread_tid); }

 

There is a little bit of extra synchronization needed in the processing thread:  see if (is_queue_empty()).  This is because the thread spends time saving files and may not be blocked on the condvar check when the other thread signals.  The if (is_queue_empty()) check is there to detect such missed signals.

 

When someone hits the "start taking pictures button", you launch the picture-taking thread, and the picture-saving thread.  When someone releases the button, or hits a stop button, you signal the threads to stop and wait for them to finish.

Note that stopping the saving thread is done by the picture-taking thread.  This is to avoid prematurely terminating the saving thread when the callback is still enqueueing a buffer.

 

Cheers,

Sean

New Contributor
EverySecond
Posts: 7
Registered: ‎12-02-2012
My Device: Playbook

Re: playbook ndk - camera_take_photo returning with error 17 - file exists

[ Edited ]

Thank you for your help and time, Sean.

 

I have implemented this queue solution that you have suggested (I'm using Qt with QMutex and QWaitCondition) and it was faster than before. But still not was as fast as I expected. I had to disable all debug messages (fprintf (stdout,...)) and now it is ~1 picture / second which is not bad at all.

 

The base error 17 problem was the same, so I have rewritten the camera roll and now everything is working fine. Nevertheless using dirent for retrieving filenames are much faster than the playbook camera roll.

 

#include <dirent.h>

class CameraRoll {
public:
  // Singleton function
static CameraRoll& getInstace() {
    static CameraRoll instance;
    return instance;
  }
  string GetFilename(camera_handle_t cameraHandle1);
private:
  string m_RollPath;
  string m_BaseFileName;
  string m_Extension;

CameraRoll();
  string LeadZero(string value1, unsigned int numberZero1);
};

CameraRoll::CameraRoll() {
  this->m_RollPath = "";
  this->m_BaseFileName = "IMG_";
  this->m_Extension = "jpg";
}

string CameraRoll::GetFilename(camera_handle_t cameraHandle1) {
  camera_error_t result1 = CAMERA_EOK;
  char pathName1[4096];
  result1 = camera_roll_get_path(cameraHandle1, pathName1, 4096);
  if (result1 != CAMERA_EOK) {
// throw exception ...
    return "";
  } else {
    this->m_RollPath = pathName1;

    // Get directory
    DIR *dir;
    struct dirent *ent;
    dir = opendir(this->m_RollPath.c_str());
    vector<string> fileNames1;
    if (dir != NULL) {
      while ((ent = readdir(dir)) != NULL) {
        fileNames1.push_back(ent->d_name);
      }
      closedir(dir);
    } else {
// throw exception ...
      return "";
    }

    // Get next
    unsigned int max1 = 0;
    for (unsigned int a = 0; a < fileNames1.size(); a++) {
      if (fileNames1[a].find(this->m_BaseFileName) == 0) {
        string value1 = fileNames1[a].substr(this->m_BaseFileName.length(), 8);
        unsigned int value2 = atoi(value1.c_str());
        if (value2 > max1) {
          max1 = value2;
        }
      }
    }

 char index1[15];
  sprintf(index1, "%d", max1 + 1);
  string fileName1 = this->m_RollPath + "/" + this->m_BaseFileName + this->LeadZero(index1, 8) + "."
       + this->m_Extension;
  return fileName1;
  }
}

string CameraRoll::LeadZero(string value1, unsigned int numberZero1) {
  while (value1.length() < numberZero1) {
    value1 = '0' + value1;
  }
  return value1;
}

Sorry for unsafe code with sprintf, atoi and others.

 

I have one other suggestion for people having disk writing speed issue. FILE* is a little bit faster than ofstream.

 

Thank you again and have a great day.

Gabor

BlackBerry Development Advisor
smcveigh
Posts: 668
Registered: ‎11-29-2011
My Device: developer

Re: playbook ndk - camera_take_photo returning with error 17 - file exists

[ Edited ]

I'm still a bit curious where you were getting an EEXIST from in the camera roll.

Was the call to camera_roll_photo_open() failing?

Can you post the exact code you were using to save files previously?

 

(and yes, I believe the original playbook camera roll code may be somewhat inefficient.. should be better on BB10)

New Contributor
EverySecond
Posts: 7
Registered: ‎12-02-2012
My Device: Playbook

Re: playbook ndk - camera_take_photo returning with error 17 - file exists

I'm trying to reproduce the error, but I can't. Still looking for the code in my SVN. I think it was my fault by calling 2 camera_take_photo in the same time. With mutex there is no such problem.

 

Old take photo thread (camera_take_photo without wait and without mutex)

void CameraModule::processTakePhoto () {
camera_error_t result1 = camera_take_photo(this->m_CameraHandle, CameraModule::photoShutterCallback, NULL, CameraModule::photoPostViewCallback, CameraModule::photoImageCallback, NULL, false); if (result1 != CAMERA_EOK) { // Here was the point where ERROR 17 was caught
} else { }
}

New take photo thread:

void CameraModule::processTakePhoto () {
CameraModule::getInstance().GetMutexTakePhoto()->lock(); camera_error_t result1 = camera_take_photo(this->m_CameraHandle, CameraModule::photoShutterCallback, NULL, CameraModule::photoPostViewCallback, CameraModule::photoImageCallback, NULL, true); if (result1 != CAMERA_EOK) { // No error
} else { }
CameraModule::getInstance().GetMutexTakePhoto()->unlock(); }

Callback for sending picture to queue:

void CameraModule::photoImageCallback(camera_handle_t cameraHandle1, camera_buffer_t * cameraBuffer1, void * params) {
    // Duplicate camera buffer
    camera_buffer_t * cameraBufferOut = new camera_buffer_t;
    memcpy(cameraBufferOut, cameraBuffer1, sizeof(camera_buffer_t));
    if (cameraBuffer1->frametype == CAMERA_FRAMETYPE_JPEG) {
      cameraBufferOut->framebuf = new uint8_t[cameraBuffer1->framedesc.jpeg.bufsize];
      memcpy(cameraBufferOut->framebuf, cameraBuffer1->framebuf, cameraBuffer1->framedesc.jpeg.bufsize);
      CameraTakePhotoHelper::getInstace().addPicture(
          pair<camera_buffer_t *, camera_handle_t>(cameraBufferOut, cameraHandle1));
    }
}

 Queue processing thread:

void CameraTakePhotoHelper::addPicture(pair<camera_buffer_t *, camera_handle_t> picture1) {
  while (this->m_PicturesQueue.size() > this->m_QueueSize) {
  }
  this->m_Mutex->lock();
  this->m_PicturesQueue.push_back(picture1);
  this->m_Mutex->unlock();
}

void CameraTakePhotoHelper::process() {
  while (!this->m_Quit) {
    if (this->m_PicturesQueue.size() != 0) {
      this->m_Mutex->lock();
      camera_buffer_t * cameraBuffer1 = this->m_PicturesQueue[0].first;
      camera_handle_t cameraHandle1 = this->m_PicturesQueue[0].second;
      this->m_PicturesQueue.erase(this->m_PicturesQueue.begin());
      this->m_Mutex->unlock();

      if (cameraBuffer1 != NULL) {
        if (cameraBuffer1->frametype == CAMERA_FRAMETYPE_JPEG) {
          int filePointer1;
          char fileName1[CAMERA_ROLL_NAMELEN];

          camera_error_t result1 = camera_roll_open_photo(cameraHandle1, &filePointer1, fileName1,
              CAMERA_ROLL_NAMELEN, CAMERA_ROLL_PHOTO_FMT_JPG);
          if (result1 == CAMERA_EOK) {
// After hours of googling, I haven't found efficient way of using int filePointer1   camera_roll_close_photo(filePointer1);
 
            FILE* outFile;
            outFile = fopen(fileName1, "wb");
            uint8_t * cameraPtr1 = cameraBuffer1->framebuf;
            unsigned int writtenBytes1 = 0;
            unsigned int blockSize1 = 65536;
            while (writtenBytes1 < cameraBuffer1->framedesc.jpeg.bufsize) {
              unsigned int numWrite1 = 0;
              numWrite1 = cameraBuffer1->framedesc.jpeg.bufsize - writtenBytes1;
              if (numWrite1 > blockSize1) {
                numWrite1 = blockSize1;
              }
              if (numWrite1 != 0) {
                fwrite(cameraPtr1, 1, numWrite1, outFile);
              }
 
              cameraPtr1 += numRead1;
              writtenBytes1 += numRead1;
            }
            fclose(outFile);
} } delete[] cameraBuffer1->framebuf; delete cameraBuffer1; }
// Should be better with WaitCondition, but I'm still learning how to use.
this->thread()->msleep(40);
}
}

 

BlackBerry Development Advisor
smcveigh
Posts: 668
Registered: ‎11-29-2011
My Device: developer

Re: playbook ndk - camera_take_photo returning with error 17 - file exists

ok cool.

I note in your old code, you had wait=false set in your camera_take_photo() call, which could have been the problem.

Looks like you've got it under control.

 

Couple of notes I'll throw out there.

 

1. if you don't care about the postview image, send NULL in for the callback.  it will save a bit of processing time in the OS and may get you a better framerate.

2. I would make sure you have your processing mutex locked as long as possible.. that includes around your check for .size() != 0.  it's actually safe to lock the mutex before entering your while() loop if you are using a condvar wait, because the wait will unlock the mutex.  Any time you are looking at or modifying the state of the queue, lock the mutex.  I see your comment in there saying you will read up on that topic :smileyhappy:  feel free to ask questions in the forum.

3. I noticed that you are opening the file on the camera roll and then closing it and then opening it again for writing.  It looks like you want to use a FILE* for writing, so that's okay, however I would suggest you close the roll file after writing.  It's okay to have 2 nested open()s.  Closing the file twice means that the media indexer will wake up twice and inspect the file, so this is just a system-level efficiency suggestion.  You mention you are having trouble using the file descriptor returned by camera_roll_photo_open().  fdopen() can be used to associate a file descriptor with a FILE* if you want.  Alternately, you can just use write() instead of fwrite().

 

Cheers,

Sean

New Contributor
EverySecond
Posts: 7
Registered: ‎12-02-2012
My Device: Playbook

Re: playbook ndk - camera_take_photo returning with error 17 - file exists

Yeah, you have saved me another hour googling for using file descriptor:smileyhappy: I knew that my solution was only temporaly. Thank you.