Android平台RTMP|RTSP直播播放器功能进阶探讨

简介: 很多开发者在跟我聊天的时候,经常问我,为什么一个RTMP或RTSP播放器,你们需要设计那么多的接口,真的有必要吗?带着这样的疑惑,我们今天聊聊Android平台RTMP、RTSP播放器常规功能,如软硬解码设置、实时音量调节、实时快照、实时录像、视频view翻转和旋转、画面填充模式设定、解码后YUV、RGB数据回调等:

我们需要怎样的直播播放器?

很多开发者在跟我聊天的时候,经常问我,为什么一个RTMP或RTSP播放器,你们需要设计那么多的接口,真的有必要吗?带着这样的疑惑,我们今天聊聊Android平台RTMP、RTSP播放器常规功能,如软硬解码设置、实时音量调节、实时快照、实时录像、视频view翻转和旋转、画面填充模式设定、解码后YUV、RGB数据回调等:

fa93784e8eb64e16b9ed4bb6e0bb32cd.jpg

延迟延迟延迟

RTMP或RTSP直播播放器,特别是在一些交互场景下,对延迟要求近乎苛刻,所以,毫秒级的延迟,是一个直播播放器聊下去的基础,200-400ms的整体时延,是大家比较期望的,是的,你没看错,RTMP的也可以做到极低延迟。

软硬解码设置

Android平台特别是超过1080p的分辨率的流数据,如果设备性能一般,软解效率不一定高,这时候建议硬解码,硬解码我们分两种,一种是设置surface模式的硬解码,这种无法快照和回调yuv、rgb数据,另一种就是常规硬解,直接出原始解码后数据的,区分两种硬解码模式,我们设计了如下的接口:

  /**
   * 设置视频硬解码下Mediacodec自行绘制模式(此种模式下,硬解码兼容性和效率更好,回调YUV/RGB和快照功能将不可用)
   *
   * @param handle: return value from SmartPlayerOpen()
   *
   * @param isHWRenderMode: 0: not enable; 1: 用SmartPlayerSetSurface设置的surface自行绘制
   *
   * @return {0} if successful
   */
  public native int SmartPlayerSetHWRenderMode(long handle, int isHWRenderMode);

调用如下:

if (isHardwareDecoder && is_enable_hardware_render_mode) {
  libPlayer.SmartPlayerSetHWRenderMode(playerHandle, 1);
}


硬解码设置如下,分别区分264、265硬解:

if (isHardwareDecoder) {
  int isSupportHevcHwDecoder = libPlayer.SetSmartPlayerVideoHevcHWDecoder(playerHandle, 1);
  int isSupportH264HwDecoder = libPlayer
    .SetSmartPlayerVideoHWDecoder(playerHandle, 1);
  Log.i(TAG, "isSupportH264HwDecoder: " + isSupportH264HwDecoder + ", isSupportHevcHwDecoder: " + isSupportHevcHwDecoder);
}

实时音量调节

实时音量调节主要用于实时静音和实时调节播放端的音量,特别是多实例播放的时候,非常有必要:

  /**
   * 设置播放音量
   *
   * @param handle: return value from SmartPlayerOpen()
   *
   * @param volume: 范围是[0, 100], 0是静音,100是最大音量, 默认是100
   *
   * @return {0} if successful
   */
  public native int SmartPlayerSetAudioVolume(long handle, int volume);

RTSP模式设置

