2 // Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 3 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "gnashconfig.h"
23 #include <boost/thread/thread.hpp>
24 #include <boost/thread/mutex.hpp>
25 #include <boost/shared_ptr.hpp>
26 #include <boost/shared_array.hpp>
27 #include <boost/scoped_ptr.hpp>
28 #include <boost/bind.hpp>
35 #if defined(WIN32) || defined(_WIN32)
36 # define LIBLTDL_DLL_IMPORT 1
50 #include "dsodefs.h" //For DSOEXPORT.
53 #include "diskstream.h"
59 #include "rtmp_server.h"
60 #include "http_server.h"
62 using namespace gnash
;
64 using namespace boost
;
69 map
<int, Handler
*> DSOEXPORT handlers
;
71 // The user config for Cygnal is loaded and parsed here:
72 static CRcInitFile
& crcfile
= CRcInitFile::getDefaultInstance();
75 :_streams(1), // note that stream 0 is reserved by the system.
76 // _diskstreams(new gnash::DiskStream[STREAMS_BLOCK]),
79 // GNASH_REPORT_FUNCTION;
80 // reserve memory for the vector as it makes vector operations
86 // GNASH_REPORT_FUNCTION;
90 Handler::sync(int /* in_fd */)
92 // GNASH_REPORT_FUNCTION;
98 Handler::addClient(int fd
, Network::protocols_supported_e proto
)
100 // GNASH_REPORT_FUNCTION;
102 boost::mutex::scoped_lock
lock(_mutex
);
104 log_debug("Adding %d to the client array.", fd
);
110 boost::shared_ptr
<HTTPServer
> http(new HTTPServer
);
118 boost::shared_ptr
<RTMPServer
> rtmp(new RTMPServer
);
123 case Network::RTMPTS
:
128 log_unimpl("Protocol %d for Handler::AddClient()", proto
);
132 _clients
.push_back(fd
);
133 _protocol
[fd
] = proto
;
135 return _clients
.size();
138 // Parse the first nessages when starting a new message handler,
139 // which is used to determine the name of the resource to
140 // initialize, or load from the cache.
142 Handler::parseFirstRequest(int fd
, gnash::Network::protocols_supported_e proto
)
144 GNASH_REPORT_FUNCTION
;
147 cygnal::Buffer
*buf
= 0;
148 boost::mutex::scoped_lock
lock(_mutex
);
156 int ret
= _http
[fd
]->readNet(fd
, buf
);
158 _http
[fd
]->processHeaderFields(buf
);
159 string hostname
, path
;
160 string::size_type pos
= _http
[fd
]->getField("host").find(":", 0);
161 if (pos
!= string::npos
) {
162 hostname
+= _http
[fd
]->getField("host").substr(0, pos
);
164 hostname
+= "localhost";
166 path
= _http
[fd
]->getFilespec();
167 key
= hostname
+ path
;
168 log_debug("HTTP key is: %s", key
);
171 log_error("HTTP key couldn't be read!");
175 size_t bytes
= http
.sniffBytesReady(fd
);
177 buf
= new cygnal::Buffer(bytes
);
181 int ret
= http
.readNet(fd
, buf
);
183 http
.processHeaderFields(buf
);
184 string hostname
, path
;
185 string::size_type pos
= http
.getField("host").find(":", 0);
186 if (pos
!= string::npos
) {
187 hostname
+= http
.getField("host").substr(0, pos
);
189 hostname
+= "localhost";
191 path
= http
.getFilespec();
192 key
= hostname
+ path
;
193 log_debug("HTTP key is: %s", key
);
196 log_error("HTTP key couldn't be read!");
205 // _rtmp[fd]->recvMsg(fd);
209 case Network::RTMPTS
:
214 log_error("FD #%d has no protocol handler registered", fd
);
222 Handler::recvMsg(int fd
)
224 // GNASH_REPORT_FUNCTION;
225 boost::mutex::scoped_lock
lock(_mutex
);
227 switch (_protocol
[fd
]) {
232 return _http
[fd
]->recvMsg(fd
);
239 case Network::RTMPTS
:
244 log_error("FD #%d has no protocol handler registered", fd
);
250 Handler::removeClient(int x
)
252 // GNASH_REPORT_FUNCTION;
254 boost::mutex::scoped_lock
lock(_mutex
);
256 vector
<int>::iterator it
;
257 for (it
= _clients
.begin(); it
< _clients
.end(); ++it
) {
259 log_debug("Removing %d from the client array.", *it
);
266 Handler::setPlugin(boost::shared_ptr
<Handler::cygnal_init_t
> &/* init */)
268 // GNASH_REPORT_FUNCTION;
269 // _plugin.reset(init.get());
273 Handler::setPlugin(Handler::cygnal_io_read_t
/* read_ptr */, Handler::cygnal_io_write_t
/* write_ptr */)
275 // GNASH_REPORT_FUNCTION;
277 _plugin
.reset(new Handler::cygnal_init_t
);
280 boost::shared_ptr
<Handler::cygnal_init_t
>
281 Handler::initModule(const std::string
& str
)
283 // GNASH_REPORT_FUNCTION;
290 if (module
[0] == '/') {
295 string
symbol(module
);
297 _pluginsdir
= PLUGINSDIR
;
298 log_security(_("Initializing module: \"%s\" from %s"), symbol
, _pluginsdir
);
300 // Update the list of loaded plugins so we only load them once.
301 if (_plugins
[module
] == 0) {
302 sl
= new SharedLib(module
, "CYGNAL_PLUGINS");
303 lt_dlsetsearchpath(_pluginsdir
.c_str());
305 _plugins
[module
] = sl
;
307 sl
= _plugins
[module
];
310 _plugin
.reset(new Handler::cygnal_init_t
);
313 symbol
.append("_init_func");
314 Handler::cygnal_io_init_t init_symptr
= reinterpret_cast<Handler::cygnal_io_init_t
>
315 (sl
->getInitEntry(symbol
));
317 log_network(_("No %s symbol in plugin"), symbol
);
319 boost::shared_ptr
<cygnal_init_t
> info
= init_symptr(_netconnect
);
320 log_network("Initialized Plugin: \"%s\": %s", info
->version
,
324 // Look for the "module"_read_init function we'll use to get data
325 // from the cgi-bin as a dynamically loadable plugin.
327 symbol
.append("_read_func");
329 Handler::cygnal_io_read_t read_symptr
= reinterpret_cast<Handler::cygnal_io_read_t
>
330 (sl
->getInitEntry(symbol
));
333 log_error(_("Couldn't get %s symbol"), symbol
);
338 _plugin
->read_func
= read_symptr
;
340 // Look for the "module"_write_init function we'll use to send data
341 // to the cgi-bin as a dynamically loadable plugin.
343 symbol
.append("_write_func");
344 Handler::cygnal_io_write_t write_symptr
= reinterpret_cast<Handler::cygnal_io_write_t
>
345 (sl
->getInitEntry(symbol
));
348 log_error(_("Couldn't get %s symbol"), symbol
);
353 _plugin
->write_func
= write_symptr
;
359 Handler::writeToPlugin(boost::uint8_t *data
, size_t size
)
361 // GNASH_REPORT_FUNCTION;
364 ret
= _plugin
->write_func(data
, size
);
370 boost::shared_ptr
<cygnal::Buffer
>
371 Handler::readFromPlugin()
373 // GNASH_REPORT_FUNCTION;
375 boost::shared_ptr
<cygnal::Buffer
> buf
;
377 buf
= _plugin
->read_func();
384 Handler::initialized()
386 // GNASH_REPORT_FUNCTION;
388 && (_clients
.size() == 1)
398 // Find a stream in the vector or Disk Streams
399 boost::shared_ptr
<gnash::DiskStream
>
400 Handler::findStream(const std::string
&filespec
)
402 // GNASH_REPORT_FUNCTION;
404 for (int i
; i
< _streams
; i
++) {
405 if (_diskstreams
[i
]->getFilespec() == filespec
) {
406 return _diskstreams
[i
];
410 return _diskstreams
[0];
413 // Create a new DiskStream
415 Handler::createStream(double /* transid */)
417 GNASH_REPORT_FUNCTION
;
419 _diskstreams
[_streams
]->setState(DiskStream::CREATED
);
424 // Create a new DiskStream
426 Handler::createStream(double /* transid */, const std::string
&filespec
)
428 GNASH_REPORT_FUNCTION
;
430 if (filespec
.empty()) {
434 _diskstreams
[_streams
]->setState(DiskStream::CREATED
);
435 _diskstreams
[_streams
]->setFilespec(filespec
);
441 Handler::playStream()
443 GNASH_REPORT_FUNCTION
;
445 // _diskstreams[int(streamid)]->setState(DiskStream::PLAY);
451 Handler::playStream(const std::string
&filespec
)
453 GNASH_REPORT_FUNCTION
;
455 boost::shared_ptr
<gnash::DiskStream
> ds
= _diskstreams
[_streams
];
457 string fullpath
= crcfile
.getDocumentRoot();
459 fullpath
+= filespec
;
460 log_debug("FILENAME: %s", fullpath
);
462 // gnash::DiskStream &ds = findStream(filespec);
463 if (ds
->getState() == DiskStream::CREATED
) {
464 if (ds
->open(fullpath
)) {
465 ds
->loadToMem(0); // FIXME: load only part of the whole file for now
466 ds
->setState(DiskStream::PLAY
);
474 // Publish a live stream
476 Handler::publishStream()
478 GNASH_REPORT_FUNCTION
;
480 return publishStream("", Handler::LIVE
);
484 Handler::publishStream(const std::string
&/*filespec */, Handler::pub_stream_e
/* op
487 GNASH_REPORT_FUNCTION
;
489 // _diskstreams[int(streamid)]->setState(DiskStream::PUBLISH);
494 // Seek within the RTMP stream
496 Handler::seekStream()
498 GNASH_REPORT_FUNCTION
;
499 // _diskstreams[int(streamid)]->setState(DiskStream::SEEK);
505 Handler::seekStream(int /* offset */)
507 GNASH_REPORT_FUNCTION
;
508 // _diskstreams[int(streamid)]->setState(DiskStream::SEEK);
513 // Pause the RTMP stream
515 Handler::pauseStream(double streamid
)
517 GNASH_REPORT_FUNCTION
;
519 _diskstreams
[int(streamid
)]->setState(DiskStream::PAUSE
);
524 // Pause the RTMP stream
526 Handler::togglePause(double streamid
)
528 GNASH_REPORT_FUNCTION
;
530 if (_diskstreams
[int(streamid
)]->getState() == DiskStream::PAUSE
) {
531 _diskstreams
[int(streamid
)]->setState(DiskStream::PLAY
);
532 } if (_diskstreams
[int(streamid
)]->getState() == DiskStream::PLAY
) {
533 _diskstreams
[int(streamid
)]->setState(DiskStream::PAUSE
);
539 // Resume the paused RTMP stream
541 Handler::resumeStream(double streamid
)
543 GNASH_REPORT_FUNCTION
;
545 togglePause(streamid
);
550 // Close the RTMP stream
552 Handler::closeStream(double streamid
)
554 GNASH_REPORT_FUNCTION
;
556 _diskstreams
[int(streamid
)]->setState(DiskStream::CLOSED
);
561 // Delete the RTMP stream
563 Handler::deleteStream(double streamid
)
565 GNASH_REPORT_FUNCTION
;
567 _diskstreams
[int(streamid
)]->setState(DiskStream::NO_STATE
);
574 // Dump internal data.
578 const char *proto_str
[] = {
590 // GNASH_REPORT_FUNCTION;
591 cerr
<< "Currently there are " <<_clients
.size() << " clients connected."
593 for (size_t i
= 0; i
< _clients
.size(); i
++) {
594 cerr
<< "Client on fd #" << _clients
[i
] << " is using "
595 << proto_str
[_protocol
[i
]] << endl
;
598 cerr
<< "Currently there are " << dec
<<_diskstreams
.size() << " DiskStreams."
600 map
<int, boost::shared_ptr
<DiskStream
> >::iterator it
;
601 for (it
= _diskstreams
.begin(); it
!= _diskstreams
.end(); ++it
) {
603 cerr
<< "DiskStream for fd #" << dec
<< it
->first
<< endl
;
610 } // end of gnash namespace
614 // indent-tabs-mode: t