~ubuntu-branches/ubuntu/precise/gnash/precise-proposed

« back to all changes in this revision

Viewing changes to .pc/fixtypos.patch/cygnal/libnet/rtmp_client.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Angel Abad, Angel Abad, Micah Gersten
  • Date: 2010-11-28 22:18:48 UTC
  • mfrom: (3.1.10 sid)
  • Revision ID: james.westby@ubuntu.com-20101128221848-apjipwy78m13612a
Tags: 0.8.8-6ubuntu1
[ Angel Abad <angelabad@ubuntu.com> ]
* Merge from debian unstable (LP: #682386). Remaining changes:
  - Add Ubuntu flash alternatives in postinst and prerm
    + update debian/browser-plugin-gnash.postinst
    + update debian/browser-plugin-gnash.prerm

[ Micah Gersten <micahg@ubuntu.com> ]
* Only install the flash alternative in /usr/lib/mozilla/plugins as the other
  locations are deprecated
  - update debian/browser-plugin-gnash.postinst

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// rtmp.cpp:  Adobe/Macromedia Real Time Message Protocol handler, for Gnash.
2
 
// 
3
 
//   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
4
 
//   Foundation, Inc
5
 
// 
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.
10
 
// 
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.
15
 
//
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
19
 
//
20
 
 
21
 
#ifdef HAVE_CONFIG_H
22
 
#include "gnashconfig.h"
23
 
#endif
24
 
 
25
 
#include <ctime>
26
 
#include <iostream>
27
 
#include <string>
28
 
#include <map>
29
 
 
30
 
#if ! (defined(_WIN32) || defined(WIN32))
31
 
#       include <netinet/in.h>
32
 
#endif
33
 
 
34
 
#include <boost/shared_ptr.hpp>
35
 
#include <boost/lexical_cast.hpp>
36
 
#include "log.h"
37
 
#include "rc.h"
38
 
#include "amf.h"
39
 
#include "rtmp.h"
40
 
#include "rtmp_client.h"
41
 
#include "network.h"
42
 
#include "element.h"
43
 
// #include "handler.h"
44
 
#include "utility.h"
45
 
#include "buffer.h"
46
 
#include "GnashSleep.h"
47
 
#include "URL.h"
48
 
 
49
 
typedef boost::shared_ptr<cygnal::Element> ElementSharedPtr;
50
 
 
51
 
namespace gnash
52
 
{
53
 
 
54
 
extern const char *ping_str[];
55
 
 
56
 
// The rcfile is loaded and parsed here:
57
 
static RcInitFile& rcfile = RcInitFile::getDefaultInstance();
58
 
 
59
 
// extern map<int, Handler *> handlers;
60
 
 
61
 
RTMPClient::RTMPClient()
62
 
    : _connected(false),
63
 
      _connections(0)
64
 
{
65
 
//    GNASH_REPORT_FUNCTION;
66
 
}
67
 
 
68
 
RTMPClient::~RTMPClient()
69
 
{
70
 
//    GNASH_REPORT_FUNCTION;
71
 
    _connected = false;
72
 
 
73
 
    _properties.clear();
74
 
//    delete _body;
75
 
}
76
 
 
77
 
 
78
 
// These are used for creating the primary objects
79
 
 
80
 
// Make the NetConnection object that is used to connect to the
81
 
// server.
82
 
boost::shared_ptr<cygnal::Buffer> 
83
 
RTMPClient::encodeConnect()
84
 
{
85
 
//     GNASH_REPORT_FUNCTION;
86
 
    
87
 
    return encodeConnect(_path.c_str());
88
 
}
89
 
 
90
 
boost::shared_ptr<cygnal::Buffer> 
91
 
RTMPClient::encodeConnect(const char *uri)
92
 
{
93
 
//     GNASH_REPORT_FUNCTION;
94
 
    
95
 
    return encodeConnect(uri, RTMPClient::DEFAULT_AUDIO_SET,
96
 
                         RTMPClient::DEFAULT_VIDEO_SET,
97
 
                         RTMPClient::SEEK);
98
 
}
99
 
 
100
 
boost::shared_ptr<cygnal::Buffer> 
101
 
RTMPClient::encodeConnect(const char *uri,
102
 
                          double audioCodecs, double videoCodecs,
103
 
                          double videoFunction)
104
 
{
105
 
    GNASH_REPORT_FUNCTION;
106
 
    using std::string;
107
 
    
108
 
    URL url(uri);
109
 
    string portstr;
110
 
 
111
 
    short port = 0;
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
121
 
 
122
 
 
123
 
    protocol = url.protocol();
124
 
    hostname = url.hostname();
125
 
    portstr = url.port();
126
 
    query = url.querystring();
127
 
 
128
 
    if (portstr.empty()) {
129
 
        if ((protocol == "http") || (protocol == "rtmpt")) {
130
 
            port = RTMPT_PORT;
131
 
        }
132
 
        if (protocol == "rtmp") {
133
 
            port = RTMP_PORT;
134
 
        }
135
 
    } else {
136
 
        port = strtol(portstr.c_str(), NULL, 0) & 0xffff;
137
 
    }
138
 
 
139
 
 
140
 
    path = url.path();
141
 
 
142
 
    string::size_type end = path.rfind('/');
143
 
    if (end != string::npos) {
144
 
        filename = path.substr(end + 1);
145
 
    }  
146
 
    
147
 
    tcUrl = uri;
148
 
    app = filename;    
149
 
    swfUrl = "http://localhost:1935/demos/videoConference.swf";
150
 
    pageUrl = "http://gnashdev.org";
151
 
    
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);
163
 
 
164
 
    return encodeConnect(app.c_str(), swfUrl.c_str(), tcUrl.c_str(),
165
 
                         audioCodecs, videoCodecs, videoFunction,
166
 
                         pageUrl.c_str());
167
 
}
168
 
 
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,
172
 
                          const char *pageUrl)
