├── Makefile.win32 ├── Makefile.gcc ├── main.c ├── data_rw.h ├── Player.as ├── rtmp_packet.h ├── amf_packet.h ├── rtmp.h ├── data_rw.c ├── rtmp_packet.c ├── amf_packet.c └── rtmp.c /Makefile.win32: -------------------------------------------------------------------------------- 1 | 2 | CC = gcc 3 | 4 | CFLAGS = -g -Wall -Wextra -Wstrict-aliasing=2 -Wcast-qual -Wcast-align -Wwrite-strings -Wfloat-equal -Wpointer-arith -DDEBUG 5 | # -Wconversion 6 | LDFLAGS = -lws2_32 -lwinmm 7 | 8 | TARGET = test.exe 9 | OBJS = main.o rtmp.o rtmp_packet.o amf_packet.o data_rw.o 10 | 11 | $(TARGET) : $(OBJS) 12 | $(CC) -o $(TARGET) $(OBJS) $(LDFLAGS) 13 | 14 | clean: 15 | rm -f $(TARGET) *.o *~ 16 | 17 | main.o: main.c rtmp.h 18 | 19 | rtmp.o: rtmp.c rtmp.h rtmp_packet.h amf_packet.h 20 | 21 | rtmp_packet.o: rtmp_packet.c rtmp_packet.h amf_packet.h data_rw.h 22 | 23 | amf_packet.o: amf_packet.c amf_packet.h data_rw.h 24 | 25 | data_rw.o: data_rw.c data_rw.h 26 | 27 | -------------------------------------------------------------------------------- /Makefile.gcc: -------------------------------------------------------------------------------- 1 | 2 | CC = gcc 3 | 4 | CFLAGS = -g -Wall -Wextra -Wstrict-aliasing=2 -Wcast-qual -Wcast-align -Wwrite-strings -Wfloat-equal -Wpointer-arith -fmudflap -DDEBUG 5 | # -Wconversion 6 | LDFLAGS = -lpthread -lmudflap 7 | 8 | TARGET = test 9 | OBJS = main.o rtmp.o rtmp_packet.o amf_packet.o data_rw.o 10 | 11 | $(TARGET) : $(OBJS) 12 | $(CC) -o $(TARGET) $(OBJS) $(LDFLAGS) 13 | 14 | clean: 15 | rm -f $(TARGET) *.o *~ 16 | 17 | main.o: main.c rtmp.h 18 | 19 | rtmp.o: rtmp.c rtmp.h rtmp_packet.h amf_packet.h 20 | 21 | rtmp_packet.o: rtmp_packet.c rtmp_packet.h amf_packet.h 22 | 23 | amf_packet.o: amf_packet.c amf_packet.h data_rw.h 24 | 25 | data_rw.o: data_rw.c data_rw.h data_rw.h 26 | 27 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #if defined(__WIN32__) || defined(WIN32) 9 | #include 10 | #endif 11 | 12 | #include "rtmp.h" 13 | #include "rtmp_packet.h" 14 | #include "amf_packet.h" 15 | 16 | 17 | int main(void) 18 | { 19 | 20 | rtmp_client_t *rc; 21 | int count; 22 | rtmp_event_t *event; 23 | 24 | rc = rtmp_client_create("rtmp://192.168.157.1:1935/fastplay/"); 25 | if (rc == NULL) { 26 | printf("failed\n"); 27 | return -1; 28 | } 29 | 30 | printf("created.\n"); 31 | 32 | count = 20; 33 | while (count--) { 34 | rtmp_client_process_message(rc); 35 | event = rtmp_client_get_event(rc); 36 | if (event != NULL) { 37 | if (strcmp(event->code, "NetConnection.Connect.Success") == 0) { 38 | rtmp_client_create_stream(rc); 39 | rtmp_client_play(rc, "test.mp4"); 40 | } 41 | rtmp_client_delete_event(rc); 42 | } 43 | sleep(1); 44 | } 45 | 46 | rtmp_client_free(rc); 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /data_rw.h: -------------------------------------------------------------------------------- 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 | #ifndef _data_rw_H_ 24 | #define _data_rw_H_ 25 | 26 | 27 | /* Set up for C function definitions, even when using C++ */ 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | 33 | extern int is_little_endian(void); 34 | extern int is_big_endian(void); 35 | 36 | extern int read_be16int(unsigned char *data); 37 | extern int read_le32int(unsigned char *data); 38 | extern int read_be32int(unsigned char *data); 39 | extern int read_be24int(unsigned char *data); 40 | extern double read_be64double(unsigned char *data); 41 | 42 | extern void write_be16int(unsigned char *data, int value); 43 | extern void write_be24int(unsigned char *data, int value); 44 | extern void write_le32int(unsigned char *data, int value); 45 | extern void write_be32int(unsigned char *data, int value); 46 | extern void write_be64double(unsigned char *data, double value); 47 | 48 | 49 | /* Ends C function definitions when using C++ */ 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /Player.as: -------------------------------------------------------------------------------- 1 | 2 | package { 3 | 4 | import flash.display.Sprite; 5 | import flash.net.NetConnection; 6 | import flash.events.NetStatusEvent; 7 | import flash.events.SecurityErrorEvent; 8 | import flash.events.AsyncErrorEvent; 9 | import flash.media.Video; 10 | import flash.net.NetStream; 11 | import flash.net.ObjectEncoding; 12 | import flash.text.TextField; 13 | 14 | public class Player extends Sprite 15 | { 16 | private var _connection : NetConnection; 17 | private var _stream : NetStream; 18 | private var _video : Video; 19 | private var _textY : Number; 20 | 21 | public function Player() 22 | { 23 | _connection = new NetConnection(); 24 | _connection.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus); 25 | _connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError); 26 | _connection.objectEncoding = ObjectEncoding.AMF0; 27 | _connection.connect("rtmp://127.0.0.1:2935/fastplay/"); 28 | _textY = 0; 29 | } 30 | 31 | private function onNetStatus( event : NetStatusEvent ) : void 32 | { 33 | var output:TextField = new TextField(); 34 | output.width = 10000; 35 | output.text = event.info.code; 36 | output.antiAliasType = flash.text.AntiAliasType.ADVANCED 37 | output.x = 0; 38 | output.y = _textY; 39 | output.scaleX = output.scaleY = 0.5; 40 | addChild(output); 41 | _textY += 10; 42 | 43 | switch(event.info.code){ 44 | case "NetConnection.Connect.Success": 45 | _stream = new NetStream(_connection); 46 | _stream.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus); 47 | _stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError); 48 | 49 | var video : Video = new Video(320, 180); 50 | video.attachNetStream(_stream); 51 | video.deblocking = 2; 52 | video.smoothing = true; 53 | 54 | _stream.play("test.mp4"); 55 | addChild(video); 56 | _video = video 57 | break; 58 | 59 | case "NetStream.Play.StreamNotFound": 60 | trace("File not found"); 61 | break; 62 | default: 63 | break; 64 | } 65 | } 66 | 67 | private function onSecurityError( event : SecurityErrorEvent ) : void 68 | { 69 | } 70 | 71 | private function onAsyncError( event : AsyncErrorEvent ) : void 72 | { 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /rtmp_packet.h: -------------------------------------------------------------------------------- 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 | #ifndef _rtmp_packet_H_ 24 | #define _rtmp_packet_H_ 25 | 26 | #include "rtmp.h" 27 | #include "amf_packet.h" 28 | 29 | 30 | /* Set up for C function definitions, even when using C++ */ 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | 36 | #define HEADER_MAGIC_01 3 37 | #define HEADER_MAGIC_04 2 38 | #define HEADER_MAGIC_08 1 39 | #define HEADER_MAGIC_12 0 40 | 41 | typedef enum rtmp_datatype rtmp_datatype_t; 42 | 43 | enum rtmp_datatype 44 | { 45 | RTMP_DATATYPE_CHUNK_SIZE = 0x01, 46 | RTMP_DATATYPE_UNKNOWN_0 = 0x02, 47 | RTMP_DATATYPE_BYTES_READ = 0x03, 48 | RTMP_DATATYPE_PING = 0x04, 49 | RTMP_DATATYPE_SERVER_BW = 0x05, 50 | RTMP_DATATYPE_CLIENT_BW = 0x06, 51 | RTMP_DATATYPE_UNKNOWN_1 = 0x07, 52 | RTMP_DATATYPE_AUDIO_DATA = 0x08, 53 | RTMP_DATATYPE_VIDEO_DATA = 0x09, 54 | RTMP_DATATYPE_UNKNOWN_2 = 0x0A, 55 | RTMP_DATATYPE_UNKNOWN_3 = 0x0B, 56 | RTMP_DATATYPE_UNKNOWN_4 = 0x0C, 57 | RTMP_DATATYPE_UNKNOWN_5 = 0x0D, 58 | RTMP_DATATYPE_UNKNOWN_6 = 0x0E, 59 | RTMP_DATATYPE_FLEX_STREAM = 0x0F, 60 | RTMP_DATATYPE_FLEX_SHARED_OBJECT = 0x10, 61 | RTMP_DATATYPE_MESSAGE = 0x11, 62 | RTMP_DATATYPE_NOTIFY = 0x12, 63 | RTMP_DATATYPE_SHARED_OBJECT = 0x13, 64 | RTMP_DATATYPE_INVOKE = 0x14, 65 | RTMP_DATATYPE_FLV_DATA = 0x16, 66 | }; 67 | 68 | typedef enum rtmp_body_type rtmp_body_type_t; 69 | 70 | enum rtmp_body_type 71 | { 72 | RTMP_BODY_TYPE_AMF, 73 | RTMP_BODY_TYPE_DATA 74 | }; 75 | 76 | typedef struct rtmp_packet_inner_amf_t rtmp_packet_inner_amf_t; 77 | 78 | struct rtmp_packet_inner_amf_t 79 | { 80 | amf_packet_t *amf; 81 | rtmp_packet_inner_amf_t *next; 82 | }; 83 | 84 | typedef struct rtmp_packet_t rtmp_packet_t; 85 | 86 | struct rtmp_packet_t 87 | { 88 | char object_id; 89 | long timer; 90 | rtmp_datatype_t data_type; 91 | long stream_id; 92 | rtmp_body_type_t body_type; 93 | rtmp_packet_inner_amf_t *inner_amf_packets; 94 | unsigned char *body_data; 95 | size_t body_data_length; 96 | }; 97 | 98 | 99 | extern rtmp_packet_t *rtmp_packet_create(void); 100 | extern void rtmp_packet_free(rtmp_packet_t *packet); 101 | 102 | extern rtmp_result_t rtmp_packet_add_amf( 103 | rtmp_packet_t *packet, 104 | amf_packet_t *amf); 105 | extern void rtmp_packet_cleanup(rtmp_packet_t *packet); 106 | 107 | extern rtmp_result_t rtmp_packet_analyze_data( 108 | rtmp_packet_t *packet, 109 | unsigned char *data, size_t data_size, 110 | size_t amf_chunk_size, 111 | size_t *packet_size); 112 | 113 | extern rtmp_result_t rtmp_packet_serialize( 114 | rtmp_packet_t *packet, 115 | unsigned char *output_buffer, size_t output_buffer_size, 116 | size_t amf_chunk_size, 117 | size_t *packet_size); 118 | 119 | extern rtmp_result_t rtmp_packet_allocate_body_data( 120 | rtmp_packet_t *packet, size_t length); 121 | 122 | extern void rtmp_packet_retrieve_status_info( 123 | rtmp_packet_t *packet, char **code, char **level); 124 | 125 | 126 | /* Ends C function definitions when using C++ */ 127 | #ifdef __cplusplus 128 | } 129 | #endif 130 | 131 | 132 | #endif 133 | -------------------------------------------------------------------------------- /amf_packet.h: -------------------------------------------------------------------------------- 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 | #ifndef _amf_packet_H_ 24 | #define _amf_packet_H_ 25 | 26 | #include "rtmp.h" 27 | 28 | 29 | /* Set up for C function definitions, even when using C++ */ 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | 35 | #define AMF_CHANK_SIZE 128 36 | 37 | typedef enum amf_datatype amf_datatype_t; 38 | 39 | enum amf_datatype 40 | { 41 | AMF_DATATYPE_NUMBER = 0x00, 42 | AMF_DATATYPE_BOOLEAN = 0x01, 43 | AMF_DATATYPE_STRING = 0x02, 44 | AMF_DATATYPE_OBJECT = 0x03, 45 | AMF_DATATYPE_MOVIECLIP = 0x04, 46 | AMF_DATATYPE_NULL = 0x05, 47 | AMF_DATATYPE_UNDEFINED = 0x06, 48 | AMF_DATATYPE_REFERENCE = 0x07, 49 | AMF_DATATYPE_ECMA_ARRAY = 0x08, 50 | AMF_DATATYPE_OBJECT_END = 0x09, 51 | AMF_DATATYPE_STRICT_ARRAY = 0x0A, 52 | AMF_DATATYPE_DATE = 0x0B, 53 | AMF_DATATYPE_LONG_STRING = 0x0C, 54 | AMF_DATATYPE_UNSUPPORTED = 0x0D, 55 | AMF_DATATYPE_RECORDSET = 0x0E, 56 | AMF_DATATYPE_XML_DOCUMENT = 0x0F, 57 | AMF_DATATYPE_TYPED_OBJECT = 0x10, 58 | }; 59 | 60 | typedef struct amf_packet_number_t amf_packet_number_t; 61 | typedef struct amf_packet_boolean_t amf_packet_boolean_t; 62 | typedef struct amf_packet_string_t amf_packet_string_t; 63 | typedef struct amf_packet_object_property_t amf_packet_object_property_t; 64 | typedef struct amf_packet_object_t amf_packet_object_t; 65 | typedef struct amf_packet_null_t amf_packet_null_t; 66 | typedef struct amf_packet_undefined_t amf_packet_undefined_t; 67 | typedef struct amf_packet_ecma_array_property_t amf_packet_ecma_array_property_t; 68 | typedef struct amf_packet_ecma_array_t amf_packet_ecma_array_t; 69 | typedef struct amf_packet_object_end_t amf_packet_object_end_t; 70 | typedef union amf_packet_t amf_packet_t; 71 | 72 | struct amf_packet_number_t 73 | { 74 | amf_datatype_t datatype; 75 | double value; 76 | }; 77 | 78 | struct amf_packet_boolean_t 79 | { 80 | amf_datatype_t datatype; 81 | int value; 82 | }; 83 | 84 | struct amf_packet_string_t 85 | { 86 | amf_datatype_t datatype; 87 | char *value; 88 | }; 89 | 90 | struct amf_packet_object_property_t 91 | { 92 | char *key; 93 | amf_packet_t *value; 94 | amf_packet_object_property_t *next; 95 | }; 96 | 97 | struct amf_packet_object_t 98 | { 99 | amf_datatype_t datatype; 100 | amf_packet_object_property_t *properties; 101 | }; 102 | 103 | struct amf_packet_null_t 104 | { 105 | amf_datatype_t datatype; 106 | }; 107 | 108 | struct amf_packet_undefined_t 109 | { 110 | amf_datatype_t datatype; 111 | }; 112 | 113 | struct amf_packet_ecma_array_t 114 | { 115 | amf_datatype_t datatype; 116 | int num; 117 | amf_packet_object_property_t *properties; 118 | }; 119 | 120 | struct amf_packet_object_end_t 121 | { 122 | amf_datatype_t datatype; 123 | }; 124 | 125 | union amf_packet_t 126 | { 127 | amf_datatype_t datatype; 128 | amf_packet_number_t number; 129 | amf_packet_boolean_t boolean; 130 | amf_packet_string_t string; 131 | amf_packet_object_t object; 132 | amf_packet_null_t null; 133 | amf_packet_undefined_t undefined; 134 | amf_packet_ecma_array_t ecma_array; 135 | amf_packet_object_end_t object_end; 136 | }; 137 | 138 | 139 | extern amf_packet_t *amf_packet_analyze_data( 140 | unsigned char *data, size_t data_size, size_t *packet_size); 141 | 142 | extern amf_packet_t *amf_packet_create_number(double number); 143 | extern amf_packet_t *amf_packet_create_boolean(int boolean); 144 | extern amf_packet_t *amf_packet_create_string(const char *string); 145 | extern amf_packet_t *amf_packet_create_null(void); 146 | extern amf_packet_t *amf_packet_create_undefined(void); 147 | extern amf_packet_t *amf_packet_create_object(void); 148 | extern rtmp_result_t amf_packet_add_property_to_object( 149 | amf_packet_t *amf, const char *key, amf_packet_t *value); 150 | 151 | extern size_t amf_packet_get_size(amf_packet_t *amf); 152 | 153 | extern size_t amf_packet_serialize( 154 | amf_packet_t *amf, 155 | unsigned char *output_buffer, size_t output_buffer_size); 156 | 157 | extern void amf_packet_free(amf_packet_t *amf); 158 | 159 | 160 | /* Ends C function definitions when using C++ */ 161 | #ifdef __cplusplus 162 | } 163 | #endif 164 | 165 | 166 | #endif 167 | -------------------------------------------------------------------------------- /rtmp.h: -------------------------------------------------------------------------------- 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 | #ifndef _rtmp_H_ 24 | #define _rtmp_H_ 25 | 26 | 27 | #ifdef MACOS_OPENTRANSPORT 28 | #include 29 | #include 30 | #else 31 | #if defined(__WIN32__) || defined(WIN32) 32 | #define __USE_W32_SOCKETS 33 | #include 34 | #ifdef __CYGWIN__ 35 | #include 36 | #endif 37 | #else /* UNIX */ 38 | #ifdef __OS2__ 39 | #include 40 | #include 41 | #endif 42 | #include 43 | #include 44 | #include 45 | #include 46 | #ifndef __BEOS__ 47 | #include 48 | #endif 49 | #ifdef linux /* FIXME: what other platforms have this? */ 50 | #include 51 | #endif 52 | #include 53 | #include 54 | #endif /* WIN32 */ 55 | #endif /* Open Transport */ 56 | 57 | 58 | /* Set up for C function definitions, even when using C++ */ 59 | #ifdef __cplusplus 60 | extern "C" { 61 | #endif 62 | 63 | #define RTMP_HANDSHAKE_SIZE 1536 64 | #define RTMP_BUFFER_SIZE 4096 65 | 66 | 67 | typedef struct rtmp_server_client_t rtmp_server_client_t; 68 | 69 | struct rtmp_server_client_t 70 | { 71 | unsigned int conn_sock; 72 | struct sockaddr_in conn_sockaddr; 73 | unsigned char received_buffer[RTMP_BUFFER_SIZE]; 74 | size_t received_size; 75 | unsigned char will_send_buffer[RTMP_BUFFER_SIZE]; 76 | size_t will_send_size; 77 | size_t amf_chunk_size; 78 | void *data; 79 | void (*process_message)(rtmp_server_client_t *rsc); 80 | unsigned char handshake[RTMP_HANDSHAKE_SIZE]; 81 | rtmp_server_client_t *prev; 82 | rtmp_server_client_t *next; 83 | }; 84 | 85 | typedef struct rtmp_server_t rtmp_server_t; 86 | 87 | struct rtmp_server_t 88 | { 89 | int conn_sock; 90 | struct sockaddr_in conn_sockaddr; 91 | int stand_by_socket; 92 | rtmp_server_client_t *client_working; 93 | rtmp_server_client_t *client_pool; 94 | }; 95 | 96 | 97 | extern rtmp_server_t *rtmp_server_create(unsigned short port_number); 98 | extern void rtmp_server_process_message(rtmp_server_t *rs); 99 | extern void rtmp_server_free(rtmp_server_t *rs); 100 | 101 | 102 | #define DEFAULT_AMF_CHUNK_SIZE 128 103 | 104 | 105 | typedef enum rtmp_result rtmp_result_t; 106 | 107 | enum rtmp_result 108 | { 109 | RTMP_SUCCESS, 110 | RTMP_ERROR_UNKNOWN, 111 | RTMP_ERROR_BUFFER_OVERFLOW, 112 | RTMP_ERROR_BROKEN_PACKET, 113 | RTMP_ERROR_DIVIDED_PACKET, 114 | RTMP_ERROR_MEMORY_ALLOCATION, 115 | RTMP_ERROR_LACKED_MEMORY, 116 | RTMP_ERROR_DISCONNECTED, 117 | }; 118 | 119 | 120 | typedef struct rtmp_event_t rtmp_event_t; 121 | 122 | struct rtmp_event_t 123 | { 124 | char *code; 125 | char *level; 126 | rtmp_event_t *next; 127 | }; 128 | 129 | typedef struct rtmp_client_t rtmp_client_t; 130 | 131 | struct rtmp_client_t 132 | { 133 | int conn_sock; 134 | unsigned char received_buffer[RTMP_BUFFER_SIZE]; 135 | size_t received_size; 136 | unsigned char will_send_buffer[RTMP_BUFFER_SIZE]; 137 | size_t will_send_size; 138 | void (*process_message)(rtmp_client_t *client); 139 | void *data; 140 | char *url; 141 | char *protocol; 142 | char *host; 143 | int port_number; 144 | char *path; 145 | size_t amf_chunk_size; 146 | unsigned char handshake[RTMP_HANDSHAKE_SIZE]; 147 | long message_number; 148 | rtmp_event_t *events; 149 | }; 150 | 151 | 152 | rtmp_client_t *rtmp_client_create(const char *url); 153 | extern void rtmp_client_free(rtmp_client_t *client); 154 | 155 | extern rtmp_event_t *rtmp_client_get_event(rtmp_client_t *client); 156 | extern void rtmp_client_delete_event(rtmp_client_t *client); 157 | 158 | extern void rtmp_client_connect( 159 | rtmp_client_t *client 160 | /* FIXME: take URL */); 161 | extern void rtmp_client_create_stream(rtmp_client_t *client); 162 | extern void rtmp_client_play(rtmp_client_t *client, const char *file_name); 163 | extern void rtmp_server_client_send_server_bandwidth(rtmp_server_client_t *rsc); 164 | extern void rtmp_server_client_send_client_bandwidth(rtmp_server_client_t *rsc); 165 | extern void rtmp_server_client_send_ping(rtmp_server_client_t *rsc); 166 | extern void rtmp_server_client_send_chunk_size(rtmp_server_client_t *rsc); 167 | extern void rtmp_server_client_send_connect_result( 168 | rtmp_server_client_t *rsc, double number); 169 | extern void rtmp_server_client_send_create_stream_result( 170 | rtmp_server_client_t *rsc, double number); 171 | extern void rtmp_server_client_send_play_result_error( 172 | rtmp_server_client_t *rsc, double number); 173 | extern void rtmp_server_client_send_play_result_success( 174 | rtmp_server_client_t *rsc, double number); 175 | 176 | extern void rtmp_client_process_message(rtmp_client_t *client); 177 | 178 | 179 | /* Ends C function definitions when using C++ */ 180 | #ifdef __cplusplus 181 | } 182 | #endif 183 | 184 | 185 | #endif 186 | -------------------------------------------------------------------------------- /data_rw.c: -------------------------------------------------------------------------------- 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 | #include 25 | #include 26 | 27 | #include "data_rw.h" 28 | 29 | 30 | typedef union endian_checker endian_checker_t; 31 | 32 | union endian_checker 33 | { 34 | long endian_value; 35 | char byte_array[sizeof(long)]; 36 | }; 37 | 38 | static endian_checker_t EndianChecker = {1}; 39 | 40 | 41 | int is_little_endian(void) 42 | { 43 | return EndianChecker.byte_array[0]; 44 | } 45 | 46 | 47 | int is_big_endian(void) 48 | { 49 | return !EndianChecker.byte_array[0]; 50 | } 51 | 52 | 53 | int read_be16int(unsigned char *data) 54 | { 55 | int value; 56 | 57 | if (is_little_endian()) { 58 | value = data[0] << 8; 59 | value += data[1]; 60 | } else { 61 | value = data[0]; 62 | value += data[1] << 8; 63 | } 64 | return value; 65 | } 66 | 67 | 68 | int read_be24int(unsigned char *data) 69 | { 70 | int value; 71 | 72 | if (is_little_endian()) { 73 | value = data[0] << 16; 74 | value += data[1] << 8; 75 | value += data[2]; 76 | } else { 77 | value = data[0]; 78 | value += data[1] << 8; 79 | value += data[2] << 16; 80 | } 81 | return value; 82 | } 83 | 84 | 85 | int read_le32int(unsigned char *data) 86 | { 87 | int value; 88 | 89 | if (is_little_endian()) { 90 | value = data[0]; 91 | value += data[1] << 8; 92 | value += data[2] << 16; 93 | value += data[3] << 24; 94 | } else { 95 | value = data[0] << 24; 96 | value += data[1] << 16; 97 | value += data[2] << 8; 98 | value += data[3]; 99 | } 100 | return value; 101 | } 102 | 103 | 104 | int read_be32int(unsigned char *data) 105 | { 106 | int value; 107 | 108 | if (is_little_endian()) { 109 | value = data[0] << 24; 110 | value += data[1] << 16; 111 | value += data[2] << 8; 112 | value += data[3]; 113 | } else { 114 | value = data[0]; 115 | value += data[1] << 8; 116 | value += data[2] << 16; 117 | value += data[3] << 24; 118 | } 119 | return value; 120 | } 121 | 122 | 123 | double read_be64double(unsigned char *data) 124 | { 125 | double value; 126 | unsigned char number_data[8]; 127 | 128 | if (is_little_endian()) { 129 | number_data[0] = data[7]; 130 | number_data[1] = data[6]; 131 | number_data[2] = data[5]; 132 | number_data[3] = data[4]; 133 | number_data[4] = data[3]; 134 | number_data[5] = data[2]; 135 | number_data[6] = data[1]; 136 | number_data[7] = data[0]; 137 | memmove(&value, number_data, 8); 138 | } else { 139 | memmove(&value, data, 8); 140 | } 141 | 142 | return value; 143 | } 144 | 145 | 146 | void write_be64double(unsigned char *data, double value) 147 | { 148 | unsigned char number_data[8]; 149 | 150 | if (is_little_endian()) { 151 | memmove(number_data, &value, 8); 152 | data[0] = number_data[7]; 153 | data[1] = number_data[6]; 154 | data[2] = number_data[5]; 155 | data[3] = number_data[4]; 156 | data[4] = number_data[3]; 157 | data[5] = number_data[2]; 158 | data[6] = number_data[1]; 159 | data[7] = number_data[0]; 160 | } else { 161 | memmove(data, &value, 8); 162 | } 163 | } 164 | 165 | 166 | void write_le32int(unsigned char *data, int value) 167 | { 168 | if (is_little_endian()) { 169 | data[0] = value & 0xFF; 170 | data[1] = (value >> 8) & 0xFF; 171 | data[2] = (value >> 16) & 0xFF; 172 | data[3] = value >> 24; 173 | } else { 174 | data[0] = value >> 24; 175 | data[1] = (value >> 16) & 0xFF; 176 | data[2] = (value >> 8) & 0xFF; 177 | data[3] = value & 0xFF; 178 | } 179 | } 180 | 181 | 182 | void write_be32int(unsigned char *data, int value) 183 | { 184 | if (is_little_endian()) { 185 | data[0] = value >> 24; 186 | data[1] = (value >> 16) & 0xFF; 187 | data[2] = (value >> 8) & 0xFF; 188 | data[3] = value & 0xFF; 189 | } else { 190 | data[0] = value & 0xFF; 191 | data[1] = (value >> 8) & 0xFF; 192 | data[2] = (value >> 16) & 0xFF; 193 | data[3] = value >> 24; 194 | } 195 | } 196 | 197 | 198 | void write_be24int(unsigned char *data, int value) 199 | { 200 | if (is_little_endian()) { 201 | data[0] = value >> 16; 202 | data[1] = (value >> 8) & 0xFF; 203 | data[2] = value & 0xFF; 204 | } else { 205 | memmove(data, &value, 3); 206 | } 207 | } 208 | 209 | 210 | void write_be16int(unsigned char *data, int value) 211 | { 212 | unsigned char int_data[2]; 213 | 214 | if (is_little_endian()) { 215 | memmove(int_data, &value, 2); 216 | data[0] = int_data[1]; 217 | data[1] = int_data[0]; 218 | } else { 219 | memmove(data, &value, 2); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /rtmp_packet.c: -------------------------------------------------------------------------------- 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 | #include 25 | #include 26 | #include 27 | 28 | #include "rtmp_packet.h" 29 | #include "amf_packet.h" 30 | #include "data_rw.h" 31 | 32 | 33 | void rtmp_packet_retrieve_body( 34 | unsigned char *body_output_buffer, 35 | unsigned char *body_chunks, size_t rtmp_body_size, 36 | size_t amf_chunk_size, int chunk_delimiter_num); 37 | static size_t rtmp_packet_insert_amf_chunk_header( 38 | unsigned char *amf_buffer, 39 | size_t amf_size, 40 | size_t amf_chunk_size, 41 | unsigned char *output_buffer); 42 | static rtmp_result_t rtmp_packet_amf_analyze( 43 | rtmp_packet_t *packet, 44 | unsigned char *amf_packets_buffer, size_t rtmp_body_size); 45 | static rtmp_result_t rtmp_packet_process_body( 46 | rtmp_packet_t *packet, unsigned char *body_buffer, size_t body_size); 47 | static rtmp_result_t rtmp_packet_serialize_amf( 48 | rtmp_packet_t *packet, 49 | size_t *total_serialized_size, 50 | size_t amf_size, size_t amf_chunk_size, size_t header_size, 51 | unsigned char *output_buffer, size_t output_buffer_size); 52 | static rtmp_result_t rtmp_packet_serialize_data( 53 | rtmp_packet_t *packet, 54 | size_t *total_serialized_size, 55 | size_t amf_chunk_size, size_t header_size, 56 | unsigned char *output_buffer, size_t output_buffer_size); 57 | 58 | 59 | rtmp_packet_t *rtmp_packet_create(void) 60 | { 61 | rtmp_packet_t *packet; 62 | 63 | packet = (rtmp_packet_t*)malloc(sizeof(rtmp_packet_t)); 64 | packet->inner_amf_packets = NULL; 65 | packet->body_data = NULL; 66 | rtmp_packet_cleanup(packet); 67 | 68 | return packet; 69 | } 70 | 71 | 72 | void rtmp_packet_cleanup(rtmp_packet_t *packet) 73 | { 74 | rtmp_packet_inner_amf_t *inner_amf; 75 | rtmp_packet_inner_amf_t *next; 76 | 77 | packet->object_id = 0; 78 | packet->timer = 0; 79 | packet->data_type = 0; 80 | packet->stream_id = 0; 81 | inner_amf = packet->inner_amf_packets; 82 | while (inner_amf) { 83 | next = inner_amf->next; 84 | amf_packet_free(inner_amf->amf); 85 | free(inner_amf); 86 | inner_amf = next; 87 | } 88 | packet->inner_amf_packets = NULL; 89 | if (packet->body_data) { 90 | free(packet->body_data); 91 | packet->body_data = NULL; 92 | } 93 | } 94 | 95 | 96 | void rtmp_packet_retrieve_body( 97 | unsigned char *body_output_buffer, 98 | unsigned char *body_chunks, size_t rtmp_body_size, 99 | size_t amf_chunk_size, int chunk_delimiter_num) 100 | { 101 | size_t body_size_count; 102 | size_t body_moved_size; 103 | size_t rtmp_body_with_chunk_delimiter_size; 104 | 105 | body_size_count = 0; 106 | body_moved_size = 0; 107 | rtmp_body_with_chunk_delimiter_size = 108 | rtmp_body_size + chunk_delimiter_num; 109 | while (body_size_count < rtmp_body_with_chunk_delimiter_size) { 110 | size_t rest = rtmp_body_with_chunk_delimiter_size - body_size_count; 111 | if (rest < amf_chunk_size) { 112 | memmove( 113 | body_output_buffer + body_moved_size, 114 | body_chunks + body_size_count, 115 | rest); 116 | } else { 117 | memmove( 118 | body_output_buffer + body_moved_size, 119 | body_chunks + body_size_count, 120 | amf_chunk_size); 121 | } 122 | body_moved_size += amf_chunk_size; 123 | body_size_count += amf_chunk_size + 1; /* 1 is delimiter(0xC3) */ 124 | } 125 | } 126 | 127 | 128 | rtmp_result_t rtmp_packet_analyze_data( 129 | rtmp_packet_t *packet, 130 | unsigned char *data, size_t data_size, 131 | size_t amf_chunk_size, 132 | size_t *packet_size) 133 | { 134 | int header_size_magic; 135 | int header_size; 136 | size_t rtmp_body_size; 137 | unsigned char *body_chunks; 138 | rtmp_result_t amf_ret; 139 | int chunk_delimiter_num; 140 | size_t amf_size_count; 141 | unsigned char *body_buffer; 142 | 143 | if (data_size == 0) { 144 | *packet_size = 0; 145 | return RTMP_ERROR_UNKNOWN; 146 | } 147 | 148 | rtmp_packet_cleanup(packet); 149 | 150 | #ifdef DEBUG 151 | printf("RTMP packet start\n"); 152 | #endif 153 | header_size_magic = data[0] >> 6; 154 | #ifdef DEBUG 155 | printf("header_size_magic: %d\n", header_size_magic); 156 | #endif 157 | packet->object_id = data[0] & 0x3F; 158 | #ifdef DEBUG 159 | printf("object_id: %d\n", packet->object_id); 160 | #endif 161 | if (header_size_magic == HEADER_MAGIC_01) { 162 | header_size = 1; 163 | *packet_size = 1; 164 | if (data_size < 1) { 165 | *packet_size = 0; 166 | return RTMP_ERROR_DIVIDED_PACKET; 167 | } 168 | return RTMP_SUCCESS; 169 | } 170 | if (data_size < 4) { 171 | *packet_size = 0; 172 | return RTMP_ERROR_DIVIDED_PACKET; 173 | } 174 | packet->timer = read_be24int(data + 1); 175 | #ifdef DEBUG 176 | printf("timer: %ld\n", packet->timer); 177 | #endif 178 | if (header_size_magic == HEADER_MAGIC_04) { 179 | header_size = 4; 180 | *packet_size = 4; 181 | return RTMP_SUCCESS; 182 | } 183 | if (data_size < 8) { 184 | *packet_size = 0; 185 | return RTMP_ERROR_DIVIDED_PACKET; 186 | } 187 | rtmp_body_size = read_be24int(data + 4); 188 | #ifdef DEBUG 189 | printf("rtmp_body_size: %d\n", rtmp_body_size); 190 | #endif 191 | chunk_delimiter_num = 0; 192 | amf_size_count = amf_chunk_size; 193 | while (amf_size_count < rtmp_body_size) { 194 | chunk_delimiter_num++; 195 | amf_size_count += amf_chunk_size; 196 | } 197 | #ifdef DEBUG 198 | printf("chunk_delimiter_num: %d\n", chunk_delimiter_num); 199 | #endif 200 | packet->data_type = data[7]; 201 | #ifdef DEBUG 202 | printf("data_type: %02x\n", packet->data_type); 203 | #endif 204 | if (header_size_magic == HEADER_MAGIC_08) { 205 | header_size = 8; 206 | if (rtmp_body_size == 0) { 207 | *packet_size = 8; 208 | return RTMP_SUCCESS; 209 | } 210 | body_chunks = data + 8; 211 | } else if (header_size_magic == HEADER_MAGIC_12) { 212 | if (data_size < 12) { 213 | *packet_size = 0; 214 | return RTMP_ERROR_DIVIDED_PACKET; 215 | } 216 | header_size = 12; 217 | packet->stream_id = read_le32int(data + 8); 218 | if (rtmp_body_size == 0) { 219 | *packet_size = 12; 220 | return RTMP_SUCCESS; 221 | } 222 | body_chunks = data + 12; 223 | } 224 | if (header_size + rtmp_body_size > data_size) { 225 | *packet_size = 0; 226 | return RTMP_ERROR_BROKEN_PACKET; 227 | } 228 | *packet_size = header_size + rtmp_body_size + chunk_delimiter_num; 229 | 230 | body_buffer = (unsigned char*)malloc(rtmp_body_size); 231 | printf("allocated %d\n", rtmp_body_size); 232 | if (body_buffer == NULL) { 233 | return RTMP_ERROR_MEMORY_ALLOCATION; 234 | } 235 | rtmp_packet_retrieve_body( 236 | body_buffer, body_chunks, rtmp_body_size, 237 | amf_chunk_size, chunk_delimiter_num); 238 | amf_ret = rtmp_packet_process_body(packet, body_buffer, rtmp_body_size); 239 | 240 | if (amf_ret != RTMP_SUCCESS) { 241 | return amf_ret; 242 | } 243 | 244 | #ifdef DEBUG 245 | printf("RTMP packet end\n"); 246 | #endif 247 | return RTMP_SUCCESS; 248 | } 249 | 250 | 251 | rtmp_result_t rtmp_packet_process_body( 252 | rtmp_packet_t *packet, unsigned char *body_buffer, size_t body_size) 253 | { 254 | rtmp_result_t amf_ret; 255 | 256 | switch (packet->data_type) { 257 | case RTMP_DATATYPE_CHUNK_SIZE: 258 | packet->body_type = RTMP_BODY_TYPE_DATA; 259 | packet->body_data = body_buffer; 260 | packet->body_data_length = body_size; 261 | break; 262 | case RTMP_DATATYPE_UNKNOWN_0: 263 | case RTMP_DATATYPE_BYTES_READ: 264 | case RTMP_DATATYPE_PING: 265 | case RTMP_DATATYPE_SERVER_BW: 266 | case RTMP_DATATYPE_CLIENT_BW: 267 | case RTMP_DATATYPE_UNKNOWN_1: 268 | case RTMP_DATATYPE_AUDIO_DATA: 269 | case RTMP_DATATYPE_VIDEO_DATA: 270 | case RTMP_DATATYPE_UNKNOWN_2: 271 | case RTMP_DATATYPE_UNKNOWN_3: 272 | case RTMP_DATATYPE_UNKNOWN_4: 273 | case RTMP_DATATYPE_UNKNOWN_5: 274 | case RTMP_DATATYPE_UNKNOWN_6: 275 | case RTMP_DATATYPE_FLEX_STREAM: 276 | case RTMP_DATATYPE_FLEX_SHARED_OBJECT: 277 | case RTMP_DATATYPE_MESSAGE: 278 | case RTMP_DATATYPE_NOTIFY: 279 | case RTMP_DATATYPE_SHARED_OBJECT: 280 | packet->body_type = RTMP_BODY_TYPE_DATA; 281 | packet->body_data = body_buffer; 282 | packet->body_data_length = body_size; 283 | break; 284 | case RTMP_DATATYPE_INVOKE: 285 | amf_ret = rtmp_packet_amf_analyze(packet, body_buffer, body_size); 286 | free(body_buffer); 287 | if (amf_ret == RTMP_SUCCESS) { 288 | packet->body_type = RTMP_BODY_TYPE_AMF; 289 | } 290 | return amf_ret; 291 | case RTMP_DATATYPE_FLV_DATA: 292 | packet->body_type = RTMP_BODY_TYPE_DATA; 293 | packet->body_data = body_buffer; 294 | break; 295 | } 296 | #ifdef DEBUG 297 | if (packet->body_type == RTMP_BODY_TYPE_DATA) { 298 | int i; 299 | printf("RTMP data start: %d\n", (int)packet->body_data_length); 300 | for (i = 0; i < (int)packet->body_data_length; ++i) { 301 | printf("%02x ", packet->body_data[i]); 302 | } 303 | printf("\n"); 304 | } 305 | #endif 306 | return RTMP_SUCCESS; 307 | } 308 | 309 | 310 | rtmp_result_t rtmp_packet_amf_analyze( 311 | rtmp_packet_t *packet, 312 | unsigned char *amf_buffer, size_t amf_buffer_size) 313 | { 314 | size_t buffer_position; 315 | amf_packet_t *amf; 316 | rtmp_packet_inner_amf_t *inner_amf; 317 | rtmp_packet_inner_amf_t *prev_inner_amf; 318 | size_t analyzed_amf_packet_size; 319 | 320 | buffer_position = 0; 321 | packet->inner_amf_packets = NULL; 322 | while (buffer_position < amf_buffer_size) { 323 | inner_amf = (rtmp_packet_inner_amf_t*)malloc( 324 | sizeof(rtmp_packet_inner_amf_t)); 325 | if (inner_amf == NULL) { 326 | return RTMP_ERROR_MEMORY_ALLOCATION; 327 | } 328 | amf = amf_packet_analyze_data( 329 | amf_buffer + buffer_position, 330 | amf_buffer_size - buffer_position, 331 | &analyzed_amf_packet_size); 332 | buffer_position += analyzed_amf_packet_size; 333 | if (amf == NULL) { 334 | free(inner_amf); 335 | return RTMP_ERROR_BROKEN_PACKET; 336 | } 337 | inner_amf->amf = amf; 338 | inner_amf->next = NULL; 339 | if (packet->inner_amf_packets == NULL) { 340 | packet->inner_amf_packets = inner_amf; 341 | } else { 342 | prev_inner_amf->next = inner_amf; 343 | } 344 | prev_inner_amf = inner_amf; 345 | } 346 | 347 | return RTMP_SUCCESS; 348 | } 349 | 350 | 351 | rtmp_result_t rtmp_packet_serialize( 352 | rtmp_packet_t *packet, 353 | unsigned char *output_buffer, size_t output_buffer_size, 354 | size_t amf_chunk_size, 355 | size_t *packet_size) 356 | { 357 | size_t header_size; 358 | size_t amf_size; 359 | size_t total_serialized_size; 360 | rtmp_packet_inner_amf_t *inner_amf; 361 | rtmp_result_t result; 362 | 363 | header_size = 12; 364 | 365 | #ifdef DEBUG 366 | printf("RTMP packet serialize start\n"); 367 | #endif 368 | if (header_size > output_buffer_size) { 369 | *packet_size = 0; 370 | return RTMP_ERROR_LACKED_MEMORY; 371 | } 372 | 373 | output_buffer[0] = (HEADER_MAGIC_12 << 6) + packet->object_id; 374 | write_be24int(output_buffer + 1, packet->timer); 375 | 376 | if (packet->body_type == RTMP_BODY_TYPE_AMF) { 377 | amf_size = 0; 378 | inner_amf = packet->inner_amf_packets; 379 | while (inner_amf) { 380 | amf_size += amf_packet_get_size(inner_amf->amf); 381 | inner_amf = inner_amf->next; 382 | } 383 | write_be24int(output_buffer + 4, (int)amf_size); 384 | } else if (packet->body_type == RTMP_BODY_TYPE_DATA) { 385 | amf_size = 0; 386 | write_be24int(output_buffer + 4, (int)packet->body_data_length); 387 | } 388 | 389 | #ifdef DEBUG 390 | printf("data_type: %02x\n", packet->data_type); 391 | #endif 392 | 393 | output_buffer[7] = packet->data_type; 394 | write_le32int(output_buffer + 8, packet->stream_id); 395 | total_serialized_size = header_size; 396 | 397 | if (packet->body_type == RTMP_BODY_TYPE_AMF) { 398 | result = rtmp_packet_serialize_amf( 399 | packet, 400 | &total_serialized_size, 401 | amf_size, amf_chunk_size, header_size, 402 | output_buffer, output_buffer_size); 403 | if (result != RTMP_SUCCESS) { 404 | *packet_size = 0; 405 | return result; 406 | } 407 | } else if (packet->body_type == RTMP_BODY_TYPE_DATA) { 408 | result = rtmp_packet_serialize_data( 409 | packet, 410 | &total_serialized_size, 411 | amf_chunk_size, header_size, 412 | output_buffer, output_buffer_size); 413 | if (result != RTMP_SUCCESS) { 414 | *packet_size = 0; 415 | return result; 416 | } 417 | } 418 | 419 | *packet_size = total_serialized_size; 420 | #ifdef DEBUG 421 | printf("RTMP packet serialize end\n"); 422 | #endif 423 | 424 | return RTMP_SUCCESS; 425 | } 426 | 427 | 428 | rtmp_result_t rtmp_packet_serialize_amf( 429 | rtmp_packet_t *packet, 430 | size_t *total_serialized_size, 431 | size_t amf_size, size_t amf_chunk_size, size_t header_size, 432 | unsigned char *output_buffer, size_t output_buffer_size) 433 | { 434 | size_t amf_with_chunk_header_size; 435 | unsigned char *amf_buffer; 436 | size_t total_serialized_amf_size; 437 | size_t serialized_amf_size; 438 | int chunk_delimiter_num; 439 | rtmp_packet_inner_amf_t *inner_amf; 440 | 441 | chunk_delimiter_num = (int)(amf_size / amf_chunk_size); 442 | amf_with_chunk_header_size = amf_size + chunk_delimiter_num; 443 | if (header_size + amf_with_chunk_header_size > output_buffer_size) { 444 | return RTMP_ERROR_LACKED_MEMORY; 445 | } 446 | 447 | amf_buffer = (unsigned char*)malloc(amf_size); 448 | 449 | #ifdef DEBUG 450 | printf("AMF serialize start\n"); 451 | #endif 452 | total_serialized_amf_size = 0; 453 | inner_amf = packet->inner_amf_packets; 454 | while (inner_amf) { 455 | serialized_amf_size = amf_packet_serialize( 456 | inner_amf->amf, 457 | amf_buffer + total_serialized_amf_size, 458 | amf_size - total_serialized_amf_size); 459 | if (serialized_amf_size == 0) { 460 | #ifdef DEBUG 461 | printf("AMF serialize error!\n"); 462 | #endif 463 | } 464 | total_serialized_amf_size += serialized_amf_size; 465 | inner_amf = inner_amf->next; 466 | } 467 | #ifdef DEBUG 468 | printf("AMF serialize end\n"); 469 | #endif 470 | 471 | *total_serialized_size += rtmp_packet_insert_amf_chunk_header( 472 | amf_buffer, amf_size, 473 | amf_chunk_size, 474 | output_buffer + *total_serialized_size); 475 | 476 | free(amf_buffer); 477 | 478 | return RTMP_SUCCESS; 479 | } 480 | 481 | 482 | rtmp_result_t rtmp_packet_serialize_data( 483 | rtmp_packet_t *packet, 484 | size_t *total_serialized_size, 485 | size_t amf_chunk_size, size_t header_size, 486 | unsigned char *output_buffer, size_t output_buffer_size) 487 | { 488 | size_t amf_with_chunk_header_size; 489 | int chunk_delimiter_num; 490 | 491 | chunk_delimiter_num = (int)(packet->body_data_length / amf_chunk_size); 492 | amf_with_chunk_header_size = packet->body_data_length + chunk_delimiter_num; 493 | if (header_size + amf_with_chunk_header_size > output_buffer_size) { 494 | return RTMP_ERROR_LACKED_MEMORY; 495 | } 496 | 497 | #ifdef DEBUG 498 | printf("RTMP data start\n"); 499 | { 500 | int i; 501 | for (i = 0; i < (int)packet->body_data_length; ++i) { 502 | printf("%02x ", packet->body_data[i]); 503 | } 504 | printf("\n"); 505 | } 506 | #endif 507 | *total_serialized_size += rtmp_packet_insert_amf_chunk_header( 508 | packet->body_data, packet->body_data_length, 509 | amf_chunk_size, 510 | output_buffer + *total_serialized_size); 511 | #ifdef DEBUG 512 | printf("RTMP data end\n"); 513 | #endif 514 | 515 | return RTMP_SUCCESS; 516 | } 517 | 518 | 519 | size_t rtmp_packet_insert_amf_chunk_header( 520 | unsigned char *amf_buffer, 521 | size_t amf_size, 522 | size_t amf_chunk_size, 523 | unsigned char *output_buffer) 524 | { 525 | size_t amf_buffer_count; 526 | size_t total_serialized_size; 527 | size_t remaining_chunk_size; 528 | 529 | total_serialized_size = 0; 530 | amf_buffer_count = 0; 531 | remaining_chunk_size = amf_size - amf_buffer_count; 532 | while (remaining_chunk_size > 0) { 533 | remaining_chunk_size = amf_size - amf_buffer_count; 534 | if (remaining_chunk_size > amf_chunk_size) { 535 | memmove( 536 | output_buffer + total_serialized_size, 537 | amf_buffer + amf_buffer_count, 538 | amf_chunk_size); 539 | amf_buffer_count += amf_chunk_size; 540 | total_serialized_size += amf_chunk_size; 541 | output_buffer[total_serialized_size] = 0xC3; 542 | total_serialized_size++; 543 | } else { 544 | memmove( 545 | output_buffer + total_serialized_size, 546 | amf_buffer + amf_buffer_count, 547 | remaining_chunk_size); 548 | amf_buffer_count += remaining_chunk_size; 549 | total_serialized_size += remaining_chunk_size; 550 | } 551 | } 552 | return total_serialized_size; 553 | } 554 | 555 | 556 | void rtmp_packet_free(rtmp_packet_t *packet) 557 | { 558 | rtmp_packet_inner_amf_t *inner_amf; 559 | rtmp_packet_inner_amf_t *next; 560 | 561 | inner_amf = packet->inner_amf_packets; 562 | while (inner_amf) { 563 | next = inner_amf->next; 564 | amf_packet_free(inner_amf->amf); 565 | free(inner_amf); 566 | inner_amf = next; 567 | } 568 | if (packet->body_data) { 569 | free(packet->body_data); 570 | } 571 | free(packet); 572 | } 573 | 574 | 575 | rtmp_result_t rtmp_packet_add_amf( 576 | rtmp_packet_t *packet, 577 | amf_packet_t *amf) 578 | { 579 | rtmp_packet_inner_amf_t *inner_amf; 580 | rtmp_packet_inner_amf_t *last_inner_amf; 581 | 582 | inner_amf = (rtmp_packet_inner_amf_t*)malloc( 583 | sizeof(rtmp_packet_inner_amf_t)); 584 | if (inner_amf == NULL) { 585 | return RTMP_ERROR_MEMORY_ALLOCATION; 586 | } 587 | inner_amf->amf = amf; 588 | inner_amf->next = NULL; 589 | if (packet->inner_amf_packets == NULL) { 590 | packet->inner_amf_packets = inner_amf; 591 | } else { 592 | last_inner_amf = packet->inner_amf_packets; 593 | while (last_inner_amf->next) { 594 | last_inner_amf = last_inner_amf->next; 595 | } 596 | last_inner_amf->next = inner_amf; 597 | } 598 | return RTMP_SUCCESS; 599 | } 600 | 601 | 602 | rtmp_result_t rtmp_packet_allocate_body_data( 603 | rtmp_packet_t *packet, size_t length) 604 | { 605 | if (packet->body_data) { 606 | free(packet->body_data); 607 | } 608 | packet->body_data = (unsigned char*)malloc(length); 609 | if (!packet->body_data) { 610 | packet->body_data_length = 0; 611 | return RTMP_ERROR_MEMORY_ALLOCATION; 612 | } 613 | packet->body_data_length = length; 614 | return RTMP_SUCCESS; 615 | } 616 | 617 | 618 | void rtmp_packet_retrieve_status_info( 619 | rtmp_packet_t *packet, char **code, char **level) 620 | { 621 | rtmp_packet_inner_amf_t *inner_amf; 622 | amf_packet_t *amf; 623 | amf_packet_object_property_t *properties; 624 | 625 | *code = NULL; 626 | *level = NULL; 627 | 628 | inner_amf = packet->inner_amf_packets; 629 | while (inner_amf) { 630 | amf = inner_amf->amf; 631 | if (amf->datatype == AMF_DATATYPE_OBJECT) { 632 | properties = amf->object.properties; 633 | while (properties) { 634 | if (strcmp(properties->key, "code") == 0) { 635 | *code = properties->value->string.value; 636 | } if (strcmp(properties->key, "level") == 0) { 637 | *level = properties->value->string.value; 638 | } 639 | properties = properties->next; 640 | } 641 | } 642 | inner_amf = inner_amf->next; 643 | } 644 | } 645 | -------------------------------------------------------------------------------- /amf_packet.c: -------------------------------------------------------------------------------- 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 | #include 25 | #include 26 | #include 27 | 28 | #include "rtmp.h" 29 | #include "amf_packet.h" 30 | #include "data_rw.h" 31 | 32 | 33 | static amf_packet_t *amf_packet_analyze_number( 34 | unsigned char *raw_data, size_t raw_data_size, size_t *packet_size); 35 | static amf_packet_t *amf_packet_analyze_boolean( 36 | unsigned char *raw_data, size_t raw_data_size, size_t *packet_size); 37 | static amf_packet_t *amf_packet_analyze_string( 38 | unsigned char *raw_data, size_t raw_data_size, size_t *packet_size); 39 | static amf_packet_t *amf_packet_analyze_object( 40 | unsigned char *raw_data, size_t raw_data_size, size_t *packet_size); 41 | static amf_packet_t *amf_packet_analyze_null(void); 42 | static amf_packet_t *amf_packet_analyze_undefined(void); 43 | static amf_packet_t *amf_packet_analyze_ecma_array( 44 | unsigned char *raw_data, size_t raw_data_size, size_t *packet_size); 45 | 46 | static size_t amf_packet_serialize_number( 47 | amf_packet_t *amf, 48 | unsigned char *output_buffer, size_t output_buffer_size); 49 | static size_t amf_packet_serialize_boolean( 50 | amf_packet_t *amf, 51 | unsigned char *output_buffer, size_t output_buffer_size); 52 | static size_t amf_packet_serialize_string( 53 | amf_packet_t *amf, 54 | unsigned char *output_buffer, size_t output_buffer_size); 55 | static size_t amf_packet_serialize_object( 56 | amf_packet_t *amf, 57 | unsigned char *output_buffer, size_t output_buffer_size); 58 | static size_t amf_packet_serialize_null( 59 | amf_packet_t *amf, 60 | unsigned char *output_buffer, size_t output_buffer_size); 61 | static size_t amf_packet_serialize_undefined( 62 | amf_packet_t *amf, 63 | unsigned char *output_buffer, size_t output_buffer_size); 64 | static size_t amf_packet_serialize_ecma_array( 65 | amf_packet_t *amf, 66 | unsigned char *output_buffer, size_t output_buffer_size); 67 | 68 | 69 | amf_packet_t *amf_packet_analyze_data( 70 | unsigned char *raw_data, size_t raw_data_size, size_t *packet_size) 71 | { 72 | amf_packet_t *amf; 73 | 74 | if (raw_data_size < 1) { 75 | return NULL; 76 | } 77 | 78 | if (raw_data[0] == 0x00 && raw_data[1] == 0x00 && raw_data[2] == 0x09) { 79 | /* FIXME: end of object without object start, why? */ 80 | amf = amf_packet_analyze_undefined(); 81 | *packet_size = 3; 82 | return amf; 83 | } 84 | 85 | switch (raw_data[0]) { 86 | case AMF_DATATYPE_NUMBER: 87 | amf = amf_packet_analyze_number( 88 | raw_data, raw_data_size, packet_size); 89 | return amf; 90 | case AMF_DATATYPE_BOOLEAN: 91 | amf = amf_packet_analyze_boolean( 92 | raw_data, raw_data_size, packet_size); 93 | return amf; 94 | case AMF_DATATYPE_STRING: 95 | amf = amf_packet_analyze_string( 96 | raw_data, raw_data_size, packet_size); 97 | return amf; 98 | case AMF_DATATYPE_OBJECT: 99 | amf = amf_packet_analyze_object( 100 | raw_data, raw_data_size, packet_size); 101 | return amf; 102 | case AMF_DATATYPE_NULL: 103 | amf = amf_packet_analyze_null(); 104 | *packet_size = 1; 105 | return amf; 106 | case AMF_DATATYPE_UNDEFINED: 107 | amf = amf_packet_analyze_undefined(); 108 | *packet_size = 1; 109 | return amf; 110 | case AMF_DATATYPE_ECMA_ARRAY: 111 | amf = amf_packet_analyze_ecma_array( 112 | raw_data, raw_data_size, packet_size); 113 | return amf; 114 | case AMF_DATATYPE_OBJECT_END: 115 | 116 | break; 117 | default: 118 | break; 119 | } 120 | 121 | *packet_size = 0; 122 | return NULL; 123 | } 124 | 125 | amf_packet_t *amf_packet_analyze_number( 126 | unsigned char *raw_data, size_t raw_data_size, size_t *packet_size) 127 | { 128 | amf_packet_t *amf; 129 | 130 | if (raw_data_size < 9) { 131 | if (packet_size) { 132 | *packet_size = 1; 133 | } 134 | return NULL; 135 | } 136 | 137 | amf = (amf_packet_t*)malloc(sizeof(amf_packet_number_t)); 138 | if (amf == NULL) { 139 | return NULL; 140 | } 141 | amf->datatype = AMF_DATATYPE_NUMBER; 142 | amf->number.value = read_be64double(raw_data + 1); 143 | #ifdef DEBUG 144 | printf("AMF number: %f\n", amf->number.value); 145 | #endif 146 | if (packet_size) { 147 | *packet_size = 9; 148 | } 149 | return amf; 150 | } 151 | 152 | 153 | amf_packet_t *amf_packet_analyze_boolean( 154 | unsigned char *raw_data, size_t raw_data_size, size_t *packet_size) 155 | { 156 | amf_packet_t *amf; 157 | 158 | if (raw_data_size < 2) { 159 | if (packet_size) { 160 | *packet_size = 0; 161 | } 162 | return NULL; 163 | } 164 | 165 | amf = (amf_packet_t*)malloc(sizeof(amf_packet_boolean_t)); 166 | if (amf == NULL) { 167 | return NULL; 168 | } 169 | amf->datatype = AMF_DATATYPE_BOOLEAN; 170 | 171 | amf->boolean.value = raw_data[1]; 172 | #ifdef DEBUG 173 | printf("AMF boolean: %d\n", amf->boolean.value); 174 | #endif 175 | 176 | if (packet_size) { 177 | *packet_size = 2; 178 | } 179 | 180 | return amf; 181 | } 182 | 183 | amf_packet_t *amf_packet_analyze_string( 184 | unsigned char *raw_data, size_t raw_data_size, size_t *packet_size) 185 | { 186 | amf_packet_t *amf; 187 | size_t string_data_length; 188 | char *string_data; 189 | 190 | if (raw_data_size < 3) { 191 | if (packet_size) { 192 | *packet_size = 0; 193 | } 194 | return NULL; 195 | } 196 | 197 | string_data_length = read_be16int(raw_data + 1); 198 | if (raw_data_size < 3 + string_data_length) { 199 | if (packet_size) { 200 | *packet_size = 0; 201 | } 202 | return NULL; 203 | } 204 | 205 | amf = (amf_packet_t*)malloc(sizeof(amf_packet_string_t)); 206 | if (amf == NULL) { 207 | return NULL; 208 | } 209 | amf->datatype = AMF_DATATYPE_STRING; 210 | 211 | string_data = (char*)malloc(string_data_length + 1); 212 | if (string_data == NULL) { 213 | free(amf); 214 | return NULL; 215 | } 216 | memset(string_data, 0x00, string_data_length + 1); 217 | 218 | memmove(string_data, raw_data + 3, string_data_length); 219 | string_data[string_data_length] = '\0'; 220 | 221 | amf->string.value = string_data; 222 | #ifdef DEBUG 223 | printf("AMF string: %d \"%s\"\n", string_data_length, string_data); 224 | #endif 225 | 226 | if (packet_size) { 227 | /* datatype(1) + length(2) + string */ 228 | *packet_size = 1 + 2 + string_data_length; 229 | } 230 | 231 | return amf; 232 | } 233 | 234 | amf_packet_t *amf_packet_analyze_object( 235 | unsigned char *raw_data, size_t raw_data_size, size_t *packet_size) 236 | { 237 | amf_packet_t *amf; 238 | int raw_data_position; 239 | size_t string_length; 240 | size_t property_packet_size; 241 | amf_packet_object_property_t *property; 242 | amf_packet_object_property_t *previous_property; 243 | 244 | if (raw_data_size < 1) { 245 | return NULL; 246 | } 247 | 248 | amf = (amf_packet_t*)malloc(sizeof(amf_packet_object_t)); 249 | if (amf == NULL) { 250 | return NULL; 251 | } 252 | amf->datatype = AMF_DATATYPE_OBJECT; 253 | 254 | raw_data_position = 1; 255 | amf->object.properties = NULL; 256 | #ifdef DEBUG 257 | printf("AMF object start\n"); 258 | #endif 259 | while (1) { 260 | string_length = read_be16int(raw_data + raw_data_position); 261 | if (string_length == 0 && raw_data[raw_data_position + 2] == 0x09) { 262 | raw_data_position += 3; 263 | break; 264 | } 265 | raw_data_position += 2; 266 | 267 | property = (amf_packet_object_property_t*) 268 | malloc(sizeof(amf_packet_object_property_t)); 269 | if (property == NULL) { 270 | *packet_size = raw_data_position; 271 | return NULL; 272 | } 273 | property->next = NULL; 274 | 275 | property->key = malloc(string_length + 1); 276 | if (property->key == NULL) { 277 | free(property); 278 | *packet_size = raw_data_position; 279 | return NULL; 280 | } 281 | memmove(property->key, raw_data + raw_data_position, string_length); 282 | printf("out\n"); 283 | property->key[string_length] = '\0'; 284 | raw_data_position += string_length; 285 | 286 | #ifdef DEBUG 287 | printf("AMF object key: %d \"%s\"\n", string_length, property->key); 288 | #endif 289 | 290 | property->value = amf_packet_analyze_data( 291 | raw_data + raw_data_position, 292 | raw_data_size - raw_data_position, 293 | &property_packet_size); 294 | raw_data_position += property_packet_size; 295 | 296 | if (amf->object.properties == NULL) { 297 | amf->object.properties = property; 298 | } else { 299 | previous_property->next = property; 300 | } 301 | previous_property = property; 302 | } 303 | #ifdef DEBUG 304 | printf("AMF object end\n"); 305 | #endif 306 | 307 | *packet_size = raw_data_position; 308 | 309 | return amf; 310 | } 311 | 312 | amf_packet_t *amf_packet_analyze_null(void) 313 | { 314 | amf_packet_t *amf; 315 | 316 | amf = (amf_packet_t*)malloc(sizeof(amf_packet_null_t)); 317 | if (amf == NULL) { 318 | return NULL; 319 | } 320 | amf->datatype = AMF_DATATYPE_NULL; 321 | #ifdef DEBUG 322 | printf("AMF null\n"); 323 | #endif 324 | 325 | return amf; 326 | } 327 | 328 | amf_packet_t *amf_packet_analyze_undefined(void) 329 | { 330 | amf_packet_t *amf; 331 | 332 | amf = (amf_packet_t*)malloc(sizeof(amf_packet_undefined_t)); 333 | if (amf == NULL) { 334 | return NULL; 335 | } 336 | amf->datatype = AMF_DATATYPE_UNDEFINED; 337 | #ifdef DEBUG 338 | printf("AMF undefined\n"); 339 | #endif 340 | 341 | return amf; 342 | } 343 | 344 | 345 | amf_packet_t *amf_packet_analyze_ecma_array( 346 | unsigned char *raw_data, size_t raw_data_size, size_t *packet_size) 347 | { 348 | amf_packet_t *amf; 349 | int raw_data_position; 350 | size_t string_length; 351 | int array_num; 352 | int i; 353 | size_t property_packet_size; 354 | amf_packet_object_property_t *property; 355 | amf_packet_object_property_t *previous_property; 356 | 357 | if (raw_data_size < 1) { 358 | return NULL; 359 | } 360 | 361 | amf = (amf_packet_t*)malloc(sizeof(amf_packet_ecma_array_t)); 362 | if (amf == NULL) { 363 | return NULL; 364 | } 365 | amf->datatype = AMF_DATATYPE_ECMA_ARRAY; 366 | 367 | array_num = read_le32int(raw_data + 1); 368 | 369 | raw_data_position = 5; 370 | amf->object.properties = NULL; 371 | #ifdef DEBUG 372 | printf("AMF ecma array start: %d\n", array_num); 373 | #endif 374 | for (i = 0; i < array_num; ++i) { 375 | string_length = read_be16int(raw_data + raw_data_position); 376 | if (string_length == 0 && raw_data[raw_data_position + 2] == 0x09) { 377 | raw_data_position += 3; 378 | break; 379 | } 380 | raw_data_position += 2; 381 | 382 | property = (amf_packet_object_property_t*) 383 | malloc(sizeof(amf_packet_object_property_t)); 384 | if (property == NULL) { 385 | *packet_size = raw_data_position; 386 | return NULL; 387 | } 388 | property->next = NULL; 389 | 390 | property->key = malloc(string_length + 1); 391 | if (property->key == NULL) { 392 | free(property); 393 | *packet_size = raw_data_position; 394 | return NULL; 395 | } 396 | memmove(property->key, raw_data + raw_data_position, string_length); 397 | property->key[string_length] = '\0'; 398 | raw_data_position += string_length; 399 | 400 | #ifdef DEBUG 401 | printf("AMF ecma array key: %d \"%s\"\n", string_length, property->key); 402 | #endif 403 | 404 | property->value = amf_packet_analyze_data( 405 | raw_data + raw_data_position, 406 | raw_data_size - raw_data_position, 407 | &property_packet_size); 408 | raw_data_position += property_packet_size; 409 | 410 | if (amf->ecma_array.properties == NULL) { 411 | amf->ecma_array.properties = property; 412 | } else { 413 | previous_property->next = property; 414 | } 415 | previous_property = property; 416 | } 417 | #ifdef DEBUG 418 | printf("AMF ecma array end\n"); 419 | #endif 420 | 421 | *packet_size = raw_data_position; 422 | 423 | return amf; 424 | } 425 | 426 | 427 | void amf_packet_free(amf_packet_t *amf) 428 | { 429 | amf_packet_object_property_t *property; 430 | amf_packet_object_property_t *next; 431 | 432 | switch (amf->datatype) { 433 | case AMF_DATATYPE_NUMBER: 434 | break; 435 | case AMF_DATATYPE_BOOLEAN: 436 | break; 437 | case AMF_DATATYPE_STRING: 438 | free(amf->string.value); 439 | break; 440 | case AMF_DATATYPE_OBJECT: 441 | property = amf->object.properties; 442 | while (property) { 443 | next = property->next; 444 | free(property->key); 445 | amf_packet_free(property->value); 446 | free(property); 447 | property = next; 448 | } 449 | break; 450 | case AMF_DATATYPE_NULL: 451 | break; 452 | case AMF_DATATYPE_UNDEFINED: 453 | break; 454 | case AMF_DATATYPE_OBJECT_END: 455 | break; 456 | default: 457 | break; 458 | } 459 | 460 | free(amf); 461 | } 462 | 463 | 464 | amf_packet_t *amf_packet_create_number(double number) 465 | { 466 | amf_packet_t *amf; 467 | 468 | amf = (amf_packet_t*)malloc(sizeof(amf_packet_number_t)); 469 | if (amf == NULL) { 470 | return NULL; 471 | } 472 | amf->datatype = AMF_DATATYPE_NUMBER; 473 | amf->number.value = number; 474 | return amf; 475 | } 476 | 477 | 478 | amf_packet_t *amf_packet_create_boolean(int boolean) 479 | { 480 | amf_packet_t *amf; 481 | 482 | amf = (amf_packet_t*)malloc(sizeof(amf_packet_boolean_t)); 483 | if (amf == NULL) { 484 | return NULL; 485 | } 486 | amf->datatype = AMF_DATATYPE_BOOLEAN; 487 | amf->boolean.value = boolean; 488 | return amf; 489 | } 490 | 491 | 492 | amf_packet_t *amf_packet_create_string(const char *string) 493 | { 494 | amf_packet_t *amf; 495 | 496 | amf = (amf_packet_t*)malloc(sizeof(amf_packet_string_t)); 497 | if (amf == NULL) { 498 | return NULL; 499 | } 500 | amf->datatype = AMF_DATATYPE_STRING; 501 | amf->string.value = (char*)malloc(strlen(string) + 1); 502 | if (amf->string.value == NULL) { 503 | free(amf); 504 | } 505 | strcpy(amf->string.value, string); 506 | return amf; 507 | } 508 | 509 | 510 | amf_packet_t *amf_packet_create_object(void) 511 | { 512 | amf_packet_t *amf; 513 | 514 | amf = (amf_packet_t*)malloc(sizeof(amf_packet_object_t)); 515 | if (amf == NULL) { 516 | return NULL; 517 | } 518 | amf->datatype = AMF_DATATYPE_OBJECT; 519 | amf->object.properties = NULL; 520 | return amf; 521 | } 522 | 523 | 524 | rtmp_result_t amf_packet_add_property_to_object( 525 | amf_packet_t *amf, const char *key, amf_packet_t *value) 526 | { 527 | amf_packet_object_property_t *property; 528 | amf_packet_object_property_t *last_property; 529 | 530 | property = (amf_packet_object_property_t*) 531 | malloc(sizeof(amf_packet_object_property_t)); 532 | if (property == NULL) { 533 | return RTMP_ERROR_MEMORY_ALLOCATION; 534 | } 535 | property->key = (char*)malloc(strlen(key) + 1); 536 | if (property->key == NULL) { 537 | free(property); 538 | return RTMP_ERROR_MEMORY_ALLOCATION; 539 | } 540 | strcpy(property->key, key); 541 | property->value = value; 542 | 543 | property->next = NULL; 544 | if (amf->object.properties == NULL) { 545 | amf->object.properties = property; 546 | } else { 547 | last_property = amf->object.properties; 548 | while (last_property->next) { 549 | last_property = last_property->next; 550 | } 551 | last_property->next = property; 552 | } 553 | 554 | return RTMP_SUCCESS; 555 | } 556 | 557 | 558 | amf_packet_t *amf_packet_create_null(void) 559 | { 560 | amf_packet_t *amf; 561 | 562 | amf = (amf_packet_t*)malloc(sizeof(amf_packet_null_t)); 563 | if (amf == NULL) { 564 | return NULL; 565 | } 566 | amf->datatype = AMF_DATATYPE_NULL; 567 | return amf; 568 | } 569 | 570 | 571 | amf_packet_t *amf_packet_create_undefined(void) 572 | { 573 | amf_packet_t *amf; 574 | 575 | amf = (amf_packet_t*)malloc(sizeof(amf_packet_undefined_t)); 576 | if (amf == NULL) { 577 | return NULL; 578 | } 579 | amf->datatype = AMF_DATATYPE_UNDEFINED; 580 | return amf; 581 | } 582 | 583 | 584 | size_t amf_packet_get_size(amf_packet_t *amf) 585 | { 586 | size_t size; 587 | amf_packet_object_property_t *property; 588 | 589 | switch (amf->datatype) { 590 | case AMF_DATATYPE_NUMBER: 591 | /* datatype(1) + number(8) */ 592 | return 9; 593 | case AMF_DATATYPE_BOOLEAN: 594 | /* datatype(1) + boolean(1) */ 595 | return 2; 596 | case AMF_DATATYPE_STRING: 597 | /* datatype(1) + length(2) + string */ 598 | return 1 + 2 + strlen(amf->string.value); 599 | case AMF_DATATYPE_OBJECT: 600 | property = amf->object.properties; 601 | size = 1; 602 | while (property) { 603 | size += 2 + strlen(property->key); /* length + key */ 604 | size += amf_packet_get_size(property->value); 605 | property = property->next; 606 | } 607 | size += 3; /* length + object end (0x09) */ 608 | return size; 609 | case AMF_DATATYPE_NULL: 610 | /* datatype(1) */ 611 | return 1; 612 | case AMF_DATATYPE_UNDEFINED: 613 | /* datatype(1) */ 614 | return 1; 615 | case AMF_DATATYPE_OBJECT_END: 616 | /* datatype(1) */ 617 | return 1; 618 | default: 619 | break; 620 | } 621 | return 0; 622 | } 623 | 624 | 625 | size_t amf_packet_serialize( 626 | amf_packet_t *amf, 627 | unsigned char *output_buffer, size_t output_buffer_size) 628 | { 629 | switch (amf->datatype) { 630 | case AMF_DATATYPE_NUMBER: 631 | return amf_packet_serialize_number( 632 | amf, output_buffer, output_buffer_size); 633 | case AMF_DATATYPE_BOOLEAN: 634 | return amf_packet_serialize_boolean( 635 | amf, output_buffer, output_buffer_size); 636 | case AMF_DATATYPE_STRING: 637 | return amf_packet_serialize_string( 638 | amf, output_buffer, output_buffer_size); 639 | case AMF_DATATYPE_OBJECT: 640 | return amf_packet_serialize_object( 641 | amf, output_buffer, output_buffer_size); 642 | case AMF_DATATYPE_NULL: 643 | return amf_packet_serialize_null( 644 | amf, output_buffer, output_buffer_size); 645 | case AMF_DATATYPE_UNDEFINED: 646 | return amf_packet_serialize_undefined( 647 | amf, output_buffer, output_buffer_size); 648 | case AMF_DATATYPE_ECMA_ARRAY: 649 | return amf_packet_serialize_ecma_array( 650 | amf, output_buffer, output_buffer_size); 651 | case AMF_DATATYPE_OBJECT_END: 652 | break; 653 | default: 654 | break; 655 | } 656 | return 0; 657 | } 658 | 659 | 660 | size_t amf_packet_serialize_number( 661 | amf_packet_t *amf, 662 | unsigned char *output_buffer, size_t output_buffer_size) 663 | { 664 | if (output_buffer_size < 9) { 665 | return 0; 666 | } 667 | 668 | output_buffer[0] = AMF_DATATYPE_NUMBER; 669 | write_be64double(output_buffer + 1, amf->number.value); 670 | #ifdef DEBUG 671 | printf("AMF serialize number: %f: ", amf->number.value); 672 | { 673 | int i; 674 | for (i = 0; i < 9; ++i) { 675 | printf("%02x, ", output_buffer[i]); 676 | } 677 | } 678 | printf("\n"); 679 | #endif 680 | 681 | return 9; 682 | } 683 | 684 | size_t amf_packet_serialize_boolean( 685 | amf_packet_t *amf, 686 | unsigned char *output_buffer, size_t output_buffer_size) 687 | { 688 | if (output_buffer_size < 2) { 689 | return 0; 690 | } 691 | 692 | output_buffer[0] = AMF_DATATYPE_BOOLEAN; 693 | memmove(output_buffer + 1, &amf->boolean.value, 1); 694 | #ifdef DEBUG 695 | printf("AMF serialize boolean: %d\n", amf->boolean.value); 696 | #endif 697 | 698 | return 2; 699 | } 700 | 701 | size_t amf_packet_serialize_string( 702 | amf_packet_t *amf, 703 | unsigned char *output_buffer, size_t output_buffer_size) 704 | { 705 | size_t length; 706 | 707 | if (output_buffer_size < (3 + strlen(amf->string.value)) ) { 708 | return 0; 709 | } 710 | 711 | output_buffer[0] = AMF_DATATYPE_STRING; 712 | length = strlen(amf->string.value); 713 | write_be16int(output_buffer + 1, (int)length); 714 | memmove(output_buffer + 3 , amf->string.value, length); 715 | #ifdef DEBUG 716 | printf("AMF serialize string: %d: '%s': ", length, amf->string.value); 717 | { 718 | int i; 719 | for (i = 0; i < 3 + (int)length; ++i) { 720 | printf("%02x, ", output_buffer[i]); 721 | } 722 | } 723 | printf("\n"); 724 | #endif 725 | 726 | return 3 + length; 727 | } 728 | 729 | size_t amf_packet_serialize_object( 730 | amf_packet_t *amf, 731 | unsigned char *output_buffer, size_t output_buffer_size) 732 | { 733 | amf_packet_object_property_t *property; 734 | size_t key_length; 735 | size_t outputed_size; 736 | size_t serialized_value_size; 737 | 738 | if (output_buffer_size < 1) { 739 | return 0; 740 | } 741 | 742 | #ifdef DEBUG 743 | printf("AMF serialize object start\n"); 744 | #endif 745 | output_buffer[0] = AMF_DATATYPE_OBJECT; 746 | outputed_size = 1; 747 | property = amf->object.properties; 748 | while (property) { 749 | key_length = strlen(property->key); 750 | if ((outputed_size + 2 + key_length) > output_buffer_size) { 751 | return 0; 752 | } 753 | write_be16int(output_buffer + outputed_size, (int)key_length); 754 | memmove( 755 | output_buffer + outputed_size + 2, 756 | property->key, key_length); 757 | #ifdef DEBUG 758 | printf("key serialize: %d: '%s': ", key_length, property->key); 759 | { 760 | int i; 761 | for (i = 0; i < 2 + (int)key_length; ++i) { 762 | printf("%02x, ", output_buffer[outputed_size + i]); 763 | } 764 | } 765 | printf("\n"); 766 | #endif 767 | outputed_size += 2 + key_length; 768 | serialized_value_size = amf_packet_serialize( 769 | property->value, 770 | output_buffer + outputed_size, 771 | output_buffer_size - outputed_size); 772 | if (serialized_value_size == 0) { 773 | return 0; 774 | } 775 | outputed_size += serialized_value_size; 776 | property = property->next; 777 | } 778 | if ((outputed_size + 3) > output_buffer_size) { 779 | return 0; 780 | } 781 | write_be16int(output_buffer + outputed_size, 0); 782 | outputed_size += 2; 783 | output_buffer[outputed_size] = 0x09; 784 | outputed_size += 1; 785 | #ifdef DEBUG 786 | printf("AMF serialize object end\n"); 787 | #endif 788 | 789 | return outputed_size; 790 | } 791 | 792 | size_t amf_packet_serialize_null( 793 | amf_packet_t *amf, 794 | unsigned char *output_buffer, size_t output_buffer_size) 795 | { 796 | if (output_buffer_size < 1 ) { 797 | return 0; 798 | } 799 | 800 | if (amf->datatype == AMF_DATATYPE_NULL) { 801 | output_buffer[0] = AMF_DATATYPE_NULL; 802 | } 803 | 804 | return 1; 805 | } 806 | 807 | size_t amf_packet_serialize_undefined( 808 | amf_packet_t *amf, 809 | unsigned char *output_buffer, size_t output_buffer_size) 810 | { 811 | if (output_buffer_size < 1 ) { 812 | return 0; 813 | } 814 | 815 | 816 | if (amf->datatype == AMF_DATATYPE_UNDEFINED) { 817 | output_buffer[0] = AMF_DATATYPE_UNDEFINED; 818 | } 819 | #ifdef DEBUG 820 | printf("AMF serialize undefined\n"); 821 | #endif 822 | 823 | return 1; 824 | } 825 | 826 | 827 | size_t amf_packet_serialize_ecma_array( 828 | amf_packet_t *amf, 829 | unsigned char *output_buffer, size_t output_buffer_size) 830 | { 831 | amf_packet_object_property_t *property; 832 | size_t key_length; 833 | size_t outputed_size; 834 | size_t serialized_value_size; 835 | 836 | if (output_buffer_size < 1) { 837 | return 0; 838 | } 839 | 840 | #ifdef DEBUG 841 | printf("AMF serialize ecma array start\n"); 842 | #endif 843 | output_buffer[0] = AMF_DATATYPE_OBJECT; 844 | outputed_size = 1; 845 | property = amf->ecma_array.properties; 846 | while (property) { 847 | key_length = strlen(property->key); 848 | if ((outputed_size + 2 + key_length) > output_buffer_size) { 849 | return 0; 850 | } 851 | write_be16int(output_buffer + outputed_size, (int)key_length); 852 | memmove( 853 | output_buffer + outputed_size + 2, 854 | property->key, key_length); 855 | #ifdef DEBUG 856 | printf("key serialize: %d: '%s': ", key_length, property->key); 857 | { 858 | int i; 859 | for (i = 0; i < 2 + (int)key_length; ++i) { 860 | printf("%02x, ", output_buffer[outputed_size + i]); 861 | } 862 | } 863 | printf("\n"); 864 | #endif 865 | outputed_size += 2 + key_length; 866 | serialized_value_size = amf_packet_serialize( 867 | property->value, 868 | output_buffer + outputed_size, 869 | output_buffer_size - outputed_size); 870 | if (serialized_value_size == 0) { 871 | return 0; 872 | } 873 | outputed_size += serialized_value_size; 874 | property = property->next; 875 | } 876 | if ((outputed_size + 3) > output_buffer_size) { 877 | return 0; 878 | } 879 | write_be16int(output_buffer + outputed_size, 0); 880 | outputed_size += 2; 881 | output_buffer[outputed_size] = 0x09; 882 | outputed_size += 1; 883 | #ifdef DEBUG 884 | printf("AMF serialize ecma array end\n"); 885 | #endif 886 | 887 | return outputed_size; 888 | } 889 | -------------------------------------------------------------------------------- /rtmp.c: -------------------------------------------------------------------------------- 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 26 | #include 27 | #else 28 | #if defined(__WIN32__) || defined(WIN32) 29 | #define __USE_W32_SOCKETS 30 | #include 31 | #ifdef __CYGWIN__ 32 | #include 33 | #endif 34 | #else /* UNIX */ 35 | #ifdef __OS2__ 36 | #include 37 | #include 38 | #endif 39 | #include 40 | #include 41 | #include 42 | #include 43 | #ifndef __BEOS__ 44 | #include 45 | #endif 46 | #ifdef linux /* FIXME: what other platforms have this? */ 47 | #include 48 | #endif 49 | #include 50 | #include 51 | #endif /* WIN32 */ 52 | #endif /* Open Transport */ 53 | 54 | #if defined(__WIN32__) || defined(WIN32) 55 | #include 56 | #endif 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | 65 | 66 | #include "rtmp.h" 67 | #include "rtmp_packet.h" 68 | #include "amf_packet.h" 69 | #include "data_rw.h" 70 | 71 | 72 | static rtmp_server_client_t *get_new_server_client(rtmp_server_t *s); 73 | static int rtmp_server_client_set_will_send_buffer( 74 | rtmp_server_client_t *rc, unsigned char *data, size_t size); 75 | static void rtmp_server_client_delete_received_buffer( 76 | rtmp_server_client_t *rsc, size_t size); 77 | static rtmp_result_t rtmp_server_client_send_and_recv( 78 | rtmp_server_client_t *rsc); 79 | static rtmp_result_t rtmp_server_client_send_packet( 80 | rtmp_server_client_t *rsc, rtmp_packet_t *packet); 81 | static void rtmp_server_client_process_packet( 82 | rtmp_server_client_t *rsc, rtmp_packet_t *packet); 83 | static void rtmp_server_client_free( 84 | rtmp_server_t *rs, rtmp_server_client_t *rsc); 85 | 86 | static void rtmp_server_client_handshake_first( 87 | rtmp_server_client_t *server_client); 88 | static void rtmp_server_client_handshake_second( 89 | rtmp_server_client_t *server_client); 90 | static void rtmp_server_client_get_packet( 91 | rtmp_server_client_t *server_client); 92 | 93 | 94 | rtmp_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 | 136 | void 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 | 197 | static 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 | 261 | static 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 | 288 | static 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 | 302 | static 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 | 317 | void 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 | 359 | void 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 | 386 | static 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 | 476 | static 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 | 498 | void 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 | 520 | void 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 | 543 | void 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 | 566 | void 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 | 585 | void 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 | 631 | void 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 | 662 | void 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 | 699 | void 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 | 746 | static 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 | 765 | static 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 | 795 | void 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 | 834 | static void rtmp_client_parse_url(rtmp_client_t *rc, const char *url); 835 | static void rtmp_client_parse_host_and_port_number( 836 | rtmp_client_t *rc, const char *host_and_port_number); 837 | 838 | static void rtmp_client_handshake_first(rtmp_client_t *rc); 839 | static void rtmp_client_handshake_second(rtmp_client_t *rc); 840 | static void rtmp_client_get_packet(rtmp_client_t *rc); 841 | static void rtmp_client_process_packet( 842 | rtmp_client_t *rc, rtmp_packet_t *packet); 843 | 844 | static int rtmp_client_set_will_send_buffer( 845 | rtmp_client_t *rc, unsigned char *data, size_t size); 846 | static void rtmp_client_delete_received_buffer( 847 | rtmp_client_t *rc, size_t size); 848 | static rtmp_result_t rtmp_client_send_packet( 849 | rtmp_client_t *rc, rtmp_packet_t *packet); 850 | static rtmp_result_t rtmp_client_add_event( 851 | rtmp_client_t *rc, char *code, char *level); 852 | 853 | 854 | rtmp_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 | 929 | void 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 | 956 | static 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 | 1035 | static 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 | 1067 | void 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 | 1126 | static 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 | 1140 | static 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 | 1155 | static 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 | 1186 | static 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 | 1213 | static 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 | 1232 | void 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 | 1309 | rtmp_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 | 1335 | rtmp_event_t *rtmp_client_get_event(rtmp_client_t *rc) 1336 | { 1337 | return rc->events; 1338 | } 1339 | 1340 | 1341 | void 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 | 1355 | rtmp_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 | 1375 | void 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 | 1423 | void 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 | 1447 | void 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 | 1473 | --------------------------------------------------------------------------------