| /* $Id$ */ |
| /* |
| * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) |
| * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| #ifndef __PJMEDIA_PORT_H__ |
| #define __PJMEDIA_PORT_H__ |
| |
| /** |
| * @file port.h |
| * @brief Port interface declaration |
| */ |
| #include <pjmedia/clock.h> |
| #include <pjmedia/event.h> |
| #include <pjmedia/format.h> |
| #include <pjmedia/frame.h> |
| #include <pjmedia/signatures.h> |
| #include <pj/assert.h> |
| #include <pj/os.h> |
| |
| |
| /** |
| @addtogroup PJMEDIA_PORT Media Ports Framework |
| @{ |
| |
| @section media_port_intro Media Port Concepts |
| |
| @subsection The Media Port |
| A media port (represented with pjmedia_port "class") provides a generic |
| and extensible framework for implementing media elements. Media element |
| itself could be a media source, sink, or processing element. A media |
| port interface basically has the following properties: |
| - media port information (pjmedia_port_info) to describe the |
| media port properties (sampling rate, number of channels, etc.), |
| - optional pointer to function to acquire frames from the port (the |
| <tt>get_frame() </tt> interface), which will be called by |
| #pjmedia_port_get_frame() public API, and |
| - optional pointer to function to store frames to the port (the |
| <tt>put_frame()</tt> interface) which will be called by |
| #pjmedia_port_put_frame() public API. |
| |
| The <tt>get_frame()</tt> and <tt>put_frame()</tt> interface of course |
| would only need to be implemented if the media port emits and/or takes |
| media frames respectively. |
| |
| Media ports are passive "objects". By default, there is no worker thread |
| to run the media flow. Applications (or other PJMEDIA |
| components, as explained in @ref PJMEDIA_PORT_CLOCK) must actively call |
| #pjmedia_port_get_frame() or #pjmedia_port_put_frame() from/to the media |
| port in order to retrieve/store media frames. |
| |
| Some media ports (such as @ref PJMEDIA_CONF and @ref PJMEDIA_RESAMPLE_PORT) |
| may be interconnected with (or encapsulate) other port, to perform the |
| combined task of the ports, while some |
| others represent the ultimate source/sink termination for the media. |
| Interconnection means the upstream media port will call <tt>get_frame()</tt> |
| and <tt>put_frame()</tt> to its downstream media port. For this to happen, |
| the media ports need to have the same format, where format is defined as |
| combination of sample format, clock rate, channel count, bits per sample, |
| and samples per frame for audio media. |
| |
| |
| @subsection port_clock_ex1 Example: Manual Resampling |
| |
| For example, suppose application wants to convert the sampling rate |
| of one WAV file to another. In this case, application would create and |
| arrange media ports connection as follows: |
| |
| \image html sample-manual-resampling.jpg |
| |
| Application would setup the media ports using the following pseudo- |
| code: |
| |
| \code |
| |
| pjmedia_port *player, *resample, *writer; |
| pj_status_t status; |
| |
| // Create the file player port. |
| status = pjmedia_wav_player_port_create(pool, |
| "Input.WAV", // file name |
| 20, // ptime. |
| PJMEDIA_FILE_NO_LOOP, // flags |
| 0, // buffer size |
| NULL, // user data. |
| &player ); |
| PJ_ASSERT_RETURN(status==PJ_SUCCESS, PJ_SUCCESS); |
| |
| // Create the resample port with specifying the target sampling rate, |
| // and with the file port as the source. This will effectively |
| // connect the resample port with the player port. |
| status = pjmedia_resample_port_create( pool, player, 8000, |
| 0, &resample); |
| PJ_ASSERT_RETURN(status==PJ_SUCCESS, PJ_SUCCESS); |
| |
| // Create the file writer, specifying the resample port's configuration |
| // as the WAV parameters. |
| status pjmedia_wav_writer_port_create(pool, |
| "Output.WAV", // file name. |
| resample->info.clock_rate, |
| resample->info.channel_count, |
| resample->info.samples_per_frame, |
| resample->info.bits_per_sample, |
| 0, // flags |
| 0, // buffer size |
| NULL, // user data. |
| &writer); |
| |
| \endcode |
| |
| |
| After the ports have been set up, application can perform the conversion |
| process by running this loop: |
| |
| \code |
| |
| pj_int16_t samplebuf[MAX_FRAME]; |
| |
| while (1) { |
| pjmedia_frame frame; |
| pj_status_t status; |
| |
| frame.buf = samplebuf; |
| frame.size = sizeof(samplebuf); |
| |
| // Get the frame from resample port. |
| status = pjmedia_port_get_frame(resample, &frame); |
| if (status != PJ_SUCCESS || frame.type == PJMEDIA_FRAME_TYPE_NONE) { |
| // End-of-file, end the conversion. |
| break; |
| } |
| |
| // Put the frame to write port. |
| status = pjmedia_port_put_frame(writer, &frame); |
| if (status != PJ_SUCCESS) { |
| // Error in writing the file. |
| break; |
| } |
| } |
| |
| \endcode |
| |
| For the sake of completeness, after the resampling process is done, |
| application would need to destroy the ports: |
| |
| \code |
| // Note: by default, destroying resample port will destroy the |
| // the downstream port too. |
| pjmedia_port_destroy(resample); |
| pjmedia_port_destroy(writer); |
| \endcode |
| |
| |
| The above steps are okay for our simple purpose of changing file's sampling |
| rate. But for other purposes, the process of reading and writing frames |
| need to be done in timely manner (for example, sending RTP packets to |
| remote stream). And more over, as the application's scope goes bigger, |
| the same pattern of manually reading/writing frames comes up more and more often, |
| thus perhaps it would be better if PJMEDIA provides mechanism to |
| automate this process. |
| |
| And indeed PJMEDIA does provide such mechanism, which is described in |
| @ref PJMEDIA_PORT_CLOCK section. |
| |
| |
| @subsection media_port_autom Automating Media Flow |
| |
| PJMEDIA provides few mechanisms to make media flows automatically |
| among media ports. This concept is described in @ref PJMEDIA_PORT_CLOCK |
| section. |
| */ |
| |
| PJ_BEGIN_DECL |
| |
| |
| /** |
| * Create 32bit port signature from ASCII characters. |
| */ |
| #define PJMEDIA_PORT_SIG(a,b,c,d) PJMEDIA_OBJ_SIG(a,b,c,d) |
| |
| |
| /** |
| * Port operation setting. |
| */ |
| typedef enum pjmedia_port_op |
| { |
| /** |
| * No change to the port TX or RX settings. |
| */ |
| PJMEDIA_PORT_NO_CHANGE, |
| |
| /** |
| * TX or RX is disabled from the port. It means get_frame() or |
| * put_frame() WILL NOT be called for this port. |
| */ |
| PJMEDIA_PORT_DISABLE, |
| |
| /** |
| * TX or RX is muted, which means that get_frame() or put_frame() |
| * will still be called, but the audio frame is discarded. |
| */ |
| PJMEDIA_PORT_MUTE, |
| |
| /** |
| * Enable TX and RX to/from this port. |
| */ |
| PJMEDIA_PORT_ENABLE |
| |
| } pjmedia_port_op; |
| |
| |
| /** |
| * Port info. |
| */ |
| typedef struct pjmedia_port_info |
| { |
| pj_str_t name; /**< Port name. */ |
| pj_uint32_t signature; /**< Port signature. */ |
| pjmedia_dir dir; /**< Port direction. */ |
| pjmedia_format fmt; /**< Format. */ |
| } pjmedia_port_info; |
| |
| /** |
| * Utility to retrieve audio clock rate/sampling rate value from |
| * pjmedia_port_info. |
| * |
| * @param pia Pointer to port info containing audio format. |
| * @return Audio clock rate. |
| */ |
| PJ_INLINE(unsigned) PJMEDIA_PIA_SRATE(const pjmedia_port_info *pia) |
| { |
| pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && |
| pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); |
| return pia->fmt.det.aud.clock_rate; |
| } |
| |
| /** |
| * Utility to retrieve audio channel count value from pjmedia_port_info. |
| * |
| * @param pia Pointer to port info containing audio format. |
| * @return Audio channel count. |
| */ |
| PJ_INLINE(unsigned) PJMEDIA_PIA_CCNT(const pjmedia_port_info *pia) |
| { |
| pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && |
| pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); |
| return pia->fmt.det.aud.channel_count; |
| } |
| |
| /** |
| * Utility to retrieve audio bits per sample value from pjmedia_port_info. |
| * |
| * @param pia Pointer to port info containing audio format. |
| * @return Number of bits per sample. |
| */ |
| PJ_INLINE(unsigned) PJMEDIA_PIA_BITS(const pjmedia_port_info *pia) |
| { |
| pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && |
| pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); |
| return pia->fmt.det.aud.bits_per_sample; |
| } |
| |
| /** |
| * Utility to retrieve audio frame interval (ptime) value from |
| * pjmedia_port_info. |
| * |
| * @param pia Pointer to port info containing audio format. |
| * @return Frame interval in msec. |
| */ |
| PJ_INLINE(unsigned) PJMEDIA_PIA_PTIME(const pjmedia_port_info *pia) |
| { |
| pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && |
| pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); |
| return pia->fmt.det.aud.frame_time_usec / 1000; |
| } |
| |
| /** |
| * This is a utility routine to retrieve the audio samples_per_frame value |
| * from port info. |
| * |
| * @param pia Pointer to port info containing audio format. |
| * @return Samples per frame value. |
| */ |
| PJ_INLINE(unsigned) PJMEDIA_PIA_SPF(const pjmedia_port_info *pia) |
| { |
| pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && |
| pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); |
| return PJMEDIA_AFD_SPF(&pia->fmt.det.aud); |
| } |
| |
| /** |
| * This is a utility routine to retrieve the average bitrate value |
| * from port info. |
| * |
| * @param pia Pointer to port info containing audio format. |
| * @return Bitrate, in bits per second. |
| */ |
| PJ_INLINE(unsigned) PJMEDIA_PIA_AVG_BPS(const pjmedia_port_info *pia) |
| { |
| pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && |
| pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); |
| return pia->fmt.det.aud.avg_bps; |
| } |
| |
| /** |
| * This is a utility routine to retrieve the maximum bitrate value |
| * from port info. |
| * |
| * @param pia Pointer to port info containing audio format. |
| * @return Bitrate, in bits per second. |
| */ |
| PJ_INLINE(unsigned) PJMEDIA_PIA_MAX_BPS(const pjmedia_port_info *pia) |
| { |
| pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && |
| pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); |
| return pia->fmt.det.aud.max_bps; |
| } |
| |
| /** |
| * This is a utility routine to retrieve the average audio frame size value |
| * from pjmedia_port_info. |
| * |
| * @param pia Pointer to port info containing audio format. |
| * @return Frame size in bytes. |
| */ |
| PJ_INLINE(unsigned) PJMEDIA_PIA_AVG_FSZ(const pjmedia_port_info *pia) |
| { |
| pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && |
| pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); |
| return PJMEDIA_AFD_AVG_FSZ(&pia->fmt.det.aud); |
| } |
| |
| /** |
| * Utility to retrieve audio frame size from maximum bitrate from |
| * pjmedia_port_info. |
| * |
| * @param pia Pointer to port info containing audio format. |
| * @return Frame size in bytes. |
| */ |
| PJ_INLINE(unsigned) PJMEDIA_PIA_MAX_FSZ(const pjmedia_port_info *pia) |
| { |
| pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && |
| pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); |
| return PJMEDIA_AFD_MAX_FSZ(&pia->fmt.det.aud); |
| } |
| |
| /** |
| * Port interface. |
| */ |
| typedef struct pjmedia_port |
| { |
| pjmedia_port_info info; /**< Port information. */ |
| |
| /** Port data can be used by the port creator to attach arbitrary |
| * value to be associated with the port. |
| */ |
| struct port_data { |
| void *pdata; /**< Pointer data. */ |
| long ldata; /**< Long data. */ |
| } port_data; |
| |
| /** |
| * Get clock source. |
| * This should only be called by #pjmedia_port_get_clock_src(). |
| */ |
| pjmedia_clock_src* (*get_clock_src)(struct pjmedia_port *this_port, |
| pjmedia_dir dir); |
| |
| /** |
| * Sink interface. |
| * This should only be called by #pjmedia_port_put_frame(). |
| */ |
| pj_status_t (*put_frame)(struct pjmedia_port *this_port, |
| pjmedia_frame *frame); |
| |
| /** |
| * Source interface. |
| * This should only be called by #pjmedia_port_get_frame(). |
| */ |
| pj_status_t (*get_frame)(struct pjmedia_port *this_port, |
| pjmedia_frame *frame); |
| |
| /** |
| * Called to destroy this port. |
| */ |
| pj_status_t (*on_destroy)(struct pjmedia_port *this_port); |
| |
| } pjmedia_port; |
| |
| |
| /** |
| * This is an auxiliary function to initialize port info for |
| * ports which deal with PCM audio. |
| * |
| * @param info The port info to be initialized. |
| * @param name Port name. |
| * @param signature Port signature. |
| * @param clock_rate Port's clock rate. |
| * @param channel_count Number of channels. |
| * @param bits_per_sample Bits per sample. |
| * @param samples_per_frame Number of samples per frame. |
| * |
| * @return PJ_SUCCESS on success. |
| */ |
| PJ_DECL(pj_status_t) pjmedia_port_info_init( pjmedia_port_info *info, |
| const pj_str_t *name, |
| unsigned signature, |
| unsigned clock_rate, |
| unsigned channel_count, |
| unsigned bits_per_sample, |
| unsigned samples_per_frame); |
| |
| /** |
| * This is an auxiliary function to initialize port info for |
| * ports which deal with PCM audio. |
| * |
| * @param info The port info to be initialized. |
| * @param name Port name. |
| * @param signature Port signature. |
| * @param dir Port's direction. |
| * @param fmt Port's media format. |
| * |
| * @return PJ_SUCCESS on success. |
| */ |
| PJ_DECL(pj_status_t) pjmedia_port_info_init2(pjmedia_port_info *info, |
| const pj_str_t *name, |
| unsigned signature, |
| pjmedia_dir dir, |
| const pjmedia_format *fmt); |
| |
| |
| /** |
| * Get a clock source from the port. |
| * |
| * @param port The media port. |
| * @param dir Media port's direction. |
| * |
| * @return The clock source or NULL if clock source is not present |
| * in the port. |
| */ |
| PJ_DECL(pjmedia_clock_src *) pjmedia_port_get_clock_src( pjmedia_port *port, |
| pjmedia_dir dir ); |
| |
| |
| /** |
| * Get a frame from the port (and subsequent downstream ports). |
| * |
| * @param port The media port. |
| * @param frame Frame to store samples. |
| * |
| * @return PJ_SUCCESS on success, or the appropriate error code. |
| */ |
| PJ_DECL(pj_status_t) pjmedia_port_get_frame( pjmedia_port *port, |
| pjmedia_frame *frame ); |
| |
| /** |
| * Put a frame to the port (and subsequent downstream ports). |
| * |
| * @param port The media port. |
| * @param frame Frame to the put to the port. |
| * |
| * @return PJ_SUCCESS on success, or the appropriate error code. |
| */ |
| PJ_DECL(pj_status_t) pjmedia_port_put_frame( pjmedia_port *port, |
| pjmedia_frame *frame ); |
| |
| /** |
| * Destroy port (and subsequent downstream ports) |
| * |
| * @param port The media port. |
| * |
| * @return PJ_SUCCESS on success, or the appropriate error code. |
| */ |
| PJ_DECL(pj_status_t) pjmedia_port_destroy( pjmedia_port *port ); |
| |
| |
| |
| PJ_END_DECL |
| |
| /** |
| * @} |
| */ |
| |
| #endif /* __PJMEDIA_PORT_H__ */ |
| |