1 /*****************************************************************
2 |
3 |      Platinum - Frame Streamer
4 |
5 | Copyright (c) 2004-2010, Plutinosoft, LLC.
6 | All rights reserved.
7 | http://www.plutinosoft.com
8 |
9 | This program is free software; you can redistribute it and/or
10 | modify it under the terms of the GNU General Public License
11 | as published by the Free Software Foundation; either version 2
12 | of the License, or (at your option) any later version.
13 |
14 | OEMs, ISVs, VARs and other distributors that combine and
15 | distribute commercially licensed software with Platinum software
16 | and do not wish to distribute the source code for the commercially
17 | licensed software under version 2, or (at your option) any later
18 | version, of the GNU General Public License (the "GPL") must enter
19 | into a commercial license agreement with Plutinosoft, LLC.
20 | licensing@plutinosoft.com
21 |
22 | This program is distributed in the hope that it will be useful,
23 | but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 | GNU General Public License for more details.
26 |
27 | You should have received a copy of the GNU General Public License
28 | along with this program; see the file LICENSE.txt. If not, write to
29 | the Free Software Foundation, Inc.,
30 | 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
31 | http://www.gnu.org/licenses/gpl-2.0.html
32 |
33 ****************************************************************/
34 
35 /*----------------------------------------------------------------------
36 |       includes
37 +---------------------------------------------------------------------*/
38 #include "Platinum.h"
39 #include "PltFrameBuffer.h"
40 #include "PltFrameStream.h"
41 #include "PltFrameServer.h"
42 
43 #include <stdlib.h>
44 
45 NPT_SET_LOCAL_LOGGER("platinum.core.framestreamer")
46 
47 /*----------------------------------------------------------------------
48 |   globals
49 +---------------------------------------------------------------------*/
50 struct Options {
51     const char* path;
52 } Options;
53 
54 /*----------------------------------------------------------------------
55 |   StreamValidator:
56 +---------------------------------------------------------------------*/
57 class StreamValidator : public PLT_StreamValidator
58 {
59 public:
StreamValidator(NPT_Reference<PLT_FrameBuffer> & buffer)60     StreamValidator(NPT_Reference<PLT_FrameBuffer>& buffer) : m_Buffer(buffer) {}
~StreamValidator()61     virtual ~StreamValidator() {}
62 
63     // PLT_StreamValidator methods
OnNewRequestAccept(const NPT_HttpRequest & request,const NPT_HttpRequestContext & context,NPT_HttpResponse & response,NPT_Reference<PLT_FrameBuffer> & buffer)64     bool OnNewRequestAccept(const NPT_HttpRequest&          request,
65                             const NPT_HttpRequestContext&   context,
66                             NPT_HttpResponse&               response,
67                             NPT_Reference<PLT_FrameBuffer>& buffer) {
68         NPT_COMPILER_UNUSED(request);
69         NPT_COMPILER_UNUSED(response);
70         NPT_COMPILER_UNUSED(context);
71         // TODO: should compare HTTP Header Accept and buffer mimetype
72         buffer = m_Buffer;
73         return true;
74     }
75 
76     NPT_Reference<PLT_FrameBuffer> m_Buffer;
77 };
78 
79 /*----------------------------------------------------------------------
80 |   FrameWriter
81 +---------------------------------------------------------------------*/
82 class FrameWriter : public NPT_Thread
83 {
84 public:
FrameWriter(NPT_Reference<PLT_FrameBuffer> & frame_buffer,const char * frame_folder)85     FrameWriter(NPT_Reference<PLT_FrameBuffer>& frame_buffer,
86                 const char*                     frame_folder) :
87         m_FrameBuffer(frame_buffer),
88         m_Aborted(false),
89         m_Folder(frame_folder)
90         {}
91 
GetPath(NPT_List<NPT_String>::Iterator & entry)92     const char* GetPath(NPT_List<NPT_String>::Iterator& entry) {
93         if (!entry) return NULL;
94 
95         if (!entry->EndsWith(".jpg", true)) {
96             return GetPath(++entry);
97         }
98 
99         return *entry;
100     }
101 
Run()102     void Run() {
103         NPT_List<NPT_String> entries;
104         const char* frame_path = NULL;
105         NPT_DataBuffer frame;
106         NPT_List<NPT_String>::Iterator entry;
107 
108         while (!m_Aborted) {
109             // has number of images changed since last time?
110             NPT_LargeSize count;
111             NPT_File::GetSize(m_Folder, count);
112 
113             if (entries.GetItemCount() == 0 || entries.GetItemCount() != count) {
114                 NPT_File::ListDir(m_Folder, entries);
115                 entry = entries.GetFirstItem();
116                 if (!entry) {
117                     // Wait a bit before continuing
118                     NPT_System::Sleep(NPT_TimeInterval(0.2f));
119                     continue;
120                 }
121 
122                 // set delay based on number of files if necessary
123                 m_Delay = NPT_TimeInterval((float)1.f/entries.GetItemCount());
124             }
125 
126             // look for path to next image
127             if (!(frame_path = GetPath(entry))) {
128                 // loop back if necessary
129                 entry = entries.GetFirstItem();
130                 continue;
131             }
132 
133             if (NPT_FAILED(NPT_File::Load(NPT_FilePath::Create(m_Folder, frame_path), frame))) {
134                 NPT_LOG_SEVERE_1("Image \"%s\" not found!", frame_path?frame_path:"none");
135                 // clear previously loaded names so we reload entire set
136                 entries.Clear();
137                 continue;
138             }
139 
140             if (NPT_FAILED(m_FrameBuffer->SetNextFrame(frame.GetData(),
141                                                        frame.GetDataSize()))) {
142                 NPT_LOG_SEVERE_1("Failed to set next frame %s", frame_path);
143                 goto failure;
144             }
145 
146             // Wait before loading next frame
147             NPT_System::Sleep(m_Delay);
148 
149             // look for next entry
150             ++entry;
151         }
152 
153 failure:
154         // one more time to unblock any pending readers
155         m_FrameBuffer->Abort();
156     }
157 
158     NPT_Reference<PLT_FrameBuffer> m_FrameBuffer;
159     bool                           m_Aborted;
160     NPT_String                     m_Folder;
161     NPT_TimeInterval               m_Delay;
162 };
163 
164 /*----------------------------------------------------------------------
165 |   PrintUsageAndExit
166 +---------------------------------------------------------------------*/
167 static void
PrintUsageAndExit(char ** args)168 PrintUsageAndExit(char** args)
169 {
170     fprintf(stderr, "usage: %s <images path>\n", args[0]);
171     fprintf(stderr, "<path> : local path to serve images from\n");
172     exit(1);
173 }
174 
175 /*----------------------------------------------------------------------
176 |   ParseCommandLine
177 +---------------------------------------------------------------------*/
178 static void
ParseCommandLine(char ** args)179 ParseCommandLine(char** args)
180 {
181     char** _args = args++;
182     const char* arg;
183 
184     /* default values */
185     Options.path = NULL;
186 
187     while ((arg = *args++)) {
188         if (Options.path == NULL) {
189             Options.path = arg;
190         } else {
191             fprintf(stderr, "ERROR: too many arguments\n");
192             PrintUsageAndExit(_args);
193         }
194     }
195 
196     /* check args */
197     if (Options.path == NULL) {
198         fprintf(stderr, "ERROR: path missing\n");
199         PrintUsageAndExit(_args);
200     }
201 }
202 
203 /*----------------------------------------------------------------------
204 |       main
205 +---------------------------------------------------------------------*/
206 int
main(int argc,char ** argv)207 main(int argc, char** argv)
208 {
209     NPT_COMPILER_UNUSED(argc);
210 
211     /* parse command line */
212     ParseCommandLine(argv);
213 
214     // frame buffer
215     NPT_Reference<PLT_FrameBuffer> frame_buffer(new PLT_FrameBuffer("image/jpeg"));
216 
217     // A Framewriter reading images from a folder and writing them
218     // into frame buffer in a loop
219     FrameWriter writer(frame_buffer, Options.path);
220     writer.Start();
221 
222     // stream request validation
223     StreamValidator validator(frame_buffer);
224 
225     // frame server receiving requests and serving frames
226     // read from frame buffer
227     NPT_Reference<PLT_FrameServer> device(
228         new PLT_FrameServer(
229             "frame",
230             validator,
231             NPT_IpAddress::Any,
232             8099));
233 
234     if (NPT_FAILED(device->Start()))
235         return 1;
236 
237     char buf[256];
238     while (true) {
239         fgets(buf, 256, stdin);
240         if (*buf == 'q')
241             break;
242     }
243 
244     writer.m_Aborted = true;
245 
246     return 0;
247 }
248