针对RTSP的TCP、UDP模式设置,RTSP超时时间设置,大家可能遇到过这样的场景,有些RTSP服务或网络环境下,只支持TCP或UDP,这时候,就需要有TCP/UDP模式设置,还有自动切换TCP UDP模式选项,如果连上RTSP服务,但是收不到数据,通过RTSP timeout时间来设置重连尝试间隔:

  /**
   * 设置RTSP TCP/UDP模式(默认UDP模式)
   *
   * @param handle: return value from SmartPlayerOpen()
   *
   * @param is_using_tcp: if with 1, it will via TCP mode, while 0 with UDP mode
   *
   * @return {0} if successful
   */
  public native int SmartPlayerSetRTSPTcpMode(long handle, int is_using_tcp);
  /**
   * 设置RTSP超时时间, timeout单位为秒,必须大于0
   *
   * @param handle: return value from SmartPlayerOpen()
   *
   * @param timeout: RTSP timeout setting
   *
   * @return {0} if successful
   */
  public native int SmartPlayerSetRTSPTimeout(long handle, int timeout);
  /**
   * 设置RTSP TCP/UDP自动切换
   *
   * @param handle: return value from SmartPlayerOpen()
   *
   * NOTE: 对于RTSP来说,有些可能支持rtp over udp方式,有些可能支持使用rtp over tcp方式.
   * 为了方便使用,有些场景下可以开启自动尝试切换开关, 打开后如果udp无法播放,sdk会自动尝试tcp, 如果tcp方式播放不了,sdk会自动尝试udp.
   *
   * @param is_auto_switch_tcp_udp 如果设置1的话, sdk将在tcp和udp之间尝试切换播放,如果设置为0,则不尝试切换.
   *
   * @return {0} if successful
   */
  public native int SmartPlayerSetRTSPAutoSwitchTcpUdp(long handle, int is_auto_switch_tcp_udp);

视频翻转、view旋转

  /**
   * 设置视频垂直反转
   *
   * @param handle: return value from SmartPlayerOpen()
   *
   * @param is_flip: 0: 不反转, 1: 反转
   *
   * @return {0} if successful
   */
  public native int SmartPlayerSetFlipVertical(long handle, int is_flip);
  /**
   * 设置视频水平反转
   *
   * @param handle: return value from SmartPlayerOpen()
   *
   * @param is_flip: 0: 不反转, 1: 反转
   *
   * @return {0} if successful
   */
  public native int SmartPlayerSetFlipHorizontal(long handle, int is_flip);
  /**
   * 设置顺时针旋转, 注意除了0度之外, 其他角度都会额外消耗性能
   *
   * @param handle: return value from SmartPlayerOpen()
   *
   * @param degress: 当前支持 0度,90度, 180度, 270度 旋转
   *
   * @return {0} if successful
   */
  public native int SmartPlayerSetRotation(long handle, int degress);

设置视频画面填充模式

  /**
   * 设置视频画面的填充模式,如填充整个view、等比例填充view,如不设置,默认填充整个view
   * @param handle: return value from SmartPlayerOpen()
   * @param render_scale_mode 0: 填充整个view; 1: 等比例填充view, 默认值是0
   * @return {0} if successful
   */
  public native int SmartPlayerSetRenderScaleMode(long handle, int render_scale_mode);

设置实时回调下载速度间隔

通过设置下载速度回调间隔,上层可以知道当前的网络状态,也可根据回调的下载速度,做逻辑上的调整。

  /**
   * Set report download speed(设置实时回调下载速度)
   *
   * @param handle: return value from SmartPlayerOpen()
   *
   * @param is_report: if with 1, it will report download speed, it with 0: does not it.
   *
   * @param report_interval: report interval, unit is second, it must be greater than 0.
   *
   * @return {0} if successful
   */
  public native int SmartPlayerSetReportDownloadSpeed(long handle, int is_report, int report_interval );

实时快照

实时快照,对RTSP或RTMP播放器来说,非常有价值,用户感兴趣的帧数据,可以直接写png文件保存下来,后续还可以针对保存下来的png数据,做二次分析。

  /**
   * Set if needs to save image during playback stream(是否启动快照功能)
   *
   * @param handle: return value from SmartPlayerOpen()
   *
   * @param is_save_image: if with 1, it will save current image via the interface of SmartPlayerSaveCurImage(), if with 0: does not it
   *
   * @return {0} if successful
   */
  public native int SmartPlayerSaveImageFlag(long handle, int is_save_image);
  /**
   * Save current image during playback stream(实时快照)
   *
   * @param handle: return value from SmartPlayerOpen()
   *
   * @param imageName: image name, which including fully path, "/sdcard/daniuliveimage/daniu.png", etc.
   *
   * @return {0} if successful
   */
  public native int SmartPlayerSaveCurImage(long handle, String imageName);