173
 
{
174
 
    GNASH_REPORT_FUNCTION;
175
 
    
176
 
    cygnal::AMF amf_obj;
177
 
 
178
 
    ElementSharedPtr connect(new cygnal::Element);
179
 
    connect->makeString("connect");
180
 
 
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.
184
 
    _connections++;
185
 
    connum->makeNumber(_connections);
186
 
    
187
 
    // Make the top level object
188
 
    ElementSharedPtr obj(new cygnal::Element);
189
 
    obj->makeObject();
190
 
    
191
 
    ElementSharedPtr appnode(new cygnal::Element);
192
 
    appnode->makeString("app", app);
193
 
    obj->addProperty(appnode);
194
 
 
195
 
    const char *version = 0;
196
 
    if (rcfile.getFlashVersionString().size() > 0) {
197
 
        version = rcfile.getFlashVersionString().c_str();
198
 
    } else {
199
 
        version = "LNX 9,0,31,0";
200
 
    }  
201
 
 
202
 
    ElementSharedPtr flashVer(new cygnal::Element);
203
 
    flashVer->makeString("flashVer", version);
204
 
    obj->addProperty(flashVer);
205
 
    
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);
210
 
 
211
 
//    filespec = "rtmp://localhost:5935/oflaDemo";
212
 
    ElementSharedPtr tcUrlnode(new cygnal::Element);
213
 
    tcUrlnode->makeString("tcUrl", tcUrl);
214
 
    obj->addProperty(tcUrlnode);
215
 
 
216
 
    ElementSharedPtr fpad(new cygnal::Element);
217
 
    fpad->makeBoolean("fpad", false);
218
 
    obj->addProperty(fpad);
219
 
 
220
 
    ElementSharedPtr audioCodecsnode(new cygnal::Element);
221
 
//    audioCodecsnode->makeNumber("audioCodecs", 615);
222
 
    audioCodecsnode->makeNumber("audioCodecs", audioCodecs);
223
 
    obj->addProperty(audioCodecsnode);
224
 
    
225
 
    ElementSharedPtr videoCodecsnode(new cygnal::Element);
226
 
//    videoCodecsnode->makeNumber("videoCodecs", 124);
227
 
    videoCodecsnode->makeNumber("videoCodecs", videoCodecs);
228
 
    obj->addProperty(videoCodecsnode);
229
 
 
230
 
    ElementSharedPtr videoFunctionnode(new cygnal::Element);
231
 
//    videoFunctionnode->makeNumber("videoFunction", 0x1);
232
 
    videoFunctionnode->makeNumber("videoFunction", videoFunction);
233
 
    obj->addProperty(videoFunctionnode);
234
 
 
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);
239
 
 
240
 
#if 0
241
 
    ElementSharedPtr objencodingnode(new Element);
242
 
    objencodingnode->makeNumber("objectEncoding", 0.0);
243
 
    obj->addProperty(objencodingnode);
244
 
#endif
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();
253
 
 
254
 
    boost::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer(conobj->size() + numobj->size() + encobj->size()));
255
 
    *buf += conobj;
256
 
    *buf += numobj;
257
 
    *buf += encobj;
258
 
                   
259
 
    return buf;
260
 
}
261
 
    
262
 
bool
263
 
RTMPClient::connectToServer(const std::string &url)
264
 
{
265
 
    GNASH_REPORT_FUNCTION;
266
 
 
267
 
    URL uri(url);
268
 
 
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)) {
274
 
            return false;
275
 
        }
276
 
 
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();
282
 
 
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));
290
 
        size_t nbytes = 0;
291
 
        size_t chunk = RTMP_VIDEO_PACKET_SIZE;
292
 
        do {
293
 
            // The last packet is smaller
294
 
            if ((ncbuf->allocated() - nbytes) < static_cast<size_t>(RTMP_VIDEO_PACKET_SIZE)) {
295
 
                chunk = ncbuf->allocated() - nbytes;
296
 
            }
297
 
            newbuf->append(ncbuf->reference() + nbytes, chunk);
298
 
            nbytes  += chunk;
299
 
            if (chunk == static_cast<size_t>(RTMP_VIDEO_PACKET_SIZE)) {
300
 
                boost::uint8_t headone = 0xc3;
301
 
                *newbuf += headone;
302
 
            }
303
 
        } while (nbytes < ncbuf->allocated());
304
 
 
305
 
        boost::shared_ptr<cygnal::Buffer> head = encodeHeader(0x3,
306
 
                            RTMP::HEADER_12, ncbuf->allocated(),
307
 
                            RTMP::INVOKE, RTMPMsg::FROM_CLIENT);
308
 
 
309
 
        // Build the first handshake packet, and send it to the
310
 
        // server.
311
 
        boost::shared_ptr<cygnal::Buffer> handshake1 = handShakeRequest();
312
 
        if (!handshake1) {
313
 
            log_error("RTMP handshake request failed");
314
 
            return false;
315
 
        }
316
 
        
317
 
        boost::scoped_ptr<cygnal::Buffer> handshake2(new cygnal::Buffer
318
 
                  ((RTMP_HANDSHAKE_SIZE * 2) + newbuf->allocated()
319
 
                   + RTMP_MAX_HEADER_SIZE));
320
 
 
321
 
        // Finish the handshake process, which has to have the
322
 
        // NetConnection::connect() as part of the buffer, or Red5
323
 
        // refuses to answer.
324
 
        setTimeout(20);
325
 
#if 0
326
 
        *handshake2 = handshake1;
327
 
        *handshake2 += head;
328
 
        *handshake2 += ncbuf;
329
 
        if (!clientFinish(*handshake2)) {
330
 
#else
331
 
        *handshake2 = head;
332
 
        handshake2->append(newbuf->reference(), newbuf->allocated());
333
 
        handshake2->dump();
334
 
        if (!clientFinish(*handshake2)) {
335
 
#endif
336
 
            log_error("RTMP handshake completion failed!");
337
 
//          return (false);
338
 
        }
339
 
        
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;
344
 
        
345
 
        RTMPClient::msgque_t msgque = recvResponse();
346
 
        while (msgque.size()) {
347
 
            boost::shared_ptr<RTMPMsg> msg = msgque.front();
348
 
            msgque.pop_front();
349
 
            if (msg->getStatus() ==  RTMPMsg::NC_CONNECT_SUCCESS) {
350
 
                log_network("Sent NetConnection Connect message sucessfully");
351
 
            }               
352
 
            if (msg->getStatus() ==  RTMPMsg::NC_CONNECT_FAILED) {
353
 
                log_error("Couldn't send NetConnection Connect message,");
354
 
            }
355
 
        }
356
 
    }
357
 
 
358
 
    return true;
359
 
}
360
 
    
361
 
boost::shared_ptr<cygnal::Buffer>
362
 
RTMPClient::encodeEchoRequest(const std::string &method, double id, cygnal::Element &el)
363
 
{
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();
368
 
 
369
 
    // Encod ethe stream ID
370
 
    boost::shared_ptr<cygnal::Element>  num(new cygnal::Element);
371
 
    num->makeNumber(id);
372
 
    boost::shared_ptr<cygnal::Buffer> numobj = num->encode();
373
 
 
374
 
    // Set the NULL object element that follows the stream ID
375
 
    boost::shared_ptr<cygnal::Element> null(new cygnal::Element);
376
 
    null->makeNull();
377
 
    boost::shared_ptr<cygnal::Buffer> nullobj = null->encode();
378
 
 
379
 
    boost::shared_ptr<cygnal::Buffer> elobj = el.encode();
380
 
 
381
 
    size_t totalsize = strobj->size() + numobj->size() + nullobj->size() + elobj->size();
382
 
 
383
 
    boost::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer(totalsize));
384
 
    
385
 
    *buf += strobj;
386
 
    *buf += numobj;
387
 
    *buf += nullobj;
388
 
    *buf += elobj;
389
 
 
390
 
    return buf;
391
 
}
392
 
 
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.@.......
395
 
// 05                                                    .               
396
 
boost::shared_ptr<cygnal::Buffer> 
397
 
RTMPClient::encodeStream(double id)
398
 
{
399
 
//    GNASH_REPORT_FUNCTION;
400
 
    
401
 
    struct timespec now;
402
 
    clock_gettime (CLOCK_REALTIME, &now);
403
 
 
404
 
    boost::shared_ptr<cygnal::Element> str(new cygnal::Element);
405
 
    str->makeString("createStream");
406
 
    boost::shared_ptr<cygnal::Buffer> strobj = str->encode();
407
 
  
408
 
    boost::shared_ptr<cygnal::Element>  num(new cygnal::Element);
409
 
    num->makeNumber(id);
410
 
    boost::shared_ptr<cygnal::Buffer> numobj = num->encode();
411
 
 
412
 
    // Set the NULL object element that follows the stream ID
413
 
    boost::shared_ptr<cygnal::Element> null(new cygnal::Element);
414
 
    null->makeNull();
415
 
    boost::shared_ptr<cygnal::Buffer> nullobj = null->encode();    
416
 
 
417
 
    size_t totalsize = strobj->size() + numobj->size() + nullobj->size();
418
 
 
419
 
    boost::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer(totalsize));
420
 
 
421
 
    *buf += strobj;
422
 
    *buf += numobj;
423
 
    *buf += nullobj;
424
 
 
425
 
    return buf;
426
 
}
427
 
 
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.........'
433
 
// 10
434
 
boost::shared_ptr<cygnal::Buffer> 
435
 
RTMPClient::encodeStreamOp(double id, rtmp_op_e op, bool flag)
436
 
{
437
 
//    GNASH_REPORT_FUNCTION;
438
 
    return encodeStreamOp(id, op, flag, "", 0);
439
 
}    
440
 
 
441
 
boost::shared_ptr<cygnal::Buffer> 
442
 
RTMPClient::encodeStreamOp(double id, rtmp_op_e op, bool flag, double pos)
443
 
{
444
 
//    GNASH_REPORT_FUNCTION;
445
 
    return encodeStreamOp(id, op, flag, "", pos);
446
 
}    
447
 
 
448
 
boost::shared_ptr<cygnal::Buffer> 
449
 
RTMPClient::encodeStreamOp(double id, rtmp_op_e op, bool flag, const std::string &name)
450
 
