博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
airplay协议开发第2部(介绍接口、视频、音频的操作)
阅读量:2013 次
发布时间:2019-04-28

本文共 4791 字,大约阅读时间需要 15 分钟。

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

转载地址:http://ucfxf.baihongyu.com/

你可能感兴趣的文章
Build update.zip from your own android source code
查看>>
Android Build系统分析 一
查看>>
[Android]ListView美化:去阴影、底色、选中色
查看>>
巧妙运用ViewStub写出类似Tab选项卡(想怎么写tab就怎么写,横着写竖着写随你)
查看>>
在EditText中插出入图片
查看>>
android常用代码片段
查看>>
使用C++实现SDK之WebBrowser容器
查看>>
Android Out of Memory (OOM)
查看>>
Redis数据结构探究
查看>>
C++代码的组织
查看>>
内存泄露的排查
查看>>
软件开发:依赖关系的三条原则
查看>>
重构和增加功能
查看>>
远程调试(转)
查看>>
STL—vector删除重复元素
查看>>
lsof用法
查看>>
Hadoop C++ Pipes中context常见成员函数的作用
查看>>
Hadoop Streaming 实战: 文件分发与打包
查看>>
Spring4+quartz2集群借助邮箱或是短信实现生日的农历提醒(Quartz实现农历、阴历、公历生日提醒)...
查看>>
防止页面后退(使浏览器后退按钮失效)
查看>>