blob: 68605065776fefc531f5ceea763b30b3717398fe [file] [log] [blame]
Harald Weltece94d972013-02-11 11:34:58 +01001/* Process Queue: RTP handling tasks */
2
3/* (C) 2013 by Harald Welte <laforge@gnumonks.org>
4 *
5 * This file is part of gapk (GSM Audio Pocket Knife).
6 *
7 * gapk is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * gapk is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with gapk. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <errno.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <unistd.h>
26#include <string.h>
27
28#include <arpa/inet.h>
29
30#include <gapk/codecs.h>
31#include <gapk/formats.h>
32#include <gapk/procqueue.h>
33
34#ifndef __BYTE_ORDER
35# ifdef __APPLE__
36# define __BYTE_ORDER __DARWIN_BYTE_ORDER
37# define __LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN
38# define __BIG_ENDIAN __DARWIN_BIG_ENDIAN
39# else
40# error "__BYTE_ORDER should be defined by someone"
41# endif
42#endif
43
44/* according to RFC 3550 */
45struct rtp_hdr {
46#if __BYTE_ORDER == __LITTLE_ENDIAN
47 uint8_t csrc_count:4,
48 extension:1,
49 padding:1,
50 version:2;
51 uint8_t payload_type:7,
52 marker:1;
53#elif __BYTE_ORDER == __BIG_ENDIAN
54 uint8_t version:2,
55 padding:1,
56 extension:1,
57 csrc_count:4;
58 uint8_t marker:1,
59 payload_type:7;
60#endif
61 uint16_t sequence;
62 uint32_t timestamp;
63 uint32_t ssrc;
64} __attribute__((packed));
65
66struct rtp_x_hdr {
67 uint16_t by_profile;
68 uint16_t length;
69} __attribute__((packed));
70
71#define RTP_VERSION 2
72
73#define RTP_PT_GSM_FULL 3
74
75struct pq_state_rtp {
76 int fd;
77 int blk_len;
78
79 /* configurable */
80 uint32_t duration;
81 uint8_t payload_type;
82
83 /* auto-increment */
84 uint16_t sequence;
85 uint32_t timestamp;
86 uint32_t ssrc;
87};
88
89
90static int
91pq_cb_rtp_input(void *_state, uint8_t *out, const uint8_t *in)
92{
93 struct pq_state_rtp *state = _state;
94 uint8_t buf[state->blk_len+256];
95 struct rtp_hdr *rtph = (struct rtp_hdr *)buf;
96 struct rtp_x_hdr *rtpxh;
97 uint8_t *payload;
98 int rv, x_len, payload_len;
99
100 rv = read(state->fd, buf, sizeof(buf));
101 if (rv <= 0)
102 return -1;
103
104 if (rv < sizeof(struct rtp_hdr))
105 return -1;
106
107 if (rtph->version != RTP_VERSION)
108 return -1;
109
110 payload = buf + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
111 payload_len = rv - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
112 if (payload_len < 0)
113 return -1;
114
115 if (rtph->extension) {
116 if (payload_len < sizeof(struct rtp_x_hdr))
117 return -1;
118 rtpxh = (struct rtp_x_hdr *)payload;
119 x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
120 payload += x_len;
121 payload_len -= x_len;
122 if (payload_len < 0)
123 return -1;
124 }
125 if (rtph->padding) {
126 if (payload_len < 0)
127 return -1;
128 payload_len -= payload[payload_len -1];
129 if (payload_len < 0)
130 return -1;
131 }
132
133 state->ssrc = ntohl(rtph->ssrc);
134 state->timestamp = ntohl(rtph->timestamp);
135 state->sequence = ntohs(rtph->sequence);
136 /* FIXME: check for discontinuity, ... */
137
138 memcpy(out, payload, payload_len);
139
140 return 0;
141}
142
143static int
144pq_cb_rtp_output(void *_state, uint8_t *out, const uint8_t *in)
145{
146 struct pq_state_rtp *state = _state;
147 int len = state->blk_len + sizeof(struct rtp_hdr);
148 uint8_t buf[len];
149 struct rtp_hdr *rtph = (struct rtp_hdr *)buf;
150 uint8_t *payload;
151 int rv;
152
153 rtph->version = RTP_VERSION;
154 rtph->padding = 0;
155 rtph->extension = 0;
156 rtph->csrc_count = 0;
157 rtph->marker = 0;
158 rtph->payload_type = state->payload_type;
159 rtph->sequence = htons(state->sequence++);
160 rtph->timestamp = htonl(state->timestamp);
161 state->timestamp += state->duration;
162 rtph->ssrc = htonl(state->ssrc);
163
164 payload = buf + sizeof(*rtph);
165 memcpy(payload, in, state->blk_len);
166
167 rv = write(state->fd, buf, len);
168 return rv == len ? 0 : -1;
169}
170
171static void
172pq_cb_rtp_exit(void *_state)
173{
174 free(_state);
175}
176
177static int
178pq_queue_rtp_op(struct pq *pq, int udp_fd, unsigned int blk_len, int in_out_n)
179{
180 struct pq_item *item;
181 struct pq_state_rtp *state;
182
183 state = calloc(1, sizeof(struct pq_state_rtp));
184 if (!state)
185 return -ENOMEM;
186
187 state->fd = udp_fd;
188 state->blk_len = blk_len;
189
Harald Weltef3d2ad62017-05-27 16:42:14 +0200190 /* as we're working in GSM, the sample clock is 8000 Hz and we
191 * operate at 50 Hz (20ms) codec frames; 8000/50 = 160 samples
192 * per RTP frame */
Harald Weltece94d972013-02-11 11:34:58 +0100193 state->duration = 160;
194
195 if (in_out_n == 0) {
196 state->ssrc = rand();
197 state->sequence = random();
198 state->timestamp = random();
199 /* FIXME: other payload types!! */
200 state->payload_type = RTP_PT_GSM_FULL;
201 }
202
203 item = pq_add_item(pq);
204 if (!item) {
205 free(state);
206 return -ENOMEM;
207 }
208
209 item->len_in = in_out_n ? 0 : blk_len;
210 item->len_out = in_out_n ? blk_len : 0;
211 item->state = state;
212 item->proc = in_out_n ? pq_cb_rtp_input : pq_cb_rtp_output;
213 item->exit = pq_cb_rtp_exit;
214
215 return 0;
216}
217
218
Harald Weltef3d2ad62017-05-27 16:42:14 +0200219/*! Add RTP input to processing queue.
220 * This typically only makes sense as first item in the queue
221 * \param pq Processing Queue to add this RTP input to
222 * \param[in] udp_fd UDP file descriptor for the RTP input
223 * \param[in] blk_len Block Length to read from RTP */
Harald Weltece94d972013-02-11 11:34:58 +0100224int
225pq_queue_rtp_input(struct pq *pq, int udp_fd, unsigned int blk_len)
226{
227 return pq_queue_rtp_op(pq, udp_fd, blk_len, 1);
228}
229
Harald Weltef3d2ad62017-05-27 16:42:14 +0200230/*! Add RTP output to processing queue.
231 * This typically only makes sense as last item in the queue
232 * \param pq Processing Queue to add this RTP output to
233 * \param[in] udp_fd UDP file descriptor for the RTP output
234 * \param[in] blk_len Block Length to read from RTP */
Harald Weltece94d972013-02-11 11:34:58 +0100235int
236pq_queue_rtp_output(struct pq *pq, int udp_fd, unsigned int blk_len)
237{
238 return pq_queue_rtp_op(pq, udp_fd, blk_len, 0);
239}