Argus Camera Sample Argus Camera Sample

- Main Page
- Related Pages
- Namespaces
- Classes
- Files

- File List
- File Members

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 */
76 class GalleryItem
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 {
94 TYPE_IMAGE,
95 TYPE_VIDEO,
96 TYPE_INVALID
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 */
170 class GalleryItemImage : public GalleryItem
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
180 virtual ~GalleryItemImage()
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
216 bool GalleryItemImage::initialize()
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
282 bool GalleryItemImage::shutdown()
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 */
293 class GalleryItemVideo : public GalleryItem
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
303 virtual ~GalleryItemVideo()
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
332 bool GalleryItemVideo::initialize()
333 {
334 if (m_pipeline)
335 return true;
336
337 m_pipeline = new VideoPipeline;
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
355 bool GalleryItemVideo::shutdown()
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
366 bool GalleryItemVideo::startDisplay()
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
376 bool GalleryItemVideo::pauseDisplay()
377 {
378 if (!m_pipeline)
379 ORIGINATE_ERROR("Not initialized");
380
381 PROPAGATE_ERROR(m_pipeline->pause());
382 return true;
383 }
384
385 bool GalleryItemVideo::togglePlayBack()
386 {
387 if (!m_pipeline)
388 ORIGINATE_ERROR("Not initialized");
389
390 PROPAGATE_ERROR(m_pipeline->toggle());
391 return true;
392 }
393
394 bool GalleryItemVideo::rewind()
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 {
418 COMMAND_NONE,
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;
446 GLuint m_copyProgram;
447 GLuint m_vbo;
448
449 GalleryItemList m_itemList;
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 {
475 case GalleryItem::TYPE_IMAGE:
476 // images share one output stream
477 return m_eglImageOutputStream.get();
478 case GalleryItem::TYPE_VIDEO:
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
489 GalleryThread::GalleryThread()
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
498 GalleryThread::~GalleryThread()
499 {
500 shutdown();
501 }
502
503 bool GalleryThread::initialize()
504 {
505 PROPAGATE_ERROR(m_cmdMutex.initialize());
506 PROPAGATE_ERROR(m_cmdCond.initialize());
507
508 PROPAGATE_ERROR(Thread::initialize());
509 return true;
510 }
511
512 bool GalleryThread::shutdown()
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
520 bool GalleryThread::execute(Command command)
521 {
522 // this function is *not* to be executed in the thread itself but in the parent thread
523 ScopedMutex sm(m_cmdMutex);
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 */
537 bool GalleryThread::buildItemList()
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",
548 GalleryItem::TYPE_IMAGE },
549 { VideoPipeline::getFileExtension(VideoPipeline::VIDEO_FILE_TYPE_MP4),
550 GalleryItem::TYPE_VIDEO },
551 { VideoPipeline::getFileExtension(VideoPipeline::VIDEO_FILE_TYPE_3GP),
552 GalleryItem::TYPE_VIDEO },
553 { VideoPipeline::getFileExtension(VideoPipeline::VIDEO_FILE_TYPE_AVI),
554 GalleryItem::TYPE_VIDEO },
555 { VideoPipeline::getFileExtension(VideoPipeline::VIDEO_FILE_TYPE_H265),
556 GalleryItem::TYPE_VIDEO },
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
573 GalleryItem::Type type = GalleryItem::TYPE_INVALID;
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
635 bool GalleryThread::threadInitialize()
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 */
712 bool GalleryThread::start()
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 */
730 bool GalleryThread::stop()
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
742 bool GalleryThread::startDisplay()
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 {
756 case GalleryItem::TYPE_IMAGE:
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;
782 case GalleryItem::TYPE_VIDEO:
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
794 bool GalleryThread::pauseDisplay()
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
805 bool GalleryThread::togglePlayBack()
806 {
807 if (m_curItem == m_itemList.end())
808 return true;
809
810 PROPAGATE_ERROR((*m_curItem)->togglePlayBack());
811
812 return true;
813 }
814
815 bool GalleryThread::rewind()
816 {
817 if (m_curItem == m_itemList.end())
818 return true;
819
820 PROPAGATE_ERROR((*m_curItem)->rewind());
821
822 return true;
823 }
824
825 bool GalleryThread::threadExecute()
826 {
827 Command cmd = COMMAND_NONE;
828
829 {
830 ScopedMutex sm(m_cmdMutex);
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;
853 case COMMAND_TOGGLE_PLAY_BACK:
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
885 bool GalleryThread::threadShutdown()
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
916 TaskGallery::TaskGallery()
917 : m_initialized(false)
918 , m_running(false)
919 , m_thread(NULL)
920 {
921 }
922
923 TaskGallery::~TaskGallery()
924 {
925 shutdown();
926 }
927
928 bool TaskGallery::initialize()
929 {
930 if (m_initialized)
931 return true;
932
933 m_initialized = true;
934
935 return true;
936 }
937
938 bool TaskGallery::shutdown()
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
951 bool TaskGallery::start()
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
975 bool TaskGallery::stop()
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
985 PROPAGATE_ERROR(m_thread->execute(GalleryThread::COMMAND_SHUTDOWN));
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
997 bool TaskGallery::togglePlayBack()
998 {
999 if (!m_running)
1000 ORIGINATE_ERROR("Not running");
1001
1002 PROPAGATE_ERROR(m_thread->execute(GalleryThread::COMMAND_TOGGLE_PLAY_BACK));
1003 return true;
1004 }
1005
1006 bool TaskGallery::rewind()
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
1015 bool TaskGallery::prevItem()
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
1024 bool TaskGallery::nextItem()
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