百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

音视频开发经验:ijkplayer 实际开发中遇到的问题总结

liuian 2024-12-23 12:08 23 浏览

前言:ijkplayer 中一些问题记录优化

  • 在弱网时如何优化
  • ijkplayer 播放卡顿如何优化
  • 如何支持 https 链接播放?
  • 如何降低 ijkplayer 延迟效应
  • ijkplayer 中音视频同步,是如何做的?

一、在弱网时如何优化

好的网络下视音频能够得到及时的发送,不会造成视音频数据在本地的堆积,直播效果流畅,延时较小。而在弱网网络环境下,视音频数据发送不出去,则需要我们对视音频数据进行处理。差网络环境下对视音频数据一般有四种处理方式:缓存区设计、网络检测、丢帧处理、降码率处理。

1、缓冲区设计

视音频数据传入缓冲区,发送者从缓冲区获取数据进行发送,这样就形成了一个异步的生产者消费者模式。生产者只需要将采集、编码后的视音频数据推送到缓冲区,而消费者则负责从这个缓冲区里面取出数据发送。

视音频缓冲区

上图中只显示了视频帧,显然里面也有相应的音频帧。要构建异步的生产者消费者模式,java 中使用 LinkedBlockingQueue,可以对之后进行丢帧、插入、取出等处理。

2、网络检测

差网络处理过程中一个重要的过程是网络检测,当网络变差的时候能够快速地检测出来,然后进行相应的处理,这样对网络反应就比较灵敏,效果就会好很多。通过实时计算每秒输入缓冲区的数据和发送出去数据,如果发送出去的数据小于输入缓冲区的数据,那么说明网络带宽不行,这时候缓冲区的数据会持续增多,这时候就要启动相应的机制。

3、丢帧处理

当检测到网络变差的时候,丢帧是一个很好的应对机制。视频经过编码后有关键帧和非关键帧,关键帧也就是一副完整的图片,而非关键帧描述图像的相对变化。

丢帧策略多钟多样,可以自行定义,一个需要注意的地方是:如果要丢弃 P 帧(非关键帧),那么需要丢弃两个关键帧之间的所有非关键帧,不然的话会出现马赛克。对于丢帧策略的设计因需求而异,可以自行进行设计。如下我一个丢帧实例:

当 CPU 在处理视频帧的时候处理得太慢,默认的音视频同步方案是视频同步到音频, 导致了音频播放过快,视频跟不上。

在 ffffplayoptions.h 中,找到如下代码:

可以通过修改 framedrop 的值来解决不同步的问题,framedrop 是在视频帧处理不过来的时候丢弃一些帧达到同步的效果。具体设置,在上层 Java 层中 IjkVideoView 中:

默认 ijkplayer 中是 1,你可以自行,修改这个值。

4、降码率

在 Android 中,如果使用了硬编进行编码,在差网络环境下,我们可以实时改变硬编的码率,从而使直播更为流畅。当检测到网络环境较差的时候,在丢帧的同时,我们也可以降低视音频的码率。在 Android sdk 版本大于等于 19 的时候,可以通过传递参数给 MediaCodec,从而改变硬编编码器出来数据的码率。

Bundle bitrate = new Bundle();bitrate.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, bps * 1024);
mMediaCodec.setParameters(bitrate);

二、ijkplayer 播放卡顿如何优化

在做音频播放的时候,使用的是开源的 ijkplayer 播放器,ijkplayer 解码使用的是 ffmpeg,声音输出使用的是 audiotrack,在某机型上面播放遇到锁屏、返回后台、点击 home 键的时候会出现声音卡顿的现象,会输出下面的 log


W/AudioTrack: releaseBuffer() track 0xcce8b600 disabled due to previous underrun, restarting

我实现播放调用步骤是在 AsyncTask 中,查阅资料发现是因为在线程中播放造成的问题,经过查看 asynctask 构造方法发现,asynctask 会把线程的优先级设置为 THREADPRIORITYBACKGROUND 后台线程,于是我将线程的优先级设置为 THREADPRIORITYURGENT_AUDIO,解决了播放卡顿的问题,我猜测播放线程优先级降低,系统分配时间片会减少,会导致底层 ijk 读数据输出数据时得不到及时的回应,audiotrack 频繁的 releasebuffer,restarting 声道,造成卡顿。

三、如何支持 https 链接播放?

如果你的项目要进行加密播放 HLS 协议的视频,要想支持 https,须要在普通编译的基础上,进行一些配置。

接下来我们来编译 openssl

1.init openssl

$ cd ..    进入到ijkplayer的目下
$ ./init-android-openssl.sh   去远程仓库拉取openssl的远程代码,如果是iOS的,这里是init-ios-openssl.h

2.compile openssl

$ cd android/contrib
$ ./compile-openssl.sh clean
$ ./compile-openssl.sh all