调用如下:

btnCaptureImage.setOnClickListener(new Button.OnClickListener() {
  @SuppressLint("SimpleDateFormat")
  public void onClick(View v) {
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
      .format(new Date());
    String imageFileName = "dn_" + timeStamp; // 创建以时间命名的文件名称
    String imagePath = imageSavePath + "/" + imageFileName + ".png";
    Log.i(TAG, "imagePath:" + imagePath);
    libPlayer.SmartPlayerSaveCurImage(playerHandle, imagePath);
  }
});

实时录像

实时录像的重要性不言而喻,我们好多开发者对实时录像有些误区,录像不止是写文件就完事了,还需要更精细化的粒度控制,比如单个文件大小设置、只录视频或者音频,其他音频格式转AAC后录制等。

  /**
   * Create file directory(创建录像目录)
   *
   * @param path,  E.g: /sdcard/daniulive/rec
   *
   * <pre> The interface is only used for recording the stream data to local side. </pre>
   *
   * @return {0} if successful
   */
  public native int SmartPlayerCreateFileDirectory(String path);
  /**
   * Set recorder directory(设置录像目录)
   *
   * @param handle: return value from SmartPlayerOpen()
   *
   * @param path: the directory of recorder file
   *
   * <pre> NOTE: make sure the path should be existed, or else the setting failed. </pre>
   *
   * @return {0} if successful
   */
  public native int SmartPlayerSetRecorderDirectory(long handle, String path);
  /**
   * Set the size of every recorded file(设置单个录像文件大小,如超过设定大小则自动切换到下个文件录制)
   *
   * @param handle: return value from SmartPlayerOpen()
   *
   * @param size: (MB), (5M~500M), if not in this range, set default size with 200MB.
   *
   * @return {0} if successful
   */
  public native int SmartPlayerSetRecorderFileMaxSize(long handle, int size);
  /*
   * 设置录像时音频转AAC编码的开关
   *
   * @param handle: return value from SmartPlayerOpen()
   *
   * aac比较通用,sdk增加其他音频编码(比如speex, pcmu, pcma等)转aac的功能.
   *
   * @param is_transcode: 设置为1的话,如果音频编码不是aac,则转成aac,如果是aac,则不做转换. 设置为0的话,则不做任何转换. 默认是0.
   *
   * 注意: 转码会增加性能消耗
   *
   * @return {0} if successful
   */
  public native int SmartPlayerSetRecorderAudioTranscodeAAC(long handle, int is_transcode);
  /*
  *设置是否录视频,默认的话,如果视频源有视频就录,没有就没得录, 但有些场景下可能不想录制视频,只想录音频,所以增加个开关
  *
  *@param is_record_video: 1 表示录制视频, 0 表示不录制视频, 默认是1
  *
  * @return {0} if successful
  */
  public native int SmartPlayerSetRecorderVideo(long handle, int is_record_video);
  /*
  *设置是否录音频,默认的话,如果视频源有音频就录,没有就没得录, 但有些场景下可能不想录制音频,只想录视频,所以增加个开关
  *
  *@param is_record_audio: 1 表示录制音频, 0 表示不录制音频, 默认是1
  *
  * @return {0} if successful
  */
  public native int SmartPlayerSetRecorderAudio(long handle, int is_record_audio);
  /**
   * Start recorder stream(开始录像)
   *
   * @param handle: return value from SmartPlayerOpen()
   *
   * @return {0} if successful
   */
  public native int SmartPlayerStartRecorder(long handle);
  /**
   * Stop recorder stream(停止录像)
   *
   * @param handle: return value from SmartPlayerOpen()
   *
   * @return {0} if successful
   */
  public native int SmartPlayerStopRecorder(long handle);

