技术背景

在安防监控、远程巡检、无人机回传、智慧教育、工业控制等日益丰富的实时音视频场景中,RTSP和RTMP作为两大主流流媒体传输协议,承载着绝大部分低延迟直播业务。

RTSP(Real Time Streaming Protocol) 由RFC 2326定义,是一种应用层协议,主要用于控制实时数据的传输。RTSP本身不传输媒体数据,而是通过RTP/RTCP完成音视频的实际投递。RTSP天然适用于IP摄像头(IPC)、NVR等安防设备的流媒体接入场景,支持TCP和UDP两种传输模式,在局域网环境下可以实现极低的端到端延迟。

RTMP(Real Time Messaging Protocol) 最初由Macromedia(后被Adobe收购)设计,基于TCP传输,协议栈成熟、生态广泛。RTMP在CDN分发、互动直播、在线教育等场景中被大量使用。近年来Enhanced RTMP的推出,更进一步支持了H.265/HEVC编码,使其在带宽受限场景下依然能提供高质量的视频传输。

然而,在实际工程落地中,开发者面临的挑战远不止"能播放"这么简单:延迟控制、多实例并发、软硬解码切换、网络抖动适配、解码后数据回调对接AI算法、实时录像与快照等,都是绕不开的核心问题。

大牛直播SDK(SmartMediaKit)自2015年发布以来,持续深耕RTSP/RTMP播放领域,基于全自研内核,提供了一套覆盖Windows、Linux、Android、iOS及Unity3D的跨平台低延迟直播播放解决方案。本文将结合Android平台SmartPlayer模块的实际Demo代码,从架构设计、核心接口、关键功能实现、调用流程等维度,进行深入的技术解析。


一、整体架构设计

大牛直播SDK的Android播放模块采用三层架构设计,各层职责清晰、解耦彻底:

┌─────────────────────────────────────────────┐
│          SmartPlayer (Demo Activity)         │  ← 业务层:UI交互、功能控制
├─────────────────────────────────────────────┤
│          LibPlayerWrapper                    │  ← 封装层:状态管理、线程安全
├─────────────────────────────────────────────┤
│          SmartPlayerJniV2 (JNI)              │  ← 内核层:Native C/C++引擎
└─────────────────────────────────────────────┘

1.1 内核层:SmartPlayerJniV2

SmartPlayerJniV2 是整个播放模块的JNI入口,通过 native 方法桥接Java层与底层C/C++音视频引擎。它定义了播放器从创建到销毁的完整生命周期接口,涵盖:

  • 实例管理SmartPlayerOpen() / SmartPlayerClose()
  • 协议与连接SmartPlayerSetUrl() / SmartPlayerSetRTSPTcpMode() / SmartPlayerSetRTSPTimeout() / SmartPlayerSetRTSPAutoSwitchTcpUdp()
  • 解码控制SetSmartPlayerVideoHWDecoder() / SetSmartPlayerVideoHevcHWDecoder() / SmartPlayerSetHWRenderMode()
  • 渲染设置SmartPlayerSetSurface() / SmartPlayerSetExternalRender() / SmartPlayerSetRenderScaleMode()
  • 播放控制SmartPlayerStartPlay() / SmartPlayerStopPlay() / SmartPlayerSwitchPlaybackUrl()
  • 录像管理SmartPlayerSetRecorderDirectory() / SmartPlayerStartRecorder() / SmartPlayerStopRecorder()
  • 拉流转发SmartPlayerStartPullStream() / SmartPlayerStopPullStream()
  • 音视频调节SmartPlayerSetMute() / SmartPlayerSetAudioVolume() / SmartPlayerSetFlipVertical() / SmartPlayerSetRotation()
  • 数据回调SmartPlayerSetExternalRender() / SmartPlayerSetAudioDataCallback() / SmartPlayerSetVideoDataCallback() / SmartPlayerSetUserDataCallback()
  • RTMP加密SmartPlayerSetKey() / SmartPlayerSetDecryptionIV()
  • 外部数据投递PostVideoPacketByteBuffer() / PostAudioPacket()

Native库通过静态加载方式引入:

static { System.loadLibrary("SmartPlayer"); }

1.2 封装层:LibPlayerWrapper

LibPlayerWrapper 是对 SmartPlayerJniV2 的线程安全封装,实现了 AutoCloseable 接口,核心设计亮点包括:

(1)读写锁保护状态变更