经过以上步骤已经编译好 openssl 了,然后我们执行一下方法

$./compile-ffmpeg.sh clean
     编译ffmpeg软解码库,这个过程会生成各种架构的ffmpeg 这个过程比较耗时
$./compile-ffmpeg.sh all

相关视频推荐

ijkplayer播放器设计原理和实现|c/c++|音视频|流媒体_哔哩哔哩_bilibili

60万年薪音视频开发岗位需要掌握哪些技术点|音视频编码算法到底要掌握多少,FFmpeg命令行、API、源码分析、流媒体服务器RTMP/HLS/HTTP-FLV_哔哩哔哩_bilibili

学习地址:【免费】FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发-学习视频教程-腾讯课堂

需要更多ffmpeg/webrtc..音视频流媒体开发学习资料加群812855908领取

四、如何降低 ijkplayer 延迟效应

通过修改源文件,因为 ijkplayer 实际上是基于 ffplay.c 实现的:

ijkmedia>ijkplayer>ff_ffplay.c 这个文件

static double vp_duration(VideoState *is, Frame *vp, Frame *nextvp) {
    if(vp->serial == nextvp->serial) {
        doubleduration = nextvp->pts - vp->pts;
    if(isnan(duration) || duration <=0|| duration > is->max_frame_duration)
        return vp->duration;
      else
       return duration;
   }else{
    return 0.0;
  }
}

直接换成:

static double vp_duration(VideoState*is,Frame*vp,Frame*nextvp) {
     return vp->duration;
}

2、接着改 staticintffplayvideothread 这个方法:

static int ffplay_video_thread(void*arg){
    FFPlayer*ffp = arg;
    VideoState*is = ffp->is;
    AVFrame*frame =av_frame_alloc();
    doublepts;
    doubleduration;
    intret;
    AVRationaltb = is->video_st->time_base;
    //注释如下一行代码
    //AVRational frame_rate = av_guess_frame_rate(is->ic, is->video_st, NULL);

    //......省略部分代码

    //注释如下一行代码
    //duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational)  {frame_rate.den, frame_rate.num}) : 0);
    //直接这里写出
    duration=0.01;

   //........
}

改完后发现延迟明显降低,高分辨率开启硬解码,不支持的话会自动切换到软解,就算开启 mediacodec,如果设备不支持,显示的解码器也是 avcodec 软解。

五、ijkplayer 中音视频同步,是如何做的?

对于播放器来说,音视频同步是一个关键点,同时也是一个难点,同步效果的好坏,直接决定着播放器的质量。通常音视频同步的解决方案就是选择一个参考时钟(主 clock),播放时读取音视频帧上的时间戳,同时参考当前时钟参考时钟(主 clock)上的时间来安排播放。如下图所示:

  • 如果音视频帧的播放时间大于当前参考时钟上的时间,则不急于播放该帧,直到参考时钟达到该帧的时间戳;
  • 如果音视频帧的时间戳小于当前参考时钟上的时间,则需要“尽快”播放该帧或丢弃,以便播放进度追上参考时钟。

参考时钟的选择也有多种方式:

  • 选取视频时间戳作为参考时钟源
  • 选取音频时间戳作为参考时钟源
  • 选取外部时间作为参考时钟源

考虑人对视频、和音频的敏感度,在存在音频的情况下,优先选择音频作为主时钟源。

ijkplayer 在默认情况下也是使用音频作为参考时钟源,我们可以找到 ffffplay.c 文件中,处理同步的过程主要在视频渲染 videorefresh_thread 的线程中:

从上述实现可以看出,该方法中主要循环做两件事情:

  • 休眠等待,remainingtime 的计算在 videorefresh 中
  • 调用 video_refresh 方法,刷新视频帧

可见同步的重点是在 video_refresh 中,下面看该方法一些关键部分:

lastvp 是上一帧,vp 是当前帧,lastduration 则是根据当前帧和上一帧的 pts(Presentation Time Stamp。PTS 主要用于度量解码后的视频帧什么时候被显示出来),计算出来上一帧的显示时间,经过 computetarget_delay 方法,计算出显示当前帧需要等待的时间。

在 computetargetdelay 函数中,如果发现当前主 Clock 源不是 video,则计算当前视频时钟与主时钟的差值:

  • 如果当前视频帧落后于主时钟源,则需要减小下一帧画面的等待时间;
  • 如果视频帧超前,并且该帧的显示时间大于显示更新门槛,则显示下一帧的时间为超前的时间差加上上一帧的显示时间
  • 如果视频帧超前,并且上一帧的显示时间小于显示更新门槛,则采取加倍延时的策略。

回到 video_refresh 函数中,有如下逻辑:

frametimer 实际上就是上一帧的播放时间,而 frametimer + delay 实际上就是当前这一帧的播放时间,如果系统时间还没有到当前这一帧的播放时间,直接跳转至 display,而此时 is->forcerefresh 变量为 0,不显示当前帧,进入 videorefresh_thread 中下一次循环,并睡眠等待。

如果当前这一帧的播放时间已经过了,并且其和当前系统时间的差值超过了 AVSYNCTHRESHOLDMAX,则将当前这一帧的播放时间改为系统时间,并在后续判断是否需要丢帧,其目的是为后面帧的播放时间重新调整 frametimer,如果缓冲区中有更多的数据,并且当前的时间已经大于当前帧的持续显示时间,则丢弃当前帧,尝试显示下一帧。

否则进入正常显示当前帧的流程,调用 video_display2 开始渲染。

相关推荐

Springboot 整合 Websocket 轻松实现IM及时通讯

一、方案实践集成分为三步:添加依赖、增加配置类和消息核心类、前端集成。1.1、添加依赖<dependency><groupId>org.springframework...

SpringBoot扩展——应用Web Socket!

应用WebSocket目前,网络上的即时通信App有很多,如QQ、微信和飞书等,按照以往的技术来说,即时功能通常会采用服务器轮询和Comet技术来解决。HTTP是非持久化、单向的网络协议,在建立连接...

【Spring Boot】WebSocket 的 6 种集成方式

介绍由于前段时间我实现了一个库【SpringCloud】一个配置注解实现WebSocket集群方案以至于我对WebSocket的各种集成方式做了一些研究目前我所了解到的就是下面这些了(就一个破w...

SpringBoot生产级WebSocket集群实践,支持10万连接!

1、问题背景智慧门诊系统旨在从一定程度上解决患者面临的三长一短(挂号、看病、取药时间长,医生问诊时间短)的问题。实现“诊前、诊中、诊后”实时智能一体化,整合完善医院工作流程。围绕门诊看病的各个环节,让...

Spring Boot3 中 WebSocket 实现数据实时通信全解析

各位互联网大厂的开发同仁们,在如今的互联网应用开发中,实时通信功能越来越重要。比如在线聊天、数据推送、实时通知等场景,都离不开高效的实时通信技术。而WebSocket作为一种高效的双向通信协议,在...

Java WebSocket 示例(java nio websocket)

一、环境准备1.依赖配置(Maven)在pom.xml中添加WebSocket依赖:xml<!--SpringBootWebSocket--><dependen...

Spring Boot整合WebSocket:开启实时通信之旅

SpringBoot整合WebSocket:开启实时通信之旅今天咱们来聊聊SpringBoot整合WebSocket这件大事儿。说到实时通信,你是不是第一时间想到QQ、微信这些聊天工具?没错,We...

Spring Boot3 竟能如此轻松整合 WebSocket 技术,你还不知道?

在当今互联网大厂的软件开发领域,实时通信的需求愈发迫切。无论是在线聊天应用、实时数据更新,还是协同办公系统,都离不开高效的实时通信技术支持。而WebSocket作为一种能够实现浏览器与服务器之间持...

Spring Boot集成WebSocket(springboot集成websocket)

一、基础配置依赖引入<dependency><groupId>org.springframework.boot</groupId><artifactId>...

Springboot下的WebSocket开发(springboot websocket server)

今天遇到一个需求,需要对接第三方扫码跳转。一种方案是前端页面轮询后端服务,但是这种空轮询会虚耗资源,实时性比较差而且也不优雅。所以决定使用另一种方案,websocket。以前就知道websocket,...

springboot websocket开发(java spring boot websocket)

maven依赖SpringBoot2.0对WebSocket的支持简直太棒了,直接就有包可以引入<dependency><groupId>org....

Python界面(GUI)编程PyQt5窗体小部件

一、简介在Qt(和大多数用户界面)中,“小部件”是用户可以与之交互的UI组件的名称。用户界面由布置在窗口内的多个小部件组成。Qt带有大量可用的小部件,也允许您创建自己的自定义和自定义小部件。二、小部件...

实战PyQt5: 014-下拉列表框控件QComboBox

QComboBox简介QComboBox下拉列表框,是一个集按钮和下拉列表选项于一体的部件。QComboBox提供了一种向用户呈现选项列表的方式,其占用最小量的屏幕空间。QComboBox中的常用方法...

Python小白逆袭!7天吃透PyQt6,独立开发超酷桌面应用

PythonGUI编程:PyQt6从入门到实战的全面指南在Python的庞大生态系统中,PyQt6作为一款强大的GUI(GraphicalUserInterface,图形用户界面)编程框架,为开...

如何用 PyQt6 打造一个功能完善的 SQLite 数据库管理工具

如何使用PyQt6和qt_material库,打造一个功能完善的SQLite数据库管理工具,轻松管理和查询SQLite数据库。一、目标数据库连接与表管理:支持连接SQLite数据库...