pjmedia系列开篇

pjlib系列完结后,开始进入pjmedia系列。pjmedia是多媒体栈,可以把它按照抽象的分解大小分解为几个对象,下面通过simpleua.c示例代码解析这些对象和数据流。

从对象关系来看:

1、 pjmedia_endpt,代表一个媒体端点,端点可以理解为一个节点,可以是服务器或者客户端,一个设备一般只会有唯一一个端点,而且在初始化的时候创建。

2、pjmedia_session,代表一次会话,一个会话可以只有两个,也可以有多个参与者会议。

3、pjmedia_stream,代表一个流,在于一方进行通讯时,一般可以有音频流、视频流。

4、pjmedia_channel,只有一个方向的媒体流,也就是说,对应音频流,要发送通道和接收通道两个channel。

以上是范围从大到小的媒体对象,下面介绍其它对象。

5、pjmedia_transport,传输对象,封装了所有从网络接收数据和发送数据到网络的操作,可以是UDP、SRTP、ICE等传输方式。

6、pjmedia_snd_port,音频设备对象,最终的音频裸流都要在设备上播放,从麦克风采集,此对象封装所有设备操作。

从数据流来看:

pjmedia系列开篇_第1张图片

左边为最底层,音频设备的播放和采集,通过回调接口,调用stream.c生产或消化数据,最后通过transport传输接口进行发送接收。当然,其中还夹杂着前后处理、编解码、抖动缓冲区等,后面的章节会对各个对象及相关的音频处理进行描述。

最后来分析一下实例代码simpleua.c中对pjmedia的使用方法。

初始化

在main函数中,前半部分主要是对sip的初始化,对pjmedia的初始化大概从358行开始

1、创建媒体端点

static pjmedia_endpt	    *g_med_endpt;   /* Media endpoint.		*/

#if PJ_HAS_THREADS
    status = pjmedia_endpt_create(&cp.factory, NULL, 1, &g_med_endpt);
#else
    status = pjmedia_endpt_create(&cp.factory, 
				  pjsip_endpt_get_ioqueue(g_endpt), 
				  0, &g_med_endpt);
#endif

这里我们默认看有多线程的流程。

2、创建udp网络接口

static pjmedia_transport    *g_med_transport[MAX_MEDIA_CNT];
   
    /* 
     * Create media transport used to send/receive RTP/RTCP socket.
     * One media transport is needed for each call. Application may
     * opt to re-use the same media transport for subsequent calls.
     */
    for (i = 0; i < PJ_ARRAY_SIZE(g_med_transport); ++i) {
	status = pjmedia_transport_udp_create3(g_med_endpt, AF, NULL, NULL, 
					       RTP_PORT + i*2, 0, 
					       &g_med_transport[i]);

可以看出,虽然初始化还没有建立媒体通讯,但是预先创建了若干传输对象。

3、loop处理sip事件

    /* Loop until one call is completed */
    for (;!g_complete;) {
	pj_time_val timeout = {0, 10};
	pjsip_endpt_handle_events(g_endpt, &timeout);
    }

初始化的最后是一个循环等待处理sip对象的操作。

建立媒体通讯

当sip协商成功后,则要开始媒体通讯,发生在函数call_on_media_update

1、创建并启动流对象

static pjmedia_stream       *g_med_stream;  /* Call's audio stream.	*/
    
    /* Create stream info based on the media audio SDP. */
    status = pjmedia_stream_info_from_sdp(&stream_info, inv->dlg->pool,
					  g_med_endpt,
					  local_sdp, remote_sdp, 0);
    
    /* Create new audio media stream, passing the stream info, and also the
     * media socket that we created earlier.
     */
    status = pjmedia_stream_create(g_med_endpt, inv->dlg->pool, &stream_info,
				   g_med_transport[0], NULL, &g_med_stream);

    /* Start the audio stream */
    status = pjmedia_stream_start(g_med_stream);

这个实例并没有创建session,而是直接创建流对象。先从sip协商的sdp中获取媒体信息,然后根据这些信息创建流对象,并紧接着启动流。这里只有音频流,视频流暂不纳入分析范围。

2、启动网络传输

    /* Start the UDP media transport */
    pjmedia_transport_media_start(g_med_transport[0], 0, 0, 0, 0);

前面讲到,初始化的时候预创建了若干传输对象,但是并没有启动,等到协商成功后,才启动网络传输。

3、创建音频设备对象

static pjmedia_snd_port	    *g_snd_port;    /* Sound device.		*/

    /* Get the media port interface of the audio stream. 
     * Media port interface is basicly a struct containing get_frame() and
     * put_frame() function. With this media port interface, we can attach
     * the port interface to conference bridge, or directly to a sound
     * player/recorder device.
     */
    pjmedia_stream_get_port(g_med_stream, &media_port);

    /* Create sound port */
    pjmedia_snd_port_create(inv->pool,
                            PJMEDIA_AUD_DEFAULT_CAPTURE_DEV,
                            PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV,
                            PJMEDIA_PIA_SRATE(&media_port->info),/* clock rate	    */
                            PJMEDIA_PIA_CCNT(&media_port->info),/* channel count    */
                            PJMEDIA_PIA_SPF(&media_port->info), /* samples per frame*/
                            PJMEDIA_PIA_BITS(&media_port->info),/* bits per sample  */
                            0,
                            &g_snd_port);

    status = pjmedia_snd_port_connect(g_snd_port, media_port);

这里先从流对象取出媒体接口,其中的媒体接口包含了数据回调,这些后面分析数据流再重点讲,然后创建并启动音频设备对象。

结束销毁

    /* Destroy audio ports. Destroy the audio port first
     * before the stream since the audio port has threads
     * that get/put frames to the stream.
     */
    if (g_snd_port)
	pjmedia_snd_port_destroy(g_snd_port);


    /* Destroy streams */
    if (g_med_stream)
	pjmedia_stream_destroy(g_med_stream);

    /* Destroy media transports */
    for (i = 0; i < MAX_MEDIA_CNT; ++i) {
	if (g_med_transport[i])
	    pjmedia_transport_close(g_med_transport[i]);
    }

    /* Deinit pjmedia endpoint */
    if (g_med_endpt)
	pjmedia_endpt_destroy(g_med_endpt);

    /* Deinit pjsip endpoint */
    if (g_endpt)
	pjsip_endpt_destroy(g_endpt);

    /* Release pool */
    if (pool)
	pj_pool_release(pool);

当主线程退出loop时,则销毁所有创建的对象。

你可能感兴趣的:(pjproject)