airplay协议开发第2部(介绍接口、视频、音频的操作)
发布日期:2021-07-01 04:36:36 浏览次数:3 分类:技术文章

本文共 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

转载地址:https://mylinux.blog.csdn.net/article/details/88973219 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:airplay协议开发第3部(mdnsd注册airplay服务)
下一篇:airplay协议开发第1部(搭建测试环境)

发表评论

最新留言

表示我来过!
[***.240.166.169]2024年04月19日 18时53分48秒