blob: 25a9e995f35d016d099bd911922ff7e694717daa [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjmedia/master_port.h>
21#include <pjmedia/clock.h>
22#include <pjmedia/errno.h>
23#include <pj/assert.h>
24#include <pj/lock.h>
25#include <pj/pool.h>
26#include <pj/string.h>
27
28
29struct pjmedia_master_port
30{
31 unsigned options;
32 pjmedia_clock *clock;
33 pjmedia_port *u_port;
34 pjmedia_port *d_port;
35 unsigned buff_size;
36 void *buff;
37 pj_lock_t *lock;
38};
39
40
41static void clock_callback(const pj_timestamp *ts, void *user_data);
42
43
44/*
45 * Create a master port.
46 *
47 */
48PJ_DEF(pj_status_t) pjmedia_master_port_create( pj_pool_t *pool,
49 pjmedia_port *u_port,
50 pjmedia_port *d_port,
51 unsigned options,
52 pjmedia_master_port **p_m)
53{
54 pjmedia_master_port *m;
55 unsigned clock_rate;
56 unsigned channel_count;
57 unsigned samples_per_frame;
58 unsigned bytes_per_frame;
59 pjmedia_audio_format_detail *u_afd, *d_afd;
60 pj_status_t status;
61
62 /* Sanity check */
63 PJ_ASSERT_RETURN(pool && u_port && d_port && p_m, PJ_EINVAL);
64
65 u_afd = pjmedia_format_get_audio_format_detail(&u_port->info.fmt, PJ_TRUE);
66 d_afd = pjmedia_format_get_audio_format_detail(&d_port->info.fmt, PJ_TRUE);
67
68 /* Both ports MUST have equal clock rate */
69 PJ_ASSERT_RETURN(u_afd->clock_rate == d_afd->clock_rate,
70 PJMEDIA_ENCCLOCKRATE);
71
72 /* Both ports MUST have equal samples per frame */
73 PJ_ASSERT_RETURN(PJMEDIA_PIA_SPF(&u_port->info)==
74 PJMEDIA_PIA_SPF(&d_port->info),
75 PJMEDIA_ENCSAMPLESPFRAME);
76
77 /* Both ports MUST have equal channel count */
78 PJ_ASSERT_RETURN(u_afd->channel_count == d_afd->channel_count,
79 PJMEDIA_ENCCHANNEL);
80
81
82 /* Get clock_rate and samples_per_frame from one of the port. */
83 clock_rate = u_afd->clock_rate;
84 samples_per_frame = PJMEDIA_PIA_SPF(&u_port->info);
85 channel_count = u_afd->channel_count;
86
87
88 /* Get the bytes_per_frame value, to determine the size of the
89 * buffer. We take the larger size of the two ports.
90 */
91 bytes_per_frame = PJMEDIA_AFD_AVG_FSZ(u_afd);
92 if (PJMEDIA_AFD_AVG_FSZ(d_afd) > bytes_per_frame)
93 bytes_per_frame = PJMEDIA_AFD_AVG_FSZ(d_afd);
94
95
96 /* Create the master port instance */
97 m = PJ_POOL_ZALLOC_T(pool, pjmedia_master_port);
98 m->options = options;
99 m->u_port = u_port;
100 m->d_port = d_port;
101
102
103 /* Create buffer */
104 m->buff_size = bytes_per_frame;
105 m->buff = pj_pool_alloc(pool, m->buff_size);
106 if (!m->buff)
107 return PJ_ENOMEM;
108
109 /* Create lock object */
110 status = pj_lock_create_simple_mutex(pool, "mport", &m->lock);
111 if (status != PJ_SUCCESS)
112 return status;
113
114 /* Create media clock */
115 status = pjmedia_clock_create(pool, clock_rate, channel_count,
116 samples_per_frame, options, &clock_callback,
117 m, &m->clock);
118 if (status != PJ_SUCCESS) {
119 pj_lock_destroy(m->lock);
120 return status;
121 }
122
123 /* Done */
124 *p_m = m;
125
126 return PJ_SUCCESS;
127}
128
129
130/*
131 * Start the media flow.
132 */
133PJ_DEF(pj_status_t) pjmedia_master_port_start(pjmedia_master_port *m)
134{
135 PJ_ASSERT_RETURN(m && m->clock, PJ_EINVAL);
136 PJ_ASSERT_RETURN(m->u_port && m->d_port, PJ_EINVALIDOP);
137
138 return pjmedia_clock_start(m->clock);
139}
140
141
142/*
143 * Stop the media flow.
144 */
145PJ_DEF(pj_status_t) pjmedia_master_port_stop(pjmedia_master_port *m)
146{
147 PJ_ASSERT_RETURN(m && m->clock, PJ_EINVAL);
148
149 return pjmedia_clock_stop(m->clock);
150}
151
152
153/* Poll the master port clock */
154PJ_DEF(pj_bool_t) pjmedia_master_port_wait( pjmedia_master_port *m,
155 pj_bool_t wait,
156 pj_timestamp *ts)
157{
158 PJ_ASSERT_RETURN(m && m->clock, PJ_FALSE);
159
160 return pjmedia_clock_wait(m->clock, wait, ts);
161}
162
163/*
164 * Callback to be called for each clock ticks.
165 */
166static void clock_callback(const pj_timestamp *ts, void *user_data)
167{
168 pjmedia_master_port *m = (pjmedia_master_port*) user_data;
169 pjmedia_frame frame;
170 pj_status_t status;
171
172
173 /* Lock access to ports. */
174 pj_lock_acquire(m->lock);
175
176 /* Get frame from upstream port and pass it to downstream port */
177 pj_bzero(&frame, sizeof(frame));
178 frame.buf = m->buff;
179 frame.size = m->buff_size;
180 frame.timestamp.u64 = ts->u64;
181
182 status = pjmedia_port_get_frame(m->u_port, &frame);
183 if (status != PJ_SUCCESS)
184 frame.type = PJMEDIA_FRAME_TYPE_NONE;
185
186 status = pjmedia_port_put_frame(m->d_port, &frame);
187
188 /* Get frame from downstream port and pass it to upstream port */
189 pj_bzero(&frame, sizeof(frame));
190 frame.buf = m->buff;
191 frame.size = m->buff_size;
192 frame.timestamp.u64 = ts->u64;
193
194 status = pjmedia_port_get_frame(m->d_port, &frame);
195 if (status != PJ_SUCCESS)
196 frame.type = PJMEDIA_FRAME_TYPE_NONE;
197
198 status = pjmedia_port_put_frame(m->u_port, &frame);
199
200 /* Release lock */
201 pj_lock_release(m->lock);
202}
203
204
205/*
206 * Change the upstream port.
207 */
208PJ_DEF(pj_status_t) pjmedia_master_port_set_uport(pjmedia_master_port *m,
209 pjmedia_port *port)
210{
211 PJ_ASSERT_RETURN(m && port, PJ_EINVAL);
212
213 /* Only supports audio for now */
214 PJ_ASSERT_RETURN(port->info.fmt.type==PJMEDIA_TYPE_AUDIO, PJ_ENOTSUP);
215
216 /* If we have downstream port, make sure they have matching samples per
217 * frame.
218 */
219 if (m->d_port) {
220 PJ_ASSERT_RETURN(
221 PJMEDIA_PIA_PTIME(&port->info) ==
222 PJMEDIA_PIA_PTIME(&m->d_port->info),
223 PJMEDIA_ENCSAMPLESPFRAME
224 );
225 }
226
227 pj_lock_acquire(m->lock);
228
229 m->u_port = port;
230
231 pj_lock_release(m->lock);
232
233 return PJ_SUCCESS;
234}
235
236
237/*
238 * Get the upstream port.
239 */
240PJ_DEF(pjmedia_port*) pjmedia_master_port_get_uport(pjmedia_master_port*m)
241{
242 PJ_ASSERT_RETURN(m, NULL);
243 return m->u_port;
244}
245
246
247/*
248 * Change the downstream port.
249 */
250PJ_DEF(pj_status_t) pjmedia_master_port_set_dport(pjmedia_master_port *m,
251 pjmedia_port *port)
252{
253 PJ_ASSERT_RETURN(m && port, PJ_EINVAL);
254
255 /* Only supports audio for now */
256 PJ_ASSERT_RETURN(port->info.fmt.type==PJMEDIA_TYPE_AUDIO, PJ_ENOTSUP);
257
258 /* If we have upstream port, make sure they have matching samples per
259 * frame.
260 */
261 if (m->u_port) {
262 PJ_ASSERT_RETURN(
263 PJMEDIA_PIA_PTIME(&port->info) ==
264 PJMEDIA_PIA_PTIME(&m->u_port->info),
265 PJMEDIA_ENCSAMPLESPFRAME
266 );
267 }
268
269 pj_lock_acquire(m->lock);
270
271 m->d_port = port;
272
273 pj_lock_release(m->lock);
274
275 return PJ_SUCCESS;
276}
277
278
279/*
280 * Get the downstream port.
281 */
282PJ_DEF(pjmedia_port*) pjmedia_master_port_get_dport(pjmedia_master_port*m)
283{
284 PJ_ASSERT_RETURN(m, NULL);
285 return m->d_port;
286}
287
288
289/*
290 * Destroy the master port, and optionally destroy the u_port and
291 * d_port ports.
292 */
293PJ_DEF(pj_status_t) pjmedia_master_port_destroy(pjmedia_master_port *m,
294 pj_bool_t destroy_ports)
295{
296 PJ_ASSERT_RETURN(m, PJ_EINVAL);
297
298 if (m->clock) {
299 pjmedia_clock_destroy(m->clock);
300 m->clock = NULL;
301 }
302
303 if (m->u_port && destroy_ports) {
304 pjmedia_port_destroy(m->u_port);
305 m->u_port = NULL;
306 }
307
308 if (m->d_port && destroy_ports) {
309 pjmedia_port_destroy(m->d_port);
310 m->d_port = NULL;
311 }
312
313 if (m->lock) {
314 pj_lock_destroy(m->lock);
315 m->lock = NULL;
316 }
317
318 return PJ_SUCCESS;
319}
320
321