总结

实际上,除了上述提到的功能外,RTMP或RTSP播放器,还需要支持的客制化功能有支持回调H.264/H.265/AAC数据,或回到解码后的YUV或RGB数据,设置播放缓冲、设置快速播放、设置音频输出类型等,一个通用的RTSP、RTMP播放器,延迟是基础、功能完备是加分项,性能优异稳定性好,才敢推给客户,让客户在现场放心使用,无后顾之忧。

相关文章
|
1月前
|
Java Android开发 Swift
安卓与iOS开发对比:平台选择对项目成功的影响
【10月更文挑战第4天】在移动应用开发的世界中,选择合适的平台是至关重要的。本文将深入探讨安卓和iOS两大主流平台的开发环境、用户基础、市场份额和开发成本等方面的差异,并分析这些差异如何影响项目的最终成果。通过比较这两个平台的优势与挑战,开发者可以更好地决定哪个平台更适合他们的项目需求。
109 1
|
2月前
|
IDE Android开发 iOS开发
探索Android与iOS开发的差异:平台选择对项目成功的影响
【9月更文挑战第27天】在移动应用开发的世界中,Android和iOS是两个主要的操作系统平台。每个系统都有其独特的开发环境、工具和用户群体。本文将深入探讨这两个平台的关键差异点,并分析这些差异如何影响应用的性能、用户体验和最终的市场表现。通过对比分析,我们将揭示选择正确的开发平台对于确保项目成功的重要作用。
|
29天前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
81 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
2月前
|
开发工具 Android开发 iOS开发
安卓与iOS开发环境对比:选择适合你的平台
【9月更文挑战第26天】在移动应用开发的广阔天地中,安卓和iOS是两大巨头。它们各自拥有独特的优势和挑战,影响着开发者的选择和决策。本文将深入探讨这两个平台的开发环境,帮助你理解它们的核心差异,并指导你根据个人或项目需求做出明智的选择。无论你是初学者还是资深开发者,了解这些平台的异同都至关重要。让我们一起探索,找到最适合你的那片开发天地。
|
Android开发 开发者
《Android进阶解密》| 每日读本书
Android进阶系列第二部,畅销书《Android进阶之光》作者又一力作!本书基于Android 8.0,将系统源码和应用开发结合讲解,帮助读者融会贯通,破解Android应用开发进阶秘密。每日搜罗最具权威专业书籍,更多图书请关注“每日读本书”。
1175 0
|
3天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
5天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。
|
7天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
【10月更文挑战第35天】在数字化时代,安卓应用的开发成为了一个热门话题。本文旨在通过浅显易懂的语言,带领初学者了解安卓开发的基础知识,同时为有一定经验的开发者提供进阶技巧。我们将一起探讨如何从零开始构建第一个安卓应用,并逐步深入到性能优化和高级功能的实现。无论你是编程新手还是希望提升技能的开发者,这篇文章都将为你提供有价值的指导和灵感。
|
5天前
|
存储 API 开发工具
探索安卓开发:从基础到进阶
【10月更文挑战第37天】在这篇文章中,我们将一起探索安卓开发的奥秘。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和建议。我们将从安卓开发的基础开始,逐步深入到更复杂的主题,如自定义组件、性能优化等。最后,我们将通过一个代码示例来展示如何实现一个简单的安卓应用。让我们一起开始吧!
|
6天前
|
存储 XML JSON
探索安卓开发:从新手到专家的旅程
【10月更文挑战第36天】在这篇文章中,我们将一起踏上一段激动人心的旅程,从零基础开始,逐步深入安卓开发的奥秘。无论你是编程新手,还是希望扩展技能的老手,这里都有适合你的知识宝藏等待发掘。通过实际的代码示例和深入浅出的解释,我们将解锁安卓开发的关键技能,让你能够构建自己的应用程序,甚至贡献于开源社区。准备好了吗?让我们开始吧!
18 2