前言:
最近在进行一个PC对PC端的直播功能的研发,主要需要实现从PC端捕获桌面处理成H264视频流,以及采集PC端的扬声器的声音处理成AAC音频流,通过RTMP推流到服务器端中,然后客户端可以从服务器中拉流并播放视频,最终实现直播的功能。该项目主要运用到FFMPEG实现音视频的编解码和拉流,使用SDL进行对音视频的播放。
在实现该功能的过程中,总共可以分为如下几个模块:
1、音频的采集、编码;
2、桌面捕获和图像编码;
3、音视频的同步及推流(核心);
4、音视频的拉流及解码;
5、音视频同步播放(核心);
其中音视频编解码是项目的基础,音视频的同步是项目的核心,在后续的文章中将分章节讲述该项目实现的过程。
本章节将讲解下对于拉流播放器同步播放的思路:
本项目的进行音视频同步播放的策略与第三节的策略是有联系的,也是以系统时间为基准时间,这与第三节的同步推流的思路对应起来了。
(1)音视频同步的本质是:音频数据和视频数据在播放时相互追赶或者等待对方的过程,谁慢了就去追赶前方的,谁快了就等待后方的,直至慢慢的双方保持一个趋于稳定的状态。
(2)本项目的具体做法思路:因为屏幕广播要求的是流畅性和实时性比较高,为了避免卡顿的情况本项目在音视频同步工作中采用的仅仅是谁慢了就去追赶谁,直至双方的差距保持在一个阈值之内。
如下为音频追赶视频的代码部分:
(1)在下面代码中本项目主要是设置了一个阈值300ms,当音频数据的pts落后于视频数据的pts大于300ms时,音频数据进行丢帧处理,以此来追赶视频数据,达到同步的目的。
(2)由于音频数据如果一下丢帧过多会导致有严重卡顿或者杂音会影响用户的体验,因此在本项目中的音频数据丢帧时是每10帧丢一帧,其余帧正常播放。
else if (audio_pts_tmp + 300 < vedio_pts_tmp)//音频落后需要丢帧追赶视频
{
num_release = 300 / 20;//大概判断需要丢多少帧
num_release = (num_release > ((list_rtmp_data_ptr)list_head)->count_node ? ((list_rtmp_data_ptr)list_head)->count_node : num_release);
xhlog("audio throw num_release:%d\n", num_release);
for (i = 0; i < num_release; i++)
{
list_ptr = list_get_rtmp_data((list_rtmp_data_ptr)list_head);
if (list_ptr == NULL)
{
xhlog("list_get_rtmp_data list_ptr is NULL\n");
break;
}
if (i % 10 == 0)//每5帧丢一帧
{
}
else
{
audio_pts_tmp = list_ptr->pts;
SDL_START_PLAYER((LPVOID)(list_ptr->packet.data));
}
//av_packet_unref(&(list_ptr->packet));
//free(list_ptr);
//开启线程用来播放数据和处理数据
//hThread_release_audio_data = CreateThread(NULL, NULL, pthread_release_audio_data, list_ptr, 0, &dwThreadId_release_audio_data);//释放资源
pthread_release_audio_data(list_ptr);
}
}
如下部分为视频追赶音频的代码部分:
if (vedio_pts_tmp + 40 < audio_pts_tmp)//追赶
{
xhlog("vedio_pts_tmp + 40 < audio_pts_tmp1, %p %p v_pts:%ld, a_pts:%ld", list_ptr, list_head, vedio_pts_tmp, audio_pts_tmp);
//WLog(LOG_DEBUG, "vedio_pts_tmp + 40 < audio_pts_tmp1, %p %p v_pts:%ld, a_pts:%ld", list_ptr, list_head, vedio_pts_tmp, audio_pts_tmp);
listnode_release_rtmp_data(list_ptr);//丢帧
xhlog("vedio_pts_tmp + 40 < audio_pts_tmp2");
//WLog(LOG_DEBUG, "vedio_pts_tmp + 40 < audio_pts_tmp2");
//开启线程用来播放数据和处理数据
//hThread_release_vedio_data = CreateThread(NULL, NULL, listnode_release_rtmp_data, list_ptr, 0, &dwThreadId_release_vedio_data);//释放资源
continue;
}
总结:音视频同步的本质其实就是使音频和视频播放时PTS值达到相互协调在一个合理的阈值区间范围,方法策略有很多种,本文仅仅提供了实际项目使用到的追赶策略以供参考,如若读者还有其他更好的策略希望能私信或者在评论区与我交流学习。