277 lines
7.1 KiB
C++
277 lines
7.1 KiB
C++
#include <stdint.h>
|
|
#include "sockutil.h"
|
|
#include "sys/pollfd.h"
|
|
#include "sys/thread.h"
|
|
#include "rtp-demuxer.h"
|
|
#include "rtp-profile.h"
|
|
#include "rtp-payload.h"
|
|
#include "rtcp-header.h"
|
|
#include "rtp.h"
|
|
#include "time64.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#if defined(OS_WINDOWS)
|
|
#define strcasecmp _stricmp
|
|
#endif
|
|
|
|
struct rtp_context_t
|
|
{
|
|
FILE *fp;
|
|
FILE *frtp;
|
|
|
|
char encoding[64];
|
|
socket_t socket[2];
|
|
struct sockaddr_storage ss[2];
|
|
|
|
char rtp_buffer[64 * 1024];
|
|
char rtcp_buffer[32 * 1024];
|
|
|
|
struct rtp_demuxer_t* demuxer;
|
|
};
|
|
|
|
static int rtp_read(struct rtp_context_t* ctx, socket_t s)
|
|
{
|
|
int r;
|
|
uint8_t size[2];
|
|
static int i, n = 0;
|
|
socklen_t len;
|
|
struct sockaddr_storage ss;
|
|
len = sizeof(ss);
|
|
|
|
r = recvfrom(s, ctx->rtp_buffer, sizeof(ctx->rtp_buffer), 0, (struct sockaddr*)&ss, &len);
|
|
if (r < 12)
|
|
return -1;
|
|
assert(0 == socket_addr_compare((const struct sockaddr*) & ss, (const struct sockaddr*) & ctx->ss[0]));
|
|
|
|
n += r;
|
|
if(0 == i++ % 100)
|
|
printf("packet: %d, seq: %u, size: %d/%d\n", i, ((uint8_t)ctx->rtp_buffer[2] << 8) | (uint8_t)ctx->rtp_buffer[3], r, n);
|
|
|
|
size[0] = r >> 8;
|
|
size[1] = r >> 0;
|
|
fwrite(size, 1, sizeof(size), ctx->frtp);
|
|
fwrite(ctx->rtp_buffer, 1, r, ctx->frtp);
|
|
|
|
r = rtp_demuxer_input(ctx->demuxer, ctx->rtp_buffer, r);
|
|
return r;
|
|
}
|
|
|
|
static int rtcp_read(struct rtp_context_t* ctx, socket_t s)
|
|
{
|
|
int r;
|
|
socklen_t len;
|
|
struct sockaddr_storage ss;
|
|
len = sizeof(ss);
|
|
r = recvfrom(s, ctx->rtcp_buffer, sizeof(ctx->rtcp_buffer), 0, (struct sockaddr*)&ss, &len);
|
|
if (r < 12)
|
|
return -1;
|
|
assert(0 == socket_addr_compare((const struct sockaddr*)&ss, (const struct sockaddr*)&ctx->ss[1]));
|
|
|
|
r = rtp_demuxer_input(ctx->demuxer, ctx->rtcp_buffer, r);
|
|
if (RTCP_BYE == r)
|
|
{
|
|
printf("finished\n");
|
|
}
|
|
fflush(ctx->fp);
|
|
return r;
|
|
}
|
|
|
|
static int rtp_receiver(struct rtp_context_t* ctx, socket_t rtp[2], int timeout)
|
|
{
|
|
int i, r;
|
|
// int interval;
|
|
time64_t clock;
|
|
struct pollfd fds[2];
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
fds[i].fd = rtp[i];
|
|
fds[i].events = POLLIN;
|
|
fds[i].revents = 0;
|
|
}
|
|
|
|
clock = time64_now();
|
|
while (1)
|
|
{
|
|
// RTCP report
|
|
r = rtp_demuxer_rtcp(ctx->demuxer, ctx->rtcp_buffer, sizeof(ctx->rtcp_buffer));
|
|
if (r > 0)
|
|
r = socket_sendto(rtp[1], ctx->rtcp_buffer, r, 0, (const struct sockaddr*) & ctx->ss[1], socket_addr_len((const struct sockaddr*) & ctx->ss[1]));
|
|
|
|
r = poll(fds, 2, timeout);
|
|
while (-1 == r && EINTR == errno)
|
|
r = poll(fds, 2, timeout);
|
|
|
|
if (0 == r)
|
|
{
|
|
continue; // timeout
|
|
}
|
|
else if (r < 0)
|
|
{
|
|
return r; // error
|
|
}
|
|
else
|
|
{
|
|
if (0 != fds[0].revents)
|
|
{
|
|
rtp_read(ctx, rtp[0]);
|
|
fds[0].revents = 0;
|
|
}
|
|
|
|
if (0 != fds[1].revents)
|
|
{
|
|
rtcp_read(ctx, rtp[1]);
|
|
fds[1].revents = 0;
|
|
}
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static int rtp_onpacket(void* param, const void *packet, int bytes, uint32_t timestamp, int flags)
|
|
{
|
|
const uint8_t start_code[] = { 0, 0, 0, 1 };
|
|
struct rtp_context_t* ctx;
|
|
ctx = (struct rtp_context_t*)param;
|
|
if (0 == strcmp("H264", ctx->encoding) || 0 == strcmp("H265", ctx->encoding))
|
|
{
|
|
fwrite(start_code, 1, 4, ctx->fp);
|
|
}
|
|
else if (0 == strcasecmp("mpeg4-generic", ctx->encoding))
|
|
{
|
|
uint8_t adts[7];
|
|
int len = bytes + 7;
|
|
uint8_t profile = 2;
|
|
uint8_t sampling_frequency_index = 4;
|
|
uint8_t channel_configuration = 2;
|
|
adts[0] = 0xFF; /* 12-syncword */
|
|
adts[1] = 0xF0 /* 12-syncword */ | (0 << 3)/*1-ID*/ | (0x00 << 2) /*2-layer*/ | 0x01 /*1-protection_absent*/;
|
|
adts[2] = ((profile - 1) << 6) | ((sampling_frequency_index & 0x0F) << 2) | ((channel_configuration >> 2) & 0x01);
|
|
adts[3] = ((channel_configuration & 0x03) << 6) | ((len >> 11) & 0x03); /*0-original_copy*/ /*0-home*/ /*0-copyright_identification_bit*/ /*0-copyright_identification_start*/
|
|
adts[4] = (uint8_t)(len >> 3);
|
|
adts[5] = ((len & 0x07) << 5) | 0x1F;
|
|
adts[6] = 0xFC | ((len / 1024) & 0x03);
|
|
fwrite(adts, 1, sizeof(adts), ctx->fp);
|
|
}
|
|
else if (0 == strcmp("MP4A-LATM", ctx->encoding))
|
|
{
|
|
// add ADTS header
|
|
}
|
|
fwrite(packet, 1, bytes, ctx->fp);
|
|
(void)timestamp;
|
|
(void)flags;
|
|
|
|
if (0 == strcmp("H264", ctx->encoding))
|
|
{
|
|
uint8_t type = *(uint8_t*)packet & 0x1f;
|
|
if (0 < type && type <= 5)
|
|
{
|
|
// VCL frame
|
|
}
|
|
}
|
|
else if (0 == strcmp("H265", ctx->encoding))
|
|
{
|
|
uint8_t type = (*(uint8_t*)packet >> 1) & 0x3f;
|
|
if (type <= 32)
|
|
{
|
|
// VCL frame
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int STDCALL rtp_worker(void* param)
|
|
{
|
|
struct rtp_context_t* ctx;
|
|
ctx = (struct rtp_context_t*)param;
|
|
|
|
rtp_receiver(ctx, ctx->socket, 2000);
|
|
|
|
rtp_demuxer_destroy(&ctx->demuxer);
|
|
fclose(ctx->frtp);
|
|
fclose(ctx->fp);
|
|
free(ctx);
|
|
return 0;
|
|
}
|
|
|
|
void rtp_receiver_test(socket_t rtp[2], const char* peer, int peerport[2], int payload, const char* encoding)
|
|
{
|
|
size_t n;
|
|
pthread_t t;
|
|
struct rtp_context_t* ctx;
|
|
const struct rtp_profile_t* profile;
|
|
|
|
ctx = malloc(sizeof(*ctx));
|
|
if(!ctx) return;
|
|
snprintf(ctx->rtp_buffer, sizeof(ctx->rtp_buffer), "%s.%d.%d.%s", peer, peerport[0], payload, encoding);
|
|
snprintf(ctx->rtcp_buffer, sizeof(ctx->rtcp_buffer), "%s.%d.%d.%s.rtp", peer, peerport[0], payload, encoding);
|
|
ctx->fp = fopen(ctx->rtp_buffer, "wb");
|
|
ctx->frtp = fopen(ctx->rtcp_buffer, "wb");
|
|
|
|
socket_getrecvbuf(rtp[0], &n);
|
|
socket_setrecvbuf(rtp[0], 512*1024);
|
|
socket_getrecvbuf(rtp[0], &n);
|
|
|
|
profile = rtp_profile_find(payload);
|
|
ctx->demuxer = rtp_demuxer_create(100, profile ? profile->frequency : 90000, payload, encoding, rtp_onpacket, ctx);
|
|
if (NULL == ctx->demuxer)
|
|
return; // ignore
|
|
|
|
assert(0 == socket_addr_from(&ctx->ss[0], NULL, peer, (u_short)peerport[0]));
|
|
assert(0 == socket_addr_from(&ctx->ss[1], NULL, peer, (u_short)peerport[1]));
|
|
//assert(0 == connect(rtp[0], (struct sockaddr*)&ctx->ss[0], len));
|
|
//assert(0 == connect(rtp[1], (struct sockaddr*)&ctx->ss[1], len));
|
|
|
|
snprintf(ctx->encoding, sizeof(ctx->encoding), "%s", encoding);
|
|
ctx->socket[0] = rtp[0];
|
|
ctx->socket[1] = rtp[1];
|
|
if (0 == thread_create(&t, rtp_worker, ctx))
|
|
thread_detach(t);
|
|
}
|
|
|
|
static struct rtp_context_t* s_ctx[8];
|
|
void* rtp_receiver_tcp_test(uint8_t interleave1, uint8_t interleave2, int payload, const char* encoding)
|
|
{
|
|
struct rtp_context_t* ctx;
|
|
const struct rtp_profile_t* profile;
|
|
|
|
ctx = malloc(sizeof(struct rtp_context_t));
|
|
if(!ctx) return NULL;
|
|
snprintf(ctx->rtp_buffer, sizeof(ctx->rtp_buffer), "tcp.%d.%s", payload, encoding);
|
|
snprintf(ctx->rtcp_buffer, sizeof(ctx->rtcp_buffer), "tcp.%d.%s.rtp", payload, encoding);
|
|
ctx->fp = fopen(ctx->rtp_buffer, "wb");
|
|
ctx->frtp = fopen(ctx->rtcp_buffer, "wb");
|
|
snprintf(ctx->encoding, sizeof(ctx->encoding), "%s", encoding);
|
|
assert(interleave1 / 2 < sizeof(s_ctx) / sizeof(s_ctx[0]));
|
|
s_ctx[interleave1 / 2] = ctx;
|
|
|
|
profile = rtp_profile_find(payload);
|
|
ctx->demuxer = rtp_demuxer_create(100, profile ? profile->frequency : 90000, payload, encoding, rtp_onpacket, ctx);
|
|
return ctx;
|
|
}
|
|
|
|
void rtp_receiver_tcp_input(uint8_t channel, const void* data, uint16_t bytes)
|
|
{
|
|
int r;
|
|
uint8_t size[2];
|
|
struct rtp_context_t* ctx = s_ctx[channel / 2];
|
|
|
|
if (0 == channel % 2)
|
|
{
|
|
size[0] = bytes >> 8;
|
|
size[1] = bytes >> 0;
|
|
fwrite(size, 1, sizeof(size), ctx->frtp);
|
|
fwrite(data, 1, bytes, ctx->frtp);
|
|
}
|
|
|
|
if (ctx->demuxer)
|
|
{
|
|
r = rtp_demuxer_input(ctx->demuxer, data, bytes);
|
|
assert(r >= 0);
|
|
}
|
|
}
|