blob: 3e9dfb5c2b63aae5de3a909c58eb01628a9e51e9 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id$ */
2/*
3 * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjmedia/vid_tee.h>
20#include <pjmedia/converter.h>
21#include <pjmedia/errno.h>
22#include <pj/array.h>
23#include <pj/log.h>
24#include <pj/pool.h>
25
26
27#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
28
29
30#define TEE_PORT_NAME "vid_tee"
31#define TEE_PORT_SIGN PJMEDIA_SIG_PORT_VID_TEE
32
33#define THIS_FILE "vid_tee.c"
34
35typedef struct vid_tee_dst_port
36{
37 pjmedia_port *dst;
38 unsigned option;
39} vid_tee_dst_port;
40
41
42typedef struct vid_tee_port
43{
44 pjmedia_port base;
45 pj_pool_t *pool;
46 pj_pool_factory *pf;
47 pj_pool_t *buf_pool;
48 void *buf[2];
49 unsigned buf_cnt;
50 pj_size_t buf_size;
51 unsigned dst_port_maxcnt;
52 unsigned dst_port_cnt;
53 vid_tee_dst_port *dst_ports;
54 pj_uint8_t *put_frm_flag;
55
56 struct vid_tee_conv_t {
57 pjmedia_converter *conv;
58 pj_size_t conv_buf_size;
59 } *tee_conv;
60} vid_tee_port;
61
62
63static pj_status_t tee_put_frame(pjmedia_port *port, pjmedia_frame *frame);
64static pj_status_t tee_get_frame(pjmedia_port *port, pjmedia_frame *frame);
65static pj_status_t tee_destroy(pjmedia_port *port);
66
67/*
68 * Create a video tee port with the specified source media port.
69 */
70PJ_DEF(pj_status_t) pjmedia_vid_tee_create( pj_pool_t *pool,
71 const pjmedia_format *fmt,
72 unsigned max_dst_cnt,
73 pjmedia_port **p_vid_tee)
74{
75 vid_tee_port *tee;
76 pj_str_t name_st;
77 const pjmedia_video_format_info *vfi;
78 pjmedia_video_apply_fmt_param vafp;
79 pj_status_t status;
80
81 PJ_ASSERT_RETURN(pool && fmt && p_vid_tee, PJ_EINVAL);
82 PJ_ASSERT_RETURN(fmt->type == PJMEDIA_TYPE_VIDEO, PJ_EINVAL);
83
84 /* Allocate video tee structure */
85 tee = PJ_POOL_ZALLOC_T(pool, vid_tee_port);
86 tee->pf = pool->factory;
87 tee->pool = pj_pool_create(tee->pf, "video tee", 500, 500, NULL);
88
89 /* Initialize video tee structure */
90 tee->dst_port_maxcnt = max_dst_cnt;
91 tee->dst_ports = (vid_tee_dst_port*)
92 pj_pool_calloc(pool, max_dst_cnt,
93 sizeof(vid_tee_dst_port));
94 tee->tee_conv = (struct vid_tee_conv_t *)
95 pj_pool_calloc(pool, max_dst_cnt,
96 sizeof(struct vid_tee_conv_t));
97 tee->put_frm_flag = (pj_uint8_t*)
98 pj_pool_calloc(pool, max_dst_cnt,
99 sizeof(tee->put_frm_flag[0]));
100
101 /* Initialize video tee buffer, its size is one frame */
102 vfi = pjmedia_get_video_format_info(NULL, fmt->id);
103 if (vfi == NULL)
104 return PJMEDIA_EBADFMT;
105
106 pj_bzero(&vafp, sizeof(vafp));
107 vafp.size = fmt->det.vid.size;
108 status = vfi->apply_fmt(vfi, &vafp);
109 if (status != PJ_SUCCESS)
110 return status;
111
112 tee->buf_size = vafp.framebytes;
113
114 /* Initialize video tee port */
115 status = pjmedia_port_info_init2(&tee->base.info,
116 pj_strset2(&name_st, (char*)TEE_PORT_NAME),
117 TEE_PORT_SIGN,
118 PJMEDIA_DIR_ENCODING,
119 fmt);
120 if (status != PJ_SUCCESS)
121 return status;
122
123 tee->base.get_frame = &tee_get_frame;
124 tee->base.put_frame = &tee_put_frame;
125 tee->base.on_destroy = &tee_destroy;
126
127 /* Done */
128 *p_vid_tee = &tee->base;
129
130 return PJ_SUCCESS;
131}
132
133static void realloc_buf(vid_tee_port *vid_tee,
134 unsigned buf_cnt, pj_size_t buf_size)
135{
136 unsigned i;
137
138 if (buf_cnt > vid_tee->buf_cnt)
139 vid_tee->buf_cnt = buf_cnt;
140
141 if (buf_size > vid_tee->buf_size) {
142 /* We need a larger buffer here. */
143 vid_tee->buf_size = buf_size;
144 if (vid_tee->buf_pool) {
145 pj_pool_release(vid_tee->buf_pool);
146 vid_tee->buf_pool = NULL;
147 }
148 vid_tee->buf[0] = vid_tee->buf[1] = NULL;
149 }
150
151 if (!vid_tee->buf_pool) {
152 vid_tee->buf_pool = pj_pool_create(vid_tee->pf, "video tee buffer",
153 1000, 1000, NULL);
154 }
155
156 for (i = 0; i < vid_tee->buf_cnt; i++) {
157 if (!vid_tee->buf[i])
158 vid_tee->buf[i] = pj_pool_alloc(vid_tee->buf_pool,
159 vid_tee->buf_size);
160 }
161}
162
163/*
164 * Add a destination media port to the video tee.
165 */
166PJ_DEF(pj_status_t) pjmedia_vid_tee_add_dst_port(pjmedia_port *vid_tee,
167 unsigned option,
168 pjmedia_port *port)
169{
170 vid_tee_port *tee = (vid_tee_port*)vid_tee;
171 pjmedia_video_format_detail *vfd;
172
173 PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN,
174 PJ_EINVAL);
175
176 if (tee->dst_port_cnt >= tee->dst_port_maxcnt)
177 return PJ_ETOOMANY;
178
179 if (vid_tee->info.fmt.id != port->info.fmt.id)
180 return PJMEDIA_EBADFMT;
181
182 vfd = pjmedia_format_get_video_format_detail(&port->info.fmt, PJ_TRUE);
183 if (vfd->size.w != vid_tee->info.fmt.det.vid.size.w ||
184 vfd->size.h != vid_tee->info.fmt.det.vid.size.h)
185 {
186 return PJMEDIA_EBADFMT;
187 }
188
189 realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)?
190 1: 0, tee->buf_size);
191
192 pj_bzero(&tee->tee_conv[tee->dst_port_cnt], sizeof(tee->tee_conv[0]));
193 tee->dst_ports[tee->dst_port_cnt].dst = port;
194 tee->dst_ports[tee->dst_port_cnt].option = option;
195 ++tee->dst_port_cnt;
196
197 return PJ_SUCCESS;
198}
199
200
201/*
202 * Add a destination media port to the video tee. Create a converter if
203 * necessary.
204 */
205PJ_DEF(pj_status_t) pjmedia_vid_tee_add_dst_port2(pjmedia_port *vid_tee,
206 unsigned option,
207 pjmedia_port *port)
208{
209 vid_tee_port *tee = (vid_tee_port*)vid_tee;
210 pjmedia_video_format_detail *vfd;
211
212 PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN,
213 PJ_EINVAL);
214
215 if (tee->dst_port_cnt >= tee->dst_port_maxcnt)
216 return PJ_ETOOMANY;
217
218 pj_bzero(&tee->tee_conv[tee->dst_port_cnt], sizeof(tee->tee_conv[0]));
219
220 /* Check if we need to create a converter. */
221 vfd = pjmedia_format_get_video_format_detail(&port->info.fmt, PJ_TRUE);
222 if (vid_tee->info.fmt.id != port->info.fmt.id ||
223 vfd->size.w != vid_tee->info.fmt.det.vid.size.w ||
224 vfd->size.h != vid_tee->info.fmt.det.vid.size.h)
225 {
226 const pjmedia_video_format_info *vfi;
227 pjmedia_video_apply_fmt_param vafp;
228 pjmedia_conversion_param conv_param;
229 pj_status_t status;
230
231 vfi = pjmedia_get_video_format_info(NULL, port->info.fmt.id);
232 if (vfi == NULL)
233 return PJMEDIA_EBADFMT;
234
235 pj_bzero(&vafp, sizeof(vafp));
236 vafp.size = port->info.fmt.det.vid.size;
237 status = vfi->apply_fmt(vfi, &vafp);
238 if (status != PJ_SUCCESS)
239 return status;
240
241 realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)?
242 2: 1, vafp.framebytes);
243
244 pjmedia_format_copy(&conv_param.src, &vid_tee->info.fmt);
245 pjmedia_format_copy(&conv_param.dst, &port->info.fmt);
246
247 status = pjmedia_converter_create(
248 NULL, tee->pool, &conv_param,
249 &tee->tee_conv[tee->dst_port_cnt].conv);
250 if (status != PJ_SUCCESS)
251 return status;
252
253 tee->tee_conv[tee->dst_port_cnt].conv_buf_size = vafp.framebytes;
254 } else {
255 realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)?
256 1: 0, tee->buf_size);
257 }
258
259 tee->dst_ports[tee->dst_port_cnt].dst = port;
260 tee->dst_ports[tee->dst_port_cnt].option = option;
261 ++tee->dst_port_cnt;
262
263 return PJ_SUCCESS;
264}
265
266
267/*
268 * Remove a destination media port from the video tee.
269 */
270PJ_DEF(pj_status_t) pjmedia_vid_tee_remove_dst_port(pjmedia_port *vid_tee,
271 pjmedia_port *port)
272{
273 vid_tee_port *tee = (vid_tee_port*)vid_tee;
274 unsigned i;
275
276 PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN,
277 PJ_EINVAL);
278
279 for (i = 0; i < tee->dst_port_cnt; ++i) {
280 if (tee->dst_ports[i].dst == port) {
281 if (tee->tee_conv[i].conv)
282 pjmedia_converter_destroy(tee->tee_conv[i].conv);
283
284 pj_array_erase(tee->dst_ports, sizeof(tee->dst_ports[0]),
285 tee->dst_port_cnt, i);
286 pj_array_erase(tee->tee_conv, sizeof(tee->tee_conv[0]),
287 tee->dst_port_cnt, i);
288 --tee->dst_port_cnt;
289 return PJ_SUCCESS;
290 }
291 }
292
293 return PJ_ENOTFOUND;
294}
295
296
297static pj_status_t tee_put_frame(pjmedia_port *port, pjmedia_frame *frame)
298{
299 vid_tee_port *tee = (vid_tee_port*)port;
300 unsigned i, j;
301 const pj_uint8_t PUT_FRM_DONE = 1;
302
303 pj_bzero(tee->put_frm_flag, tee->dst_port_cnt *
304 sizeof(tee->put_frm_flag[0]));
305
306 for (i = 0; i < tee->dst_port_cnt; ++i) {
307 pjmedia_frame frame_ = *frame;
308
309 if (tee->put_frm_flag[i])
310 continue;
311
312 if (tee->tee_conv[i].conv) {
313 pj_status_t status;
314
315 frame_.buf = tee->buf[0];
316 frame_.size = tee->tee_conv[i].conv_buf_size;
317 status = pjmedia_converter_convert(tee->tee_conv[i].conv,
318 frame, &frame_);
319 if (status != PJ_SUCCESS) {
320 PJ_LOG(3, (THIS_FILE,
321 "Failed to convert frame for destination"
322 " port %d (%.*s)", i,
323 tee->dst_ports[i].dst->info.name.slen,
324 tee->dst_ports[i].dst->info.name.ptr));
325 continue;
326 }
327 }
328
329 /* Find other destination ports which has the same format so
330 * we don't need to do the same conversion twice.
331 */
332 for (j = i; j < tee->dst_port_cnt; ++j) {
333 pjmedia_frame framep;
334
335 if (tee->put_frm_flag[j] ||
336 (tee->dst_ports[j].dst->info.fmt.id !=
337 tee->dst_ports[i].dst->info.fmt.id) ||
338 (tee->dst_ports[j].dst->info.fmt.det.vid.size.w !=
339 tee->dst_ports[i].dst->info.fmt.det.vid.size.w) ||
340 (tee->dst_ports[j].dst->info.fmt.det.vid.size.h !=
341 tee->dst_ports[i].dst->info.fmt.det.vid.size.h))
342 {
343 continue;
344 }
345
346 framep = frame_;
347 /* For dst_ports that do in-place processing, we need to duplicate
348 * the data source first.
349 */
350 if (tee->dst_ports[j].option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)
351 {
352 PJ_ASSERT_RETURN(tee->buf_size <= frame_.size, PJ_ETOOBIG);
353 framep.buf = tee->buf[tee->buf_cnt-1];
354 framep.size = frame_.size;
355 pj_memcpy(framep.buf, frame_.buf, frame_.size);
356 }
357
358 /* Deliver the data */
359 pjmedia_port_put_frame(tee->dst_ports[j].dst, &framep);
360 tee->put_frm_flag[j] = PUT_FRM_DONE;
361
362 if (!tee->tee_conv[i].conv)
363 break;
364 }
365 }
366
367 return PJ_SUCCESS;
368}
369
370static pj_status_t tee_get_frame(pjmedia_port *port, pjmedia_frame *frame)
371{
372 PJ_UNUSED_ARG(port);
373 PJ_UNUSED_ARG(frame);
374
375 pj_assert(!"Bug! Tee port get_frame() shouldn't be called.");
376
377 return PJ_EBUG;
378}
379
380static pj_status_t tee_destroy(pjmedia_port *port)
381{
382 vid_tee_port *tee = (vid_tee_port*)port;
383
384 PJ_ASSERT_RETURN(port && port->info.signature==TEE_PORT_SIGN, PJ_EINVAL);
385
386 pj_pool_release(tee->pool);
387 if (tee->buf_pool)
388 pj_pool_release(tee->buf_pool);
389
390 pj_bzero(tee, sizeof(*tee));
391
392 return PJ_SUCCESS;
393}
394
395
396#endif /* PJMEDIA_HAS_VIDEO */