├── README.md ├── CMakeLists.txt ├── test.cpp ├── sdp.h ├── RtspPlayer.hpp ├── sdp.c └── RtspPlayer.cpp /README.md: -------------------------------------------------------------------------------- 1 | # Simple-Rtsp-Client 2 | Simple Rtsp Client 3 | 4 | ## Reference third-party sdp parser 5 | Sdp Parser use https://github.com/ubitux/Simple-SDP 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Simple-Rtsp-Client) 2 | 3 | set(CMAKE_C_STANDARD 99) 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | set(SRC RtspPlayer.cpp sdp.c test.cpp) 7 | 8 | add_executable(Simple-Rtsp-Client ${SRC}) 9 | -------------------------------------------------------------------------------- /test.cpp: -------------------------------------------------------------------------------- 1 | #include "RtspPlayer.hpp" 2 | 3 | using namespace RK; 4 | 5 | int main(int argc, char **argv) { 6 | RtspPlayer::Ptr player = std::make_shared(); 7 | player->Play("rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov"); 8 | 9 | getchar(); 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /sdp.h: -------------------------------------------------------------------------------- 1 | #ifndef SDP_H 2 | #define SDP_H 3 | 4 | #include 5 | 6 | struct sdp_connection { 7 | char *nettype; 8 | char *addrtype; 9 | char *address; 10 | }; 11 | 12 | struct sdp_bandwidth { 13 | char *bwtype; 14 | char *bandwidth; 15 | }; 16 | 17 | struct sdp_payload { 18 | char *_payload; 19 | 20 | unsigned char proto_version; 21 | struct sdp_origin { 22 | char *username; 23 | long long int sess_id; 24 | long long int sess_version; 25 | char *nettype; 26 | char *addrtype; 27 | char *addr; 28 | } origin; 29 | char *session_name; 30 | char *information; 31 | char *uri; 32 | char **emails; 33 | size_t emails_count; 34 | char **phones; 35 | size_t phones_count; 36 | struct sdp_connection conn; 37 | struct sdp_bandwidth *bw; 38 | size_t bw_count; 39 | struct sdp_time { 40 | time_t start_time; 41 | time_t stop_time; 42 | struct sdp_repeat { 43 | time_t interval; 44 | time_t duration; 45 | time_t *offsets; 46 | size_t offsets_count; 47 | } *repeat; 48 | size_t repeat_count; 49 | } *times; 50 | size_t times_count; 51 | struct sdp_zone_adjustments { 52 | time_t adjust; 53 | time_t offset; 54 | } *zone_adjustments; 55 | size_t zone_adjustments_count; 56 | char *encrypt_key; 57 | char **attributes; 58 | size_t attributes_count; 59 | struct sdp_media { 60 | struct sdp_info { 61 | char *type; 62 | int port; 63 | int port_n; 64 | char *proto; 65 | int *fmt; 66 | size_t fmt_count; 67 | } info; 68 | char *title; 69 | struct sdp_connection conn; 70 | struct sdp_bandwidth *bw; 71 | size_t bw_count; 72 | char *encrypt_key; 73 | char **attributes; 74 | size_t attributes_count; 75 | } *medias; 76 | size_t medias_count; 77 | }; 78 | 79 | struct sdp_payload *sdp_parse(const char *payload); 80 | void sdp_destroy(struct sdp_payload *sdp); 81 | void sdp_dump(struct sdp_payload *sdp); 82 | 83 | char *sdp_get_attr(char **attr, size_t nattr, char *key); 84 | int sdp_has_flag_attr(char **attr, size_t nattr, char *flag); 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /RtspPlayer.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // RtspPlayer.hpp 3 | // toolForTest 4 | // 5 | // Created by cx on 2018/9/6. 6 | // Copyright © 2018年 cx. All rights reserved. 7 | // 8 | 9 | #ifndef RtspPlayer_hpp 10 | #define RtspPlayer_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | extern "C" { 21 | #include "sdp.h" 22 | } 23 | 24 | namespace RK { 25 | 26 | enum RtspPlayerState { 27 | RtspSendOptions = 0, 28 | RtspHandleOptions, 29 | RtspSendDescribe, 30 | RtspHandleDescribe, 31 | RtspSendVideoSetup, 32 | RtspHandleVideoSetup, 33 | RtspSendAudioSetup, 34 | RtspHandleAudioSetup, 35 | RtspSendPlay, 36 | RtspHandlePlay, 37 | RtspSendPause, 38 | RtspHandlePause, 39 | RtspSendTerminate, 40 | RtspHandleTerminate, 41 | RtspIdle, 42 | RtspTurnOff, 43 | }; 44 | 45 | enum RtspPlayerCSeq { 46 | RTSPOPTIONS = 1, 47 | RTSPDESCRIBE, 48 | RTSPVIDEO_SETUP, 49 | RTSPAUDIO_SETUP, 50 | RTSPPLAY, 51 | RTSPPAUSE, 52 | RTSPTEARDOWN, 53 | }; 54 | 55 | // h264 nalu 56 | struct Nalu { 57 | unsigned type :5; 58 | unsigned nal_ref_idc :2; 59 | unsigned forbidden_zero_bit :1; 60 | }; 61 | 62 | // h264 rtp fu 63 | struct FU { 64 | unsigned type :5; 65 | unsigned R :1; 66 | unsigned E :1; 67 | unsigned S :1; 68 | }; 69 | 70 | class RtspPlayer { 71 | public: 72 | typedef std::shared_ptr Ptr; 73 | RtspPlayer(); 74 | ~RtspPlayer(); 75 | bool Play(std::string url); 76 | void Stop(); 77 | protected: 78 | bool NetworkInit(const char *ip, const short port); 79 | bool RTPSocketInit(int videoPort, int audioPort); 80 | bool getIPFromUrl(std::string url, char *ip, unsigned short *port); 81 | void EventInit(); 82 | bool HandleRtspMsg(const char *buf, ssize_t bufsize); 83 | void HandleRtspState(); 84 | 85 | void HandleRtpMsg(const char *buf, ssize_t bufsize); 86 | 87 | // rtsp message send/handle function 88 | void SendDescribe(std::string url); 89 | void HandleDescribe(const char *buf, ssize_t bufsize); 90 | void RtspSetup(const std::string url, int track, int CSeq, char *proto, short rtp_port, short rtcp_port); 91 | void SendVideoSetup(); 92 | bool HandleVideoSetup(const char *buf, ssize_t bufsize); 93 | void SendPlay(const std::string url); 94 | 95 | std::vector GetSDPFromMessage(const char *buffer, size_t length, const char *pattern); 96 | private: 97 | std::atomic _Terminated; 98 | std::atomic _NetWorked; 99 | std::shared_ptr _PlayThreadPtr; 100 | std::atomic _PlayState; 101 | fd_set _readfd; 102 | fd_set _writefd; 103 | fd_set _errorfd; 104 | 105 | std::string _rtspurl; 106 | char _rtspip[256]; 107 | 108 | int _Eventfd = 0; 109 | int _RtspSocket = 0; 110 | int _RtpVideoSocket = 0; 111 | int _RtpAudioSocket = 0; 112 | struct sockaddr_in _RtpVideoAddr; 113 | 114 | struct sdp_payload *_SdpParser; 115 | 116 | long _RtspSessionID = 0; 117 | std::function onVideoFrameGet; 118 | 119 | FILE *_fp; 120 | }; 121 | 122 | } //namespace RK 123 | #endif /* RtspPlayer_hpp */ 124 | -------------------------------------------------------------------------------- /sdp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "sdp.h" 7 | 8 | static char *load_next_entry(char *p, char *key, char **value) 9 | { 10 | char *endl; 11 | 12 | if (!p) 13 | goto fail; 14 | 15 | endl = strstr(p, "\r\n"); 16 | if (!endl) 17 | endl = strchr(p, '\n'); 18 | 19 | if (endl) 20 | while (*endl == '\r' || *endl == '\n') 21 | *endl++ = '\0'; 22 | else 23 | endl = &p[strlen(p)]; 24 | 25 | if (!p[0] || p[1] != '=') 26 | goto fail; 27 | 28 | *key = p[0]; 29 | *value = &p[2]; 30 | 31 | return endl; 32 | 33 | fail: 34 | *key = 0; 35 | *value = NULL; 36 | return NULL; 37 | } 38 | 39 | static char *split_values(char *p, char sep, char *fmt, ...) 40 | { 41 | va_list va; 42 | 43 | va_start(va, fmt); 44 | while (*p == sep) 45 | p++; 46 | while (*fmt) { 47 | char **s, *tmp; 48 | int *i; 49 | long long int *l; 50 | time_t *t; 51 | 52 | switch (*fmt++) { 53 | case 's': 54 | s = va_arg(va, char **); 55 | *s = p; 56 | tmp = strchr(p, sep); 57 | if (tmp) { 58 | p = tmp; 59 | while (*p == sep) 60 | *p++ = '\0'; 61 | } else { 62 | p = &p[strlen(p)]; 63 | } 64 | break; 65 | case 'l': 66 | l = va_arg(va, long long int *); 67 | *l = strtoll(p, &tmp, 10); 68 | if (tmp == p) 69 | *p = 0; 70 | else 71 | p = tmp; 72 | break; 73 | case 'i': 74 | i = va_arg(va, int *); 75 | *i = strtol(p, &tmp, 10); 76 | if (tmp == p) 77 | *p = 0; 78 | else 79 | p = tmp; 80 | break; 81 | case 't': 82 | t = va_arg(va, time_t *); 83 | *t = strtol(p, &tmp, 10); 84 | if (tmp == p) { 85 | *p = 0; 86 | } else { 87 | p = tmp; 88 | switch (*p) { 89 | case 'd': *t *= 86400; p++; break; 90 | case 'h': *t *= 3600; p++; break; 91 | case 'm': *t *= 60; p++; break; 92 | } 93 | } 94 | break; 95 | } 96 | while (*p == sep) 97 | p++; 98 | } 99 | va_end(va); 100 | return p; 101 | } 102 | 103 | #define GET_CONN_INFO(connf_ptr) do { \ 104 | if (key == 'c') { \ 105 | struct sdp_connection *c = connf_ptr; \ 106 | split_values(value, ' ', "sss", &c->nettype, &c->addrtype, \ 107 | &c->address); \ 108 | p = load_next_entry(p, &key, &value); \ 109 | } \ 110 | } while (0) 111 | 112 | #define GET_BANDWIDTH_INFO(bw) do { \ 113 | int n; \ 114 | while (key == 'b') { \ 115 | ADD_ENTRY(bw); \ 116 | n = bw ## _count - 1; \ 117 | split_values(value, ':', "ss", &bw[n].bwtype, \ 118 | &bw[n].bandwidth); \ 119 | p = load_next_entry(p, &key, &value); \ 120 | } \ 121 | } while (0) 122 | 123 | #define LOAD_FACULTATIVE_STR(k, field) do { \ 124 | if (key == k) { \ 125 | field = value; \ 126 | p = load_next_entry(p, &key, &value); \ 127 | } \ 128 | } while (0) 129 | 130 | #define LOAD_MULTIPLE_FACULTATIVE_STR(k, field) do { \ 131 | while (key == k) { \ 132 | ADD_ENTRY(field); \ 133 | field[field ## _count - 1] = value; \ 134 | p = load_next_entry(p, &key, &value); \ 135 | } \ 136 | } while (0) 137 | 138 | #define ADD_ENTRY(field) do { \ 139 | field ## _count++; \ 140 | if (!field) { \ 141 | field = calloc(1, sizeof(*field)); \ 142 | } else { \ 143 | int n = field ## _count; \ 144 | field = realloc(field, sizeof(*field) * n); \ 145 | memset(&field[n - 1], 0, sizeof(*field)); \ 146 | } \ 147 | if (!(field)) \ 148 | goto fail; \ 149 | } while (0) 150 | 151 | struct sdp_payload *sdp_parse(const char *payload) 152 | { 153 | struct sdp_payload *sdp = calloc(1, sizeof(*sdp)); 154 | char *p, key, *value; 155 | 156 | if (!sdp) 157 | goto fail; 158 | 159 | p = sdp->_payload = strdup(payload); 160 | if (!p) 161 | goto fail; 162 | 163 | /* Protocol version (mandatory, only 0 supported) */ 164 | p = load_next_entry(p, &key, &value); 165 | if (key != 'v') 166 | goto fail; 167 | sdp->proto_version = value[0] - '0'; 168 | if (sdp->proto_version != 0 || value[1]) 169 | goto fail; 170 | 171 | /* Origin field (mandatory) */ 172 | p = load_next_entry(p, &key, &value); 173 | if (key != 'o') 174 | goto fail; 175 | else { 176 | struct sdp_origin *o = &sdp->origin; 177 | split_values(value, ' ', "sllsss", &o->username, &o->sess_id, 178 | &o->sess_version, &o->nettype, &o->addrtype, &o->addr); 179 | } 180 | 181 | /* Session name field (mandatory) */ 182 | p = load_next_entry(p, &key, &value); 183 | if (key != 's') 184 | goto fail; 185 | sdp->session_name = value; 186 | p = load_next_entry(p, &key, &value); 187 | 188 | /* Information field */ 189 | LOAD_FACULTATIVE_STR('i', sdp->information); 190 | 191 | /* URI field */ 192 | LOAD_FACULTATIVE_STR('u', sdp->uri); 193 | 194 | /* Email addresses */ 195 | LOAD_MULTIPLE_FACULTATIVE_STR('e', sdp->emails); 196 | 197 | /* Phone numbers */ 198 | LOAD_MULTIPLE_FACULTATIVE_STR('p', sdp->phones); 199 | 200 | /* Connection information */ 201 | GET_CONN_INFO(&sdp->conn); 202 | 203 | /* Bandwidth fields */ 204 | GET_BANDWIDTH_INFO(sdp->bw); 205 | 206 | /* Time fields (at least one mandatory) */ 207 | do { 208 | struct sdp_time *tf; 209 | 210 | ADD_ENTRY(sdp->times); 211 | tf = &sdp->times[sdp->times_count - 1]; 212 | split_values(value, ' ', "tt", &tf->start_time, &tf->stop_time); 213 | p = load_next_entry(p, &key, &value); 214 | 215 | while (key == 'r') { 216 | struct sdp_repeat *rf; 217 | 218 | ADD_ENTRY(tf->repeat); 219 | rf = &tf->repeat[tf->repeat_count - 1]; 220 | value = split_values(value, ' ', "tt", &rf->interval, &rf->duration); 221 | while (*value) { 222 | int n = rf->offsets_count; 223 | ADD_ENTRY(rf->offsets); 224 | value = split_values(value, ' ', "t", &rf->offsets[n]); 225 | } 226 | p = load_next_entry(p, &key, &value); 227 | } 228 | } while (key == 't'); 229 | 230 | /* Zone adjustments */ 231 | if (key == 'z') { 232 | while (*value) { 233 | int n = sdp->zone_adjustments_count; 234 | struct sdp_zone_adjustments *za; 235 | 236 | ADD_ENTRY(sdp->zone_adjustments); 237 | za = &sdp->zone_adjustments[n]; 238 | value = split_values(value, ' ', "tt", &za->adjust, &za->offset); 239 | } 240 | p = load_next_entry(p, &key, &value); 241 | } 242 | 243 | /* Encryption key */ 244 | LOAD_FACULTATIVE_STR('k', sdp->encrypt_key); 245 | 246 | /* Media attributes */ 247 | LOAD_MULTIPLE_FACULTATIVE_STR('a', sdp->attributes); 248 | 249 | /* Media descriptions */ 250 | while (key == 'm') { 251 | struct sdp_media *md; 252 | 253 | ADD_ENTRY(sdp->medias); 254 | md = &sdp->medias[sdp->medias_count - 1]; 255 | 256 | value = split_values(value, ' ', "s", &md->info.type); 257 | md->info.port = strtol(value, &value, 10); 258 | md->info.port_n = *value == '/' ? strtol(value + 1, &value, 10) : 0; 259 | value = split_values(value, ' ', "s", &md->info.proto); 260 | while (*value) { 261 | ADD_ENTRY(md->info.fmt); 262 | value = split_values(value, ' ', "i", &md->info.fmt[md->info.fmt_count - 1]); 263 | } 264 | p = load_next_entry(p, &key, &value); 265 | 266 | LOAD_FACULTATIVE_STR('i', md->title); 267 | GET_CONN_INFO(&md->conn); 268 | GET_BANDWIDTH_INFO(md->bw); 269 | LOAD_FACULTATIVE_STR('k', md->encrypt_key); 270 | LOAD_MULTIPLE_FACULTATIVE_STR('a', md->attributes); 271 | } 272 | 273 | return sdp; 274 | 275 | fail: 276 | sdp_destroy(sdp); 277 | return NULL; 278 | } 279 | 280 | void sdp_destroy(struct sdp_payload *sdp) 281 | { 282 | size_t i, j; 283 | 284 | if (sdp) { 285 | free(sdp->_payload); 286 | free(sdp->emails); 287 | free(sdp->phones); 288 | free(sdp->bw); 289 | for (i = 0; i < sdp->times_count; i++) { 290 | for (j = 0; j < sdp->times[i].repeat_count; j++) 291 | free(sdp->times[i].repeat[j].offsets); 292 | free(sdp->times[i].repeat); 293 | } 294 | free(sdp->times); 295 | free(sdp->zone_adjustments); 296 | free(sdp->attributes); 297 | for (i = 0; i < sdp->medias_count; i++) { 298 | free(sdp->medias[i].info.fmt); 299 | free(sdp->medias[i].bw); 300 | free(sdp->medias[i].attributes); 301 | } 302 | free(sdp->medias); 303 | } 304 | free(sdp); 305 | } 306 | 307 | char *sdp_get_attr(char **attr, size_t nattr, char *key) 308 | { 309 | size_t i, klen = strlen(key); 310 | 311 | for (i = 0; i < nattr; i++) 312 | if (!strncmp(attr[i], key, klen) && attr[i][klen] == ':') 313 | return &attr[i][klen + 1]; 314 | return NULL; 315 | } 316 | 317 | int sdp_has_flag_attr(char **attr, size_t nattr, char *flag) 318 | { 319 | size_t i; 320 | 321 | for (i = 0; i < nattr; i++) 322 | if (!strcmp(attr[i], flag)) 323 | return 1; 324 | return 0; 325 | } 326 | 327 | void sdp_dump(struct sdp_payload *sdp) 328 | { 329 | size_t i, j, k; 330 | 331 | if (!sdp) { 332 | printf("invalid SDP\n"); 333 | return; 334 | } 335 | 336 | printf("v=%d\n", sdp->proto_version); 337 | printf("o=%s %lld %lld %s %s %s\n", sdp->origin.username, 338 | sdp->origin.sess_id, sdp->origin.sess_version, sdp->origin.nettype, 339 | sdp->origin.addrtype, sdp->origin.addr); 340 | printf("s=%s\n", sdp->session_name); 341 | 342 | if (sdp->information) printf("i=%s\n", sdp->information); 343 | if (sdp->uri) printf("u=%s\n", sdp->uri); 344 | 345 | for (i = 0; i < sdp->emails_count; i++) printf("e=%s\n", sdp->emails[i]); 346 | for (i = 0; i < sdp->phones_count; i++) printf("p=%s\n", sdp->phones[i]); 347 | 348 | if (sdp->conn.nettype && sdp->conn.addrtype && sdp->conn.address) 349 | printf("c=%s %s %s\n", 350 | sdp->conn.nettype, sdp->conn.addrtype, sdp->conn.address); 351 | 352 | for (i = 0; i < sdp->bw_count; i++) 353 | printf("b=%s:%s\n", sdp->bw[i].bwtype, sdp->bw[i].bandwidth); 354 | 355 | for (i = 0; i < sdp->times_count; i++) { 356 | struct sdp_time *t = &sdp->times[i]; 357 | printf("t=%ld %ld\n", t->start_time, t->stop_time); 358 | for (j = 0; j < t->repeat_count; j++) { 359 | struct sdp_repeat *r = &t->repeat[j]; 360 | printf("r=%ld %ld", r->interval, r->duration); 361 | for (k = 0; k < r->offsets_count; k++) 362 | printf(" %ld", r->offsets[k]); 363 | printf("\n"); 364 | } 365 | } 366 | 367 | if (sdp->zone_adjustments_count) { 368 | printf("z="); 369 | for (i = 0; i < sdp->zone_adjustments_count; i++) 370 | printf("%ld %ld%s", sdp->zone_adjustments[i].adjust, 371 | sdp->zone_adjustments[i].offset, 372 | i + 1 < sdp->zone_adjustments_count ? " " : ""); 373 | printf("\n"); 374 | } 375 | 376 | if (sdp->encrypt_key) 377 | printf("k=%s\n", sdp->encrypt_key); 378 | 379 | for (i = 0; i < sdp->attributes_count; i++) 380 | printf("a=%s\n", sdp->attributes[i]); 381 | 382 | for (i = 0; i < sdp->medias_count; i++) { 383 | struct sdp_media *m = &sdp->medias[i]; 384 | struct sdp_info *info = &m->info; 385 | 386 | printf("m=%s %d", info->type, info->port); 387 | if (info->port_n) 388 | printf("/%d", info->port_n); 389 | printf(" %s", info->proto); 390 | for (j = 0; j < info->fmt_count; j++) 391 | printf(" %d", info->fmt[j]); 392 | printf("\n"); 393 | 394 | if (m->title) printf("i=%s\n", m->title); 395 | if (m->conn.nettype && m->conn.addrtype && m->conn.address) 396 | printf("c=%s %s %s\n", 397 | m->conn.nettype, m->conn.addrtype, m->conn.address); 398 | for (j = 0; j < m->bw_count; j++) 399 | printf("b=%s:%s\n", m->bw[j].bwtype, m->bw[j].bandwidth); 400 | if (m->encrypt_key) printf("k=%s\n", m->encrypt_key); 401 | for (j = 0; j < m->attributes_count; j++) 402 | printf("a=%s\n", m->attributes[j]); 403 | } 404 | } 405 | -------------------------------------------------------------------------------- /RtspPlayer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // RtspPlayer.cpp 3 | // toolForTest 4 | // 5 | // Created by cx on 2018/9/6. 6 | // Copyright © 2018年 cx. All rights reserved. 7 | // 8 | 9 | #include "RtspPlayer.hpp" 10 | #include 11 | 12 | #define MODULE_TAG "RtspPlayer" 13 | 14 | #define log(tag,fmt,...)\ 15 | do {\ 16 | printf("%s:", tag);\ 17 | printf(fmt, ##__VA_ARGS__);\ 18 | printf("\n");\ 19 | } while(0) 20 | 21 | #define SetNextState(x) _PlayState = x; 22 | 23 | #define VIDEO_RTP_PORT (12000) 24 | #define VIDEO_RTCP_PORT (12001) 25 | 26 | static FILE *fp; 27 | 28 | namespace RK { 29 | RtspPlayer::RtspPlayer() { 30 | _Terminated = false; 31 | _NetWorked = false; 32 | _PlayState = RtspIdle; 33 | } 34 | 35 | RtspPlayer::~RtspPlayer() { 36 | Stop(); 37 | } 38 | 39 | bool RtspPlayer::getIPFromUrl(std::string url, char *ip, unsigned short *port) { 40 | unsigned int dstip[4] = {0}; 41 | int dstport = 0; 42 | int field = sscanf(url.c_str(), "rtsp://%d.%d.%d.%d:%d", &dstip[0], &dstip[1], &dstip[2], &dstip[3], &dstport); 43 | if (field < 4) { 44 | log(MODULE_TAG, "failed to get ip from url"); 45 | return false; 46 | } else if (field == 4) { 47 | sprintf(ip, "%d.%d.%d.%d", dstip[0], dstip[1], dstip[2], dstip[3]); 48 | *port = dstport = 554; 49 | } else if (field == 5) { 50 | sprintf(ip, "%d.%d.%d.%d", dstip[0], dstip[1], dstip[2], dstip[3]); 51 | *port = dstport; 52 | } else { 53 | log(MODULE_TAG, "failed to get ip from url"); 54 | return false; 55 | } 56 | 57 | return true; 58 | } 59 | 60 | bool RtspPlayer::NetworkInit(const char *ip, const short port) { 61 | _RtspSocket = ::socket(AF_INET, SOCK_STREAM, 0); 62 | if (_RtspSocket < 0) { 63 | log(MODULE_TAG, "network init failed"); 64 | return false; 65 | } 66 | _Eventfd = std::max(_RtspSocket, _Eventfd); 67 | 68 | int ul = true; 69 | if (::ioctl(_RtspSocket, FIONBIO, &ul) < 0) { 70 | log(MODULE_TAG, "set socket non block failed"); 71 | return false; 72 | } 73 | 74 | struct sockaddr_in serverAddr; 75 | serverAddr.sin_family = AF_INET; 76 | serverAddr.sin_port = htons(port); 77 | serverAddr.sin_addr.s_addr = inet_addr(ip); 78 | 79 | if (::connect(_RtspSocket, (struct sockaddr *)&serverAddr, (socklen_t)sizeof(serverAddr)) == 0) { 80 | log(MODULE_TAG, "sync connect success"); 81 | } else if (errno == EINPROGRESS){ 82 | log(MODULE_TAG, "async connecting..."); 83 | } else { 84 | log(MODULE_TAG, "invalid connect"); 85 | return false; 86 | } 87 | 88 | _NetWorked = true; 89 | return true; 90 | } 91 | 92 | bool RtspPlayer::RTPSocketInit(int videoPort, int audioPort) { 93 | if (videoPort) { 94 | _RtpVideoSocket = ::socket(AF_INET, SOCK_DGRAM, 0); 95 | if (_RtpVideoSocket < 0) { 96 | log(MODULE_TAG, "rtp video socket init failed"); 97 | return false; 98 | } 99 | _Eventfd = std::max(_RtpVideoSocket, _Eventfd); 100 | 101 | int ul = true; 102 | if (::ioctl(_RtpVideoSocket, FIONBIO, &ul) < 0) { 103 | log(MODULE_TAG, "failed to set rtp video socket non block"); 104 | ::close(_RtpVideoSocket); 105 | return false; 106 | } 107 | 108 | _RtpVideoAddr.sin_family = AF_INET; 109 | //_RtpVideoAddr.sin_addr.s_addr = inet_addr(_rtspip); 110 | _RtpVideoAddr.sin_addr.s_addr = INADDR_ANY; 111 | _RtpVideoAddr.sin_port = htons(VIDEO_RTP_PORT); 112 | 113 | if (::bind(_RtpVideoSocket, (const struct sockaddr *)&_RtpVideoAddr, (socklen_t)sizeof(_RtpVideoAddr)) < 0) { 114 | log(MODULE_TAG, "failed to bind rtp video socket error %d %s", errno, strerror(errno)); 115 | return false; 116 | } 117 | 118 | return true; 119 | } 120 | 121 | return false; 122 | } 123 | 124 | void RtspPlayer::EventInit() { 125 | FD_ZERO(&_readfd); 126 | FD_ZERO(&_writefd); 127 | FD_ZERO(&_errorfd); 128 | 129 | FD_SET(_RtspSocket, &_readfd); 130 | FD_SET(_RtspSocket, &_writefd); 131 | FD_SET(_RtspSocket, &_errorfd); 132 | } 133 | 134 | std::vector RtspPlayer::GetSDPFromMessage(const char *buffer, size_t length, const char *pattern) { 135 | char *tempBuffer = (char *)malloc(length + 1); 136 | strcpy(tempBuffer, buffer); 137 | 138 | std::vector rvector; 139 | char* tmpStr = strtok(tempBuffer, pattern); 140 | while (tmpStr != NULL) 141 | { 142 | rvector.push_back(std::string(tmpStr)); 143 | tmpStr = strtok(NULL, pattern); 144 | } 145 | 146 | free(tempBuffer); 147 | 148 | return rvector; 149 | } 150 | 151 | void RtspPlayer::SendDescribe(std::string url) { 152 | char buf[1024]; 153 | sprintf(buf, "DESCRIBE %s RTSP/1.0\r\n" 154 | "Accept: application/sdp\r\n" 155 | "CSeq: %d\r\n" 156 | "User-Agent: Lavf58.12.100\r\n" 157 | "\r\n", url.c_str(), RTSPDESCRIBE); 158 | 159 | ::send(_RtspSocket, buf, strlen(buf), 0); 160 | } 161 | 162 | void RtspPlayer::HandleDescribe(const char *buf, ssize_t bufsize) { 163 | std::vector rvector = GetSDPFromMessage(buf, bufsize, "\r\n"); 164 | std::string sdp; 165 | for (auto substr : rvector) { 166 | if (strstr(substr.c_str(),"Session:")) { 167 | ::sscanf(substr.c_str(), "Session:%ld", &_RtspSessionID); 168 | } else if (strchr(substr.c_str(), '=')) { 169 | sdp.append(substr); 170 | sdp.append("\n"); 171 | } 172 | } 173 | 174 | _SdpParser = sdp_parse(sdp.c_str()); 175 | } 176 | 177 | void RtspPlayer::RtspSetup(const std::string url, int track, int CSeq, char *proto, short rtp_port, short rtcp_port) { 178 | char buf[1024]; 179 | sprintf(buf, "SETUP %s/trackID=%d RTSP/1.0\r\n" 180 | "CSeq: %d\r\n" 181 | "User-Agent: Lavf58.12.100\r\n" 182 | "Transport: %s;unicast;client_port=%d-%d\r\n" 183 | "\r\n", url.c_str(), track, CSeq, proto, rtp_port, rtcp_port); 184 | 185 | ::send(_RtspSocket, buf, strlen(buf), 0); 186 | } 187 | 188 | void RtspPlayer::SendVideoSetup() { 189 | int i = 0, j = 0; 190 | int videoTrackID = 0; 191 | for (i = 0; i < _SdpParser->medias_count; i++) { 192 | if (strcmp(_SdpParser->medias[i].info.type, "video") == 0) { 193 | for (j = 0; j < _SdpParser->medias[i].attributes_count; j++) { 194 | if (strstr(_SdpParser->medias[i].attributes[j], "trackID")) { 195 | ::sscanf(_SdpParser->medias[i].attributes[j], "control:trackID=%d", &videoTrackID); 196 | } 197 | } 198 | RtspSetup(_rtspurl, videoTrackID, RTSPVIDEO_SETUP, _SdpParser->medias[i].info.proto, VIDEO_RTP_PORT, VIDEO_RTCP_PORT); 199 | } 200 | } 201 | } 202 | 203 | bool RtspPlayer::HandleVideoSetup(const char *buf, ssize_t bufsize) { 204 | int rtp_port = 0; 205 | int rtcp_port = 0; 206 | int remote_port = 0; 207 | int remote_rtcp_port = 0; 208 | 209 | if (strstr(buf, "client_port=")) { 210 | ::sscanf(strstr(buf, "client_port="), "client_port=%d-%d", &rtp_port, &rtcp_port); 211 | } 212 | 213 | if(strstr(buf, "server_port=")) { 214 | ::sscanf(strstr(buf, "server_port="), "server_port=%d-%d", &remote_port, &remote_rtcp_port); 215 | } 216 | 217 | if (!RTPSocketInit(rtp_port ? rtp_port : VIDEO_RTP_PORT, 0)) { 218 | log(MODULE_TAG, "rtp socket init failed"); 219 | return false; 220 | } 221 | 222 | struct sockaddr_in remoteAddr; 223 | remoteAddr.sin_family = AF_INET; 224 | remoteAddr.sin_port = htons(remote_port); 225 | remoteAddr.sin_addr.s_addr = inet_addr(_rtspip); 226 | 227 | const unsigned char natpacket[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 228 | ::sendto(_RtpVideoSocket, natpacket, sizeof(natpacket), 0, (const struct sockaddr *)&remoteAddr, (socklen_t)sizeof(remoteAddr)); 229 | 230 | return true; 231 | } 232 | 233 | void RtspPlayer::SendPlay(const std::string url) { 234 | char buf[1024]; 235 | sprintf(buf, "PLAY %s RTSP/1.0\r\n" 236 | "CSeq: %u\r\n" 237 | "Session: %ld\r\n" 238 | "Range: npt=0.000-\r\n" // Range 239 | "User-Agent: Lavf58.12.100\r\n" 240 | "\r\n", url.c_str(), RTSPPLAY, _RtspSessionID); 241 | 242 | ::send(_RtspSocket, buf, strlen(buf), 0); 243 | } 244 | 245 | bool RtspPlayer::HandleRtspMsg(const char *buf, ssize_t bufsize) { 246 | int MsgType = 0; 247 | if (::sscanf(buf, "%*[^C]CSeq:%d", &MsgType) != 1) { 248 | log(MODULE_TAG, "invalid rtsp message"); 249 | return false; 250 | } 251 | 252 | switch (MsgType) { 253 | case RTSPOPTIONS: 254 | 255 | break; 256 | case RTSPDESCRIBE: 257 | HandleDescribe(buf, bufsize); 258 | SetNextState(RtspSendVideoSetup); 259 | break; 260 | case RTSPVIDEO_SETUP: 261 | if (HandleVideoSetup(buf, bufsize)) { 262 | SetNextState(RtspSendPlay); 263 | } 264 | break; 265 | 266 | case RTSPAUDIO_SETUP: 267 | break; 268 | 269 | case RTSPPLAY: 270 | break; 271 | 272 | default: 273 | log(MODULE_TAG, "unknow rtsp message"); 274 | break; 275 | } 276 | 277 | return true; 278 | } 279 | 280 | void RtspPlayer::HandleRtspState() { 281 | switch (_PlayState.load()) { 282 | case RtspSendOptions: 283 | log(MODULE_TAG, "rtsp send options"); 284 | break; 285 | case RtspHandleOptions: 286 | log(MODULE_TAG, "rtsp handle options"); 287 | break; 288 | case RtspSendDescribe: 289 | log(MODULE_TAG, "rtsp send describe"); 290 | SendDescribe(_rtspurl); 291 | break; 292 | case RtspHandleDescribe: 293 | log(MODULE_TAG, "rtsp handle describe"); 294 | break; 295 | case RtspSendVideoSetup: 296 | log(MODULE_TAG, "rtsp send video setup"); 297 | SendVideoSetup(); 298 | break; 299 | case RtspHandleVideoSetup: 300 | log(MODULE_TAG, "rtsp handle video setup"); 301 | break; 302 | case RtspSendAudioSetup: 303 | log(MODULE_TAG, "rtsp send audio setup"); 304 | break; 305 | case RtspHandleAudioSetup: 306 | log(MODULE_TAG, "rtsp handle audio setup"); 307 | break; 308 | case RtspSendPlay: 309 | log(MODULE_TAG, "rtsp send play"); 310 | SendPlay(_rtspurl); 311 | break; 312 | case RtspHandlePlay: 313 | log(MODULE_TAG, "rtsp handle play"); 314 | break; 315 | case RtspSendPause: 316 | log(MODULE_TAG, "rtsp send pause"); 317 | break; 318 | case RtspHandlePause: 319 | log(MODULE_TAG, "rtsp handle pause"); 320 | break; 321 | case RtspIdle: 322 | break; 323 | default: 324 | log(MODULE_TAG, "unkonw rtsp state"); 325 | break; 326 | } 327 | 328 | SetNextState(RtspIdle); 329 | } 330 | 331 | #define RTP_OFFSET (12) 332 | #define FU_OFFSET (14) 333 | 334 | void RtspPlayer::HandleRtpMsg(const char *buf, ssize_t bufsize) { 335 | char header[] = {0, 0, 0, 1}; 336 | struct Nalu nalu = *(struct Nalu *)(buf + RTP_OFFSET); 337 | 338 | if (!fp) { 339 | fp = ::fopen("test.h264", "w+"); 340 | if (!fp) { 341 | log(MODULE_TAG, "failed to oepen test.h264"); 342 | return; 343 | } 344 | } 345 | 346 | if (nalu.type >= 0 && nalu.type < 24) { //one nalu 347 | ::fwrite(header, 4, 1, fp); 348 | ::fwrite(buf + RTP_OFFSET, bufsize - RTP_OFFSET, 1, fp); 349 | ::fflush(fp); 350 | } else if (nalu.type == 28) { //fu-a slice 351 | struct FU fu; 352 | char in = buf[RTP_OFFSET + 1]; 353 | fu.S = in >> 7; 354 | fu.E = (in >> 6) & 0x01; 355 | fu.R = (in >> 5) & 0x01; 356 | fu.type = in & 0x1f; 357 | 358 | if (fu.S == 1) { 359 | char naluType = nalu.forbidden_zero_bit << 7 | nalu.nal_ref_idc << 5 | fu.type; 360 | ::fwrite(header, 4, 1, fp); 361 | ::fwrite(&naluType, 1, 1, fp); 362 | ::fwrite(buf + FU_OFFSET, bufsize - FU_OFFSET, 1, fp); 363 | ::fflush(fp); 364 | } else if (fu.E == 1) { 365 | ::fwrite(buf + FU_OFFSET, bufsize - FU_OFFSET, 1, fp); 366 | ::fflush(fp); 367 | } else { 368 | ::fwrite(buf + FU_OFFSET, bufsize - FU_OFFSET, 1, fp); 369 | ::fflush(fp); 370 | } 371 | } 372 | } 373 | 374 | bool RtspPlayer::Play(std::string url) { 375 | char ip[256]; 376 | unsigned short port = 0; 377 | _rtspurl = url; 378 | 379 | if (!getIPFromUrl(url, ip, &port)) { 380 | log(MODULE_TAG, "get ip and port failed"); 381 | return false; 382 | } 383 | ::memcpy(_rtspip, ip, sizeof(ip)); 384 | 385 | if (!NetworkInit(ip, port)) { 386 | log(MODULE_TAG, "network uninitizial"); 387 | return false; 388 | } 389 | 390 | EventInit(); 391 | 392 | // internal rtsp play thread 393 | _PlayThreadPtr = std::make_shared([&] { 394 | char recvbuf[2048]; 395 | 396 | while (!_Terminated) { 397 | FD_ZERO(&_readfd); 398 | FD_ZERO(&_errorfd); 399 | FD_SET(_RtspSocket, &_readfd); 400 | FD_SET(_RtspSocket, &_errorfd); 401 | 402 | // rtp video socket has connected 403 | if (_RtpVideoSocket) { 404 | FD_SET(_RtpVideoSocket, &_readfd); 405 | } 406 | 407 | int r = ::select(_Eventfd + 1, &_readfd, &_writefd, &_errorfd, NULL); 408 | if (r < 0) { 409 | log(MODULE_TAG, "event error..."); 410 | break; 411 | } else if (r == 0) { 412 | log(MODULE_TAG, "event over time..."); 413 | } else { 414 | if (FD_ISSET(_RtspSocket, &_readfd)) { 415 | ::memset(recvbuf, 0, sizeof(recvbuf)); 416 | ssize_t recvbytes = ::recv(_RtspSocket, recvbuf, sizeof(recvbuf), 0); 417 | if (recvbytes <= 0) { 418 | log(MODULE_TAG, "socket peer close"); 419 | break; 420 | } else { 421 | if (!HandleRtspMsg(recvbuf, recvbytes)) { 422 | log(MODULE_TAG, "failed to handle rtsp msg"); 423 | } 424 | } 425 | } 426 | 427 | if (FD_ISSET(_RtpVideoSocket, &_readfd)) { 428 | socklen_t socklen = sizeof(_RtpVideoAddr); 429 | ::memset(recvbuf, 0, sizeof(recvbuf)); 430 | ssize_t recvbytes = ::recvfrom(_RtpVideoSocket, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&_RtpVideoAddr, &socklen); 431 | log(MODULE_TAG, "recv rtp video packet %ld bytes", recvbytes); 432 | 433 | HandleRtpMsg(recvbuf, recvbytes); 434 | } 435 | 436 | if (FD_ISSET(_RtspSocket, &_writefd)) { 437 | log(MODULE_TAG, "async connect success"); 438 | SetNextState(RtspSendDescribe); 439 | FD_CLR(_RtspSocket, &_writefd); 440 | } 441 | 442 | if (FD_ISSET(_RtspSocket, &_errorfd)) { 443 | log(MODULE_TAG, "event error occur"); 444 | break; 445 | } 446 | } 447 | 448 | HandleRtspState(); 449 | } 450 | 451 | fclose(fp); 452 | }); 453 | 454 | return true; 455 | } 456 | 457 | void RtspPlayer::Stop() { 458 | _Terminated = true; 459 | _PlayState = RtspTurnOff; 460 | _PlayThreadPtr->join(); 461 | } 462 | } 463 | --------------------------------------------------------------------------------