1 /*
2 * Copyright 2008-2014 Arsen Chaloyan
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * $Id: mpf_context.c 2181 2014-09-14 04:29:38Z achaloyan@gmail.com $
17 */
18
19 #ifdef WIN32
20 #pragma warning(disable: 4127)
21 #endif
22 #include <apr_ring.h>
23 #include "mpf_context.h"
24 #include "mpf_termination.h"
25 #include "mpf_stream.h"
26 #include "mpf_bridge.h"
27 #include "mpf_multiplier.h"
28 #include "mpf_mixer.h"
29 #include "apt_log.h"
30
31 /** Item of the association matrix */
32 typedef struct {
33 unsigned char on;
34 } matrix_item_t;
35
36 /** Item of the association matrix header */
37 typedef struct {
38 mpf_termination_t *termination;
39 unsigned char tx_count;
40 unsigned char rx_count;
41 } header_item_t;
42
43 /** Media processing context */
44 struct mpf_context_t {
45 /** Ring entry */
46 APR_RING_ENTRY(mpf_context_t) link;
47 /** Back pointer to the context factory */
48 mpf_context_factory_t *factory;
49 /** Pool to allocate memory from */
50 apr_pool_t *pool;
51 /** Informative name of the context used for debugging */
52 const char *name;
53 /** External object */
54 void *obj;
55
56 /** Max number of terminations in the context */
57 apr_size_t capacity;
58 /** Current number of terminations in the context */
59 apr_size_t count;
60 /** Header of the association matrix */
61 header_item_t *header;
62 /** Association matrix, which represents the topology */
63 matrix_item_t **matrix;
64
65 /** Array of media processing objects constructed while
66 applying topology based on association matrix */
67 apr_array_header_t *mpf_objects;
68 };
69
70 /** Factory of media contexts */
71 struct mpf_context_factory_t {
72 /** Ring head */
73 APR_RING_HEAD(mpf_context_head_t, mpf_context_t) head;
74 };
75
76
77 static APR_INLINE apt_bool_t stream_direction_compatibility_check(mpf_termination_t *termination1, mpf_termination_t *termination2);
78 static mpf_object_t* mpf_context_bridge_create(mpf_context_t *context, apr_size_t i);
79 static mpf_object_t* mpf_context_multiplier_create(mpf_context_t *context, apr_size_t i);
80 static mpf_object_t* mpf_context_mixer_create(mpf_context_t *context, apr_size_t j);
81
82
mpf_context_factory_create(apr_pool_t * pool)83 MPF_DECLARE(mpf_context_factory_t*) mpf_context_factory_create(apr_pool_t *pool)
84 {
85 mpf_context_factory_t *factory = apr_palloc(pool, sizeof(mpf_context_factory_t));
86 APR_RING_INIT(&factory->head, mpf_context_t, link);
87 return factory;
88 }
89
mpf_context_factory_destroy(mpf_context_factory_t * factory)90 MPF_DECLARE(void) mpf_context_factory_destroy(mpf_context_factory_t *factory)
91 {
92 mpf_context_t *context;
93 while(!APR_RING_EMPTY(&factory->head, mpf_context_t, link)) {
94 context = APR_RING_FIRST(&factory->head);
95 mpf_context_destroy(context);
96 APR_RING_REMOVE(context, link);
97 }
98 }
99
mpf_context_factory_process(mpf_context_factory_t * factory)100 MPF_DECLARE(apt_bool_t) mpf_context_factory_process(mpf_context_factory_t *factory)
101 {
102 mpf_context_t *context;
103 for(context = APR_RING_FIRST(&factory->head);
104 context != APR_RING_SENTINEL(&factory->head, mpf_context_t, link);
105 context = APR_RING_NEXT(context, link)) {
106
107 mpf_context_process(context);
108 }
109
110 return TRUE;
111 }
112
113
mpf_context_create(mpf_context_factory_t * factory,const char * name,void * obj,apr_size_t max_termination_count,apr_pool_t * pool)114 MPF_DECLARE(mpf_context_t*) mpf_context_create(
115 mpf_context_factory_t *factory,
116 const char *name,
117 void *obj,
118 apr_size_t max_termination_count,
119 apr_pool_t *pool)
120 {
121 apr_size_t i,j;
122 matrix_item_t *matrix_item;
123 header_item_t *header_item;
124 mpf_context_t *context = apr_palloc(pool,sizeof(mpf_context_t));
125 APR_RING_ELEM_INIT(context,link);
126 context->factory = factory;
127 context->obj = obj;
128 context->pool = pool;
129 context->name = name;
130 if(!context->name) {
131 context->name = apr_psprintf(pool,"0x%pp",context);
132 }
133 context->capacity = max_termination_count;
134 context->count = 0;
135 context->mpf_objects = apr_array_make(pool,1,sizeof(mpf_object_t*));
136 context->header = apr_palloc(pool,context->capacity * sizeof(header_item_t));
137 context->matrix = apr_palloc(pool,context->capacity * sizeof(matrix_item_t*));
138 for(i=0; i<context->capacity; i++) {
139 header_item = &context->header[i];
140 header_item->termination = NULL;
141 header_item->tx_count = 0;
142 header_item->rx_count = 0;
143 context->matrix[i] = apr_palloc(pool,context->capacity * sizeof(matrix_item_t));
144 for(j=0; j<context->capacity; j++) {
145 matrix_item = &context->matrix[i][j];
146 matrix_item->on = 0;
147 }
148 }
149
150 return context;
151 }
152
mpf_context_destroy(mpf_context_t * context)153 MPF_DECLARE(apt_bool_t) mpf_context_destroy(mpf_context_t *context)
154 {
155 apr_size_t i;
156 mpf_termination_t *termination;
157 for(i=0; i<context->capacity; i++){
158 termination = context->header[i].termination;
159 if(termination) {
160 mpf_context_termination_subtract(context,termination);
161 mpf_termination_subtract(termination);
162 }
163 }
164 return TRUE;
165 }
166
mpf_context_object_get(const mpf_context_t * context)167 MPF_DECLARE(void*) mpf_context_object_get(const mpf_context_t *context)
168 {
169 return context->obj;
170 }
171
mpf_context_termination_add(mpf_context_t * context,mpf_termination_t * termination)172 MPF_DECLARE(apt_bool_t) mpf_context_termination_add(mpf_context_t *context, mpf_termination_t *termination)
173 {
174 apr_size_t i;
175 header_item_t *header_item;
176 for(i=0; i<context->capacity; i++) {
177 header_item = &context->header[i];
178 if(header_item->termination) {
179 continue;
180 }
181 if(!context->count) {
182 apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Add Media Context %s",context->name);
183 APR_RING_INSERT_TAIL(&context->factory->head,context,mpf_context_t,link);
184 }
185
186 header_item->termination = termination;
187 header_item->tx_count = 0;
188 header_item->rx_count = 0;
189
190 termination->slot = i;
191 context->count++;
192 return TRUE;
193 }
194 return FALSE;
195 }
196
mpf_context_termination_subtract(mpf_context_t * context,mpf_termination_t * termination)197 MPF_DECLARE(apt_bool_t) mpf_context_termination_subtract(mpf_context_t *context, mpf_termination_t *termination)
198 {
199 header_item_t *header_item1;
200 header_item_t *header_item2;
201 matrix_item_t *item;
202 apr_size_t j,k;
203 apr_size_t i = termination->slot;
204 if(i >= context->capacity) {
205 return FALSE;
206 }
207 header_item1 = &context->header[i];
208 if(header_item1->termination != termination) {
209 return FALSE;
210 }
211
212 for(j=0,k=0; j<context->capacity && k<context->count; j++) {
213 header_item2 = &context->header[j];
214 if(!header_item2->termination) {
215 continue;
216 }
217 k++;
218
219 item = &context->matrix[i][j];
220 if(item->on) {
221 item->on = 0;
222 header_item1->tx_count--;
223 header_item2->rx_count--;
224 }
225
226 item = &context->matrix[j][i];
227 if(item->on) {
228 item->on = 0;
229 header_item2->tx_count--;
230 header_item1->rx_count--;
231 }
232 }
233 header_item1->termination = NULL;
234
235 termination->slot = (apr_size_t)-1;
236 context->count--;
237 if(!context->count) {
238 apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Remove Media Context %s",context->name);
239 APR_RING_REMOVE(context,link);
240 }
241 return TRUE;
242 }
243
mpf_context_association_add(mpf_context_t * context,mpf_termination_t * termination1,mpf_termination_t * termination2)244 MPF_DECLARE(apt_bool_t) mpf_context_association_add(mpf_context_t *context, mpf_termination_t *termination1, mpf_termination_t *termination2)
245 {
246 header_item_t *header_item1;
247 matrix_item_t *matrix_item1;
248 header_item_t *header_item2;
249 matrix_item_t *matrix_item2;
250 apr_size_t i = termination1->slot;
251 apr_size_t j = termination2->slot;
252 if(i >= context->capacity || j >= context->capacity) {
253 return FALSE;
254 }
255
256 header_item1 = &context->header[i];
257 header_item2 = &context->header[j];
258
259 if(header_item1->termination != termination1 || header_item2->termination != termination2) {
260 return FALSE;
261 }
262
263 matrix_item1 = &context->matrix[i][j];
264 matrix_item2 = &context->matrix[j][i];
265
266 /* 1 -> 2 */
267 if(!matrix_item1->on) {
268 if(stream_direction_compatibility_check(header_item1->termination,header_item2->termination) == TRUE) {
269 matrix_item1->on = 1;
270 header_item1->tx_count ++;
271 header_item2->rx_count ++;
272 }
273 }
274
275 /* 2 -> 1 */
276 if(!matrix_item2->on) {
277 if(stream_direction_compatibility_check(header_item2->termination,header_item1->termination) == TRUE) {
278 matrix_item2->on = 1;
279 header_item2->tx_count ++;
280 header_item1->rx_count ++;
281 }
282 }
283 return TRUE;
284 }
285
mpf_context_association_remove(mpf_context_t * context,mpf_termination_t * termination1,mpf_termination_t * termination2)286 MPF_DECLARE(apt_bool_t) mpf_context_association_remove(mpf_context_t *context, mpf_termination_t *termination1, mpf_termination_t *termination2)
287 {
288 header_item_t *header_item1;
289 matrix_item_t *matrix_item1;
290 header_item_t *header_item2;
291 matrix_item_t *matrix_item2;
292 apr_size_t i = termination1->slot;
293 apr_size_t j = termination2->slot;
294 if(i >= context->capacity || j >= context->capacity) {
295 return FALSE;
296 }
297
298 header_item1 = &context->header[i];
299 header_item2 = &context->header[j];
300
301 if(header_item1->termination != termination1 || header_item2->termination != termination2) {
302 return FALSE;
303 }
304
305 matrix_item1 = &context->matrix[i][j];
306 matrix_item2 = &context->matrix[j][i];
307
308 /* 1 -> 2 */
309 if(matrix_item1->on == 1) {
310 matrix_item1->on = 0;
311 header_item1->tx_count --;
312 header_item2->rx_count --;
313 }
314
315 /* 2 -> 1 */
316 if(matrix_item2->on == 1) {
317 matrix_item2->on = 0;
318 header_item2->tx_count --;
319 header_item1->rx_count --;
320 }
321 return TRUE;
322 }
323
mpf_context_associations_reset(mpf_context_t * context)324 MPF_DECLARE(apt_bool_t) mpf_context_associations_reset(mpf_context_t *context)
325 {
326 apr_size_t i,j,k;
327 header_item_t *header_item1;
328 header_item_t *header_item2;
329 matrix_item_t *item;
330
331 /* destroy existing topology / if any */
332 mpf_context_topology_destroy(context);
333
334 /* reset assigned associations */
335 for(i=0,k=0; i<context->capacity && k<context->count; i++) {
336 header_item1 = &context->header[i];
337 if(!header_item1->termination) {
338 continue;
339 }
340 k++;
341
342 if(!header_item1->tx_count && !header_item1->rx_count) {
343 continue;
344 }
345
346 for(j=i; j<context->capacity; j++) {
347 header_item2 = &context->header[j];
348 if(!header_item2->termination) {
349 continue;
350 }
351
352 item = &context->matrix[i][j];
353 if(item->on) {
354 item->on = 0;
355 header_item1->tx_count--;
356 header_item2->rx_count--;
357 }
358
359 item = &context->matrix[j][i];
360 if(item->on) {
361 item->on = 0;
362 header_item2->tx_count--;
363 header_item1->rx_count--;
364 }
365 }
366 }
367 return TRUE;
368 }
369
mpf_context_object_add(mpf_context_t * context,mpf_object_t * object)370 static apt_bool_t mpf_context_object_add(mpf_context_t *context, mpf_object_t *object)
371 {
372 if(!object) {
373 return FALSE;
374 }
375
376 APR_ARRAY_PUSH(context->mpf_objects, mpf_object_t*) = object;
377 #if 1
378 mpf_object_trace(object);
379 #endif
380 return TRUE;
381 }
382
mpf_context_topology_apply(mpf_context_t * context)383 MPF_DECLARE(apt_bool_t) mpf_context_topology_apply(mpf_context_t *context)
384 {
385 apr_size_t i,k;
386 header_item_t *header_item;
387 mpf_object_t *object;
388
389 /* first destroy existing topology / if any */
390 mpf_context_topology_destroy(context);
391
392 for(i=0,k=0; i<context->capacity && k<context->count; i++) {
393 header_item = &context->header[i];
394 if(!header_item->termination) {
395 continue;
396 }
397 k++;
398
399 if(header_item->tx_count > 0) {
400 object = NULL;
401 if(header_item->tx_count == 1) {
402 object = mpf_context_bridge_create(context,i);
403 }
404 else { /* tx_count > 1 */
405 object = mpf_context_multiplier_create(context,i);
406 }
407
408 mpf_context_object_add(context,object);
409 }
410 if(header_item->rx_count > 1) {
411 object = mpf_context_mixer_create(context,i);
412 mpf_context_object_add(context,object);
413 }
414 }
415
416 return TRUE;
417 }
418
mpf_context_topology_destroy(mpf_context_t * context)419 MPF_DECLARE(apt_bool_t) mpf_context_topology_destroy(mpf_context_t *context)
420 {
421 if(context->mpf_objects->nelts) {
422 int i;
423 mpf_object_t *object;
424 for(i=0; i<context->mpf_objects->nelts; i++) {
425 object = APR_ARRAY_IDX(context->mpf_objects,i,mpf_object_t*);
426 mpf_object_destroy(object);
427 }
428 apr_array_clear(context->mpf_objects);
429 }
430 return TRUE;
431 }
432
mpf_context_process(mpf_context_t * context)433 MPF_DECLARE(apt_bool_t) mpf_context_process(mpf_context_t *context)
434 {
435 int i;
436 mpf_object_t *object;
437 for(i=0; i<context->mpf_objects->nelts; i++) {
438 object = APR_ARRAY_IDX(context->mpf_objects,i,mpf_object_t*);
439 if(object && object->process) {
440 object->process(object);
441 }
442 }
443 return TRUE;
444 }
445
446
mpf_context_bridge_create(mpf_context_t * context,apr_size_t i)447 static mpf_object_t* mpf_context_bridge_create(mpf_context_t *context, apr_size_t i)
448 {
449 header_item_t *header_item1 = &context->header[i];
450 header_item_t *header_item2;
451 matrix_item_t *item;
452 apr_size_t j;
453 for(j=0; j<context->capacity; j++) {
454 header_item2 = &context->header[j];
455 if(!header_item2->termination) {
456 continue;
457 }
458 item = &context->matrix[i][j];
459 if(!item->on) {
460 continue;
461 }
462
463 if(header_item2->rx_count > 1) {
464 /* mixer will be created instead */
465 return NULL;
466 }
467
468 /* create bridge i -> j */
469 if(header_item1->termination && header_item2->termination) {
470 return mpf_bridge_create(
471 header_item1->termination->audio_stream,
472 header_item2->termination->audio_stream,
473 header_item1->termination->codec_manager,
474 context->name,
475 context->pool);
476 }
477 }
478 return NULL;
479 }
480
mpf_context_multiplier_create(mpf_context_t * context,apr_size_t i)481 static mpf_object_t* mpf_context_multiplier_create(mpf_context_t *context, apr_size_t i)
482 {
483 mpf_audio_stream_t **sink_arr;
484 header_item_t *header_item1 = &context->header[i];
485 header_item_t *header_item2;
486 matrix_item_t *item;
487 apr_size_t j,k;
488 sink_arr = apr_palloc(context->pool,header_item1->tx_count * sizeof(mpf_audio_stream_t*));
489 for(j=0,k=0; j<context->capacity && k<header_item1->tx_count; j++) {
490 header_item2 = &context->header[j];
491 if(!header_item2->termination) {
492 continue;
493 }
494 item = &context->matrix[i][j];
495 if(!item->on) {
496 continue;
497 }
498 sink_arr[k] = header_item2->termination->audio_stream;
499 k++;
500 }
501 return mpf_multiplier_create(
502 header_item1->termination->audio_stream,
503 sink_arr,
504 header_item1->tx_count,
505 header_item1->termination->codec_manager,
506 context->name,
507 context->pool);
508 }
509
mpf_context_mixer_create(mpf_context_t * context,apr_size_t j)510 static mpf_object_t* mpf_context_mixer_create(mpf_context_t *context, apr_size_t j)
511 {
512 mpf_audio_stream_t **source_arr;
513 header_item_t *header_item1 = &context->header[j];
514 header_item_t *header_item2;
515 matrix_item_t *item;
516 apr_size_t i,k;
517 source_arr = apr_palloc(context->pool,header_item1->rx_count * sizeof(mpf_audio_stream_t*));
518 for(i=0,k=0; i<context->capacity && k<header_item1->rx_count; i++) {
519 header_item2 = &context->header[i];
520 if(!header_item2->termination) {
521 continue;
522 }
523 item = &context->matrix[i][j];
524 if(!item->on) {
525 continue;
526 }
527 source_arr[k] = header_item2->termination->audio_stream;
528 k++;
529 }
530 return mpf_mixer_create(
531 source_arr,
532 header_item1->rx_count,
533 header_item1->termination->audio_stream,
534 header_item1->termination->codec_manager,
535 context->name,
536 context->pool);
537 }
538
stream_direction_compatibility_check(mpf_termination_t * termination1,mpf_termination_t * termination2)539 static APR_INLINE apt_bool_t stream_direction_compatibility_check(mpf_termination_t *termination1, mpf_termination_t *termination2)
540 {
541 mpf_audio_stream_t *source = termination1->audio_stream;
542 mpf_audio_stream_t *sink = termination2->audio_stream;
543 if(source && (source->direction & STREAM_DIRECTION_RECEIVE) == STREAM_DIRECTION_RECEIVE &&
544 sink && (sink->direction & STREAM_DIRECTION_SEND) == STREAM_DIRECTION_SEND) {
545 return TRUE;
546 }
547 return FALSE;
548 }
549