#include #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 #include #include #include #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); } }