1
/* $Id: vid_tee.c 3773 2011-09-23 04:06:01Z nanang $ */
3
* Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
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.
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.
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
19
#include <pjmedia/vid_tee.h>
20
#include <pjmedia/converter.h>
21
#include <pjmedia/errno.h>
27
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
30
#define TEE_PORT_NAME "vid_tee"
31
#define TEE_PORT_SIGN PJMEDIA_SIG_PORT_VID_TEE
33
#define THIS_FILE "vid_tee.c"
35
typedef struct vid_tee_dst_port
42
typedef struct vid_tee_port
51
unsigned dst_port_maxcnt;
52
unsigned dst_port_cnt;
53
vid_tee_dst_port *dst_ports;
54
pj_uint8_t *put_frm_flag;
56
struct vid_tee_conv_t {
57
pjmedia_converter *conv;
58
pj_size_t conv_buf_size;
63
static pj_status_t tee_put_frame(pjmedia_port *port, pjmedia_frame *frame);
64
static pj_status_t tee_get_frame(pjmedia_port *port, pjmedia_frame *frame);
65
static pj_status_t tee_destroy(pjmedia_port *port);
68
* Create a video tee port with the specified source media port.
70
PJ_DEF(pj_status_t) pjmedia_vid_tee_create( pj_pool_t *pool,
71
const pjmedia_format *fmt,
73
pjmedia_port **p_vid_tee)
77
const pjmedia_video_format_info *vfi;
78
pjmedia_video_apply_fmt_param vafp;
81
PJ_ASSERT_RETURN(pool && fmt && p_vid_tee, PJ_EINVAL);
82
PJ_ASSERT_RETURN(fmt->type == PJMEDIA_TYPE_VIDEO, PJ_EINVAL);
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);
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]));
101
/* Initialize video tee buffer, its size is one frame */
102
vfi = pjmedia_get_video_format_info(NULL, fmt->id);
104
return PJMEDIA_EBADFMT;
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)
112
tee->buf_size = vafp.framebytes;
114
/* Initialize video tee port */
115
status = pjmedia_port_info_init2(&tee->base.info,
116
pj_strset2(&name_st, (char*)TEE_PORT_NAME),
118
PJMEDIA_DIR_ENCODING,
120
if (status != PJ_SUCCESS)
123
tee->base.get_frame = &tee_get_frame;
124
tee->base.put_frame = &tee_put_frame;
125
tee->base.on_destroy = &tee_destroy;
128
*p_vid_tee = &tee->base;
133
static void realloc_buf(vid_tee_port *vid_tee,
134
unsigned buf_cnt, pj_size_t buf_size)
138
if (buf_cnt > vid_tee->buf_cnt)
139
vid_tee->buf_cnt = buf_cnt;
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;
148
vid_tee->buf[0] = vid_tee->buf[1] = NULL;
151
if (!vid_tee->buf_pool) {
152
vid_tee->buf_pool = pj_pool_create(vid_tee->pf, "video tee buffer",
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,
164
* Add a destination media port to the video tee.
166
PJ_DEF(pj_status_t) pjmedia_vid_tee_add_dst_port(pjmedia_port *vid_tee,
170
vid_tee_port *tee = (vid_tee_port*)vid_tee;
171
pjmedia_video_format_detail *vfd;
173
PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN,
176
if (tee->dst_port_cnt >= tee->dst_port_maxcnt)
179
if (vid_tee->info.fmt.id != port->info.fmt.id)
180
return PJMEDIA_EBADFMT;
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)
186
return PJMEDIA_EBADFMT;
189
realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)?
190
1: 0, tee->buf_size);
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;
202
* Add a destination media port to the video tee. Create a converter if
205
PJ_DEF(pj_status_t) pjmedia_vid_tee_add_dst_port2(pjmedia_port *vid_tee,
209
vid_tee_port *tee = (vid_tee_port*)vid_tee;
210
pjmedia_video_format_detail *vfd;
212
PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN,
215
if (tee->dst_port_cnt >= tee->dst_port_maxcnt)
218
pj_bzero(&tee->tee_conv[tee->dst_port_cnt], sizeof(tee->tee_conv[0]));
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)
226
const pjmedia_video_format_info *vfi;
227
pjmedia_video_apply_fmt_param vafp;
228
pjmedia_conversion_param conv_param;
231
vfi = pjmedia_get_video_format_info(NULL, port->info.fmt.id);
233
return PJMEDIA_EBADFMT;
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)
241
realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)?
242
2: 1, vafp.framebytes);
244
pjmedia_format_copy(&conv_param.src, &vid_tee->info.fmt);
245
pjmedia_format_copy(&conv_param.dst, &port->info.fmt);
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)
253
tee->tee_conv[tee->dst_port_cnt].conv_buf_size = vafp.framebytes;
255
realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)?
256
1: 0, tee->buf_size);
259
tee->dst_ports[tee->dst_port_cnt].dst = port;
260
tee->dst_ports[tee->dst_port_cnt].option = option;
268
* Remove a destination media port from the video tee.
270
PJ_DEF(pj_status_t) pjmedia_vid_tee_remove_dst_port(pjmedia_port *vid_tee,
273
vid_tee_port *tee = (vid_tee_port*)vid_tee;
276
PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN,
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);
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);
297
static pj_status_t tee_put_frame(pjmedia_port *port, pjmedia_frame *frame)
299
vid_tee_port *tee = (vid_tee_port*)port;
301
const pj_uint8_t PUT_FRM_DONE = 1;
303
pj_bzero(tee->put_frm_flag, tee->dst_port_cnt *
304
sizeof(tee->put_frm_flag[0]));
306
for (i = 0; i < tee->dst_port_cnt; ++i) {
307
pjmedia_frame frame_ = *frame;
309
if (tee->put_frm_flag[i])
312
if (tee->tee_conv[i].conv) {
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,
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));
329
/* Find other destination ports which has the same format so
330
* we don't need to do the same conversion twice.
332
for (j = i; j < tee->dst_port_cnt; ++j) {
333
pjmedia_frame framep;
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))
347
/* For dst_ports that do in-place processing, we need to duplicate
348
* the data source first.
350
if (tee->dst_ports[j].option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)
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);
358
/* Deliver the data */
359
pjmedia_port_put_frame(tee->dst_ports[j].dst, &framep);
360
tee->put_frm_flag[j] = PUT_FRM_DONE;
362
if (!tee->tee_conv[i].conv)
370
static pj_status_t tee_get_frame(pjmedia_port *port, pjmedia_frame *frame)
373
PJ_UNUSED_ARG(frame);
375
pj_assert(!"Bug! Tee port get_frame() shouldn't be called.");
380
static pj_status_t tee_destroy(pjmedia_port *port)
382
vid_tee_port *tee = (vid_tee_port*)port;
384
PJ_ASSERT_RETURN(port && port->info.signature==TEE_PORT_SIGN, PJ_EINVAL);
386
pj_pool_release(tee->pool);
388
pj_pool_release(tee->buf_pool);
390
pj_bzero(tee, sizeof(*tee));
396
#endif /* PJMEDIA_HAS_VIDEO */