[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Gnash-commit] /srv/bzr/gnash/trunk r11389: merge from branch.
From: |
Rob Savoye |
Subject: |
[Gnash-commit] /srv/bzr/gnash/trunk r11389: merge from branch. |
Date: |
Tue, 25 Aug 2009 18:29:00 -0000 |
User-agent: |
Bazaar (1.16.1) |
------------------------------------------------------------
revno: 11389 [merge]
committer: Rob Savoye <address@hidden>
branch nick: trunk
timestamp: Fri 2009-08-07 21:21:12 -0600
message:
merge from branch.
modified:
libbase/log.cpp
libbase/log.h
libcore/asobj/NetConnection_as.cpp
libcore/asobj/flash/net/SharedObject_as.cpp
libnet/diskstream.cpp
libnet/network.cpp
libnet/rtmp.h
libnet/rtmp_client.cpp
libnet/rtmp_client.h
testsuite/network.all/SharedObject_as.hx
utilities/processor.cpp
utilities/rtmpget.cpp
=== modified file 'libbase/log.cpp'
--- a/libbase/log.cpp 2009-05-20 08:37:23 +0000
+++ b/libbase/log.cpp 2009-08-08 00:55:45 +0000
@@ -146,6 +146,12 @@
}
void
+processLog_network(const boost::format& fmt)
+{
+ dbglogfile.log(N_("NETWORK"), fmt.str());
+}
+
+void
processLog_error(const boost::format& fmt)
{
dbglogfile.log(N_("ERROR"), fmt.str());
=== modified file 'libbase/log.h'
--- a/libbase/log.h 2009-06-07 21:14:22 +0000
+++ b/libbase/log.h 2009-08-08 00:55:45 +0000
@@ -136,10 +136,18 @@
_actiondump = x;
}
+ void setNetwork(int x) {
+ _network = x;
+ }
+
int getActionDump() const {
return _actiondump;
}
+ int getNetwork() const {
+ return _network;
+ }
+
void setParserDump (int x) {
_parserdump = x;
}
@@ -204,6 +212,9 @@
/// Whether to dump all SWF actions
bool _actiondump;
+ /// Whether to dump all networking actions
+ bool _network;
+
/// Whether to dump parser output
bool _parserdump;
@@ -223,6 +234,7 @@
};
+DSOEXPORT void processLog_network(const boost::format& fmt);
DSOEXPORT void processLog_error(const boost::format& fmt);
DSOEXPORT void processLog_unimpl(const boost::format& fmt);
DSOEXPORT void processLog_trace(const boost::format& fmt);
@@ -251,7 +263,7 @@
/// the code. Append the name to log_ to call the function, e.g.
/// log_error, log_unimpl.
#define LOG_TYPES (error) (debug) (unimpl) (aserror) (swferror) \
- (amferror) (security) (action) (parse) (trace) (abc)
+ (amferror) (security) (action) (parse) (trace) (abc) (network)
/// This actually creates the template functions using the TOKENIZE
/// functions above. The templates look like this:
@@ -342,6 +354,10 @@
#define VERBOSE_MALFORMED_AMF 1
#endif
+// Define to 0 this to remove Networking verbosity at compile-time
+#ifndef VERBOSE_NETWORKING
+#define VERBOSE_NETWORKING 1
+#endif
#if VERBOSE_PARSE
#define IF_VERBOSE_PARSE(x) do { if (
LogFile::getDefaultInstance().getParserDump() ) { x; } } while (0);
@@ -355,6 +371,12 @@
#define IF_VERBOSE_ACTION(x)
#endif
+#if VERBOSE_ACTION
+#define IF_VERBOSE_NETWORK(x) do { if (
LogFile::getDefaultInstance().getNetwork() ) { x; } } while (0);
+#else
+#define IF_VERBOSE_NETWORK(x)
+#endif
+
#if VERBOSE_ASCODING_ERRORS
// TODO: check if it's worth to check verbosity level too...
#define IF_VERBOSE_ASCODING_ERRORS(x) { if (
gnash::RcInitFile::getDefaultInstance().showASCodingErrors() ) { x; } }
=== modified file 'libcore/asobj/NetConnection_as.cpp'
--- a/libcore/asobj/NetConnection_as.cpp 2009-08-04 17:39:22 +0000
+++ b/libcore/asobj/NetConnection_as.cpp 2009-08-08 03:21:12 +0000
@@ -846,24 +846,19 @@
URL url(uri, getRunResources(*this).baseURL());
- if ( url.protocol() == "rtmp" )
- {
- LOG_ONCE(log_unimpl("NetConnection.connect(%s): RTMP not "
- "yet supported", url) );
- notifyStatus(CONNECT_FAILED);
- return;
- }
-
- if ( url.protocol() != "http" )
- {
+ if ((url.protocol() != "rtmp")
+ || (url.protocol() != "rtmpt")
+ || (url.protocol() != "rtmpts")
+ || (url.protocol() != "https")
+ || (url.protocol() == "http")) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("NetConnection.connect(%s): invalid connection "
- "protocol", url);
- );
+ log_aserror("NetConnection.connect(%s): invalid connection "
+ "protocol", url);
+ );
notifyStatus(CONNECT_FAILED);
return;
}
-
+
// This is for HTTP remoting
if (!URLAccessManager::allow(url)) {
=== modified file 'libcore/asobj/flash/net/SharedObject_as.cpp'
--- a/libcore/asobj/flash/net/SharedObject_as.cpp 2009-08-04 17:39:22
+0000
+++ b/libcore/asobj/flash/net/SharedObject_as.cpp 2009-08-08 03:21:12
+0000
@@ -41,9 +41,11 @@
#include "VM.h"
#include "Property.h"
#include "string_table.h"
+#include "rc.h" // for use of rcfile
#include "URLAccessManager.h"
+#include "network.h"
+#include "rtmp_client.h"
#include "URL.h"
-#include "rc.h" // for use of rcfile
#include "NetConnection_as.h"
#include <boost/scoped_array.hpp>
@@ -211,19 +213,15 @@
private:
SimpleBuffer& _buf;
-
VM& _vm;
-
string_table& _st;
-
PropertiesOffsetTable& _offsetTable;
-
bool _error;
};
} // anonymous namespace
-class SharedObject_as: public as_object
+class SharedObject_as: public as_object, public RTMPClient
{
public:
@@ -232,7 +230,8 @@
SharedObject_as()
: as_object(getSharedObjectInterface()),
_data(0),
- _persistance(0)
+ _persistance(0),
+ _connected(false)
{
}
@@ -290,10 +289,13 @@
/// Process the connect(uri) method.
void connect(NetConnection_as *obj, const std::string& uri);
- NetConnection_as *_netconn;
+ void setURI(const std::string &url) { _uri = url; }
+ std::string &getURI() { return _uri; }
+
+ bool isConnected() { return _connected; };
+ void isConnected(bool x) { _connected = x; };
protected:
-
#ifdef GNASH_USE_GC
void markReachableResources() const;
#endif
@@ -302,8 +304,9 @@
as_object *_data;
bool _persistance;
-
- SOL _sol;
+ SOL _sol;
+ bool _connected;
+ std::string _uri;
};
@@ -871,13 +874,21 @@
if (fn.nargs > 1) {
const as_value& uri = fn.arg(1);
const VM& vm = getVM(fn);
- const std::string& uriStr = uri.to_string_versioned(vm.getSWFVersion());
+ const std::string& uriStr = uri.to_string_versioned(vm.getSWFVersion());
}
boost::intrusive_ptr<NetConnection_as> nc =
boost::dynamic_pointer_cast<NetConnection_as>(
fn.arg(0).to_object(*getGlobal(fn)));
// This is always set without validification.fooc->setURI(uriStr);
+ string str = nc->getURI();
+ obj->setPath(str);
+ URL uri = nc->getURI();
+ Network *net = new Network;
+
+ net->setProtocol(uri.protocol());
+ net->setHost(uri.hostname());
+ net->setPort(strtol(uri.port().c_str(), NULL, 0) & 0xffff);
// Check first arg for validity
if (getSWFVersion(fn) > 6) {
@@ -888,7 +899,6 @@
log_unimpl("SharedObject.connect(%s): args after the first are "
"not supported", ss.str());
}
-// nc->connect(uriStr);
nc->connect();
}
@@ -920,18 +930,21 @@
as_value
sharedobject_send(const fn_call& fn)
{
+ GNASH_REPORT_FUNCTION;
+
boost::intrusive_ptr<SharedObject_as> obj =
ensureType<SharedObject_as>(fn.this_ptr);
- UNUSED(obj);
- LOG_ONCE(log_unimpl("SharedObject.send"));
+ if (obj->isConnected() == false) {
+ obj->connectToServer(obj->getURI());
+ }
+
return as_value();
}
as_value
sharedobject_flush(const fn_call& fn)
-{
-
+{
GNASH_REPORT_FUNCTION;
boost::intrusive_ptr<SharedObject_as> obj =
=== modified file 'libnet/diskstream.cpp'
--- a/libnet/diskstream.cpp 2009-05-04 22:20:08 +0000
+++ b/libnet/diskstream.cpp 2009-08-05 17:22:03 +0000
@@ -46,6 +46,12 @@
#include "cache.h"
#include "getclocktime.hpp"
+// This is Linux specific, but offers better I/O for sending
+// files out a network connection.
+#ifdef HAVE_SENDFILE
+# include <sys/sendfile.h>
+#endif
+
#include <boost/thread/mutex.hpp>
static boost::mutex io_mutex;
=== modified file 'libnet/network.cpp'
--- a/libnet/network.cpp 2009-07-18 23:10:13 +0000
+++ b/libnet/network.cpp 2009-08-08 02:48:35 +0000
@@ -833,7 +833,7 @@
int
Network::readNet(int fd, byte_t *buffer, int nbytes, int timeout)
{
-// GNASH_REPORT_FUNCTION;
+// GNASH_REPORT_FUNCTION;
fd_set fdset;
int ret = -1;
@@ -962,14 +962,14 @@
int
Network::writeNet(amf::Buffer *buffer)
{
-// GNASH_REPORT_FUNCTION;
+// GNASH_REPORT_FUNCTION;
return writeNet(buffer->reference(), buffer->allocated());
};
int
Network::writeNet(int fd, amf::Buffer *buffer)
{
-// GNASH_REPORT_FUNCTION;
+// GNASH_REPORT_FUNCTION;
return writeNet(fd, buffer->reference(), buffer->allocated());
};
@@ -977,7 +977,7 @@
int
Network::writeNet(amf::Buffer &buffer)
{
-// GNASH_REPORT_FUNCTION;
+// GNASH_REPORT_FUNCTION;
return writeNet(buffer.reference(), buffer.allocated());
};
@@ -985,20 +985,21 @@
int
Network::writeNet(int fd, amf::Buffer &buffer)
{
-// GNASH_REPORT_FUNCTION;
+// GNASH_REPORT_FUNCTION;
return writeNet(fd, buffer.reference(), buffer.allocated());
};
int
Network::writeNet(const std::string& data)
{
-// GNASH_REPORT_FUNCTION;
+// GNASH_REPORT_FUNCTION;
return writeNet(reinterpret_cast<const byte_t *>(data.c_str()),
data.size());
}
int
Network::writeNet(const byte_t *data, int nbytes)
{
+// GNASH_REPORT_FUNCTION;
return writeNet(_sockfd, data, nbytes, _timeout);
}
@@ -1017,12 +1018,15 @@
int
Network::writeNet(int fd, const byte_t *data, int nbytes)
{
+// GNASH_REPORT_FUNCTION;
return writeNet(fd, data, nbytes, _timeout);
}
int
Network::writeNet(int fd, const byte_t *buffer, int nbytes, int timeout)
{
+// GNASH_REPORT_FUNCTION;
+
fd_set fdset;
int ret = -1;
=== modified file 'libnet/rtmp.h'
--- a/libnet/rtmp.h 2009-07-26 01:56:07 +0000
+++ b/libnet/rtmp.h 2009-08-08 02:47:08 +0000
@@ -36,8 +36,11 @@
namespace gnash
{
+const boost::uint8_t RTMP_VERSION = 0x3;
const boost::uint8_t RTMP_HANDSHAKE = 0x3;
const int RTMP_HANDSHAKE_SIZE = 1536;
+const int RTMP_RANDOM_SIZE = 1528;
+const int RTMP_HANDSHAKE_HEADER_SIZE = 8;
const int MAX_AMF_INDEXES = 64;
const int RTMP_HEADSIZE_MASK = 0xc0;
@@ -48,6 +51,7 @@
const int PING_MSG_SIZE = 6;
const int RTMP_SYSTEM_CHANNEL = 2;
+
// For terminating sequences, a byte with value 0x09 is used.
const char TERMINATOR = 0x09;
@@ -82,17 +86,38 @@
typedef std::map<const char*, amf::Element> AMFProperties;
typedef std::deque<CQue *> queues_t;
typedef enum {
- RAW=0x0,
- ADPCM=0x01,
- MP3=0x02,
- NELLYMOSER_8khz=0x05,
- NEYYNOSER=0x6
+ RAW = 0x0001,
+ ADPCM = 0x0002,
+ MP3 = 0x0004,
+ INTEL = 0x0005,
+ CELT = 0x0008, // unique to Gnash
+ NELLY8 = 0x0020,
+ NELLY = 0x0040,
+ G711A = 0x0080,
+ G711U = 0x0100,
+ NELLY16 = 0x0200,
+ AAC = 0x0400,
+ SPEEX = 0x0800,
+ DEFAULT_AUDIO_SET = 0x0267,
+ ALLAUDIO = 0x0fff
} audiocodecs_e;
typedef enum {
- H263=0x2,
- SCREEN0x3,
- VP6=0x4
+ UNUSED = 0x0001,
+ JPEG = 0x0002,
+ SORENSON = 0x4,
+ ADOBE = 0x0008,
+ VP6 = 0x0010,
+ VP6ALPHA = 0x0020,
+ SCREEN2 = 0x0040,
+ H264 = 0x0080,
+ DEFAULT_VIDEO_SET = 0x007c,
+ ALLVIDEO = 0x00ff
} videocodecs_e;
+ typedef enum {
+ SEEK = 0x1,
+ AMF0 = 0x0,
+ AMF3 = 0x3
+ } videofunction_e;
// The second byte of the AMF file/stream is appears to be 0x00 if the
// client is the Flash Player and 0x01 if the client is the FlashCom
// server.
@@ -113,19 +138,19 @@
INVOKE = 0x14,
FLV_DATA = 0x16
} content_types_e;
-// typedef enum {
-// CONNECT = 0x1,
-// DISCONNECT = 0x2,
-// SET_ATTRIBUTE = 0x3,
-// UPDATE_DATA = 0x4,
-// UPDATE_ATTRIBUTE = 0x5,
-// SEND_MESSAGE = 0x6,
-// STATUS = 0x7,
-// CLEAR_DATA = 0x8,
-// DELETE_DATA = 0x9,
-// DELETE_ATTRIBUTE = 0xa,
-// INITIAL_DATA = 0xb
-// } sharedobj_types_e;
+ typedef enum {
+ CREATE = 0x1, // Client sends event
+ DELETE = 0x2, // Client sends event
+ REQUEST_CHANGE = 0x3, // Client sends event
+ CHANGE = 0x4, // Server sends event
+ SUCCESS_CLIENT = 0x5, // Server sends event
+ SEND_MESSAGE = 0x6, // Client sends event
+ STATUS = 0x7, // Server sends evetn
+ CLEAR = 0x8, // Server sends event
+ DELETE_SLOT = 0x9, // Server sends event
+ REQUEST_DELETE_SLOT = 0xa,// Client sends event
+ SUCCESS_SERVER = 0xb // Server sends event
+ } sharedobj_types_e;
typedef enum {
PING_CLEAR = 0x0, // clear the stream
PING_PLAY = 0x1, // clear the playing buffer
@@ -190,6 +215,10 @@
RTMPMsg::rtmp_source_e src_dest;
content_types_e type;
} rtmp_head_t;
+ typedef struct {
+ boost::uint32_t uptime;
+ boost::uint8_t version[4];
+ } rtmp_handshake_head_t;
typedef enum {
HEADER_12 = 0x0,
HEADER_8 = 0x40,
@@ -322,6 +351,7 @@
CQue _queues[MAX_AMF_INDEXES];
// queues_t _channels;
amf::Buffer _buffer;
+ rtmp_handshake_head_t _handshake_header;
};
} // end of gnash namespace
=== modified file 'libnet/rtmp_client.cpp'
--- a/libnet/rtmp_client.cpp 2009-07-26 01:56:07 +0000
+++ b/libnet/rtmp_client.cpp 2009-08-08 02:50:32 +0000
@@ -21,6 +21,7 @@
#include "gnashconfig.h"
#endif
+#include <ctime>
#include <iostream>
#include <string>
#include <map>
@@ -41,6 +42,7 @@
#include "utility.h"
#include "buffer.h"
#include "GnashSleep.h"
+#include "URL.h"
using namespace gnash;
using namespace std;
@@ -80,11 +82,98 @@
// Make the NetConnection object that is used to connect to the
// server.
boost::shared_ptr<Buffer>
+RTMPClient::encodeConnect()
+{
+// GNASH_REPORT_FUNCTION;
+
+ return encodeConnect(_path.c_str());
+}
+
+boost::shared_ptr<Buffer>
+RTMPClient::encodeConnect(const char *uri)
+{
+// GNASH_REPORT_FUNCTION;
+
+ return encodeConnect(uri, RTMPClient::DEFAULT_AUDIO_SET,
+ RTMPClient::DEFAULT_VIDEO_SET,
+ RTMPClient::SEEK);
+}
+
+boost::shared_ptr<Buffer>
+RTMPClient::encodeConnect(const char *uri,
+ double audioCodecs, double videoCodecs,
+ double videoFunction)
+{
+ GNASH_REPORT_FUNCTION;
+
+ URL url(uri);
+ string portstr;
+
+ short port = 0;
+ string protocol; // the network protocol, rtmp or http
+ string query; // any queries for the host
+ string app; // the application name
+ string path; // the path to the file on the server
+ string tcUrl; // the tcUrl field
+ string swfUrl; // the swfUrl field
+ string filename; // the filename to play
+ string pageUrl; // the pageUrl field
+ string hostname; // the hostname of the server
+
+
+ protocol = url.protocol();
+ hostname = url.hostname();
+ log_network("hostname: %s", hostname);
+ portstr = url.port();
+ query = url.querystring();
+
+ if (portstr.empty()) {
+ if ((protocol == "http") || (protocol == "rtmpt")) {
+ port = RTMPT_PORT;
+ }
+ if (protocol == "rtmp") {
+ port = RTMP_PORT;
+ }
+ } else {
+ port = strtol(portstr.c_str(), NULL, 0) & 0xffff;
+ }
+
+
+ path = url.path();
+
+ string::size_type end = path.rfind('/');
+ if (end != string::npos) {
+ filename = path.substr(end + 1);
+ }
+
+ tcUrl = uri;
+ app = filename;
+ swfUrl = "mediaplayer.swf";
+ pageUrl = "http://gnashdev.org";
+
+ log_network("URL is %s", url);
+ log_network("Protocol is %s", protocol);
+ log_network("Host is %s", hostname);
+ log_network("Port is %s", port);
+ log_network("Path is %s", path);
+ log_network("Filename is %s", filename);
+ log_network("App is %s", app);
+ log_network("Query is %s", query);
+ log_network("tcUrl is %s", tcUrl);
+ log_network("swfUrl is %s", swfUrl);
+ log_network("pageUrl is %s", pageUrl);
+
+ return encodeConnect(app.c_str(), swfUrl.c_str(), tcUrl.c_str(),
+ audioCodecs, videoCodecs, videoFunction,
+ pageUrl.c_str());
+}
+
+boost::shared_ptr<Buffer>
RTMPClient::encodeConnect(const char *app, const char *swfUrl, const char
*tcUrl,
double audioCodecs, double videoCodecs, double
videoFunction,
const char *pageUrl)
{
-// GNASH_REPORT_FUNCTION;
+ GNASH_REPORT_FUNCTION;
AMF amf_obj;
@@ -171,7 +260,75 @@
return buf;
}
-
+
+bool
+RTMPClient::connectToServer(const std::string &/* url */)
+{
+ GNASH_REPORT_FUNCTION;
+
+ // If we're currently not connected, build and send the
+ // initial handshake packet.
+ if (connected() == false) {
+ createClient();
+
+ // Build the NetConnection Packet, which seems to need
+ // to be on the end of the second block of handshake data.
+ // We build this here so we can get the total encoded
+ // size of the object.
+ boost::shared_ptr<amf::Buffer> ncbuf = encodeConnect();
+ boost::shared_ptr<amf::Buffer> head = encodeHeader(0x3,
+ RTMP::HEADER_12, ncbuf->allocated(),
+ RTMP::INVOKE, RTMPMsg::FROM_CLIENT);
+
+
+ // Build the first handshake packet, and send it to the
+ // server.
+ boost::shared_ptr<amf::Buffer> handshake1 = handShakeRequest();
+ if (!handshake1) {
+ log_error("RTMP handshake request failed");
+ return false;
+ }
+
+ boost::scoped_ptr<amf::Buffer> handshake2(new amf::Buffer
+ ((RTMP_HANDSHAKE_SIZE * 2) + ncbuf->allocated()));
+
+ *handshake2 = handshake1;
+ *handshake2 += ncbuf;
+
+ // Finish the handshake process, which has to have the
+ // NetConnection::connect() as part of the buffer, or Red5
+ // refuses to answer.
+ setTimeout(20);
+#if 0
+ if (!clientFinish(*handshake2)) {
+#else
+ if (!clientFinish(*ncbuf)) {
+#endif
+ log_error("RTMP handshake completion failed!");
+// return (false);
+ }
+
+ // give the server time to process our NetConnection::connect() request
+ boost::shared_ptr<amf::Buffer> response;
+ boost::shared_ptr<RTMP::rtmp_head_t> rthead;
+ boost::shared_ptr<RTMP::queues_t> que;
+
+ RTMPClient::msgque_t msgque = recvResponse();
+ while (msgque.size()) {
+ boost::shared_ptr<RTMPMsg> msg = msgque.front();
+ msgque.pop_front();
+ if (msg->getStatus() == RTMPMsg::NC_CONNECT_SUCCESS) {
+ log_network("Sent NetConnection Connect message sucessfully");
+ }
+ if (msg->getStatus() == RTMPMsg::NC_CONNECT_FAILED) {
+ log_error("Couldn't send NetConnection Connect message,");
+ }
+ }
+ }
+
+ return true;
+}
+
boost::shared_ptr<amf::Buffer>
RTMPClient::encodeEchoRequest(const std::string &method, double id,
amf::Element &el)
{
@@ -360,112 +517,172 @@
// A request for a handshake is initiated by sending a byte with a
// value of 0x3, followed by a message body of unknown format.
-bool
+boost::shared_ptr<amf::Buffer>
RTMPClient::handShakeRequest()
{
GNASH_REPORT_FUNCTION;
+ boost::uint32_t zero = 0;
// Make a buffer to hold the handshake data.
- _handshake = new Buffer(RTMP_HANDSHAKE_SIZE+1);
- if (!_handshake) {
- return false;
+ boost::shared_ptr<amf::Buffer> handshake(new
Buffer(RTMP_HANDSHAKE_SIZE+1));
+ if (!handshake) {
+ return handshake;
}
- // All RTMP connections start with a 0x3
- *_handshake = RTMP_HANDSHAKE;
-
- // Since we don't know what the format is, create a pattern we can
- // recognize if we stumble across it later on.
- for (int i=0; i<RTMP_HANDSHAKE_SIZE; i++) {
+ // All RTMP connections start with the RTMP version number
+ // which should always be 0x3.
+ *handshake = RTMP_VERSION;
+
+ // Get the uptime for the header
+ // yes, we loose precision here but it's only a 4 byte field
+ time_t t;
+ time(&t);
+ boost::uint32_t tt = t;
+ *handshake += tt;
+
+ // This next field in the header for the RTMP handshake should be zeros
+ *handshake += zero;
+
+ // The handshake contains random data after the initial header
+ for (int i=0; i<RTMP_RANDOM_SIZE; i++) {
boost::uint8_t pad = i^256;
- *_handshake += pad;
+ *handshake += pad;
}
- int ret = writeNet(_handshake);
- if (ret) {
- return true;
- } else {
- return false;
+ int ret = writeNet(*handshake);
+ if (ret <= 0) {
+ handshake.reset();
}
+
+ return handshake;
}
// The client finishes the handshake process by sending the second
// data block we get from the server as the response
-bool
+
+boost::shared_ptr<amf::Buffer>
RTMPClient::clientFinish()
{
- GNASH_REPORT_FUNCTION;
+// GNASH_REPORT_FUNCTION;
+
amf::Buffer data;
return clientFinish(data);
}
-bool
+boost::shared_ptr<amf::Buffer>
RTMPClient::clientFinish(amf::Buffer &data)
{
GNASH_REPORT_FUNCTION;
bool done = false;
int ret = 0;
+ int retries = 5;
int offset = 0;
-
- // The total size of incoming bytes is twice the handshake size, plus the
handshake
- // header byte. Then we append the NetConnection::connect packet as well.
- size_t maxsize = (RTMP_HANDSHAKE_SIZE*2)+1+data.size();
- boost::shared_ptr<amf::Buffer> buf(new amf::Buffer(maxsize));
+ // Create the initial buffer to hold the response, and keep reading data
+ // until it is populated
+ // The handhake for this phase is twice the size of the initial handshake
+ // we sent previously, plus one byte for the RTMP version header.
+ int max_size = (RTMP_HANDSHAKE_SIZE*2) + 1;
+ boost::shared_ptr<amf::Buffer> handshake1(new amf::Buffer(
+ max_size + data.allocated()));
do {
- ret = readNet(buf->reference() + offset, buf->size() - offset);
+ ret = readNet(handshake1->end(), max_size - offset);
offset += ret;
- buf->setSeekPointer(buf->reference() + offset);
- if (offset == (RTMP_HANDSHAKE_SIZE*2)+1) {
+ handshake1->setSeekPointer(handshake1->reference() + offset);
+ if ((offset >= max_size) || (ret >= max_size)) {
+ handshake1->setSeekPointer(handshake1->reference() + max_size);
+// log_network("Read entire packet of %d bytes", ret);
done = true;
}
if (ret < 0) {
log_error (_("Couldn't read data block in handshake!"));
+ handshake1.reset();
+ return handshake1;
+ }
+ // if retries equals zero, then we're done trying
+ if (retries == 0) {
done = true;
+ } else {
+ --retries;
}
- } while (!done);
+ } while (!done);
+
+ if (handshake1->allocated() == max_size) {
+ log_network (_("Read data block in handshake, got %d bytes."),
+ handshake1->allocated());
+ } else {
+ log_error("Couldn't read data block in handshake, read %d bytes!",
+ handshake1->allocated());
+ }
+
+ _handshake_header.uptime = ntohl(*reinterpret_cast<boost::uint32_t *>
+ (handshake1->reference() + 1));
+
+ log_network("RTMP Handshake header: Uptime: %u", _handshake_header.uptime);
+
+#if 0
+ if (memcmp(handshake2->reference() + RTMP_HANDSHAKE_SIZE + 8,
+ _handshake->reference() + 8, RTMP_RANDOM_SIZE-8) == 0) {
+ log_network("Handshake matched");
+ } else {
+ log_network("Handshake didn't match");
+// return false;
+ }
+#endif
+
+ // Make a new buffer big enough to hold the handshake, data, and header
byte
+ boost::shared_ptr<amf::Buffer> handshake2(new amf::Buffer(
+ RTMP_HANDSHAKE_SIZE + data.allocated()));
- if (buf->allocated() > RTMP_HANDSHAKE_SIZE) {
- log_debug (_("Read first data block in handshake"));
- } else {
- log_error (_("Couldn't read first data block in handshake"));
- }
- if (buf->allocated() > RTMP_HANDSHAKE_SIZE*2) {
- log_debug (_("Read second data block in handshake"));
- } else {
- log_error (_("Couldn't read second data block in handshake"));
- return false;
- }
+ // Copy the timestamp from the message we just received.
+ handshake2->copy(handshake1->reference()+1, sizeof(boost::uint32_t));
#if 1
- if (memcmp(buf->reference() + RTMP_HANDSHAKE_SIZE + 1,
_handshake->reference(), RTMP_HANDSHAKE_SIZE) == 0) {
- log_debug("Handshake matched");
- } else {
- log_debug("Handshake didn't match");
-// return false;
- }
- *buf += data;
+ // The next timestamp is the one we just received bumped up a tiny bit.
+ // I have no clue if this is correct, but fom hex dumps the previous
+ // timestamp should be the baseline, and this is just that time plus
+ // whatever it took to get the message. The 7 is a bit randomly chosen.
+ boost::uint32_t tt = htonl(_handshake_header.uptime + 7);
+#else
+ // Get the uptime for the header
+ // yes, we loose precision here but it's only a 4 byte field
+ time_t t;
+ time(&t);
+ boost::uint32_t tt = t;
#endif
-
- // For some reason, Red5 won't connect unless the connect packet is
- // part of the final handshake packet. Sending the identical data with
- // two writeNet()s won't connect. Go figure...
- _handshake->resize(RTMP_HANDSHAKE_SIZE + data.size());
- // FIXME: unless the handshake is all zeros, Gnash won't connect to
- // Red5 for some reason. Cygnal isn't so picky.
- _handshake->clear();
- _handshake->setSeekPointer(_handshake->reference() + RTMP_HANDSHAKE_SIZE);
+ *handshake2 += tt;
+
+ // Add the handshake data
+ boost::uint8_t *start = handshake1->reference() + RTMP_HANDSHAKE_SIZE
+ + 1 + 8;
+ handshake2->append(start, RTMP_RANDOM_SIZE);
// Add the NetConnection::connect() packet
- _handshake->append(data.reference(), data.allocated());
- ret = writeNet(_handshake->reference(), _handshake->allocated());
+ *handshake2 += data;
+
+ // Write the second chunk to the server
+ log_network("About to write %d bytes, data is: %d bytes.",
+ handshake2->allocated(),
+ data.allocated());
+ log_network("Client response header for handshake 2: %s",
hexify(handshake2->reference(), 12, false));
+ log_network("Data in response for handshake 2: %s",
hexify(handshake1->reference() + RTMP_HANDSHAKE_SIZE + 1, 12, false));
+#if 0
+ ret = writeNet(handshake2->reference()+RTMP_HANDSHAKE_SIZE,
+ RTMP_HANDSHAKE_SIZE + data.allocated() + 1);
+#else
+ ret = writeNet(*handshake2);
+#endif
if ( ret <= 0 ) {
- return false;
+ log_error("Couldn't write the second handshake packet!");
+ handshake1.reset();
+ return handshake1;
+ } else {
+ _connected = false;
}
-
+
// Since the handshake completed sucessfully, we're connected.
- _connected == true;
+ _connected = true;
- return true;
+ return handshake1;
}
// Get and process an RTMP response. After reading all the data, then we have
@@ -483,13 +700,14 @@
boost::shared_ptr<amf::Buffer> response = recvMsg();
if (!response) {
log_error("Got no response from the RTMP server");
+ return msgque;
}
// when doing remoting calls I don't see this problem with an empty packet
from Red5,
// but when I do streaming, it's always there, so we need to remove it.
boost::uint8_t *pktstart = response->reference();
if (*pktstart == 0xff) {
- log_debug("Got empty packet in buffer.");
+ log_network("Got empty packet in buffer.");
pktstart++;
}
@@ -508,7 +726,7 @@
// is indexed by the channel number, the second queue is all the messages
that
// have arrived for that channel.
while (que->size()) { // see if there are any messages at all
- log_debug("%s: There are %d channel queues in the RTMP input queue, %d
messages in front queue",
+ log_network("%s: There are %d channel queues in the RTMP input queue,
%d messages in front queue",
__PRETTY_FUNCTION__, que->size(), que->front()->size());
// Get the CQue for the first channel
CQue *channel_q = que->front();
@@ -540,7 +758,7 @@
case RTMP::PING:
{
boost::shared_ptr<RTMP::rtmp_ping_t> ping =
decodePing(ptr->reference() + rthead->head_size);
- log_debug("Got a Ping type %s", ping_str[ping->type]);
+ log_network("Got a Ping type %s", ping_str[ping->type]);
break;
}
case RTMP::SERVER:
=== modified file 'libnet/rtmp_client.h'
--- a/libnet/rtmp_client.h 2009-07-26 01:56:07 +0000
+++ b/libnet/rtmp_client.h 2009-08-08 02:50:32 +0000
@@ -32,6 +32,7 @@
#include "network.h"
#include "buffer.h"
#include "dsodefs.h"
+#include "URL.h"
namespace gnash
{
@@ -44,15 +45,24 @@
bool handShakeWait();
// bool handShakeResponse();
- bool clientFinish();
- DSOEXPORT bool clientFinish(amf::Buffer &data);
- DSOEXPORT bool handShakeRequest();
+ boost::shared_ptr<amf::Buffer> clientFinish();
+ DSOEXPORT boost::shared_ptr<amf::Buffer> clientFinish(amf::Buffer &data);
+ DSOEXPORT boost::shared_ptr<amf::Buffer> handShakeRequest();
// These are used for creating the primary objects
- // Create the initial object sent to the server, which is
NetConnection::connect()
- DSOEXPORT boost::shared_ptr<amf::Buffer> encodeConnect(const char *app,
const char *swfUrl, const char *tcUrl,
- double audioCodecs, double videoCodecs,
double videoFunction,
- const char *pageUrl);
+ // Create the initial object sent to the server, which
+ // is NetConnection::connect()
+ DSOEXPORT boost::shared_ptr<amf::Buffer> encodeConnect();
+ DSOEXPORT boost::shared_ptr<amf::Buffer> encodeConnect(const char *uri);
+ DSOEXPORT boost::shared_ptr<amf::Buffer> encodeConnect(const char *uri,
double audioCodecs, double videoCodecs,
+ double videoFunction);
+ DSOEXPORT boost::shared_ptr<amf::Buffer> encodeConnect(const char *app,
+ const char *swfUrl, const char *tcUrl,
+ double audioCodecs, double videoCodecs, double
videoFunction,
+ const char *pageUrl);
+
+ DSOEXPORT bool connectToServer(const std::string &url);
+
// Create the second object sent to the server, which is
NetStream():;NetStream()
DSOEXPORT boost::shared_ptr<amf::Buffer> encodeStream(double id);
boost::shared_ptr<amf::Buffer> encodeStreamOp(double id, rtmp_op_e op,
bool flag);
=== modified file 'testsuite/network.all/SharedObject_as.hx'
--- a/testsuite/network.all/SharedObject_as.hx 2009-08-04 03:13:31 +0000
+++ b/testsuite/network.all/SharedObject_as.hx 2009-08-05 17:23:53 +0000
@@ -69,25 +69,30 @@
DejaGnu.note("No RTMP port specified, defaulting to "+rtmpport);
}
- var nc:NetConnection = new NetConnection();
// The Adobe flash player only supports remoting with RTMP
rtmpuri = "rtmp://"+hostname+":"+rtmpport+"/fitcDemo";
-
+
+ var nc:NetConnection = new NetConnection();
+// addEventListeners();
+ nc.connect(rtmpuri);
+ DejaGnu.note("Connecting to "+rtmpuri);
+
#if flash9
- var x1:SharedObject = SharedObject.getRemote("sharedobjecttest",
rtmpuri, true);
+ var x1:SharedObject = SharedObject.getRemote("rsoltest", nc.uri, true);
#else
- var x1:SharedObject = SharedObject.getRemote("sharedobjecttest",
rtmpuri, true);
+ var x1:SharedObject = SharedObject.getRemote("rsoltest", nc.uri, true);
#end
+
if (Std.is(x1, SharedObject)) {
DejaGnu.pass("SharedObject class exists");
} else {
DejaGnu.fail("SharedObject class doesn't exist");
}
- DejaGnu.note("SharedObject type is "+Type.typeof(x1));
+// DejaGnu.note("SharedObject type is "+Type.typeof(x1));
// var ns:NetStream = new NetStream(nc);
#if flash9
-// nc.addEventListener(NetStatusEvent.NET_STATUS, ncOnStatus);
+ nc.addEventListener(NetStatusEvent.NET_STATUS, ncOnStatus);
#else
nc.setID = function(id) {
DejaGnu.note("Got a setID() from "+rtmpuri);
@@ -106,11 +111,12 @@
// DejaGnu.note(e.description);
#end
x1.connect(nc);
-
-// Reflect.callMethod(x1, Reflect.field(x1, "connect"), []);
- DejaGnu.note("Connecting to "+rtmpuri);
-
- x1.send("sharedobjecttest", "Hello World");
+// x1.addEventListener(SyncEvent.SYNC, syncHandler);
+
+ x1.data.whoami = "me";
+ x1.send("rsoltest", "Hello World");
+
+ DejaGnu.note("Sent SharedObject to "+rtmpuri);
#if flash9
x1.setDirty("data");
=== modified file 'utilities/processor.cpp'
--- a/utilities/processor.cpp 2009-07-13 09:04:26 +0000
+++ b/utilities/processor.cpp 2009-08-08 02:48:06 +0000
@@ -276,7 +276,7 @@
dbglogfile.setVerbosity();
}
- while ((c = getopt (argc, argv, ":hwvapr:gf:d:")) != -1) {
+ while ((c = getopt (argc, argv, ":hwvapr:gf:d:n")) != -1) {
switch (c) {
case 'h':
usage (argv[0]);
@@ -297,6 +297,9 @@
#else
log_error (_("The debugger has been disabled at configuration
time"));
#endif
+ case 'n':
+ dbglogfile.setNetwork(true);
+ break;
case 'a':
#if VERBOSE_ACTION
dbglogfile.setActionDump(true);
=== modified file 'utilities/rtmpget.cpp'
--- a/utilities/rtmpget.cpp 2009-06-07 21:14:22 +0000
+++ b/utilities/rtmpget.cpp 2009-08-05 17:23:14 +0000
@@ -131,8 +131,8 @@
{ 'a', "app", Arg_parser::yes },
{ 'p', "path", Arg_parser::yes },
{ 'f', "filename", Arg_parser::yes },
- { 't', "tcurl", Arg_parser::yes },
- { 's', "swfurl", Arg_parser::yes },
+ { 't', "tcurl", Arg_parser::yes },
+ { 's', "swfurl", Arg_parser::yes },
{ 'u', "url", Arg_parser::yes },
{ 'n', "netdebug", Arg_parser::no }
};
@@ -152,12 +152,14 @@
dbglogfile.setVerbosity(rcfile.verbosityLevel());
}
+#if 0
string app; // the application name
string path; // the path to the file on the server
string tcUrl; // the tcUrl field
string swfUrl; // the swfUrl field
string filename; // the filename to play
-
+#endif
+
// Handle command line arguments
for( int i = 0; i < parser.arguments(); ++i ) {
const int code = parser.code(i);
@@ -216,132 +218,23 @@
return EXIT_FAILURE;
}
- RTMPClient client;
- short port = 0;
- string protocol; // the network protocol, rtmp or http
- string query; // any queries for the host
- string pageUrl; // the pageUrl field
- string hostname; // the hostname of the server
-
- URL url( infiles[0] );
- string portstr;
-
// Trap ^C (SIGINT) so we can kill all the threads
act.sa_handler = cntrlc_handler;
sigaction (SIGINT, &act, NULL);
- protocol = url.protocol();
- hostname = url.hostname();
- log_debug("hostname: %s", hostname);
- portstr = url.port();
- query = url.querystring();
-
- if ( portstr.empty() )
- {
- if ((protocol == "http") || (protocol == "rtmpt")) {
- port = RTMPT_PORT;
- }
- if (protocol == "rtmp") {
- port = RTMP_PORT;
- }
- }
- else
- {
- port = strtol(portstr.c_str(), NULL, 0) & 0xffff;
- }
-
-
- if ( path.empty() )
- {
- path = url.path();
- }
-
- if ( filename.empty() )
- {
- string::size_type end = path.rfind('/');
- if (end != string::npos) {
- filename = path.substr(end + 1);
- }
- }
-
-
- if (tcUrl.empty()) {
- tcUrl = protocol + "://" + hostname;
- if (!portstr.empty()) {
- tcUrl += ":" + portstr;
- }
- if (!query.empty()) {
- tcUrl += "/" + query;
- } else {
- tcUrl += "/" + path;
- }
- }
-
- if (app.empty())
- {
-
- // Get the application name
- // rtmp://localhost/application/resource
- // ^^^^^^^^^^^ <-- appname is this
- //
- app = path;
- if ( ! filename.empty() )
- {
- string::size_type end = app.rfind(filename);
- if (end != string::npos) {
- app = app.substr(0, end);
- }
- }
-
- // drop slashes
- string::size_type end = app.find_first_not_of('/');
- if (end != string::npos) {
- app = app.substr(end);
- }
- end = app.find_last_not_of('/');
- if (end != string::npos) {
- app = app.substr(0, end+1);
- }
-
- if (!query.empty()) {
- app = path;
- app += "?" + query;
- }
- }
-
- if (swfUrl.empty()) {
- swfUrl = "mediaplayer.swf";
- }
- if (pageUrl.empty()) {
- pageUrl = "http://gnashdev.org";
- }
-
- log_debug("URL is %s", url);
- log_debug("Protocol is %s", protocol);
- log_debug("Host is %s", hostname);
- log_debug("Port is %s", port);
- log_debug("Path is %s", path);
- log_debug("Filename is %s", filename);
- log_debug("App is %s", app);
- log_debug("Query is %s", query);
- log_debug("tcUrl is %s", tcUrl);
- log_debug("swfUrl is %s", swfUrl);
- log_debug("pageUrl is %s", pageUrl);
-
+ RTMPClient client;
client.toggleDebug(netdebug);
if (client.createClient(hostname, port) == false) {
log_error("Can't connect to RTMP server %s", hostname);
exit(-1);
}
- if ( ! client.handShakeRequest() )
- {
+ if (! client.handShakeRequest()) {
log_error("RTMP handshake request failed");
exit(EXIT_FAILURE);
}
- if ( ! client.clientFinish() )
- {
+ if (! client.clientFinish()) {
log_error("RTMP handshake completion failed");
exit(EXIT_FAILURE);
}
@@ -351,7 +244,13 @@
// RTMP::rtmp_head_t *rthead = 0;
// int ret = 0;
log_debug("Sending NetConnection Connect message,");
- BufferSharedPtr buf2 = client.encodeConnect(app.c_str(), swfUrl.c_str(),
tcUrl.c_str(), 615, 124, 1, pageUrl.c_str());
+#if 0
+ BufferSharedPtr buf2 = client.encodeConnect(app.c_str(), swfUrl.c_str(),
tcUrl.c_str(), RTMPClient::DEFAULT_AUDIO_SET,
+ RTMPClient::DEFAULT_VIDEO_SET,
+ RTMPClient::SEEK, pageUrl.c_str());
+#else
+ BufferSharedPtr buf2 = client.encodeConnect(infiles[0]);
+#endif
// BufferSharedPtr buf2 =
client.encodeConnect("video/2006/sekt/gate06/tablan_valentin",
"mediaplayer.swf",
"rtmp://velblod.videolectures.net/video/2006/sekt/gate06/tablan_valentin", 615,
124, 1, "http://gnashdev.org");
// BufferSharedPtr buf2 = client.encodeConnect("oflaDemo",
"http://192.168.1.70/software/gnash/tests/ofla_demo.swf",
"rtmp://localhost/oflaDemo/stream", 615, 124, 1,
"http://192.168.1.70/software/gnash/tests/index.html");
//buf2->resize(buf2->size() - 6); // FIXME: encodeConnect returns the
wrong size for the buffer!
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Gnash-commit] /srv/bzr/gnash/trunk r11389: merge from branch.,
Rob Savoye <=