stream-deploy/ZLM/3rdpart/media-server/libsip/test/sip-uac-test2.cpp

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();
}