1 /*
2 * Copyright (C) 2011 Raphael Coeffic
3 *
4 * For the code parts originating from rtmpdump (rtmpsrv.c):
5 * Copyright (C) 2009 Andrej Stepanchuk
6 * Copyright (C) 2009 Howard Chu
7 *
8 * This file is part of SEMS, a free SIP media server.
9 *
10 * SEMS is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version. This program is released under
14 * the GPL with the additional exemption that compiling, linking,
15 * and/or using OpenSSL is allowed.
16 *
17 * For a license to use the SEMS software under conditions
18 * other than those described here, or to purchase support for this
19 * software, please contact iptel.org by e-mail at the following addresses:
20 * info@iptel.org
21 *
22 * SEMS is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 */
31
32 #include <string.h>
33
34 #include "RtmpSender.h"
35 #include "RtmpUtils.h"
36 #include "log.h"
37
RtmpSender(RTMP * r)38 RtmpSender::RtmpSender(RTMP* r)
39 : has_work(false),
40 p_rtmp(r),
41 running(false)
42 {
43 }
44
~RtmpSender()45 RtmpSender::~RtmpSender()
46 {
47 // flush the packet queue
48 m_q_send.lock();
49 while(!q_send.empty()){
50 RTMPPacket p = q_send.front();
51 q_send.pop();
52 RTMPPacket_Free(&p);
53 }
54 m_q_send.unlock();
55 }
56
push_back(const RTMPPacket & p)57 int RtmpSender::push_back(const RTMPPacket& p)
58 {
59 RTMPPacket np = p;
60 if(!RTMPPacket_Alloc(&np,np.m_nBodySize)){
61 ERROR("could not allocate packet.\n");
62 return 0;
63 }
64 memcpy(np.m_body,p.m_body,p.m_nBodySize);
65
66 m_q_send.lock();
67 q_send.push(np);
68 has_work.set(!q_send.empty());
69 m_q_send.unlock();
70
71 return 1;
72 }
73
run()74 void RtmpSender::run()
75 {
76 running.set(true);
77
78 while(running.get()){
79
80 //wait for some work
81 // (at most 1s)
82 //DBG("waiting for work...\n");
83 has_work.wait_for();//_to(1000);
84
85 // send packets in the queue
86 m_q_send.lock();
87 while(!q_send.empty()){
88 RTMPPacket p = q_send.front();
89 q_send.pop();
90 m_q_send.unlock();
91
92 if((p.m_nBodySize > (unsigned)p_rtmp->m_outChunkSize) &&
93 (p.m_packetType == RTMP_PACKET_TYPE_AUDIO)) {
94 // adapt chunk size to the maximum body size
95 // (TODO: set a reasonable max value (spec: 64K))
96 p_rtmp->m_outChunkSize = p.m_nBodySize;
97 SendChangeChunkSize();
98 }
99
100 if(!RTMP_SendPacket(p_rtmp,&p,FALSE)) {
101 ERROR("could not send packet.\n");
102 }
103
104 RTMPPacket_Free(&p);
105
106 m_q_send.lock();
107 }
108 has_work.set(!q_send.empty());
109 m_q_send.unlock();
110 }
111 }
112
on_stop()113 void RtmpSender::on_stop()
114 {
115 running.set(false);
116 }
117
SendChangeChunkSize()118 int RtmpSender::SendChangeChunkSize()
119 {
120 RTMPPacket packet;
121 char pbuf[384], *pend = pbuf+sizeof(pbuf);
122
123 packet.m_nChannel = 0x02;
124 packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
125 packet.m_packetType = 0x01; // SetChunkSize
126 packet.m_nTimeStamp = 0;
127 packet.m_nInfoField2 = 0;
128 packet.m_hasAbsTimestamp = 0;
129 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
130
131 packet.m_nBodySize = 4;
132
133 AMF_EncodeInt32(packet.m_body, pend, p_rtmp->m_outChunkSize);
134 DBG("changing send chunk size to %i\n",p_rtmp->m_outChunkSize);
135
136 return RTMP_SendPacket(p_rtmp,&packet,FALSE);
137 }
138
SendConnectResult(double txn)139 int RtmpSender::SendConnectResult(double txn)
140 {
141 RTMPPacket packet;
142 char pbuf[384], *pend = pbuf+sizeof(pbuf);
143 AMFObject obj;
144 AMFObjectProperty p, op;
145 AVal av;
146
147 packet.m_nChannel = CONTROL_CHANNEL;
148 packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
149 packet.m_packetType = INVOKE_PTYPE;
150 packet.m_nTimeStamp = 0;
151 packet.m_nInfoField2 = 0;
152 packet.m_hasAbsTimestamp = 0;
153 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
154
155 char *enc = packet.m_body;
156 enc = AMF_EncodeString(enc, pend, &av__result);
157 enc = AMF_EncodeNumber(enc, pend, txn);
158 *enc++ = AMF_OBJECT;
159
160 STR2AVAL(av, "FMS/3,5,1,525");
161 enc = AMF_EncodeNamedString(enc, pend, &av_fmsVer, &av);
162 enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 31.0);
163 enc = AMF_EncodeNamedNumber(enc, pend, &av_mode, 1.0);
164 *enc++ = 0;
165 *enc++ = 0;
166 *enc++ = AMF_OBJECT_END;
167
168 *enc++ = AMF_OBJECT;
169
170 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
171 STR2AVAL(av, "NetConnection.Connect.Success");
172 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av);
173 STR2AVAL(av, "Connection succeeded.");
174 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av);
175 enc = AMF_EncodeNamedNumber(enc, pend, &av_objectEncoding, p_rtmp->m_fEncoding);
176 STR2AVAL(p.p_name, "version");
177 STR2AVAL(p.p_vu.p_aval, "3,5,1,525");
178 p.p_type = AMF_STRING;
179 obj.o_num = 1;
180 obj.o_props = &p;
181 op.p_type = AMF_OBJECT;
182 STR2AVAL(op.p_name, "data");
183 op.p_vu.p_object = obj;
184 enc = AMFProp_Encode(&op, enc, pend);
185 *enc++ = 0;
186 *enc++ = 0;
187 *enc++ = AMF_OBJECT_END;
188 *enc++ = 0;
189 *enc++ = 0;
190 *enc++ = AMF_OBJECT_END;
191
192 packet.m_nBodySize = enc - packet.m_body;
193
194 return push_back(packet);
195 }
196
SendRegisterResult(double txn,const char * str)197 int RtmpSender::SendRegisterResult(double txn, const char* str)
198 {
199 RTMPPacket packet;
200 char pbuf[512], *pend = pbuf+sizeof(pbuf);
201
202 packet.m_nChannel = CONTROL_CHANNEL;
203 packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
204 packet.m_packetType = INVOKE_PTYPE;
205 packet.m_nTimeStamp = 0;
206 packet.m_nInfoField2 = 0;
207 packet.m_hasAbsTimestamp = 0;
208 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
209
210 AVal av;
211 char *enc = packet.m_body;
212
213 enc = AMF_EncodeString(enc, pend, &av__result);
214 enc = AMF_EncodeNumber(enc, pend, txn);
215 *enc++ = AMF_NULL;
216 *enc++ = AMF_OBJECT;
217
218 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
219 STR2AVAL(av, str);
220 enc = AMF_EncodeNamedString(enc, pend, &av_uri, &av);
221 *enc++ = 0;
222 *enc++ = 0;
223 *enc++ = AMF_OBJECT_END;
224
225 packet.m_nBodySize = enc - packet.m_body;
226
227 return push_back(packet);
228 }
229
SendErrorResult(double txn,const char * str)230 int RtmpSender::SendErrorResult(double txn, const char* str)
231 {
232 RTMPPacket packet;
233 char pbuf[512], *pend = pbuf+sizeof(pbuf);
234
235 packet.m_nChannel = CONTROL_CHANNEL;
236 packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
237 packet.m_packetType = INVOKE_PTYPE;
238 packet.m_nTimeStamp = 0;
239 packet.m_nInfoField2 = 0;
240 packet.m_hasAbsTimestamp = 0;
241 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
242
243 AVal av;
244 char *enc = packet.m_body;
245
246 enc = AMF_EncodeString(enc, pend, &av__error);
247 enc = AMF_EncodeNumber(enc, pend, txn);
248 *enc++ = AMF_NULL;
249 *enc++ = AMF_OBJECT;
250
251 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_error);
252 STR2AVAL(av, str);
253 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av);
254 *enc++ = 0;
255 *enc++ = 0;
256 *enc++ = AMF_OBJECT_END;
257
258 packet.m_nBodySize = enc - packet.m_body;
259
260 return push_back(packet);
261 }
262
SendResultNumber(double txn,double ID)263 int RtmpSender::SendResultNumber(double txn, double ID)
264 {
265 RTMPPacket packet;
266 char pbuf[256], *pend = pbuf+sizeof(pbuf);
267
268 packet.m_nChannel = CONTROL_CHANNEL;
269 packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
270 packet.m_packetType = INVOKE_PTYPE;
271 packet.m_nTimeStamp = 0;
272 packet.m_nInfoField2 = 0;
273 packet.m_hasAbsTimestamp = 0;
274 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
275
276 char *enc = packet.m_body;
277 enc = AMF_EncodeString(enc, pend, &av__result);
278 enc = AMF_EncodeNumber(enc, pend, txn);
279 *enc++ = AMF_NULL;
280 enc = AMF_EncodeNumber(enc, pend, ID);
281
282 packet.m_nBodySize = enc - packet.m_body;
283
284 return push_back(packet);
285 }
286
287
SendPlayStart()288 int RtmpSender::SendPlayStart()
289 {
290 RTMPPacket packet;
291 char pbuf[384], *pend = pbuf+sizeof(pbuf);
292
293 packet.m_nChannel = CONTROL_CHANNEL;
294 packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
295 packet.m_packetType = INVOKE_PTYPE;
296 packet.m_nTimeStamp = 0;
297 packet.m_nInfoField2 = 0;
298 packet.m_hasAbsTimestamp = 0;
299 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
300
301 char *enc = packet.m_body;
302 enc = AMF_EncodeString(enc, pend, &av_onStatus);
303 enc = AMF_EncodeNumber(enc, pend, 0);
304
305 *enc++ = AMF_NULL;//rco: seems to be needed
306 *enc++ = AMF_OBJECT;
307
308 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
309 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av_NetStream_Play_Start);
310 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av_Started_playing);
311 enc = AMF_EncodeNamedString(enc, pend, &av_details, &p_rtmp->Link.playpath);
312 enc = AMF_EncodeNamedString(enc, pend, &av_clientid, &av_clientid);
313 *enc++ = 0;
314 *enc++ = 0;
315 *enc++ = AMF_OBJECT_END;
316
317 packet.m_nBodySize = enc - packet.m_body;
318 return push_back(packet);
319 }
320
SendPlayStop()321 int RtmpSender::SendPlayStop()
322 {
323 RTMPPacket packet;
324 char pbuf[384], *pend = pbuf+sizeof(pbuf);
325
326 packet.m_nChannel = CONTROL_CHANNEL;
327 packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
328 packet.m_packetType = INVOKE_PTYPE;
329 packet.m_nTimeStamp = 0;
330 packet.m_nInfoField2 = 0;
331 packet.m_hasAbsTimestamp = 0;
332 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
333
334 char *enc = packet.m_body;
335 enc = AMF_EncodeString(enc, pend, &av_onStatus);
336 enc = AMF_EncodeNumber(enc, pend, 0);
337
338 *enc++ = AMF_NULL;//rco: needed!
339 *enc++ = AMF_OBJECT;
340
341 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
342 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av_NetStream_Play_Stop);
343 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av_Stopped_playing);
344 enc = AMF_EncodeNamedString(enc, pend, &av_details, &p_rtmp->Link.playpath);
345 enc = AMF_EncodeNamedString(enc, pend, &av_clientid, &av_clientid);
346 *enc++ = 0;
347 *enc++ = 0;
348 *enc++ = AMF_OBJECT_END;
349
350 packet.m_nBodySize = enc - packet.m_body;
351 return push_back(packet);
352 }
353
SendPause(int DoPause,int iTime)354 int RtmpSender::SendPause(int DoPause, int iTime)
355 {
356 RTMPPacket packet;
357 char pbuf[256], *pend = pbuf + sizeof(pbuf);
358 char *enc;
359
360 packet.m_nChannel = 0x08; /* video channel */
361 packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
362 packet.m_packetType = 0x14; /* invoke */
363 packet.m_nTimeStamp = 0;
364 packet.m_nInfoField2 = 0;
365 packet.m_hasAbsTimestamp = 0;
366 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
367
368 enc = packet.m_body;
369 enc = AMF_EncodeString(enc, pend, &av_pause);
370 enc = AMF_EncodeNumber(enc, pend, ++p_rtmp->m_numInvokes);
371 *enc++ = AMF_NULL;
372 enc = AMF_EncodeBoolean(enc, pend, DoPause);
373 enc = AMF_EncodeNumber(enc, pend, (double)iTime);
374
375 packet.m_nBodySize = enc - packet.m_body;
376
377 DBG("%d, pauseTime=%d", DoPause, iTime);
378 return push_back(packet);
379 }
380
381 /*
382 from http://jira.red5.org/confluence/display/docs/Ping:
383
384 Ping is the most mysterious message in RTMP and till now we haven't fully interpreted it yet. In summary, Ping message is used as a special command that are exchanged between client and server. This page aims to document all known Ping messages. Expect the list to grow.
385
386 The type of Ping packet is 0x4 and contains two mandatory parameters and two optional parameters. The first parameter is the type of Ping and in short integer. The second parameter is the target of the ping. As Ping is always sent in Channel 2 (control channel) and the target object in RTMP header is always 0 which means the Connection object, it's necessary to put an extra parameter to indicate the exact target object the Ping is sent to. The second parameter takes this responsibility. The value has the same meaning as the target object field in RTMP header. (The second value could also be used as other purposes, like RTT Ping/Pong. It is used as the timestamp.) The third and fourth parameters are optional and could be looked upon as the parameter of the Ping packet. Below is an unexhausted list of Ping messages.
387
388 * type 0: Clear the stream. No third and fourth parameters. The second parameter could be 0. After the connection is established, a Ping 0,0 will be sent from server to client. The message will also be sent to client on the start of Play and in response of a Seek or Pause/Resume request. This Ping tells client to re-calibrate the clock with the timestamp of the next packet server sends.
389 * type 1: Tell the stream to clear the playing buffer.
390 * type 3: Buffer time of the client. The third parameter is the buffer time in millisecond.
391 * type 4: Reset a stream. Used together with type 0 in the case of VOD. Often sent before type 0.
392 * type 6: Ping the client from server. The second parameter is the current time.
393 * type 7: Pong reply from client. The second parameter is the time the server sent with his ping request.
394 * type 26: SWFVerification request
395 * type 27: SWFVerification response
396 */
397 int
SendCtrl(short nType,unsigned int nObject,unsigned int nTime)398 RtmpSender::SendCtrl(short nType, unsigned int nObject, unsigned int nTime)
399 {
400 RTMPPacket packet;
401 char pbuf[256], *pend = pbuf + sizeof(pbuf);
402 int nSize;
403 char *buf;
404
405 DBG("sending ctrl. type: 0x%04x", (unsigned short)nType);
406
407 packet.m_nChannel = 0x02; /* control channel (ping) */
408 packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
409 packet.m_packetType = 0x04; /* ctrl */
410 packet.m_nTimeStamp = 0; /* RTMP_GetTime(); */
411 packet.m_nInfoField2 = 0;
412 packet.m_hasAbsTimestamp = 0;
413 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
414
415 switch(nType) {
416 case 0x03: nSize = 10; break; /* buffer time */
417 case 0x1A: nSize = 3; break; /* SWF verify request */
418 case 0x1B: nSize = 44; break; /* SWF verify response */
419 default: nSize = 6; break;
420 }
421
422 packet.m_nBodySize = nSize;
423
424 buf = packet.m_body;
425 buf = AMF_EncodeInt16(buf, pend, nType);
426
427 if (nType == 0x1B)
428 {
429 #ifdef CRYPTO
430 memcpy(buf, rtmp.Link.SWFVerificationResponse, 42);
431 DBG("Sending SWFVerification response: ");
432 RTMP_LogHex(RTMP_LOGDEBUG, (uint8_t *)packet.m_body, packet.m_nBodySize);
433 #endif
434 }
435 else if (nType == 0x1A)
436 {
437 *buf = nObject & 0xff;
438 }
439 else
440 {
441 if (nSize > 2)
442 buf = AMF_EncodeInt32(buf, pend, nObject);
443
444 if (nSize > 6)
445 buf = AMF_EncodeInt32(buf, pend, nTime);
446 }
447
448 return push_back(packet);
449 }
450
SendStreamBegin()451 int RtmpSender::SendStreamBegin()
452 {
453 return SendCtrl(0, 1, 0);
454 }
455
SendStreamEOF()456 int RtmpSender::SendStreamEOF()
457 {
458 return SendCtrl(1, 1, 0);
459 }
460
SendCallStatus(int status)461 int RtmpSender::SendCallStatus(int status)
462 {
463 RTMPPacket packet;
464 char pbuf[384], *pend = pbuf+sizeof(pbuf);
465
466 packet.m_nChannel = CONTROL_CHANNEL;
467 packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
468 packet.m_packetType = INVOKE_PTYPE;
469 packet.m_nTimeStamp = 0;
470 packet.m_nInfoField2 = 0;
471 packet.m_hasAbsTimestamp = 0;
472 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
473
474 char *enc = packet.m_body;
475 enc = AMF_EncodeString(enc, pend, &av_onStatus);
476 enc = AMF_EncodeNumber(enc, pend, 0);
477
478 *enc++ = AMF_NULL;//rco: needed!
479 *enc++ = AMF_OBJECT;
480 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
481 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av_Sono_Call_Status);
482 enc = AMF_EncodeNamedNumber(enc, pend, &av_status_code, status);
483 *enc++ = 0;
484 *enc++ = 0;
485 *enc++ = AMF_OBJECT_END;
486
487 packet.m_nBodySize = enc - packet.m_body;
488 return push_back(packet);
489 }
490
NotifyIncomingCall(const string & uri)491 int RtmpSender::NotifyIncomingCall(const string& uri)
492 {
493 RTMPPacket packet;
494 char pbuf[384], *pend = pbuf+sizeof(pbuf);
495
496 packet.m_nChannel = CONTROL_CHANNEL;
497 packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
498 packet.m_packetType = INVOKE_PTYPE;
499 packet.m_nTimeStamp = 0;
500 packet.m_nInfoField2 = 0;
501 packet.m_hasAbsTimestamp = 0;
502 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
503
504 char *enc = packet.m_body;
505 enc = AMF_EncodeString(enc, pend, &av_onStatus);
506 enc = AMF_EncodeNumber(enc, pend, 0);
507
508 *enc++ = AMF_NULL;//rco: needed!
509 *enc++ = AMF_OBJECT;
510 AVal tmp_uri = _AVC(uri.c_str());
511 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
512 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av_Sono_Call_Incoming);
513 enc = AMF_EncodeNamedString(enc, pend, &av_uri, &tmp_uri);
514 *enc++ = 0;
515 *enc++ = 0;
516 *enc++ = AMF_OBJECT_END;
517
518 packet.m_nBodySize = enc - packet.m_body;
519 return push_back(packet);
520 }
521
522