1199 lines
37 KiB
C++
1199 lines
37 KiB
C++
#include <map>
|
|
#include <memory>
|
|
#include <string>
|
|
#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 <inttypes.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#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<struct sip_uac_transaction_t> 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<AVPacketQueue> pkts;
|
|
std::shared_ptr<VodFileSource> 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<std::string, std::shared_ptr<sip_uac_test2_session_t> > 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));
|
|
snprintf(protocol, 16, "%s", 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_t> 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<MP4FileReader> 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<sip_uac_test2_session_t> listener(s); // fixme: s->addref()
|
|
std::shared_ptr<VodFileSource> 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<avpacket_t> 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 struct cstring_t* id, const void* data, int bytes)
|
|
{
|
|
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);
|
|
}
|
|
|
|
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* redialog, const struct cstring_t* id, const void* data, int bytes)
|
|
{
|
|
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<sip_uac_test2_session_t> 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(!redialog)
|
|
return 0; // ignore
|
|
|
|
// in dialog
|
|
return sip_uas_oninvite_indialog(ctx, it->second.get(), req, t, redialog, id, data, bytes);
|
|
}
|
|
ctx->sessions.insert(std::make_pair(std::string(id->p, id->n), s));
|
|
}
|
|
|
|
struct ice_transport_handler_t handler = {
|
|
ice_transport_ondata,
|
|
ice_transport_onbind,
|
|
ice_transport_onconnected,
|
|
};
|
|
|
|
s->pkts = std::shared_ptr<AVPacketQueue>(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<AVPacketQueue> pkts(new AVPacketQueue(200));
|
|
// std::shared_ptr<MP4FileReader> 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<VodFileSource> source(new VodFileSource(reader, pkts));
|
|
//
|
|
// s->pkts = pkts;
|
|
//s->source = source;
|
|
|
|
//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, struct sip_dialog_t* dialog, const struct cstring_t* id, int code, const void* data, int bytes)
|
|
{
|
|
struct sip_uac_test2_t* ctx = (struct sip_uac_test2_t*)param; assert(ctx);
|
|
AutoThreadLocker locker(ctx->locker);
|
|
auto it = ctx->sessions.find(std::string(id->p, id->n));
|
|
if (ctx->sessions.end() == it)
|
|
return 0;
|
|
sip_uac_test2_session_t* s = it->second.get(); 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, const struct cstring_t* id)
|
|
{
|
|
struct sip_uac_test2_t* ctx = (struct sip_uac_test2_t*)param;
|
|
|
|
std::shared_ptr<sip_uac_test2_session_t> s;
|
|
sip_uac_test2_t::TSessions::iterator it;
|
|
{
|
|
AutoThreadLocker locker(ctx->locker);
|
|
it = ctx->sessions.find(std::string(id->p, id->n));
|
|
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, const struct cstring_t* id)
|
|
{
|
|
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, 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<MP4FileReader> 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<sip_uac_test2_session_t> listener(s); // fixme: s->addref()
|
|
std::shared_ptr<VodFileSource> 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, const struct cstring_t* id, int code)
|
|
{
|
|
const cstring_t* h;
|
|
std::shared_ptr<struct sip_uac_test2_session_t> 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());
|
|
|
|
sip_uac_ack(t, NULL, 0, NULL);
|
|
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<sip_uac_transaction_t> 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<sip_uac_test2_session_t> s(new sip_uac_test2_session_t());
|
|
s->pkts = std::shared_ptr<AVPacketQueue>(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:bob@biloxi.com>", "sip:registrar.biloxi.com", 7200, sip_uac_message_onregister, test);
|
|
std::shared_ptr<sip_uac_transaction_t> 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, const struct cstring_t* id)
|
|
{
|
|
return 0;
|
|
}
|
|
static int sip_uas_onnotify(void* param, const struct sip_message_t* req, struct sip_uas_transaction_t* t, const struct sip_event_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 sip_event_t* event)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int sip_uas_onrefer(void* param, const struct sip_message_t* req, struct sip_uas_transaction_t* t)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int sip_uas_onprack(void* param, const struct sip_message_t* req, struct sip_uas_transaction_t* t, const struct cstring_t* id, 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, const struct cstring_t* id, 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, const struct cstring_t* id, 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;
|
|
memset(&test, 0, sizeof(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_onrefer,
|
|
sip_uas_onmessage,
|
|
};
|
|
|
|
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();
|
|
}
|