播放器的启动、停止等操作涉及状态标志位(is_playing_is_pulling_is_recording_)的变更,LibPlayerWrapper 使用 ReentrantReadWriteLock 确保多线程安全:

private final ReadWriteLock rw_lock_ = new ReentrantReadWriteLock(true);
private final java.util.concurrent.locks.Lock write_lock_ = rw_lock_.writeLock();
private final java.util.concurrent.locks.Lock read_lock_ = rw_lock_.readLock();

以启动播放为例,启动成功后在写锁保护下更新状态:

public boolean StartPlayer(SurfaceView view, Surface surface, NTExternalRender external_render,
                           int render_scale_mode, boolean is_fast_startup,
                           boolean is_hardware_decoder, boolean is_low_latency,
                           boolean is_hw_render_mode) {
    if (!check_native_handle())
        return false;

    if (is_playing()) {
        Log.e(TAG, "already playing, native_handle:" + get());
        return false;
    }

    lib_player_.SmartPlayerSetSurface(get(), view);
    lib_player_.SmartPlayerSetExternalRender(get(), external_render);
    lib_player_.SmartPlayerSetRenderScaleMode(get(), render_scale_mode);
    lib_player_.SmartPlayerSetFastStartup(get(), is_fast_startup ? 1 : 0);
    lib_player_.SmartPlayerSetAudioOutputType(get(), 1);

    if (is_hardware_decoder) {
        int isSupportH264HwDecoder = lib_player_.SetSmartPlayerVideoHWDecoder(get(), 1);
        int isSupportHevcHwDecoder = lib_player_.SetSmartPlayerVideoHevcHWDecoder(get(), 1);
        lib_player_.SmartPlayerSetHWRenderMode(get(), is_hw_render_mode ? 1 : 0);
    }

    lib_player_.SmartPlayerSetLowLatencyMode(get(), is_low_latency ? 1 : 0);

    int ret = lib_player_.SmartPlayerStartPlay(get());
    if (ret != OK) {
        Log.e(TAG, "call SmartPlayerStartPlay failed, native_handle:" + get() + ", ret:" + ret);
        return false;
    }

    write_lock_.lock();
    try {
        this.is_playing_ = true;
    } finally {
        write_lock_.unlock();
    }

    return true;
}

(2)高频数据投递使用读锁 + tryLock

对于视频数据投递这类高频调用,采用 read_lock_.tryLock() 非阻塞方式获取读锁,避免在播放器关闭过程中产生阻塞:

public boolean PostVideoPacketByteBuffer(int codec_id,
                                         java.nio.ByteBuffer packet, int offset, int size,
                                         long timestamp_ms, int is_timestamp_discontinuity,
                                         int is_key, byte[] extra_data, int extra_data_size,
                                         int width, int height) {
    if (!check_native_handle() || !is_running())
        return false;

    if (!read_lock_.tryLock())
        return false;

    try {
        if (!check_native_handle() || !is_running())
            return false;

        return OK == lib_player_.PostVideoPacketByteBuffer(get(), codec_id, packet, offset, size,
                timestamp_ms, is_timestamp_discontinuity, is_key, extra_data, extra_data_size, width, height);
    } catch (Exception e) {
        Log.e(TAG, "PostVideoPacketByteBuffer Exception:", e);
        return false;
    } finally {
        read_lock_.unlock();
    }
}

(3)资源安全释放

LibPlayerWrapper 支持多重释放保障机制:close() 方法实现 AutoCloseablerelease() 方法提供显式释放,finalize() 作为兜底保护。释放时按序停止播放、拉流、录像,最后关闭Native句柄:

public void release() {
    if (empty())
        return;

    if (is_playing())
        StopPlayer();

    if (is_pulling())
        StopPullStream();

    if (is_recording())
        StopRecorder();

    long handle;
    write_lock_.lock();
    try {
        handle = this.native_handle_;
        this.native_handle_ = 0;
        clear_all_running_flags();
    } finally {
        write_lock_.unlock();
    }

    if (lib_player_ != null && handle != 0)
        lib_player_.SmartPlayerClose(handle);
}

1.3 业务层:SmartPlayer Demo Activity

SmartPlayer 作为Demo Activity,完整展示了播放器的集成方式和功能调用,覆盖了URL输入、播放/停止、录像管理、参数调节等全部交互逻辑。

Android平台RTSP播放器时延测试


二、核心功能与实现详解

2.1 播放器初始化

播放器初始化是整个调用流程的起点,完成Native实例创建和关键参数配置:

private boolean initPlayer() {
    long handle = libPlayer.SmartPlayerOpen(appContext);
    if (handle == 0) {
        Log.e(TAG, "SmartPlayerOpen 失败");
        return false;
    }

    playerWrapper.set(libPlayer, handle);

    libPlayer.SetSmartPlayerEventCallbackV2(handle, new PlayerEventCallback());
    libPlayer.SmartPlayerSetBuffer(handle, playBuffer);
    libPlayer.SmartPlayerSetReportDownloadSpeed(handle, 1, 2);
    libPlayer.SmartPlayerSetRTSPTcpMode(handle, 1);
    libPlayer.SmartPlayerSetRTSPTimeout(handle, 10);
    libPlayer.SmartPlayerSetRTSPAutoSwitchTcpUdp(handle, 1);
    libPlayer.SmartPlayerSaveImageFlag(handle, 1);

    setPlaybackUrl(playbackUrl);
    setEncryptionKey();

    return true;
}

关键配置参数说明:

配置项 说明
SmartPlayerSetBuffer 缓冲时间(毫秒),0~5000,低延迟场景设为0
SmartPlayerSetRTSPTcpMode 1:TCP模式,0:UDP模式,TCP穿透性更好
SmartPlayerSetRTSPTimeout RTSP连接超时时间(秒)
SmartPlayerSetRTSPAutoSwitchTcpUdp TCP/UDP自动切换,提升兼容性
SmartPlayerSaveImageFlag 启用快照功能
SetSmartPlayerEventCallbackV2 事件回调,用于状态感知
SmartPlayerSetReportDownloadSpeed 定时上报下载速度

2.2 低延迟播放

低延迟是大牛直播SDK的核心竞争力之一,通过以下组合配置可实现毫秒级延迟:

// 快速启动模式(秒开)
libPlayer.SmartPlayerSetFastStartup(handle, 1);

// 低延迟模式
libPlayer.SmartPlayerSetLowLatencyMode(handle, 1);

// 缓冲设为0
libPlayer.SmartPlayerSetBuffer(handle, 0);

在SmartPlayer Demo中,这些参数通过按钮动态控制:

private void toggleLowLatency() {
    if (playerWrapper.is_running()) {
        showToast("播放/录像中无法切换");
        return;
    }
    isLowLatency = !isLowLatency;
    if (isLowLatency) playBuffer = 0;
    btnLowLatency.setText(isLowLatency ? "超低延时" : "正常延时");
}

private void toggleFastStartup() {
    if (playerWrapper.is_running()) {
        showToast("播放/录像中无法切换");
        return;
    }
    isFastStartup = !isFastStartup;
    btnFastStartup.setText(isFastStartup ? "秒开:开" : "秒开:关");
}

在实际应用中,大牛直播SDK可将RTSP播放延迟控制在100~200ms级别,RTMP播放延迟同样可达到100~200ms级别,远优于基于FFmpeg或VLC的通用方案。

2.3 软硬解码灵活切换

SDK同时支持H.264和H.265的软解码与硬解码,开发者可根据设备性能灵活选择:

private void toggleHardwareDecoder() {
    if (playerWrapper.is_running()) {
        showToast("播放/录像中无法切换");
        return;
    }
    isHardwareDecoder = !isHardwareDecoder;
    btnHardwareDecoder.setText(isHardwareDecoder ? "硬解码" : "软解码");
}

LibPlayerWrapper.StartPlayer() 中,硬解码的配置逻辑如下:

if (is_hardware_decoder) {
    int isSupportH264HwDecoder = lib_player_.SetSmartPlayerVideoHWDecoder(get(), 1);
    int isSupportHevcHwDecoder = lib_player_.SetSmartPlayerVideoHevcHWDecoder(get(), 1);

    Log.i(TAG, "isSupportH264HwDecoder: " + isSupportH264HwDecoder 
          + ", isSupportHevcHwDecoder: " + isSupportHevcHwDecoder);
    lib_player_.SmartPlayerSetHWRenderMode(get(), is_hw_render_mode ? 1 : 0);
}

硬解码模式下还支持 MediaCodec自行绘制模式(HW Render Mode),此模式绕过SDK渲染环节,由MediaCodec直接输出到Surface,兼容性和效率更好。

2.4 事件回调机制

SDK提供了完善的事件回调体系,通过 NTSmartEventCallbackV2 接口,开发者可以实时感知播放器的各种状态变化:

private class PlayerEventCallback implements NTSmartEventCallbackV2 {
    @Override
    public void onNTSmartEventCallbackV2(long handle, int id, long param1,
                                         long param2, String param3, String param4, Object param5) {
        String event = formatEventMessage(id, param1, param2, param3);
        if (!event.isEmpty()) {
            Log.i(TAG, event);
            handler.obtainMessage(MSG_PLAYER_EVENT, event).sendToTarget();
        }
    }
}

支持的事件类型覆盖了播放全生命周期:

事件ID 含义
EVENT_DANIULIVE_ERC_PLAYER_STARTED 开始播放
EVENT_DANIULIVE_ERC_PLAYER_CONNECTING 连接中
EVENT_DANIULIVE_ERC_PLAYER_CONNECTED 已连接
EVENT_DANIULIVE_ERC_PLAYER_CONNECTION_FAILED 连接失败
EVENT_DANIULIVE_ERC_PLAYER_DISCONNECTED 连接断开
EVENT_DANIULIVE_ERC_PLAYER_STOP 已停止
EVENT_DANIULIVE_ERC_PLAYER_RESOLUTION_INFO 分辨率信息
EVENT_DANIULIVE_ERC_PLAYER_NO_MEDIADATA_RECEIVED 无数据接收
EVENT_DANIULIVE_ERC_PLAYER_SWITCH_URL 切换URL中
EVENT_DANIULIVE_ERC_PLAYER_CAPTURE_IMAGE 快照结果
EVENT_DANIULIVE_ERC_PLAYER_RECORDER_START_NEW_FILE 录像开始新文件
EVENT_DANIULIVE_ERC_PLAYER_ONE_RECORDER_FILE_FINISHED 录像文件完成
EVENT_DANIULIVE_ERC_PLAYER_DOWNLOAD_SPEED 下载速度上报
EVENT_DANIULIVE_ERC_PLAYER_NEED_KEY 需要解密Key
EVENT_DANIULIVE_ERC_PLAYER_KEY_ERROR Key错误

事件回调通过 WeakReference + Handler 安全传递到UI线程,避免内存泄漏:

private static class MainHandler extends Handler {
    private final WeakReference<SmartPlayer> activityRef;

    MainHandler(SmartPlayer activity) {
        this.activityRef = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        SmartPlayer activity = activityRef.get();
        if (activity == null) return;

        if (msg.what == MSG_PLAYER_EVENT) {
            activity.txtEventMsg.setText("Event: " + msg.obj);
        } else if (msg.what == MSG_USER_DATA) {
            activity.txtUserDataMsg.setText("数据: " + msg.obj);
        }
    }
}

2.5 实时录像

SDK支持边播边录,录像功能与播放功能可独立启停、互不干扰。录像相关配置包括设置录像目录、单文件最大尺寸、音频转码等:

private void startRecording() {
    if (playbackUrl == null || playbackUrl.isEmpty()) {
        showToast("请先输入播放URL");
        return;
    }

    if (playerWrapper.empty() && !initPlayer()) {
        return;
    }

    // 配置录像
    if (recordDir != null && !recordDir.isEmpty()) {
        if (libPlayer.SmartPlayerCreateFileDirectory(recordDir) == 0) {
            playerWrapper.SetRecorderDirectory(recordDir);
            playerWrapper.SetRecorderFileMaxSize(200);  // 单文件200MB
        }
    }

    if (!playerWrapper.StartRecorder(true)) {  // true: 非AAC音频转AAC
        Log.e(TAG, "StartRecorder 失败");
        return;
    }

    updateButtonStates();
}

LibPlayerWrapper 中录像的启动和停止同样受写锁保护:

public boolean StartRecorder(boolean is_audio_transcode_aac) {
    if (!check_native_handle())
        return false;

    if (is_recording()) {
        Log.e(TAG, "already recording, native_handle:" + get());
        return false;
    }

    lib_player_.SmartPlayerSetRecorderAudioTranscodeAAC(get(), is_audio_transcode_aac ? 1 : 0);

    int ret = lib_player_.SmartPlayerStartRecorder(get());
    if (ret != OK) {
        return false;
    }

    write_lock_.lock();
    try {
        this.is_recording_ = true;
    } finally {
        write_lock_.unlock();
    }

    return true;
}

录像文件以MP4格式存储,当单个文件达到设定大小后自动切割到新文件,通过事件回调通知文件路径。

2.6 录像文件管理与回放

SDK Demo中还提供了完整的录像管理模块(RecorderManager)和录像回放模块(RecorderPlayback),形成了"录制→管理→回放→删除"的完整闭环。

RecorderManager 采用异步加载方式扫描录像文件目录,在后台线程读取每个MP4文件的大小和时长信息,避免阻塞UI线程:

private void refreshFileList() {
    mProgressBar.setVisibility(View.VISIBLE);
    recFileListView.setVisibility(View.INVISIBLE);

    new Thread(new Runnable() {
        @Override
        public void run() {
            final ArrayList<ArrayList<String>> resultList = loadFileListInBackground();

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (isFinishing()) return;
                    fileList = resultList;
                    updateListView();
                    mProgressBar.setVisibility(View.GONE);
                }
            });
        }
    }).start();
}

RecorderPlayback 实现了基于 VideoView 的录像回放,并在Android 8.0+设备上使用 MediaPlayer.SEEK_CLOSEST 实现精准帧级Seek,而非仅Seek到关键帧:

@Override
public void onStopTrackingTouch(SeekBar seekBar) {
    int targetPosition = seekBar.getProgress();
    if (playVideoView != null) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && mMediaPlayer != null) {
            try {
                mMediaPlayer.seekTo(targetPosition, MediaPlayer.SEEK_CLOSEST);
            } catch (Exception e) {
                playVideoView.seekTo(targetPosition);
            }
        } else {
            playVideoView.seekTo(targetPosition);
        }
    }
    mHandler.sendEmptyMessage(MSG_UPDATE_PROGRESS);
}

2.7 实时快照

快照功能支持JPEG和PNG两种格式,通过 CaptureImage 接口实现:

private void captureImage() {
    if (!playerWrapper.is_playing()) {
        showToast("请先开始播放");
        return;
    }
    if (imageDateFormat == null)
        imageDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss_SSS", Locale.getDefault());

    String imagePath = imageSavePath + "/" + imageDateFormat.format(new Date()) + ".jpeg";
    boolean success = playerWrapper.CaptureImage(0, 100, imagePath, "snapshot");
    showToast(success ? "快照已保存" : "快照失败");
}

CaptureImage 接口参数说明:

  • compress_format:0为JPEG,1为PNG
  • quality:图像质量(0~100),仅对JPEG有效
  • file_name:完整保存路径
  • user_data_string:用户自定义标识,通过事件回调返回

2.8 解码后数据回调(对接AI算法)

SDK支持将解码后的YUV/RGB数据通过 NTExternalRender 回调给上层,开发者可直接对接YOLO、OpenCV等AI算法模块。Demo中提供了RGBA和I420两种回调实现示例:

RGBA回调实现:

private static class RGBAExternalRender implements NTExternalRender {
    private int width_;
    private int height_;
    private int row_bytes_;
    private ByteBuffer rgba_buffer_;

    @Override
    public int getNTFrameFormat() {
        return NT_FRAME_FORMAT_RGBA;
    }

    @Override
    public void onNTFrameSizeChanged(int width, int height) {
        width_ = width;
        height_ = height;
        row_bytes_ = width_ * 4;
        rgba_buffer_ = ByteBuffer.allocateDirect(row_bytes_ * height_);
    }

    @Override
    public ByteBuffer getNTPlaneByteBuffer(int index) {
        if (index == 0) return rgba_buffer_;
        return null;
    }

    @Override
    public int getNTPlanePerRowBytes(int index) {
        if (index == 0) return row_bytes_;
        return 0;
    }

    public void onNTRenderFrame(int width, int height, long timestamp) {
        if (rgba_buffer_ == null) return;
        rgba_buffer_.rewind();
        // 在此处对接AI算法,处理RGBA图像数据
    }
}

I420回调实现:

private static class I420ExternalRender implements NTExternalRender {
    private int width_, height_;
    private int y_row_bytes_, u_row_bytes_, v_row_bytes_;
    private ByteBuffer y_buffer_, u_buffer_, v_buffer_;

    @Override
    public int getNTFrameFormat() {
        return NT_FRAME_FORMAT_I420;
    }

    @Override
    public void onNTFrameSizeChanged(int width, int height) {
        width_ = width;
        height_ = height;
        y_row_bytes_ = width;
        u_row_bytes_ = (width + 1) / 2;
        v_row_bytes_ = (width + 1) / 2;
        y_buffer_ = ByteBuffer.allocateDirect(y_row_bytes_ * height_);
        u_buffer_ = ByteBuffer.allocateDirect(u_row_bytes_ * ((height_ + 1) / 2));
        v_buffer_ = ByteBuffer.allocateDirect(v_row_bytes_ * ((height_ + 1) / 2));
    }

    @Override
    public ByteBuffer getNTPlaneByteBuffer(int index) {
        switch (index) {
            case 0: return y_buffer_;
            case 1: return u_buffer_;
            case 2: return v_buffer_;
            default: return null;
        }
    }

    public void onNTRenderFrame(int width, int height, long timestamp) {
        if (y_buffer_ == null || u_buffer_ == null || v_buffer_ == null) return;
        y_buffer_.rewind();
        u_buffer_.rewind();
        v_buffer_.rewind();
        // 在此处对接AI视觉算法
    }
}

启用ExternalRender时,只需在启动播放时传入对应实例:

boolean success = playerWrapper.StartPlayer(surfaceView, null, new I420ExternalRender(imageSavePath), 1,
        isFastStartup, isHardwareDecoder, isLowLatency, isHardwareRenderMode);

2.9 SEI用户数据回调

SDK支持H.264/H.265 SEI扩展用户数据的回调,可用于传输时间戳同步信息、业务自定义数据等:

private class UserDataCallback implements NTUserDataCallback {
    private int bufferSize = 0;
    private ByteBuffer buffer;

    @Override
    public ByteBuffer getUserDataByteBuffer(int size) {
        if (size < 1) return null;
        if (size <= bufferSize && buffer != null) return buffer;
        bufferSize = size + 512;
        buffer = ByteBuffer.allocateDirect(bufferSize);
        return buffer;
    }

    @Override
    public void onUserDataCallback(int ret, int dataType, int size,
                                   long timestamp, long reserve1, long reserve2) {
        if (dataType == 2 && buffer != null) {
            buffer.rewind();
            byte[] data = new byte[size];
            buffer.get(data);
            handler.obtainMessage(MSG_USER_DATA, new String(data)).sendToTarget();
        }
    }
}

2.10 其他实用功能

画面翻转与旋转:

private void toggleFlipVertical() {
    isFlipVertical = !isFlipVertical;
    if (!playerWrapper.empty())
        libPlayer.SmartPlayerSetFlipVertical(playerWrapper.get(), isFlipVertical ? 1 : 0);
}

private void toggleRotation() {
    rotateDegrees = (rotateDegrees + 90) % 360;
    if (!playerWrapper.empty())
        libPlayer.SmartPlayerSetRotation(playerWrapper.get(), rotateDegrees);
}

播放URL动态切换:

private void toggleSwitchUrl() {
    if (playerWrapper.empty()) return;
    switchUrlFlag = !switchUrlFlag;
    libPlayer.SmartPlayerSwitchPlaybackUrl(playerWrapper.get(), switchUrlFlag ? switchUrl : playbackUrl);
}

音量调节:

private void setAudioVolume(int volume) {
    audioVolume = volume;
    if (!playerWrapper.empty())
        libPlayer.SmartPlayerSetAudioVolume(playerWrapper.get(), audioVolume);
}

Android平台RTMP直播播放器延迟测试


三、RTSP/RTMP播放模块完整调用流程

以最常见的RTSP播放场景为例,完整调用流程如下:

1. SmartPlayerOpen()                     // 创建播放实例
2. SetSmartPlayerEventCallbackV2()       // 设置事件回调
3. SmartPlayerSetUrl()                   // 设置RTSP/RTMP URL
4. SmartPlayerSetRTSPTcpMode()           // 配置RTSP传输模式
5. SmartPlayerSetRTSPTimeout()           // 配置RTSP超时
6. SmartPlayerSetRTSPAutoSwitchTcpUdp()  // 配置TCP/UDP自动切换
7. SmartPlayerSetBuffer()                // 配置缓冲
8. SmartPlayerSetFastStartup()           // 配置秒开
9. SmartPlayerSetLowLatencyMode()        // 配置低延迟模式
10. SmartPlayerSetSurface()              // 设置渲染Surface
11. SetSmartPlayerVideoHWDecoder()       // 可选:配置硬解码
12. SmartPlayerSetHWRenderMode()         // 可选:配置硬解码渲染模式
13. SmartPlayerSetExternalRender()       // 可选:设置YUV/RGB数据回调
14. SmartPlayerSetMute()                 // 可选:静音控制
15. SmartPlayerSetAudioVolume()          // 可选:音量控制
16. SmartPlayerStartPlay()               // 开始播放
    ...
17. SmartPlayerStopPlay()                // 停止播放
18. SmartPlayerClose()                   // 释放实例

如需同时录像,在步骤16之前插入:

SmartPlayerCreateFileDirectory()         // 创建录像目录
SmartPlayerSetRecorderDirectory()        // 设置录像目录
SmartPlayerSetRecorderFileMaxSize()      // 设置单文件大小限制
SmartPlayerSetRecorderAudioTranscodeAAC() // 音频转码设置
SmartPlayerStartRecorder()               // 开始录像

四、功能能力矩阵

功能维度 能力项
协议支持 RTSP、RTMP、Enhanced RTMP
编码支持 H.264、H.265/HEVC、AAC、PCMA、PCMU、Speex
解码模式 软解码、H.264硬解码、H.265硬解码
渲染模式 SurfaceView渲染、GLSurfaceView渲染、MediaCodec直接渲染
延迟控制 秒开(Fast Startup)、超低延迟模式(Low Latency Mode)
音视频控制 静音、音量调节(0~100)、画面翻转、旋转(0°/90°/180°/270°)
图像调节 亮度、对比度、饱和度(需GLSurfaceView)
录像 边播边录、自动分段、音频转AAC、可选录视频/录音频
快照 JPEG/PNG格式、自定义质量
数据回调 RGBA/I420解码后数据、编码后音视频数据、SEI用户数据
网络适配 RTSP TCP/UDP自动切换、超时设置、断网重连、下载速度上报
安全 RTMP流AES加密解密(支持Key+IV)
URL管理 播放中动态切换URL
多实例 支持同时多路播放
外部数据 支持投递外部编码后的音视频数据、RTP Receiver接入

Android平台Unity3D下RTMP播放器延迟测试


五、典型应用场景

5.1 安防监控

对接海康、大华等品牌IPC的RTSP流,支持多路同时播放,毫秒级延迟满足实时监控需求。RTSP TCP/UDP自动切换机制确保在不同网络环境下的兼容性。

5.2 无人机/机器人远程操控

无人机和机器人回传画面对延迟极为敏感,SDK的超低延迟模式配合秒开功能,可将端到端延迟控制在200ms以内,满足实时操控的精度要求。

5.3 智慧教育与远程培训

通过RTMP协议实现远程教学直播,支持边播边录、实时快照,方便回放和资料存档。

5.4 AI视觉分析

通过解码后数据回调接口(I420/RGBA),将视频帧直接输入YOLO、OpenCV等算法模块,实现实时目标检测、人脸识别等AI应用,无需额外的解码环节。

5.5 工业巡检与智慧交通

在工业手持终端、车载设备等Android平台上,SDK的轻量级设计和低资源占用特性使其能够稳定运行,配合录像和快照功能满足取证留档需求。


六、总结

大牛直播SDK(SmartMediaKit)的Android平台RTSP/RTMP播放模块,经过十余年的持续打磨,在以下方面形成了突出的技术优势:

低延迟:自研协议栈和解码引擎,RTSP/RTMP播放延迟可控制在100~200ms级别,远优于通用开源方案。

高性能:支持H.264/H.265软硬解灵活切换,MediaCodec直接渲染模式最大化利用硬件能力,多路并发播放稳定可靠。

功能全面:从播放、录像、快照、拉流转发、数据回调到音视频调节,形成了完整的功能矩阵,几乎覆盖所有实时播放场景的需求。

架构灵活:三层架构设计(JNI内核→线程安全封装→业务Demo),LibPlayerWrapper 的读写锁机制和资源管理策略确保了工程级的稳定性。模块化解耦设计使得播放、录像、拉流等功能可独立启停、灵活组合。

跨平台一致:同一套内核支持Windows、Linux、Android、iOS及Unity3D,一次集成、多端运行,降低了跨平台适配的工程成本。

对于有实时音视频播放需求的Android开发者而言,大牛直播SDK提供的不仅是一个播放器API,更是一套可直接应用于生产环境的、经过大量场景验证的工程级解决方案。


📎 CSDN官方博客:音视频牛哥-CSDN博客

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