lwip/contrib/apps/rtp/rtp.c

309 lines
9.7 KiB
C
Raw Normal View History

/**
* @file
* RTP client/server module
*
*/
/*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
*/
#include "lwip/opt.h"
#if LWIP_SOCKET && LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
#include "lwip/sys.h"
#include "lwip/sockets.h"
#include "rtp.h"
#include "rtpdata.h"
#include <string.h>
/** This is an example of a "RTP" client/server based on a MPEG4 bitstream (with socket API).
*/
/**
* RTP_DEBUG: Enable debugging for RTP.
*/
#ifndef RTP_DEBUG
#define RTP_DEBUG LWIP_DBG_ON
#endif
/** RTP stream port */
#ifndef RTP_STREAM_PORT
#define RTP_STREAM_PORT 4000
#endif
/** RTP stream multicast address as IPv4 address in "u32_t" format */
#ifndef RTP_STREAM_ADDRESS
#define RTP_STREAM_ADDRESS inet_addr("232.0.0.0")
#endif
/** RTP send delay - in milliseconds */
#ifndef RTP_SEND_DELAY
#define RTP_SEND_DELAY 40
#endif
/** RTP receive timeout - in milliseconds */
#ifndef RTP_RECV_TIMEOUT
#define RTP_RECV_TIMEOUT 2000
#endif
/** RTP stats display period - in received packets */
#ifndef RTP_RECV_STATS
#define RTP_RECV_STATS 50
#endif
/** RTP macro to let the application process the data */
#ifndef RTP_RECV_PROCESSING
#define RTP_RECV_PROCESSING(p,s)
#endif
/** RTP packet/payload size */
#define RTP_PACKET_SIZE 1500
#define RTP_PAYLOAD_SIZE 1024
/** RTP header constants */
#define RTP_VERSION 0x80
#define RTP_TIMESTAMP_INCREMENT 3600
#define RTP_SSRC 0
#define RTP_PAYLOADTYPE 96
#define RTP_MARKER_MASK 0x80
/** RTP message header */
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h"
#endif
PACK_STRUCT_BEGIN
struct rtp_hdr {
PACK_STRUCT_FLD_8(u8_t version);
PACK_STRUCT_FLD_8(u8_t payloadtype);
PACK_STRUCT_FIELD(u16_t seqNum);
PACK_STRUCT_FIELD(u32_t timestamp);
PACK_STRUCT_FIELD(u32_t ssrc);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/epstruct.h"
#endif
/** RTP packets */
static u8_t rtp_send_packet[RTP_PACKET_SIZE];
static u8_t rtp_recv_packet[RTP_PACKET_SIZE];
/**
* RTP send packets
*/
static void
rtp_send_packets( int sock, struct sockaddr_in* to)
{
struct rtp_hdr* rtphdr;
u8_t* rtp_payload;
size_t rtp_payload_size;
size_t rtp_data_index;
/* prepare RTP packet */
rtphdr = (struct rtp_hdr*)rtp_send_packet;
rtphdr->version = RTP_VERSION;
rtphdr->payloadtype = 0;
rtphdr->ssrc = PP_HTONL(RTP_SSRC);
rtphdr->timestamp = lwip_htonl(lwip_ntohl(rtphdr->timestamp) + RTP_TIMESTAMP_INCREMENT);
/* send RTP stream packets */
rtp_data_index = 0;
do {
rtp_payload = rtp_send_packet+sizeof(struct rtp_hdr);
rtp_payload_size = LWIP_MIN(RTP_PAYLOAD_SIZE, sizeof(rtp_data) - rtp_data_index);
MEMCPY(rtp_payload, rtp_data + rtp_data_index, rtp_payload_size);
/* set MARKER bit in RTP header on the last packet of an image */
if ((rtp_data_index + rtp_payload_size) >= sizeof(rtp_data)) {
rtphdr->payloadtype = RTP_PAYLOADTYPE | RTP_MARKER_MASK;
} else {
rtphdr->payloadtype = RTP_PAYLOADTYPE;
}
/* send RTP stream packet */
if (lwip_sendto(sock, rtp_send_packet, sizeof(struct rtp_hdr) + rtp_payload_size,
0, (struct sockaddr *)to, sizeof(struct sockaddr)) >= 0) {
rtphdr->seqNum = lwip_htons((u16_t)(lwip_ntohs(rtphdr->seqNum) + 1));
rtp_data_index += rtp_payload_size;
} else {
LWIP_DEBUGF(RTP_DEBUG, ("rtp_sender: not sendto==%i\n", errno));
}
}while (rtp_data_index < sizeof(rtp_data));
}
/**
* RTP send thread
*/
static void
rtp_send_thread(void *arg)
{
int sock;
struct sockaddr_in local;
struct sockaddr_in to;
u32_t rtp_stream_address;
LWIP_UNUSED_ARG(arg);
/* initialize RTP stream address */
rtp_stream_address = RTP_STREAM_ADDRESS;
/* if we got a valid RTP stream address... */
if (rtp_stream_address != 0) {
/* create new socket */
sock = lwip_socket(AF_INET, SOCK_DGRAM, 0);
if (sock >= 0) {
/* prepare local address */
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = PP_HTONS(INADDR_ANY);
local.sin_addr.s_addr = PP_HTONL(INADDR_ANY);
/* bind to local address */
if (lwip_bind(sock, (struct sockaddr *)&local, sizeof(local)) == 0) {
/* prepare RTP stream address */
memset(&to, 0, sizeof(to));
to.sin_family = AF_INET;
to.sin_port = PP_HTONS(RTP_STREAM_PORT);
to.sin_addr.s_addr = rtp_stream_address;
/* send RTP packets */
memset(rtp_send_packet, 0, sizeof(rtp_send_packet));
while (1) {
rtp_send_packets( sock, &to);
sys_msleep(RTP_SEND_DELAY);
}
}
/* close the socket */
lwip_close(sock);
}
}
}
/**
* RTP recv thread
*/
static void
rtp_recv_thread(void *arg)
{
int sock;
struct sockaddr_in local;
struct sockaddr_in from;
int fromlen;
struct ip_mreq ipmreq;
struct rtp_hdr* rtphdr;
u32_t rtp_stream_address;
int timeout;
int result;
int recvrtppackets = 0;
int lostrtppackets = 0;
u16_t lastrtpseq = 0;
LWIP_UNUSED_ARG(arg);
/* initialize RTP stream address */
rtp_stream_address = RTP_STREAM_ADDRESS;
/* if we got a valid RTP stream address... */
if (rtp_stream_address != 0) {
/* create new socket */
sock = lwip_socket(AF_INET, SOCK_DGRAM, 0);
if (sock >= 0) {
/* prepare local address */
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = PP_HTONS(RTP_STREAM_PORT);
local.sin_addr.s_addr = PP_HTONL(INADDR_ANY);
/* bind to local address */
if (lwip_bind(sock, (struct sockaddr *)&local, sizeof(local)) == 0) {
/* set recv timeout */
timeout = RTP_RECV_TIMEOUT;
result = lwip_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
if (result) {
LWIP_DEBUGF(RTP_DEBUG, ("rtp_recv_thread: setsockopt(SO_RCVTIMEO) failed: errno=%d\n", errno));
}
/* prepare multicast "ip_mreq" struct */
ipmreq.imr_multiaddr.s_addr = rtp_stream_address;
ipmreq.imr_interface.s_addr = PP_HTONL(INADDR_ANY);
/* join multicast group */
if (lwip_setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &ipmreq, sizeof(ipmreq)) == 0) {
/* receive RTP packets */
while(1) {
fromlen = sizeof(from);
result = lwip_recvfrom(sock, rtp_recv_packet, sizeof(rtp_recv_packet), 0,
(struct sockaddr *)&from, (socklen_t *)&fromlen);
if ((result > 0) && ((size_t)result >= sizeof(struct rtp_hdr))) {
size_t recved = (size_t)result;
rtphdr = (struct rtp_hdr *)rtp_recv_packet;
recvrtppackets++;
if ((lastrtpseq == 0) || ((lastrtpseq + 1) == lwip_ntohs(rtphdr->seqNum))) {
RTP_RECV_PROCESSING((rtp_recv_packet + sizeof(rtp_hdr)), (recved-sizeof(rtp_hdr)));
LWIP_UNUSED_ARG(recved); /* just in case... */
} else {
lostrtppackets++;
}
lastrtpseq = lwip_ntohs(rtphdr->seqNum);
if ((recvrtppackets % RTP_RECV_STATS) == 0) {
LWIP_DEBUGF(RTP_DEBUG, ("rtp_recv_thread: recv %6i packet(s) / lost %4i packet(s) (%.4f%%)...\n", recvrtppackets, lostrtppackets, (lostrtppackets*100.0)/recvrtppackets));
}
} else {
LWIP_DEBUGF(RTP_DEBUG, ("rtp_recv_thread: recv timeout...\n"));
}
}
/* leave multicast group */
/* TODO: this code is never reached
result = lwip_setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &ipmreq, sizeof(ipmreq));
if (result) {
LWIP_DEBUGF(RTP_DEBUG, ("rtp_recv_thread: setsockopt(IP_DROP_MEMBERSHIP) failed: errno=%d\n", errno));
}*/
}
}
/* close the socket */
lwip_close(sock);
}
}
}
void
rtp_init(void)
{
sys_thread_new("rtp_send_thread", rtp_send_thread, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);
sys_thread_new("rtp_recv_thread", rtp_recv_thread, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);
}
#endif /* LWIP_SOCKET && LWIP_IGMP */