{
451
 
//    GNASH_REPORT_FUNCTION;
452
 
    return encodeStreamOp(id, op, flag, name, 0);
453
 
}
454
 
 
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.
457
 
//
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)
463
 
{
464
 
//    GNASH_REPORT_FUNCTION;
465
 
 
466
 
    // Set the operations command name
467
 
    cygnal::Element str;
468
 
    switch (op) {
469
 
      case STREAM_PLAY:         // play the existing stream
470
 
          str.makeString("play");
471
 
          break;
472
 
      case STREAM_PAUSE:        // pause the existing stream
473
 
          str.makeString("pause");
474
 
          break;
475
 
      case STREAM_PUBLISH:      // publish the existing stream
476
 
          str.makeString("publish");
477
 
          break;
478
 
      case STREAM_STOP:         // stop the existing stream
479
 
          str.makeString("stop");
480
 
          break;
481
 
      case STREAM_SEEK:         // seek in the existing stream
482
 
          str.makeString("seek");
483
 
          break;
484
 
      default:
485
 
          boost::shared_ptr<cygnal::Buffer> foo;
486
 
          return foo;
487
 
    };
488
 
 
489
 
    boost::shared_ptr<cygnal::Buffer> strobj = str.encode();
490
 
 
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();
495
 
 
496
 
    // Set the NULL object element that follows the stream ID
497
 
    cygnal::Element null;
498
 
    null.makeNull();
499
 
    boost::shared_ptr<cygnal::Buffer> nullobj = null.encode();    
500
 
 
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();    
508
 
    }
509
 
 
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();
516
 
    }
517
 
 
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; 
522
 
    if (!name.empty()) {
523
 
        cygnal::Element filespec;
524
 
        filespec.makeString(name);
525
 
        fileobj = filespec.encode();
526
 
    }
527
 
 
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();
535
 
 
536
 
    boost::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer(pktsize));    
537
 
    *buf += strobj;
538
 
    *buf += stridobj;
539
 
    *buf += nullobj;
540
 
    if ( boolobj ) *buf += boolobj;
541
 
    if ( fileobj ) *buf += fileobj;
542
 
    if ( posobj ) *buf += posobj;
543
 
 
544
 
    return buf;
545
 
}
546
 
 
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()
551
 
{
552
 
    GNASH_REPORT_FUNCTION;
553
 
    boost::uint32_t zero = 0;
554
 
 
555
 
    // Make a buffer to hold the handshake data.
556
 
    boost::shared_ptr<cygnal::Buffer> handshake(new cygnal::Buffer(RTMP_HANDSHAKE_SIZE+1));
557
 
    if (!handshake) {
558
 
        return handshake;
559
 
    }
560
 
 
561
 
    // All RTMP connections start with the RTMP version number
562
 
    // which should always be 0x3.
563
 
    *handshake = RTMP_VERSION;
564
 
 
565
 
    *handshake += RTMP::getTime();
566
 
 
567
 
    // This next field in the header for the RTMP handshake should be zeros
568
 
    *handshake += zero;
569
 
 
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;
573
 
        *handshake += pad;
574
 
    }
575
 
    
576
 
    int ret = writeNet(*handshake);
577
 
    if (ret <= 0) {
578
 
        handshake.reset();
579
 
    }
580
 
                                             
581
 
    return handshake;
582
 
}
583
 
 
584
 
// The client finishes the handshake process by sending the second
585
 
// data block we get from the server as the response
586
 
 
587
 
boost::shared_ptr<cygnal::Buffer>
588
 
RTMPClient::clientFinish()
589
 
{
590
 
//     GNASH_REPORT_FUNCTION;
591
 
 
592
 
    cygnal::Buffer data;
593
 
    return clientFinish(data);
594
 
}
595
 
 
596
 
boost::shared_ptr<cygnal::Buffer>
597
 
RTMPClient::clientFinish(cygnal::Buffer &data)
598
 
{
599
 
    GNASH_REPORT_FUNCTION;
600
 
    bool done = false;
601
 
    int ret = 0;
602
 
    int retries = 5;
603
 
    int offset = 0;
604
 
    
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()));
612
 
    do {
613
 
        ret = readNet(handshake1->end(), max_size - offset);
614
 
        offset += ret;
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);
619
 
            done = true;
