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部(介绍接口、视频、音频的操作)相关推荐

  1. airplay协议开发第3部(mdnsd注册airplay服务)

    1 服务注册 Airplay通过mdnsd对外发布2个服务,在发布服务的时候,说明自己支持的功能.例如,在发布 raop服务的时候,会说明传输音频是使用UDP还是TCP. 那么,手机在发现raop服务 ...

  2. 微信小程序会员卡开发(开发效果示例图+详细介绍+接口说明)

    说实话呢,这次微信会员卡的开发文档是真的乱,四分五裂,我也是东凑西拿的才完成会员卡的开发,把开发经验和踩坑给大家说一下,希望对大家有所帮助 开发出来是这个效果如下图: JAVA微信会员卡接口踩坑详细 ...

  3. 接口测试,接口协议以及常用接口测试工具介绍

    一.前言:什么是接口 1.接口指的是软件提供给外界的一种服务.作用在于使其内部的数据能被外部进行修改的交互方式. 例如:USB接口,电脑里的数据可以看成是内容数据,而U盘里的数据可以看成是外部数据,那 ...

  4. 接口测试、接口协议以及常用接口测试工具介绍

    目录 一.前言:什么是接口 二.接口协议以及对应的接口测试工具 三.接口测试如何设计测试用例? 四.接口组成? 五.总结 一.前言:什么是接口 1.接口指的是软件提供给外界的一种服务.作用在于使其内部 ...

  5. Linux驱动学习--HDMI开发(一) 相关协议及传输原理的介绍

    目录 一.引言 二.HDMI硬件接口及相关协议 -------> TMDS(转换最小化差分信令) -------> CEC(消费电子控制) -------> DDC(显示数据通道) ...

  6. GB28181协议开发介绍

    GB28181协议简介 GB/T28181-2011 <安全防范视频监控联网系统信息传输.交换.控制技术要求>是由公安部科技信息化局提出,由全国安全防范报警系统标准化技术委员会(SAC/T ...

  7. 使用loadrunner javavuser协议开发脚本实战

    众所周知,Java语言是一门非常适合网络开发的语言,用Java语言进行性能测试脚本开发将会大大提高测试开发效率.LoadRunner中提供了对Java虚拟用户的支持.Java虚拟用户依托于JDK,这大 ...

  8. 微信开放平台 公众号第三方平台开发 教程一 平台介绍

    教程导航: 微信开放平台 公众号第三方平台开发 教程一 平台介绍 微信开放平台 公众号第三方平台开发 教程二 创建公众号第三方平台 微信开放平台 公众号第三方平台开发 教程三 一键登录授权给第三方平台 ...

  9. USB1.1 协议开发

    USB1.1 开发 ** 1 USB1.1协议概述 USB(Universal Serial Bus)是一种总线的接口形式,协议 1.1 版本由 Microsoft.Intel.IBM 等 7 家公司 ...

最新文章

  1. java网络编程(四)
  2. php 安装rabbitmq拓展_安装 php-rabbit: RabbitMQ 的 PHP 扩展
  3. 【转】Asp.net MVC Comet推送
  4. LeetCode MySQL 1174. 即时食物配送 II
  5. 顺便抹了下眼眶的飞鸽传书官方网站
  6. 获取和保存当前屏幕的截图 实现的C++代码如下
  7. 为什么 Linux 默认页大小是 4KB
  8. 以下构成python循环结构的方法中正确的是_python教程:python循环结构
  9. java静态方法求n,java之静态属性和静态方法
  10. 跨界营销策划案例合集(共13份)
  11. HTML5详细介绍及使用
  12. cgcs2000大地坐标系地图_MapGIS国土空间数据2000大地坐标系转换系统
  13. 台式机linux系统无线上网,CentOS 6.5 安装无线网卡驱动实现无线上网
  14. 视频教程-Kali Linux渗透测试全程课与脚本语言编程系列课程-渗透测试
  15. The bean ‘XXX‘ could not be injected because it is a JDK dynamic proxy
  16. 计算机微程序控制器实验报告,计算机组成原理实验报告三:微程序控制器实验.doc...
  17. kali使用笔记本自带无线网卡_不会选无线网卡看过来!再也不花冤枉钱了.........
  18. springboot影院售票小程序毕业设计源码111154
  19. bash: vagrant_重新引入Vagrant:从PHP开始的正确方法
  20. 基础知识【H5新增知识1】

热门文章

  1. 基于阿里云函数计算实现需要用到超大依赖包的 Python 无服务器计算
  2. Ops School Curriculum:巨细靡遗的运维小百科
  3. 张小龙-年薪近三亿的微信之父
  4. Android兼容性优化-Android 8.0设置Activity透明主题崩溃
  5. windows下用Python把png图片转化为pdf文件
  6. uc/OS-II操作系统:uc/OS中的任务_下(如不懂——>请收下我的膝盖)
  7. 系统安装部署系列教程(五):精简母盘和集成补丁
  8. DT CQT测试软件不包括哪个状态,DT-CQT测试方法.ppt
  9. echarts柱形图x轴y轴互换_Echarts Y轴底纹改为虚线与设置柱状图为圆角
  10. 无车承运(网络货运)系统一