1
// rtmp.cpp: Adobe/Macromedia Real Time Message Protocol handler, for Gnash.
3
// Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
6
// This program is free software; you can redistribute it and/or modify
7
// it under the terms of the GNU General Public License as published by
8
// the Free Software Foundation; either version 3 of the License, or
9
// (at your option) any later version.
11
// This program is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
// GNU General Public License for more details.
16
// You should have received a copy of the GNU General Public License
17
// along with this program; if not, write to the Free Software
18
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
#include "gnashconfig.h"
30
#if ! (defined(_WIN32) || defined(WIN32))
31
# include <netinet/in.h>
34
#include <boost/shared_ptr.hpp>
35
#include <boost/lexical_cast.hpp>
40
#include "rtmp_client.h"
43
// #include "handler.h"
46
#include "GnashSleep.h"
49
typedef boost::shared_ptr<cygnal::Element> ElementSharedPtr;
54
extern const char *ping_str[];
56
// The rcfile is loaded and parsed here:
57
static RcInitFile& rcfile = RcInitFile::getDefaultInstance();
59
// extern map<int, Handler *> handlers;
61
RTMPClient::RTMPClient()
65
// GNASH_REPORT_FUNCTION;
68
RTMPClient::~RTMPClient()
70
// GNASH_REPORT_FUNCTION;
78
// These are used for creating the primary objects
80
// Make the NetConnection object that is used to connect to the
82
boost::shared_ptr<cygnal::Buffer>
83
RTMPClient::encodeConnect()
85
// GNASH_REPORT_FUNCTION;
87
return encodeConnect(_path.c_str());
90
boost::shared_ptr<cygnal::Buffer>
91
RTMPClient::encodeConnect(const char *uri)
93
// GNASH_REPORT_FUNCTION;
95
return encodeConnect(uri, RTMPClient::DEFAULT_AUDIO_SET,
96
RTMPClient::DEFAULT_VIDEO_SET,
100
boost::shared_ptr<cygnal::Buffer>
101
RTMPClient::encodeConnect(const char *uri,
102
double audioCodecs, double videoCodecs,
103
double videoFunction)
105
GNASH_REPORT_FUNCTION;
112
string protocol; // the network protocol, rtmp or http
113
string query; // any queries for the host
114
string app; // the application name
115
string path; // the path to the file on the server
116
string tcUrl; // the tcUrl field
117
string swfUrl; // the swfUrl field
118
string filename; // the filename to play
119
string pageUrl; // the pageUrl field
120
string hostname; // the hostname of the server
123
protocol = url.protocol();
124
hostname = url.hostname();
125
portstr = url.port();
126
query = url.querystring();
128
if (portstr.empty()) {
129
if ((protocol == "http") || (protocol == "rtmpt")) {
132
if (protocol == "rtmp") {
136
port = strtol(portstr.c_str(), NULL, 0) & 0xffff;
142
string::size_type end = path.rfind('/');
143
if (end != string::npos) {
144
filename = path.substr(end + 1);
149
swfUrl = "http://localhost:1935/demos/videoConference.swf";
150
pageUrl = "http://gnashdev.org";
152
log_network("URL is %s", url);
153
log_network("Protocol is %s", protocol);
154
log_network("Host is %s", hostname);
155
log_network("Port is %s", port);
156
log_network("Path is %s", path);
157
log_network("Filename is %s", filename);
158
log_network("App is %s", app);
159
log_network("Query is %s", query);
160
log_network("tcUrl is %s", tcUrl);
161
log_network("swfUrl is %s", swfUrl);
162
log_network("pageUrl is %s", pageUrl);
164
return encodeConnect(app.c_str(), swfUrl.c_str(), tcUrl.c_str(),
165
audioCodecs, videoCodecs, videoFunction,
169
boost::shared_ptr<cygnal::Buffer>
170
RTMPClient::encodeConnect(const char *app, const char *swfUrl, const char *tcUrl,
171
double audioCodecs, double videoCodecs, double videoFunction,
174
GNASH_REPORT_FUNCTION;
178
ElementSharedPtr connect(new cygnal::Element);
179
connect->makeString("connect");
181
ElementSharedPtr connum(new cygnal::Element);
182
// update the counter for the number of connections. This number is used heavily
183
// in RTMP to help keep communications clear when there are multiple streams.
185
connum->makeNumber(_connections);
187
// Make the top level object
188
ElementSharedPtr obj(new cygnal::Element);
191
ElementSharedPtr appnode(new cygnal::Element);
192
appnode->makeString("app", app);
193
obj->addProperty(appnode);
195
const char *version = 0;
196
if (rcfile.getFlashVersionString().size() > 0) {
197
version = rcfile.getFlashVersionString().c_str();
199
version = "LNX 9,0,31,0";
202
ElementSharedPtr flashVer(new cygnal::Element);
203
flashVer->makeString("flashVer", version);
204
obj->addProperty(flashVer);
206
ElementSharedPtr swfUrlnode(new cygnal::Element);
207
// swfUrl->makeString("swfUrl", "http://192.168.1.70/software/gnash/tests/ofla_demo.swf");
208
swfUrlnode->makeString("swfUrl", swfUrl);
209
obj->addProperty(swfUrlnode);
211
// filespec = "rtmp://localhost:5935/oflaDemo";
212
ElementSharedPtr tcUrlnode(new cygnal::Element);
213
tcUrlnode->makeString("tcUrl", tcUrl);
214
obj->addProperty(tcUrlnode);
216
ElementSharedPtr fpad(new cygnal::Element);
217
fpad->makeBoolean("fpad", false);
218
obj->addProperty(fpad);
220
ElementSharedPtr audioCodecsnode(new cygnal::Element);
221
// audioCodecsnode->makeNumber("audioCodecs", 615);
222
audioCodecsnode->makeNumber("audioCodecs", audioCodecs);
223
obj->addProperty(audioCodecsnode);
225
ElementSharedPtr videoCodecsnode(new cygnal::Element);
226
// videoCodecsnode->makeNumber("videoCodecs", 124);
227
videoCodecsnode->makeNumber("videoCodecs", videoCodecs);
228
obj->addProperty(videoCodecsnode);
230
ElementSharedPtr videoFunctionnode(new cygnal::Element);
231
// videoFunctionnode->makeNumber("videoFunction", 0x1);
232
videoFunctionnode->makeNumber("videoFunction", videoFunction);
233
obj->addProperty(videoFunctionnode);
235
ElementSharedPtr pageUrlnode(new cygnal::Element);
236
// pageUrlnode->makeString("pageUrl", "http://x86-ubuntu/software/gnash/tests/");
237
pageUrlnode->makeString("pageUrl", pageUrl);
238
obj->addProperty(pageUrlnode);
241
ElementSharedPtr objencodingnode(new Element);
242
objencodingnode->makeNumber("objectEncoding", 0.0);
243
obj->addProperty(objencodingnode);
245
// size_t total_size = 227;
246
// Buffer *out = encodeHeader(0x3, RTMP::HEADER_12, total_size,
247
// RTMP::INVOKE, RTMP::FROM_CLIENT);
248
// const char *rtmpStr = "03 00 00 04 00 01 1f 14 00 00 00 00";
249
// Buffer *rtmpBuf = hex2mem(rtmpStr);
250
boost::shared_ptr<cygnal::Buffer> conobj = connect->encode();
251
boost::shared_ptr<cygnal::Buffer> numobj = connum->encode();
252
boost::shared_ptr<cygnal::Buffer> encobj = obj->encode();
254
boost::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer(conobj->size() + numobj->size() + encobj->size()));
263
RTMPClient::connectToServer(const std::string &url)
265
GNASH_REPORT_FUNCTION;
269
// If we're currently not connected, build and send the
270
// initial handshake packet.
271
if (connected() == false) {
272
short port = strtol(uri.port().c_str(), NULL, 0) & 0xffff;
273
if (!createClient(uri.hostname(), port)) {
277
// Build the NetConnection Packet, which seems to need
278
// to be on the end of the second block of handshake data.
279
// We build this here so we can get the total encoded
280
// size of the object.
281
boost::shared_ptr<cygnal::Buffer> ncbuf = encodeConnect();
283
// As at this point we don't have an RTMP connection,
284
// we can't use the regular sendMsg(), that handles the RTMP
285
// headers for continuation packets. We know a this point it's
286
// always one by, so we just add it by hand. It doesn't matter
287
// as long as the channel number matches the one used to
288
// create the initial RTMP packet header.
289
boost::scoped_ptr<cygnal::Buffer> newbuf(new cygnal::Buffer(ncbuf->size() + 5));
291
size_t chunk = RTMP_VIDEO_PACKET_SIZE;
293
// The last packet is smaller
294
if ((ncbuf->allocated() - nbytes) < static_cast<size_t>(RTMP_VIDEO_PACKET_SIZE)) {
295
chunk = ncbuf->allocated() - nbytes;
297
newbuf->append(ncbuf->reference() + nbytes, chunk);
299
if (chunk == static_cast<size_t>(RTMP_VIDEO_PACKET_SIZE)) {
300
boost::uint8_t headone = 0xc3;
303
} while (nbytes < ncbuf->allocated());
305
boost::shared_ptr<cygnal::Buffer> head = encodeHeader(0x3,
306
RTMP::HEADER_12, ncbuf->allocated(),
307
RTMP::INVOKE, RTMPMsg::FROM_CLIENT);
309
// Build the first handshake packet, and send it to the
311
boost::shared_ptr<cygnal::Buffer> handshake1 = handShakeRequest();
313
log_error("RTMP handshake request failed");
317
boost::scoped_ptr<cygnal::Buffer> handshake2(new cygnal::Buffer
318
((RTMP_HANDSHAKE_SIZE * 2) + newbuf->allocated()
319
+ RTMP_MAX_HEADER_SIZE));
321
// Finish the handshake process, which has to have the
322
// NetConnection::connect() as part of the buffer, or Red5
323
// refuses to answer.
326
*handshake2 = handshake1;
328
*handshake2 += ncbuf;
329
if (!clientFinish(*handshake2)) {
332
handshake2->append(newbuf->reference(), newbuf->allocated());
334
if (!clientFinish(*handshake2)) {
336
log_error("RTMP handshake completion failed!");
340
// give the server time to process our NetConnection::connect() request
341
boost::shared_ptr<cygnal::Buffer> response;
342
boost::shared_ptr<RTMP::rtmp_head_t> rthead;
343
boost::shared_ptr<RTMP::queues_t> que;
345
RTMPClient::msgque_t msgque = recvResponse();
346
while (msgque.size()) {
347
boost::shared_ptr<RTMPMsg> msg = msgque.front();
349
if (msg->getStatus() == RTMPMsg::NC_CONNECT_SUCCESS) {
350
log_network("Sent NetConnection Connect message sucessfully");
352
if (msg->getStatus() == RTMPMsg::NC_CONNECT_FAILED) {
353
log_error("Couldn't send NetConnection Connect message,");
361
boost::shared_ptr<cygnal::Buffer>
362
RTMPClient::encodeEchoRequest(const std::string &method, double id, cygnal::Element &el)
364
// GNASH_REPORT_FUNCTION;
365
boost::shared_ptr<cygnal::Element> str(new cygnal::Element);
366
str->makeString(method);
367
boost::shared_ptr<cygnal::Buffer> strobj = str->encode();
369
// Encod ethe stream ID
370
boost::shared_ptr<cygnal::Element> num(new cygnal::Element);
372
boost::shared_ptr<cygnal::Buffer> numobj = num->encode();
374
// Set the NULL object element that follows the stream ID
375
boost::shared_ptr<cygnal::Element> null(new cygnal::Element);
377
boost::shared_ptr<cygnal::Buffer> nullobj = null->encode();
379
boost::shared_ptr<cygnal::Buffer> elobj = el.encode();
381
size_t totalsize = strobj->size() + numobj->size() + nullobj->size() + elobj->size();
383
boost::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer(totalsize));
393
// 43 00 1a 21 00 00 19 14 02 00 0c 63 72 65 61 74 C..!.......creat
394
// 65 53 74 72 65 61 6d 00 40 08 00 00 00 00 00 00 eStream.@.......
396
boost::shared_ptr<cygnal::Buffer>
397
RTMPClient::encodeStream(double id)
399
// GNASH_REPORT_FUNCTION;
402
clock_gettime (CLOCK_REALTIME, &now);
404
boost::shared_ptr<cygnal::Element> str(new cygnal::Element);
405
str->makeString("createStream");
406
boost::shared_ptr<cygnal::Buffer> strobj = str->encode();
408
boost::shared_ptr<cygnal::Element> num(new cygnal::Element);
410
boost::shared_ptr<cygnal::Buffer> numobj = num->encode();
412
// Set the NULL object element that follows the stream ID
413
boost::shared_ptr<cygnal::Element> null(new cygnal::Element);
415
boost::shared_ptr<cygnal::Buffer> nullobj = null->encode();
417
size_t totalsize = strobj->size() + numobj->size() + nullobj->size();
419
boost::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer(totalsize));
428
// 127.0.0.1:38167 -> 127.0.0.1:1935 [AP]
429
// 08 00 1b 1b 00 00 2a 14 01 00 00 00 02 00 04 70 ......*........p
430
// 6c 61 79 00 00 00 00 00 00 00 00 00 05 02 00 16 lay.............
431
// 6f 6e 32 5f 66 6c 61 73 68 38 5f 77 5f 61 75 64 on2_flash8_w_aud
432
// 69 6f 2e 66 6c 76 c2 00 03 00 00 00 01 00 00 27 io.flv.........'
434
boost::shared_ptr<cygnal::Buffer>
435
RTMPClient::encodeStreamOp(double id, rtmp_op_e op, bool flag)
437
// GNASH_REPORT_FUNCTION;
438
return encodeStreamOp(id, op, flag, "", 0);
441
boost::shared_ptr<cygnal::Buffer>
442
RTMPClient::encodeStreamOp(double id, rtmp_op_e op, bool flag, double pos)
444
// GNASH_REPORT_FUNCTION;
445
return encodeStreamOp(id, op, flag, "", pos);
448
boost::shared_ptr<cygnal::Buffer>
449
RTMPClient::encodeStreamOp(double id, rtmp_op_e op, bool flag, const std::string &name)
451
// GNASH_REPORT_FUNCTION;
452
return encodeStreamOp(id, op, flag, name, 0);
455
// A seek packet is the operation name "seek", followed by the
456
// stream ID, then a NULL object, followed by the location to seek to.
458
// A pause packet is the operation name "pause", followed by the stream ID,
459
// then a NULL object, a boolean (always true from what I can tell), and then
460
// a location, which appears to always be 0.
461
boost::shared_ptr<cygnal::Buffer>
462
RTMPClient::encodeStreamOp(double id, rtmp_op_e op, bool flag, const std::string &name, double pos)
464
// GNASH_REPORT_FUNCTION;
466
// Set the operations command name
469
case STREAM_PLAY: // play the existing stream
470
str.makeString("play");
472
case STREAM_PAUSE: // pause the existing stream
473
str.makeString("pause");
475
case STREAM_PUBLISH: // publish the existing stream
476
str.makeString("publish");
478
case STREAM_STOP: // stop the existing stream
479
str.makeString("stop");
481
case STREAM_SEEK: // seek in the existing stream
482
str.makeString("seek");
485
boost::shared_ptr<cygnal::Buffer> foo;
489
boost::shared_ptr<cygnal::Buffer> strobj = str.encode();
491
// Set the stream ID, which follows the command
492
cygnal::Element strid;
493
strid.makeNumber(id);
494
boost::shared_ptr<cygnal::Buffer> stridobj = strid.encode();
496
// Set the NULL object element that follows the stream ID
497
cygnal::Element null;
499
boost::shared_ptr<cygnal::Buffer> nullobj = null.encode();
501
// Set the BOOLEAN object element that is the last field in the packet
502
// (SEEK and PLAY don't use the boolean flag)
503
boost::shared_ptr<cygnal::Buffer> boolobj;
504
if ((op != STREAM_SEEK) && (op != STREAM_PLAY)) {
505
cygnal::Element boolean;
506
boolean.makeBoolean(flag);
507
boolobj = boolean.encode();
510
// The seek command also may have an optional location to seek to
511
boost::shared_ptr<cygnal::Buffer> posobj;
512
if ((op == STREAM_PAUSE) || (op == STREAM_SEEK)) {
513
cygnal::Element seek;
514
seek.makeNumber(pos);
515
posobj = seek.encode();
518
// The play command has an optional field, which is the name of the file
519
// used for the stream. A Play command without this name set play an
520
// existing stream that is already open.
521
boost::shared_ptr<cygnal::Buffer> fileobj;
523
cygnal::Element filespec;
524
filespec.makeString(name);
525
fileobj = filespec.encode();
528
// Calculate the packet size, rather than use the default as we want to
529
// to be concious of the memory usage. The command name and the optional
530
// file name are the only two dynamically sized fields.
531
size_t pktsize = strobj->size() + stridobj->size() + nullobj->size();
532
if ( boolobj ) pktsize += boolobj->size();
533
if ( fileobj ) pktsize += fileobj->size();
534
if ( posobj ) pktsize += posobj->size();
536
boost::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer(pktsize));
540
if ( boolobj ) *buf += boolobj;
541
if ( fileobj ) *buf += fileobj;
542
if ( posobj ) *buf += posobj;
547
// A request for a handshake is initiated by sending a byte with a
548
// value of 0x3, followed by a message body of unknown format.
549
boost::shared_ptr<cygnal::Buffer>
550
RTMPClient::handShakeRequest()
552
GNASH_REPORT_FUNCTION;
553
boost::uint32_t zero = 0;
555
// Make a buffer to hold the handshake data.
556
boost::shared_ptr<cygnal::Buffer> handshake(new cygnal::Buffer(RTMP_HANDSHAKE_SIZE+1));
561
// All RTMP connections start with the RTMP version number
562
// which should always be 0x3.
563
*handshake = RTMP_VERSION;
565
*handshake += RTMP::getTime();
567
// This next field in the header for the RTMP handshake should be zeros
570
// The handshake contains random data after the initial header
571
for (int i=0; i<RTMP_RANDOM_SIZE; i++) {
572
boost::uint8_t pad = i^256;
576
int ret = writeNet(*handshake);
584
// The client finishes the handshake process by sending the second
585
// data block we get from the server as the response
587
boost::shared_ptr<cygnal::Buffer>
588
RTMPClient::clientFinish()
590
// GNASH_REPORT_FUNCTION;
593
return clientFinish(data);
596
boost::shared_ptr<cygnal::Buffer>
597
RTMPClient::clientFinish(cygnal::Buffer &data)
599
GNASH_REPORT_FUNCTION;
605
// Create the initial buffer to hold the response, and keep reading data
606
// until it is populated
607
// The handhake for this phase is twice the size of the initial handshake
608
// we sent previously, plus one byte for the RTMP version header.
609
int max_size = (RTMP_HANDSHAKE_SIZE*2) + 1;
610
boost::shared_ptr<cygnal::Buffer> handshake1(new cygnal::Buffer(
611
max_size + data.allocated()));
613
ret = readNet(handshake1->end(), max_size - offset);
615
handshake1->setSeekPointer(handshake1->reference() + offset);
616
if ((offset >= max_size) || (ret >= max_size)) {
617
handshake1->setSeekPointer(handshake1->reference() + max_size);
618
// log_network("Read entire packet of %d bytes", ret);
622
log_error (_("Couldn't read data block in handshake!"));
626
// if retries equals zero, then we're done trying
634
if (handshake1->allocated() == boost::lexical_cast<size_t>(max_size)) {
635
log_network (_("Read data block in handshake, got %d bytes."),
636
handshake1->allocated());
638
log_error("Couldn't read data block in handshake, read %d bytes!",
639
handshake1->allocated());
642
_handshake_header.uptime = ntohl(*reinterpret_cast<boost::uint32_t *>
643
(handshake1->reference() + 1));
645
log_network("RTMP Handshake header: Uptime: %u", _handshake_header.uptime);
648
if (memcmp(handshake2->reference() + RTMP_HANDSHAKE_SIZE + 8,
649
_handshake->reference() + 8, RTMP_RANDOM_SIZE-8) == 0) {
650
log_network("Handshake matched");
652
log_network("Handshake didn't match");
657
// Make a new buffer big enough to hold the handshake, data, and header byte
658
boost::shared_ptr<cygnal::Buffer> handshake2(new cygnal::Buffer(
659
RTMP_HANDSHAKE_SIZE + data.allocated()));
661
// Copy the timestamp from the message we just received.
662
handshake2->copy(handshake1->reference()+1, sizeof(boost::uint32_t));
665
// The next timestamp is the one we just received bumped up a tiny bit.
666
// I have no clue if this is correct, but fom hex dumps the previous
667
// timestamp should be the baseline, and this is just that time plus
668
// whatever it took to get the message. The 7 is a bit randomly chosen.
669
boost::uint32_t tt = htonl(_handshake_header.uptime + 7);
671
// Get the uptime for the header
672
// yes, we loose precision here but it's only a 4 byte field
675
boost::uint32_t tt = t;
679
// Add the handshake data
680
boost::uint8_t *start = handshake1->reference() + RTMP_HANDSHAKE_SIZE
682
handshake2->append(start, RTMP_RANDOM_SIZE);
683
// Add the NetConnection::connect() packet
686
// Write the second chunk to the server
687
log_network("About to write %d bytes, data is: %d bytes.",
688
handshake2->allocated(),
690
log_network("Client response header for handshake 2: %s", hexify(handshake2->reference(), 12, false));
691
log_network("Data in response for handshake 2: %s", hexify(handshake1->reference() + RTMP_HANDSHAKE_SIZE + 1, 12, false));
693
ret = writeNet(handshake2->reference()+RTMP_HANDSHAKE_SIZE,
694
RTMP_HANDSHAKE_SIZE + data.allocated() + 1);
696
ret = writeNet(*handshake2);
699
log_error("Couldn't write the second handshake packet!");
706
// Since the handshake completed sucessfully, we're connected.
712
// Get and process an RTMP response. After reading all the data, then we have
713
// split it up on the chunksize boudaries, and into the respective queues
716
RTMPClient::recvResponse()
718
GNASH_REPORT_FUNCTION;
720
RTMPClient::msgque_t msgque;
722
// Read the responses back from the server. This is usually a series of system
723
// messages on channel 2, and the response message on channel 3 from our request.
724
boost::shared_ptr<cygnal::Buffer> response = recvMsg();
726
log_error("Got no response from the RTMP server");
730
// when doing remoting calls I don't see this problem with an empty packet from Red5,
731
// but when I do streaming, it's always there, so we need to remove it.
732
boost::uint8_t *pktstart = response->reference();
733
if (*pktstart == 0xff) {
734
log_network("Got empty packet in buffer.");
738
// The response packet contains multiple messages for multiple channels, so we
739
// we have to split the Buffer into seperate messages on a chunksize boundary.
740
boost::shared_ptr<RTMP::rtmp_head_t> rthead;
741
boost::shared_ptr<RTMP::queues_t> que = split(pktstart, response->allocated()-1);
743
// If we got no responses, something obviously went wrong.
745
log_error("No response from INVOKE of NetConnection connect");
749
// There is a queue of queues used to hold all the messages. The first queue
750
// is indexed by the channel number, the second queue is all the messages that
751
// have arrived for that channel.
752
while (que->size()) { // see if there are any messages at all
753
log_network("%s: There are %d channel queues in the RTMP input queue, %d messages in front queue",
754
__PRETTY_FUNCTION__, que->size(), que->front()->size());
755
// Get the CQue for the first channel
756
CQue *channel_q = que->front();
757
que->pop_front(); // remove this Cque from the top level que
759
while (channel_q->size()) {
760
// Get the first message in the channel queue
761
boost::shared_ptr<cygnal::Buffer> ptr = channel_q->pop();
763
if (ptr) { // If there is legit data
764
rthead = decodeHeader(ptr->reference());
766
log_error("Couldn't decode RTMP message header");
769
switch (rthead->type) {
771
log_error("RTMP packet can't be of none type!");
773
case RTMP::CHUNK_SIZE:
774
log_unimpl("Server message data packet");
777
log_unimpl("Abort packet");
779
case RTMP::BYTES_READ:
780
log_unimpl("Bytes Read data packet");
784
boost::shared_ptr<RTMP::rtmp_ping_t> ping = decodePing(ptr->reference() + rthead->head_size);
785
log_network("Got a Ping type %s", ping_str[ping->type]);
788
case RTMP::WINDOW_SIZE:
789
log_unimpl("Set Window Size message data packet");
791
case RTMP::SET_BANDWITH:
792
log_unimpl("Set Bandwidthmessage data packet");
795
log_unimpl("Route from other server packet");
797
case RTMP::AUDIO_DATA:
799
boost::shared_ptr<RTMPMsg> msg = decodeMsgBody(ptr->reference() + rthead->head_size, rthead->bodysize);
801
msgque.push_back(msg);
805
case RTMP::VIDEO_DATA:
807
boost::shared_ptr<RTMPMsg> msg = decodeMsgBody(ptr->reference() + rthead->head_size, rthead->bodysize);
809
msgque.push_back(msg);
813
case RTMP::SHARED_OBJ:
814
log_unimpl("AMF0 Shared Object data packet message");
816
case RTMP::AMF3_NOTIFY:
817
log_unimpl("AMF3 Notify data packet message");
819
case RTMP::AMF3_SHARED_OBJ:
820
log_unimpl("AMF3 Shared Object data packet message");
822
case RTMP::AMF3_INVOKE:
823
log_unimpl("AMF0 Invoke packet message");
826
log_unimpl("AMF0 Notify data packet message");
830
boost::shared_ptr<RTMPMsg> msg = decodeMsgBody(ptr->reference() + rthead->head_size, rthead->bodysize);
832
msgque.push_back(msg);
837
log_unimpl("Flv data packet message");
840
log_error("Couldn't decode RTMP message Body");
852
// RTMPClient::packetRequest()
854
// GNASH_REPORT_FUNCTION;
858
} // end of gnash namespace
862
// indent-tabs-mode: t