620
 
        }
621
 
        if (ret < 0) {
622
 
            log_error (_("Couldn't read data block in handshake!"));
623
 
            handshake1.reset();
624
 
            return handshake1;
625
 
        }
626
 
        // if retries equals zero, then we're done trying
627
 
        if (retries == 0) {
628
 
            done = true;
629
 
        } else {
630
 
            --retries;
631
 
        }
632
 
    } while (!done);
633
 
 
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());
637
 
    } else {
638
 
        log_error("Couldn't read data block in handshake, read %d bytes!",
639
 
                  handshake1->allocated());
640
 
    }    
641
 
 
642
 
    _handshake_header.uptime = ntohl(*reinterpret_cast<boost::uint32_t *>
643
 
                                     (handshake1->reference() + 1));
644
 
 
645
 
    log_network("RTMP Handshake header: Uptime: %u", _handshake_header.uptime);
646
 
 
647
 
#if 0
648
 
    if (memcmp(handshake2->reference() + RTMP_HANDSHAKE_SIZE + 8,
649
 
               _handshake->reference() + 8, RTMP_RANDOM_SIZE-8) == 0) {
650
 
        log_network("Handshake matched");
651
 
    } else {
652
 
        log_network("Handshake didn't match");
653
 
//      return false;
654
 
    }
655
 
#endif
656
 
 
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()));
660
 
    
661
 
    // Copy the timestamp from the message we just received.
662
 
    handshake2->copy(handshake1->reference()+1, sizeof(boost::uint32_t));
663
 
 
664
 
#if 1
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);
670
 
#else
671
 
    // Get the uptime for the header
672
 
    // yes, we loose precision here but it's only a 4 byte field
673
 
    time_t t;
674
 
    time(&t);
675
 
    boost::uint32_t tt = t;
676
 
#endif
677
 
    *handshake2 += tt;
678
 
 
679
 
    // Add the handshake data
680
 
    boost::uint8_t *start = handshake1->reference() + RTMP_HANDSHAKE_SIZE
681
 
        + 1 + 8;
682
 
    handshake2->append(start, RTMP_RANDOM_SIZE);
683
 
    // Add the NetConnection::connect() packet
684
 
    *handshake2 += data;
685
 
 
686
 
    // Write the second chunk to the server
687
 
    log_network("About to write %d bytes, data is: %d bytes.",
688
 
              handshake2->allocated(),
689
 
              data.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));
692
 
#if 0
693
 
    ret = writeNet(handshake2->reference()+RTMP_HANDSHAKE_SIZE,
694
 
                   RTMP_HANDSHAKE_SIZE + data.allocated() + 1);
695
 
#else
696
 
    ret = writeNet(*handshake2);
697
 
#endif
698
 
    if ( ret <= 0 ) {
699
 
        log_error("Couldn't write the second handshake packet!");
700
 
        handshake1.reset();
701
 
        return handshake1;
702
 
    } else {
703
 
        _connected = false;
704
 
    }
705
 
 
706
 
    // Since the handshake completed sucessfully, we're connected.
707
 
    _connected = true;
708
 
 
709
 
    return handshake1;
710
 
}
711
 
 
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
714
 
// for each channel.
715
 
RTMPClient::msgque_t
716
 
RTMPClient::recvResponse()
717
 
{
718
 
    GNASH_REPORT_FUNCTION;
719
 
 
720
 
    RTMPClient::msgque_t msgque;
721
 
    
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();
725
 
    if (!response) {
726
 
        log_error("Got no response from the RTMP server");
727
 
        return msgque;
728
 
    }
729
 
 
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.");
735
 
        pktstart++;
736
 
    }
737
 
 
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);
742
 
 
743
 
    // If we got no responses, something obviously went wrong.
744
 
    if (!que->size()) {
745
 
        log_error("No response from INVOKE of NetConnection connect");
746
 
        
747
 
    }
