Argus Camera Sample
Argus Camera Sample
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Gallery.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * 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 NVIDIA CORPORATION nor the names of its
13  * contributors may be used to endorse or promote products derived
14  * from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  * Gallery implementation file.
32  * The gallery task creates a thread handling scanning for items, loading of images and displaying
33  * them, using a playback video pipeline for displaying videos.
34  * The task communicates with the thread through commands.
35  * Image gallery items share one EGL stream, image data is written to that stream. Video gallery
36  * items each have an EGL stream.
37  * EGL streams are enabled for the current visible item only. The composer displays them on the
38  * screen.
39  */
40 
41 #define GL_GLEXT_PROTOTYPES
42 
43 #include <GLES3/gl31.h>
44 #include <GLES2/gl2ext.h>
45 
46 #include <stdio.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <dirent.h>
50 #include <assert.h>
51 
52 #include <list>
53 
54 #include "Gallery.h"
55 #include "Composer.h"
56 #include "Dispatcher.h"
57 #include "Error.h"
58 #include "Ordered.h"
59 #include "Mutex.h"
60 #include "ConditionVariable.h"
61 #include "UniquePointer.h"
62 #include "Thread.h"
63 #include "GLContext.h"
64 #include "VideoPipeline.h"
65 
66 extern "C" {
67 #include "jpeglib.h"
68 }
69 
70 namespace ArgusSamples
71 {
72 
73 /**
74  * Represents an item in the gallery.
75  */
77 {
78 public:
79  GalleryItem(const char *fileName, time_t modTime)
80  : m_fileName(fileName)
81  , m_modTime(modTime)
82  {
83  }
84 
85  virtual ~GalleryItem()
86  {
87  }
88 
89  /**
90  * item types
91  */
92  enum Type
93  {
97  };
98 
99  /**
100  * Compare function for sort(). Returns true if the first argument goes before the second
101  * argument, and false otherwise.
102  */
103  friend bool operator<(const GalleryItem &l, const GalleryItem &r)
104  {
105  return (difftime(l.m_modTime, r.m_modTime) > 0);
106  }
107 
108  /**
109  * Initialize
110  */
111  virtual bool initialize() = 0;
112 
113  /**
114  * shutdown
115  */
116  virtual bool shutdown() = 0;
117 
118  /**
119  * Start the display
120  */
121  virtual bool startDisplay() { return true; }
122 
123  /**
124  * Pause the display
125  */
126  virtual bool pauseDisplay() { return true; }
127 
128  /**
129  * Toggle playback
130  */
131  virtual bool togglePlayBack() { return true; }
132 
133  /**
134  * Rewind
135  */
136  virtual bool rewind() { return true; }
137 
138  /**
139  * Get the item type
140  */
141  virtual Type getType() const = 0;
142 
143  /**
144  * Get the file name
145  */
146  const std::string& getFileName() const
147  {
148  return m_fileName;
149  }
150 
151 protected:
152  std::string m_fileName;
153  time_t m_modTime;
154 
155  GalleryItem();
156 };
157 
158 /**
159  * Compare function for sort(). Returns true if the first argument goes before the second
160  * argument, and false otherwise.
161  */
162 static bool galleryItemCompare(const GalleryItem* const &l, const GalleryItem* const &r)
163 {
164  return (*l < *r);
165 }
166 
167 /**
168  * A gallery image. Can load JPEG images. Holds image data in CPU memory.
169  */
171 {
172 public:
173  GalleryItemImage(const char *fileName, time_t modTime)
174  : GalleryItem(fileName, modTime)
175  , m_width(0)
176  , m_height(0)
177  {
178  }
179 
181  {
182  PROPAGATE_ERROR_CONTINUE(shutdown());
183  }
184 
185  /** @name GalleryItem methods */
186  /**@{*/
187  virtual Type getType() const
188  {
189  return TYPE_IMAGE;
190  }
191  virtual bool initialize();
192  virtual bool shutdown();
193  /**@}*/
194 
195  size_t getWidth() const
196  {
197  return m_width;
198  }
199 
200  size_t getHeight() const
201  {
202  return m_height;
203  }
204 
205  const uint8_t* getData() const
206  {
207  return m_data.data();
208  }
209 
210 private:
211  size_t m_width;
212  size_t m_height;
213  std::vector<uint8_t> m_data;
214 };
215 
217 {
218  // already loaded?
219  if (!m_data.empty())
220  return true;
221 
222  struct jpeg_decompress_struct info;
223  struct jpeg_error_mgr err;
224  std::vector<JSAMPLE*> rowPointers;
225  JDIMENSION read;
226  bool success = false;
227 
228  // Open file.
229  FILE *file = fopen(m_fileName.c_str(), "rb");
230  if (!file)
231  ORIGINATE_ERROR("Could not open file '%s'.", m_fileName.c_str());
232 
233  // Prepare for jpeg decompression.
234  memset(&info, 0, sizeof(info));
235  info.err = jpeg_std_error(&err);
236  jpeg_create_decompress(&info);
237 #ifdef TEGRA_ACCELERATE
238  // Tegra JPEG acceleration seems to be broken, image is all black. We need to disable
239  // hardware acceleration.
240  jpeg_set_hardware_acceleration_parameters_dec(&info, false, 0, 0, 0, 0, false);
241 #endif
242  jpeg_stdio_src(&info, file);
243  if (jpeg_read_header(&info, TRUE) != JPEG_HEADER_OK)
244  ORIGINATE_ERROR_FAIL("Invalid JPEG image file '%s'.", m_fileName.c_str());
245  if (jpeg_start_decompress(&info) != TRUE)
246  ORIGINATE_ERROR_FAIL("Invalid JPEG image file '%s'.", m_fileName.c_str());
247 
248  // Determine image format.
249  if (info.output_components != 3)
250  ORIGINATE_ERROR_FAIL("Only RGB JPEGs supported.");
251 
252  // Read the image size.
253  m_width = info.output_width;
254  m_height = info.output_height;
255 
256  // Resize vector for the output.
257  m_data.resize(m_width * m_height * info.output_components);
258 
259  // Allocate and set row pointers.
260  rowPointers.resize(m_height);
261  for (size_t row = 0; row < m_height; ++row)
262  rowPointers[row] = m_data.data() + row * m_width * info.output_components;
263 
264  // Read the image data.
265  read = 0;
266  while (read < m_height)
267  read += jpeg_read_scanlines(&info, &rowPointers[read], m_height - read);
268 
269  success = true;
270 
271  // Fallthrough
272 fail:
273  if (jpeg_finish_decompress(&info) != TRUE)
274  REPORT_ERROR("jpeg_finish_decompress() failed.");
275  jpeg_destroy_decompress(&info);
276  if (fclose(file) != 0)
277  REPORT_ERROR("fclose() failed.");
278 
279  return success;
280 }
281 
283 {
284  m_data.clear();
285  m_width = 0;
286  m_height = 0;
287  return true;
288 }
289 
290 /**
291  * A gallery video. Outputs to an EGL stream.
292  */
294 {
295 public:
296  GalleryItemVideo(const char *fileName, time_t modTime)
297  : GalleryItem(fileName, modTime)
298  , m_pipeline(NULL)
299  , m_eglStream(EGL_NO_STREAM_KHR)
300  {
301  }
302 
304  {
305  PROPAGATE_ERROR_CONTINUE(shutdown());
306  }
307 
308  /** @name GalleryItem methods */
309  /**@{*/
310  virtual Type getType() const
311  {
312  return TYPE_VIDEO;
313  }
314  virtual bool initialize();
315  virtual bool shutdown();
316  virtual bool startDisplay();
317  virtual bool pauseDisplay();
318  virtual bool togglePlayBack();
319  virtual bool rewind();
320  /**@}*/
321 
322  EGLStreamKHR getEGLStream() const
323  {
324  return m_eglStream;
325  }
326 
327 private:
328  VideoPipeline *m_pipeline; ///! playback pipeline
329  EGLStreamKHR m_eglStream;
330 };
331 
333 {
334  if (m_pipeline)
335  return true;
336 
338  if (!m_pipeline)
339  ORIGINATE_ERROR("Failed to allocate video pipeline");
340 
341  PROPAGATE_ERROR(m_pipeline->setupForPlayback(&m_eglStream, m_fileName.c_str()));
342  PROPAGATE_ERROR(Composer::getInstance().bindStream(m_eglStream));
343 
344  // set to pause
345  PROPAGATE_ERROR(m_pipeline->pause());
346 
347  // query size
348  float aspectRatio = 1.0f;
349  PROPAGATE_ERROR(m_pipeline->getAspectRatio(&aspectRatio));
350  PROPAGATE_ERROR(Composer::getInstance().setStreamAspectRatio(m_eglStream, aspectRatio));
351 
352  return true;
353 }
354 
356 {
357  if (m_pipeline)
358  {
359  PROPAGATE_ERROR_CONTINUE(Composer::getInstance().unbindStream(m_eglStream));
360  delete m_pipeline;
361  m_pipeline = NULL;
362  }
363  return true;
364 }
365 
367 {
368  if (!m_pipeline)
369  ORIGINATE_ERROR("Not initialized");
370 
371  // start in paused state
372  PROPAGATE_ERROR(m_pipeline->pause());
373  return true;
374 }
375 
377 {
378  if (!m_pipeline)
379  ORIGINATE_ERROR("Not initialized");
380 
381  PROPAGATE_ERROR(m_pipeline->pause());
382  return true;
383 }
384 
386 {
387  if (!m_pipeline)
388  ORIGINATE_ERROR("Not initialized");
389 
390  PROPAGATE_ERROR(m_pipeline->toggle());
391  return true;
392 }
393 
395 {
396  if (!m_pipeline)
397  ORIGINATE_ERROR("Not initialized");
398 
399  PROPAGATE_ERROR(m_pipeline->rewind());
400  return true;
401 }
402 
403 /**
404  * This class handles creation of a thread scanning for supported images/videos, loading them and
405  * writing the content to an EGLStream.
406  */
407 class GalleryThread : public Thread
408 {
409 public:
410  GalleryThread();
411  ~GalleryThread();
412 
413  bool initialize();
414  bool shutdown();
415 
416  enum Command
417  {
419  COMMAND_SHUTDOWN, //!< shutdown
420  COMMAND_NEXT, //!< next item
421  COMMAND_PREV, //!< previous item
422  COMMAND_START, //!< start replay
423  COMMAND_TOGGLE_PLAY_BACK, //!< toggle playback
424  COMMAND_REWIND, //!< rewind
425  COMMAND_STOP, //!< stop replay
426  };
427 
428  typedef std::list<GalleryItem*> GalleryItemList;
429 
430  /**
431  * Execute a command
432  */
433  bool execute(Command command);
434 
435 private:
436  ConditionVariable m_cmdCond; //! command condition variable
437  Mutex m_cmdMutex; //! command mutex
438  std::list<Ordered<Command> > m_cmdList; //! command list, written by parent thread
439 
440  GLContext m_context;
441 
442  // resources used to display images
443  EGLSurface m_eglOutputSurface;
444  EGLStreamHolder m_eglImageOutputStream;
445  GLuint m_textureID;
447  GLuint m_vbo;
448 
450  GalleryItemList::iterator m_curItem;
451 
452  virtual bool threadInitialize();
453  virtual bool threadExecute();
454  virtual bool threadShutdown();
455 
456  bool buildItemList();
457 
458  bool start();
459  bool stop();
460 
461  bool startDisplay();
462  bool pauseDisplay();
463  bool togglePlayBack();
464  bool rewind();
465 
466  /**
467  * Get the current output stream
468  */
469  EGLStreamKHR getOutputStream() const
470  {
471  if (m_curItem != m_itemList.end())
472  {
473  switch ((*m_curItem)->getType())
474  {
476  // images share one output stream
477  return m_eglImageOutputStream.get();
479  // each video has its own stream
480  return static_cast<GalleryItemVideo*>(*m_curItem)->getEGLStream();
481  default:
482  break;
483  }
484  }
485  return EGL_NO_STREAM_KHR;
486  }
487 };
488 
490  : m_eglOutputSurface(EGL_NO_SURFACE)
491  , m_textureID(0)
492  , m_copyProgram(0)
493  , m_vbo(0)
494  , m_curItem(m_itemList.end())
495 {
496 }
497 
499 {
500  shutdown();
501 }
502 
504 {
505  PROPAGATE_ERROR(m_cmdMutex.initialize());
506  PROPAGATE_ERROR(m_cmdCond.initialize());
507 
508  PROPAGATE_ERROR(Thread::initialize());
509  return true;
510 }
511 
513 {
514  PROPAGATE_ERROR_CONTINUE(Thread::shutdown());
515  PROPAGATE_ERROR_CONTINUE(m_cmdMutex.shutdown());
516  PROPAGATE_ERROR_CONTINUE(m_cmdCond.shutdown());
517  return true;
518 }
519 
521 {
522  // this function is *not* to be executed in the thread itself but in the parent thread
524  PROPAGATE_ERROR(sm.expectLocked());
525 
526  m_cmdList.push_back(command);
527 
528  PROPAGATE_ERROR(m_cmdCond.signal());
529 
530  return true;
531 }
532 
533 /**
534  * Builds a list of gallery items by scanning the output path for image and video files. The
535  * list is sorted with the newest files first.
536  */
538 {
539  bool success = true;
540  struct extTypePair
541  {
542  const char *ext;
543  GalleryItem::Type type;
544  };
545  const extTypePair extensions[] =
546  {
547  { "jpg",
557  };
558 
559  // open output directory
560  const std::string path(Dispatcher::getInstance().m_outputPath.get());
561  DIR *directory = opendir(path.c_str());
562  if (directory != NULL)
563  {
564  // scan all files
565  struct dirent *entry;
566  while ((entry = readdir(directory)))
567  {
568  // we are looking for files only
569  if (entry->d_type != DT_REG)
570  continue;
571 
572  // check for supported extensions
574  const size_t fileNameLen = strlen(entry->d_name);
575  for (size_t index = 0; index < sizeof(extensions) / sizeof(extensions[0]); ++index)
576  {
577  const extTypePair *ext = &extensions[index];
578 
579  // filename should be longer than '.ext', ext has no '.' therefore +1
580  const size_t extLen = strlen(ext->ext);
581  if (fileNameLen >= extLen + 1)
582  {
583  if ((entry->d_name[fileNameLen - extLen - 1] == '.') &&
584  (strcasecmp(&entry->d_name[fileNameLen - extLen], ext->ext) == 0))
585  {
586  type = ext->type;
587  break;
588  }
589  }
590  }
591  if (type == GalleryItem::TYPE_INVALID)
592  continue;
593 
594  std::string fullName;
595 
596  fullName = path;
597  fullName += "/";
598  fullName += entry->d_name;
599 
600  // get status on the file
601  struct stat fileStat;
602  if (stat(fullName.c_str(), &fileStat) != 0)
603  ORIGINATE_ERROR_FAIL("Failed to query file status on '%s'", fullName.c_str());
604 
605  UniquePointer<GalleryItem> item;
606  if (type == GalleryItem::TYPE_VIDEO)
607  {
608  item.reset(new GalleryItemVideo(fullName.c_str(), fileStat.st_mtime));
609  }
610  else
611  {
612  assert(type == GalleryItem::TYPE_IMAGE);
613  item.reset(new GalleryItemImage(fullName.c_str(), fileStat.st_mtime));
614  }
615  if (!item)
616  ORIGINATE_ERROR("Failed to create gallery item");
617  m_itemList.push_back(item.release());
618  }
619  }
620 
621  m_itemList.sort(galleryItemCompare);
622 
623  goto pass;
624 
625 fail:
626  success = false;
627 
628 pass:
629  if (directory != NULL)
630  closedir(directory);
631 
632  return success;
633 }
634 
636 {
637  Composer &composer = Composer::getInstance();
638  //! @todo Using 1920x1080 for now. Should use the image size, but this would require creating
639  // one stream and one surface for each image because surfaces can't be resized
640  const uint32_t streamWidth = 1920;
641  const uint32_t streamHeight = 1080;
642 
643  // create the EGL output stream
644  PROPAGATE_ERROR(m_eglImageOutputStream.create(composer.getEGLDisplay()));
645  CHECK_STREAM_STATE(m_eglImageOutputStream, CREATED);
646 
647  // bind the output stream to the composer
648  PROPAGATE_ERROR(composer.bindStream(m_eglImageOutputStream.get()));
649  PROPAGATE_ERROR(composer.setStreamAspectRatio(m_eglImageOutputStream.get(),
650  (float)streamWidth / (float)streamHeight));
651  CHECK_STREAM_STATE(m_eglImageOutputStream, CONNECTING);
652 
653  // create a EGL context and make it current to the output surface
654  PROPAGATE_ERROR(m_context.initialize(composer.getEGLDisplay()));
655 
656  // create the EGL output surface and connect the EGL output stream to it
657  PROPAGATE_ERROR(m_context.createEGLStreamProducerSurface(&m_eglOutputSurface,
658  m_eglImageOutputStream.get(), streamWidth, streamHeight));
659  CHECK_STREAM_STATE(m_eglImageOutputStream, EMPTY);
660 
661  PROPAGATE_ERROR(m_context.makeCurrent(m_eglOutputSurface));
662 
663  // create a texture used for images
664  glGenTextures(1, &m_textureID);
665  if (m_textureID == 0)
666  ORIGINATE_ERROR("Failed to create GL texture");
667 
668  // Create the shader programs
669  static const char vtxSrc[] =
670  "#version 300 es\n"
671  "#extension GL_ARB_explicit_uniform_location : require\n"
672  "in layout(location = 0) vec2 vertex;\n"
673  "out vec2 vTexCoord;\n"
674  "layout(location = 0) uniform vec2 offset;\n"
675  "layout(location = 1) uniform vec2 scale;\n"
676  "void main() {\n"
677  " gl_Position = vec4((offset + vertex * scale) * 2.0 - 1.0, 0.0, 1.0);\n"
678  " vTexCoord = vec2(vertex.x, vertex.y);\n"
679  "}\n";
680 
681  static const char copyFrgSrc[] =
682  "#version 300 es\n"
683  "precision highp float;\n"
684  "uniform sampler2D texSampler;\n"
685  "in vec2 vTexCoord;\n"
686  "out vec4 fragColor;\n"
687  "void main() {\n"
688  " fragColor = texture(texSampler, vTexCoord);\n"
689  "}\n";
690  PROPAGATE_ERROR(m_context.createProgram(vtxSrc, copyFrgSrc, &m_copyProgram));
691 
692  // Setup vertex state.
693  static const GLfloat vertices[] =
694  {
695  0.0f, 0.0f,
696  0.0f, 1.0f,
697  1.0f, 0.0f,
698  1.0f, 1.0f,
699  };
700  glGenBuffers(1, &m_vbo);
701  glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
702  glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
703  glBindBuffer(GL_ARRAY_BUFFER, 0);
704  glEnableVertexAttribArray(0);
705 
706  return true;
707 }
708 
709 /**
710  * Start. Build the item list.
711  */
713 {
714  if (!m_itemList.empty())
715  ORIGINATE_ERROR("Item list should be empty");
716 
717  // build a list of all items
718  PROPAGATE_ERROR(buildItemList());
719  if (m_itemList.empty())
720  m_curItem = m_itemList.end();
721  else
722  m_curItem = m_itemList.begin();
723 
724  return true;
725 }
726 
727 /**
728  * Stop. Pause display and free the item list.
729  */
731 {
732  PROPAGATE_ERROR(pauseDisplay());
733 
734  for (GalleryItemList::iterator it = m_itemList.begin(); it != m_itemList.end(); ++it)
735  delete (*it);
736  m_itemList.clear();
737  m_curItem = m_itemList.end();
738 
739  return true;
740 }
741 
743 {
744  if (m_curItem == m_itemList.end())
745  return true;
746 
747  std::ostringstream message;
748 
749  message << "Displaying '" << (*m_curItem)->getFileName() << "'" << std::endl;
750  Dispatcher::getInstance().message(message.str().c_str());
751 
752  PROPAGATE_ERROR((*m_curItem)->initialize());
753 
754  switch ((*m_curItem)->getType())
755  {
757  {
758  GalleryItemImage *image = static_cast<GalleryItemImage*>(*m_curItem);
759 
760  // draw it to the surface
761  glClear(GL_COLOR_BUFFER_BIT);
762 
763  glBindTexture(GL_TEXTURE_2D, m_textureID);
764  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
765  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
766  // load the item into the texture
767  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image->getWidth(), image->getHeight(), 0,
768  GL_RGB, GL_UNSIGNED_BYTE, reinterpret_cast<const void*>(image->getData()));
769 
770  // copy from the input to the output
771  glUseProgram(m_copyProgram);
772  glUniform2f(0, 0.0f, 0.0f); // offset
773  glUniform2f(1, 1.0f, 1.0f); // scale
774  glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
775  glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
776  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
777 
778  // the swap will put the image into the output EGL stream
779  PROPAGATE_ERROR(m_context.swapBuffers(m_eglOutputSurface));
780  }
781  break;
783  PROPAGATE_ERROR(static_cast<GalleryItemVideo*>(*m_curItem)->startDisplay());
784  break;
785  default:
786  ORIGINATE_ERROR("Unhandled gallery item type");
787  }
788 
789  PROPAGATE_ERROR(Composer::getInstance().setStreamActive(getOutputStream(), true));
790 
791  return true;
792 }
793 
795 {
796  if (m_curItem == m_itemList.end())
797  return true;
798 
799  PROPAGATE_ERROR(Composer::getInstance().setStreamActive(getOutputStream(), false));
800  PROPAGATE_ERROR((*m_curItem)->pauseDisplay());
801 
802  return true;
803 }
804 
806 {
807  if (m_curItem == m_itemList.end())
808  return true;
809 
810  PROPAGATE_ERROR((*m_curItem)->togglePlayBack());
811 
812  return true;
813 }
814 
816 {
817  if (m_curItem == m_itemList.end())
818  return true;
819 
820  PROPAGATE_ERROR((*m_curItem)->rewind());
821 
822  return true;
823 }
824 
826 {
827  Command cmd = COMMAND_NONE;
828 
829  {
831  PROPAGATE_ERROR(sm.expectLocked());
832 
833  // if the list is empty wait for new commands
834  if (m_cmdList.empty())
835  PROPAGATE_ERROR(m_cmdCond.wait(m_cmdMutex));
836 
837  // get the command from the list
838  cmd = m_cmdList.front();
839  m_cmdList.pop_front();
840  }
841 
842  switch(cmd)
843  {
844  case COMMAND_NONE:
845  break;
846  case COMMAND_SHUTDOWN:
847  PROPAGATE_ERROR(requestShutdown());
848  break;
849  case COMMAND_START:
850  PROPAGATE_ERROR(start());
851  PROPAGATE_ERROR(startDisplay());
852  break;
854  PROPAGATE_ERROR(togglePlayBack());
855  break;
856  case COMMAND_REWIND:
857  PROPAGATE_ERROR(rewind());
858  break;
859  case COMMAND_STOP:
860  PROPAGATE_ERROR(stop());
861  break;
862  case COMMAND_PREV:
863  if ((m_curItem != m_itemList.end()) && (m_curItem != m_itemList.begin()))
864  {
865  PROPAGATE_ERROR(pauseDisplay());
866  --m_curItem;
867  PROPAGATE_ERROR(startDisplay());
868  }
869  break;
870  case COMMAND_NEXT:
871  if ((m_curItem != m_itemList.end()) && (&(*m_curItem) != &m_itemList.back()))
872  {
873  PROPAGATE_ERROR(pauseDisplay());
874  ++m_curItem;
875  PROPAGATE_ERROR(startDisplay());
876  }
877  break;
878  default:
879  ORIGINATE_ERROR("Invalid command %d", cmd);
880  }
881 
882  return true;
883 }
884 
886 {
887  Composer &composer = Composer::getInstance();
888 
889  PROPAGATE_ERROR_CONTINUE(stop());
890 
891  if (m_eglOutputSurface != EGL_NO_SURFACE)
892  {
893  eglDestroySurface(composer.getEGLDisplay(), m_eglOutputSurface);
894  m_eglOutputSurface = EGL_NO_SURFACE;
895  }
896 
897  // unbind the EGL output stream from the composer
898  PROPAGATE_ERROR_CONTINUE(composer.unbindStream(m_eglImageOutputStream.get()));
899  CHECK_STREAM_STATE(m_eglImageOutputStream, DISCONNECTED);
900  // and destroy the EGL output stream
901  PROPAGATE_ERROR_CONTINUE(m_eglImageOutputStream.destroy());
902 
903  // free GL resources
904  glDeleteTextures(1, &m_textureID);
905  m_textureID = 0;
906  glDeleteProgram(m_copyProgram);
907  m_copyProgram = 0;
908  glDeleteBuffers(1, &m_vbo);
909  m_vbo = 0;
910 
911  PROPAGATE_ERROR_CONTINUE(m_context.cleanup());
912 
913  return true;
914 }
915 
917  : m_initialized(false)
918  , m_running(false)
919  , m_thread(NULL)
920 {
921 }
922 
924 {
925  shutdown();
926 }
927 
929 {
930  if (m_initialized)
931  return true;
932 
933  m_initialized = true;
934 
935  return true;
936 }
937 
939 {
940  if (!m_initialized)
941  return true;
942 
943  // stop the module
944  PROPAGATE_ERROR_CONTINUE(stop());
945 
946  m_initialized = false;
947 
948  return true;
949 }
950 
952 {
953  if (!m_initialized)
954  ORIGINATE_ERROR("Not initialized");
955  if (m_running)
956  return true;
957 
958  // create the gallery thread, it will load and display the items
959  UniquePointer<GalleryThread> galleryThread(new GalleryThread());
960  if (!galleryThread)
961  ORIGINATE_ERROR("Out of memory");
962 
963  PROPAGATE_ERROR(galleryThread->initialize());
964  m_thread = galleryThread.release();
965 
966  PROPAGATE_ERROR(m_thread->waitRunning());
967 
968  PROPAGATE_ERROR(m_thread->execute(GalleryThread::COMMAND_START));
969 
970  m_running = true;
971 
972  return true;
973 }
974 
976 {
977  if (!m_initialized)
978  ORIGINATE_ERROR("Not initialized");
979  if (!m_running)
980  return true;
981 
982  PROPAGATE_ERROR(m_thread->execute(GalleryThread::COMMAND_STOP));
983 
984  // send the shutdown command
986  // destroy the thread
987  PROPAGATE_ERROR(m_thread->shutdown());
988 
989  delete m_thread;
990  m_thread = NULL;
991 
992  m_running = false;
993 
994  return true;
995 }
996 
998 {
999  if (!m_running)
1000  ORIGINATE_ERROR("Not running");
1001 
1003  return true;
1004 }
1005 
1007 {
1008  if (!m_running)
1009  ORIGINATE_ERROR("Not running");
1010 
1011  PROPAGATE_ERROR(m_thread->execute(GalleryThread::COMMAND_REWIND));
1012  return true;
1013 }
1014 
1016 {
1017  if (!m_running)
1018  ORIGINATE_ERROR("Not running");
1019 
1020  PROPAGATE_ERROR(m_thread->execute(GalleryThread::COMMAND_PREV));
1021  return true;
1022 }
1023 
1025 {
1026  if (!m_running)
1027  ORIGINATE_ERROR("Not running");
1028 
1029  PROPAGATE_ERROR(m_thread->execute(GalleryThread::COMMAND_NEXT));
1030  return true;
1031 }
1032 
1033 }; // namespace ArgusSamples