#include #include #include #include "sockutil.h" #include "sys/atomic.h" #include "sys/thread.h" #include "sys/system.h" #include "sys/pollfd.h" #include "port/network.h" #include "port/ip-route.h" #include "sip-uac.h" #include "sip-uas.h" #include "sip-message.h" #include "sip-transport.h" #include "sip-timer.h" #include "port/ip-route.h" #include "http-parser.h" #include "http-header-auth.h" #include "rtp.h" #include "rtp-profile.h" #include "rtp-payload.h" #include "mov-format.h" #include "mpeg4-aac.h" #include "mpeg4-avc.h" #include "mpeg4-hevc.h" #include "stun-proto.h" #include "ice-agent.h" #include "stun-agent.h" #include "uri-parse.h" #include "cstringext.h" #include "base64.h" #include "time64.h" #include "sdp.h" #include "md5.h" #include "aio-timeout.h" #include #include #include #include #include "rtsp-media.h" #include "rtp-sender.h" #include "../test/ice-transport.h" #include "../test/media/mp4-file-reader.h" #define SIP_PWD "1234" #define SIP_HOST "119.23.15.234" #define SIP_FROM "sip:1002@127.0.0.1:5061" #define SIP_PEER "sip:1001@192.168.1.100" #define SIP_EXPIRED 60 #define TURN_SERVER NULL #define TURN_USR "test" #define TURN_PWD "123456" extern "C" void rtp_receiver_test(socket_t rtp[2], const char* peer, int peerport[2], int payload, const char* encoding); static int rtp_packet_send(void* param, const void *packet, int bytes, uint32_t timestamp, int flags); struct sip_uac_transport_address_t { socket_t udp; socket_t tcp; socklen_t addrlen; struct sockaddr_storage addr; }; struct sip_uac_test2_t; struct sip_uac_test2_session_t : public VodFileSource::IListener { struct sip_uac_test2_t *ctx; char buffer[2 * 1024 * 1024]; std::string user; std::string from; struct sip_uas_transaction_t* t; std::shared_ptr tuac; struct ice_transport_t* avt; struct sip_uac_transport_address_t transport; struct rtsp_media_t medias[3]; // peer sdp media int nmedia; struct av_media_t { int stream; // stream index, base 0 int connected; struct sip_uac_test2_session_t* s; char sdp[1204]; struct rtsp_media_t* m; int fmt; enum AVPACKET_CODEC_ID codec; void* decoder; struct rtp_sender_t sender; time64_t clock; int track; FILE* fp; union { struct mpeg4_aac_t aac; struct mpeg4_avc_t avc; struct mpeg4_hevc_t hevc; } u; } audio, video; bool running; pthread_t th; std::shared_ptr pkts; std::shared_ptr source; virtual int OnPacket(struct avpacket_t* pkt) { return pkts->Push(pkt); } }; struct sip_uac_test2_t { bool running; char usr[64]; char local[65]; socket_t udp; socket_t tcp; struct sip_agent_t* sip; struct sip_uac_transport_address_t transport; // for register struct http_header_www_authenticate_t auth; int nonce_count; char callid[64]; int cseq; ThreadLocker locker; typedef std::map > TSessions; TSessions sessions; }; static int sip_uac_transport_via(void* transport, const char* destination, char protocol[16], char local[128], char dns[128]) { int r; char ip[65]; u_short port; struct uri_t* uri; struct sip_uac_transport_address_t *t = (struct sip_uac_transport_address_t *)transport; // rfc3263 4.1 Selecting a Transport Protocol // 1. If the URI specifies a transport protocol in the transport parameter, // that transport protocol SHOULD be used. Otherwise, if no transport // protocol is specified, but the TARGET(maddr) is a numeric IP address, // the client SHOULD use UDP for a SIP URI, and TCP for a SIPS URI. // 2. if no transport protocol is specified, and the TARGET is not numeric, // but an explicit port is provided, the client SHOULD use UDP for a SIP URI, // and TCP for a SIPS URI // 3. Otherwise, if no transport protocol or port is specified, and the target // is not a numeric IP address, the client SHOULD perform a NAPTR query for // the domain in the URI. // The client SHOULD try the first record. If an attempt should fail, based on // the definition of failure in Section 4.3, the next SHOULD be tried, and if // that should fail, the next SHOULD be tried, and so on. t->addrlen = sizeof(t->addr); memset(&t->addr, 0, sizeof(t->addr)); strcpy(protocol, socket_invalid == t->tcp ? "UDP" : "TCP"); uri = uri_parse(destination, strlen(destination)); if (!uri) return -1; // invalid uri // rfc3263 4-Client Usage (p5) // once a SIP server has successfully been contacted (success is defined below), // all retransmissions of the SIP request and the ACK for non-2xx SIP responses // to INVITE MUST be sent to the same host. // Furthermore, a CANCEL for a particular SIP request MUST be sent to the same // SIP server that the SIP request was delivered to. // TODO: sips port r = socket_addr_from(&t->addr, &t->addrlen, uri->host, uri->port ? uri->port : SIP_PORT); if (0 == r) { socket_addr_to((struct sockaddr*)&t->addr, t->addrlen, ip, &port); socket_getname(socket_invalid == t->tcp ? t->udp : t->tcp, local, &port); r = ip_route_get(ip, local); if (0 == r) { dns[0] = 0; struct sockaddr_storage ss; socklen_t len = sizeof(ss); if (0 == socket_addr_from(&ss, &len, local, port)) socket_addr_name((struct sockaddr*)&ss, len, dns, 128); if (SIP_PORT != port) snprintf(local + strlen(local), 128 - strlen(local), ":%hu", port); if (NULL == strchr(dns, '.')) snprintf(dns, 128, "%s", local); // don't have valid dns } } uri_free(uri); return r; } static int sip_uac_transport_send(void* transport, const void* data, size_t bytes) { struct sip_uac_transport_address_t *t = (struct sip_uac_transport_address_t *)transport; //char p1[1024]; //char p2[1024]; ((char*)data)[bytes] = 0; //printf("%s\n", (const char*)data); int r = socket_sendto(socket_invalid == t->tcp ? t->udp : t->tcp, data, bytes, 0, (struct sockaddr*)&t->addr, t->addrlen); return r == bytes ? 0 : -1; } static int sip_uas_transport_send(void* param, const struct cstring_t* /*protocol*/, const struct cstring_t* peer, const struct cstring_t* received, int rport, const void* data, int bytes) { struct sip_uac_test2_t *test = (struct sip_uac_test2_t *)param; std::shared_ptr uri(uri_parse(peer->p, peer->n), uri_free); if (!uri.get()) return -1; // invalid uri socklen_t addrlen; struct sockaddr_storage addr; int r = socket_addr_from(&addr, &addrlen, cstrvalid(received) ? received->p : uri->host, rport > 0 ? rport : (uri->port ? uri->port : SIP_PORT)); if (0 != r) return -1; // invalid //char p1[1024]; //char p2[1024]; ((char*)data)[bytes] = 0; //printf("%s\n\n", (const char*)data); r = socket_sendto(socket_invalid == test->tcp ? test->udp : test->tcp, data, bytes, 0, (struct sockaddr*)&addr, addrlen); return r == bytes ? 0 : -1; } static int sdp_media_negotiate(const struct rtsp_media_t* m) { int i, j; const int payloads[] = { RTP_PAYLOAD_PCMA, RTP_PAYLOAD_PCMU }; const char* payloads2[] = { "H264", "H265", "MP4V-ES", "MP4A-LATM", "MPEG4-GENERIC", "opus" }; assert(0 == strcasecmp("IP4", m->addrtype) || 0 == strcasecmp("IP6", m->addrtype)); for (i = 0; i < m->avformat_count; i++) { if(m->avformats[i].fmt < RTP_PAYLOAD_DYNAMIC) { //for(j = 0; j < sizeof(payloads)/sizeof(payloads[0]); j++) //{ // if(payloads[j] == m->avformats[i].fmt) // return i; //} } else { for(j = 0; j < sizeof(payloads2)/sizeof(payloads2[0]); j++) { if(0 == strcmp(m->avformats[i].encoding, payloads2[j])) return i; } } } return -1; } //static uint8_t sdp_media_find(const struct rtsp_media_t* medias, int count, const char* media, uint8_t payload, const char* encoding) //{ // int i, j; // const struct rtsp_media_t* m; // for (i = 0; i < count; i++) // { // m = medias + i; // if(0 != strcmp(m->media, media)) // continue; // // if (payload < RTP_PAYLOAD_DYNAMIC) // { // for (j = 0; j < m->avformat_count; j++) // { // if (payload == m->avformats[j].fmt) // return payload; // } // } // else // { // for (j = 0; j < m->avformat_count; j++) // { // if (0 == strcmp(m->avformats[j].encoding, encoding)) // return m->avformats[j].fmt; // } // } // } // return payload; // invalid //} static void mp4_onvideo(void* param, uint32_t track, uint8_t object, int width, int height, const void* extra, size_t bytes) { sip_uac_test2_session_t* s = (sip_uac_test2_session_t*)param; s->video.track = track; s->video.fp = fopen("sipvideo.h264", "wb"); s->video.s = s; s->video.sender.onpacket = rtp_packet_send; s->video.sender.param = &s->video; char ip[SOCKET_ADDRLEN]; u_short port; struct sockaddr_storage addr[2]; assert(0 == ice_transport_getaddr(s->avt, s->video.stream, 1, &addr[0])); assert(0 == ice_transport_getaddr(s->avt, s->video.stream, 2, &addr[1])); socket_addr_to((struct sockaddr*)&addr[0], socket_addr_len((struct sockaddr*)&addr[0]), ip, &port); if (MOV_OBJECT_H264 == object) { s->video.codec = AVCODEC_VIDEO_H264; mpeg4_avc_decoder_configuration_record_load((const uint8_t*)extra, bytes, &s->video.u.avc); rtp_sender_init_video(&s->video.sender, "RTP/AVP", port, RTP_PAYLOAD_H264, "H264", 90000, extra, bytes); } else if (MOV_OBJECT_HEVC == object) { s->video.codec = AVCODEC_VIDEO_H265; mpeg4_hevc_decoder_configuration_record_load((const uint8_t*)extra, bytes, &s->video.u.hevc); rtp_sender_init_video(&s->video.sender, "RTP/AVP", port, RTP_PAYLOAD_H265, "H265", 90000, extra, bytes); } else if (MOV_OBJECT_MP4V == object) { s->video.codec = AVCODEC_VIDEO_MPEG4; rtp_sender_init_video(&s->video.sender, "RTP/AVP", port, RTP_PAYLOAD_MP4V, "MP4V-ES", 90000, extra, bytes); } else { assert(0); return; } int n = snprintf(s->video.sdp, sizeof(s->video.sdp), "%s", (char*)s->video.sender.buffer); n += ice_transport_getsdp(s->avt, s->video.stream, (char*)s->video.sender.buffer + n, sizeof(s->video.sender.buffer) - n); } static void mp4_onaudio(void* param, uint32_t track, uint8_t object, int channel_count, int bit_per_sample, int sample_rate, const void* extra, size_t bytes) { sip_uac_test2_session_t* s = (sip_uac_test2_session_t*)param; s->audio.track = track; s->audio.fp = fopen("sipaudio.pcm", "wb"); s->audio.s = s; s->audio.sender.onpacket = rtp_packet_send; s->audio.sender.param = &s->audio; char ip[SOCKET_ADDRLEN]; u_short port; struct sockaddr_storage addr[2]; assert(0 == ice_transport_getaddr(s->avt, s->audio.stream, 1, &addr[0])); assert(0 == ice_transport_getaddr(s->avt, s->audio.stream, 2, &addr[1])); socket_addr_to((struct sockaddr*)&addr[0], socket_addr_len((struct sockaddr*)&addr[0]), ip, &port); if (MOV_OBJECT_AAC == object || MOV_OBJECT_AAC_LOW == object) { s->audio.codec = AVCODEC_AUDIO_AAC; mpeg4_aac_audio_specific_config_load((const uint8_t*)extra, bytes, &s->audio.u.aac); rtp_sender_init_audio(&s->audio.sender, "RTP/AVP", port, RTP_PAYLOAD_LATM, "MP4A-LATM", sample_rate, channel_count, extra, bytes); } else if (MOV_OBJECT_OPUS == object) { s->audio.codec = AVCODEC_AUDIO_OPUS; rtp_sender_init_audio(&s->audio.sender, "RTP/AVP", port, RTP_PAYLOAD_OPUS, "opus", sample_rate, channel_count, extra, bytes); } else if (MOV_OBJECT_G711u == object) { s->audio.codec = AVCODEC_AUDIO_PCM; rtp_sender_init_audio(&s->audio.sender, "RTP/AVP", port, RTP_PAYLOAD_PCMU, "", sample_rate, channel_count, extra, bytes); } else if (MOV_OBJECT_G711a == object) { s->audio.codec = AVCODEC_AUDIO_PCM; rtp_sender_init_audio(&s->audio.sender, "RTP/AVP", port, RTP_PAYLOAD_PCMA, "", sample_rate, channel_count, extra, bytes); } else { assert(0); return; } int n = snprintf(s->audio.sdp, sizeof(s->audio.sdp), "%s", (char*)s->audio.sender.buffer); n += ice_transport_getsdp(s->avt, s->audio.stream, (char*)s->audio.sender.buffer + n, sizeof(s->audio.sender.buffer) - n); } static void ice_transport_onconnected(void* param, uint64_t flags, uint64_t mask) { struct sip_uac_test2_session_t* s = (struct sip_uac_test2_session_t*)param; for (int stream = 0; stream < 2; stream++) { sip_uac_test2_session_t::av_media_t* av = s->video.stream == stream ? &s->video : &s->audio; av->connected = (flags & ((int64_t)1 << stream)) ? 1 : 0; //for (int component = 0; component < 2; component++) //{ // assert(0 == ice_transport_get_candidate(s->ice, av->stream, component + 1, &av->local[component])); //} } printf("ice_transport_onconnected 0x%x\n", (unsigned int)flags); s->source->Play(); // TODO: reinvite } static void ice_transport_onbind(void* param, int code) { const char* pattern = "v=0\n" "o=%s 0 0 IN IP4 %s\n" "s=Talk\n" "c=IN IP4 %s\n" "t=0 0\n" "%s%s"; // audio/video char reply[4*1024]; struct sip_uac_test2_session_t* s = (struct sip_uac_test2_session_t*)param; if (0 == code) { std::shared_ptr reader(new MP4FileReader("e:\\video\\mp4\\name.opus.mp4")); struct mov_reader_trackinfo_t info = { mp4_onvideo, mp4_onaudio }; reader->GetInfo(&info, s); std::shared_ptr listener(s); // fixme: s->addref() std::shared_ptr source(new VodFileSource(reader, listener)); s->source = source; // default connect address u_short port; char host[SOCKET_ADDRLEN]; struct sockaddr_storage addr; ice_transport_getaddr(s->avt, 0, 1, &addr); socket_addr_to((struct sockaddr*)&addr, socket_addr_len((struct sockaddr*)&addr), host, &port); // TODO: PRACK add 100rel/precondiation //sip_uac_add_header(s->tuac, "Supported", "100ref"); //sip_uac_add_header(s->tuac, "Supported", "precondition"); // TODO: add Recv-Info //sip_uac_add_header(s->tuac, "Recv-Info", ""); // TODO: add Allow-Events //sip_uac_add_header(s->tuac, "Allow-Events", ""); sip_uas_add_header(s->t, "Content-Type", "application/sdp"); //sip_uas_add_header(s->t, "Contact", "sip:" SIP_USR "@" LOCAL_HOST); snprintf(reply, sizeof(reply), pattern, s->user.c_str(), host, host, s->audio.m ? (char*)s->audio.sender.buffer : "", s->video.m ? (char*)s->video.sender.buffer : ""); assert(0 == sip_uas_reply(s->t, 200, reply, strlen(reply), s->ctx)); ice_transport_connect(s->avt, s->medias, s->nmedia); } else { assert(0 == sip_uas_reply(s->t, 501, "Internal Server Error", 21, s->ctx)); } } static void ice_transport_ondata(void* param, int stream, int component, const void* data, int bytes) { struct sip_uac_test2_session_t* s = (struct sip_uac_test2_session_t*)param; sip_uac_test2_session_t::av_media_t* av = s->audio.stream == stream ? &s->audio : &s->video; if (1 == component) { assert(rtp_payload_decode_input(av->decoder, data, bytes) >= 0); //assert(0 == rtp_onreceived(av->sender.rtp, data, bytes)); } else { //assert(0 == rtp_onreceived_rtcp(av->decoder, data, bytes)); } } static int rtp_packet_onrecv(void* param, const void *packet, int bytes, uint32_t timestamp, int flags) { sip_uac_test2_session_t::av_media_t* av = (sip_uac_test2_session_t::av_media_t*)param; return bytes == fwrite(packet, 1, bytes, av->fp) ? 0 : ferror(av->fp); } static int rtp_packet_send(void* param, const void *packet, int bytes, uint32_t timestamp, int flags) { sip_uac_test2_session_t::av_media_t* av = (sip_uac_test2_session_t::av_media_t*)param; return ice_transport_send(av->s->avt, av->stream, 1, packet, bytes); } static int rtcp_report(struct sip_uac_test2_session_t::av_media_t* av, time64_t clock) { int r; int interval = rtp_rtcp_interval(av->sender.rtp); if (av->clock + interval < clock) { r = rtp_rtcp_report(av->sender.rtp, av->sender.buffer, sizeof(av->sender.buffer)); r = ice_transport_send(av->s->avt, av->stream, 2, av->sender.buffer, r); //printf("rtcp_report: %d\n", r); av->clock = clock; } return 0; } static int STDCALL sip_work_thread(void* param) { time64_t clock; struct sip_uac_test2_session_t* s = (struct sip_uac_test2_session_t*)param; while (s->running) { clock = time64_now(); // RTCP report if (s->audio.sender.rtp && s->audio.connected) rtcp_report(&s->audio, clock); if (s->video.sender.rtp && s->video.connected) rtcp_report(&s->video, clock); for (std::shared_ptr pkt(s->pkts->FrontWait(0), avpacket_release); pkt.get(); pkt.reset(s->pkts->FrontWait(0), avpacket_release)) { if (pkt->stream->stream == s->audio.track && s->audio.m) { uint32_t timestamp = s->audio.sender.timestamp + (uint32_t)(pkt->pts * (s->audio.sender.frequency / 1000) /*kHz*/); rtp_payload_encode_input(s->audio.sender.encoder, pkt->data, pkt->size, timestamp); //printf("send audio[%d] packet pts: %" PRId64 ", timestamp: %u\n", s->audio.sender.frequency, pkt->pts, timestamp); } else if (pkt->stream->stream == s->video.track && s->video.m) { int n = h264_mp4toannexb(&s->video.u.avc, pkt->data, pkt->size, s->buffer, sizeof(s->buffer)); uint32_t timestamp = s->video.sender.timestamp + (uint32_t)(pkt->pts * (s->video.sender.frequency / 1000) /*kHz*/); rtp_payload_encode_input(s->video.sender.encoder, s->buffer, n, timestamp); //printf("send video[%d] packet pts: %" PRId64 ", timestamp: %u\n", s->video.sender.frequency, pkt->pts, timestamp); } else { //assert(0); // ignore } s->pkts->Pop(); } } //size_t n = rtp_rtcp_bye(m->rtp, rtcp, sizeof(rtcp)); s->source->Pause(); return 0; } static int sip_uas_oninvite_indialog(sip_uac_test2_t* ctx, sip_uac_test2_session_t* s, const struct sip_message_t* req, struct sip_uas_transaction_t* t, struct sip_dialog_t* dialog, const void* data, int bytes, void** session) { const char* pattern = "v=0\n" "o=%s 0 0 IN IP4 %s\n" "s=Talk\n" "c=IN IP4 %s\n" "t=0 0\n" "%s%s"; // audio/video // default connect address u_short port; char host[SOCKET_ADDRLEN]; struct sockaddr_storage addr; ice_transport_getaddr(s->avt, 0, 1, &addr); socket_addr_to((struct sockaddr*)&addr, socket_addr_len((struct sockaddr*)&addr), host, &port); char reply[4 * 1024]; int n = snprintf(reply, sizeof(reply), pattern, s->user.c_str(), host, host, "", ""); if (s->audio.m) { n += snprintf(reply + n, sizeof(reply) - n, "%s", s->audio.sdp); n += ice_transport_getsdp(s->avt, s->audio.stream, reply + n, sizeof(reply) - n); } if (s->video.m) { n += snprintf(reply + n, sizeof(reply) - n, "%s", s->video.sdp); n += ice_transport_getsdp(s->avt, s->video.stream, reply + n, sizeof(reply) - n); } *session = s; s->t = t; sip_uas_add_header(t, "Content-Type", "application/sdp"); //sip_uas_add_header(s->t, "Contact", "sip:" SIP_USR "@" LOCAL_HOST); assert(0 == sip_uas_reply(t, 200, reply, strlen(reply), ctx)); return 0; } static int sip_uas_oninvite(void* param, const struct sip_message_t* req, struct sip_uas_transaction_t* t, struct sip_dialog_t* dialog, const void* data, int bytes, void** session) { sip_uac_test2_t* ctx = (sip_uac_test2_t*)param; const cstring_t* h = sip_message_get_header_by_name(req, "Content-Type"); if (0 != cstrcasecmp(h, "application/sdp")) { assert(0); return 0; // discard } std::shared_ptr s(new sip_uac_test2_session_t()); s->from = std::string(req->from.uri.host.p, req->from.uri.host.n); { sip_uac_test2_t::TSessions::iterator it; AutoThreadLocker locker(ctx->locker); it = ctx->sessions.find(s->from); if (ctx->sessions.end() != it) { if(!dialog) return 0; // ignore // in dialog return sip_uas_oninvite_indialog(ctx, it->second.get(), req, t, dialog, data, bytes, session); } ctx->sessions.insert(std::make_pair(s->from, s)); } struct ice_transport_handler_t handler = { ice_transport_ondata, ice_transport_onbind, ice_transport_onconnected, }; s->pkts = std::shared_ptr(new AVPacketQueue(200)); s->avt = ice_transport_create(0, &handler, s.get()); memset(&s->audio, 0, sizeof(s->audio)); memset(&s->video, 0, sizeof(s->video)); s->user = ctx->usr; s->ctx = ctx; s->t = t; s->nmedia = rtsp_media_sdp((const char*)data, bytes, s->medias, sizeof(s->medias) / sizeof(s->medias[0])); for (int i = 0; i < s->nmedia; i++) { struct rtsp_media_t* m = s->medias + i; if (0 == strcmp("audio", m->media)) { int j = sdp_media_negotiate(m); if(-1 == j) continue; struct rtp_payload_t handler; handler.alloc = NULL; handler.free = NULL; handler.packet = rtp_packet_onrecv; s->audio.decoder = rtp_payload_decode_create(m->avformats[j].fmt, m->avformats[j].encoding, &handler, &s->audio); if (NULL == s->audio.decoder) { assert(0); continue; // ignore } s->audio.m = m; s->audio.fmt = j; s->audio.stream = i; //socket_addr_from(&s->audio.remote[0], &addrlen, m->address, m->port[0]); //socket_addr_from(&s->audio.remote[1], &addrlen, m->address, m->port[1]); } else if (0 == strcmp("video", m->media)) { int j = sdp_media_negotiate(m); if (-1 == j) continue; struct rtp_payload_t handler; handler.alloc = NULL; handler.free = NULL; handler.packet = rtp_packet_onrecv; s->video.decoder = rtp_payload_decode_create(m->avformats[j].fmt, m->avformats[j].encoding, &handler, &s->video); if (NULL == s->video.decoder) { assert(0); continue; // ignore } s->video.m = m; s->video.fmt = j; s->video.stream = i; //socket_addr_from(&s->video.remote[0], &addrlen, m->address, m->port[0]); //socket_addr_from(&s->video.remote[1], &addrlen, m->address, m->port[1]); } } struct sockaddr_storage stun; memset(&stun, 0, sizeof(stun)); socket_addr_from_ipv4((struct sockaddr_in*)&stun, TURN_SERVER, STUN_PORT); ice_transport_bind(s->avt, s->nmedia, 2, TURN_SERVER ? (struct sockaddr*)&stun : NULL, 0, TURN_USR, TURN_PWD); // std::shared_ptr pkts(new AVPacketQueue(200)); // std::shared_ptr reader(new MP4FileReader("/Users/ireader/video/opus.mp4")); // struct mov_reader_trackinfo_t info = { mp4_onvideo, mp4_onaudio}; // reader->GetInfo(&info, s.get()); // std::shared_ptr source(new VodFileSource(reader, pkts)); // // s->pkts = pkts; //s->source = source; *session = s.get(); //sip_uas_add_header(t, "Content-Type", "application/sdp"); //sip_uas_add_header(t, "Contact", "sip:" SIP_USR "@" LOCAL_HOST); // snprintf(reply, sizeof(reply), pattern, SIP_USR, LOCAL_HOST, LOCAL_HOST, s->audio.decoder?(char*)s->audio.sender.buffer:"", s->video.decoder?(char*)s->video.sender.buffer:""); //assert(0 == sip_uas_reply(t, 200, reply, strlen(reply))); s->running = true; thread_create(&s->th, sip_work_thread, s.get()); return 0; } /// @param[in] code 0-ok, other-sip status code /// @return 0-ok, other-error static int sip_uas_onack(void* param, const struct sip_message_t* req, struct sip_uas_transaction_t* t, void* session, struct sip_dialog_t* dialog, int code, const void* data, int bytes) { struct sip_uac_test2_t* ctx = (struct sip_uac_test2_t*)param; assert(ctx); sip_uac_test2_session_t* s = (sip_uac_test2_session_t*)session; assert(s); printf("sip_uas_onack[%p]: %d\n", s, code); if (200 <= code && code < 300) { //if(s->source.get()) // s->source->Play(); } else { s->running = false; if (s->source.get()) s->source->Pause(); thread_destroy(s->th); AutoThreadLocker locker(ctx->locker); ctx->sessions.erase(s->from); } return 0; } /// on terminating a session(dialog) static int sip_uas_onbye(void* param, const struct sip_message_t* req, struct sip_uas_transaction_t* t, void* session) { struct sip_uac_test2_t* ctx = (struct sip_uac_test2_t*)param; sip_uac_test2_session_t* p = (sip_uac_test2_session_t*)session; std::shared_ptr s; sip_uac_test2_t::TSessions::iterator it; { AutoThreadLocker locker(ctx->locker); it = ctx->sessions.find(p->from); if (it == ctx->sessions.end()) return sip_uas_reply(t, 481, NULL, 0, param); s = it->second; ctx->sessions.erase(it); } s->running = false; s->source->Pause(); thread_destroy(s->th); return sip_uas_reply(t, 200, NULL, 0, param); } /// cancel a transaction(should be an invite transaction) static int sip_uas_oncancel(void* param, const struct sip_message_t* req, struct sip_uas_transaction_t* t, void* session) { return sip_uas_reply(t, 200, NULL, 0, param); } /// @param[in] expires in seconds static int sip_uas_onregister(void* param, const struct sip_message_t* req, struct sip_uas_transaction_t* t, const char* user, const char* location, int expires) { return sip_uas_reply(t, 200, NULL, 0, param); } static int sip_uas_onmessage(void* param, const struct sip_message_t* req, struct sip_uas_transaction_t* t, void* session, const void* payload, int bytes) { return sip_uas_reply(t, 200, NULL, 0, param); } static void sip_uac_ice_transport_onbind(void* param, int code) { const char* pattern = "v=0\n" "o=%s 0 0 IN IP4 %s\n" "s=Talk\n" "c=IN IP4 %s\n" "t=0 0\n" "%s%s"; // audio/video char buffer[4*1024]; struct sip_uac_test2_session_t* s = (struct sip_uac_test2_session_t*)param; if (0 == code) { std::shared_ptr reader(new MP4FileReader("e:\\video\\mp4\\name.opus.mp4")); struct mov_reader_trackinfo_t info = { mp4_onvideo, mp4_onaudio }; reader->GetInfo(&info, s); std::shared_ptr listener(s); // fixme: s->addref() std::shared_ptr source(new VodFileSource(reader, listener)); s->source = source; // default connect address u_short port; char host[SOCKET_ADDRLEN]; struct sockaddr_storage addr; ice_transport_getaddr(s->avt, 0, 1, &addr); socket_addr_to((struct sockaddr*)&addr, socket_addr_len((struct sockaddr*)&addr), host, &port); // TODO: PRACK add 100rel/precondiation //sip_uac_add_header(s->tuac, "Supported", "100ref"); //sip_uac_add_header(s->tuac, "Supported", "precondition"); // TODO: add Recv-Info //sip_uac_add_header(s->tuac, "Recv-Info", ""); // TODO: add Allow-Events //sip_uac_add_header(s->tuac, "Allow-Events", ""); sip_uac_add_header(s->tuac.get(), "Content-Type", "application/sdp"); int n = snprintf(buffer, sizeof(buffer), pattern, s->user.c_str(), host, host, (char*)s->audio.sender.buffer, (char*)s->video.sender.buffer); struct sip_transport_t t = { sip_uac_transport_via, sip_uac_transport_send, }; assert(0 == sip_uac_send(s->tuac.get(), buffer, n, &t, &s->transport)); } else { assert(0); } } static int sip_uac_oninvited(void* param, const struct sip_message_t* reply, struct sip_uac_transaction_t* t, struct sip_dialog_t* dialog, int code, void** session) { const cstring_t* h; std::shared_ptr s; struct sip_uac_test2_t *ctx = (struct sip_uac_test2_t*)param; if (reply) { sip_uac_test2_t::TSessions::iterator it; AutoThreadLocker locker(ctx->locker); it = ctx->sessions.find(SIP_PEER); if (it == ctx->sessions.end()) return 0; // ignore s = it->second; } // TODO: handle reply->recv_info if (200 <= code && code < 300) { h = sip_message_get_header_by_name(reply, "Content-Type"); if (!h || 0 != cstrcasecmp(h, "application/sdp")) { assert(0); return 0; } s->nmedia = rtsp_media_sdp((const char*)reply->payload, reply->size, s->medias, sizeof(s->medias) / sizeof(s->medias[0])); assert(0 == ice_transport_connect(s->avt, s->medias, s->nmedia)); for (int i = 0; i < s->nmedia; i++) { struct rtsp_media_t* m = s->medias + i; if (0 == strcmp("audio", m->media)) { int j = sdp_media_negotiate(m); if (-1 == j) continue; struct rtp_payload_t handler; handler.alloc = NULL; handler.free = NULL; handler.packet = rtp_packet_onrecv; s->audio.decoder = rtp_payload_decode_create(m->avformats[j].fmt, m->avformats[j].encoding, &handler, &s->audio); if (NULL == s->audio.decoder) { assert(0); continue; // ignore } assert(i == s->audio.stream); s->audio.m = m; s->audio.fmt = j; } else if (0 == strcmp("video", m->media)) { int j = sdp_media_negotiate(m); if (-1 == j) continue; struct rtp_payload_t handler; handler.alloc = NULL; handler.free = NULL; handler.packet = rtp_packet_onrecv; s->video.decoder = rtp_payload_decode_create(m->avformats[j].fmt, m->avformats[j].encoding, &handler, &s->video); if (NULL == s->video.decoder) { assert(0); continue; // ignore } assert(i == s->video.stream); s->video.m = m; s->video.fmt = j; } } s->from = SIP_PEER; s->running = true; thread_create(&s->th, sip_work_thread, s.get()); *session = s.get(); sip_uac_ack(t, NULL, 0); return 0; } else if (code == 183) { h = sip_message_get_header_by_name(reply, "Require"); if (!h || (0 != cstrcasecmp(h, "100rel") && 0 != cstrcasecmp(h, "precondition"))) { assert(0); return 0; } assert(dialog); //struct sip_uac_transaction_t* prack = sip_uac_prack(ctx->sip, dialog, sip_uac_onprack, ctx); //sip_uac_add_header(prack, "Supported", "precondition"); //sip_uac_send(prack, sdp, 0, transport, param); return 0; } else if (code >= 300) { // TODO: delete session printf("sip_uac_oninvited code: %d\n", code); return 0; } else { // trying return 0; } } static void sip_uac_invite_test(struct sip_uac_test2_t *ctx) { char buffer[1024]; std::shared_ptr t(sip_uac_invite(ctx->sip, SIP_FROM, SIP_PEER, sip_uac_oninvited, ctx), sip_uac_transaction_release); if (HTTP_AUTHENTICATION_DIGEST == ctx->auth.scheme) { ++ctx->auth.nc; snprintf(ctx->auth.uri, sizeof(ctx->auth.uri), "%s", SIP_PEER); snprintf(ctx->auth.username, sizeof(ctx->auth.username), "%s", ctx->usr); http_header_auth(&ctx->auth, SIP_PWD, "INVITE", NULL, 0, buffer, sizeof(buffer)); sip_uac_add_header(t.get(), "Proxy-Authorization", buffer); } struct ice_transport_handler_t handler = { ice_transport_ondata, sip_uac_ice_transport_onbind, ice_transport_onconnected, }; std::shared_ptr s(new sip_uac_test2_session_t()); s->pkts = std::shared_ptr(new AVPacketQueue(200)); s->avt = ice_transport_create(0, &handler, s.get()); memset(&s->audio, 0, sizeof(s->audio)); memset(&s->video, 0, sizeof(s->video)); s->user = ctx->usr; s->tuac = t; { AutoThreadLocker locker(ctx->locker); if (ctx->sessions.end() != ctx->sessions.find(SIP_PEER)) return; // ignore ctx->sessions.insert(std::make_pair(SIP_PEER, s)); } s->audio.stream = 0; s->video.stream = 1; s->transport.udp = ctx->udp; s->transport.tcp = ctx->tcp; struct sockaddr_storage stun; memset(&stun, 0, sizeof(stun)); assert(0 == socket_addr_from_ipv4((struct sockaddr_in*)&stun, TURN_SERVER, STUN_PORT)); ice_transport_bind(s->avt, 2, 2, TURN_SERVER ? (struct sockaddr*)&stun : NULL, 0, TURN_USR, TURN_PWD); } static void sip_uac_register_test(struct sip_uac_test2_t *test); static int sip_uac_onregister(void* param, const struct sip_message_t* reply, struct sip_uac_transaction_t* t, int code) { struct sip_uac_test2_t *test = (struct sip_uac_test2_t *)param; const cstring_t* h; if (0 == test->callid[0] && reply) { h = sip_message_get_header_by_name(reply, "Call-ID"); if (h) { snprintf(test->callid, sizeof(test->callid), "%.*s", (int)h->n, h->p); h = sip_message_get_header_by_name(reply, "CSeq"); test->cseq = atoi(h->p); } } if (200 <= code && code < 300) { printf("Register OK\n"); //sip_uac_invite_test(test); } else if (401 == code) { // Unauthorized memset(&test->auth, 0, sizeof(test->auth)); h = sip_message_get_header_by_name(reply, "WWW-Authenticate"); assert(0 == http_header_www_authenticate(h->p, &test->auth)); test->nonce_count = 0; switch (test->auth.scheme) { case HTTP_AUTHENTICATION_DIGEST: sip_uac_register_test(test); break; case HTTP_AUTHENTICATION_BASIC: assert(0); break; default: assert(0); } } else { } return 0; } static void sip_uac_register_test(struct sip_uac_test2_t *test) { char buffer[256]; //t = sip_uac_register(uac, "Bob ", "sip:registrar.biloxi.com", 7200, sip_uac_message_onregister, test); std::shared_ptr t(sip_uac_register(test->sip, SIP_FROM, "sip:" SIP_HOST, SIP_EXPIRED, sip_uac_onregister, test), sip_uac_transaction_release); if (test->callid[0]) { // All registrations from a UAC SHOULD use the same Call-ID sip_uac_add_header(t.get(), "Call-ID", test->callid); snprintf(buffer, sizeof(buffer), "%u REGISTER", ++test->cseq); sip_uac_add_header(t.get(), "CSeq", buffer); } if (HTTP_AUTHENTICATION_DIGEST == test->auth.scheme) { // https://blog.csdn.net/yunlianglinfeng/article/details/81109380 // http://www.voidcn.com/article/p-oqqbqgvd-bgn.html ++test->auth.nc; snprintf(test->auth.uri, sizeof(test->auth.uri), "sip:%s", SIP_HOST); snprintf(test->auth.username, sizeof(test->auth.username), "%s", test->usr); http_header_auth(&test->auth, SIP_PWD, "REGISTER", NULL, 0, buffer, sizeof(buffer)); sip_uac_add_header(t.get(), "Authorization", buffer); } struct sip_transport_t transport = { sip_uac_transport_via, sip_uac_transport_send, }; assert(0 == sip_uac_send(t.get(), NULL, 0, &transport, &test->transport)); } static int STDCALL TimerThread(void* param) { uint64_t clock = 0; struct sip_uac_test2_t* ctx = (struct sip_uac_test2_t*)param; while (ctx->running) { uint64_t now = time64_now(); if (clock + SIP_EXPIRED * 1000 < now) { clock = now; sip_uac_register_test(ctx); } system_sleep(5); } return 0; } static int sip_uac_test_process(struct sip_uac_test2_t* test) { uint8_t buffer[4 * 1024]; http_parser_t* parser; http_parser_t* request; http_parser_t* response; request = http_parser_create(HTTP_PARSER_RESPONSE, NULL, NULL); response = http_parser_create(HTTP_PARSER_REQUEST, NULL, NULL); do { socklen_t addrlen; struct sockaddr_storage addr; addrlen = sizeof(addr); memset(buffer, 0, sizeof(buffer)); int r = socket_recvfrom(socket_invalid==test->tcp ? test->udp : test->tcp, buffer, sizeof(buffer), 0, (struct sockaddr*)&addr, &addrlen); if (-1 == r && EINTR == errno) continue; printf("\n%s\n", buffer); parser = 0 == strncasecmp("SIP", (char*)buffer, 3) ? request : response; size_t n = r; assert(0 == http_parser_input(parser, buffer, &n)); struct sip_message_t* msg = sip_message_create(parser == response ? SIP_MESSAGE_REQUEST : SIP_MESSAGE_REPLY); assert(0 == sip_message_load(msg, parser)); assert(0 == sip_agent_input(test->sip, msg, test)); sip_message_destroy(msg); http_parser_clear(parser); } while (1); http_parser_destroy(request); http_parser_destroy(response); return 0; } static int sip_uas_onsubscribe(void* param, const struct sip_message_t* req, struct sip_uas_transaction_t* t, sip_subscribe_t* subscribe, void** sub) { return 0; } static int sip_uas_onnotify(void* param, const struct sip_message_t* req, struct sip_uas_transaction_t* t, void* session, const struct cstring_t* event) { return 0; } static int sip_uas_onpublish(void* param, const struct sip_message_t* req, struct sip_uas_transaction_t* t, const struct cstring_t* event) { return 0; } static int sip_uas_onrefer(void* param, const struct sip_message_t* req, struct sip_uas_transaction_t* t, void* session) { return 0; } static int sip_uas_onprack(void* param, const struct sip_message_t* req, struct sip_uas_transaction_t* t, void* session, struct sip_dialog_t* dialog, const void* data, int bytes) { return 0; } static int sip_uas_onupdate(void* param, const struct sip_message_t* req, struct sip_uas_transaction_t* t, void* session, struct sip_dialog_t* dialog, const void* data, int bytes) { return 0; } static int sip_uas_oninfo(void* param, const struct sip_message_t* req, struct sip_uas_transaction_t* t, void* session, struct sip_dialog_t* dialog, const struct cstring_t* package, const void* data, int bytes) { return 0; } void sip_uac_test2(void) { socket_init(); sip_timer_init(); struct sip_uac_test2_t test; test.running = true; test.callid[0] = 0; struct sip_uas_handler_t handler = { sip_uas_transport_send, sip_uas_onregister, sip_uas_oninvite, sip_uas_onack, sip_uas_onprack, sip_uas_onupdate, sip_uas_oninfo, sip_uas_onbye, sip_uas_oncancel, sip_uas_onsubscribe, sip_uas_onnotify, sip_uas_onpublish, sip_uas_onmessage, sip_uas_onrefer, }; assert(1 == sscanf(SIP_FROM, "sip:%[^@]", test.usr)); ip_route_get(SIP_HOST, test.local); test.udp = socket_udp(); //test.tcp = socket_connect_host(SIP_HOST, SIP_PORT, 2000); //if(socket_invalid != test.tcp) // socket_setnonblock(test.tcp, 0); test.tcp = socket_invalid; test.sip = sip_agent_create(&handler); socket_bind_any(test.udp, SIP_PORT); test.transport.udp = test.udp; test.transport.tcp = test.tcp; pthread_t th; thread_create(&th, TimerThread, &test); //sip_uac_register_test(&test); sip_uac_test_process(&test); thread_destroy(th); sip_agent_destroy(test.sip); socket_close(test.udp); socket_cleanup(); sip_timer_cleanup(); }