748
 
 
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
758
 
 
759
 
        while (channel_q->size()) {
760
 
            // Get the first message in the channel queue
761
 
            boost::shared_ptr<cygnal::Buffer> ptr = channel_q->pop();
762
 
            ptr->dump();
763
 
            if (ptr) {          // If there is legit data
764
 
                rthead = decodeHeader(ptr->reference());
765
 
                if (!rthead) {
766
 
                    log_error("Couldn't decode RTMP message header");
767
 
                    continue;
768
 
                }
769
 
                switch (rthead->type) {
770
 
                  case RTMP::NONE:
771
 
                      log_error("RTMP packet can't be of none type!");
772
 
                      break;
773
 
                  case RTMP::CHUNK_SIZE:
774
 
                      log_unimpl("Server message data packet");
775
 
                      break;
776
 
                  case RTMP::ABORT:
777
 
                      log_unimpl("Abort packet");
778
 
                      break;
779
 
                  case RTMP::BYTES_READ:
780
 
                      log_unimpl("Bytes Read data packet");
781
 
                      break;
782
 
                  case RTMP::USER:
783
 
                  {
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]);
786
 
                      break;
787
 
                  }
788
 
                  case RTMP::WINDOW_SIZE:
789
 
                      log_unimpl("Set Window Size message data packet");
790
 
                      break;
791
 
                  case RTMP::SET_BANDWITH:
792
 
                      log_unimpl("Set Bandwidthmessage data packet");
793
 
                      break;
794
 
                  case RTMP::ROUTE:
795
 
                      log_unimpl("Route from other server packet");
796
 
                      break;
797
 
                  case RTMP::AUDIO_DATA:
798
 
                  {
799
 
                      boost::shared_ptr<RTMPMsg> msg = decodeMsgBody(ptr->reference() + rthead->head_size, rthead->bodysize);
800
 
                      if (msg) {
801
 
                          msgque.push_back(msg);
802
 
                      }
803
 
                      break;
804
 
                  }
805
 
                  case RTMP::VIDEO_DATA:
806
 
                  {
807
 
                      boost::shared_ptr<RTMPMsg> msg = decodeMsgBody(ptr->reference() + rthead->head_size, rthead->bodysize);
808
 
                      if (msg) {
809
 
                          msgque.push_back(msg);
810
 
                      }
811
 
                      break;
812
 
                  }
813
 
                  case RTMP::SHARED_OBJ:
814
 
                      log_unimpl("AMF0 Shared Object data packet message");
815
 
                      break;
816
 
                  case RTMP::AMF3_NOTIFY:
817
 
                      log_unimpl("AMF3 Notify data packet message");
818
 
                      break;
819
 
                  case RTMP::AMF3_SHARED_OBJ:
820
 
                      log_unimpl("AMF3 Shared Object data packet message");
821
 
                      break;
822
 
                  case RTMP::AMF3_INVOKE:
823
 
                      log_unimpl("AMF0 Invoke packet message");
824
 
                      break;
825
 
                  case RTMP::NOTIFY:
826
 
                      log_unimpl("AMF0 Notify data packet message");
827
 
                      break;
828
 
                  case RTMP::INVOKE:
829
 
                  {
830
 
                      boost::shared_ptr<RTMPMsg> msg = decodeMsgBody(ptr->reference() + rthead->head_size, rthead->bodysize);
831
 
                      if (msg) {
832
 
                          msgque.push_back(msg);
833
 
                      }
834
 
                      break;
835
 
                  }
836
 
                  case RTMP::FLV_DATA:
837
 
                      log_unimpl("Flv data packet message");
838
 
                      break;
839
 
                  default :
840
 
                      log_error("Couldn't decode RTMP message Body");
841
 
                      break;
842
 
                }
843
 
            }
844
 
        }
845
 
    }
846
 
    
847
 
    return msgque;
848
 
}
849
 
 
850
 
 
851
 
// bool
852
 
// RTMPClient::packetRequest()
853
 
// {
854
 
//     GNASH_REPORT_FUNCTION;
855
 
//     return false;
856
 
// }
857
 
 
858
 
} // end of gnash namespace
859
 
 
860
 
// local Variables:
861
 
// mode: C++
862
 
// indent-tabs-mode: t
863
 
// End: