Android平台如何实现多路低延迟RTSP|RTMP播放?

本文涉及的产品
视觉智能开放平台,分割抠图1万点
视觉智能开放平台,图像通用资源包5000点
视觉智能开放平台,视频通用资源包5000点
简介: 本文档详细介绍了大牛直播SDK在Android平台上实现RTSP与RTMP流媒体播放及录像功能的技术细节。早在2015年,SDK的第一版就已经支持了多实例播放,并且通过简单的实例封装就能轻松实现。文档中提供了代码示例,展示了如何开启播放、停止播放以及开始和停止录像等功能。此外,SDK还提供了丰富的配置选项,例如设置录像目录、文件大小限制、转码选项等。总结部分列出了该SDK的关键特性,包括但不限于高稳定性和低延迟的播放能力、多实例支持、事件回调、硬解码支持、网络状态监控以及复杂的网络环境处理等。这些功能使得SDK能够应对各种应用场景,特别是在对延迟和稳定性有极高要求的情况下表现优异。

技术背景

实际上,我们在2015年做Android平台RTSP、RTMP播放模块的时候,第一版就支持了多实例播放,因为SDK设计比较灵活,做个简单的player实例封装即可实现多实例播放(Android Unity的就有多路demo),所以官方一直没有正式demo,本次也是有个开发者提到,希望测试下我们多路播放的效果,自己又不想做封装,索性给做个版本。

技术实现

废话不多说,先上图:

image.gif

我们针对的功能展示,主要是播放和录像这块,先说播放:

/*
 * SmartPlayer.java
 * Author: https://daniusdk.com
 * Created by DaniuLive on 2015/09/26.
 */
class ButtonPlayback1Listener implements View.OnClickListener {
    public void onClick(View v) {
        if (stream_player_1_.is_playing()) {
            Log.i(TAG, "Stop player1..");
            boolean iRet = stream_player_1_.StopPlayer();
            if (!iRet) {
                Log.e(TAG, "Call StopPlayer failed..");
                return;
            }
            stream_player_1_.try_release();
            btn_playback1.setText("开始播放1");
            SetViewVisibility(surface_view_1_);
        } else {
            Log.i(TAG, "Start playback stream1++");
            int play_buffer = 0;
            int is_using_tcp = 0;
            if(!stream_player_1_.OpenPlayerHandle(playback_url_1_, play_buffer, is_using_tcp))
                return;
            stream_player_1_.SetView(surface_view_1_);
            boolean is_mute = false;
            boolean iPlaybackRet = stream_player_1_.StartPlayer(isHardwareDecoder, is_enable_hardware_render_mode, is_mute);
            if (!iPlaybackRet) {
                Log.e(TAG, "Call StartPlayer failed..");
                return;
            }
            btn_playback1.setText("停止播放1");
        }
    }
}

image.gif

对应的OpenPlayerHandle()实现如下:

/*
 * LibPlayerWrapper.java.java
 * Author: https://daniusdk.com
 */
public boolean OpenPlayerHandle(String playback_url, int play_buffer, int is_using_tcp) {
    if (check_native_handle())
        return true;
    if(!isValidRtspOrRtmpUrl(playback_url))
        return false;
    long handle = lib_player_.SmartPlayerOpen(application_context());
    if (0==handle) {
        Log.e(TAG, "sdk open failed!");
        return false;
    }
    lib_player_.SetSmartPlayerEventCallbackV2(handle, new EventHandleV2());
    lib_player_.SmartPlayerSetBuffer(handle, play_buffer);
    // set report download speed(默认2秒一次回调 用户可自行调整report间隔)
    lib_player_.SmartPlayerSetReportDownloadSpeed(handle, 1, 4);
    boolean isFastStartup = true;
    lib_player_.SmartPlayerSetFastStartup(handle, isFastStartup ? 1 : 0);
    //设置RTSP超时时间
    int rtsp_timeout = 10;
    lib_player_.SmartPlayerSetRTSPTimeout(handle, rtsp_timeout);
    //设置RTSP TCP/UDP模式自动切换
    int is_auto_switch_tcp_udp = 1;
    lib_player_.SmartPlayerSetRTSPAutoSwitchTcpUdp(handle, is_auto_switch_tcp_udp);
    lib_player_.SmartPlayerSaveImageFlag(handle, 1);
    // It only used when playback RTSP stream..
    lib_player_.SmartPlayerSetRTSPTcpMode(handle, is_using_tcp);
    lib_player_.DisableEnhancedRTMP(handle, 0);
    lib_player_.SmartPlayerSetUrl(handle, playback_url);
    set(handle);
    return true;
}

image.gif

对应的开始播放、停止播放设计:

/*
 * LibPlayerWrapper.java
 * Author: https://daniusdk.com
 */
public boolean StartPlayer(boolean is_hardware_decoder, boolean is_enable_hardware_render_mode, boolean is_mute) {
    if (is_playing()) {
        Log.e(TAG, "already playing, native_handle:" + get());
        return false;
    }
    SetPlayerParam(is_hardware_decoder, is_enable_hardware_render_mode, is_mute);
    int ret = lib_player_.SmartPlayerStartPlay(get());
    if (ret != OK) {
        Log.e(TAG, "call StartPlay failed, native_handle:" + get() + ", ret:" + ret);
        return false;
    }
    write_lock_.lock();
    try {
        this.is_playing_ = true;
    } finally {
        write_lock_.unlock();
    }
    Log.i(TAG, "call StartPlayer OK, native_handle:" + get());
    return true;
}
public boolean StopPlayer() {
    if (!check_native_handle())
        return false;
    if (!is_playing()) {
        Log.w(TAG, "it's not playing, native_handle:" + get());
        return false;
    }
    boolean is_need_call = false;
    write_lock_.lock();
    try {
        if (this.is_playing_) {
            this.is_playing_ = false;
            is_need_call = true;
        }
    } finally {
        write_lock_.unlock();
    }
    if (is_need_call)
        lib_player_.SmartPlayerStopPlay(get());
    return true;
}

image.gif

录像设计:

/*
 * SmartPlayer.java
 * Author: https://daniusdk.com
 */
class ButtonRecorder1Listener implements View.OnClickListener {
    public void onClick(View v) {
        if (stream_player_1_.is_recording()) {
            Log.i(TAG, "Stop recorder1..");
            boolean iRet = stream_player_1_.StopRecorder();
            if (!iRet) {
                Log.e(TAG, "Call StopRecorder failed..");
                return;
            }
            stream_player_1_.try_release();
            btn_recorder1.setText("开始录像1");
        } else {
            Log.i(TAG, "Start recorder stream1++");
            int play_buffer = 0;
            int is_using_tcp = 0;
            if(!stream_player_1_.OpenPlayerHandle(playback_url_1_, play_buffer, is_using_tcp))
                return;
            stream_player_1_.ConfigRecorderParam(recDir, 400, 1, 1, 1);
            boolean iRecRet = stream_player_1_.StartRecorder();
            if (!iRecRet) {
                Log.e(TAG, "Call StartRecorder failed..");
                return;
            }
            btn_recorder1.setText("停止录像1");
        }
    }
}

image.gif

录像参数配置选项:

/*
 * LibPlayerWrapper.java
 * Author: https://daniusdk.com
 */
public boolean ConfigRecorderParam(String rec_dir, int file_max_size, int is_transcode_aac,
                                   int is_record_video, int is_record_audio) {
    if(!check_native_handle())
        return false;
    if (null == rec_dir || rec_dir.isEmpty())
        return false;
    int ret = lib_player_.SmartPlayerCreateFileDirectory(rec_dir);
    if (ret != 0) {
        Log.e(TAG, "Create record dir failed, path:" + rec_dir);
        return false;
    }
    if (lib_player_.SmartPlayerSetRecorderDirectory(get(), rec_dir) != 0) {
        Log.e(TAG, "Set record dir failed , path:" + rec_dir);
        return false;
    }
    if (lib_player_.SmartPlayerSetRecorderFileMaxSize(get(),file_max_size) != 0) {
        Log.e(TAG, "SmartPlayerSetRecorderFileMaxSize failed.");
        return false;
    }
    lib_player_.SmartPlayerSetRecorderAudioTranscodeAAC(get(), is_transcode_aac);
    // 更细粒度控制录像的, 一般情况无需调用
    lib_player_.SmartPlayerSetRecorderVideo(get(), is_record_video);
    lib_player_.SmartPlayerSetRecorderAudio(get(), is_record_audio);
    return true;
}

image.gif

开始录像、结束录像:

/*
 * LibPlayerWrapper.java
 * Author: https://daniusdk.com
 */
public boolean StartRecorder() {
    if (is_recording()) {
        Log.e(TAG, "already recording, native_handle:" + get());
        return false;
    }
    int ret = lib_player_.SmartPlayerStartRecorder(get());
    if (ret != OK) {
        Log.e(TAG, "call SmartPlayerStartRecorder failed, native_handle:" + get() + ", ret:" + ret);
        return false;
    }
    write_lock_.lock();
    try {
        this.is_recording_ = true;
    } finally {
        write_lock_.unlock();
    }
    Log.i(TAG, "call SmartPlayerStartRecorder OK, native_handle:" + get());
    return true;
}
public boolean StopRecorder() {
    if (!check_native_handle())
        return false;
    if (!is_recording()) {
        Log.w(TAG, "it's not recording, native_handle:" + get());
        return false;
    }
    boolean is_need_call = false;
    write_lock_.lock();
    try {
        if (this.is_recording_) {
            this.is_recording_ = false;
            is_need_call = true;
        }
    } finally {
        write_lock_.unlock();
    }
    if (is_need_call)
        lib_player_.SmartPlayerStopRecorder(get());
    return true;
}

image.gif

总结

说了这么多,以RTSP播放为例,大概说下实现的功能:

  • [支持播放协议]高稳定、超低延迟、业内首屈一指的RTSP直播播放器SDK;
  • [多实例播放]支持多实例播放;
  • [事件回调]支持网络状态、buffer状态等回调;
  • [视频格式]支持H.265、H.264,此外,还支持RTSP MJPEG播放;
  • [音频格式]支持AAC/PCMA/PCMU;
  • [H.264/H.265软解码]支持H.264/H.265软解;
  • [H.264硬解码]Windows/Android/iOS支持特定机型H.264硬解;
  • [H.265硬解]Windows/Android/iOS支持特定机型H.265硬解;
  • [H.264/H.265硬解码]Android支持设置Surface模式硬解和普通模式硬解码;
  • [RTSP模式设置]支持RTSP TCP/UDP模式设置;
  • [RTSP TCP/UDP自动切换]支持RTSP TCP、UDP模式自动切换;
  • [RTSP超时设置]支持RTSP超时时间设置,单位:秒;
  • [RTSP 401认证处理]支持上报RTSP 401事件,如URL携带鉴权信息,会自动处理;
  • [缓冲时间设置]支持buffer time设置;
  • [首屏秒开]支持首屏秒开模式;
  • [复杂网络处理]支持断网重连等各种网络环境自动适配;
  • [快速切换URL]支持播放过程中,快速切换其他URL,内容切换更快;
  • [音视频多种render机制]Android平台,视频:surfaceview/OpenGL ES,音频:AudioTrack/OpenSL ES;
  • [实时静音]支持播放过程中,实时静音/取消静音;
  • [实时音量调节]支持播放过程中实时调节音量;
  • [实时快照]支持播放过程中截取当前播放画面;
  • [只播关键帧]Windows平台支持实时设置是否只播放关键帧;
  • [渲染角度]支持0°,90°,180°和270°四个视频画面渲染角度设置;
  • [渲染镜像]支持水平反转、垂直反转模式设置;
  • [等比例缩放]支持图像等比例缩放绘制(Android设置surface模式硬解模式不支持);
  • [实时下载速度更新]支持当前下载速度实时回调(支持设置回调时间间隔);
  • [解码前视频数据回调]支持H.264/H.265数据回调;
  • [解码后视频数据回调]支持解码后YUV/RGB数据回调;
  • [解码前音频数据回调]支持AAC/PCMA/PCMU数据回调;
  • [音视频自适应]支持播放过程中,音视频信息改变后自适应;
  • [扩展录像功能]完美支持和录像SDK组合使用。

上面只是简单的播放、录像的演示,除此之外,大牛直播SDK的RTSP、RTMP播放器海康实现播放缓冲设置、软硬解码设置、实时快照、实时音量调节、实时解码后数据回调等。毫秒级延迟,完全满足对延迟、稳定性要求苛刻的场景下。感兴趣的开发者,可以单独和我沟通。

相关文章
|
2月前
|
存储 编解码 监控
Android平台GB28181执法记录仪技术方案与实现
本文介绍了大牛直播SDK的SmartGBD在执法记录仪场景中的应用。GB28181协议作为视频监控联网的国家标准,为设备互联互通提供规范。SmartGBD专为Android平台设计,支持音视频采集、编码与传输,具备自适应算法和多功能扩展优势。文章分析了执法记录仪的需求,如实时音视频传输、设备管理及数据安全,并详细阐述了基于SmartGBD的技术实现方案,包括环境准备、SDK集成、设备注册、音视频处理及功能扩展等步骤。最后展望了SmartGBD在未来智慧物联领域的广阔应用前景。
85 13
|
2月前
|
存储 编解码 开发工具
Android平台毫秒级低延迟HTTP-FLV直播播放器技术探究与实现
本文详细探讨了在Android平台上实现HTTP-FLV播放器的过程。首先介绍了FLV格式的基础,包括文件头和标签结构。接着分析了HTTP-FLV传输原理,通过分块传输实现流畅播放。然后重点讲解了播放器的实现步骤,涵盖网络请求、数据解析、音视频解码与渲染,以及播放控制功能的设计。文章还讨论了性能优化和网络异常处理的方法,并总结了HTTP-FLV播放器的技术价值,尤其是在特定场景下的应用意义。
134 11
|
2月前
|
监控 Shell Linux
Android调试终极指南:ADB安装+多设备连接+ANR日志抓取全流程解析,覆盖环境变量配置/多设备调试/ANR日志分析全流程,附Win/Mac/Linux三平台解决方案
ADB(Android Debug Bridge)是安卓开发中的重要工具,用于连接电脑与安卓设备,实现文件传输、应用管理、日志抓取等功能。本文介绍了 ADB 的基本概念、安装配置及常用命令。包括:1) 基本命令如 `adb version` 和 `adb devices`;2) 权限操作如 `adb root` 和 `adb shell`;3) APK 操作如安装、卸载应用;4) 文件传输如 `adb push` 和 `adb pull`;5) 日志记录如 `adb logcat`;6) 系统信息获取如屏幕截图和录屏。通过这些功能,用户可高效调试和管理安卓设备。
|
5月前
|
IDE 开发工具 Android开发
移动应用开发之旅:探索Android和iOS平台
在这篇文章中,我们将深入探讨移动应用开发的两个主要平台——Android和iOS。我们将了解它们的操作系统、开发环境和工具,并通过代码示例展示如何在这两个平台上创建一个简单的“Hello World”应用。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧,帮助你更好地理解和掌握移动应用开发。
149 17
|
7月前
|
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开发知识可参考相关书籍。
232 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
Android开发 Java
Android VideoView播放视频控制:开始、暂停、快进(3)
Android VideoView播放视频控制:开始、暂停、快进(3) 本文在附录参考文章(1)的基础上增加的Android VideoView播放视频时候的控制。
2032 0
|
2月前
|
JavaScript Linux 网络安全
Termux安卓终端美化与开发实战:从下载到插件优化,小白也能玩转Linux
Termux是一款安卓平台上的开源终端模拟器,支持apt包管理、SSH连接及Python/Node.js/C++开发环境搭建,被誉为“手机上的Linux系统”。其特点包括零ROOT权限、跨平台开发和强大扩展性。本文详细介绍其安装准备、基础与高级环境配置、必备插件推荐、常见问题解决方法以及延伸学习资源,帮助用户充分利用Termux进行开发与学习。适用于Android 7+设备,原创内容转载请注明来源。
383 76
|
3月前
|
JavaScript 搜索推荐 Android开发
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
98 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
|
3月前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
246 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
3月前
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
85 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex