Coverage Report

Created: 2025-12-05 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pjsip/pjmedia/include/pjmedia/port.h
Line
Count
Source
1
/* 
2
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
3
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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
#ifndef __PJMEDIA_PORT_H__
20
#define __PJMEDIA_PORT_H__
21
22
/**
23
 * @file port.h
24
 * @brief Port interface declaration
25
 */
26
#include <pjmedia/clock.h>
27
#include <pjmedia/event.h>
28
#include <pjmedia/format.h>
29
#include <pjmedia/frame.h>
30
#include <pjmedia/signatures.h>
31
#include <pj/assert.h>
32
#include <pj/lock.h>
33
#include <pj/os.h>
34
35
36
/**
37
  @addtogroup PJMEDIA_PORT Media Ports Framework
38
  @{
39
40
  @section media_port_intro Media Port Concepts
41
  
42
  @subsection The Media Port
43
  A media port (represented with pjmedia_port "class") provides a generic
44
  and extensible framework for implementing media elements. Media element
45
  itself could be a media source, sink, or processing element. A media
46
  port interface basically has the following properties:
47
  - media port information (pjmedia_port_info) to describe the
48
  media port properties (sampling rate, number of channels, etc.),
49
  - optional pointer to function to acquire frames from the port (the
50
    <tt>get_frame() </tt> interface), which will be called by
51
    #pjmedia_port_get_frame() public API, and
52
  - optional pointer to function to store frames to the port (the
53
    <tt>put_frame()</tt> interface) which will be called by
54
    #pjmedia_port_put_frame() public API.
55
56
  The <tt>get_frame()</tt> and <tt>put_frame()</tt> interface of course
57
  would only need to be implemented if the media port emits and/or takes
58
  media frames respectively.
59
  
60
  Media ports are passive "objects". By default, there is no worker thread
61
  to run the media flow. Applications (or other PJMEDIA
62
  components, as explained in @ref PJMEDIA_PORT_CLOCK) must actively call
63
  #pjmedia_port_get_frame() or #pjmedia_port_put_frame() from/to the media
64
  port in order to retrieve/store media frames.
65
  
66
  Some media ports (such as @ref PJMEDIA_CONF and @ref PJMEDIA_RESAMPLE_PORT)
67
  may be interconnected with (or encapsulate) other port, to perform the
68
  combined task of the ports, while some
69
  others represent the ultimate source/sink termination for the media. 
70
  Interconnection means the upstream media port will call <tt>get_frame()</tt>
71
  and <tt>put_frame()</tt> to its downstream media port. For this to happen,
72
  the media ports need to have the same format, where format is defined as
73
  combination of sample format, clock rate, channel count, bits per sample,
74
  and samples per frame for audio media.
75
76
77
  @subsection port_clock_ex1 Example: Manual Resampling
78
79
  For example, suppose application wants to convert the sampling rate
80
  of one WAV file to another. In this case, application would create and
81
  arrange media ports connection as follows:
82
83
  \img{pjmedia/docs/sample-manual-resampling.jpg}
84
85
  Application would setup the media ports using the following pseudo-
86
  code:
87
88
  \code
89
  
90
      pjmedia_port *player, *resample, *writer;
91
      pj_status_t status;
92
  
93
      // Create the file player port.
94
      status = pjmedia_wav_player_port_create(pool, 
95
                                              "Input.WAV",          // file name
96
                                              20,                   // ptime.
97
                                              PJMEDIA_FILE_NO_LOOP, // flags
98
                                              0,                    // buffer size
99
                                              NULL,                 // user data.
100
                                              &player );
101
      PJ_ASSERT_RETURN(status==PJ_SUCCESS, PJ_SUCCESS);
102
  
103
      // Create the resample port with specifying the target sampling rate, 
104
      // and with the file port as the source. This will effectively
105
      // connect the resample port with the player port.
106
      status = pjmedia_resample_port_create( pool, player, 8000, 
107
                                             0, &resample);
108
      PJ_ASSERT_RETURN(status==PJ_SUCCESS, PJ_SUCCESS);
109
  
110
      // Create the file writer, specifying the resample port's configuration
111
      // as the WAV parameters.
112
      status pjmedia_wav_writer_port_create(pool, 
113
                                            "Output.WAV",  // file name.
114
                                            resample->info.clock_rate,
115
                                            resample->info.channel_count,
116
                                            resample->info.samples_per_frame,
117
                                            resample->info.bits_per_sample,
118
                                            0,          // flags
119
                                            0,          // buffer size
120
                                            NULL,       // user data.
121
                                            &writer);
122
  
123
  \endcode
124
125
  
126
  After the ports have been set up, application can perform the conversion
127
  process by running this loop:
128
 
129
  \code
130
  
131
        pj_int16_t samplebuf[MAX_FRAME];
132
        
133
        while (1) {
134
            pjmedia_frame frame;
135
            pj_status_t status;
136
  
137
            frame.buf = samplebuf;
138
            frame.size = sizeof(samplebuf);
139
  
140
            // Get the frame from resample port.
141
            status = pjmedia_port_get_frame(resample, &frame);
142
            if (status != PJ_SUCCESS || frame.type == PJMEDIA_FRAME_TYPE_NONE) {
143
                // End-of-file, end the conversion.
144
                break;
145
            }
146
  
147
            // Put the frame to write port.
148
            status = pjmedia_port_put_frame(writer, &frame);
149
            if (status != PJ_SUCCESS) {
150
                // Error in writing the file.
151
                break;
152
            }
153
        }
154
  
155
  \endcode
156
 
157
  For the sake of completeness, after the resampling process is done, 
158
  application would need to destroy the ports:
159
  
160
  \code
161
        // Note: by default, destroying resample port will destroy the
162
        //       the downstream port too.
163
        pjmedia_port_destroy(resample);
164
        pjmedia_port_destroy(writer);
165
  \endcode
166
 
167
 
168
  The above steps are okay for our simple purpose of changing file's sampling
169
  rate. But for other purposes, the process of reading and writing frames
170
  need to be done in timely manner (for example, sending RTP packets to
171
  remote stream). And more over, as the application's scope goes bigger,
172
  the same pattern of manually reading/writing frames comes up more and more often,
173
  thus perhaps it would be better if PJMEDIA provides mechanism to 
174
  automate this process.
175
  
176
  And indeed PJMEDIA does provide such mechanism, which is described in 
177
  @ref PJMEDIA_PORT_CLOCK section.
178
179
180
  @subsection media_port_autom Automating Media Flow
181
182
  PJMEDIA provides few mechanisms to make media flows automatically
183
  among media ports. This concept is described in @ref PJMEDIA_PORT_CLOCK 
184
  section.
185
*/
186
187
PJ_BEGIN_DECL
188
189
190
/* Since media port's callback is called synchronously and has a return value,
191
 * it can introduce a deadlock when a mutex is held before calling it.
192
 * To prevent this, media ports' set_eof_cb() and set_cb() functions have
193
 * been deprecated and replaced by set_eof_cb2() and set_cb2(), which
194
 * will call the callback asynchronously without expecting any return value.
195
 *
196
 * See also https://github.com/pjsip/pjproject/issues/2251.
197
 */
198
#ifndef DEPRECATED_FOR_TICKET_2251
199
#  define DEPRECATED_FOR_TICKET_2251    0
200
#endif
201
202
203
/**
204
 * Create 32bit port signature from ASCII characters.
205
 */
206
#define PJMEDIA_PORT_SIG(a,b,c,d)               PJMEDIA_OBJ_SIG(a,b,c,d)
207
208
209
/**
210
 * Port operation setting.
211
 */
212
typedef enum pjmedia_port_op
213
{
214
    /** 
215
     * No change to the port TX or RX settings.
216
     */
217
    PJMEDIA_PORT_NO_CHANGE,
218
219
    /**
220
     * TX or RX is disabled from the port. It means get_frame() or
221
     * put_frame() WILL NOT be called for this port.
222
     */
223
    PJMEDIA_PORT_DISABLE,
224
225
    /**
226
     * TX or RX is muted, which means that get_frame() or put_frame()
227
     * will still be called, but the audio frame is discarded.
228
     */
229
    PJMEDIA_PORT_MUTE,
230
231
    /**
232
     * Enable TX and RX to/from this port.
233
     */
234
    PJMEDIA_PORT_ENABLE
235
236
} pjmedia_port_op;
237
238
239
/**
240
 * Port info.
241
 */
242
typedef struct pjmedia_port_info
243
{
244
    pj_str_t        name;               /**< Port name.                     */
245
    pj_uint32_t     signature;          /**< Port signature.                */
246
    pjmedia_dir     dir;                /**< Port direction.                */
247
    pjmedia_format  fmt;                /**< Format.                        */
248
} pjmedia_port_info;
249
250
/**
251
 * Utility to retrieve audio clock rate/sampling rate value from
252
 * pjmedia_port_info.
253
 *
254
 * @param pia           Pointer to port info containing audio format.
255
 * @return              Audio clock rate.
256
 */
257
PJ_INLINE(unsigned) PJMEDIA_PIA_SRATE(const pjmedia_port_info *pia)
258
0
{
259
0
    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
260
0
              pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
261
0
    return pia->fmt.det.aud.clock_rate;
262
0
}
263
264
/**
265
 * Utility to retrieve audio channel count value from pjmedia_port_info.
266
 *
267
 * @param pia           Pointer to port info containing audio format.
268
 * @return              Audio channel count.
269
 */
270
PJ_INLINE(unsigned) PJMEDIA_PIA_CCNT(const pjmedia_port_info *pia)
271
0
{
272
0
    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
273
0
              pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
274
0
    return pia->fmt.det.aud.channel_count;
275
0
}
276
277
/**
278
 * Utility to retrieve audio bits per sample value from pjmedia_port_info.
279
 *
280
 * @param pia           Pointer to port info containing audio format.
281
 * @return              Number of bits per sample.
282
 */
283
PJ_INLINE(unsigned) PJMEDIA_PIA_BITS(const pjmedia_port_info *pia)
284
0
{
285
0
    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
286
0
              pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
287
0
    return pia->fmt.det.aud.bits_per_sample;
288
0
}
289
290
/**
291
 * Utility to retrieve audio frame interval (ptime) value from
292
 * pjmedia_port_info.
293
 *
294
 * @param pia           Pointer to port info containing audio format.
295
 * @return              Frame interval in msec.
296
 */
297
PJ_INLINE(unsigned) PJMEDIA_PIA_PTIME(const pjmedia_port_info *pia)
298
0
{
299
0
    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
300
0
              pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
301
0
    return pia->fmt.det.aud.frame_time_usec / 1000;
302
0
}
303
304
/**
305
 * This is a utility routine to retrieve the audio samples_per_frame value
306
 * from port info.
307
 *
308
 * @param pia           Pointer to port info containing audio format.
309
 * @return              Samples per frame value.
310
 */
311
PJ_INLINE(unsigned) PJMEDIA_PIA_SPF(const pjmedia_port_info *pia)
312
0
{
313
0
    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
314
0
              pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
315
0
    return PJMEDIA_AFD_SPF(&pia->fmt.det.aud);
316
0
}
317
318
/**
319
 * This is a utility routine to retrieve the average bitrate value
320
 * from port info.
321
 *
322
 * @param pia           Pointer to port info containing audio format.
323
 * @return              Bitrate, in bits per second.
324
 */
325
PJ_INLINE(unsigned) PJMEDIA_PIA_AVG_BPS(const pjmedia_port_info *pia)
326
0
{
327
0
    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
328
0
              pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
329
0
    return pia->fmt.det.aud.avg_bps;
330
0
}
331
332
/**
333
 * This is a utility routine to retrieve the maximum bitrate value
334
 * from port info.
335
 *
336
 * @param pia           Pointer to port info containing audio format.
337
 * @return              Bitrate, in bits per second.
338
 */
339
PJ_INLINE(unsigned) PJMEDIA_PIA_MAX_BPS(const pjmedia_port_info *pia)
340
0
{
341
0
    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
342
0
              pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
343
0
    return pia->fmt.det.aud.max_bps;
344
0
}
345
346
/**
347
 * This is a utility routine to retrieve the average audio frame size value
348
 * from pjmedia_port_info.
349
 *
350
 * @param pia           Pointer to port info containing audio format.
351
 * @return              Frame size in bytes.
352
 */
353
PJ_INLINE(unsigned) PJMEDIA_PIA_AVG_FSZ(const pjmedia_port_info *pia)
354
0
{
355
0
    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
356
0
              pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
357
0
    return PJMEDIA_AFD_AVG_FSZ(&pia->fmt.det.aud);
358
0
}
359
360
/**
361
 * Utility to retrieve audio frame size from maximum bitrate from
362
 * pjmedia_port_info.
363
 *
364
 * @param pia           Pointer to port info containing audio format.
365
 * @return              Frame size in bytes.
366
 */
367
PJ_INLINE(unsigned) PJMEDIA_PIA_MAX_FSZ(const pjmedia_port_info *pia)
368
0
{
369
0
    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
370
0
              pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
371
0
    return PJMEDIA_AFD_MAX_FSZ(&pia->fmt.det.aud);
372
0
}
373
374
/**
375
 * Port interface.
376
 */
377
typedef struct pjmedia_port
378
{
379
    pjmedia_port_info    info;              /**< Port information.  */
380
381
    /** Port data can be used by the port creator to attach arbitrary
382
     *  value to be associated with the port.
383
     */
384
    struct port_data {
385
        void            *pdata;             /**< Pointer data.      */
386
        long             ldata;             /**< Long data.         */
387
    } port_data;
388
389
    /**
390
     * Group lock.
391
     *
392
     * This is optional, but if this port is registered to the audio/video
393
     * conference bridge, the bridge will create one if the port has none.
394
     */
395
    pj_grp_lock_t       *grp_lock;
396
397
    /**
398
     * Get clock source.
399
     * This should only be called by #pjmedia_port_get_clock_src().
400
     */
401
    pjmedia_clock_src* (*get_clock_src)(struct pjmedia_port *this_port,
402
                                        pjmedia_dir dir);
403
404
    /**
405
     * Sink interface. 
406
     * This should only be called by #pjmedia_port_put_frame().
407
     */
408
    pj_status_t (*put_frame)(struct pjmedia_port *this_port, 
409
                             pjmedia_frame *frame);
410
411
    /**
412
     * Source interface. 
413
     * This should only be called by #pjmedia_port_get_frame().
414
     */
415
    pj_status_t (*get_frame)(struct pjmedia_port *this_port, 
416
                             pjmedia_frame *frame);
417
418
    /**
419
     * Destructor.
420
     * This should only be called by #pjmedia_port_destroy().
421
     */
422
    pj_status_t (*on_destroy)(struct pjmedia_port *this_port);
423
424
} pjmedia_port;
425
426
427
/**
428
 * This is an auxiliary function to initialize port info for
429
 * ports which deal with PCM audio.
430
 *
431
 * @param info              The port info to be initialized.
432
 * @param name              Port name.
433
 * @param signature         Port signature.
434
 * @param clock_rate        Port's clock rate.
435
 * @param channel_count     Number of channels.
436
 * @param bits_per_sample   Bits per sample.
437
 * @param samples_per_frame Number of samples per frame.
438
 *
439
 * @return                  PJ_SUCCESS on success.
440
 */
441
PJ_DECL(pj_status_t) pjmedia_port_info_init( pjmedia_port_info *info,
442
                                             const pj_str_t *name,
443
                                             unsigned signature,
444
                                             unsigned clock_rate,
445
                                             unsigned channel_count,
446
                                             unsigned bits_per_sample,
447
                                             unsigned samples_per_frame);
448
449
/**
450
 * This is an auxiliary function to initialize port info for
451
 * ports which deal with PCM audio.
452
 *
453
 * @param info              The port info to be initialized.
454
 * @param name              Port name.
455
 * @param signature         Port signature.
456
 * @param dir               Port's direction.
457
 * @param fmt               Port's media format.
458
 *
459
 * @return                  PJ_SUCCESS on success.
460
 */
461
PJ_DECL(pj_status_t) pjmedia_port_info_init2(pjmedia_port_info *info,
462
                                             const pj_str_t *name,
463
                                             unsigned signature,
464
                                             pjmedia_dir dir,
465
                                             const pjmedia_format *fmt);
466
467
468
/**
469
 * Get a clock source from the port.
470
 *
471
 * @param port      The media port.
472
 * @param dir       Media port's direction.
473
 *
474
 * @return          The clock source or NULL if clock source is not present
475
 *                  in the port.
476
 */
477
PJ_DECL(pjmedia_clock_src *) pjmedia_port_get_clock_src( pjmedia_port *port,
478
                                                         pjmedia_dir dir );
479
480
481
/**
482
 * Get a frame from the port (and subsequent downstream ports).
483
 *
484
 * @param port      The media port.
485
 * @param frame     Frame to store samples.
486
 *
487
 * @return          PJ_SUCCESS on success, or the appropriate error code.
488
 */
489
PJ_DECL(pj_status_t) pjmedia_port_get_frame( pjmedia_port *port,
490
                                             pjmedia_frame *frame );
491
492
/**
493
 * Put a frame to the port (and subsequent downstream ports).
494
 *
495
 * @param port      The media port.
496
 * @param frame     Frame to the put to the port.
497
 *
498
 * @return          PJ_SUCCESS on success, or the appropriate error code.
499
 */
500
PJ_DECL(pj_status_t) pjmedia_port_put_frame( pjmedia_port *port,
501
                                             pjmedia_frame *frame );
502
503
/**
504
 * Destroy port (and subsequent downstream ports).
505
 *
506
 * Note that if the port has group lock, instead of destroying the port
507
 * immediately, this function will just decrease the reference counter.
508
 *
509
 * @param port      The media port.
510
 *
511
 * @return          PJ_SUCCESS on success, or the appropriate error code.
512
 */
513
PJ_DECL(pj_status_t) pjmedia_port_destroy( pjmedia_port *port );
514
515
516
517
/*
518
 *******************************************************************
519
 * Helper functions.
520
 *******************************************************************
521
 */
522
523
/**
524
 * This is a helper function to initialize the port's group lock. This
525
 * function will create a group lock if NULL is passed, initialize the group
526
 * lock by adding the port's destructor to the group lock handler list, and
527
 * increment the reference counter.
528
 *
529
 * Normally this function is only called by media port implementation,
530
 * application should never call this function.
531
 *
532
 * This function will check whether the port implements on_destroy(),
533
 * because when working with conference bridge, a port is required to manage
534
 * its lifetime via group lock (e.g: initialized by this function),
535
 * so basically it must use its own pool and the pool is normally
536
 * released from its on_destroy().
537
 *
538
 * Also note that this function will add a group lock destroy handler
539
 * that calls port's on_destroy(), so port implementation can release
540
 * its resources from on_destroy() as usual.
541
 *
542
 * @param port              The pjmedia port to be initialized.
543
 * @param pool              The pool, this can be a temporary pool as
544
 *                          group lock will create and use its internal pool.
545
 * @param glock             The group lock, or create a new one if NULL.
546
 *
547
 * @return                  PJ_SUCCESS on success, PJ_EEXISTS if group lock
548
 *                          is already initialized, or the other appropriate
549
 *                          error code.
550
 */
551
PJ_DECL(pj_status_t) pjmedia_port_init_grp_lock( pjmedia_port *port,
552
                                                 pj_pool_t *pool,
553
                                                 pj_grp_lock_t *glock );
554
555
556
/**
557
 * This is a helper function to increase the port reference counter.
558
 *
559
 * @param port              The PJMEDIA port.
560
 *
561
 * @return                  PJ_SUCCESS on success.
562
 */
563
PJ_DECL(pj_status_t) pjmedia_port_add_ref( pjmedia_port *port );
564
565
566
/**
567
 * This is a helper function to decrease the port reference counter.
568
 *
569
 * @param port              The PJMEDIA port.
570
 *
571
 * @return                  PJ_SUCCESS on success.
572
 */
573
PJ_DECL(pj_status_t) pjmedia_port_dec_ref( pjmedia_port *port );
574
575
576
/**
577
 * This is a helper function to add port destructor handler.
578
 *
579
 * Application can use this function to schedule its resource release.
580
 * Note that application cannot release the resources related to the port,
581
 * e.g: memory pool (custom ports that do not use its own pool),
582
 * immediately after removing the port from the conference bridge
583
 * as the port removal is done asynchronously.
584
 *
585
 * Usually this function is called after adding the port to the conference
586
 * bridge.
587
 *
588
 * @param port              The PJMEDIA port.
589
 * @param member            A pointer to be passed to the handler.
590
 * @param handler           The destroy handler.
591
 *
592
 * @return                  PJ_SUCCESS on success.
593
 */
594
PJ_DECL(pj_status_t) pjmedia_port_add_destroy_handler(
595
                                            pjmedia_port* port,
596
                                            void *member,
597
                                            pj_grp_lock_handler handler);
598
599
600
/**
601
 * This is a helper function to remove previously registered
602
 * destructor handler.
603
 *
604
 * @param port              The PJMEDIA port.
605
 * @param member            A pointer to be passed to the handler.
606
 * @param handler           The destroy handler.
607
 *
608
 * @return                  PJ_SUCCESS on success.
609
 */
610
PJ_DECL(pj_status_t) pjmedia_port_del_destroy_handler(
611
                                            pjmedia_port* port,
612
                                            void *member,
613
                                            pj_grp_lock_handler handler);
614
615
616
PJ_END_DECL
617
618
/**
619
 * @}
620
 */
621
622
#endif  /* __PJMEDIA_PORT_H__ */
623