媒体端口(pjmedia_port)框架

本文介绍PJMEDIA媒体端口框架的基本概念及应用实例,包括媒体端口的分类、属性及其如何通过get_frame()和put_frame()回调函数实现媒体帧的发送与接收。此外,还详细介绍了媒体端口之间的互联机制,以及一个使用该框架实现音频采样率转换的示例程序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

媒体端口(pjmedia_port)框架~~PJMEDIA的基础概念


媒体端口(也可以称作pjmedia_port“类”)提供一个通用和可扩展实现媒体要素的框架。媒体要素分为:源要素、目标要素、处理要素。媒体端口界面提供下列基础属性:

    -媒体端口信息(pjmedia_port_info)####用于描述采样率、通道数量等媒体属性

    -可选项(媒体端口如果只上传流,则无此项)回调函数get_frame()被pjmedia_port_get_frame()函数调用,从端口获得多帧数据。

    -可选项(媒体端口如果只下载流,则无此项)回调函数put_frame()被pjmedia_port_put_frame()函数调用,从端口推送多帧数据。

    只要实现get_frame()put_frame()回调内容,即可引导接口实现各媒体端口(源、目标、处理)发出/取得的媒体帧。

    媒体端口是被动对象。缺省的媒体流程运行不需要工作线程的支持。应用程序或PJMEDIA媒体组件,想从媒体端口取得/推送数据,必须不断地调用pjmedia_port_get_frame()或pjmedia_port_put_frame()函数。

    一些媒体端口,例如PJMEDIA_CONF(会议)和PJMEDIA_RESAMPLE_PORT(更换采样率端口),要封装为与其它端口互联,以调用其它端口的协作任务,直至其最终源/目标端口终止媒体。端口互联意味着:上传流媒体端口要先调用get_frame()取帧数据,并调用put_frame()给它的下载端口传帧数据。上述过程的前提条件: 上下行的媒体端口必须具有相同的格式。格式是采样格式、时钟速率、通道个数、每次采样比特数、每帧采样数等等音频媒体相关参数的组合。

 

    示例程序port_clock_ex1,用于手工处理重采样。此例子中,假设应用程序将要读取一个WAV文件数据,转换采样率后写入其它WAV文件,下图展示了应用程序创建和安置多个媒体端口的方式


应用程序将使用下列伪码设置多个媒体端口:

 

     pjmedia_port *player, *resample, *writer;

     pj_status_t status;

 

     //创建文件播放端口

     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);

 

     //player端口为源要素,使用设定的目标采样速率创建重采样端口,建立resample端口与player端口的有效连接。

     status = pjmedia_resample_port_create( pool, player, 8000,0, &resample);

     PJ_ASSERT_RETURN(status==PJ_SUCCESS, PJ_SUCCESS);

 

     //resample端口设置创建文件写入WAV参数。

     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);

 

   全部端口设置完成后,应用程序在循环中调用转换处理:

 

   pj_int16_t samplebuf[MAX_FRAME];

  

   while (1) {

       pjmedia_frame frame;

       pj_status_t status;

 

       frame.buf = samplebuf;

       frame.size = sizeof(samplebuf);

 

       //resample端口取帧数据

       status = pjmedia_port_get_frame(resample,&frame);

       if (status != PJ_SUCCESS || frame.type ==PJMEDIA_FRAME_TYPE_NONE) {

           //读到文件结束时,停止转换

           break;

       }

 

       //推帧数据到writer端口

       status = pjmedia_port_put_frame(writer,&frame);

       if (status != PJ_SUCCESS) {

           //写文件错误处理

           break;

       }

   }

 

    出于完整性的考虑,重采样处理完成后,应用程序将要销毁创建的全部端口:

 

    //注意:resample端口被销毁时,也会缺省地销毁给它推帧数据的端口(player端口)

   pjmedia_port_destroy(resample);

   pjmedia_port_destroy(writer);

 

    对于这个简单的WAV文件采样频率转换的例子,以上步骤就足够了。但是其他更为复杂的目的,读和写语音帧的过程需要以定时的方式进行,例如发送RTP包到一个远程的流对象。更进一步,随着应用程序规模的逐渐增大,手动读写语音帧的操作越来越频繁,如果PJMEDIA提供自动处理这个过程的机制,这样会更好些。其实,PJMEDIA已经提供了使媒体流在媒体端口之间自行流动的机制,在 PJMEDIA_PORT_CLOCK 节中予以阐明。

 


### pjsip 中自定义视频流的实现 在pjsip中处理自定义视频流涉及多个组件之间的协作,特别是`pjmedia_port`接口的应用。为了实现在pjsip内传输定制化的视频数据,需构建一个能够与现有框架无缝集成的新模块。 #### 创建自定义视频端口 基于`pjmedia_port`结构体设计新的类或函数集,用于表示特定于应用程序逻辑的视频输入/输出节点。此过程包括但不限于重写`put_frame()`和`get_frame()`成员函数,以便它们可以按照预期方式操作图像帧缓冲区[^3]。 ```c typedef struct custom_video_port { pjmedia_port base; } custom_video_port; static pj_status_t put_custom_frame(pjmedia_port *port, pjmedia_frame *frame) { /* 用户应在此处编写代码以处理传入的数据 */ } static pj_status_t get_custom_frame(pjmedia_port *port, pjmedia_frame *frame) { /* 用户应在此处编写代码以准备传出的数据 */ } ``` #### 集成到会议桥 一旦有了上述自定义端口,则可通过将其加入至会议桥梁实例的方式使其参与到实际通信环节当中去。这一步骤通常涉及到调用诸如`pjmedia_conf_add_port()`之类的API来建立连接关系,并指定新创建的对象作为参数之一传递给这些方法。 ```c /* 假设已有一个有效的会议配置对象 'conf' 和初始化好的自定义视频端口 'cvp' */ status = pjmedia_conf_add_port(conf->conf, &cvp.base); if (status != PJ_SUCCESS){ // 错误处理... } ``` #### 使用专用线程管理 考虑到实时性和性能因素,在执行像获取摄像头画面这样的耗时任务时建议采用独立的工作进程来进行异步处理。此时应当利用pjsip提供的线程库——即通过`pj_thread_create()`函数启动专门的任务调度者,从而确保整个系统的稳定运行以及高效的资源利用率[^1]。 ```c void worker_proc_video(void *arg) { while (!shutdown_flag_set()) { // 调用 video device API 获取最新一帧的画面信息, // 并经由先前设置好的路径转发出去。 status = pjmedia_vid_dev_get_frame(...); if (status == PJ_SUCCESS) send_to_rtp_stream(...); sleep_for_next_cycle(); } } // 启动工作线程前先准备好必要的环境变量和其他依赖项... pj_thread_desc desc; pj_thread_t *thread; pj_thread_create(pool, "video_worker", worker_proc_video, arg, priority, tattr, &desc, &thread); ``` 以上便是针对pjsip环境下开发自定义视频流解决方案的大致步骤概述。值得注意的是具体细节可能会依据项目需求和个人偏好有所不同,因此开发者还需参照官方文档深入研究各个部分的具体实现机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值