root/lang/c/librtmp/rtmp.c

Revision 36527, 39.1 kB (checked in by itkz, 5 years ago)

add indent for DEBUG code

Line 
1/*
2    librtmp
3    Copyright (C) 2009 ITOYANAGI Kazunori
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License as published by the Free Software Foundation; either
8    version 2 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with this library; if not, write to the Free
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
19    ITOYANAGI Kazunori
20    kazunori@itoyanagi.name
21*/
22
23
24#ifdef MACOS_OPENTRANSPORT
25#include <OpenTransport.h>
26#include <OpenTptInternet.h>
27#else
28#if defined(__WIN32__) || defined(WIN32)
29#define __USE_W32_SOCKETS
30#include <windows.h>
31#ifdef __CYGWIN__
32#include <netinet/in.h>
33#endif
34#else /* UNIX */
35#ifdef __OS2__
36#include <types.h>
37#include <sys/ioctl.h>
38#endif
39#include <sys/time.h>
40#include <unistd.h>
41#include <fcntl.h>
42#include <netinet/in.h>
43#ifndef __BEOS__
44#include <arpa/inet.h>
45#endif
46#ifdef linux /* FIXME: what other platforms have this? */
47#include <netinet/tcp.h>
48#endif
49#include <netdb.h>
50#include <sys/socket.h>
51#endif /* WIN32 */
52#endif /* Open Transport */
53
54#if defined(__WIN32__) || defined(WIN32)
55#include <mmsystem.h>
56#endif
57
58#include <stdio.h>
59#include <stdlib.h>
60#include <string.h>
61#include <unistd.h>
62#include <time.h>
63#include <errno.h>
64
65
66#include "rtmp.h"
67#include "rtmp_packet.h"
68#include "amf_packet.h"
69#include "data_rw.h"
70
71
72static rtmp_server_client_t *get_new_server_client(rtmp_server_t *s);
73static int rtmp_server_client_set_will_send_buffer(
74    rtmp_server_client_t *rc, unsigned char *data, size_t size);
75static void rtmp_server_client_delete_received_buffer(
76    rtmp_server_client_t *rsc, size_t size);
77static rtmp_result_t rtmp_server_client_send_and_recv(
78    rtmp_server_client_t *rsc);
79static rtmp_result_t rtmp_server_client_send_packet(
80    rtmp_server_client_t *rsc, rtmp_packet_t *packet);
81static void rtmp_server_client_process_packet(
82    rtmp_server_client_t *rsc, rtmp_packet_t *packet);
83static void rtmp_server_client_free(
84    rtmp_server_t *rs, rtmp_server_client_t *rsc);
85
86static void rtmp_server_client_handshake_first(
87    rtmp_server_client_t *server_client);
88static void rtmp_server_client_handshake_second(
89    rtmp_server_client_t *server_client);
90static void rtmp_server_client_get_packet(
91    rtmp_server_client_t *server_client);
92
93
94rtmp_server_t *rtmp_server_create(unsigned short port_number)
95{
96    rtmp_server_t *rtmp_server;
97    int ret;
98
99    rtmp_server = (rtmp_server_t*)malloc(sizeof(rtmp_server_t));
100    if (rtmp_server == NULL) {
101        return NULL;
102    }
103    rtmp_server->client_pool = NULL;
104    rtmp_server->client_working = NULL;
105    rtmp_server->stand_by_socket = -1;
106
107    rtmp_server->conn_sock = socket(AF_INET, SOCK_STREAM, 0);
108    if (rtmp_server->conn_sock == -1) {
109        rtmp_server_free(rtmp_server);
110        return NULL;
111    }
112    rtmp_server->stand_by_socket = 1;
113
114    rtmp_server->conn_sockaddr.sin_family = AF_INET;
115    rtmp_server->conn_sockaddr.sin_addr.s_addr = INADDR_ANY;
116    rtmp_server->conn_sockaddr.sin_port = htons(port_number);
117    ret = bind(
118        rtmp_server->conn_sock,
119        (struct sockaddr*)&(rtmp_server->conn_sockaddr),
120        sizeof(rtmp_server->conn_sockaddr));
121    if (ret == -1) {
122        rtmp_server_free(rtmp_server);
123        return NULL;
124    }
125
126    ret = listen(rtmp_server->conn_sock, 10);
127    if (ret == -1) {
128        rtmp_server_free(rtmp_server);
129        return NULL;
130    }
131
132    return rtmp_server;
133}
134
135
136void rtmp_server_process_message(rtmp_server_t *rs)
137{
138    rtmp_server_client_t *rsc;
139    rtmp_server_client_t *next;
140    int client_sock;
141#ifdef __MINGW32__
142    int addrlen;
143#else
144    socklen_t addrlen;
145#endif
146    fd_set fdset;
147    int ret;
148    struct timeval timeout;
149    rtmp_result_t result;
150
151    FD_ZERO(&fdset);
152    FD_SET((unsigned int)rs->conn_sock, &fdset);
153    timeout.tv_sec = 0;
154    timeout.tv_usec = 0;
155    ret = select(rs->conn_sock + 1, &fdset, NULL, NULL, &timeout);
156    if (ret == 1) {
157        rsc = get_new_server_client(rs);
158
159        addrlen = sizeof(rs->conn_sockaddr);
160        client_sock = accept(
161            rs->conn_sock,
162            (struct sockaddr*)&(rs->conn_sockaddr),
163            &addrlen);
164        if (client_sock == -1) {
165            return;
166        }
167
168        rsc->conn_sock = client_sock;
169        rsc->prev = NULL;
170        rsc->next = rs->client_working;
171        if (rs->client_working == NULL) {
172            rs->client_working = rsc;
173        } else {
174            rs->client_working->prev = rsc;
175            rs->client_working = rsc;
176        }
177    }
178
179    rsc = rs->client_working;
180    while (rsc) {
181        result = rtmp_server_client_send_and_recv(rsc);
182        rsc->process_message(rsc);
183        if (result == RTMP_ERROR_DISCONNECTED) {
184#ifdef DEBUG
185        printf("client disconnected\n");
186#endif
187            next = rsc->next;
188            rtmp_server_client_free(rs, rsc);
189            rsc = next;
190        } else {
191            rsc = rsc->next;
192        }
193    }
194}
195
196
197static rtmp_result_t rtmp_server_client_send_and_recv(rtmp_server_client_t *rsc)
198{
199    fd_set fdset;
200    int ret;
201    struct timeval timeout;
202    int received_size;
203    int sent_size;
204
205    FD_ZERO(&fdset);
206    FD_SET(rsc->conn_sock, &fdset);
207    timeout.tv_sec = 0;
208    timeout.tv_usec = 0;
209    ret = select(rsc->conn_sock + 1, &fdset, NULL, NULL, &timeout);
210    if (ret == 1) {
211        received_size = recv(
212            rsc->conn_sock,
213            (void*)(rsc->received_buffer + rsc->received_size),
214            RTMP_BUFFER_SIZE - rsc->received_size, 0);
215        if (received_size == -1) {
216            return RTMP_ERROR_DISCONNECTED;
217        }
218#ifdef DEBUG
219        if (received_size > 0) {
220            printf("received: %d\n", received_size);
221        }
222#endif
223        rsc->received_size += received_size;
224    }
225
226    rsc->process_message(rsc);
227
228    if (rsc->will_send_size > 0) {
229        FD_ZERO(&fdset);
230        FD_SET(rsc->conn_sock, &fdset);
231        timeout.tv_sec = 0;
232        timeout.tv_usec = 0;
233        ret = select(rsc->conn_sock + 1, NULL, &fdset, NULL, &timeout);
234        if (ret == 1) {
235            sent_size = send(
236                rsc->conn_sock,
237                rsc->will_send_buffer,
238                rsc->will_send_size, 0);
239            if (sent_size == -1) {
240                return RTMP_ERROR_DISCONNECTED;
241            }
242#ifdef DEBUG
243            if (sent_size > 0) {
244                printf("sent: %d\n", sent_size);
245            }
246#endif
247            if (rsc->will_send_size - sent_size > 0) {
248                memmove(
249                    rsc->will_send_buffer,
250                    rsc->will_send_buffer + sent_size,
251                    rsc->will_send_size - sent_size);
252            }
253            rsc->will_send_size -= sent_size;
254        }
255    }
256
257    return RTMP_SUCCESS;
258}
259
260
261static rtmp_server_client_t *get_new_server_client(rtmp_server_t *rs)
262{
263    rtmp_server_client_t *rsc;
264
265    if (rs->client_pool == NULL) {
266        rsc = (rtmp_server_client_t*)malloc(sizeof(rtmp_server_client_t));
267        if (rsc == NULL) {
268            return NULL;
269        }
270    } else {
271        rsc = rs->client_pool;
272        rs->client_pool = rsc->next;
273        if (rs->client_pool) {
274            rs->client_pool->prev = NULL;
275        }
276    }
277
278
279    rsc->received_size = 0;
280    rsc->will_send_size = 0;
281    rsc->amf_chunk_size = DEFAULT_AMF_CHUNK_SIZE;
282    rsc->process_message = rtmp_server_client_handshake_first;
283
284    return rsc;
285}
286
287
288static int rtmp_server_client_set_will_send_buffer(
289    rtmp_server_client_t *rsc, unsigned char *data, size_t size)
290{
291    if (rsc->will_send_size + size > RTMP_BUFFER_SIZE) {
292        return RTMP_ERROR_BUFFER_OVERFLOW;
293    }
294    memmove(
295        rsc->will_send_buffer + rsc->will_send_size,
296        data, size);
297    rsc->will_send_size += size;
298    return RTMP_SUCCESS;
299}
300
301
302static void rtmp_server_client_delete_received_buffer(
303    rtmp_server_client_t *rsc, size_t size)
304{
305    if (size >= rsc->received_size) {
306        rsc->received_size = 0;
307    } else {
308        memmove(
309            rsc->received_buffer,
310            rsc->received_buffer + size,
311            rsc->received_size - size);
312        rsc->received_size -= size;
313    }
314}
315
316
317void rtmp_server_client_handshake_first(rtmp_server_client_t *rsc)
318{
319    unsigned char magic[] = {0x03};
320    int i;
321#if defined(__WIN32__) || defined(WIN32)
322    DWORD now;
323#else
324    unsigned long now;
325#endif
326
327    if (rsc->received_size >= (1 + RTMP_HANDSHAKE_SIZE)) {
328        rtmp_server_client_set_will_send_buffer(
329            rsc, magic, 1);
330#if defined(__WIN32__) || defined(WIN32)
331        now = timeGetTime();
332#else
333        now = time(NULL) * 1000;
334#endif
335        write_le32int(rsc->handshake, (int)now);
336        write_le32int(rsc->handshake + 4, 0);
337        for (i = 8; i < RTMP_HANDSHAKE_SIZE; ++i) {
338            rsc->handshake[i] = (unsigned char)(rand() % 256);
339        }
340        rtmp_server_client_set_will_send_buffer(
341            rsc,
342            rsc->handshake,
343            RTMP_HANDSHAKE_SIZE);
344        rtmp_server_client_set_will_send_buffer(
345            rsc,
346            rsc->received_buffer + 1,
347            RTMP_HANDSHAKE_SIZE);
348        rtmp_server_client_delete_received_buffer(
349            rsc,
350            1 + RTMP_HANDSHAKE_SIZE);
351#ifdef DEBUG
352        printf("handshake 1\n");
353#endif
354        rsc->process_message = rtmp_server_client_handshake_second;
355    }
356}
357
358
359void rtmp_server_client_handshake_second(rtmp_server_client_t *rsc)
360{
361    unsigned char *client_signature;
362    unsigned char *response;
363
364    if (rsc->received_size >= RTMP_HANDSHAKE_SIZE) {
365        client_signature = rsc->received_buffer;
366        response = rsc->received_buffer;
367#ifdef DEBUG
368        if (memcmp(rsc->handshake, response, RTMP_HANDSHAKE_SIZE) == 0) {
369            printf("handshake response OK!\n");
370        }
371#endif
372        rtmp_server_client_delete_received_buffer(
373            rsc, RTMP_HANDSHAKE_SIZE);
374#ifdef DEBUG
375        printf("handshake 2\n");
376#endif
377        rsc->data = rtmp_packet_create();
378        rsc->process_message = rtmp_server_client_get_packet;
379        rtmp_server_client_send_server_bandwidth(rsc);
380        rtmp_server_client_send_client_bandwidth(rsc);
381        rtmp_server_client_send_ping(rsc);
382    }
383}
384
385
386static void rtmp_server_client_process_packet(
387    rtmp_server_client_t *rsc, rtmp_packet_t *packet)
388{
389    rtmp_packet_inner_amf_t *inner_amf;
390    amf_packet_t *amf;
391    char *command;
392    double number;
393    char *code;
394    char *level;
395
396    switch (packet->data_type) {
397    case RTMP_DATATYPE_CHUNK_SIZE:
398        break;
399    case RTMP_DATATYPE_BYTES_READ:
400        break;
401    case RTMP_DATATYPE_PING:
402        break;
403    case RTMP_DATATYPE_SERVER_BW:
404        break;
405    case RTMP_DATATYPE_CLIENT_BW:
406        break;
407    case RTMP_DATATYPE_AUDIO_DATA:
408        break;
409    case RTMP_DATATYPE_VIDEO_DATA:
410        break;
411    case RTMP_DATATYPE_MESSAGE:
412        break;
413    case RTMP_DATATYPE_NOTIFY:
414        inner_amf = packet->inner_amf_packets;
415        amf = inner_amf->amf;
416        if (amf->datatype != AMF_DATATYPE_STRING) {
417            break;
418        }
419        command = amf->string.value;
420#ifdef DEBUG
421        printf("notify command: %s\n", command);
422#endif
423        rtmp_packet_retrieve_status_info(packet, &code, &level);
424        if (code == NULL || level == NULL) {
425            break;
426        }
427#ifdef DEBUG
428        printf("code: %s\n", code);
429        printf("level: %s\n", level);
430#endif
431        /* FIXME: add event */
432        break;
433    case RTMP_DATATYPE_SHARED_OBJECT:
434        break;
435    case RTMP_DATATYPE_INVOKE:
436        inner_amf = packet->inner_amf_packets;
437        amf = inner_amf->amf;
438        if (amf->datatype != AMF_DATATYPE_STRING) {
439            break;
440        }
441        command = amf->string.value;
442        amf = inner_amf->next->amf;
443        if (amf->datatype != AMF_DATATYPE_NUMBER) {
444            break;
445        }
446        number = amf->number.value;
447#ifdef DEBUG
448        printf("invoke command: %s\n", command);
449#endif
450        if (strcmp(command, "_result") == 0) {
451            rtmp_packet_retrieve_status_info(packet, &code, &level);
452            if (code == NULL || level == NULL) {
453                break;
454            }
455#ifdef DEBUG
456            printf("code: %s\n", code);
457            printf("level: %s\n", level);
458#endif
459            /* FIXME: add event */
460        } else if (strcmp(command, "connect") == 0) {
461//            rsc->amf_chunk_size = 4096;
462            rtmp_server_client_send_chunk_size(rsc);
463            rtmp_server_client_send_connect_result(rsc, number);
464        } else if (strcmp(command, "createStream") == 0) {
465            rtmp_server_client_send_create_stream_result(rsc, number);
466        } else if (strcmp(command, "play") == 0) {
467            rtmp_server_client_send_play_result_success(rsc, number);
468        }
469        break;
470    default:
471        break;
472    }
473}
474
475
476static rtmp_result_t rtmp_server_client_send_packet(
477    rtmp_server_client_t *rsc, rtmp_packet_t *packet)
478{
479    rtmp_result_t result;
480    size_t packet_size;
481
482    unsigned char fuck[1024];
483    result = rtmp_packet_serialize(
484        packet,
485        fuck,
486        1024,
487        rsc->amf_chunk_size,
488        &packet_size);
489    if (result == RTMP_SUCCESS) {
490        rtmp_server_client_set_will_send_buffer(
491            rsc, fuck, packet_size);
492    }
493
494    return RTMP_SUCCESS;
495}
496
497
498void rtmp_server_client_send_server_bandwidth(
499    rtmp_server_client_t *rsc)
500{
501    rtmp_packet_t *rtmp_packet;
502
503    rtmp_packet = (rtmp_packet_t*)rsc->data;
504    rtmp_packet_cleanup(rtmp_packet);
505    rtmp_packet->object_id = 2;
506    rtmp_packet->timer = 0;
507    rtmp_packet->data_type = RTMP_DATATYPE_SERVER_BW;
508    rtmp_packet->stream_id = 0;
509    rtmp_packet->body_type = RTMP_BODY_TYPE_DATA;
510    rtmp_packet_allocate_body_data(rtmp_packet, 4);
511    rtmp_packet->body_data[0] = 0x00;
512    rtmp_packet->body_data[1] = 0x26;
513    rtmp_packet->body_data[2] = 0x25;
514    rtmp_packet->body_data[3] = 0xA0;
515
516    rtmp_server_client_send_packet(rsc, rtmp_packet);
517}
518
519
520void rtmp_server_client_send_client_bandwidth(
521    rtmp_server_client_t *rsc)
522{
523    rtmp_packet_t *rtmp_packet;
524
525    rtmp_packet = (rtmp_packet_t*)rsc->data;
526    rtmp_packet_cleanup(rtmp_packet);
527    rtmp_packet->object_id = 2;
528    rtmp_packet->timer = 0;
529    rtmp_packet->data_type = RTMP_DATATYPE_CLIENT_BW;
530    rtmp_packet->stream_id = 0;
531    rtmp_packet->body_type = RTMP_BODY_TYPE_DATA;
532    rtmp_packet_allocate_body_data(rtmp_packet, 5);
533    rtmp_packet->body_data[0] = 0x00;
534    rtmp_packet->body_data[1] = 0x26;
535    rtmp_packet->body_data[2] = 0x25;
536    rtmp_packet->body_data[3] = 0xA0;
537    rtmp_packet->body_data[4] = 0x02;
538
539    rtmp_server_client_send_packet(rsc, rtmp_packet);
540}
541
542
543void rtmp_server_client_send_ping(rtmp_server_client_t *rsc)
544{
545    rtmp_packet_t *rtmp_packet;
546
547    rtmp_packet = (rtmp_packet_t*)rsc->data;
548    rtmp_packet_cleanup(rtmp_packet);
549    rtmp_packet->object_id = 2;
550    rtmp_packet->timer = 0;
551    rtmp_packet->data_type = RTMP_DATATYPE_PING;
552    rtmp_packet->stream_id = 0;
553    rtmp_packet->body_type = RTMP_BODY_TYPE_DATA;
554    rtmp_packet_allocate_body_data(rtmp_packet, 6);
555    rtmp_packet->body_data[0] = 0x00;
556    rtmp_packet->body_data[1] = 0x00;
557    rtmp_packet->body_data[2] = 0x00;
558    rtmp_packet->body_data[3] = 0x00;
559    rtmp_packet->body_data[4] = 0x00;
560    rtmp_packet->body_data[5] = 0x00;
561
562    rtmp_server_client_send_packet(rsc, rtmp_packet);
563}
564
565
566void rtmp_server_client_send_chunk_size(
567    rtmp_server_client_t *rsc)
568{
569    rtmp_packet_t *rtmp_packet;
570
571    rtmp_packet = (rtmp_packet_t*)rsc->data;
572    rtmp_packet_cleanup(rtmp_packet);
573    rtmp_packet->object_id = 2;
574    rtmp_packet->timer = 0;
575    rtmp_packet->data_type = RTMP_DATATYPE_PING;
576    rtmp_packet->stream_id = 0;
577    rtmp_packet->body_type = RTMP_BODY_TYPE_DATA;
578    rtmp_packet_allocate_body_data(rtmp_packet, 4);
579    write_be32int(rtmp_packet->body_data, rsc->amf_chunk_size);
580
581    rtmp_server_client_send_packet(rsc, rtmp_packet);
582}
583
584
585void rtmp_server_client_send_connect_result(
586    rtmp_server_client_t *rsc, double number)
587{
588    rtmp_packet_t *rtmp_packet;
589    amf_packet_t *amf_object;
590
591    rtmp_packet = (rtmp_packet_t*)rsc->data;
592    rtmp_packet_cleanup(rtmp_packet);
593    rtmp_packet->object_id = 3;
594    rtmp_packet->timer = 0;
595    rtmp_packet->data_type = RTMP_DATATYPE_INVOKE;
596    rtmp_packet->stream_id = 0;
597    rtmp_packet->body_type = RTMP_BODY_TYPE_AMF;
598
599    rtmp_packet_add_amf(
600        rtmp_packet,
601        amf_packet_create_string("_result"));
602    rtmp_packet_add_amf(
603        rtmp_packet,
604        amf_packet_create_number(number));
605
606    amf_object = amf_packet_create_object();
607    amf_packet_add_property_to_object(
608        amf_object, "fmsver", amf_packet_create_string("librtmp 0.1"));
609    amf_packet_add_property_to_object(
610        amf_object, "capabilities", amf_packet_create_number(31));
611    rtmp_packet_add_amf(rtmp_packet, amf_object);
612
613    amf_object = amf_packet_create_object();
614    amf_packet_add_property_to_object(
615        amf_object, "level", amf_packet_create_string("status"));
616    amf_packet_add_property_to_object(
617        amf_object, "code", amf_packet_create_string("NetConnection.Connect.Success"));
618    amf_packet_add_property_to_object(
619        amf_object, "description", amf_packet_create_string("Connection succeeded."));
620    amf_packet_add_property_to_object(
621        amf_object, "clientid", amf_packet_create_number(313639155));
622    /* FIXME: increment client id */
623    amf_packet_add_property_to_object(
624        amf_object, "objectEncoding", amf_packet_create_number(0));
625    rtmp_packet_add_amf(rtmp_packet, amf_object);
626
627    rtmp_server_client_send_packet(rsc, rtmp_packet);
628}
629
630
631void rtmp_server_client_send_create_stream_result(
632    rtmp_server_client_t *rsc, double number)
633{
634    rtmp_packet_t *rtmp_packet;
635
636    rtmp_packet = (rtmp_packet_t*)rsc->data;
637    rtmp_packet_cleanup(rtmp_packet);
638    rtmp_packet->object_id = 3;
639    rtmp_packet->timer = 0;
640    rtmp_packet->data_type = RTMP_DATATYPE_INVOKE;
641    rtmp_packet->stream_id = 0;
642    rtmp_packet->body_type = RTMP_BODY_TYPE_AMF;
643
644    rtmp_packet_add_amf(
645        rtmp_packet,
646        amf_packet_create_string("_result"));
647    rtmp_packet_add_amf(
648        rtmp_packet,
649        amf_packet_create_number(number));
650    rtmp_packet_add_amf(
651        rtmp_packet,
652        amf_packet_create_null());
653    rtmp_packet_add_amf(
654        rtmp_packet,
655        amf_packet_create_number(15125));
656    /* FIXME: What's this number */
657
658    rtmp_server_client_send_packet(rsc, rtmp_packet);
659}
660
661
662void rtmp_server_client_send_play_result_success(
663    rtmp_server_client_t *rsc, double number)
664{
665    rtmp_packet_t *rtmp_packet;
666    amf_packet_t *amf_object;
667
668    rtmp_packet = (rtmp_packet_t*)rsc->data;
669    rtmp_packet_cleanup(rtmp_packet);
670    rtmp_packet->object_id = 5;
671    rtmp_packet->timer = 0;
672    rtmp_packet->data_type = RTMP_DATATYPE_INVOKE;
673    rtmp_packet->stream_id = 1; /* FIXME */
674    rtmp_packet->body_type = RTMP_BODY_TYPE_AMF;
675
676    rtmp_packet_add_amf(
677        rtmp_packet,
678        amf_packet_create_string("onStatus"));
679    rtmp_packet_add_amf(
680        rtmp_packet,
681        amf_packet_create_number(number));
682    rtmp_packet_add_amf(
683        rtmp_packet,
684        amf_packet_create_null());
685
686    amf_object = amf_packet_create_object();
687    amf_packet_add_property_to_object(
688        amf_object, "code", amf_packet_create_string("NetStream.Play.Start"));
689    amf_packet_add_property_to_object(
690        amf_object, "level", amf_packet_create_string("status"));
691    amf_packet_add_property_to_object(
692        amf_object, "description", amf_packet_create_string(""));
693    rtmp_packet_add_amf(rtmp_packet, amf_object);
694
695    rtmp_server_client_send_packet(rsc, rtmp_packet);
696}
697
698
699void rtmp_server_client_send_play_result_error(
700    rtmp_server_client_t *rsc, double number)
701{
702    rtmp_packet_t *rtmp_packet;
703    amf_packet_t *amf_object;
704
705    rtmp_packet = (rtmp_packet_t*)rsc->data;
706    rtmp_packet_cleanup(rtmp_packet);
707    rtmp_packet->object_id = 3;
708    rtmp_packet->timer = 0;
709    rtmp_packet->data_type = RTMP_DATATYPE_INVOKE;
710    rtmp_packet->stream_id = 0; /* FIXME: 8byte header */
711    rtmp_packet->body_type = RTMP_BODY_TYPE_AMF;
712
713    rtmp_packet_add_amf(
714        rtmp_packet,
715        amf_packet_create_string("onStatus"));
716    rtmp_packet_add_amf(
717        rtmp_packet,
718        amf_packet_create_number(number));
719    rtmp_packet_add_amf(
720        rtmp_packet,
721        amf_packet_create_null());
722/* Flash Player crashes when this code is available
723    rtmp_packet_add_amf(
724        rtmp_packet,
725        amf_packet_create_number(15125));
726*/
727
728    amf_object = amf_packet_create_object();
729    amf_packet_add_property_to_object(
730        amf_object, "level", amf_packet_create_string("error"));
731    amf_packet_add_property_to_object(
732        amf_object, "code", amf_packet_create_string("NetStream.Play.StreamNotFound"));
733    amf_packet_add_property_to_object(
734        amf_object, "description", amf_packet_create_string("Failed to play test.mp4; stream not found."));
735    amf_packet_add_property_to_object(
736        amf_object, "clientid", amf_packet_create_number(313639155));
737    /* FIXME: increment client id */
738    amf_packet_add_property_to_object(
739        amf_object, "details", amf_packet_create_string("test.mp4"));
740    rtmp_packet_add_amf(rtmp_packet, amf_object);
741
742    rtmp_server_client_send_packet(rsc, rtmp_packet);
743}
744
745
746static void rtmp_server_client_get_packet(rtmp_server_client_t *rsc)
747{
748    rtmp_result_t ret;
749    size_t packet_size;
750    rtmp_packet_t *packet;
751
752    packet = (rtmp_packet_t*)rsc->data;
753    ret = rtmp_packet_analyze_data(
754        packet,
755        rsc->received_buffer, rsc->received_size,
756        rsc->amf_chunk_size,
757        &packet_size);
758    if (ret == RTMP_SUCCESS) {
759        rtmp_server_client_delete_received_buffer(rsc, packet_size);
760        rtmp_server_client_process_packet(rsc, packet);
761    }
762}
763
764
765static void rtmp_server_client_free(rtmp_server_t *rs, rtmp_server_client_t *rsc)
766{
767    if (rsc->prev) {
768        rsc->prev->next = rsc->next;
769    } else {
770        rs->client_working = rsc->next;
771        if (rsc->next) {
772            rsc->next->prev = NULL;
773        }
774    }
775    if (rsc->next) {
776        rsc->next->prev = rsc->prev;
777    } else {
778        if (rsc->prev) {
779            rsc->prev->next = NULL;
780        }
781    }
782    if (rsc->data) {
783        rtmp_packet_free((rtmp_packet_t*)rsc->data);
784    }
785#ifdef __USE_W32_SOCKETS
786        closesocket(rsc->conn_sock);
787        WSACleanup();
788#else
789        close(rsc->conn_sock);
790#endif
791    free(rsc);
792}
793
794
795void rtmp_server_free(rtmp_server_t *rs)
796{
797    rtmp_server_client_t *rsc;
798    rtmp_server_client_t *next;
799
800    rsc = rs->client_working;
801    while (rsc) {
802        next = rsc->next;
803        rtmp_server_client_free(rs, rsc);
804        rsc = next;
805    }
806    rsc = rs->client_pool;
807    while (rsc) {
808        free(rsc);
809    }
810    if (rs->stand_by_socket) {
811#ifdef __USE_W32_SOCKETS
812        closesocket(rs->conn_sock);
813        WSACleanup();
814#else
815        close(rs->conn_sock);
816#endif
817    }
818    free(rs);
819}
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834static void rtmp_client_parse_url(rtmp_client_t *rc, const char *url);
835static void rtmp_client_parse_host_and_port_number(
836    rtmp_client_t *rc, const char *host_and_port_number);
837
838static void rtmp_client_handshake_first(rtmp_client_t *rc);
839static void rtmp_client_handshake_second(rtmp_client_t *rc);
840static void rtmp_client_get_packet(rtmp_client_t *rc);
841static void rtmp_client_process_packet(
842    rtmp_client_t *rc, rtmp_packet_t *packet);
843
844static int rtmp_client_set_will_send_buffer(
845    rtmp_client_t *rc, unsigned char *data, size_t size);
846static void rtmp_client_delete_received_buffer(
847    rtmp_client_t *rc, size_t size);
848static rtmp_result_t rtmp_client_send_packet(
849    rtmp_client_t *rc, rtmp_packet_t *packet);
850static rtmp_result_t rtmp_client_add_event(
851    rtmp_client_t *rc, char *code, char *level);
852
853
854rtmp_client_t *rtmp_client_create(const char *url)
855{
856    rtmp_client_t *rc;
857    int ret;
858#ifdef __USE_W32_SOCKETS
859    WSADATA data;
860#endif
861    struct sockaddr_in conn_sockaddr;
862
863#ifdef __USE_W32_SOCKETS
864    WSAStartup(MAKEWORD(2, 0), &data);
865#endif
866
867    srand((unsigned)time(NULL));
868
869    rc = (rtmp_client_t*)malloc(sizeof(rtmp_client_t));
870    if (rc == NULL) {
871        return NULL;
872    }
873
874    rc->conn_sock = -1;
875
876    rc->protocol = NULL;
877    rc->host = NULL;
878    rc->port_number = -1;
879    rtmp_client_parse_url(rc, url);
880    if (rc->url == NULL || rc->protocol == NULL || rc->host == NULL || rc->path == NULL) {
881        rtmp_client_free(rc);
882        return NULL;
883    }
884
885    memset(&conn_sockaddr, 0, sizeof(conn_sockaddr));
886    conn_sockaddr.sin_family = AF_INET;
887    conn_sockaddr.sin_addr.s_addr = inet_addr(rc->host);
888    conn_sockaddr.sin_port = htons(rc->port_number);
889    if (conn_sockaddr.sin_addr.s_addr == INADDR_NONE) {
890        rtmp_client_free(rc);
891        return NULL;
892    }
893
894    rc->conn_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
895    if (rc->conn_sock == -1) {
896        rtmp_client_free(rc);
897        return NULL;
898    }
899
900#ifdef __USE_W32_SOCKETS
901    ret = connect(
902        rc->conn_sock,
903        (PSOCKADDR)&conn_sockaddr, sizeof(conn_sockaddr));
904    if (ret == SOCKET_ERROR) {
905        rtmp_client_free(rc);
906        return NULL;
907    }
908#else
909    ret = connect(
910        rc->conn_sock, (const struct sockaddr*)&(conn_sockaddr),
911        sizeof(conn_sockaddr));
912    if (ret == -1) {
913        rtmp_client_free(rc);
914        return NULL;
915    }
916#endif
917
918    rc->amf_chunk_size = DEFAULT_AMF_CHUNK_SIZE;
919    rc->received_size = 0;
920    rc->will_send_size = 0;
921    rc->process_message = rtmp_client_handshake_first;
922    rc->message_number = 0.0;
923    rc->events = NULL;
924
925    return rc;
926}
927
928
929void rtmp_client_free(rtmp_client_t *rc)
930{
931    /* FIXME: clean up some memory */
932    if (rc->conn_sock != -1) {
933#ifdef __USE_W32_SOCKETS
934        closesocket(rc->conn_sock);
935        WSACleanup();
936#else
937        close(rc->conn_sock);
938#endif
939    }
940
941    if (rc->url) {
942        free(rc->url);
943    }
944    if (rc->protocol) {
945        free(rc->protocol);
946    }
947    if (rc->host) {
948        free(rc->host);
949    }
950    if (rc->path) {
951        free(rc->path);
952    }
953}
954
955
956static void rtmp_client_parse_url(rtmp_client_t *rc, const char *url)
957{
958    int i;
959    int position;
960    int length;
961    char previous_charactor;
962    char *host_and_port_number;
963
964    for (i = 0; url[i]; ++i) {
965        if (url[i] == ':') {
966            if (i == 0) {
967                return;
968            }
969            rc->protocol = malloc(i + 1);
970            if (rc->protocol == NULL) {
971                return;
972            }
973            strncpy(rc->protocol, url, i);
974#ifdef DEBUG
975            printf("protocol: %s\n", rc->protocol);
976#endif
977            position = i;
978            break;
979        }
980    }
981    if (url[position] == '\0') {
982        return;
983    }
984    if (rc->protocol == NULL) {
985        return;
986    }
987
988    previous_charactor = url[position - 1];
989    for (i = 0; url[position + i]; ++i) {
990        if (previous_charactor == '/' && url[position + i] == '/') {
991            position += i;
992            break;
993        }
994        previous_charactor = url[position + i];
995    }
996    if (url[position] == '\0') {
997        return;
998    }
999    position++;
1000
1001    for (i = 0; url[position + i]; ++i) {
1002        if (url[position + i] == '/') {
1003            if (i == 0) {
1004                break;
1005            }
1006            host_and_port_number = malloc(i + 1);
1007            if (host_and_port_number == NULL) {
1008                return;
1009            }
1010            strncpy(host_and_port_number, url + position, i);
1011            rtmp_client_parse_host_and_port_number(
1012                rc, host_and_port_number);
1013            free(host_and_port_number);
1014            position += i;
1015            break;
1016        }
1017    }
1018    if (url[position] == '\0') {
1019        return;
1020    }
1021    position++;
1022
1023    length = strlen(url + position);
1024    rc->path = malloc(length + 1);
1025    if (rc->path == NULL) {
1026        return;
1027    }
1028    strncpy(rc->path, url + position, length);
1029#ifdef DEBUG
1030    printf("path: %s\n", rc->path);
1031#endif
1032}
1033
1034
1035static void rtmp_client_parse_host_and_port_number(
1036    rtmp_client_t *rc, const char *host_and_port_number)
1037{
1038    int i;
1039
1040    for (i = 0; host_and_port_number[i]; ++i) {
1041        if (host_and_port_number[i] == ':') {
1042            rc->host = malloc(i + 1);
1043            if (rc->host == NULL) {
1044                return;
1045            }
1046            strncpy(rc->host, host_and_port_number, i);
1047            break;
1048        }
1049    }
1050    if (rc->host == NULL) {
1051        rc->host = malloc(strlen(host_and_port_number) + 1);
1052        if (rc->host == NULL) {
1053            return;
1054        }
1055        strcpy(rc->host, host_and_port_number);
1056        rc->port_number = 1935;
1057    } else {
1058        rc->port_number = atoi(host_and_port_number + i + 1);
1059    }
1060#ifdef DEBUG
1061    printf("host: %s\n", rc->host);
1062    printf("port_number: %d\n", rc->port_number);
1063#endif
1064}
1065
1066
1067void rtmp_client_process_message(rtmp_client_t *rc)
1068{
1069    fd_set fdset;
1070    int ret;
1071    int received_size;
1072    int sent_size;
1073    struct timeval timeout;
1074
1075    FD_ZERO(&fdset);
1076    FD_SET(rc->conn_sock, &fdset);
1077    timeout.tv_sec = 0;
1078    timeout.tv_usec = 0;
1079    ret = select(rc->conn_sock + 1, &fdset, NULL, NULL, &timeout);
1080    if (ret == 1) {
1081        received_size = recv(
1082            rc->conn_sock,
1083            rc->received_buffer + rc->received_size,
1084            RTMP_BUFFER_SIZE - rc->received_size, 0);
1085        /* FIXME: process finishing when recv returns -1 */
1086        if (received_size > 0) {
1087#ifdef DEBUG
1088            printf("received: %d\n", received_size);
1089#endif
1090            rc->received_size += received_size;
1091        }
1092    }
1093
1094    rc->process_message(rc);
1095
1096    if (rc->will_send_size > 0) {
1097        FD_ZERO(&fdset);
1098        FD_SET(rc->conn_sock, &fdset);
1099        timeout.tv_sec = 0;
1100        timeout.tv_usec = 0;
1101        ret = select(rc->conn_sock + 1, NULL, &fdset, NULL, &timeout);
1102        if (ret == 1) {
1103            sent_size = send(
1104                rc->conn_sock,
1105                rc->will_send_buffer,
1106                rc->will_send_size, 0);
1107#ifdef DEBUG
1108            if (sent_size > 0) {
1109                printf("sent: %d\n", sent_size);
1110            }
1111#endif
1112            if (sent_size != -1) {
1113                if (rc->will_send_size - sent_size > 0) {
1114                    memmove(
1115                        rc->will_send_buffer,
1116                        rc->will_send_buffer + sent_size,
1117                        rc->will_send_size - sent_size);
1118                }
1119                rc->will_send_size -= sent_size;
1120            }
1121        }
1122    }
1123}
1124
1125
1126static int rtmp_client_set_will_send_buffer(
1127    rtmp_client_t *rc, unsigned char *data, size_t size)
1128{
1129    if (rc->will_send_size + size > RTMP_BUFFER_SIZE) {
1130        return RTMP_ERROR_BUFFER_OVERFLOW;
1131    }
1132    memmove(
1133        rc->will_send_buffer + rc->will_send_size,
1134        data, size);
1135    rc->will_send_size += size;
1136    return RTMP_SUCCESS;
1137}
1138
1139
1140static void rtmp_client_delete_received_buffer(
1141    rtmp_client_t *rc, size_t size)
1142{
1143    if (size >= rc->received_size) {
1144        rc->received_size = 0;
1145    } else {
1146        memmove(
1147            rc->received_buffer,
1148            rc->received_buffer + size,
1149            rc->received_size - size);
1150        rc->received_size -= size;
1151    }
1152}
1153
1154
1155static void rtmp_client_handshake_first(rtmp_client_t *rc)
1156{
1157    unsigned char magic[] = {0x03};
1158    int i;
1159#if defined(__WIN32__) || defined(WIN32)
1160    DWORD now;
1161#else
1162    unsigned long now;
1163#endif
1164
1165    rtmp_client_set_will_send_buffer(rc, magic, 1);
1166
1167#if defined(__WIN32__) || defined(WIN32)
1168    now = timeGetTime();
1169#else
1170    now = time(NULL) * 1000;
1171#endif
1172    write_le32int(rc->handshake, (int)now);
1173    write_le32int(rc->handshake + 4, 0);
1174    for (i = 8; i < RTMP_HANDSHAKE_SIZE; ++i) {
1175        rc->handshake[i] = (unsigned char)(rand() % 256);
1176    }
1177    rtmp_client_set_will_send_buffer(
1178        rc, rc->handshake, RTMP_HANDSHAKE_SIZE);
1179#ifdef DEBUG
1180    printf("handshake 1\n");
1181#endif
1182    rc->process_message = rtmp_client_handshake_second;
1183}
1184
1185
1186static void rtmp_client_handshake_second(rtmp_client_t *rc)
1187{
1188    unsigned char *server_signature;
1189    unsigned char *response;
1190
1191    if (rc->received_size >= (1 + RTMP_HANDSHAKE_SIZE * 2)) {
1192        server_signature = rc->received_buffer + 1;
1193        response = rc->received_buffer + 1 + RTMP_HANDSHAKE_SIZE;
1194        rtmp_client_set_will_send_buffer(
1195            rc, server_signature, RTMP_HANDSHAKE_SIZE);
1196#ifdef DEBUG
1197        if (memcmp(rc->handshake, response, RTMP_HANDSHAKE_SIZE) == 0) {
1198            printf("handshake response OK!\n");
1199        }
1200#endif
1201        rtmp_client_delete_received_buffer(
1202            rc, 1 + RTMP_HANDSHAKE_SIZE * 2);
1203#ifdef DEBUG
1204        printf("handshake 2\n");
1205#endif
1206        rc->data = rtmp_packet_create();
1207        rc->process_message = rtmp_client_get_packet;
1208        rtmp_client_connect(rc);
1209    }
1210}
1211
1212
1213static void rtmp_client_get_packet(rtmp_client_t *rc)
1214{
1215    rtmp_result_t ret;
1216    size_t packet_size;
1217    rtmp_packet_t *packet;
1218
1219    packet = (rtmp_packet_t*)rc->data;
1220    ret = rtmp_packet_analyze_data(
1221        packet,
1222        rc->received_buffer, rc->received_size,
1223        rc->amf_chunk_size,
1224        &packet_size);
1225    if (ret == RTMP_SUCCESS) {
1226        rtmp_client_delete_received_buffer(rc, packet_size);
1227        rtmp_client_process_packet(rc, packet);
1228    }
1229}
1230
1231
1232void rtmp_client_process_packet(
1233    rtmp_client_t *rc, rtmp_packet_t *packet)
1234{
1235    rtmp_packet_inner_amf_t *inner_amf;
1236    amf_packet_t *amf;
1237    char *command;
1238    char *code;
1239    char *level;
1240
1241    switch (packet->data_type) {
1242    case RTMP_DATATYPE_CHUNK_SIZE:
1243        rc->amf_chunk_size = read_be32int(packet->body_data);
1244        break;
1245    case RTMP_DATATYPE_BYTES_READ:
1246        break;
1247    case RTMP_DATATYPE_PING:
1248        break;
1249    case RTMP_DATATYPE_SERVER_BW:
1250        break;
1251    case RTMP_DATATYPE_CLIENT_BW:
1252        break;
1253    case RTMP_DATATYPE_AUDIO_DATA:
1254        break;
1255    case RTMP_DATATYPE_VIDEO_DATA:
1256        break;
1257    case RTMP_DATATYPE_MESSAGE:
1258        break;
1259    case RTMP_DATATYPE_NOTIFY:
1260        inner_amf = packet->inner_amf_packets;
1261        amf = inner_amf->amf;
1262        if (amf->datatype != AMF_DATATYPE_STRING) {
1263            break;
1264        }
1265        command = amf->string.value;
1266#ifdef DEBUG
1267        printf("command: %s\n", command);
1268#endif
1269        rtmp_packet_retrieve_status_info(packet, &code, &level);
1270        if (code == NULL || level == NULL) {
1271            break;
1272        }
1273#ifdef DEBUG
1274        printf("code: %s\n", code);
1275        printf("level: %s\n", level);
1276#endif
1277        rtmp_client_add_event(rc, code, level);
1278        break;
1279    case RTMP_DATATYPE_SHARED_OBJECT:
1280        break;
1281    case RTMP_DATATYPE_INVOKE:
1282        inner_amf = packet->inner_amf_packets;
1283        amf = inner_amf->amf;
1284        if (amf->datatype != AMF_DATATYPE_STRING) {
1285            break;
1286        }
1287        command = amf->string.value;
1288#ifdef DEBUG
1289        printf("command: %s\n", command);
1290#endif
1291        if (strcmp(command, "_result") == 0) {
1292            rtmp_packet_retrieve_status_info(packet, &code, &level);
1293            if (code == NULL || level == NULL) {
1294                break;
1295            }
1296#ifdef DEBUG
1297            printf("code: %s\n", code);
1298            printf("level: %s\n", level);
1299#endif
1300            rtmp_client_add_event(rc, code, level);
1301        }
1302        break;
1303    default:
1304        break;
1305    }
1306}
1307
1308
1309rtmp_result_t rtmp_client_add_event(
1310    rtmp_client_t *rc, char *code, char *level)
1311{
1312    rtmp_event_t *event;
1313    rtmp_event_t *last_event;
1314
1315    event = (rtmp_event_t*)malloc(sizeof(rtmp_event_t));
1316    event->code = (char*)malloc(strlen(code) + 1);
1317    strcpy(event->code, code);
1318    event->level = (char*)malloc(strlen(level) + 1);
1319    strcpy(event->level, level);
1320    event->next = NULL;
1321    if (rc->events == NULL) {
1322        rc->events = event;
1323    } else {
1324        last_event = rc->events;
1325        while (last_event->next != NULL) {
1326            last_event = last_event->next;
1327        }
1328        last_event->next = event;
1329    }
1330
1331    return RTMP_SUCCESS;
1332}
1333
1334
1335rtmp_event_t *rtmp_client_get_event(rtmp_client_t *rc)
1336{
1337    return rc->events;
1338}
1339
1340
1341void rtmp_client_delete_event(rtmp_client_t *rc)
1342{
1343    rtmp_event_t *delete_event;
1344    rtmp_event_t *next_event;
1345
1346    delete_event = rc->events;
1347    free(delete_event->code);
1348    free(delete_event->level);
1349    free(delete_event);
1350
1351    rc->events = next_event;
1352}
1353
1354
1355rtmp_result_t rtmp_client_send_packet(
1356    rtmp_client_t *rc, rtmp_packet_t *packet)
1357{
1358    rtmp_result_t result;
1359    size_t packet_size;
1360
1361    unsigned char fuck[1024];
1362    result = rtmp_packet_serialize(
1363        packet,
1364        fuck,
1365        1024,
1366        rc->amf_chunk_size,
1367        &packet_size);
1368    rtmp_client_set_will_send_buffer(
1369        rc, fuck, packet_size);
1370
1371    return RTMP_SUCCESS;
1372}
1373
1374
1375void rtmp_client_connect(rtmp_client_t *rc)
1376{
1377    rtmp_packet_t *rtmp_packet;
1378    amf_packet_t *amf_object;
1379
1380    rtmp_packet = (rtmp_packet_t*)rc->data;
1381    rtmp_packet_cleanup(rtmp_packet);
1382    rtmp_packet->data_type = RTMP_DATATYPE_INVOKE;
1383    rtmp_packet->object_id = 3;
1384
1385    rc->message_number++;
1386    rtmp_packet_add_amf(
1387        rtmp_packet,
1388        amf_packet_create_string("connect"));
1389    rtmp_packet_add_amf(
1390        rtmp_packet,
1391        amf_packet_create_number((double)rc->message_number));
1392
1393    amf_object = amf_packet_create_object();
1394    amf_packet_add_property_to_object(
1395        amf_object, "app", amf_packet_create_string(rc->path));
1396    amf_packet_add_property_to_object(
1397        amf_object, "flashVer", amf_packet_create_string("WIN 10,0,12,36"));
1398    amf_packet_add_property_to_object(
1399        amf_object, "swfUrl", amf_packet_create_undefined());
1400    amf_packet_add_property_to_object(
1401        amf_object, "tcUrl",
1402        amf_packet_create_string(rc->url));
1403    amf_packet_add_property_to_object(
1404        amf_object, "fpad", amf_packet_create_boolean(0));
1405    amf_packet_add_property_to_object(
1406        amf_object, "capabilities", amf_packet_create_number(15.0));
1407    amf_packet_add_property_to_object(
1408        amf_object, "audioCodecs", amf_packet_create_number(1639.0));
1409    amf_packet_add_property_to_object(
1410        amf_object, "videoCodecs", amf_packet_create_number(252.0));
1411    amf_packet_add_property_to_object(
1412        amf_object, "videoFunction", amf_packet_create_number(1.0));
1413    amf_packet_add_property_to_object(
1414        amf_object, "pageUrl", amf_packet_create_undefined());
1415    amf_packet_add_property_to_object(
1416        amf_object, "objectEncoding", amf_packet_create_number(0.0));
1417    rtmp_packet_add_amf(rtmp_packet, amf_object);
1418
1419    rtmp_client_send_packet(rc, rtmp_packet);
1420}
1421
1422
1423void rtmp_client_create_stream(rtmp_client_t *rc)
1424{
1425    rtmp_packet_t *rtmp_packet;
1426
1427    rtmp_packet = (rtmp_packet_t*)rc->data;
1428    rtmp_packet_cleanup(rtmp_packet);
1429    rtmp_packet->data_type = RTMP_DATATYPE_INVOKE;
1430    rtmp_packet->object_id = 3;
1431
1432    rc->message_number++;
1433    rtmp_packet_add_amf(
1434        rtmp_packet,
1435        amf_packet_create_string("createStream"));
1436    rtmp_packet_add_amf(
1437        rtmp_packet,
1438        amf_packet_create_number((double)rc->message_number));
1439    rtmp_packet_add_amf(
1440        rtmp_packet,
1441        amf_packet_create_null());
1442
1443    rtmp_client_send_packet(rc, rtmp_packet);
1444}
1445
1446
1447void rtmp_client_play(rtmp_client_t *rc, const char *file_name)
1448{
1449    rtmp_packet_t *rtmp_packet;
1450
1451    rtmp_packet = (rtmp_packet_t*)rc->data;
1452    rtmp_packet_cleanup(rtmp_packet);
1453    rtmp_packet->data_type = RTMP_DATATYPE_INVOKE;
1454    rtmp_packet->object_id = 3;
1455
1456    rc->message_number++;
1457    rtmp_packet_add_amf(
1458        rtmp_packet,
1459        amf_packet_create_string("play"));
1460    rtmp_packet_add_amf(
1461        rtmp_packet,
1462        amf_packet_create_number((double)rc->message_number));
1463    rtmp_packet_add_amf(
1464        rtmp_packet,
1465        amf_packet_create_null());
1466    rtmp_packet_add_amf(
1467        rtmp_packet,
1468        amf_packet_create_string(file_name));
1469
1470    rtmp_client_send_packet(rc, rtmp_packet);
1471}
1472
Note: See TracBrowser for help on using the browser.