fix #194, writev multiple msgs, support 6k+ 250kbps clients. 2.0.15.
正在显示
8 个修改的文件
包含
251 行增加
和
29 行删除
| @@ -242,6 +242,7 @@ Supported operating systems and hardware: | @@ -242,6 +242,7 @@ Supported operating systems and hardware: | ||
| 242 | * 2013-10-17, Created.<br/> | 242 | * 2013-10-17, Created.<br/> |
| 243 | 243 | ||
| 244 | ## History | 244 | ## History |
| 245 | +* v2.0, 2014-11-08, fix [#194](https://github.com/winlinvip/simple-rtmp-server/issues/194), writev multiple msgs, support 6k+ 250kbps clients. 2.0.15. | ||
| 245 | * v2.0, 2014-11-08, fix [#194](https://github.com/winlinvip/simple-rtmp-server/issues/194), optmized st for timeout recv. pulse to 500ms. 2.0.14. | 246 | * v2.0, 2014-11-08, fix [#194](https://github.com/winlinvip/simple-rtmp-server/issues/194), optmized st for timeout recv. pulse to 500ms. 2.0.14. |
| 246 | * v2.0, 2014-11-08, fix [#195](https://github.com/winlinvip/simple-rtmp-server/issues/195), remove the confuse code st_usleep(0). 2.0.13. | 247 | * v2.0, 2014-11-08, fix [#195](https://github.com/winlinvip/simple-rtmp-server/issues/195), remove the confuse code st_usleep(0). 2.0.13. |
| 247 | * v2.0, 2014-11-08, fix [#191](https://github.com/winlinvip/simple-rtmp-server/issues/191), configure --export-librtmp-project and --export-librtmp-single. 2.0.11. | 248 | * v2.0, 2014-11-08, fix [#191](https://github.com/winlinvip/simple-rtmp-server/issues/191), configure --export-librtmp-project and --export-librtmp-single. 2.0.11. |
| @@ -449,6 +450,7 @@ Performance benchmark history, on virtual box: | @@ -449,6 +450,7 @@ Performance benchmark history, on virtual box: | ||
| 449 | * 2014-11-11, SRS 1.0.5, 2700clients, 85%CPU, 66MB. (1.0 equals 2.0.12) | 450 | * 2014-11-11, SRS 1.0.5, 2700clients, 85%CPU, 66MB. (1.0 equals 2.0.12) |
| 450 | * 2014-11-12, SRS 2.0.14, 2700clients, 69%CPU, 59MB. | 451 | * 2014-11-12, SRS 2.0.14, 2700clients, 69%CPU, 59MB. |
| 451 | * 2014-11-12, SRS 2.0.14, 3500clients, 95%CPU, 78MB. | 452 | * 2014-11-12, SRS 2.0.14, 3500clients, 95%CPU, 78MB. |
| 453 | +* 2014-11-13, SRS 2.0.15, 6000clients, 82%CPU, 203MB. | ||
| 452 | 454 | ||
| 453 | Latest benchmark(2014-07-12): | 455 | Latest benchmark(2014-07-12): |
| 454 | 456 |
| @@ -525,10 +525,7 @@ int SrsRtmpConn::playing(SrsSource* source) | @@ -525,10 +525,7 @@ int SrsRtmpConn::playing(SrsSource* source) | ||
| 525 | int64_t starttime = -1; | 525 | int64_t starttime = -1; |
| 526 | 526 | ||
| 527 | while (true) { | 527 | while (true) { |
| 528 | - // collect elapse for pithy print. | ||
| 529 | - pithy_print.elapse(); | ||
| 530 | - | ||
| 531 | - // to use isolate thread to recv, can improve about 5% performance. | 528 | + // TODO: to use isolate thread to recv, can improve about 5% performance. |
| 532 | // @see: https://github.com/winlinvip/simple-rtmp-server/issues/196 | 529 | // @see: https://github.com/winlinvip/simple-rtmp-server/issues/196 |
| 533 | // read from client. | 530 | // read from client. |
| 534 | if (true) { | 531 | if (true) { |
| @@ -539,6 +536,7 @@ int SrsRtmpConn::playing(SrsSource* source) | @@ -539,6 +536,7 @@ int SrsRtmpConn::playing(SrsSource* source) | ||
| 539 | if (ret == ERROR_SOCKET_TIMEOUT) { | 536 | if (ret == ERROR_SOCKET_TIMEOUT) { |
| 540 | // it's ok, do nothing. | 537 | // it's ok, do nothing. |
| 541 | ret = ERROR_SUCCESS; | 538 | ret = ERROR_SUCCESS; |
| 539 | + srs_verbose("recv timeout, ignore. ret=%d", ret); | ||
| 542 | } else if (ret != ERROR_SUCCESS) { | 540 | } else if (ret != ERROR_SUCCESS) { |
| 543 | if (!srs_is_client_gracefully_close(ret)) { | 541 | if (!srs_is_client_gracefully_close(ret)) { |
| 544 | srs_error("recv client control message failed. ret=%d", ret); | 542 | srs_error("recv client control message failed. ret=%d", ret); |
| @@ -554,6 +552,9 @@ int SrsRtmpConn::playing(SrsSource* source) | @@ -554,6 +552,9 @@ int SrsRtmpConn::playing(SrsSource* source) | ||
| 554 | } | 552 | } |
| 555 | } | 553 | } |
| 556 | 554 | ||
| 555 | + // collect elapse for pithy print. | ||
| 556 | + pithy_print.elapse(); | ||
| 557 | + | ||
| 557 | // get messages from consumer. | 558 | // get messages from consumer. |
| 558 | int count = 0; | 559 | int count = 0; |
| 559 | if ((ret = consumer->dump_packets(msgs.size, msgs.msgs, count)) != ERROR_SUCCESS) { | 560 | if ((ret = consumer->dump_packets(msgs.size, msgs.msgs, count)) != ERROR_SUCCESS) { |
| @@ -568,22 +569,16 @@ int SrsRtmpConn::playing(SrsSource* source) | @@ -568,22 +569,16 @@ int SrsRtmpConn::playing(SrsSource* source) | ||
| 568 | " time=%"PRId64", msgs=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d", | 569 | " time=%"PRId64", msgs=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d", |
| 569 | pithy_print.age(), count, | 570 | pithy_print.age(), count, |
| 570 | kbps->get_send_kbps(), kbps->get_send_kbps_30s(), kbps->get_send_kbps_5m(), | 571 | kbps->get_send_kbps(), kbps->get_send_kbps_30s(), kbps->get_send_kbps_5m(), |
| 571 | - kbps->get_recv_kbps(), kbps->get_recv_kbps_30s(), kbps->get_recv_kbps_5m()); | 572 | + kbps->get_recv_kbps(), kbps->get_recv_kbps_30s(), kbps->get_recv_kbps_5m() |
| 573 | + ); | ||
| 572 | } | 574 | } |
| 573 | 575 | ||
| 574 | - // sendout messages | ||
| 575 | - // @remark, becareful, all msgs must be free explicitly, | ||
| 576 | - // free by send_and_free_message or srs_freep. | ||
| 577 | - for (int i = 0; i < count; i++) { | ||
| 578 | - SrsSharedPtrMessage* msg = msgs.msgs[i]; | ||
| 579 | - | ||
| 580 | - // the send_message will free the msg, | ||
| 581 | - // so set the msgs[i] to NULL. | ||
| 582 | - msgs.msgs[i] = NULL; | ||
| 583 | - | ||
| 584 | - // only when user specifies the duration, | ||
| 585 | - // we start to collect the durations for each message. | ||
| 586 | - if (user_specified_duration_to_stop) { | 576 | + // only when user specifies the duration, |
| 577 | + // we start to collect the durations for each message. | ||
| 578 | + if (user_specified_duration_to_stop) { | ||
| 579 | + for (int i = 0; i < count; i++) { | ||
| 580 | + SrsSharedPtrMessage* msg = msgs.msgs[i]; | ||
| 581 | + | ||
| 587 | // foreach msg, collect the duration. | 582 | // foreach msg, collect the duration. |
| 588 | // @remark: never use msg when sent it, for the protocol sdk will free it. | 583 | // @remark: never use msg when sent it, for the protocol sdk will free it. |
| 589 | if (starttime < 0 || starttime > msg->header.timestamp) { | 584 | if (starttime < 0 || starttime > msg->header.timestamp) { |
| @@ -592,12 +587,23 @@ int SrsRtmpConn::playing(SrsSource* source) | @@ -592,12 +587,23 @@ int SrsRtmpConn::playing(SrsSource* source) | ||
| 592 | duration += msg->header.timestamp - starttime; | 587 | duration += msg->header.timestamp - starttime; |
| 593 | starttime = msg->header.timestamp; | 588 | starttime = msg->header.timestamp; |
| 594 | } | 589 | } |
| 595 | - | 590 | + } |
| 591 | + | ||
| 592 | + // sendout messages | ||
| 593 | + // @remark, becareful, all msgs must be free explicitly, | ||
| 594 | + // free by send_and_free_message or srs_freep. | ||
| 595 | + if (count > 0) { | ||
| 596 | // no need to assert msg, for the rtmp will assert it. | 596 | // no need to assert msg, for the rtmp will assert it. |
| 597 | - if ((ret = rtmp->send_and_free_message(msg, res->stream_id)) != ERROR_SUCCESS) { | ||
| 598 | - srs_error("send message to client failed. ret=%d", ret); | ||
| 599 | - return ret; | ||
| 600 | - } | 597 | + ret = rtmp->send_and_free_messages(msgs.msgs, count, res->stream_id); |
| 598 | + } | ||
| 599 | + for (int i = 0; i < count; i++) { | ||
| 600 | + // the send_message will free the msg, | ||
| 601 | + // so set the msgs[i] to NULL. | ||
| 602 | + msgs.msgs[i] = NULL; | ||
| 603 | + } | ||
| 604 | + if (ret != ERROR_SUCCESS) { | ||
| 605 | + srs_error("send messages to client failed. ret=%d", ret); | ||
| 606 | + return ret; | ||
| 601 | } | 607 | } |
| 602 | 608 | ||
| 603 | // if duration specified, and exceed it, stop play live. | 609 | // if duration specified, and exceed it, stop play live. |
| @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 31 | // current release version | 31 | // current release version |
| 32 | #define VERSION_MAJOR 2 | 32 | #define VERSION_MAJOR 2 |
| 33 | #define VERSION_MINOR 0 | 33 | #define VERSION_MINOR 0 |
| 34 | -#define VERSION_REVISION 14 | 34 | +#define VERSION_REVISION 15 |
| 35 | // server info. | 35 | // server info. |
| 36 | #define RTMP_SIG_SRS_KEY "SRS" | 36 | #define RTMP_SIG_SRS_KEY "SRS" |
| 37 | #define RTMP_SIG_SRS_ROLE "origin/edge server" | 37 | #define RTMP_SIG_SRS_ROLE "origin/edge server" |
| @@ -97,6 +97,19 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -97,6 +97,19 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 97 | // always use fmt0 as cache. | 97 | // always use fmt0 as cache. |
| 98 | //#define SRS_CONSTS_RTMP_MAX_FMT3_HEADER_SIZE 5 | 98 | //#define SRS_CONSTS_RTMP_MAX_FMT3_HEADER_SIZE 5 |
| 99 | 99 | ||
| 100 | +/** | ||
| 101 | +* for performance issue, | ||
| 102 | +* the iovs cache, @see https://github.com/winlinvip/simple-rtmp-server/issues/194 | ||
| 103 | +* iovs cache for multiple messages for each connections. | ||
| 104 | +*/ | ||
| 105 | +#define SRS_CONSTS_IOVS_MAX 1024 | ||
| 106 | +/** | ||
| 107 | +* for performance issue, | ||
| 108 | +* the c0c3 cache, @see https://github.com/winlinvip/simple-rtmp-server/issues/194 | ||
| 109 | +* c0c3 cache for multiple messages for each connections. | ||
| 110 | +*/ | ||
| 111 | +#define SRS_CONSTS_C0C3_HEADERS_MAX 4096 | ||
| 112 | + | ||
| 100 | /////////////////////////////////////////////////////////// | 113 | /////////////////////////////////////////////////////////// |
| 101 | /////////////////////////////////////////////////////////// | 114 | /////////////////////////////////////////////////////////// |
| 102 | /////////////////////////////////////////////////////////// | 115 | /////////////////////////////////////////////////////////// |
| @@ -771,6 +771,11 @@ int SrsRtmpServer::send_and_free_message(SrsMessage* msg, int stream_id) | @@ -771,6 +771,11 @@ int SrsRtmpServer::send_and_free_message(SrsMessage* msg, int stream_id) | ||
| 771 | return protocol->send_and_free_message(msg, stream_id); | 771 | return protocol->send_and_free_message(msg, stream_id); |
| 772 | } | 772 | } |
| 773 | 773 | ||
| 774 | +int SrsRtmpServer::send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id) | ||
| 775 | +{ | ||
| 776 | + return protocol->send_and_free_messages(msgs, nb_msgs, stream_id); | ||
| 777 | +} | ||
| 778 | + | ||
| 774 | int SrsRtmpServer::send_and_free_packet(SrsPacket* packet, int stream_id) | 779 | int SrsRtmpServer::send_and_free_packet(SrsPacket* packet, int stream_id) |
| 775 | { | 780 | { |
| 776 | return protocol->send_and_free_packet(packet, stream_id); | 781 | return protocol->send_and_free_packet(packet, stream_id); |
| @@ -368,6 +368,15 @@ public: | @@ -368,6 +368,15 @@ public: | ||
| 368 | */ | 368 | */ |
| 369 | virtual int send_and_free_message(SrsMessage* msg, int stream_id); | 369 | virtual int send_and_free_message(SrsMessage* msg, int stream_id); |
| 370 | /** | 370 | /** |
| 371 | + * send the RTMP message and always free it. | ||
| 372 | + * user must never free or use the msg after this method, | ||
| 373 | + * for it will always free the msg. | ||
| 374 | + * @param msgs, the msgs to send out, never be NULL. | ||
| 375 | + * @param nb_msgs, the size of msgs to send out. | ||
| 376 | + * @param stream_id, the stream id of packet to send over, 0 for control message. | ||
| 377 | + */ | ||
| 378 | + virtual int send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id); | ||
| 379 | + /** | ||
| 371 | * send the RTMP packet and always free it. | 380 | * send the RTMP packet and always free it. |
| 372 | * user must never free or use the packet after this method, | 381 | * user must never free or use the packet after this method, |
| 373 | * for it will always free the packet. | 382 | * for it will always free the packet. |
| @@ -29,6 +29,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -29,6 +29,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 29 | #include <srs_core_autofree.hpp> | 29 | #include <srs_core_autofree.hpp> |
| 30 | #include <srs_kernel_utility.hpp> | 30 | #include <srs_kernel_utility.hpp> |
| 31 | 31 | ||
| 32 | +#include <stdlib.h> | ||
| 32 | using namespace std; | 33 | using namespace std; |
| 33 | 34 | ||
| 34 | // when got a messae header, there must be some data, | 35 | // when got a messae header, there must be some data, |
| @@ -404,7 +405,15 @@ SrsProtocol::SrsProtocol(ISrsProtocolReaderWriter* io) | @@ -404,7 +405,15 @@ SrsProtocol::SrsProtocol(ISrsProtocolReaderWriter* io) | ||
| 404 | in_buffer = new SrsBuffer(); | 405 | in_buffer = new SrsBuffer(); |
| 405 | skt = io; | 406 | skt = io; |
| 406 | 407 | ||
| 407 | - in_chunk_size = out_chunk_size = SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE; | 408 | + in_chunk_size = SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE; |
| 409 | + out_chunk_size = SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE; | ||
| 410 | + | ||
| 411 | + nb_out_iovs = SRS_CONSTS_IOVS_MAX; | ||
| 412 | + out_iovs = (iovec*)malloc(sizeof(iovec) * nb_out_iovs); | ||
| 413 | + // each chunk consumers atleast 2 iovs | ||
| 414 | + srs_assert(nb_out_iovs >= 2); | ||
| 415 | + | ||
| 416 | + warned_c0c3_caches = false; | ||
| 408 | } | 417 | } |
| 409 | 418 | ||
| 410 | SrsProtocol::~SrsProtocol() | 419 | SrsProtocol::~SrsProtocol() |
| @@ -421,6 +430,12 @@ SrsProtocol::~SrsProtocol() | @@ -421,6 +430,12 @@ SrsProtocol::~SrsProtocol() | ||
| 421 | } | 430 | } |
| 422 | 431 | ||
| 423 | srs_freep(in_buffer); | 432 | srs_freep(in_buffer); |
| 433 | + | ||
| 434 | + // alloc by malloc, use free directly. | ||
| 435 | + if (out_iovs) { | ||
| 436 | + free(out_iovs); | ||
| 437 | + out_iovs = NULL; | ||
| 438 | + } | ||
| 424 | } | 439 | } |
| 425 | 440 | ||
| 426 | void SrsProtocol::set_recv_timeout(int64_t timeout_us) | 441 | void SrsProtocol::set_recv_timeout(int64_t timeout_us) |
| @@ -560,7 +575,7 @@ int SrsProtocol::do_send_message(SrsMessage* msg) | @@ -560,7 +575,7 @@ int SrsProtocol::do_send_message(SrsMessage* msg) | ||
| 560 | // always has header | 575 | // always has header |
| 561 | int nbh = 0; | 576 | int nbh = 0; |
| 562 | char* header = NULL; | 577 | char* header = NULL; |
| 563 | - generate_chunk_header(&msg->header, p == msg->payload, &nbh, &header); | 578 | + generate_chunk_header(out_c0c3_cache, &msg->header, p == msg->payload, &nbh, &header); |
| 564 | srs_assert(nbh > 0); | 579 | srs_assert(nbh > 0); |
| 565 | 580 | ||
| 566 | // header iov | 581 | // header iov |
| @@ -590,10 +605,130 @@ int SrsProtocol::do_send_message(SrsMessage* msg) | @@ -590,10 +605,130 @@ int SrsProtocol::do_send_message(SrsMessage* msg) | ||
| 590 | return ret; | 605 | return ret; |
| 591 | } | 606 | } |
| 592 | 607 | ||
| 593 | -void SrsProtocol::generate_chunk_header(SrsMessageHeader* mh, bool c0, int* pnbh, char** ph) | 608 | +int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs) |
| 594 | { | 609 | { |
| 595 | - char* cache = out_c0c3_cache; | 610 | + int ret = ERROR_SUCCESS; |
| 611 | + | ||
| 612 | + // TODO: FIXME: use cache system instead. | ||
| 613 | + int iov_index = 0; | ||
| 614 | + iovec* iov = out_iovs + iov_index; | ||
| 615 | + | ||
| 616 | + int c0c3_cache_index = 0; | ||
| 617 | + char* c0c3_cache = out_c0c3_caches + c0c3_cache_index; | ||
| 618 | + | ||
| 619 | + // try to send use the c0c3 header cache, | ||
| 620 | + // if cache is consumed, try another loop. | ||
| 621 | + for (int i = 0; i < nb_msgs; i++) { | ||
| 622 | + SrsMessage* msg = msgs[i]; | ||
| 623 | + | ||
| 624 | + // ignore empty message. | ||
| 625 | + if (!msg->payload || msg->size <= 0) { | ||
| 626 | + srs_info("ignore empty message."); | ||
| 627 | + continue; | ||
| 628 | + } | ||
| 629 | + | ||
| 630 | + // we donot use the complex basic header, | ||
| 631 | + // ensure the basic header is 1bytes. | ||
| 632 | + if (msg->header.perfer_cid < 2) { | ||
| 633 | + srs_warn("change the chunk_id=%d to default=%d", | ||
| 634 | + msg->header.perfer_cid, RTMP_CID_ProtocolControl); | ||
| 635 | + msg->header.perfer_cid = RTMP_CID_ProtocolControl; | ||
| 636 | + } | ||
| 637 | + | ||
| 638 | + // p set to current write position, | ||
| 639 | + // it's ok when payload is NULL and size is 0. | ||
| 640 | + char* p = msg->payload; | ||
| 641 | + char* pend = msg->payload + msg->size; | ||
| 642 | + | ||
| 643 | + // always write the header event payload is empty. | ||
| 644 | + while (p < pend) { | ||
| 645 | + // always has header | ||
| 646 | + int nbh = 0; | ||
| 647 | + char* header = NULL; | ||
| 648 | + generate_chunk_header(c0c3_cache, &msg->header, p == msg->payload, &nbh, &header); | ||
| 649 | + srs_assert(nbh > 0); | ||
| 650 | + | ||
| 651 | + // header iov | ||
| 652 | + iov[0].iov_base = header; | ||
| 653 | + iov[0].iov_len = nbh; | ||
| 654 | + | ||
| 655 | + // payload iov | ||
| 656 | + int payload_size = pend - p; | ||
| 657 | + if (payload_size > out_chunk_size) { | ||
| 658 | + payload_size = out_chunk_size; | ||
| 659 | + } | ||
| 660 | + iov[1].iov_base = p; | ||
| 661 | + iov[1].iov_len = payload_size; | ||
| 662 | + | ||
| 663 | + // consume sendout bytes. | ||
| 664 | + p += payload_size; | ||
| 665 | + | ||
| 666 | + // realloc the iovs if exceed, | ||
| 667 | + // for we donot know how many messges maybe to send entirely, | ||
| 668 | + // we just alloc the iovs, it's ok. | ||
| 669 | + if (iov_index >= nb_out_iovs - 2) { | ||
| 670 | + nb_out_iovs += SRS_CONSTS_IOVS_MAX; | ||
| 671 | + int realloc_size = sizeof(iovec) * nb_out_iovs; | ||
| 672 | + out_iovs = (iovec*)realloc(out_iovs, realloc_size); | ||
| 673 | + } | ||
| 674 | + | ||
| 675 | + // to next pair of iovs | ||
| 676 | + iov_index += 2; | ||
| 677 | + iov = out_iovs + iov_index; | ||
| 678 | + | ||
| 679 | + // to next c0c3 header cache | ||
| 680 | + c0c3_cache_index += nbh; | ||
| 681 | + c0c3_cache = out_c0c3_caches + c0c3_cache_index; | ||
| 682 | + | ||
| 683 | + // the cache header should never be realloc again, | ||
| 684 | + // for the ptr is set to iovs, so we just warn user to set larger | ||
| 685 | + // and use another loop to send again. | ||
| 686 | + int c0c3_left = SRS_CONSTS_C0C3_HEADERS_MAX - c0c3_cache_index; | ||
| 687 | + if (c0c3_left < SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE) { | ||
| 688 | + // only warn once for a connection. | ||
| 689 | + if (!warned_c0c3_caches) { | ||
| 690 | + srs_warn("c0c3 cache header too small, recoment to %d", | ||
| 691 | + SRS_CONSTS_C0C3_HEADERS_MAX + SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE); | ||
| 692 | + warned_c0c3_caches = true; | ||
| 693 | + } | ||
| 694 | + | ||
| 695 | + // when c0c3 cache dry, | ||
| 696 | + // sendout all messages and reset the cache, then send again. | ||
| 697 | + if ((ret = skt->writev(out_iovs, iov_index, NULL)) != ERROR_SUCCESS) { | ||
| 698 | + srs_error("send with writev failed. ret=%d", ret); | ||
| 699 | + return ret; | ||
| 700 | + } | ||
| 596 | 701 | ||
| 702 | + // reset caches, while these cache ensure | ||
| 703 | + // atleast we can sendout a chunk. | ||
| 704 | + iov_index = 0; | ||
| 705 | + iov = out_iovs + iov_index; | ||
| 706 | + | ||
| 707 | + c0c3_cache_index = 0; | ||
| 708 | + c0c3_cache = out_c0c3_caches + c0c3_cache_index; | ||
| 709 | + } | ||
| 710 | + } | ||
| 711 | + } | ||
| 712 | + | ||
| 713 | + // maybe the iovs already sendout when c0c3 cache dry, | ||
| 714 | + // so just ignore when no iovs to send. | ||
| 715 | + if (iov_index <= 0) { | ||
| 716 | + return ret; | ||
| 717 | + } | ||
| 718 | + | ||
| 719 | + // send by writev | ||
| 720 | + // sendout header and payload by writev. | ||
| 721 | + // decrease the sys invoke count to get higher performance. | ||
| 722 | + if ((ret = skt->writev(out_iovs, iov_index, NULL)) != ERROR_SUCCESS) { | ||
| 723 | + srs_error("send with writev failed. ret=%d", ret); | ||
| 724 | + return ret; | ||
| 725 | + } | ||
| 726 | + | ||
| 727 | + return ret; | ||
| 728 | +} | ||
| 729 | + | ||
| 730 | +void SrsProtocol::generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, int* pnbh, char** ph) | ||
| 731 | +{ | ||
| 597 | // to directly set the field. | 732 | // to directly set the field. |
| 598 | char* pp = NULL; | 733 | char* pp = NULL; |
| 599 | 734 | ||
| @@ -856,6 +991,34 @@ int SrsProtocol::send_and_free_message(SrsMessage* msg, int stream_id) | @@ -856,6 +991,34 @@ int SrsProtocol::send_and_free_message(SrsMessage* msg, int stream_id) | ||
| 856 | return ret; | 991 | return ret; |
| 857 | } | 992 | } |
| 858 | 993 | ||
| 994 | +int SrsProtocol::send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id) | ||
| 995 | +{ | ||
| 996 | + // always not NULL msg. | ||
| 997 | + srs_assert(msgs); | ||
| 998 | + srs_assert(nb_msgs > 0); | ||
| 999 | + | ||
| 1000 | + // update the stream id in header. | ||
| 1001 | + for (int i = 0; i < nb_msgs; i++) { | ||
| 1002 | + SrsMessage* msg = msgs[i]; | ||
| 1003 | + // we assume that the stream_id in a group must be the same. | ||
| 1004 | + if (msg->header.stream_id == stream_id) { | ||
| 1005 | + break; | ||
| 1006 | + } | ||
| 1007 | + msg->header.stream_id = stream_id; | ||
| 1008 | + } | ||
| 1009 | + | ||
| 1010 | + // donot use the auto free to free the msg, | ||
| 1011 | + // for performance issue. | ||
| 1012 | + int ret = do_send_messages(msgs, nb_msgs); | ||
| 1013 | + | ||
| 1014 | + for (int i = 0; i < nb_msgs; i++) { | ||
| 1015 | + SrsMessage* msg = msgs[i]; | ||
| 1016 | + srs_freep(msg); | ||
| 1017 | + } | ||
| 1018 | + | ||
| 1019 | + return ret; | ||
| 1020 | +} | ||
| 1021 | + | ||
| 859 | int SrsProtocol::send_and_free_packet(SrsPacket* packet, int stream_id) | 1022 | int SrsProtocol::send_and_free_packet(SrsPacket* packet, int stream_id) |
| 860 | { | 1023 | { |
| 861 | int ret = ERROR_SUCCESS; | 1024 | int ret = ERROR_SUCCESS; |
| @@ -47,6 +47,7 @@ class SrsAmf0Any; | @@ -47,6 +47,7 @@ class SrsAmf0Any; | ||
| 47 | class SrsMessageHeader; | 47 | class SrsMessageHeader; |
| 48 | class SrsMessage; | 48 | class SrsMessage; |
| 49 | class SrsChunkStream; | 49 | class SrsChunkStream; |
| 50 | +class SrsSharedPtrMessage; | ||
| 50 | 51 | ||
| 51 | /** | 52 | /** |
| 52 | * 4.1. Message Header | 53 | * 4.1. Message Header |
| @@ -221,6 +222,15 @@ private: | @@ -221,6 +222,15 @@ private: | ||
| 221 | */ | 222 | */ |
| 222 | iovec out_iov[2]; | 223 | iovec out_iov[2]; |
| 223 | /** | 224 | /** |
| 225 | + * cache for multiple messages send | ||
| 226 | + */ | ||
| 227 | + iovec* out_iovs; | ||
| 228 | + int nb_out_iovs; | ||
| 229 | + // the c0c3 cache cannot be realloc. | ||
| 230 | + char out_c0c3_caches[SRS_CONSTS_C0C3_HEADERS_MAX]; | ||
| 231 | + // whether warned user to increase the c0c3 header cache. | ||
| 232 | + bool warned_c0c3_caches; | ||
| 233 | + /** | ||
| 224 | * output chunk size, default to 128, set by config. | 234 | * output chunk size, default to 128, set by config. |
| 225 | */ | 235 | */ |
| 226 | int32_t out_chunk_size; | 236 | int32_t out_chunk_size; |
| @@ -276,6 +286,15 @@ public: | @@ -276,6 +286,15 @@ public: | ||
| 276 | */ | 286 | */ |
| 277 | virtual int send_and_free_message(SrsMessage* msg, int stream_id); | 287 | virtual int send_and_free_message(SrsMessage* msg, int stream_id); |
| 278 | /** | 288 | /** |
| 289 | + * send the RTMP message and always free it. | ||
| 290 | + * user must never free or use the msg after this method, | ||
| 291 | + * for it will always free the msg. | ||
| 292 | + * @param msgs, the msgs to send out, never be NULL. | ||
| 293 | + * @param nb_msgs, the size of msgs to send out. | ||
| 294 | + * @param stream_id, the stream id of packet to send over, 0 for control message. | ||
| 295 | + */ | ||
| 296 | + virtual int send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id); | ||
| 297 | + /** | ||
| 279 | * send the RTMP packet and always free it. | 298 | * send the RTMP packet and always free it. |
| 280 | * user must never free or use the packet after this method, | 299 | * user must never free or use the packet after this method, |
| 281 | * for it will always free the packet. | 300 | * for it will always free the packet. |
| @@ -349,6 +368,11 @@ private: | @@ -349,6 +368,11 @@ private: | ||
| 349 | */ | 368 | */ |
| 350 | virtual int do_send_message(SrsMessage* msg); | 369 | virtual int do_send_message(SrsMessage* msg); |
| 351 | /** | 370 | /** |
| 371 | + * send out the messages, donot free it, | ||
| 372 | + * the caller must free the param msgs. | ||
| 373 | + */ | ||
| 374 | + virtual int do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs); | ||
| 375 | + /** | ||
| 352 | * generate the chunk header for msg. | 376 | * generate the chunk header for msg. |
| 353 | * @param mh, the header of msg to send. | 377 | * @param mh, the header of msg to send. |
| 354 | * @param c0, whether the first chunk, the c0 chunk. | 378 | * @param c0, whether the first chunk, the c0 chunk. |
| @@ -356,7 +380,7 @@ private: | @@ -356,7 +380,7 @@ private: | ||
| 356 | * @param ph, output the header cache. | 380 | * @param ph, output the header cache. |
| 357 | * user should never free it, it's cached header. | 381 | * user should never free it, it's cached header. |
| 358 | */ | 382 | */ |
| 359 | - virtual void generate_chunk_header(SrsMessageHeader* mh, bool c0, int* pnbh, char** ph); | 383 | + virtual void generate_chunk_header(char* cache, SrsMessageHeader* mh, bool c0, int* pnbh, char** ph); |
| 360 | /** | 384 | /** |
| 361 | * imp for decode_message | 385 | * imp for decode_message |
| 362 | */ | 386 | */ |
-
请 注册 或 登录 后发表评论