airplay协议开发第2部(介绍接口、视频、音频的操作)
1 AirplayLibrary项目提供的接口
AirplayLibrary项目编译出Airplay.dll动态库,对外提供的接口函数如下:
//========================================
//启动 airplay 服务;
//friendname -- airplay服务的名称;
//width -- 显示设备的宽度;
//height -- 显示设备的高度;
//cb -- airplay 回调操作的集合;
//========================================
int XinDawn_StartMediaServer(char *friendname, int width, int height, airplay_callbacks_t *cb);
//========================================
//停止 airplay 服务;
//========================================
void XinDawn_StopMediaServer();
所以,AirplaySdkExample项目引用了Airplay.dll动态库之后,只需要调用
XinDawn_StartMediaServer()函数就可以启动airplay服务器,那么,airplay接收到音视频数据的时候,就调用airplay_callbacks_t *cb参数配置的回调函数进行操作。
那么,在Airplay.dll中的airplay服务,就可以调用当前AirplaySdkExample项目定义的函数。例如airplay_callbacks_t回调函数的定义如下:
//========================================
//airplay 的回调函数集合;
//========================================
struct airplay_callbacks_s
{
void *cls;
/* Compulsory callback functions */
void(*AirPlayPlayback_Open) (void *cls, char *url, float fPosition);
void(*AirPlayPlayback_Play) (void *cls);
void(*AirPlayPlayback_Pause)(void *cls);
void(*AirPlayPlayback_Stop) (void *cls);
void(*AirPlayPlayback_Seek)(void *cls, long fPosition);
void(*AirPlayPlayback_SetVolume)(void *cls, int volume);
void(*AirPlayPlayback_ShowPhoto)(void *cls, unsigned char *data, long long size);
long(*AirPlayPlayback_GetDuration)(void *cls);
long(*AirPlayPlayback_GetPostion)(void *cls);
int(*AirPlayPlayback_IsPlaying)(void *cls);
int(*AirPlayPlayback_IsPaused)(void *cls);
void(*AirPlayAudio_Init)(void *cls, int bits, int channels, int samplerate, int isaudio);
void(*AirPlayAudio_Process)(void *cls, const void *buffer, int buflen, double timestamp, uint32_t seqnum);
void(*AirPlayAudio_destroy)(void *cls);
void(*AirPlayAudio_SetVolume)(void *cls, int volume);//1-100
void(*AirPlayAudio_SetMetadata) (void *cls, const void *buffer, int buflen);
void(*AirPlayAudio_SetCoverart)(void *cls, const void *buffer, int buflen);
void(*AirPlayAudio_Flush)(void *cls);
void(*AirPlayMirroring_Play)(void *cls, int width, int height, const void *buffer, int buflen, int payloadtype, double timestamp);
void(*AirPlayMirroring_Process)(void *cls, const void *buffer, int buflen, int payloadtype, double timestamp);
void(*AirPlayMirroring_Stop)(void *cls);
};
那么,在 start_airplay()函数中,启动airplay服务的时候,有配置:
ao.AirPlayMirroring_Process = AirPlayOutputFunctions::mirroring_process;
然后,进入XinDawn_StartMediaServer()函数,有:
raop_cbs.mirroring_ process = cb->AirPlayMirroring_Play;
...
raop = raop_init(10, &raop_cbs, g_pem_key, NULL);
进入raop_init()函数,有:
memcpy(&raop->callbacks, callbacks, sizeof(raop_callbacks_t));
此时,把raop_cbs对象配置的所有回调函数,都拷贝到raop->callbacks中。其中,raop就是负责音视频数据接收的对象。
当airplay模块接收到镜像数据的时候,调用的操作如下:
airplay->callbacks.mirroring_play(airplay->callbacks.cls, 0, 0, p_buffer, d_size+1, d_type, 0.0);
此时,通过函数指针mirroring_ process,调用了AirPlayOutputFunctions::mirroring_process()函数。
最终,在AirplaySdkExample项目中,可以定义AirPlayOutputFunctions::mirroring_process()函数接收airplay的镜像视频数据,就可以通过ffmpeg和SDL进行视频帧的解码显示。
2 播放视频
Airplay.dll中采集到的airplay音视频数据,都存放到一个数据队列中,如下:
typedef struct __xdw_air_decoder_q
{
//存放视频帧的队列;
struct xdw_q_head video_pkt_q;
//存放音频帧的队列;
struct xdw_q_head audio_pkt_q;
} xdw_air_decoder_q;
那么,在Airplay.dll采集到视频数据,存放到 video_pkt_q 队列中,AirplaySdkExample项目中的video_thread_loop()函数就从对应的 video_pkt_q 队列中取出视频帧,然后,使用SDL框架显示该视频帧。
视频镜像的接口函数如下:
(1) 在start_airplay()函数中操作如下:
//镜像的操作;
ao.AirPlayMirroring_Play = AirPlayOutputFunctions::mirroring_play;
ao.AirPlayMirroring_Process = AirPlayOutputFunctions::mirroring_process;
ao.AirPlayMirroring_Stop = AirPlayOutputFunctions::mirroring_stop;
最终,在XinDawn_StartMediaServer()函数中,把这些回调函数,注册给raop对象。
那么,当接收视频帧的时候,调用:
airplay->callbacks.mirroring_process(airplay->callbacks.cls, p_buffer, d_size, d_type, 0.0);
此时,调用AirPlayOutputFunctions::mirroring_process()函数处理。
在该函数中,把数据封装成 H264 数据元,存放到视频队列中。最终, 由 video_thread_loop() 线程进行处理。
3 音频播放
在VideoSource::AirPlayOutputFunctions::audio_init()函数中,创建SDL播放音频的对象:
wanted_spec.callback = AirPlayOutputFunctions::sdl_audio_callback;
而且,这个函数,在 start_airplay()函数中配置:
ao.AirPlayAudio_Init = AirPlayOutputFunctions::audio_init;
最终,在XinDawn_StartMediaServer()函数中配置:
raop_cbs.audio_init = cb->AirPlayAudio_Init;
那么,当 airplay 获取一个音频需要处理的时候,就调用raop_cbs.audio_init()回调函数,初始化一个音频对象。
其中 sdl_audio_callback() 函数是由 SDL 播放音频的回调函数,该函数的逻辑是:
(1) 从 音频队列中获取音频数据;
(2) 使用SDL框架播放音频数据;
那么,airplay获取到一个音频数据帧的时候,操作是:
void VideoSource::AirPlayOutputFunctions::audio_process(void *cls, const void *buffer, int buflen, double timestamp, uint32_t seqnum)
{
...
xdw_q_push(&frm_node->list, &(((VideoSource *)cls)->xdw_decoder_q.audio_pkt_q));
}
}
此时,把音频数据帧存放到xdw_decoder_q.audio_pkt_q音频队列中。
那么,SDL的回调函数sdl_audio_callback(),就可以从音频队列中获取音频数据来处理。
现在分析了前端注册airplay回调函数的操作,和回调函数调用的流程。下面就开始分析airplay协议的服务注册和协议的交互。
更多交流可以QQ 1523520001,备注 airplay
airplay协议开发第2部(介绍接口、视频、音频的操作)相关推荐
- airplay协议开发第3部(mdnsd注册airplay服务)
1 服务注册 Airplay通过mdnsd对外发布2个服务,在发布服务的时候,说明自己支持的功能.例如,在发布 raop服务的时候,会说明传输音频是使用UDP还是TCP. 那么,手机在发现raop服务 ...
- 微信小程序会员卡开发(开发效果示例图+详细介绍+接口说明)
说实话呢,这次微信会员卡的开发文档是真的乱,四分五裂,我也是东凑西拿的才完成会员卡的开发,把开发经验和踩坑给大家说一下,希望对大家有所帮助 开发出来是这个效果如下图: JAVA微信会员卡接口踩坑详细 ...
- 接口测试,接口协议以及常用接口测试工具介绍
一.前言:什么是接口 1.接口指的是软件提供给外界的一种服务.作用在于使其内部的数据能被外部进行修改的交互方式. 例如:USB接口,电脑里的数据可以看成是内容数据,而U盘里的数据可以看成是外部数据,那 ...
- 接口测试、接口协议以及常用接口测试工具介绍
目录 一.前言:什么是接口 二.接口协议以及对应的接口测试工具 三.接口测试如何设计测试用例? 四.接口组成? 五.总结 一.前言:什么是接口 1.接口指的是软件提供给外界的一种服务.作用在于使其内部 ...
- Linux驱动学习--HDMI开发(一) 相关协议及传输原理的介绍
目录 一.引言 二.HDMI硬件接口及相关协议 -------> TMDS(转换最小化差分信令) -------> CEC(消费电子控制) -------> DDC(显示数据通道) ...
- GB28181协议开发介绍
GB28181协议简介 GB/T28181-2011 <安全防范视频监控联网系统信息传输.交换.控制技术要求>是由公安部科技信息化局提出,由全国安全防范报警系统标准化技术委员会(SAC/T ...
- 使用loadrunner javavuser协议开发脚本实战
众所周知,Java语言是一门非常适合网络开发的语言,用Java语言进行性能测试脚本开发将会大大提高测试开发效率.LoadRunner中提供了对Java虚拟用户的支持.Java虚拟用户依托于JDK,这大 ...
- 微信开放平台 公众号第三方平台开发 教程一 平台介绍
教程导航: 微信开放平台 公众号第三方平台开发 教程一 平台介绍 微信开放平台 公众号第三方平台开发 教程二 创建公众号第三方平台 微信开放平台 公众号第三方平台开发 教程三 一键登录授权给第三方平台 ...
- USB1.1 协议开发
USB1.1 开发 ** 1 USB1.1协议概述 USB(Universal Serial Bus)是一种总线的接口形式,协议 1.1 版本由 Microsoft.Intel.IBM 等 7 家公司 ...
最新文章
- java网络编程(四)
- php 安装rabbitmq拓展_安装 php-rabbit: RabbitMQ 的 PHP 扩展
- 【转】Asp.net MVC Comet推送
- LeetCode MySQL 1174. 即时食物配送 II
- 顺便抹了下眼眶的飞鸽传书官方网站
- 获取和保存当前屏幕的截图 实现的C++代码如下
- 为什么 Linux 默认页大小是 4KB
- 以下构成python循环结构的方法中正确的是_python教程:python循环结构
- java静态方法求n,java之静态属性和静态方法
- 跨界营销策划案例合集(共13份)
- HTML5详细介绍及使用
- cgcs2000大地坐标系地图_MapGIS国土空间数据2000大地坐标系转换系统
- 台式机linux系统无线上网,CentOS 6.5 安装无线网卡驱动实现无线上网
- 视频教程-Kali Linux渗透测试全程课与脚本语言编程系列课程-渗透测试
- The bean ‘XXX‘ could not be injected because it is a JDK dynamic proxy
- 计算机微程序控制器实验报告,计算机组成原理实验报告三:微程序控制器实验.doc...
- kali使用笔记本自带无线网卡_不会选无线网卡看过来!再也不花冤枉钱了.........
- springboot影院售票小程序毕业设计源码111154
- bash: vagrant_重新引入Vagrant:从PHP开始的正确方法
- 基础知识【H5新增知识1】
热门文章
- 基于阿里云函数计算实现需要用到超大依赖包的 Python 无服务器计算
- Ops School Curriculum:巨细靡遗的运维小百科
- 张小龙-年薪近三亿的微信之父
- Android兼容性优化-Android 8.0设置Activity透明主题崩溃
- windows下用Python把png图片转化为pdf文件
- uc/OS-II操作系统:uc/OS中的任务_下(如不懂——>请收下我的膝盖)
- 系统安装部署系列教程(五):精简母盘和集成补丁
- DT CQT测试软件不包括哪个状态,DT-CQT测试方法.ppt
- echarts柱形图x轴y轴互换_Echarts Y轴底纹改为虚线与设置柱状图为圆角
- 无车承运(网络货运)系统一